Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdae1c59ab | ||
|
|
0b00e19ed9 | ||
|
|
c8d8126bd0 | ||
|
|
0de9f32ae8 | ||
|
|
14ae1246b7 | ||
|
|
dc59af8622 | ||
|
|
45730cfab3 | ||
|
|
4e13a09c50 | ||
|
|
009d2fe2d6 | ||
|
|
e872ba7a9e | ||
|
|
9d9c6b5847 | ||
|
|
8ca6454807 | ||
|
|
0e63a70505 | ||
|
|
f1b00cffc8 | ||
|
|
442320a8ae | ||
|
|
af401d03a3 | ||
|
|
80a2a35bc3 | ||
|
|
fca5f9fd6f | ||
|
|
38c30f8dd8 | ||
|
|
c942700427 | ||
|
|
cde35439e0 | ||
|
|
4f908db69e | ||
|
|
320d132925 | ||
|
|
7ae2a7bd84 | ||
|
|
fd34bf594c | ||
|
|
996230174c | ||
|
|
8857707606 | ||
|
|
d6c1fcbe04 | ||
|
|
79cac793c0 | ||
|
|
5de6b6b529 | ||
|
|
3d2bedf8d0 | ||
|
|
8ea3d8ad90 | ||
|
|
a0127019c3 | ||
|
|
7a333e4104 | ||
|
|
799fe99537 | ||
|
|
3b02b0ba4b | ||
|
|
85217b08bd | ||
|
|
dcff622d43 | ||
|
|
a3db00f270 | ||
|
|
769e43e334 | ||
|
|
8d8ddea1a3 | ||
|
|
068725c5b0 | ||
|
|
710775f435 | ||
|
|
0fd0108507 | ||
|
|
3c62cc6bba | ||
|
|
333b1bfb6c | ||
|
|
d1ace4f344 | ||
|
|
637a75d61a | ||
|
|
355d55bd34 | ||
|
|
7038b5734c | ||
|
|
1ecf2860cf | ||
|
|
034f65e9e8 | ||
|
|
607a1968e6 | ||
|
|
3f54994db0 | ||
|
|
2695aa9e0d | ||
|
|
e247dcc141 | ||
|
|
b774d0a507 | ||
|
|
4976fcc91a | ||
|
|
878aa58ec6 | ||
|
|
475a0664c5 | ||
|
|
4625b1257f | ||
|
|
21d54bcaac | ||
|
|
7383db4dac | ||
|
|
afb65f6ace | ||
|
|
1f1c751b6e | ||
|
|
a3f31f51f3 | ||
|
|
e63995b3f3 | ||
|
|
dd3e894747 | ||
|
|
df355eceb4 | ||
|
|
84cb00a94d | ||
|
|
992a7bbad5 | ||
|
|
a458153098 | ||
|
|
fe5258b41e | ||
|
|
d9be337669 | ||
|
|
7bd6f39dc3 | ||
|
|
b247052a64 | ||
|
|
276f824707 | ||
|
|
048b463b30 | ||
|
|
9f5fb15097 | ||
|
|
2072c26a96 | ||
|
|
4da2092908 | ||
|
|
3ab9dcc3bd | ||
|
|
18f702faf7 | ||
|
|
3a95128b22 | ||
|
|
631e2f07f6 | ||
|
|
7fa3509e2e | ||
|
|
86ec742f97 | ||
|
|
d9a07fba67 | ||
|
|
4cd90e02e2 | ||
|
|
1f3dfed19e | ||
|
|
2ae481ff6b | ||
|
|
c7664b0636 |
@@ -1,3 +1,7 @@
|
|||||||
|
**/.git
|
||||||
|
.git
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/refs/heads
|
||||||
**/*_test.go
|
**/*_test.go
|
||||||
|
|
||||||
build/_workspace
|
build/_workspace
|
||||||
|
|||||||
23
.github/CODEOWNERS
vendored
23
.github/CODEOWNERS
vendored
@@ -2,21 +2,12 @@
|
|||||||
# Each line is a file pattern followed by one or more owners.
|
# Each line is a file pattern followed by one or more owners.
|
||||||
|
|
||||||
accounts/usbwallet @karalabe
|
accounts/usbwallet @karalabe
|
||||||
accounts/scwallet @gballet
|
accounts/abi @gballet
|
||||||
accounts/abi @gballet @MariusVanDerWijden
|
|
||||||
cmd/clef @holiman
|
|
||||||
consensus @karalabe
|
consensus @karalabe
|
||||||
core/ @karalabe @holiman @rjl493456442
|
core/ @karalabe @holiman
|
||||||
eth/ @karalabe @holiman @rjl493456442
|
eth/ @karalabe
|
||||||
eth/catalyst/ @gballet
|
les/ @zsfelfoldi
|
||||||
eth/tracers/ @s1na
|
light/ @zsfelfoldi
|
||||||
graphql/ @gballet @s1na
|
mobile/ @karalabe
|
||||||
les/ @zsfelfoldi @rjl493456442
|
|
||||||
light/ @zsfelfoldi @rjl493456442
|
|
||||||
node/ @fjl
|
|
||||||
p2p/ @fjl @zsfelfoldi
|
p2p/ @fjl @zsfelfoldi
|
||||||
rpc/ @fjl @holiman
|
whisper/ @gballet @gluk256
|
||||||
p2p/simulations @fjl
|
|
||||||
p2p/protocols @fjl
|
|
||||||
p2p/testing @fjl
|
|
||||||
signer/ @holiman
|
|
||||||
|
|||||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -30,11 +30,11 @@ Please make sure your contributions adhere to our coding guidelines:
|
|||||||
Before you submit a feature request, please check and make sure that it isn't
|
Before you submit a feature request, please check and make sure that it isn't
|
||||||
possible through some other means. The JavaScript-enabled console is a powerful
|
possible through some other means. The JavaScript-enabled console is a powerful
|
||||||
feature in the right hands. Please check our
|
feature in the right hands. Please check our
|
||||||
[Geth documentation page](https://geth.ethereum.org/docs/) for more info
|
[Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info
|
||||||
and help.
|
and help.
|
||||||
|
|
||||||
## Configuration, dependencies, and tests
|
## Configuration, dependencies, and tests
|
||||||
|
|
||||||
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide)
|
Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
|
||||||
for more details on configuring your environment, managing project dependencies
|
for more details on configuring your environment, managing project dependencies
|
||||||
and testing procedures.
|
and testing procedures.
|
||||||
|
|||||||
26
.github/ISSUE_TEMPLATE.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Hi there,
|
||||||
|
|
||||||
|
please note that this is an issue tracker reserved for bug reports and feature requests.
|
||||||
|
|
||||||
|
For general questions please use the gitter channel or the Ethereum stack exchange at https://ethereum.stackexchange.com.
|
||||||
|
|
||||||
|
#### System information
|
||||||
|
|
||||||
|
Geth version: `geth version`
|
||||||
|
OS & Version: Windows/Linux/OSX
|
||||||
|
Commit hash : (if `develop`)
|
||||||
|
|
||||||
|
#### Expected behaviour
|
||||||
|
|
||||||
|
|
||||||
|
#### Actual behaviour
|
||||||
|
|
||||||
|
|
||||||
|
#### Steps to reproduce the behaviour
|
||||||
|
|
||||||
|
|
||||||
|
#### Backtrace
|
||||||
|
|
||||||
|
````
|
||||||
|
[backtrace]
|
||||||
|
````
|
||||||
31
.github/ISSUE_TEMPLATE/bug.md
vendored
31
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
name: Report a bug
|
|
||||||
about: Something with go-ethereum is not working as expected
|
|
||||||
title: ''
|
|
||||||
labels: 'type:bug'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
#### System information
|
|
||||||
|
|
||||||
Geth version: `geth version`
|
|
||||||
CL client & version: e.g. lighthouse/nimbus/prysm@v1.0.0
|
|
||||||
OS & Version: Windows/Linux/OSX
|
|
||||||
Commit hash : (if `develop`)
|
|
||||||
|
|
||||||
#### Expected behaviour
|
|
||||||
|
|
||||||
|
|
||||||
#### Actual behaviour
|
|
||||||
|
|
||||||
|
|
||||||
#### Steps to reproduce the behaviour
|
|
||||||
|
|
||||||
|
|
||||||
#### Backtrace
|
|
||||||
|
|
||||||
````
|
|
||||||
[backtrace]
|
|
||||||
````
|
|
||||||
|
|
||||||
When submitting logs: please submit them as text and not screenshots.
|
|
||||||
17
.github/ISSUE_TEMPLATE/feature.md
vendored
17
.github/ISSUE_TEMPLATE/feature.md
vendored
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Request a feature
|
|
||||||
about: Report a missing feature - e.g. as a step before submitting a PR
|
|
||||||
title: ''
|
|
||||||
labels: 'type:feature'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
# Rationale
|
|
||||||
|
|
||||||
Why should this feature exist?
|
|
||||||
What are the use-cases?
|
|
||||||
|
|
||||||
# Implementation
|
|
||||||
|
|
||||||
Do you have ideas regarding the implementation of this feature?
|
|
||||||
Are you willing to implement this feature?
|
|
||||||
9
.github/ISSUE_TEMPLATE/question.md
vendored
9
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
name: Ask a question
|
|
||||||
about: Something is unclear
|
|
||||||
title: ''
|
|
||||||
labels: 'type:docs'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
This should only be used in very rare cases e.g. if you are not 100% sure if something is a bug or asking a question that leads to improving the documentation. For general questions please use [discord](https://discord.gg/nthXNEv) or the Ethereum stack exchange at https://ethereum.stackexchange.com.
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,7 +24,6 @@ build/_vendor/pkg
|
|||||||
|
|
||||||
# used by the Makefile
|
# used by the Makefile
|
||||||
/build/_workspace/
|
/build/_workspace/
|
||||||
/build/cache/
|
|
||||||
/build/bin/
|
/build/bin/
|
||||||
/geth*.zip
|
/geth*.zip
|
||||||
|
|
||||||
@@ -43,7 +42,6 @@ profile.cov
|
|||||||
/dashboard/assets/node_modules
|
/dashboard/assets/node_modules
|
||||||
/dashboard/assets/stats.json
|
/dashboard/assets/stats.json
|
||||||
/dashboard/assets/bundle.js
|
/dashboard/assets/bundle.js
|
||||||
/dashboard/assets/bundle.js.map
|
|
||||||
/dashboard/assets/package-lock.json
|
/dashboard/assets/package-lock.json
|
||||||
|
|
||||||
**/yarn-error.log
|
**/yarn-error.log
|
||||||
|
|||||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -1,8 +1,3 @@
|
|||||||
[submodule "tests"]
|
[submodule "tests"]
|
||||||
path = tests/testdata
|
path = tests/testdata
|
||||||
url = https://github.com/ethereum/tests
|
url = https://github.com/ethereum/tests
|
||||||
shallow = true
|
|
||||||
[submodule "evm-benchmarks"]
|
|
||||||
path = tests/evm-benchmarks
|
|
||||||
url = https://github.com/ipsilon/evm-benchmarks
|
|
||||||
shallow = true
|
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
# This file configures github.com/golangci/golangci-lint.
|
|
||||||
|
|
||||||
run:
|
|
||||||
timeout: 20m
|
|
||||||
tests: true
|
|
||||||
# default is true. Enables skipping of directories:
|
|
||||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
|
||||||
skip-dirs-use-default: true
|
|
||||||
skip-files:
|
|
||||||
- core/genesis_alloc.go
|
|
||||||
|
|
||||||
linters:
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
|
||||||
- goconst
|
|
||||||
- goimports
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- misspell
|
|
||||||
- unconvert
|
|
||||||
- typecheck
|
|
||||||
- unused
|
|
||||||
- staticcheck
|
|
||||||
- bidichk
|
|
||||||
- durationcheck
|
|
||||||
- exportloopref
|
|
||||||
- whitespace
|
|
||||||
|
|
||||||
# - structcheck # lots of false positives
|
|
||||||
# - errcheck #lot of false positives
|
|
||||||
# - contextcheck
|
|
||||||
# - errchkjson # lots of false positives
|
|
||||||
# - errorlint # this check crashes
|
|
||||||
# - exhaustive # silly check
|
|
||||||
# - makezero # false positives
|
|
||||||
# - nilerr # several intentional
|
|
||||||
|
|
||||||
linters-settings:
|
|
||||||
gofmt:
|
|
||||||
simplify: true
|
|
||||||
goconst:
|
|
||||||
min-len: 3 # minimum length of string constant
|
|
||||||
min-occurrences: 6 # minimum number of occurrences
|
|
||||||
|
|
||||||
issues:
|
|
||||||
exclude-rules:
|
|
||||||
- path: crypto/bn256/cloudflare/optate.go
|
|
||||||
linters:
|
|
||||||
- deadcode
|
|
||||||
- staticcheck
|
|
||||||
- path: internal/build/pgp.go
|
|
||||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
|
||||||
- path: core/vm/contracts.go
|
|
||||||
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
|
||||||
- path: accounts/usbwallet/trezor.go
|
|
||||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
|
||||||
- path: accounts/usbwallet/trezor/
|
|
||||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
|
||||||
exclude:
|
|
||||||
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
|
||||||
- 'SA1019: strings.Title is deprecated'
|
|
||||||
- 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.'
|
|
||||||
- 'SA1029: should not use built-in type string as key for value'
|
|
||||||
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>
|
Joseph Goulden <joegoulden@gmail.com>
|
||||||
Abel Nieto <abel.nieto90@gmail.com> <anietoro@uwaterloo.ca>
|
|
||||||
|
|
||||||
Afri Schoedon <58883403+q9f@users.noreply.github.com>
|
Nick Savers <nicksavers@gmail.com>
|
||||||
Afri Schoedon <5chdn@users.noreply.github.com> <58883403+q9f@users.noreply.github.com>
|
|
||||||
|
|
||||||
Alec Perseghin <aperseghin@gmail.com>
|
Maran Hidskes <maran.hidskes@gmail.com>
|
||||||
|
|
||||||
Aleksey Smyrnov <i@soar.name>
|
Taylor Gerring <taylor.gerring@gmail.com>
|
||||||
|
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
|
||||||
Alex Leverington <alex@ethdev.com>
|
|
||||||
Alex Leverington <alex@ethdev.com> <subtly@users.noreply.github.com>
|
|
||||||
|
|
||||||
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
|
|
||||||
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com> <leshiy12345678@gmail.com>
|
|
||||||
|
|
||||||
Alexey Akhunov <akhounov@gmail.com>
|
|
||||||
|
|
||||||
Alon Muroch <alonmuroch@gmail.com>
|
|
||||||
|
|
||||||
Andrey Petrov <shazow@gmail.com>
|
|
||||||
Andrey Petrov <shazow@gmail.com> <andrey.petrov@shazow.net>
|
|
||||||
|
|
||||||
Arkadiy Paronyan <arkadiy@ethdev.com>
|
|
||||||
|
|
||||||
Armin Braun <me@obrown.io>
|
|
||||||
|
|
||||||
Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
|
|
||||||
|
|
||||||
Austin Roberts <code@ausiv.com>
|
|
||||||
Austin Roberts <code@ausiv.com> <git@ausiv.com>
|
|
||||||
|
|
||||||
Bas van Kervel <bas@ethdev.com>
|
Bas van Kervel <bas@ethdev.com>
|
||||||
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
|
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
|
||||||
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
|
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
|
||||||
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
|
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
|
||||||
|
|
||||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
Sven Ehlert <sven@ethdev.com>
|
||||||
Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
|
||||||
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Vitalik Buterin <v@buterin.com>
|
||||||
|
|
||||||
Cheng Li <lob4tt@gmail.com>
|
Marian Oancea <contact@siteshop.ro>
|
||||||
|
|
||||||
Chris Ziogas <ziogaschr@gmail.com>
|
|
||||||
Chris Ziogas <ziogaschr@gmail.com> <ziogas_chr@hotmail.com>
|
|
||||||
|
|
||||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
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>
|
Dimitry Khokhlov <winsvega@mail.ru>
|
||||||
|
|
||||||
Domino Valdano <dominoplural@gmail.com>
|
Roman Mandeleil <roman.mandeleil@gmail.com>
|
||||||
Domino Valdano <dominoplural@gmail.com> <jeff@okcupid.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 <enriquefynn@gmail.com>
|
||||||
|
|
||||||
Enrique Fynn <me@enriquefynn.com>
|
Vincent G <caktux@gmail.com>
|
||||||
Enrique Fynn <me@enriquefynn.com> <enriquefynn@gmail.com>
|
|
||||||
|
|
||||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
RJ Catalano <catalanor0220@gmail.com>
|
||||||
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.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>
|
||||||
Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
|
Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
|
||||||
|
|
||||||
|
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||||
|
|
||||||
|
Louis Holbrook <dev@holbrook.no>
|
||||||
|
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
|
||||||
|
|
||||||
|
Thomas Bocek <tom@tomp2p.net>
|
||||||
|
|
||||||
|
Victor Tran <vu.tran54@gmail.com>
|
||||||
|
|
||||||
|
Justin Drake <drakefjustin@gmail.com>
|
||||||
|
|
||||||
Frank Wang <eternnoir@gmail.com>
|
Frank Wang <eternnoir@gmail.com>
|
||||||
|
|
||||||
Gary Rong <garyrong0905@gmail.com>
|
Gary Rong <garyrong0905@gmail.com>
|
||||||
|
|
||||||
Gavin Wood <i@gavwood.com>
|
|
||||||
|
|
||||||
Gregg Dourgarian <greggd@tempworks.com>
|
|
||||||
|
|
||||||
Guillaume Ballet <gballet@gmail.com>
|
|
||||||
Guillaume Ballet <gballet@gmail.com> <3272758+gballet@users.noreply.github.com>
|
|
||||||
|
|
||||||
Guillaume Nicolas <guin56@gmail.com>
|
Guillaume Nicolas <guin56@gmail.com>
|
||||||
|
|
||||||
Hanjiang Yu <delacroix.yu@gmail.com>
|
|
||||||
Hanjiang Yu <delacroix.yu@gmail.com> <42531996+de1acr0ix@users.noreply.github.com>
|
|
||||||
|
|
||||||
Heiko Hees <heiko@heiko.org>
|
|
||||||
|
|
||||||
Henning Diedrich <hd@eonblast.com>
|
|
||||||
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
|
|
||||||
|
|
||||||
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
|
|
||||||
|
|
||||||
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
|
||||||
Iskander (Alex) Sharipov <quasilyte@gmail.com> <i.sharipov@corp.vk.com>
|
|
||||||
|
|
||||||
Jae Kwon <jkwon.work@gmail.com>
|
|
||||||
|
|
||||||
Janoš Guljaš <janos@resenje.org> <janos@users.noreply.github.com>
|
|
||||||
Janoš Guljaš <janos@resenje.org> Janos Guljas <janos@resenje.org>
|
|
||||||
|
|
||||||
Jared Wasinger <j-wasinger@hotmail.com>
|
|
||||||
|
|
||||||
Jason Carver <jacarver@linkedin.com>
|
|
||||||
Jason Carver <jacarver@linkedin.com> <ut96caarrs@snkmail.com>
|
|
||||||
|
|
||||||
Javier Peletier <jm@epiclabs.io>
|
|
||||||
Javier Peletier <jm@epiclabs.io> <jpeletier@users.noreply.github.com>
|
|
||||||
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
|
|
||||||
|
|
||||||
Jens Agerberg <github@agerberg.me>
|
|
||||||
|
|
||||||
Joseph Chow <ethereum@outlook.com>
|
|
||||||
Joseph Chow <ethereum@outlook.com> ethers <TODO>
|
|
||||||
|
|
||||||
|
|
||||||
Joseph Goulden <joegoulden@gmail.com>
|
|
||||||
|
|
||||||
Justin Drake <drakefjustin@gmail.com>
|
|
||||||
|
|
||||||
Kenso Trabing <ktrabing@acm.org>
|
|
||||||
Kenso Trabing <ktrabing@acm.org> <kenso.trabing@bloomwebsite.com>
|
|
||||||
|
|
||||||
Liang Ma <liangma@liangbit.com>
|
|
||||||
Liang Ma <liangma@liangbit.com> <liangma.ul@gmail.com>
|
|
||||||
|
|
||||||
Louis Holbrook <dev@holbrook.no>
|
|
||||||
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
|
|
||||||
|
|
||||||
Maran Hidskes <maran.hidskes@gmail.com>
|
|
||||||
|
|
||||||
Marian Oancea <contact@siteshop.ro>
|
|
||||||
|
|
||||||
Martin Becze <mjbecze@gmail.com>
|
|
||||||
Martin Becze <mjbecze@gmail.com> <wanderer@users.noreply.github.com>
|
|
||||||
|
|
||||||
Martin Lundfall <martin.lundfall@protonmail.com>
|
|
||||||
|
|
||||||
Matt Garnett <14004106+lightclient@users.noreply.github.com>
|
|
||||||
|
|
||||||
Matthew Halpern <matthalp@gmail.com>
|
|
||||||
Matthew Halpern <matthalp@gmail.com> <matthalp@google.com>
|
|
||||||
|
|
||||||
Michael Riabzev <michael@starkware.co>
|
|
||||||
|
|
||||||
Nchinda Nchinda <nchinda2@gmail.com>
|
|
||||||
|
|
||||||
Nick Dodson <silentcicero@outlook.com>
|
|
||||||
|
|
||||||
Nick Johnson <arachnid@notdot.net>
|
|
||||||
|
|
||||||
Nick Savers <nicksavers@gmail.com>
|
|
||||||
|
|
||||||
Nishant Das <nishdas93@gmail.com>
|
|
||||||
Nishant Das <nishdas93@gmail.com> <nish1993@hotmail.com>
|
|
||||||
|
|
||||||
Olivier Hervieu <olivier.hervieu@gmail.com>
|
|
||||||
|
|
||||||
Pascal Dierich <pascal@merkleplant.xyz>
|
|
||||||
Pascal Dierich <pascal@merkleplant.xyz> <pascal@pascaldierich.com>
|
|
||||||
|
|
||||||
RJ Catalano <catalanor0220@gmail.com>
|
|
||||||
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
|
|
||||||
|
|
||||||
Ralph Caraveo <deckarep@gmail.com>
|
|
||||||
|
|
||||||
Rene Lubov <41963722+renaynay@users.noreply.github.com>
|
|
||||||
|
|
||||||
Robert Zaremba <robert@zaremba.ch>
|
|
||||||
Robert Zaremba <robert@zaremba.ch> <robert.zaremba@scale-it.pl>
|
|
||||||
|
|
||||||
Roman Mandeleil <roman.mandeleil@gmail.com>
|
|
||||||
|
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
|
||||||
|
|
||||||
Sven Ehlert <sven@ethdev.com>
|
|
||||||
|
|
||||||
Taylor Gerring <taylor.gerring@gmail.com>
|
|
||||||
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
|
|
||||||
|
|
||||||
Thomas Bocek <tom@tomp2p.net>
|
|
||||||
|
|
||||||
Tim Cooijmans <timcooijmans@gmail.com>
|
|
||||||
|
|
||||||
Valentin Wüstholz <wuestholz@gmail.com>
|
Valentin Wüstholz <wuestholz@gmail.com>
|
||||||
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
|
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
|
||||||
|
|
||||||
Victor Tran <vu.tran54@gmail.com>
|
Armin Braun <me@obrown.io>
|
||||||
|
|
||||||
Viktor Trón <viktor.tron@gmail.com>
|
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||||
|
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
|
||||||
Ville Sundell <github@solarius.fi>
|
|
||||||
|
|
||||||
Vincent G <caktux@gmail.com>
|
|
||||||
|
|
||||||
Vitalik Buterin <v@buterin.com>
|
|
||||||
|
|
||||||
Vlad Gluhovsky <gluk256@gmail.com>
|
|
||||||
Vlad Gluhovsky <gluk256@gmail.com> <gluk256@users.noreply.github.com>
|
|
||||||
|
|
||||||
Wenshao Zhong <wzhong20@uic.edu>
|
|
||||||
Wenshao Zhong <wzhong20@uic.edu> <11510383@mail.sustc.edu.cn>
|
|
||||||
Wenshao Zhong <wzhong20@uic.edu> <374662347@qq.com>
|
|
||||||
|
|
||||||
Will Villanueva <hello@willvillanueva.com>
|
|
||||||
|
|
||||||
Xiaobing Jiang <s7v7nislands@gmail.com>
|
|
||||||
|
|
||||||
Xudong Liu <33193253+r1cs@users.noreply.github.com>
|
|
||||||
|
|
||||||
Yohann Léon <sybiload@gmail.com>
|
|
||||||
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
|
|
||||||
|
|
||||||
Ziyuan Zhong <zzy.albert@163.com>
|
|
||||||
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
|
||||||
|
|
||||||
meowsbits <b5c6@protonmail.com>
|
|
||||||
meowsbits <b5c6@protonmail.com> <45600330+meowsbits@users.noreply.github.com>
|
|
||||||
|
|
||||||
nedifi <103940716+nedifi@users.noreply.github.com>
|
|
||||||
|
|
||||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
|
||||||
|
|||||||
268
.travis.yml
268
.travis.yml
@@ -1,20 +1,50 @@
|
|||||||
language: go
|
language: go
|
||||||
go_import_path: github.com/ethereum/go-ethereum
|
go_import_path: github.com/ethereum/go-ethereum
|
||||||
sudo: false
|
sudo: false
|
||||||
jobs:
|
matrix:
|
||||||
allow_failures:
|
|
||||||
- stage: build
|
|
||||||
os: osx
|
|
||||||
go: 1.17.x
|
|
||||||
env:
|
|
||||||
- azure-osx
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
|
sudo: required
|
||||||
|
go: 1.10.x
|
||||||
|
script:
|
||||||
|
- sudo modprobe fuse
|
||||||
|
- sudo chmod 666 /dev/fuse
|
||||||
|
- sudo chown root:$USER /etc/fuse.conf
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
|
# These are the latest Go versions.
|
||||||
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
|
sudo: required
|
||||||
|
go: 1.11.x
|
||||||
|
script:
|
||||||
|
- sudo modprobe fuse
|
||||||
|
- sudo chmod 666 /dev/fuse
|
||||||
|
- sudo chown root:$USER /etc/fuse.conf
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
go: 1.11.x
|
||||||
|
script:
|
||||||
|
- echo "Increase the maximum number of open file descriptors on macOS"
|
||||||
|
- NOFILE=20480
|
||||||
|
- sudo sysctl -w kern.maxfiles=$NOFILE
|
||||||
|
- sudo sysctl -w kern.maxfilesperproc=$NOFILE
|
||||||
|
- sudo launchctl limit maxfiles $NOFILE $NOFILE
|
||||||
|
- sudo launchctl limit maxfiles
|
||||||
|
- ulimit -S -n $NOFILE
|
||||||
|
- ulimit -n
|
||||||
|
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
# This builder only tests code linters on latest version of Go
|
# This builder only tests code linters on latest version of Go
|
||||||
- stage: lint
|
- os: linux
|
||||||
os: linux
|
dist: xenial
|
||||||
dist: bionic
|
go: 1.11.x
|
||||||
go: 1.20.x
|
|
||||||
env:
|
env:
|
||||||
- lint
|
- lint
|
||||||
git:
|
git:
|
||||||
@@ -22,51 +52,13 @@ jobs:
|
|||||||
script:
|
script:
|
||||||
- go run build/ci.go lint
|
- go run build/ci.go lint
|
||||||
|
|
||||||
# These builders create the Docker sub-images for multi-arch push and each
|
|
||||||
# will attempt to push the multi-arch image if they are the last builder
|
|
||||||
- stage: build
|
|
||||||
if: type = push
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
dist: bionic
|
|
||||||
go: 1.20.x
|
|
||||||
env:
|
|
||||||
- docker
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
git:
|
|
||||||
submodules: false # avoid cloning ethereum/tests
|
|
||||||
before_install:
|
|
||||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
|
||||||
script:
|
|
||||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
|
||||||
|
|
||||||
- stage: build
|
|
||||||
if: type = push
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
dist: bionic
|
|
||||||
go: 1.20.x
|
|
||||||
env:
|
|
||||||
- docker
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
git:
|
|
||||||
submodules: false # avoid cloning ethereum/tests
|
|
||||||
before_install:
|
|
||||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
|
||||||
script:
|
|
||||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
|
||||||
|
|
||||||
# This builder does the Ubuntu PPA upload
|
# This builder does the Ubuntu PPA upload
|
||||||
- stage: build
|
- if: type = push
|
||||||
if: type = push
|
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: xenial
|
||||||
go: 1.20.x
|
go: 1.11.x
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
- GO111MODULE=on
|
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
addons:
|
addons:
|
||||||
@@ -83,15 +75,13 @@ jobs:
|
|||||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||||
|
|
||||||
# This builder does the Linux Azure uploads
|
# This builder does the Linux Azure uploads
|
||||||
- stage: build
|
- if: type = push
|
||||||
if: type = push
|
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: xenial
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.20.x
|
go: 1.11.x
|
||||||
env:
|
env:
|
||||||
- azure-linux
|
- azure-linux
|
||||||
- GO111MODULE=on
|
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
addons:
|
addons:
|
||||||
@@ -100,91 +90,127 @@ jobs:
|
|||||||
- gcc-multilib
|
- gcc-multilib
|
||||||
script:
|
script:
|
||||||
# Build for the primary platforms that Trusty can manage
|
# Build for the primary platforms that Trusty can manage
|
||||||
- go run build/ci.go install -dlgo
|
- go run build/ci.go install
|
||||||
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
- go run build/ci.go install -dlgo -arch 386
|
- go run build/ci.go install -arch 386
|
||||||
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# Switch over GCC to cross compilation (breaks 386, hence why do it here only)
|
# Switch over GCC to cross compilation (breaks 386, hence why do it here only)
|
||||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||||
- sudo ln -s /usr/include/asm-generic /usr/include/asm
|
- sudo ln -s /usr/include/asm-generic /usr/include/asm
|
||||||
|
|
||||||
- GOARM=5 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
|
- GOARM=5 go run build/ci.go install -arch arm -cc arm-linux-gnueabi-gcc
|
||||||
- GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
- GOARM=6 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
|
- GOARM=6 go run build/ci.go install -arch arm -cc arm-linux-gnueabi-gcc
|
||||||
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
- GOARM=7 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabihf-gcc
|
- GOARM=7 go run build/ci.go install -arch arm -cc arm-linux-gnueabihf-gcc
|
||||||
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
|
- go run build/ci.go install -arch arm64 -cc aarch64-linux-gnu-gcc
|
||||||
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the OSX Azure uploads
|
# This builder does the Linux Azure MIPS xgo uploads
|
||||||
- stage: build
|
- if: type = push
|
||||||
if: type = push
|
os: linux
|
||||||
os: osx
|
dist: xenial
|
||||||
go: 1.20.x
|
services:
|
||||||
|
- docker
|
||||||
|
go: 1.11.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-linux-mips
|
||||||
- GO111MODULE=on
|
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install -dlgo
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
|
||||||
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
|
||||||
|
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# These builders run the tests
|
- go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
|
||||||
- stage: build
|
- for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
|
||||||
os: linux
|
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
arch: amd64
|
|
||||||
dist: bionic
|
|
||||||
go: 1.20.x
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
script:
|
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
|
||||||
|
|
||||||
- stage: build
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
|
||||||
if: type = pull_request
|
- for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
|
||||||
os: linux
|
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
arch: arm64
|
|
||||||
dist: bionic
|
|
||||||
go: 1.19.x
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
script:
|
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
|
||||||
|
|
||||||
- stage: build
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
|
||||||
|
- for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
|
||||||
|
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
# This builder does the Android Maven and Azure uploads
|
||||||
|
- if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: xenial
|
||||||
go: 1.19.x
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- oracle-java8-installer
|
||||||
|
- oracle-java8-set-default
|
||||||
|
language: android
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
- platform-tools
|
||||||
|
- tools
|
||||||
|
- android-15
|
||||||
|
- android-19
|
||||||
|
- android-24
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- azure-android
|
||||||
|
- maven-android
|
||||||
|
git:
|
||||||
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
before_install:
|
||||||
|
- curl https://storage.googleapis.com/golang/go1.11.5.linux-amd64.tar.gz | tar -xz
|
||||||
|
- export PATH=`pwd`/go/bin:$PATH
|
||||||
|
- export GOROOT=`pwd`/go
|
||||||
|
- export GOPATH=$HOME/go
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
# Build the Android archive and upload it to Maven Central and Azure
|
||||||
|
- curl https://dl.google.com/android/repository/android-ndk-r17b-linux-x86_64.zip -o android-ndk-r17b.zip
|
||||||
|
- unzip -q android-ndk-r17b.zip && rm android-ndk-r17b.zip
|
||||||
|
- mv android-ndk-r17b $HOME
|
||||||
|
- export ANDROID_NDK=$HOME/android-ndk-r17b
|
||||||
|
|
||||||
|
- mkdir -p $GOPATH/src/github.com/ethereum
|
||||||
|
- ln -s `pwd` $GOPATH/src/github.com/ethereum
|
||||||
|
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
|
||||||
|
|
||||||
|
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
|
||||||
|
- if: type = push
|
||||||
|
os: osx
|
||||||
|
go: 1.11.x
|
||||||
|
env:
|
||||||
|
- azure-osx
|
||||||
|
- azure-ios
|
||||||
|
- cocoapods-ios
|
||||||
|
git:
|
||||||
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
script:
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go archive -type tar -signer OSX_SIGNING_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 -deploy trunk -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the Azure archive purges to avoid accumulating junk
|
# This builder does the Azure archive purges to avoid accumulating junk
|
||||||
- stage: build
|
- if: type = cron
|
||||||
if: type = cron
|
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: xenial
|
||||||
go: 1.20.x
|
go: 1.11.x
|
||||||
env:
|
env:
|
||||||
- azure-purge
|
- azure-purge
|
||||||
- GO111MODULE=on
|
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go purge -store gethstore/builds -days 14
|
- go run build/ci.go purge -store gethstore/builds -days 14
|
||||||
|
|
||||||
# This builder executes race tests
|
|
||||||
- stage: build
|
|
||||||
if: type = cron
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
go: 1.20.x
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
script:
|
|
||||||
- go run build/ci.go test -race -coverage $TEST_PACKAGES
|
|
||||||
|
|
||||||
|
|||||||
477
AUTHORS
477
AUTHORS
@@ -1,591 +1,174 @@
|
|||||||
# This is the official list of go-ethereum authors for copyright purposes.
|
# This is the official list of go-ethereum authors for copyright purposes.
|
||||||
|
|
||||||
6543 <6543@obermui.de>
|
|
||||||
a e r t h <aerth@users.noreply.github.com>
|
|
||||||
Aaron Buchwald <aaron.buchwald56@gmail.com>
|
|
||||||
Abel Nieto <abel.nieto90@gmail.com>
|
|
||||||
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>
|
Afri Schoedon <5chdn@users.noreply.github.com>
|
||||||
Agustin Armellini Fischer <armellini13@gmail.com>
|
Agustin Armellini Fischer <armellini13@gmail.com>
|
||||||
Ahyun <urbanart2251@gmail.com>
|
|
||||||
Airead <fgh1987168@gmail.com>
|
Airead <fgh1987168@gmail.com>
|
||||||
Alan Chen <alanchchen@users.noreply.github.com>
|
Alan Chen <alanchchen@users.noreply.github.com>
|
||||||
Alejandro Isaza <alejandro.isaza@gmail.com>
|
Alejandro Isaza <alejandro.isaza@gmail.com>
|
||||||
Aleksey Smyrnov <i@soar.name>
|
|
||||||
Ales Katona <ales@coinbase.com>
|
Ales Katona <ales@coinbase.com>
|
||||||
Alex Beregszaszi <alex@rtfs.hu>
|
|
||||||
Alex Leverington <alex@ethdev.com>
|
Alex Leverington <alex@ethdev.com>
|
||||||
Alex Mazalov <mazalov@gmail.com>
|
|
||||||
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
|
|
||||||
Alex Prut <1648497+alexprut@users.noreply.github.com>
|
|
||||||
Alex Wu <wuyiding@gmail.com>
|
Alex Wu <wuyiding@gmail.com>
|
||||||
Alexander van der Meij <alexandervdm@users.noreply.github.com>
|
|
||||||
Alexander Yastrebov <yastrebov.alex@gmail.com>
|
|
||||||
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
||||||
Alexey Akhunov <akhounov@gmail.com>
|
|
||||||
Alexey Shekhirin <a.shekhirin@gmail.com>
|
|
||||||
alexwang <39109351+dipingxian2@users.noreply.github.com>
|
|
||||||
Ali Atiia <42751398+aliatiia@users.noreply.github.com>
|
|
||||||
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
||||||
am2rican5 <am2rican5@gmail.com>
|
|
||||||
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 <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>
|
Anton Evangelatov <anton.evangelatov@gmail.com>
|
||||||
Antonio Salazar Cardozo <savedfastcool@gmail.com>
|
|
||||||
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
||||||
Armani Ferrante <armaniferrante@berkeley.edu>
|
Armani Ferrante <armaniferrante@berkeley.edu>
|
||||||
Armin Braun <me@obrown.io>
|
Armin Braun <me@obrown.io>
|
||||||
Aron Fischer <github@aron.guru>
|
Aron Fischer <github@aron.guru>
|
||||||
atsushi-ishibashi <atsushi.ishibashi@finatext.com>
|
|
||||||
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>
|
Bas van Kervel <bas@ethdev.com>
|
||||||
Benjamin Brent <benjamin@benjaminbrent.com>
|
Benjamin Brent <benjamin@benjaminbrent.com>
|
||||||
benma <mbencun@gmail.com>
|
|
||||||
Benoit Verkindt <benoit.verkindt@gmail.com>
|
Benoit Verkindt <benoit.verkindt@gmail.com>
|
||||||
Binacs <bin646891055@gmail.com>
|
|
||||||
bloonfield <bloonfield@163.com>
|
|
||||||
Bo <bohende@gmail.com>
|
Bo <bohende@gmail.com>
|
||||||
Bo Ye <boy.e.computer.1982@outlook.com>
|
Bo Ye <boy.e.computer.1982@outlook.com>
|
||||||
Bob Glickstein <bobg@users.noreply.github.com>
|
Bob Glickstein <bobg@users.noreply.github.com>
|
||||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
|
||||||
Brandon Harden <b.harden92@gmail.com>
|
|
||||||
Brent <bmperrea@gmail.com>
|
|
||||||
Brian Schroeder <bts@gmail.com>
|
Brian Schroeder <bts@gmail.com>
|
||||||
Bruno Škvorc <bruno@skvorc.me>
|
|
||||||
C. Brown <hackdom@majoolr.io>
|
|
||||||
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
CDsigma <cdsigma271@gmail.com>
|
|
||||||
Ceelog <chenwei@ceelog.org>
|
|
||||||
Ceyhun Onur <ceyhun.onur@avalabs.org>
|
|
||||||
chabashilah <doumodoumo@gmail.com>
|
|
||||||
changhong <changhong.yu@shanbay.com>
|
|
||||||
Chase Wright <mysticryuujin@gmail.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>
|
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 A. Nagy <nagy.da@gmail.com>
|
||||||
Daniel Perez <daniel@perez.sh>
|
|
||||||
Daniel Sloof <goapsychadelic@gmail.com>
|
Daniel Sloof <goapsychadelic@gmail.com>
|
||||||
Darioush Jalali <darioush.jalali@avalabs.org>
|
|
||||||
Darrel Herbst <dherbst@gmail.com>
|
Darrel Herbst <dherbst@gmail.com>
|
||||||
Dave Appleton <calistralabs@gmail.com>
|
Dave Appleton <calistralabs@gmail.com>
|
||||||
Dave McGregor <dave.s.mcgregor@gmail.com>
|
|
||||||
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>
|
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 Shulyak <yashulyak@gmail.com>
|
||||||
Dmitry Zenovich <dzenovich@gmail.com>
|
|
||||||
Domino Valdano <dominoplural@gmail.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>
|
Egon Elbre <egonelbre@gmail.com>
|
||||||
Elad <theman@elad.im>
|
|
||||||
Eli <elihanover@yahoo.com>
|
|
||||||
Elias Naur <elias.naur@gmail.com>
|
Elias Naur <elias.naur@gmail.com>
|
||||||
Elliot Shepherd <elliot@identitii.com>
|
Elliot Shepherd <elliot@identitii.com>
|
||||||
Emil <mursalimovemeel@gmail.com>
|
Enrique Fynn <enriquefynn@gmail.com>
|
||||||
emile <emile@users.noreply.github.com>
|
|
||||||
Emmanuel T Odeke <odeke@ualberta.ca>
|
|
||||||
Eng Zer Jun <engzerjun@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>
|
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||||
Ethan Buchman <ethan@coinculture.info>
|
Ethan Buchman <ethan@coinculture.info>
|
||||||
ethersphere <thesw@rm.eth>
|
|
||||||
Eugene Lepeico <eugenelepeico@gmail.com>
|
|
||||||
Eugene Valeyev <evgen.povt@gmail.com>
|
Eugene Valeyev <evgen.povt@gmail.com>
|
||||||
Evangelos Pappas <epappas@evalonlabs.com>
|
Evangelos Pappas <epappas@evalonlabs.com>
|
||||||
Everton Fraga <ev@ethereum.org>
|
|
||||||
Evgeny <awesome.observer@yandex.com>
|
|
||||||
Evgeny Danilenko <6655321@bk.ru>
|
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>
|
Fabian Vogelsteller <fabian@frozeman.de>
|
||||||
Fabio Barone <fabio.barone.co@gmail.com>
|
Fabio Barone <fabio.barone.co@gmail.com>
|
||||||
Fabio Berger <fabioberger1991@gmail.com>
|
Fabio Berger <fabioberger1991@gmail.com>
|
||||||
FaceHo <facehoshi@gmail.com>
|
FaceHo <facehoshi@gmail.com>
|
||||||
Felipe Strozberg <48066928+FelStroz@users.noreply.github.com>
|
|
||||||
Felix Lange <fjl@twurst.com>
|
Felix Lange <fjl@twurst.com>
|
||||||
Ferenc Szabo <frncmx@gmail.com>
|
|
||||||
ferhat elmas <elmas.ferhat@gmail.com>
|
|
||||||
Ferran Borreguero <ferranbt@protonmail.com>
|
|
||||||
Fiisio <liangcszzu@163.com>
|
Fiisio <liangcszzu@163.com>
|
||||||
Fire Man <55934298+basdevelop@users.noreply.github.com>
|
|
||||||
flowerofdream <775654398@qq.com>
|
|
||||||
fomotrader <82184770+fomotrader@users.noreply.github.com>
|
|
||||||
ForLina <471133417@qq.com>
|
|
||||||
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
|
|
||||||
Frank Wang <eternnoir@gmail.com>
|
Frank Wang <eternnoir@gmail.com>
|
||||||
Franklin <mr_franklin@126.com>
|
|
||||||
Furkan KAMACI <furkankamaci@gmail.com>
|
Furkan KAMACI <furkankamaci@gmail.com>
|
||||||
Fuyang Deng <dengfuyang@outlook.com>
|
|
||||||
GagziW <leon.stanko@rwth-aachen.de>
|
|
||||||
Gary Rong <garyrong0905@gmail.com>
|
Gary Rong <garyrong0905@gmail.com>
|
||||||
Gautam Botrel <gautam.botrel@gmail.com>
|
|
||||||
George Ornbo <george@shapeshed.com>
|
George Ornbo <george@shapeshed.com>
|
||||||
Giuseppe Bertone <bertone.giuseppe@gmail.com>
|
|
||||||
Greg Colvin <greg@colvin.org>
|
|
||||||
Gregg Dourgarian <greggd@tempworks.com>
|
Gregg Dourgarian <greggd@tempworks.com>
|
||||||
Gregory Markou <16929357+GregTheGreek@users.noreply.github.com>
|
|
||||||
Guifel <toowik@gmail.com>
|
|
||||||
Guilherme Salgado <gsalgado@gmail.com>
|
|
||||||
Guillaume Ballet <gballet@gmail.com>
|
Guillaume Ballet <gballet@gmail.com>
|
||||||
Guillaume Nicolas <guin56@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>
|
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 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>
|
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>
|
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
|
||||||
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
|
||||||
Ivan Bogatyy <bogatyi@gmail.com>
|
|
||||||
Ivan Daniluk <ivan.daniluk@gmail.com>
|
Ivan Daniluk <ivan.daniluk@gmail.com>
|
||||||
Ivo Georgiev <ivo@strem.io>
|
|
||||||
jacksoom <lifengliu1994@gmail.com>
|
|
||||||
Jae Kwon <jkwon.work@gmail.com>
|
Jae Kwon <jkwon.work@gmail.com>
|
||||||
James Prestwich <10149425+prestwich@users.noreply.github.com>
|
|
||||||
Jamie Pitts <james.pitts@gmail.com>
|
Jamie Pitts <james.pitts@gmail.com>
|
||||||
Janoš Guljaš <janos@resenje.org>
|
Janoš Guljaš <janos@users.noreply.github.com>
|
||||||
Jared Wasinger <j-wasinger@hotmail.com>
|
|
||||||
Jason Carver <jacarver@linkedin.com>
|
Jason Carver <jacarver@linkedin.com>
|
||||||
Javier Peletier <jm@epiclabs.io>
|
|
||||||
Javier Sagredo <jasataco@gmail.com>
|
|
||||||
Jay <codeholic.arena@gmail.com>
|
|
||||||
Jay Guo <guojiannan1101@gmail.com>
|
Jay Guo <guojiannan1101@gmail.com>
|
||||||
Jaynti Kanani <jdkanani@gmail.com>
|
|
||||||
Jeff Prestes <jeffprestes@gmail.com>
|
|
||||||
Jeff R. Allen <jra@nella.org>
|
Jeff R. Allen <jra@nella.org>
|
||||||
Jeff Wentworth <jeff@curvegrid.com>
|
|
||||||
Jeffery Robert Walsh <rlxrlps@gmail.com>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||||
Jens Agerberg <github@agerberg.me>
|
Jens Agerberg <github@agerberg.me>
|
||||||
Jeremy McNevin <jeremy.mcnevin@optum.com>
|
|
||||||
Jeremy Schlatter <jeremy.schlatter@gmail.com>
|
|
||||||
Jerzy Lasyk <jerzylasyk@gmail.com>
|
|
||||||
Jesse Tane <jesse.tane@gmail.com>
|
|
||||||
Jia Chenhui <jiachenhui1989@gmail.com>
|
Jia Chenhui <jiachenhui1989@gmail.com>
|
||||||
Jim McDonald <Jim@mcdee.net>
|
Jim McDonald <Jim@mcdee.net>
|
||||||
jk-jeongkyun <45347815+jeongkyun-oh@users.noreply.github.com>
|
|
||||||
jkcomment <jkcomment@gmail.com>
|
|
||||||
JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
|
|
||||||
Joel Burget <joelburget@gmail.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 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>
|
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 Clark-Casey <justincc@justincc.org>
|
||||||
Justin Drake <drakefjustin@gmail.com>
|
Justin Drake <drakefjustin@gmail.com>
|
||||||
Justus <jus@gtsbr.org>
|
|
||||||
Kawashima <91420903+sscodereth@users.noreply.github.com>
|
|
||||||
ken10100147 <sunhongping@kanjian.com>
|
|
||||||
Kenji Siu <kenji@isuntv.com>
|
Kenji Siu <kenji@isuntv.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>
|
Kobi Gurkan <kobigurk@gmail.com>
|
||||||
komika <komika@komika.org>
|
|
||||||
Konrad Feldmeier <konrad@brainbot.com>
|
Konrad Feldmeier <konrad@brainbot.com>
|
||||||
Kris Shinn <raggamuffin.music@gmail.com>
|
|
||||||
Kristofer Peterson <svenski123@users.noreply.github.com>
|
|
||||||
Kumar Anirudha <mail@anirudha.dev>
|
|
||||||
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
||||||
Kushagra Sharma <ksharm01@gmail.com>
|
|
||||||
Kwuaint <34888408+kwuaint@users.noreply.github.com>
|
|
||||||
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
|
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
|
||||||
Lee Bousfield <ljbousfield@gmail.com>
|
|
||||||
Lefteris Karapetsas <lefteris@refu.co>
|
Lefteris Karapetsas <lefteris@refu.co>
|
||||||
Leif Jurvetson <leijurv@gmail.com>
|
Leif Jurvetson <leijurv@gmail.com>
|
||||||
Leo Shklovskii <leo@thermopylae.net>
|
Leo Shklovskii <leo@thermopylae.net>
|
||||||
LeoLiao <leofantast@gmail.com>
|
|
||||||
Lewis Marshall <lewis@lmars.net>
|
Lewis Marshall <lewis@lmars.net>
|
||||||
lhendre <lhendre2@gmail.com>
|
|
||||||
Li Dongwei <lidw1988@126.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>
|
Lio李欧 <lionello@users.noreply.github.com>
|
||||||
lmittmann <lmittmann@users.noreply.github.com>
|
|
||||||
Lorenzo Manacorda <lorenzo@kinvolk.io>
|
|
||||||
Louis Holbrook <dev@holbrook.no>
|
Louis Holbrook <dev@holbrook.no>
|
||||||
Luca Zeug <luclu@users.noreply.github.com>
|
Luca Zeug <luclu@users.noreply.github.com>
|
||||||
Lucas Hendren <lhendre2@gmail.com>
|
|
||||||
lzhfromustc <43191155+lzhfromustc@users.noreply.github.com>
|
|
||||||
Magicking <s@6120.eu>
|
Magicking <s@6120.eu>
|
||||||
manlio <manlio.poltronieri@gmail.com>
|
|
||||||
Maran Hidskes <maran.hidskes@gmail.com>
|
Maran Hidskes <maran.hidskes@gmail.com>
|
||||||
Marek Kotewicz <marek.kotewicz@gmail.com>
|
Marek Kotewicz <marek.kotewicz@gmail.com>
|
||||||
Mariano Cortesi <mcortesi@gmail.com>
|
|
||||||
Marius van der Wijden <m.vanderwijden@live.de>
|
|
||||||
Mark <markya0616@gmail.com>
|
Mark <markya0616@gmail.com>
|
||||||
Mark Rushakoff <mark.rushakoff@gmail.com>
|
|
||||||
mark.lin <mark@maicoin.com>
|
|
||||||
Martin Alex Philip Dawson <u1356770@gmail.com>
|
|
||||||
Martin Holst Swende <martin@swende.se>
|
Martin Holst Swende <martin@swende.se>
|
||||||
Martin Klepsch <martinklepsch@googlemail.com>
|
|
||||||
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 Di Ferrante <mattdf@users.noreply.github.com>
|
||||||
Matthew Halpern <matthalp@gmail.com>
|
|
||||||
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
|
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
|
||||||
Max Sistemich <mafrasi2@googlemail.com>
|
|
||||||
Maxim Zhiburt <zhiburt@gmail.com>
|
|
||||||
Maximilian Meister <mmeister@suse.de>
|
Maximilian Meister <mmeister@suse.de>
|
||||||
me020523 <me020523@gmail.com>
|
|
||||||
Melvin Junhee Woo <melvin.woo@groundx.xyz>
|
|
||||||
meowsbits <b5c6@protonmail.com>
|
|
||||||
Micah Zoltu <micah@zoltu.net>
|
Micah Zoltu <micah@zoltu.net>
|
||||||
Michael Forney <mforney@mforney.org>
|
|
||||||
Michael Riabzev <michael@starkware.co>
|
|
||||||
Michael Ruminer <michael.ruminer+github@gmail.com>
|
Michael Ruminer <michael.ruminer+github@gmail.com>
|
||||||
michael1011 <me@michael1011.at>
|
|
||||||
Miguel Mota <miguelmota2@gmail.com>
|
Miguel Mota <miguelmota2@gmail.com>
|
||||||
Mike Burr <mburr@nightmare.com>
|
|
||||||
Mikhail Mikheev <mmvsha73@gmail.com>
|
|
||||||
milesvant <milesvant@gmail.com>
|
|
||||||
Miro <mirokuratczyk@users.noreply.github.com>
|
|
||||||
Miya Chen <miyatlchen@gmail.com>
|
Miya Chen <miyatlchen@gmail.com>
|
||||||
Mohanson <mohanson@outlook.com>
|
|
||||||
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>
|
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 Dodson <silentcicero@outlook.com>
|
||||||
Nick Johnson <arachnid@notdot.net>
|
Nick Johnson <arachnid@notdot.net>
|
||||||
Nicolas Feignon <nfeignon@gmail.com>
|
|
||||||
Nicolas Guillaume <gunicolas@sqli.com>
|
Nicolas Guillaume <gunicolas@sqli.com>
|
||||||
Nikita Kozhemyakin <enginegl.ec@gmail.com>
|
|
||||||
Nikola Madjarevic <nikola.madjarevic@gmail.com>
|
|
||||||
Nilesh Trivedi <nilesh@hypertrack.io>
|
|
||||||
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>
|
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>
|
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 Litvak <litvakpol@012.net.il>
|
||||||
Paul-Armand Verhaegen <paularmand.verhaegen@gmail.com>
|
|
||||||
Paulo L F Casaretto <pcasaretto@gmail.com>
|
Paulo L F Casaretto <pcasaretto@gmail.com>
|
||||||
Paweł Bylica <chfast@gmail.com>
|
Paweł Bylica <chfast@gmail.com>
|
||||||
Pedro Gomes <otherview@gmail.com>
|
|
||||||
Pedro Pombeiro <PombeirP@users.noreply.github.com>
|
|
||||||
Peter Broadhurst <peter@themumbles.net>
|
|
||||||
peter cresswell <pcresswell@gmail.com>
|
|
||||||
Peter Pratscher <pratscher@gmail.com>
|
Peter Pratscher <pratscher@gmail.com>
|
||||||
Peter Simard <petesimard56@gmail.com>
|
|
||||||
Petr Mikusek <petr@mikusek.info>
|
Petr Mikusek <petr@mikusek.info>
|
||||||
Philip Schlump <pschlump@gmail.com>
|
|
||||||
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>
|
|
||||||
Péter Szilágyi <peterke@gmail.com>
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
qd-ethan <31876119+qdgogogo@users.noreply.github.com>
|
RJ Catalano <catalanor0220@gmail.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>
|
|
||||||
Ramesh Nair <ram@hiddentao.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 Catalinas Jiménez <r@untroubled.be>
|
||||||
Ricardo Domingos <ricardohsd@gmail.com>
|
Ricardo Domingos <ricardohsd@gmail.com>
|
||||||
Richard Hart <richardhart92@gmail.com>
|
Richard Hart <richardhart92@gmail.com>
|
||||||
Rick <rick.no@groundx.xyz>
|
|
||||||
RJ Catalano <catalanor0220@gmail.com>
|
|
||||||
Rob <robert@rojotek.com>
|
Rob <robert@rojotek.com>
|
||||||
Rob Mulholand <rmulholand@8thlight.com>
|
Robert Zaremba <robert.zaremba@scale-it.pl>
|
||||||
Robert Zaremba <robert@zaremba.ch>
|
|
||||||
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>
|
Russ Cox <rsc@golang.org>
|
||||||
Ryan Schneider <ryanleeschneider@gmail.com>
|
|
||||||
ryanc414 <ryan@tokencard.io>
|
|
||||||
Rémy Roy <remyroy@remyroy.com>
|
Rémy Roy <remyroy@remyroy.com>
|
||||||
S. Matthew English <s-matthew-english@users.noreply.github.com>
|
S. Matthew English <s-matthew-english@users.noreply.github.com>
|
||||||
salanfe <salanfe@users.noreply.github.com>
|
|
||||||
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>
|
|
||||||
Shintaro Kaneko <kaneshin0120@gmail.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>
|
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||||
Sparty <vignesh.crysis@gmail.com>
|
|
||||||
Stein Dekker <dekker.stein@gmail.com>
|
Stein Dekker <dekker.stein@gmail.com>
|
||||||
Steve Gattuso <steve@stevegattuso.me>
|
|
||||||
Steve Ruckdashel <steve.ruckdashel@gmail.com>
|
|
||||||
Steve Waldman <swaldman@mchange.com>
|
Steve Waldman <swaldman@mchange.com>
|
||||||
Steven E. Harris <seh@panix.com>
|
|
||||||
Steven Roose <stevenroose@gmail.com>
|
Steven Roose <stevenroose@gmail.com>
|
||||||
stompesi <stompesi@gmail.com>
|
|
||||||
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>
|
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>
|
Thomas Bocek <tom@tomp2p.net>
|
||||||
thomasmodeneis <thomas.modeneis@gmail.com>
|
|
||||||
thumb8432 <thumb8432@gmail.com>
|
|
||||||
Ti Zhou <tizhou1986@gmail.com>
|
Ti Zhou <tizhou1986@gmail.com>
|
||||||
tia-99 <67107070+tia-99@users.noreply.github.com>
|
|
||||||
Tim Cooijmans <timcooijmans@gmail.com>
|
|
||||||
Tobias Hildebrandt <79341166+tobias-hildebrandt@users.noreply.github.com>
|
|
||||||
Tosh Camille <tochecamille@gmail.com>
|
Tosh Camille <tochecamille@gmail.com>
|
||||||
tsarpaul <Litvakpol@012.net.il>
|
|
||||||
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>
|
Valentin Wüstholz <wuestholz@gmail.com>
|
||||||
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
|
|
||||||
Victor Farazdagi <simple.square@gmail.com>
|
Victor Farazdagi <simple.square@gmail.com>
|
||||||
Victor Tran <vu.tran54@gmail.com>
|
Victor Tran <vu.tran54@gmail.com>
|
||||||
Vie <yangchenzhong@gmail.com>
|
|
||||||
Viktor Trón <viktor.tron@gmail.com>
|
Viktor Trón <viktor.tron@gmail.com>
|
||||||
Ville Sundell <github@solarius.fi>
|
Ville Sundell <github@solarius.fi>
|
||||||
vim88 <vim88vim88@gmail.com>
|
|
||||||
Vincent G <caktux@gmail.com>
|
Vincent G <caktux@gmail.com>
|
||||||
Vincent Serpoul <vincent@serpoul.com>
|
|
||||||
Vinod Damle <vdamle@users.noreply.github.com>
|
|
||||||
Vitalik Buterin <v@buterin.com>
|
Vitalik Buterin <v@buterin.com>
|
||||||
Vitaly Bogdanov <vsbogd@gmail.com>
|
|
||||||
Vitaly V <vvelikodny@gmail.com>
|
Vitaly V <vvelikodny@gmail.com>
|
||||||
Vivek Anand <vivekanand1101@users.noreply.github.com>
|
Vivek Anand <vivekanand1101@users.noreply.github.com>
|
||||||
Vlad Bokov <razum2um@mail.ru>
|
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
||||||
Vlad Gluhovsky <gluk256@gmail.com>
|
|
||||||
Ward Bradt <wardbradt5@gmail.com>
|
|
||||||
Water <44689567+codeoneline@users.noreply.github.com>
|
|
||||||
wbt <wbt@users.noreply.github.com>
|
|
||||||
weimumu <934657014@qq.com>
|
|
||||||
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>
|
Yohann Léon <sybiload@gmail.com>
|
||||||
Yoichi Hirai <i@yoichihirai.com>
|
Yoichi Hirai <i@yoichihirai.com>
|
||||||
Yole <007yuyue@gmail.com>
|
|
||||||
Yondon Fu <yondon.fu@gmail.com>
|
Yondon Fu <yondon.fu@gmail.com>
|
||||||
YOSHIDA Masanori <masanori.yoshida@gmail.com>
|
|
||||||
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>
|
Zach <zach.ramsay@gmail.com>
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
zah <zahary@gmail.com>
|
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
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>
|
Zoe Nolan <github@zoenolan.org>
|
||||||
Zou Guangxian <zouguangxian@gmail.com>
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
Łukasz Kurowski <crackcomm@users.noreply.github.com>
|
am2rican5 <am2rican5@gmail.com>
|
||||||
Łukasz Zimnoch <lukaszzimnoch1994@gmail.com>
|
ayeowch <ayeowch@gmail.com>
|
||||||
|
b00ris <b00ris@mail.ru>
|
||||||
|
bailantaotao <Edwin@maicoin.com>
|
||||||
|
baizhenxuan <nkbai@163.com>
|
||||||
|
bloonfield <bloonfield@163.com>
|
||||||
|
changhong <changhong.yu@shanbay.com>
|
||||||
|
evgk <evgeniy.kamyshev@gmail.com>
|
||||||
|
ferhat elmas <elmas.ferhat@gmail.com>
|
||||||
|
holisticode <holistic.computing@gmail.com>
|
||||||
|
jtakalai <juuso.takalainen@streamr.com>
|
||||||
|
ken10100147 <sunhongping@kanjian.com>
|
||||||
|
ligi <ligi@ligi.de>
|
||||||
|
mark.lin <mark@maicoin.com>
|
||||||
|
necaremus <necaremus@gmail.com>
|
||||||
|
njupt-moon <1015041018@njupt.edu.cn>
|
||||||
|
nkbai <nkbai@163.com>
|
||||||
|
rhaps107 <dod-source@yandex.ru>
|
||||||
|
slumber1122 <slumber1122@gmail.com>
|
||||||
|
sunxiaojun2014 <sunxiaojun-xy@360.cn>
|
||||||
|
terasum <terasum@163.com>
|
||||||
|
tsarpaul <Litvakpol@012.net.il>
|
||||||
|
xiekeyang <xiekeyang@users.noreply.github.com>
|
||||||
|
yoza <yoza.is12s@gmail.com>
|
||||||
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
||||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||||
大彬 <hz_stb@163.com>
|
Ralph Caraveo <deckarep@gmail.com>
|
||||||
沉风 <myself659@users.noreply.github.com>
|
|
||||||
贺鹏飞 <hpf@hackerful.cn>
|
|
||||||
陈佳 <chenjiablog@gmail.com>
|
|
||||||
유용환 <33824408+eric-yoo@users.noreply.github.com>
|
|
||||||
|
|||||||
57
COPYING
57
COPYING
@@ -1,7 +1,7 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2014 The go-ethereum Authors.
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
@@ -617,58 +617,3 @@ reviewing courts shall apply local law that most closely approximates
|
|||||||
an absolute waiver of all civil liability in connection with the
|
an absolute waiver of all civil liability in connection with the
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
copy of the Program in return for a fee.
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
|
||||||
notice like this when it starts in an interactive mode:
|
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
||||||
|
|||||||
23
Dockerfile
23
Dockerfile
@@ -1,20 +1,10 @@
|
|||||||
# Support setting various labels on the final image
|
|
||||||
ARG COMMIT=""
|
|
||||||
ARG VERSION=""
|
|
||||||
ARG BUILDNUM=""
|
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.20-alpine as builder
|
FROM golang:1.11-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers
|
||||||
|
|
||||||
# Get dependencies - will also be cached if we won't change go.mod/go.sum
|
|
||||||
COPY go.mod /go-ethereum/
|
|
||||||
COPY go.sum /go-ethereum/
|
|
||||||
RUN cd /go-ethereum && go mod download
|
|
||||||
|
|
||||||
ADD . /go-ethereum
|
ADD . /go-ethereum
|
||||||
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
|
RUN cd /go-ethereum && make geth
|
||||||
|
|
||||||
# Pull Geth into a second stage deploy alpine container
|
# Pull Geth into a second stage deploy alpine container
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
@@ -24,10 +14,3 @@ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
|
|||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 30303 30303/udp
|
||||||
ENTRYPOINT ["geth"]
|
ENTRYPOINT ["geth"]
|
||||||
|
|
||||||
# Add some metadata labels to help programatic image consumption
|
|
||||||
ARG COMMIT=""
|
|
||||||
ARG VERSION=""
|
|
||||||
ARG BUILDNUM=""
|
|
||||||
|
|
||||||
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
|
||||||
|
|||||||
@@ -1,20 +1,10 @@
|
|||||||
# Support setting various labels on the final image
|
|
||||||
ARG COMMIT=""
|
|
||||||
ARG VERSION=""
|
|
||||||
ARG BUILDNUM=""
|
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.20-alpine as builder
|
FROM golang:1.11-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers
|
||||||
|
|
||||||
# Get dependencies - will also be cached if we won't change go.mod/go.sum
|
|
||||||
COPY go.mod /go-ethereum/
|
|
||||||
COPY go.sum /go-ethereum/
|
|
||||||
RUN cd /go-ethereum && go mod download
|
|
||||||
|
|
||||||
ADD . /go-ethereum
|
ADD . /go-ethereum
|
||||||
RUN cd /go-ethereum && go run build/ci.go install -static
|
RUN cd /go-ethereum && make all
|
||||||
|
|
||||||
# Pull all binaries into a second stage deploy alpine container
|
# Pull all binaries into a second stage deploy alpine container
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
@@ -23,10 +13,3 @@ RUN apk add --no-cache ca-certificates
|
|||||||
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 30303 30303/udp
|
||||||
|
|
||||||
# Add some metadata labels to help programatic image consumption
|
|
||||||
ARG COMMIT=""
|
|
||||||
ARG VERSION=""
|
|
||||||
ARG BUILDNUM=""
|
|
||||||
|
|
||||||
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
|
||||||
|
|||||||
137
Makefile
137
Makefile
@@ -2,37 +2,152 @@
|
|||||||
# with Go source code. If you know what GOPATH is then you probably
|
# with Go source code. If you know what GOPATH is then you probably
|
||||||
# don't need to bother with make.
|
# don't need to bother with make.
|
||||||
|
|
||||||
.PHONY: geth android ios evm all test clean
|
.PHONY: geth android ios geth-cross swarm evm all test clean
|
||||||
|
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
|
||||||
|
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
|
||||||
|
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
|
||||||
|
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
|
||||||
|
|
||||||
GOBIN = ./build/bin
|
GOBIN = $(shell pwd)/build/bin
|
||||||
GO ?= latest
|
GO ?= latest
|
||||||
GORUN = env GO111MODULE=on go run
|
|
||||||
|
|
||||||
geth:
|
geth:
|
||||||
$(GORUN) build/ci.go install ./cmd/geth
|
build/env.sh go run build/ci.go install ./cmd/geth
|
||||||
@echo "Done building."
|
@echo "Done building."
|
||||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||||
|
|
||||||
|
swarm:
|
||||||
|
build/env.sh go run build/ci.go install ./cmd/swarm
|
||||||
|
@echo "Done building."
|
||||||
|
@echo "Run \"$(GOBIN)/swarm\" to launch swarm."
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(GORUN) build/ci.go install
|
build/env.sh go run build/ci.go install
|
||||||
|
|
||||||
|
android:
|
||||||
|
build/env.sh go run build/ci.go aar --local
|
||||||
|
@echo "Done building."
|
||||||
|
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
|
||||||
|
|
||||||
|
ios:
|
||||||
|
build/env.sh go run build/ci.go xcode --local
|
||||||
|
@echo "Done building."
|
||||||
|
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
|
||||||
|
|
||||||
test: all
|
test: all
|
||||||
$(GORUN) build/ci.go test
|
build/env.sh go run build/ci.go test
|
||||||
|
|
||||||
lint: ## Run linters.
|
lint: ## Run linters.
|
||||||
$(GORUN) build/ci.go lint
|
build/env.sh go run build/ci.go lint
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
env GO111MODULE=on go clean -cache
|
./build/clean_go_build_cache.sh
|
||||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||||
|
|
||||||
# The devtools target installs tools required for 'go generate'.
|
# The devtools target installs tools required for 'go generate'.
|
||||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||||
|
|
||||||
devtools:
|
devtools:
|
||||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
env GOBIN= go get -u golang.org/x/tools/cmd/stringer
|
||||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
env GOBIN= go get -u github.com/kevinburke/go-bindata/go-bindata
|
||||||
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
|
env GOBIN= go get -u github.com/fjl/gencodec
|
||||||
|
env GOBIN= go get -u github.com/golang/protobuf/protoc-gen-go
|
||||||
env GOBIN= go install ./cmd/abigen
|
env GOBIN= go install ./cmd/abigen
|
||||||
|
@type "npm" 2> /dev/null || echo 'Please install node.js and npm'
|
||||||
@type "solc" 2> /dev/null || echo 'Please install solc'
|
@type "solc" 2> /dev/null || echo 'Please install solc'
|
||||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
||||||
|
|
||||||
|
swarm-devtools:
|
||||||
|
env GOBIN= go install ./cmd/swarm/mimegen
|
||||||
|
|
||||||
|
# Cross Compilation Targets (xgo)
|
||||||
|
|
||||||
|
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
|
||||||
|
@echo "Full cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-*
|
||||||
|
|
||||||
|
geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 geth-linux-mips64le
|
||||||
|
@echo "Linux cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-*
|
||||||
|
|
||||||
|
geth-linux-386:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
|
||||||
|
@echo "Linux 386 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep 386
|
||||||
|
|
||||||
|
geth-linux-amd64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
|
||||||
|
@echo "Linux amd64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
|
||||||
|
|
||||||
|
geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
|
||||||
|
@echo "Linux ARM cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm
|
||||||
|
|
||||||
|
geth-linux-arm-5:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
|
||||||
|
@echo "Linux ARMv5 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
|
||||||
|
|
||||||
|
geth-linux-arm-6:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
|
||||||
|
@echo "Linux ARMv6 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
|
||||||
|
|
||||||
|
geth-linux-arm-7:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
|
||||||
|
@echo "Linux ARMv7 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
|
||||||
|
|
||||||
|
geth-linux-arm64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
|
||||||
|
@echo "Linux ARM64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
|
||||||
|
|
||||||
|
geth-linux-mips:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPS cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips
|
||||||
|
|
||||||
|
geth-linux-mipsle:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPSle cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mipsle
|
||||||
|
|
||||||
|
geth-linux-mips64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPS64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
|
||||||
|
|
||||||
|
geth-linux-mips64le:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPS64le cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
|
||||||
|
|
||||||
|
geth-darwin: geth-darwin-386 geth-darwin-amd64
|
||||||
|
@echo "Darwin cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-darwin-*
|
||||||
|
|
||||||
|
geth-darwin-386:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
|
||||||
|
@echo "Darwin 386 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
|
||||||
|
|
||||||
|
geth-darwin-amd64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
|
||||||
|
@echo "Darwin amd64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
|
||||||
|
|
||||||
|
geth-windows: geth-windows-386 geth-windows-amd64
|
||||||
|
@echo "Windows cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-windows-*
|
||||||
|
|
||||||
|
geth-windows-386:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
|
||||||
|
@echo "Windows 386 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-windows-* | grep 386
|
||||||
|
|
||||||
|
geth-windows-amd64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
|
||||||
|
@echo "Windows amd64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
||||||
|
|||||||
390
README.md
390
README.md
@@ -1,367 +1,311 @@
|
|||||||
## Go Ethereum
|
## Go Ethereum
|
||||||
|
|
||||||
Official Golang execution layer implementation of the Ethereum protocol.
|
Official golang implementation of the Ethereum protocol.
|
||||||
|
|
||||||
[](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc)
|
)](https://godoc.org/github.com/ethereum/go-ethereum)
|
||||||
[](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
[](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
||||||
[](https://travis-ci.com/ethereum/go-ethereum)
|
[](https://travis-ci.org/ethereum/go-ethereum)
|
||||||
[](https://discord.gg/nthXNEv)
|
[](https://discord.gg/nthXNEv)
|
||||||
|
|
||||||
Automated builds are available for stable releases and the unstable master branch. Binary
|
Automated builds are available for stable releases and the unstable master branch.
|
||||||
archives are published at https://geth.ethereum.org/downloads/.
|
Binary archives are published at https://geth.ethereum.org/downloads/.
|
||||||
|
|
||||||
## Building the source
|
## Building the source
|
||||||
|
|
||||||
For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth).
|
For prerequisites and detailed build instructions please read the
|
||||||
|
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
||||||
|
on the wiki.
|
||||||
|
|
||||||
Building `geth` requires both a Go (version 1.18 or later) and a C compiler. You can install
|
Building geth requires both a Go (version 1.9 or later) and a C compiler.
|
||||||
them using your favourite package manager. Once the dependencies are installed, run
|
You can install them using your favourite package manager.
|
||||||
|
Once the dependencies are installed, run
|
||||||
|
|
||||||
```shell
|
make geth
|
||||||
make geth
|
|
||||||
```
|
|
||||||
|
|
||||||
or, to build the full suite of utilities:
|
or, to build the full suite of utilities:
|
||||||
|
|
||||||
```shell
|
make all
|
||||||
make all
|
|
||||||
```
|
|
||||||
|
|
||||||
## Executables
|
## Executables
|
||||||
|
|
||||||
The go-ethereum project comes with several wrappers/executables found in the `cmd`
|
The go-ethereum project comes with several wrappers/executables found in the `cmd` directory.
|
||||||
directory.
|
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| :--------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|:----------:|-------------|
|
||||||
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/interface/command-line-options) for command line options. |
|
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. |
|
||||||
| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. |
|
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
|
||||||
| `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. |
|
|
||||||
| `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details. |
|
|
||||||
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
|
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
|
||||||
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). |
|
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
|
||||||
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
|
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
|
||||||
|
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
|
||||||
|
| `swarm` | Swarm daemon and tools. This is the entrypoint for the Swarm network. `swarm --help` for command line options and subcommands. See [Swarm README](https://github.com/ethereum/go-ethereum/tree/master/swarm) for more information. |
|
||||||
|
| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |
|
||||||
|
|
||||||
## Running `geth`
|
## Running geth
|
||||||
|
|
||||||
Going through all the possible command line flags is out of scope here (please consult our
|
Going through all the possible command line flags is out of scope here (please consult our
|
||||||
[CLI Wiki page](https://geth.ethereum.org/docs/interface/command-line-options)),
|
[CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)), but we've
|
||||||
but we've enumerated a few common parameter combos to get you up to speed quickly
|
enumerated a few common parameter combos to get you up to speed quickly on how you can run your
|
||||||
on how you can run your own `geth` instance.
|
own Geth instance.
|
||||||
|
|
||||||
### Hardware Requirements
|
|
||||||
|
|
||||||
Minimum:
|
|
||||||
|
|
||||||
* CPU with 2+ cores
|
|
||||||
* 4GB RAM
|
|
||||||
* 1TB free storage space to sync the Mainnet
|
|
||||||
* 8 MBit/sec download Internet service
|
|
||||||
|
|
||||||
Recommended:
|
|
||||||
|
|
||||||
* Fast CPU with 4+ cores
|
|
||||||
* 16GB+ RAM
|
|
||||||
* High-performance SSD with at least 1TB of free space
|
|
||||||
* 25+ MBit/sec download Internet service
|
|
||||||
|
|
||||||
### Full node on the main Ethereum network
|
### Full node on the main Ethereum network
|
||||||
|
|
||||||
By far the most common scenario is people wanting to simply interact with the Ethereum
|
By far the most common scenario is people wanting to simply interact with the Ethereum network:
|
||||||
network: create accounts; transfer funds; deploy and interact with contracts. For this
|
create accounts; transfer funds; deploy and interact with contracts. For this particular use-case
|
||||||
particular use case, the user doesn't care about years-old historical data, so we can
|
the user doesn't care about years-old historical data, so we can fast-sync quickly to the current
|
||||||
sync quickly to the current state of the network. To do so:
|
state of the network. To do so:
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth console
|
$ geth console
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will:
|
This command will:
|
||||||
* Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag),
|
|
||||||
causing it to download more data in exchange for avoiding processing the entire history
|
|
||||||
of the Ethereum network, which is very CPU intensive.
|
|
||||||
* Start the built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
|
|
||||||
(via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://github.com/ChainSafe/web3.js/blob/0.20.7/DOCUMENTATION.md)
|
|
||||||
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs),
|
|
||||||
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server).
|
|
||||||
This tool is optional and if you leave it out you can always attach it to an already running
|
|
||||||
`geth` instance with `geth attach`.
|
|
||||||
|
|
||||||
### A Full node on the Görli test network
|
* Start geth in fast sync mode (default, can be changed with the `--syncmode` flag), causing it to
|
||||||
|
download more data in exchange for avoiding processing the entire history of the Ethereum network,
|
||||||
|
which is very CPU intensive.
|
||||||
|
* Start up Geth's built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console),
|
||||||
|
(via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API)
|
||||||
|
as well as Geth's own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs).
|
||||||
|
This tool is optional and if you leave it out you can always attach to an already running Geth instance
|
||||||
|
with `geth attach`.
|
||||||
|
|
||||||
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
### Full node on the Ethereum test network
|
||||||
contracts, you almost certainly would like to do that without any real money involved until
|
|
||||||
you get the hang of the entire system. In other words, instead of attaching to the main
|
|
||||||
network, you want to join the **test** network with your node, which is fully equivalent to
|
|
||||||
the main network, but with play-Ether only.
|
|
||||||
|
|
||||||
```shell
|
Transitioning towards developers, if you'd like to play around with creating Ethereum contracts, you
|
||||||
$ geth --goerli console
|
almost certainly would like to do that without any real money involved until you get the hang of the
|
||||||
|
entire system. In other words, instead of attaching to the main network, you want to join the **test**
|
||||||
|
network with your node, which is fully equivalent to the main network, but with play-Ether only.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth --testnet console
|
||||||
```
|
```
|
||||||
|
|
||||||
The `console` subcommand has the same meaning as above and is equally
|
The `console` subcommand have the exact same meaning as above and they are equally useful on the
|
||||||
useful on the testnet too.
|
testnet too. Please see above for their explanations if you've skipped to here.
|
||||||
|
|
||||||
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
|
Specifying the `--testnet` flag however will reconfigure your Geth instance a bit:
|
||||||
|
|
||||||
* Instead of connecting to the main Ethereum network, the client will connect to the Görli
|
* Instead of using the default data directory (`~/.ethereum` on Linux for example), Geth will nest
|
||||||
test network, which uses different P2P bootnodes, different network IDs and genesis
|
itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on Linux). Note, on OSX
|
||||||
states.
|
and Linux this also means that attaching to a running testnet node requires the use of a custom
|
||||||
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
endpoint since `geth attach` will try to attach to a production node endpoint by default. E.g.
|
||||||
will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on
|
`geth attach <datadir>/testnet/geth.ipc`. Windows users are not affected by this.
|
||||||
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
|
* Instead of connecting the main Ethereum network, the client will connect to the test network,
|
||||||
requires the use of a custom endpoint since `geth attach` will try to attach to a
|
which uses different P2P bootnodes, different network IDs and genesis states.
|
||||||
production node endpoint by default, e.g.,
|
|
||||||
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
|
|
||||||
this.
|
|
||||||
|
|
||||||
*Note: Although some internal protective measures prevent transactions from
|
*Note: Although there are some internal protective measures to prevent transactions from crossing
|
||||||
crossing over between the main network and test network, you should always
|
over between the main network and test network, you should make sure to always use separate accounts
|
||||||
use separate accounts for play and real money. Unless you manually move
|
for play-money and real-money. Unless you manually move accounts, Geth will by default correctly
|
||||||
accounts, `geth` will by default correctly separate the two networks and will not make any
|
separate the two networks and will not make any accounts available between them.*
|
||||||
accounts available between them.*
|
|
||||||
|
|
||||||
### Full node on the Rinkeby test network
|
### Full node on the Rinkeby test network
|
||||||
|
|
||||||
Go Ethereum also supports connecting to the older proof-of-authority based test network
|
The above test network is a cross client one based on the ethash proof-of-work consensus algorithm. As such, it has certain extra overhead and is more susceptible to reorganization attacks due to the network's low difficulty / security. Go Ethereum also supports connecting to a proof-of-authority based test network called [*Rinkeby*](https://www.rinkeby.io) (operated by members of the community). This network is lighter, more secure, but is only supported by go-ethereum.
|
||||||
called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community.
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth --rinkeby console
|
$ geth --rinkeby console
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a
|
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a configuration file via:
|
||||||
configuration file via:
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth --config /path/to/your_config.toml
|
$ geth --config /path/to/your_config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
To get an idea of how the file should look like you can use the `dumpconfig` subcommand to
|
To get an idea how the file should look like you can use the `dumpconfig` subcommand to export your existing configuration:
|
||||||
export your existing configuration:
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth --your-favourite-flags dumpconfig
|
$ geth --your-favourite-flags dumpconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
*Note: This works only with `geth` v1.6.0 and above.*
|
*Note: This works only with geth v1.6.0 and above.*
|
||||||
|
|
||||||
#### Docker quick start
|
#### Docker quick start
|
||||||
|
|
||||||
One of the quickest ways to get Ethereum up and running on your machine is by using
|
One of the quickest ways to get Ethereum up and running on your machine is by using Docker:
|
||||||
Docker:
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
|
docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
|
||||||
-p 8545:8545 -p 30303:30303 \
|
-p 8545:8545 -p 30303:30303 \
|
||||||
ethereum/client-go
|
ethereum/client-go
|
||||||
```
|
```
|
||||||
|
|
||||||
This will start `geth` in snap-sync mode with a DB memory allowance of 1GB, as the
|
This will start geth in fast-sync mode with a DB memory allowance of 1GB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image.
|
||||||
above command does. It will also create a persistent volume in your home directory for
|
|
||||||
saving your blockchain as well as map the default ports. There is also an `alpine` tag
|
|
||||||
available for a slim version of the image.
|
|
||||||
|
|
||||||
Do not forget `--http.addr 0.0.0.0`, if you want to access RPC from other containers
|
Do not forget `--rpcaddr 0.0.0.0`, if you want to access RPC from other containers and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not accessible from the outside.
|
||||||
and/or hosts. By default, `geth` binds to the local interface and RPC endpoints are not
|
|
||||||
accessible from the outside.
|
|
||||||
|
|
||||||
### Programmatically interfacing `geth` nodes
|
### Programatically interfacing Geth nodes
|
||||||
|
|
||||||
As a developer, sooner rather than later you'll want to start interacting with `geth` and the
|
As a developer, sooner rather than later you'll want to start interacting with Geth and the Ethereum
|
||||||
Ethereum network via your own programs and not manually through the console. To aid
|
network via your own programs and not manually through the console. To aid this, Geth has built-in
|
||||||
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.github.io/execution-apis/api-documentation/)
|
support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and
|
||||||
and [`geth` specific APIs](https://geth.ethereum.org/docs/rpc/server)).
|
[Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be
|
||||||
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
|
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platforms, and named pipes on Windows).
|
||||||
platforms, and named pipes on Windows).
|
|
||||||
|
|
||||||
The IPC interface is enabled by default and exposes all the APIs supported by `geth`,
|
The IPC interface is enabled by default and exposes all the APIs supported by Geth, whereas the HTTP
|
||||||
whereas the HTTP and WS interfaces need to manually be enabled and only expose a
|
and WS interfaces need to manually be enabled and only expose a subset of APIs due to security reasons.
|
||||||
subset of APIs due to security reasons. These can be turned on/off and configured as
|
These can be turned on/off and configured as you'd expect.
|
||||||
you'd expect.
|
|
||||||
|
|
||||||
HTTP based JSON-RPC API options:
|
HTTP based JSON-RPC API options:
|
||||||
|
|
||||||
* `--http` Enable the HTTP-RPC server
|
* `--rpc` Enable the HTTP-RPC server
|
||||||
* `--http.addr` HTTP-RPC server listening interface (default: `localhost`)
|
* `--rpcaddr` HTTP-RPC server listening interface (default: "localhost")
|
||||||
* `--http.port` HTTP-RPC server listening port (default: `8545`)
|
* `--rpcport` HTTP-RPC server listening port (default: 8545)
|
||||||
* `--http.api` API's offered over the HTTP-RPC interface (default: `eth,net,web3`)
|
* `--rpcapi` API's offered over the HTTP-RPC interface (default: "eth,net,web3")
|
||||||
* `--http.corsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
|
* `--rpccorsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
|
||||||
* `--ws` Enable the WS-RPC server
|
* `--ws` Enable the WS-RPC server
|
||||||
* `--ws.addr` WS-RPC server listening interface (default: `localhost`)
|
* `--wsaddr` WS-RPC server listening interface (default: "localhost")
|
||||||
* `--ws.port` WS-RPC server listening port (default: `8546`)
|
* `--wsport` WS-RPC server listening port (default: 8546)
|
||||||
* `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`)
|
* `--wsapi` API's offered over the WS-RPC interface (default: "eth,net,web3")
|
||||||
* `--ws.origins` Origins from which to accept WebSocket requests
|
* `--wsorigins` Origins from which to accept websockets requests
|
||||||
* `--ipcdisable` Disable the IPC-RPC server
|
* `--ipcdisable` Disable the IPC-RPC server
|
||||||
* `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,txpool,web3`)
|
* `--ipcapi` API's offered over the IPC-RPC interface (default: "admin,debug,eth,miner,net,personal,shh,txpool,web3")
|
||||||
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
||||||
|
|
||||||
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to
|
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect
|
||||||
connect via HTTP, WS or IPC to a `geth` node configured with the above flags and you'll
|
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](https://www.jsonrpc.org/specification)
|
||||||
need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You
|
on all transports. You can reuse the same connection for multiple requests!
|
||||||
can reuse the same connection for multiple requests!
|
|
||||||
|
|
||||||
**Note: Please understand the security implications of opening up an HTTP/WS based
|
**Note: Please understand the security implications of opening up an HTTP/WS based transport before
|
||||||
transport before doing so! Hackers on the internet are actively trying to subvert
|
doing so! Hackers on the internet are actively trying to subvert Ethereum nodes with exposed APIs!
|
||||||
Ethereum nodes with exposed APIs! Further, all browser tabs can access locally
|
Further, all browser tabs can access locally running webservers, so malicious webpages could try to
|
||||||
running web servers, so malicious web pages could try to subvert locally available
|
subvert locally available APIs!**
|
||||||
APIs!**
|
|
||||||
|
|
||||||
### Operating a private network
|
### Operating a private network
|
||||||
|
|
||||||
Maintaining your own private network is more involved as a lot of configurations taken for
|
Maintaining your own private network is more involved as a lot of configurations taken for granted in
|
||||||
granted in the official networks need to be manually set up.
|
the official networks need to be manually set up.
|
||||||
|
|
||||||
#### Defining the private genesis state
|
#### Defining the private genesis state
|
||||||
|
|
||||||
First, you'll need to create the genesis state of your networks, which all nodes need to be
|
First, you'll need to create the genesis state of your networks, which all nodes need to be aware of
|
||||||
aware of and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`):
|
and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"chainId": <arbitrary positive integer>,
|
"chainId": 0,
|
||||||
"homesteadBlock": 0,
|
"homesteadBlock": 0,
|
||||||
"eip150Block": 0,
|
|
||||||
"eip155Block": 0,
|
"eip155Block": 0,
|
||||||
"eip158Block": 0,
|
"eip158Block": 0
|
||||||
"byzantiumBlock": 0,
|
|
||||||
"constantinopleBlock": 0,
|
|
||||||
"petersburgBlock": 0,
|
|
||||||
"istanbulBlock": 0,
|
|
||||||
"berlinBlock": 0,
|
|
||||||
"londonBlock": 0
|
|
||||||
},
|
},
|
||||||
"alloc": {},
|
"alloc" : {},
|
||||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
"difficulty": "0x20000",
|
"difficulty" : "0x20000",
|
||||||
"extraData": "",
|
"extraData" : "",
|
||||||
"gasLimit": "0x2fefd8",
|
"gasLimit" : "0x2fefd8",
|
||||||
"nonce": "0x0000000000000042",
|
"nonce" : "0x0000000000000042",
|
||||||
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp": "0x00"
|
"timestamp" : "0x00"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above fields should be fine for most purposes, although we'd recommend changing
|
The above fields should be fine for most purposes, although we'd recommend changing the `nonce` to
|
||||||
the `nonce` to some random value so you prevent unknown remote nodes from being able
|
some random value so you prevent unknown remote nodes from being able to connect to you. If you'd
|
||||||
to connect to you. If you'd like to pre-fund some accounts for easier testing, create
|
like to pre-fund some accounts for easier testing, you can populate the `alloc` field with account
|
||||||
the accounts and populate the `alloc` field with their addresses.
|
configs:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"alloc": {
|
"alloc": {
|
||||||
"0x0000000000000000000000000000000000000001": {
|
"0x0000000000000000000000000000000000000001": {"balance": "111111111"},
|
||||||
"balance": "111111111"
|
"0x0000000000000000000000000000000000000002": {"balance": "222222222"}
|
||||||
},
|
|
||||||
"0x0000000000000000000000000000000000000002": {
|
|
||||||
"balance": "222222222"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
With the genesis state defined in the above JSON file, you'll need to initialize **every**
|
With the genesis state defined in the above JSON file, you'll need to initialize **every** Geth node
|
||||||
`geth` node with it prior to starting it up to ensure all blockchain parameters are correctly
|
with it prior to starting it up to ensure all blockchain parameters are correctly set:
|
||||||
set:
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth init path/to/genesis.json
|
$ geth init path/to/genesis.json
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Creating the rendezvous point
|
#### Creating the rendezvous point
|
||||||
|
|
||||||
With all nodes that you want to run initialized to the desired genesis state, you'll need to
|
With all nodes that you want to run initialized to the desired genesis state, you'll need to start a
|
||||||
start a bootstrap node that others can use to find each other in your network and/or over
|
bootstrap node that others can use to find each other in your network and/or over the internet. The
|
||||||
the internet. The clean way is to configure and run a dedicated bootnode:
|
clean way is to configure and run a dedicated bootnode:
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ bootnode --genkey=boot.key
|
$ bootnode --genkey=boot.key
|
||||||
$ bootnode --nodekey=boot.key
|
$ bootnode --nodekey=boot.key
|
||||||
```
|
```
|
||||||
|
|
||||||
With the bootnode online, it will display an [`enode` URL](https://ethereum.org/en/developers/docs/networking-layer/network-addresses/#enode)
|
With the bootnode online, it will display an [`enode` URL](https://github.com/ethereum/wiki/wiki/enode-url-format)
|
||||||
that other nodes can use to connect to it and exchange peer information. Make sure to
|
that other nodes can use to connect to it and exchange peer information. Make sure to replace the
|
||||||
replace the displayed IP address information (most probably `[::]`) with your externally
|
displayed IP address information (most probably `[::]`) with your externally accessible IP to get the
|
||||||
accessible IP to get the actual `enode` URL.
|
actual `enode` URL.
|
||||||
|
|
||||||
*Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less
|
*Note: You could also use a full fledged Geth node as a bootnode, but it's the less recommended way.*
|
||||||
recommended way.*
|
|
||||||
|
|
||||||
#### Starting up your member nodes
|
#### Starting up your member nodes
|
||||||
|
|
||||||
With the bootnode operational and externally reachable (you can try
|
With the bootnode operational and externally reachable (you can try `telnet <ip> <port>` to ensure
|
||||||
`telnet <ip> <port>` to ensure it's indeed reachable), start every subsequent `geth`
|
it's indeed reachable), start every subsequent Geth node pointed to the bootnode for peer discovery
|
||||||
node pointed to the bootnode for peer discovery via the `--bootnodes` flag. It will
|
via the `--bootnodes` flag. It will probably also be desirable to keep the data directory of your
|
||||||
probably also be desirable to keep the data directory of your private network separated, so
|
private network separated, so do also specify a custom `--datadir` flag.
|
||||||
do also specify a custom `--datadir` flag.
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above>
|
$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above>
|
||||||
```
|
```
|
||||||
|
|
||||||
*Note: Since your network will be completely cut off from the main and test networks, you'll
|
*Note: Since your network will be completely cut off from the main and test networks, you'll also
|
||||||
also need to configure a miner to process transactions and create new blocks for you.*
|
need to configure a miner to process transactions and create new blocks for you.*
|
||||||
|
|
||||||
#### Running a private miner
|
#### Running a private miner
|
||||||
|
|
||||||
|
Mining on the public Ethereum network is a complex task as it's only feasible using GPUs, requiring
|
||||||
|
an OpenCL or CUDA enabled `ethminer` instance. For information on such a setup, please consult the
|
||||||
|
[EtherMining subreddit](https://www.reddit.com/r/EtherMining/) and the [Genoil miner](https://github.com/Genoil/cpp-ethereum)
|
||||||
|
repository.
|
||||||
|
|
||||||
In a private network setting a single CPU miner instance is more than enough for
|
In a private network setting however, a single CPU miner instance is more than enough for practical
|
||||||
practical purposes as it can produce a stable stream of blocks at the correct intervals
|
purposes as it can produce a stable stream of blocks at the correct intervals without needing heavy
|
||||||
without needing heavy resources (consider running on a single thread, no need for multiple
|
resources (consider running on a single thread, no need for multiple ones either). To start a Geth
|
||||||
ones either). To start a `geth` instance for mining, run it with all your usual flags, extended
|
instance for mining, run it with all your usual flags, extended by:
|
||||||
by:
|
|
||||||
|
|
||||||
```shell
|
```
|
||||||
$ geth <usual-flags> --mine --miner.threads=1 --miner.etherbase=0x0000000000000000000000000000000000000000
|
$ geth <usual-flags> --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000
|
||||||
```
|
```
|
||||||
|
|
||||||
Which will start mining blocks and transactions on a single CPU thread, crediting all
|
Which will start mining blocks and transactions on a single CPU thread, crediting all proceedings to
|
||||||
proceedings to the account specified by `--miner.etherbase`. You can further tune the mining
|
the account specified by `--etherbase`. You can further tune the mining by changing the default gas
|
||||||
by changing the default gas limit blocks converge to (`--miner.targetgaslimit`) and the price
|
limit blocks converge to (`--targetgaslimit`) and the price transactions are accepted at (`--gasprice`).
|
||||||
transactions are accepted at (`--miner.gasprice`).
|
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
Thank you for considering helping out with the source code! We welcome contributions
|
Thank you for considering to help out with the source code! We welcome contributions from
|
||||||
from anyone on the internet, and are grateful for even the smallest of fixes!
|
anyone on the internet, and are grateful for even the smallest of fixes!
|
||||||
|
|
||||||
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
|
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
|
||||||
for the maintainers to review and merge into the main code base. If you wish to submit
|
for the maintainers to review and merge into the main code base. If you wish to submit more
|
||||||
more complex changes though, please check up with the core devs first on [our Discord Server](https://discord.gg/invite/nthXNEv)
|
complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum)
|
||||||
to ensure those changes are in line with the general philosophy of the project and/or get
|
to ensure those changes are in line with the general philosophy of the project and/or get some
|
||||||
some early feedback which can make both your efforts much lighter as well as our review
|
early feedback which can make both your efforts much lighter as well as our review and merge
|
||||||
and merge procedures quick and simple.
|
procedures quick and simple.
|
||||||
|
|
||||||
Please make sure your contributions adhere to our coding guidelines:
|
Please make sure your contributions adhere to our coding guidelines:
|
||||||
|
|
||||||
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting)
|
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||||
guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||||
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary)
|
|
||||||
guidelines.
|
|
||||||
* Pull requests need to be based on and opened against the `master` branch.
|
* Pull requests need to be based on and opened against the `master` branch.
|
||||||
* Commit messages should be prefixed with the package(s) they modify.
|
* Commit messages should be prefixed with the package(s) they modify.
|
||||||
* E.g. "eth, rpc: make trace configs optional"
|
* E.g. "eth, rpc: make trace configs optional"
|
||||||
|
|
||||||
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/geth-developer/dev-guide)
|
Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
|
||||||
for more details on configuring your environment, managing project dependencies, and
|
for more details on configuring your environment, managing project dependencies and testing procedures.
|
||||||
testing procedures.
|
|
||||||
|
|
||||||
### Contributing to geth.ethereum.org
|
|
||||||
|
|
||||||
For contributions to the [go-ethereum website](https://geth.ethereum.org), please checkout and raise pull requests against the `website` branch.
|
|
||||||
For more detailed instructions please see the `website` branch [README](https://github.com/ethereum/go-ethereum/tree/website#readme) or the
|
|
||||||
[contributing](https://geth.ethereum.org/docs/developers/geth-developer/contributing) page of the website.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the
|
The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the
|
||||||
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
|
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), also
|
||||||
also included in our repository in the `COPYING.LESSER` file.
|
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
|
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also included
|
||||||
included in our repository in the `COPYING` file.
|
in our repository in the `COPYING` file.
|
||||||
|
|||||||
175
SECURITY.md
175
SECURITY.md
@@ -1,175 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Please see [Releases](https://github.com/ethereum/go-ethereum/releases). We recommend using the [most recently released version](https://github.com/ethereum/go-ethereum/releases/latest).
|
|
||||||
|
|
||||||
## Audit reports
|
|
||||||
|
|
||||||
Audit reports are published in the `docs` folder: https://github.com/ethereum/go-ethereum/tree/master/docs/audits
|
|
||||||
|
|
||||||
| Scope | Date | Report Link |
|
|
||||||
| ------- | ------- | ----------- |
|
|
||||||
| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) |
|
|
||||||
| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) |
|
|
||||||
| `Discv5` | 20191015 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2019-10-15_Discv5_audit_LeastAuthority.pdf) |
|
|
||||||
| `Discv5` | 20200124 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2020-01-24_DiscV5_audit_Cure53.pdf) |
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
**Please do not file a public ticket** mentioning the vulnerability.
|
|
||||||
|
|
||||||
To find out how to disclose a vulnerability in Ethereum visit [https://bounty.ethereum.org](https://bounty.ethereum.org) or email bounty@ethereum.org. Please read the [disclosure page](https://github.com/ethereum/go-ethereum/security/advisories?state=published) for more information about publicly disclosed security vulnerabilities.
|
|
||||||
|
|
||||||
Use the built-in `geth version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json) file which contains known security vulnerabilities concerning `geth`, and cross-check the data against its own version number.
|
|
||||||
|
|
||||||
The following key may be used to communicate sensitive information to developers.
|
|
||||||
|
|
||||||
Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A`
|
|
||||||
|
|
||||||
```
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Version: SKS 1.1.6
|
|
||||||
Comment: Hostname: pgp.mit.edu
|
|
||||||
|
|
||||||
mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaYneAk3Bp1
|
|
||||||
82GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9L8c8yiqry1ZTCmYM
|
|
||||||
qCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUim+y7buJDtoNf7YILlhDQXN8q
|
|
||||||
lHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0bfUo9pexOn7LS4SojoJmsm/5dp6AoKlac
|
|
||||||
48cZU5zwR9AYcq/nvkrfmf2WkObg/xRdEvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/y
|
|
||||||
PFE335k+ujjZCPOu7OwjzDk7M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXCho
|
|
||||||
yI8vbfp4dGvCvYqvQAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+F
|
|
||||||
nQOUgg2Hh8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c
|
|
||||||
2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZEZCjMXxB
|
|
||||||
8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQABtDRFdGhlcmV1bSBG
|
|
||||||
b3VuZGF0aW9uIEJ1ZyBCb3VudHkgPGJvdW50eUBldGhlcmV1bS5vcmc+iQIcBBEBCAAGBQJa
|
|
||||||
FCY6AAoJEHoMA3Q0/nfveH8P+gJBPo9BXZL8isUfbUWjwLi81Yi70hZqIJUnz64SWTqBzg5b
|
|
||||||
mCZ69Ji5637THsxQetS2ARabz0DybQ779FhD/IWnqV9T3KuBM/9RzJtuhLzKCyMrAINPMo28
|
|
||||||
rKWdunHHarpuR4m3tL2zWJkle5QVYb+vkZXJJE98PJw+N4IYeKKeCs2ubeqZu636GA0sMzzB
|
|
||||||
Jn3m/dRRA2va+/zzbr6F6b51ynzbMxWKTsJnstjC8gs8EeI+Zcd6otSyelLtCUkk3h5sTvpV
|
|
||||||
Wv67BNSU0BYsMkxyFi9PUyy07Wixgeas89K5jG1oOtDva/FkpRHrTE/WA5OXDRcLrHJM+SwD
|
|
||||||
CwqcLQqJd09NxwUW1iKeBmPptTiOGu1Gv2o7aEyoaWrHRBO7JuYrQrj6q2B3H1Je0zjAd2qt
|
|
||||||
09ni2bLwLn4LA+VDpprNTO+eZDprv09s2oFSU6NwziHybovu0y7X4pADGkK2evOM7c86PohX
|
|
||||||
QRQ1M1T16xLj6wP8/Ykwl6v/LUk7iDPXP3GPILnh4YOkwBR3DsCOPn8098xy7FxEELmupRzt
|
|
||||||
Cj9oC7YAoweeShgUjBPzb+nGY1m6OcFfbUPBgFyMMfwF6joHbiVIO+39+Ut2g2ysZa7KF+yp
|
|
||||||
XqVDqyEkYXsOLb25OC7brt8IJEPgBPwcHK5GNag6RfLxnQV+iVZ9KNH1yQgSiQI+BBMBAgAo
|
|
||||||
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCWglh+gUJBaNgWAAKCRDojTM0+l9qCgQ2
|
|
||||||
D/4udJpV4zGIZW1yNaVvtd3vfKsTLi7GIRJLUBqVb2Yx/uhnN8jTl/tAhCVosCQ1pzvi9kMl
|
|
||||||
s8qO1vu2kw5EWFFkwK96roI8pTql3VIjwhRVQrCkR7oAk/eUd1U/nt2q6J4UTYeVgqbq4dsI
|
|
||||||
ZZTRyPJMD667YpuAIcaah+w9j/E5xksYQdMeprnDrQkkBCb4FIMqfDzBPKvEa8DcQr949K85
|
|
||||||
kxhr6LDq9i5l4Egxt2JdH8DaR4GLca6+oHy0MyPs/bZOsfmZUObfM2oZgPpqYM96JanhzO1j
|
|
||||||
dpnItyBii2pc+kNx5nMOf4eikE/MBv+WUJ0TttWzApGGmFUzDhtuEvRH9NBjtJ/pMrYspIGu
|
|
||||||
O/QNY5KKOKQTvVIlwGcm8dTsSkqtBDSUwZyWbfKfKOI1/RhM9dC3gj5/BOY57DYYV4rdTK01
|
|
||||||
ZtYjuhdfs2bhuP1uF/cgnSSZlv8azvf7Egh7tHPnYxvLjfq1bJAhCIX0hNg0a81/ndPAEFky
|
|
||||||
fSko+JPKvdSvsUcSi2QQ4U2HX//jNBjXRfG4F0utgbJnhXzEckz6gqt7wSDZH2oddVuO8Ssc
|
|
||||||
T7sK+CdXthSKnRyuI+sGUpG+6glpKWIfYkWFKNZWuQ+YUatY3QEDHXTIioycSmV8p4d/g/0S
|
|
||||||
V6TegidLxY8bXMkbqz+3n6FArRffv5MH7qt3cYkCPgQTAQIAKAUCWCXhOwIbAwUJAeEzgAYL
|
|
||||||
CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6I0zNPpfagrN/w/+Igp3vtYdNunikw3yHnYf
|
|
||||||
Jkm0MmaMDUM9mtsaXVN6xb9n25N3Xa3GWCpmdsbYZ8334tI/oQ4/NHq/bEI5WFH5F1aFkMkm
|
|
||||||
5AJVLuUkipCtmCZ5NkbRPJA9l0uNUUE6uuFXBhf4ddu7jb0jMetRF/kifJHVCCo5fISUNhLp
|
|
||||||
7bwcWq9qgDQNZNYMOo4s9WX5Tl+5x4gTZdd2/cAYt49h/wnkw+huM+Jm0GojpLqIQ1jZiffm
|
|
||||||
otf5rF4L+JhIIdW0W4IIh1v9BhHVllXw+z9oj0PALstT5h8/DuKoIiirFJ4DejU85GR1KKAS
|
|
||||||
DeO19G/lSpWj1rSgFv2N2gAOxq0X+BbQTua2jdcY6JpHR4H1JJ2wzfHsHPgDQcgY1rGlmjVF
|
|
||||||
aqU73WV4/hzXc/HshK/k4Zd8uD4zypv6rFsZ3UemK0aL2zXLVpV8SPWQ61nS03x675SmDlYr
|
|
||||||
A80ENfdqvsn00JQuBVIv4Tv0Ub7NfDraDGJCst8rObjBT/0vnBWTBCebb2EsnS2iStIFkWdz
|
|
||||||
/WXs4L4Yzre1iJwqRjiuqahZR5jHsjAUf2a0O29HVHE7zlFtCFmLPClml2lGQfQOpm5klGZF
|
|
||||||
rmvus+qZ9rt35UgWHPZezykkwtWrFOwspwuCWaPDto6tgbRJZ4ftitpdYYM3dKW9IGJXBwrt
|
|
||||||
BQrMsu+lp0vDF+yJAlUEEwEIAD8CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAFiEErpbt
|
|
||||||
lp5HmwCE8+F/6I0zNPpfagoFAmEAEJwFCQycmLgACgkQ6I0zNPpfagpWoBAAhOcbMAUw6Zt0
|
|
||||||
GYzT3sR5/c0iatezPzXEXJf9ebzR8M5uPElXcxcnMx1dvXZmGPXPJKCPa99WCu1NZYy8F+Wj
|
|
||||||
GTOY9tfIkvSxhys1p/giPAmvid6uQmD+bz7ivktnyzCkDWfMA+l8lsCSEqVlaq6y5T+a6SWB
|
|
||||||
6TzC2S0MPb/RrC/7DpwyrNYWumvyVJh09adm1Mw/UGgst/sZ8eMaRYEd3X0yyT1CBpX4zp2E
|
|
||||||
qQj9IEOTizvzv1x2jkHe5ZUeU3+nTBNlhSA+WFHUi0pfBdo2qog3Mv2EC1P2qMKoSdD5tPbA
|
|
||||||
zql1yKoHHnXOMsqdftGwbiv2sYXWvrYvmaCd3Ys/viOyt3HOy9uV2ZEtBd9Yqo9x/NZj8QMA
|
|
||||||
nY5k8jjrIXbUC89MqrJsQ6xxWQIg5ikMT7DvY0Ln89ev4oJyVvwIQAwCm4jUzFNm9bZLYDOP
|
|
||||||
5lGJCV7tF5NYVU7NxNM8vescKc40mVNK/pygS5mxhK9QYOUjZsIv8gddrl1TkqrFMuxFnTyN
|
|
||||||
WvzE29wFu/n4N1DkF+ZBqS70SlRvB+Hjz5LrDgEzF1Wf1eA/wq1dZbvMjjDVIc2VGlYp8Cp2
|
|
||||||
8ob23c1seTtYXTNYgSR5go4EpH+xi+bIWv01bQQ9xGwBbT5sm4WUeWOcmX4QewzLZ3T/wK9+
|
|
||||||
N4Ye/hmU9O34FwWJOY58EIe0OUV0aGVyZXVtIEZvdW5kYXRpb24gU2VjdXJpdHkgVGVhbSA8
|
|
||||||
c2VjdXJpdHlAZXRoZXJldW0ub3JnPokCHAQRAQgABgUCWhQmOgAKCRB6DAN0NP5372LSEACT
|
|
||||||
wZk1TASWZj5QF7rmkIM1GEyBxLE+PundNcMgM9Ktj1315ED8SmiukNI4knVS1MY99OIgXhQl
|
|
||||||
D1foF2GKdTomrwwC4012zTNyUYCY60LnPZ6Z511HG+rZgZtZrbkz0IiUpwAlhGQND77lBqem
|
|
||||||
J3K+CFX2XpDA/ojui/kqrY4cwMT5P8xPJkwgpRgw/jgdcZyJTsXdHblV9IGU4H1Vd1SgcfAf
|
|
||||||
Db3YxDUlBtzlp0NkZqxen8irLIXUQvsfuIfRUbUSkWoK/n3U/gOCajAe8ZNF07iX4OWjH4Sw
|
|
||||||
NDA841WhFWcGE+d8+pfMVfPASU3UPKH72uw86b2VgR46Av6voyMFd1pj+yCA+YAhJuOpV4yL
|
|
||||||
QaGg2Z0kVOjuNWK/kBzp1F58DWGh4YBatbhE/UyQOqAAtR7lNf0M3QF9AdrHTxX8oZeqVW3V
|
|
||||||
Fmi2mk0NwCIUv8SSrZr1dTchp04OtyXe5gZBXSfzncCSRQIUDC8OgNWaOzAaUmK299v4bvye
|
|
||||||
uSCxOysxC7Q1hZtjzFPKdljS81mRlYeUL4fHlJU9R57bg8mriSXLmn7eKrSEDm/EG5T8nRx7
|
|
||||||
TgX2MqJs8sWFxD2+bboVEu75yuFmZ//nmCBApAit9Hr2/sCshGIEpa9MQ6xJCYUxyqeJH+Cc
|
|
||||||
Aja0UfXhnK2uvPClpJLIl4RE3gm4OXeE1IkCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC
|
|
||||||
AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagr4MQ//cfp3GSbSG8dkqgctW67Fy7cQ
|
|
||||||
diiTmx3cwxY+tlI3yrNmdjtrIQMzGdqtY6LNz7aN87F8mXNf+DyVHX9+wd1Y8U+E+hVCTzKC
|
|
||||||
sefUfxTz6unD9TTcGqaoelgIPMn4IiKz1RZE6eKpfDWe6q78W1Y6x1bE0qGNSjqT/QSxpezF
|
|
||||||
E/OAm/t8RRxVxDtqz8LfH2zLea5zaC+ADj8EqgY9vX9TQa4DyVV8MgOyECCCadJQCD5O5hIA
|
|
||||||
B2gVDWwrAUw+KBwskXZ7Iq4reJTKLEmt5z9zgtJ/fABwaCFt66ojwg0/RjbO9cNA3ZwHLGwU
|
|
||||||
C6hkb6bRzIoZoMfYxVS84opiqf/Teq+t/XkBYCxbSXTJDA5MKjcVuw3N6YKWbkGP/EfQThe7
|
|
||||||
BfAKFwwIw5YmsWjHK8IQj6R6hBxzTz9rz8y1Lu8EAAFfA7OJKaboI2qbOlauH98OuOUmVtr1
|
|
||||||
TczHO+pTcgWVN0ytq2/pX5KBf4vbmULNbg3HFRq+gHx8CW+jyXGkcqjbgU/5FwtDxeqRTdGJ
|
|
||||||
SyBGNBEU6pBNolyynyaKaaJjJ/biY27pvjymL5rlz95BH3Dn16Z4RRmqwlT6eq/wFYginujg
|
|
||||||
CCE1icqOSE+Vjl7V8tV8AcgANkXKdbBE+Q8wlKsGI/kS1w4XFAYcaNHFT8qNeS8TSFXFhvU8
|
|
||||||
HylYxO79t56JAj4EEwECACgFAlgl3tgCGwMFCQHhM4AGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
|
|
||||||
AheAAAoJEOiNMzT6X2oKmUMP/0hnaL6bVyepAq2LIdvIUbHfagt/Oo/KVfZs4bkM+xJOitJR
|
|
||||||
0kwZV9PTihXFdzhL/YNWc2+LtEBtKItqkJZKmWC0E6OPXGVuU6hfFPebuzVccYJfm0Q3Ej19
|
|
||||||
VJI9Uomf59Bpak8HYyEED7WVQjoYn7XVPsonwus/9+LDX+c5vutbrUdbjga3KjHbewD93X4O
|
|
||||||
wVVoXyHEmU2Plyg8qvzFbNDylCWO7N2McO6SN6+7DitGZGr2+jO+P2R4RT1cnl2V3IRVcWZ0
|
|
||||||
OTspPSnRGVr2fFiHN/+v8G/wHPLQcJZFvYPfUGNdcYbTmhWdiY0bEYXFiNrgzCCsyad7eKUR
|
|
||||||
WN9QmxqmyqLDjUEDJCAh19ES6Vg3tqGwXk+uNUCoF30ga0TxQt6UXZJDEQFAGeASQ/RqE/q1
|
|
||||||
EAuLv8IGM8o7IqKO2pWfLuqsY6dTbKBwDzz9YOJt7EOGuPPQbHxaYStTushZmJnm7hi8lhVG
|
|
||||||
jT7qsEJdE95Il+I/mHWnXsCevaXjZugBiyV9yvOq4Hwwe2s1zKfrnQ4u0cadvGAh2eIqum7M
|
|
||||||
Y3o6nD47aJ3YmEPX/WnhI56bACa2GmWvUwjI4c0/er3esSPYnuHnM9L8Am4qQwMVSmyU80tC
|
|
||||||
MI7A9e13Mvv+RRkYFLJ7PVPdNpbW5jqX1doklFpKf6/XM+B+ngYneU+zgCUBiQJVBBMBCAA/
|
|
||||||
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBK6W7ZaeR5sAhPPhf+iNMzT6X2oKBQJh
|
|
||||||
ABCQBQkMnJi4AAoJEOiNMzT6X2oKAv0P+gJ3twBp5efNWyVLcIg4h4cOo9uD0NPvz8/fm2gX
|
|
||||||
FoOJL3MeigtPuSVfE9kuTaTuRbArzuFtdvH6G/kcRQvOlO4zyiIRHCk1gDHoIvvtn6RbRhVm
|
|
||||||
/Xo4uGIsFHst7n4A7BjicwEK5Op6Ih5Hoq19xz83YSBgBVk2fYEJIRyJiKFbyPjH0eSYe8v+
|
|
||||||
Ra5/F85ugLx1P6mMVkW+WPzULns89riW7BGTnZmXFHZp8nO2pkUlcI7F3KRG7l4kmlC50ox6
|
|
||||||
DiG/6AJCVulbAClky9C68TmJ/R1RazQxU/9IqVywsydq66tbJQbm5Z7GEti0C5jjbSRJL2oT
|
|
||||||
1xC7Rilr85PMREkPL3vegJdgj5PKlffZ/MocD/0EohiQ7wFpejFD4iTljeh0exRUwCRb6655
|
|
||||||
9ib34JSQgU8Hl4JJu+mEgd9v0ZHD0/1mMD6fnAR84zca+O3cdASbnQmzTOKcGzLIrkE8TEnU
|
|
||||||
+2UZ8Ol7SAAqmBgzY1gKOilUho6dkyCAwNL+QDpvrITDPLEFPsjyB/M2KudZSVEn+Rletju1
|
|
||||||
qkMW31qFMNlsbwzMZw+0USeGcs31Cs0B2/WQsro99CExlhS9auUFkmoVjJmYVTIYOM0zuPa4
|
|
||||||
OyGspqPhRu5hEsmMDPDWD7Aad5k4GTqogQNnuKyRliZjXXrDZqFD5nfsJSL8Ky/sJGEMuQIN
|
|
||||||
BFgl3tgBEACbgq6HTN5gEBi0lkD/MafInmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4h
|
|
||||||
YontkMaKRlCg2Rvgjvk3Zve0PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT1
|
|
||||||
9BdeAQRFvcfd+8w8f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj
|
|
||||||
26bf+2+1DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6
|
|
||||||
D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66iPsR99MQ7
|
|
||||||
FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A4tGkHl08KZ2N9o6G
|
|
||||||
rfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8greW8xB4zuf9Mkuou+RHNmo8Pe
|
|
||||||
bHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0VRxdPImKun+4LOXbfOxArOSkY6i35+gs
|
|
||||||
gkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/
|
|
||||||
bM1ACUtipMiIVeUs2uFiRjpzA1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJa
|
|
||||||
CWIIBQkFo2BYAAoJEOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg
|
|
||||||
3IHMGxDMb/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8
|
|
||||||
KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0IQ1UKKXvz
|
|
||||||
ZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0K9lneidcqtBDvlgg
|
|
||||||
JTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0TNOOE8fXlvu8iuIAMBSDL9ep6
|
|
||||||
sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd5MTi0MDRNTij431kn8T/D0LCgmoUmYYM
|
|
||||||
BgbwFhXr67axPZlKjrqR0z3F/Elv0ZPPcVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1q
|
|
||||||
Scl9HiMxjt/H6aPastH63/7wcN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4
|
|
||||||
/Lih6Z1TlwcFVap+cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1p
|
|
||||||
M6AOQPpZ85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4iQIl
|
|
||||||
BBgBAgAPBQJYJd7YAhsMBQkB4TOAAAoJEOiNMzT6X2oKTjgP/1ojCVyGyvHMLUgnX0zwrR5Q
|
|
||||||
1M5RKFz6kHwKjODVLR3Isp8I935oTQt3DY7yFDI4t0GqbYRQMtxcNEb7maianhK2trCXfhPs
|
|
||||||
6/L04igjDf5iTcmzamXN6xnh5xkz06hZJJCMuu4MvKxC9MQHCVKAwjswl/9H9JqIBXAY3E2l
|
|
||||||
LpX5P+5jDZuPxS86p3+k4Rrdp9KTGXjiuEleM3zGlz5BLWydqovOck7C2aKh27ETFpDYY0z3
|
|
||||||
yQ5AsPJyk1rAr0wrH6+ywmwWlzuQewavnrLnJ2M8iMFXpIhyHeEIU/f7o8f+dQk72rZ9CGzd
|
|
||||||
cqig2za/BS3zawZWgbv2vB2elNsIllYLdir45jxBOxx2yvJvEuu4glz78y4oJTCTAYAbMlle
|
|
||||||
5gVdPkVcGyvvVS9tinnSaiIzuvWrYHKWll1uYPm2Q1CDs06P5I7bUGAXpgQLUh/XQguy/0sX
|
|
||||||
GWqW3FS5JzP+XgcR/7UASvwBdHylubKbeqEpB7G1s+m+8C67qOrc7EQv3Jmy1YDOkhEyNig1
|
|
||||||
rmjplLuir3tC1X+D7dHpn7NJe7nMwFx2b2MpMkLA9jPPAGPp/ekcu5sxCe+E0J/4UF++K+CR
|
|
||||||
XIxgtzU2UJfp8p9x+ygbx5qHinR0tVRdIzv3ZnGsXrfxnWfSOaB582cU3VRN9INzHHax8ETa
|
|
||||||
QVDnGO5uQa+FiQI8BBgBCAAmAhsMFiEErpbtlp5HmwCE8+F/6I0zNPpfagoFAmEAELYFCQyc
|
|
||||||
mN4ACgkQ6I0zNPpfagoqAQ/+MnDjBx8JWMd/XjeFoYKx/Oo0ntkInV+ME61JTBls4PdVk+TB
|
|
||||||
8PWZdPQHw9SnTvRmykFeznXIRzuxkowjrZYXdPXBxY2b1WyD5V3Ati1TM9vqpaR4osyPs2xy
|
|
||||||
I4dzDssh9YvUsIRL99O04/65lGiYeBNuACq+yK/7nD/ErzBkDYJHhMCdadbVWUACxvVIDvro
|
|
||||||
yQeVLKMsHqMCd8BTGD7VDs79NXskPnN77pAFnkzS4Z2b8SNzrlgTc5pUiuZHIXPIpEYmsYzh
|
|
||||||
ucTU6uI3dN1PbSFHK5tG2pHb4ZrPxY3L20Dgc2Tfu5/SDApZzwvvKTqjdO891MEJ++H+ssOz
|
|
||||||
i4O1UeWKs9owWttan9+PI47ozBSKOTxmMqLSQ0f56Np9FJsV0ilGxRKfjhzJ4KniOMUBA7mP
|
|
||||||
+m+TmXfVtthJred4sHlJMTJNpt+sCcT6wLMmyc3keIEAu33gsJj3LTpkEA2q+V+ZiP6Q8HRB
|
|
||||||
402ITklABSArrPSE/fQU9L8hZ5qmy0Z96z0iyILgVMLuRCCfQOMWhwl8yQWIIaf1yPI07xur
|
|
||||||
epy6lH7HmxjjOR7eo0DaSxQGQpThAtFGwkWkFh8yki8j3E42kkrxvEyyYZDXn2YcI3bpqhJx
|
|
||||||
PtwCMZUJ3kc/skOrs6bOI19iBNaEoNX5Dllm7UHjOgWNDQkcCuOCxucKano=
|
|
||||||
=arte
|
|
||||||
-----END PGP PUBLIC KEY BLOCK------
|
|
||||||
```
|
|
||||||
@@ -19,12 +19,8 @@ package abi
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The ABI holds information about a contract's context and available
|
// The ABI holds information about a contract's context and available
|
||||||
@@ -34,13 +30,6 @@ type ABI struct {
|
|||||||
Constructor Method
|
Constructor Method
|
||||||
Methods map[string]Method
|
Methods map[string]Method
|
||||||
Events map[string]Event
|
Events map[string]Event
|
||||||
Errors map[string]Error
|
|
||||||
|
|
||||||
// Additional "special" functions introduced in solidity v0.6.0.
|
|
||||||
// It's separated from the original default fallback. Each contract
|
|
||||||
// can only define one fallback and receive function.
|
|
||||||
Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
|
|
||||||
Receive Method
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON returns a parsed ABI interface and error if it failed.
|
// JSON returns a parsed ABI interface and error if it failed.
|
||||||
@@ -51,6 +40,7 @@ func JSON(reader io.Reader) (ABI, error) {
|
|||||||
if err := dec.Decode(&abi); err != nil {
|
if err := dec.Decode(&abi); err != nil {
|
||||||
return ABI{}, err
|
return ABI{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi, nil
|
return abi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,178 +68,80 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Pack up the method ID too if not a constructor and return
|
// Pack up the method ID too if not a constructor and return
|
||||||
return append(method.ID, arguments...), nil
|
return append(method.Id(), arguments...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
|
// Unpack output in v according to the abi specification
|
||||||
|
func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
|
||||||
|
if len(output) == 0 {
|
||||||
|
return fmt.Errorf("abi: unmarshalling empty output")
|
||||||
|
}
|
||||||
// since there can't be naming collisions with contracts and events,
|
// since there can't be naming collisions with contracts and events,
|
||||||
// we need to decide whether we're calling a method or an event
|
// we need to decide whether we're calling a method or an event
|
||||||
var args Arguments
|
|
||||||
if method, ok := abi.Methods[name]; ok {
|
if method, ok := abi.Methods[name]; ok {
|
||||||
if len(data)%32 != 0 {
|
if len(output)%32 != 0 {
|
||||||
return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
|
return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(output), output)
|
||||||
}
|
}
|
||||||
args = method.Outputs
|
return method.Outputs.Unpack(v, output)
|
||||||
|
} else if event, ok := abi.Events[name]; ok {
|
||||||
|
return event.Inputs.Unpack(v, output)
|
||||||
}
|
}
|
||||||
if event, ok := abi.Events[name]; ok {
|
return fmt.Errorf("abi: could not locate named method or event")
|
||||||
args = event.Inputs
|
|
||||||
}
|
|
||||||
if args == nil {
|
|
||||||
return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
|
|
||||||
}
|
|
||||||
return args, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack unpacks the output according to the abi specification.
|
// UnmarshalJSON implements json.Unmarshaler interface
|
||||||
func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
|
|
||||||
args, err := abi.getArguments(name, data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return args.Unpack(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackIntoInterface unpacks the output in v according to the abi specification.
|
|
||||||
// It performs an additional copy. Please only use, if you want to unpack into a
|
|
||||||
// structure that does not strictly conform to the abi structure (e.g. has additional arguments)
|
|
||||||
func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
|
|
||||||
args, err := abi.getArguments(name, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
unpacked, err := args.Unpack(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return args.Copy(v, unpacked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackIntoMap unpacks a log into the provided map[string]interface{}.
|
|
||||||
func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
|
|
||||||
args, err := abi.getArguments(name, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return args.UnpackIntoMap(v, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
||||||
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
var fields []struct {
|
var fields []struct {
|
||||||
Type string
|
Type string
|
||||||
Name string
|
Name string
|
||||||
|
Constant bool
|
||||||
|
Anonymous bool
|
||||||
Inputs []Argument
|
Inputs []Argument
|
||||||
Outputs []Argument
|
Outputs []Argument
|
||||||
|
|
||||||
// Status indicator which can be: "pure", "view",
|
|
||||||
// "nonpayable" or "payable".
|
|
||||||
StateMutability string
|
|
||||||
|
|
||||||
// Deprecated Status indicators, but removed in v0.6.0.
|
|
||||||
Constant bool // True if function is either pure or view
|
|
||||||
Payable bool // True if function is payable
|
|
||||||
|
|
||||||
// Event relevant indicator represents the event is
|
|
||||||
// declared as anonymous.
|
|
||||||
Anonymous bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &fields); err != nil {
|
if err := json.Unmarshal(data, &fields); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
abi.Methods = make(map[string]Method)
|
abi.Methods = make(map[string]Method)
|
||||||
abi.Events = make(map[string]Event)
|
abi.Events = make(map[string]Event)
|
||||||
abi.Errors = make(map[string]Error)
|
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
abi.Constructor = Method{
|
||||||
case "function":
|
Inputs: field.Inputs,
|
||||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
|
||||||
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
|
||||||
case "fallback":
|
|
||||||
// New introduced function type in v0.6.0, check more detail
|
|
||||||
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
|
||||||
if abi.HasFallback() {
|
|
||||||
return errors.New("only single fallback is allowed")
|
|
||||||
}
|
}
|
||||||
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
// empty defaults to function according to the abi spec
|
||||||
case "receive":
|
case "function", "":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
abi.Methods[field.Name] = Method{
|
||||||
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
Name: field.Name,
|
||||||
if abi.HasReceive() {
|
Const: field.Constant,
|
||||||
return errors.New("only single receive is allowed")
|
Inputs: field.Inputs,
|
||||||
|
Outputs: field.Outputs,
|
||||||
}
|
}
|
||||||
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":
|
case "event":
|
||||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
abi.Events[field.Name] = Event{
|
||||||
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
Name: field.Name,
|
||||||
case "error":
|
Anonymous: field.Anonymous,
|
||||||
// Errors cannot be overloaded or overridden but are inherited,
|
Inputs: field.Inputs,
|
||||||
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodById looks up a method by the 4-byte id,
|
// MethodById looks up a method by the 4-byte id
|
||||||
// returns nil if none found.
|
// returns nil if none found
|
||||||
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
if len(sigdata) < 4 {
|
if len(sigdata) < 4 {
|
||||||
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
return nil, fmt.Errorf("data too short (% bytes) for abi method lookup", len(sigdata))
|
||||||
}
|
}
|
||||||
for _, method := range abi.Methods {
|
for _, method := range abi.Methods {
|
||||||
if bytes.Equal(method.ID, sigdata[:4]) {
|
if bytes.Equal(method.Id(), sigdata[:4]) {
|
||||||
return &method, nil
|
return &method, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventByID looks an event up by its topic hash in the
|
|
||||||
// ABI and returns nil if none found.
|
|
||||||
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
|
||||||
for _, event := range abi.Events {
|
|
||||||
if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
|
|
||||||
return &event, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasFallback returns an indicator whether a fallback function is included.
|
|
||||||
func (abi *ABI) HasFallback() bool {
|
|
||||||
return abi.Fallback.Type == Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasReceive returns an indicator whether a receive function is included.
|
|
||||||
func (abi *ABI) HasReceive() bool {
|
|
||||||
return abi.Receive.Type == Receive
|
|
||||||
}
|
|
||||||
|
|
||||||
// revertSelector is a special function selector for revert reason unpacking.
|
|
||||||
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
|
||||||
|
|
||||||
// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
|
|
||||||
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
|
|
||||||
// the provided revert reason is abi-encoded as if it were a call to a function
|
|
||||||
// `Error(string)`. So it's a special tool for it.
|
|
||||||
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, _ := NewType("string", "", nil)
|
|
||||||
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return unpacked[0].(string), nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,116 +19,65 @@ package abi
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const jsondata = `
|
const jsondata = `
|
||||||
[
|
[
|
||||||
{ "type" : "function", "name" : ""},
|
{ "type" : "function", "name" : "balance", "constant" : true },
|
||||||
{ "type" : "function", "name" : "balance", "stateMutability" : "view" },
|
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
|
||||||
{ "type" : "function", "name" : "send", "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
|
||||||
{ "type" : "function", "name" : "test", "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
|
|
||||||
{ "type" : "function", "name" : "string", "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
|
|
||||||
{ "type" : "function", "name" : "bool", "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
|
|
||||||
{ "type" : "function", "name" : "address", "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
|
|
||||||
{ "type" : "function", "name" : "uint64[2]", "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "uint64[]", "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "int8", "inputs" : [ { "name" : "inputs", "type" : "int8" } ] },
|
|
||||||
{ "type" : "function", "name" : "bytes32", "inputs" : [ { "name" : "inputs", "type" : "bytes32" } ] },
|
|
||||||
{ "type" : "function", "name" : "foo", "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
|
||||||
{ "type" : "function", "name" : "bar", "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
|
||||||
{ "type" : "function", "name" : "slice", "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "slice256", "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "sliceAddress", "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "sliceMultiAddress", "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "nestedArray", "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "nestedArray2", "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "nestedSlice", "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] },
|
|
||||||
{ "type" : "function", "name" : "receive", "inputs" : [ { "name" : "memo", "type" : "bytes" }], "outputs" : [], "payable" : true, "stateMutability" : "payable" },
|
|
||||||
{ "type" : "function", "name" : "fixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "fixedArrBytes", "stateMutability" : "view", "inputs" : [ { "name" : "bytes", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "mixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "doubleFixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
|
|
||||||
{ "type" : "function", "name" : "multipleMixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
|
|
||||||
{ "type" : "function", "name" : "overloadedNames", "stateMutability" : "view", "inputs": [ { "components": [ { "internalType": "uint256", "name": "_f", "type": "uint256" }, { "internalType": "uint256", "name": "__f", "type": "uint256"}, { "internalType": "uint256", "name": "f", "type": "uint256"}],"internalType": "struct Overloader.F", "name": "f","type": "tuple"}]}
|
|
||||||
]`
|
]`
|
||||||
|
|
||||||
var (
|
const jsondata2 = `
|
||||||
Uint256, _ = NewType("uint256", "", nil)
|
[
|
||||||
Uint32, _ = NewType("uint32", "", nil)
|
{ "type" : "function", "name" : "balance", "constant" : true },
|
||||||
Uint16, _ = NewType("uint16", "", nil)
|
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
||||||
String, _ = NewType("string", "", nil)
|
{ "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
|
||||||
Bool, _ = NewType("bool", "", nil)
|
{ "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
|
||||||
Bytes, _ = NewType("bytes", "", nil)
|
{ "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
|
||||||
Bytes32, _ = NewType("bytes32", "", nil)
|
{ "type" : "function", "name" : "address", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
|
||||||
Address, _ = NewType("address", "", nil)
|
{ "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
||||||
Uint64Arr, _ = NewType("uint64[]", "", nil)
|
{ "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
||||||
AddressArr, _ = NewType("address[]", "", nil)
|
{ "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
||||||
Int8, _ = NewType("int8", "", nil)
|
{ "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
||||||
// Special types for testing
|
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
||||||
Uint32Arr2, _ = NewType("uint32[2]", "", nil)
|
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
||||||
Uint64Arr2, _ = NewType("uint64[2]", "", nil)
|
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
||||||
Uint256Arr, _ = NewType("uint256[]", "", nil)
|
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
|
||||||
Uint256Arr2, _ = NewType("uint256[2]", "", nil)
|
{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
|
||||||
Uint256Arr3, _ = NewType("uint256[3]", "", nil)
|
{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
|
||||||
Uint256ArrNested, _ = NewType("uint256[2][2]", "", nil)
|
{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
|
||||||
Uint8ArrNested, _ = NewType("uint8[][2]", "", nil)
|
]`
|
||||||
Uint8SliceNested, _ = NewType("uint8[][]", "", nil)
|
|
||||||
TupleF, _ = NewType("tuple", "struct Overloader.F", []ArgumentMarshaling{
|
|
||||||
{Name: "_f", Type: "uint256"},
|
|
||||||
{Name: "__f", Type: "uint256"},
|
|
||||||
{Name: "f", Type: "uint256"}})
|
|
||||||
)
|
|
||||||
|
|
||||||
var methods = map[string]Method{
|
|
||||||
"": NewMethod("", "", Function, "", false, false, nil, nil),
|
|
||||||
"balance": NewMethod("balance", "balance", Function, "view", false, false, nil, nil),
|
|
||||||
"send": NewMethod("send", "send", Function, "", false, false, []Argument{{"amount", Uint256, false}}, nil),
|
|
||||||
"test": NewMethod("test", "test", Function, "", false, false, []Argument{{"number", Uint32, false}}, nil),
|
|
||||||
"string": NewMethod("string", "string", Function, "", false, false, []Argument{{"inputs", String, false}}, nil),
|
|
||||||
"bool": NewMethod("bool", "bool", Function, "", false, false, []Argument{{"inputs", Bool, false}}, nil),
|
|
||||||
"address": NewMethod("address", "address", Function, "", false, false, []Argument{{"inputs", Address, false}}, nil),
|
|
||||||
"uint64[]": NewMethod("uint64[]", "uint64[]", Function, "", false, false, []Argument{{"inputs", Uint64Arr, false}}, nil),
|
|
||||||
"uint64[2]": NewMethod("uint64[2]", "uint64[2]", Function, "", false, false, []Argument{{"inputs", Uint64Arr2, false}}, nil),
|
|
||||||
"int8": NewMethod("int8", "int8", Function, "", false, false, []Argument{{"inputs", Int8, false}}, nil),
|
|
||||||
"bytes32": NewMethod("bytes32", "bytes32", Function, "", false, false, []Argument{{"inputs", Bytes32, false}}, nil),
|
|
||||||
"foo": NewMethod("foo", "foo", Function, "", false, false, []Argument{{"inputs", Uint32, false}}, nil),
|
|
||||||
"bar": NewMethod("bar", "bar", Function, "", false, false, []Argument{{"inputs", Uint32, false}, {"string", Uint16, false}}, nil),
|
|
||||||
"slice": NewMethod("slice", "slice", Function, "", false, false, []Argument{{"inputs", Uint32Arr2, false}}, nil),
|
|
||||||
"slice256": NewMethod("slice256", "slice256", Function, "", false, false, []Argument{{"inputs", Uint256Arr2, false}}, nil),
|
|
||||||
"sliceAddress": NewMethod("sliceAddress", "sliceAddress", Function, "", false, false, []Argument{{"inputs", AddressArr, false}}, nil),
|
|
||||||
"sliceMultiAddress": NewMethod("sliceMultiAddress", "sliceMultiAddress", Function, "", false, false, []Argument{{"a", AddressArr, false}, {"b", AddressArr, false}}, nil),
|
|
||||||
"nestedArray": NewMethod("nestedArray", "nestedArray", Function, "", false, false, []Argument{{"a", Uint256ArrNested, false}, {"b", AddressArr, false}}, nil),
|
|
||||||
"nestedArray2": NewMethod("nestedArray2", "nestedArray2", Function, "", false, false, []Argument{{"a", Uint8ArrNested, false}}, nil),
|
|
||||||
"nestedSlice": NewMethod("nestedSlice", "nestedSlice", Function, "", false, false, []Argument{{"a", Uint8SliceNested, false}}, nil),
|
|
||||||
"receive": NewMethod("receive", "receive", Function, "payable", false, true, []Argument{{"memo", Bytes, false}}, []Argument{}),
|
|
||||||
"fixedArrStr": NewMethod("fixedArrStr", "fixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr", Uint256Arr2, false}}, nil),
|
|
||||||
"fixedArrBytes": NewMethod("fixedArrBytes", "fixedArrBytes", Function, "view", false, false, []Argument{{"bytes", Bytes, false}, {"fixedArr", Uint256Arr2, false}}, nil),
|
|
||||||
"mixedArrStr": NewMethod("mixedArrStr", "mixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}}, nil),
|
|
||||||
"doubleFixedArrStr": NewMethod("doubleFixedArrStr", "doubleFixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
|
|
||||||
"multipleMixedArrStr": NewMethod("multipleMixedArrStr", "multipleMixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
|
|
||||||
"overloadedNames": NewMethod("overloadedNames", "overloadedNames", Function, "view", false, false, []Argument{{"f", TupleF, false}}, nil),
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
abi := ABI{
|
Uint256, _ := NewType("uint256", nil)
|
||||||
Methods: methods,
|
exp := ABI{
|
||||||
|
Methods: map[string]Method{
|
||||||
|
"balance": {
|
||||||
|
"balance", true, nil, nil,
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"send", false, []Argument{
|
||||||
|
{"amount", Uint256, false},
|
||||||
|
}, nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
exp, err := JSON(strings.NewReader(jsondata))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deep equal fails for some reason
|
||||||
for name, expM := range exp.Methods {
|
for name, expM := range exp.Methods {
|
||||||
gotM, exist := abi.Methods[name]
|
gotM, exist := abi.Methods[name]
|
||||||
if !exist {
|
if !exist {
|
||||||
@@ -150,58 +99,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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestConstructor tests a constructor function.
|
|
||||||
// The test is based on the following contract:
|
|
||||||
//
|
|
||||||
// contract TestConstructor {
|
|
||||||
// constructor(uint256 a, uint256 b) public{}
|
|
||||||
// }
|
|
||||||
func TestConstructor(t *testing.T) {
|
|
||||||
json := `[{ "inputs": [{"internalType": "uint256","name": "a","type": "uint256" },{ "internalType": "uint256","name": "b","type": "uint256"}],"stateMutability": "nonpayable","type": "constructor"}]`
|
|
||||||
method := NewMethod("", "", Constructor, "nonpayable", false, false, []Argument{{"a", Uint256, false}, {"b", Uint256, false}}, nil)
|
|
||||||
// Test from JSON
|
|
||||||
abi, err := JSON(strings.NewReader(json))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(abi.Constructor, method) {
|
|
||||||
t.Error("Missing expected constructor")
|
|
||||||
}
|
|
||||||
// Test pack/unpack
|
|
||||||
packed, err := abi.Pack("", big.NewInt(1), big.NewInt(2))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
unpacked, err := abi.Constructor.Inputs.Unpack(packed)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTestNumbers(t *testing.T) {
|
func TestTestNumbers(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := abi.Pack("balance"); err != nil {
|
if _, err := abi.Pack("balance"); err != nil {
|
||||||
@@ -235,26 +137,69 @@ func TestTestNumbers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTestString(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("string", "hello world"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestBool(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("bool", true); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestSlice(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := make([]uint64, 2)
|
||||||
|
if _, err := abi.Pack("uint64[2]", slice); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("uint64[]", slice); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMethodSignature(t *testing.T) {
|
func TestMethodSignature(t *testing.T) {
|
||||||
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
String, _ := NewType("string", nil)
|
||||||
|
m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
|
||||||
exp := "foo(string,string)"
|
exp := "foo(string,string)"
|
||||||
if m.Sig != exp {
|
if m.Sig() != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
t.Error("signature mismatch", exp, "!=", m.Sig())
|
||||||
}
|
}
|
||||||
|
|
||||||
idexp := crypto.Keccak256([]byte(exp))[:4]
|
idexp := crypto.Keccak256([]byte(exp))[:4]
|
||||||
if !bytes.Equal(m.ID, idexp) {
|
if !bytes.Equal(m.Id(), idexp) {
|
||||||
t.Errorf("expected ids to match %x != %x", m.ID, idexp)
|
t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
|
||||||
}
|
}
|
||||||
|
|
||||||
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
|
uintt, _ := NewType("uint256", nil)
|
||||||
|
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
|
||||||
exp = "foo(uint256)"
|
exp = "foo(uint256)"
|
||||||
if m.Sig != exp {
|
if m.Sig() != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
t.Error("signature mismatch", exp, "!=", m.Sig())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method with tuple arguments
|
// Method with tuple arguments
|
||||||
s, _ := NewType("tuple", "", []ArgumentMarshaling{
|
s, _ := NewType("tuple", []ArgumentMarshaling{
|
||||||
{Name: "a", Type: "int256"},
|
{Name: "a", Type: "int256"},
|
||||||
{Name: "b", Type: "int256[]"},
|
{Name: "b", Type: "int256[]"},
|
||||||
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
|
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
|
||||||
@@ -266,54 +211,18 @@ func TestMethodSignature(t *testing.T) {
|
|||||||
{Name: "y", Type: "int256"},
|
{Name: "y", Type: "int256"},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
|
m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
|
||||||
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
||||||
if m.Sig != exp {
|
if m.Sig() != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
t.Error("signature mismatch", exp, "!=", m.Sig())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOverloadedMethodSignature(t *testing.T) {
|
|
||||||
json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`
|
|
||||||
abi, err := JSON(strings.NewReader(json))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
check := func(name string, expect string, method bool) {
|
|
||||||
if method {
|
|
||||||
if abi.Methods[name].Sig != expect {
|
|
||||||
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if abi.Events[name].Sig != expect {
|
|
||||||
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check("foo", "foo(uint256,uint256)", true)
|
|
||||||
check("foo0", "foo(uint256)", true)
|
|
||||||
check("bar", "bar(uint256)", false)
|
|
||||||
check("bar0", "bar(uint256,uint256)", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check("MyError", "MyError(uint256)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiPack(t *testing.T) {
|
func TestMultiPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4]
|
sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4]
|
||||||
@@ -323,8 +232,10 @@ func TestMultiPack(t *testing.T) {
|
|||||||
|
|
||||||
packed, err := abi.Pack("bar", uint32(10), uint16(11))
|
packed, err := abi.Pack("bar", uint32(10), uint16(11))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(packed, sig) {
|
if !bytes.Equal(packed, sig) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
@@ -335,11 +246,11 @@ func ExampleJSON() {
|
|||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
out, err := abi.Pack("isBar", common.HexToAddress("01"))
|
out, err := abi.Pack("isBar", common.HexToAddress("01"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%x\n", out)
|
fmt.Printf("%x\n", out)
|
||||||
@@ -476,7 +387,15 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
const definition = `[
|
||||||
|
{ "type" : "function", "name" : "fixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "fixedArrBytes", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "mixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type": "uint256[2]" }, { "name" : "dynArr", "type": "uint256[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "doubleFixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "fixedArr2", "type": "uint256[3]" } ] },
|
||||||
|
{ "type" : "function", "name" : "multipleMixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] }
|
||||||
|
]`
|
||||||
|
|
||||||
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -623,7 +542,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||||||
strvalue = common.RightPadBytes([]byte(strin), 32)
|
strvalue = common.RightPadBytes([]byte(strin), 32)
|
||||||
fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
|
fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
|
||||||
fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
|
fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
|
||||||
dynarroffset = math.U256Bytes(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
|
dynarroffset = U256(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
|
||||||
dynarrlength = make([]byte, 32)
|
dynarrlength = make([]byte, 32)
|
||||||
dynarrlength[31] = byte(len(dynarrin))
|
dynarrlength[31] = byte(len(dynarrin))
|
||||||
dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
||||||
@@ -650,7 +569,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultFunctionParsing(t *testing.T) {
|
func TestDefaultFunctionParsing(t *testing.T) {
|
||||||
const definition = `[{ "name" : "balance", "type" : "function" }]`
|
const definition = `[{ "name" : "balance" }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -670,7 +589,9 @@ func TestBareEvents(t *testing.T) {
|
|||||||
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
||||||
]`
|
]`
|
||||||
|
|
||||||
tuple, _ := NewType("tuple", "", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
arg0, _ := NewType("uint256", nil)
|
||||||
|
arg1, _ := NewType("address", nil)
|
||||||
|
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
||||||
|
|
||||||
expectedEvents := map[string]struct {
|
expectedEvents := map[string]struct {
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
@@ -679,12 +600,12 @@ func TestBareEvents(t *testing.T) {
|
|||||||
"balance": {false, nil},
|
"balance": {false, nil},
|
||||||
"anon": {true, nil},
|
"anon": {true, nil},
|
||||||
"args": {false, []Argument{
|
"args": {false, []Argument{
|
||||||
{Name: "arg0", Type: Uint256, Indexed: false},
|
{Name: "arg0", Type: arg0, Indexed: false},
|
||||||
{Name: "arg1", Type: Address, Indexed: true},
|
{Name: "arg1", Type: arg1, Indexed: true},
|
||||||
}},
|
}},
|
||||||
"tuple": {false, []Argument{
|
"tuple": {false, []Argument{
|
||||||
{Name: "t", Type: tuple, Indexed: false},
|
{Name: "t", Type: tuple, Indexed: false},
|
||||||
{Name: "arg1", Type: Address, Indexed: true},
|
{Name: "arg1", Type: arg1, Indexed: true},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,7 +646,6 @@ func TestBareEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestUnpackEvent is based on this contract:
|
// TestUnpackEvent is based on this contract:
|
||||||
//
|
|
||||||
// contract T {
|
// contract T {
|
||||||
// event received(address sender, uint amount, bytes memo);
|
// event received(address sender, uint amount, bytes memo);
|
||||||
// event receivedAddr(address sender);
|
// event receivedAddr(address sender);
|
||||||
@@ -734,9 +654,7 @@ func TestBareEvents(t *testing.T) {
|
|||||||
// receivedAddr(msg.sender);
|
// receivedAddr(msg.sender);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
||||||
//
|
|
||||||
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
||||||
func TestUnpackEvent(t *testing.T) {
|
func TestUnpackEvent(t *testing.T) {
|
||||||
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
||||||
@@ -761,7 +679,7 @@ func TestUnpackEvent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var ev ReceivedEvent
|
var ev ReceivedEvent
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&ev, "received", data)
|
err = abi.Unpack(&ev, "received", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -770,215 +688,52 @@ func TestUnpackEvent(t *testing.T) {
|
|||||||
Sender common.Address
|
Sender common.Address
|
||||||
}
|
}
|
||||||
var receivedAddrEv ReceivedAddrEvent
|
var receivedAddrEv ReceivedAddrEvent
|
||||||
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
|
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedMap := map[string]interface{}{}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedAddrMap := map[string]interface{}{}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests a method with no outputs
|
|
||||||
receiveMap := map[string]interface{}{}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests a method with only outputs
|
|
||||||
sendMap := map[string]interface{}{}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests a method with outputs and inputs
|
|
||||||
getMap := map[string]interface{}{}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestABI_MethodById(t *testing.T) {
|
func TestABI_MethodById(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
const abiJSON = `[
|
||||||
|
{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
|
||||||
|
{"type":"event","name":"received","anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}]},
|
||||||
|
{"type":"function","name":"fixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"}]},
|
||||||
|
{"type":"function","name":"fixedArrBytes","constant":true,"inputs":[{"name":"str","type":"bytes"},{"name":"fixedArr","type":"uint256[2]"}]},
|
||||||
|
{"type":"function","name":"mixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"}]},
|
||||||
|
{"type":"function","name":"doubleFixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"fixedArr2","type":"uint256[3]"}]},
|
||||||
|
{"type":"function","name":"multipleMixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"},{"name":"fixedArr2","type":"uint256[3]"}]},
|
||||||
|
{"type":"function","name":"balance","constant":true},
|
||||||
|
{"type":"function","name":"send","constant":false,"inputs":[{"name":"amount","type":"uint256"}]},
|
||||||
|
{"type":"function","name":"test","constant":false,"inputs":[{"name":"number","type":"uint32"}]},
|
||||||
|
{"type":"function","name":"string","constant":false,"inputs":[{"name":"inputs","type":"string"}]},
|
||||||
|
{"type":"function","name":"bool","constant":false,"inputs":[{"name":"inputs","type":"bool"}]},
|
||||||
|
{"type":"function","name":"address","constant":false,"inputs":[{"name":"inputs","type":"address"}]},
|
||||||
|
{"type":"function","name":"uint64[2]","constant":false,"inputs":[{"name":"inputs","type":"uint64[2]"}]},
|
||||||
|
{"type":"function","name":"uint64[]","constant":false,"inputs":[{"name":"inputs","type":"uint64[]"}]},
|
||||||
|
{"type":"function","name":"foo","constant":false,"inputs":[{"name":"inputs","type":"uint32"}]},
|
||||||
|
{"type":"function","name":"bar","constant":false,"inputs":[{"name":"inputs","type":"uint32"},{"name":"string","type":"uint16"}]},
|
||||||
|
{"type":"function","name":"_slice","constant":false,"inputs":[{"name":"inputs","type":"uint32[2]"}]},
|
||||||
|
{"type":"function","name":"__slice256","constant":false,"inputs":[{"name":"inputs","type":"uint256[2]"}]},
|
||||||
|
{"type":"function","name":"sliceAddress","constant":false,"inputs":[{"name":"inputs","type":"address[]"}]},
|
||||||
|
{"type":"function","name":"sliceMultiAddress","constant":false,"inputs":[{"name":"a","type":"address[]"},{"name":"b","type":"address[]"}]}
|
||||||
|
]
|
||||||
|
`
|
||||||
|
abi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
for name, m := range abi.Methods {
|
for name, m := range abi.Methods {
|
||||||
a := fmt.Sprintf("%v", m)
|
a := fmt.Sprintf("%v", m)
|
||||||
m2, err := abi.MethodById(m.ID)
|
m2, err := abi.MethodById(m.Id())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to look up ABI method: %v", err)
|
t.Fatalf("Failed to look up ABI method: %v", err)
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("%v", m2)
|
b := fmt.Sprintf("%v", m2)
|
||||||
if a != b {
|
if a != b {
|
||||||
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
|
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// test unsuccessful lookups
|
|
||||||
if _, err = abi.MethodById(crypto.Keccak256()); err == nil {
|
|
||||||
t.Error("Expected error: no method with this id")
|
|
||||||
}
|
|
||||||
// Also test empty
|
// Also test empty
|
||||||
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
||||||
t.Errorf("Expected error, too short to decode data")
|
t.Errorf("Expected error, too short to decode data")
|
||||||
@@ -990,177 +745,3 @@ func TestABI_MethodById(t *testing.T) {
|
|||||||
t.Errorf("Expected error, nil is short to decode data")
|
t.Errorf("Expected error, nil is short to decode data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABI_EventById(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
json string
|
|
||||||
event string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "",
|
|
||||||
json: `[
|
|
||||||
{"type":"event","name":"received","anonymous":false,"inputs":[
|
|
||||||
{"indexed":false,"name":"sender","type":"address"},
|
|
||||||
{"indexed":false,"name":"amount","type":"uint256"},
|
|
||||||
{"indexed":false,"name":"memo","type":"bytes"}
|
|
||||||
]
|
|
||||||
}]`,
|
|
||||||
event: "received(address,uint256,bytes)",
|
|
||||||
}, {
|
|
||||||
name: "",
|
|
||||||
json: `[
|
|
||||||
{ "constant": true, "inputs": [], "name": "name", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "approve", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
|
|
||||||
{ "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
|
|
||||||
{ "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" },
|
|
||||||
{ "constant": true, "inputs": [ { "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" } ], "name": "allowance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" },
|
|
||||||
{ "payable": true, "stateMutability": "payable", "type": "fallback" },
|
|
||||||
{ "anonymous": false, "inputs": [ { "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" },
|
|
||||||
{ "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }
|
|
||||||
]`,
|
|
||||||
event: "Transfer(address,address,uint256)",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for testnum, test := range tests {
|
|
||||||
abi, err := JSON(strings.NewReader(test.json))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
topic := test.event
|
|
||||||
topicID := crypto.Keccak256Hash([]byte(topic))
|
|
||||||
|
|
||||||
event, err := abi.EventByID(topicID)
|
|
||||||
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 {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
// event send();
|
|
||||||
// }
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
event, ok := contractAbi.Events["send"]
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpackRevert(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
input string
|
|
||||||
expect string
|
|
||||||
expectErr error
|
|
||||||
}{
|
|
||||||
{"", "", errors.New("invalid data for unpacking")},
|
|
||||||
{"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))
|
|
||||||
if c.expectErr != nil {
|
|
||||||
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -37,12 +36,11 @@ type Arguments []Argument
|
|||||||
type ArgumentMarshaling struct {
|
type ArgumentMarshaling struct {
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
InternalType string
|
|
||||||
Components []ArgumentMarshaling
|
Components []ArgumentMarshaling
|
||||||
Indexed bool
|
Indexed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
// UnmarshalJSON implements json.Unmarshaler interface
|
||||||
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
var arg ArgumentMarshaling
|
var arg ArgumentMarshaling
|
||||||
err := json.Unmarshal(data, &arg)
|
err := json.Unmarshal(data, &arg)
|
||||||
@@ -50,7 +48,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
|||||||
return fmt.Errorf("argument json err: %v", err)
|
return fmt.Errorf("argument json err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
|
argument.Type, err = NewType(arg.Type, arg.Components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -60,7 +58,19 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NonIndexed returns the arguments with indexed arguments filtered out.
|
// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
|
||||||
|
// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
|
||||||
|
func (arguments Arguments) LengthNonIndexed() int {
|
||||||
|
out := 0
|
||||||
|
for _, arg := range arguments {
|
||||||
|
if !arg.Indexed {
|
||||||
|
out++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonIndexed returns the arguments with indexed arguments filtered out
|
||||||
func (arguments Arguments) NonIndexed() Arguments {
|
func (arguments Arguments) NonIndexed() Arguments {
|
||||||
var ret []Argument
|
var ret []Argument
|
||||||
for _, arg := range arguments {
|
for _, arg := range arguments {
|
||||||
@@ -71,125 +81,170 @@ func (arguments Arguments) NonIndexed() Arguments {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[].
|
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
|
||||||
func (arguments Arguments) isTuple() bool {
|
func (arguments Arguments) isTuple() bool {
|
||||||
return len(arguments) > 1
|
return len(arguments) > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack performs the operation hexdata -> Go format.
|
// Unpack performs the operation hexdata -> Go format
|
||||||
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if len(data) == 0 {
|
// make sure the passed value is arguments pointer
|
||||||
if len(arguments.NonIndexed()) != 0 {
|
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||||
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||||
}
|
|
||||||
return make([]interface{}, 0), nil
|
|
||||||
}
|
|
||||||
return arguments.UnpackValues(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value.
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
if len(data) == 0 {
|
|
||||||
if len(arguments.NonIndexed()) != 0 {
|
|
||||||
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
|
||||||
}
|
|
||||||
return nil // Nothing to unmarshal, return
|
|
||||||
}
|
}
|
||||||
marshalledValues, err := arguments.UnpackValues(data)
|
marshalledValues, err := arguments.UnpackValues(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i, arg := range arguments.NonIndexed() {
|
if arguments.isTuple() {
|
||||||
v[arg.Name] = marshalledValues[i]
|
return arguments.unpackTuple(v, marshalledValues)
|
||||||
|
}
|
||||||
|
return arguments.unpackAtomic(v, marshalledValues[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack sets the unmarshalled value to go format.
|
||||||
|
// Note the dst here must be settable.
|
||||||
|
func unpack(t *Type, dst interface{}, src interface{}) error {
|
||||||
|
var (
|
||||||
|
dstVal = reflect.ValueOf(dst).Elem()
|
||||||
|
srcVal = reflect.ValueOf(src)
|
||||||
|
)
|
||||||
|
|
||||||
|
if t.T != TupleTy && !((t.T == SliceTy || t.T == ArrayTy) && t.Elem.T == TupleTy) {
|
||||||
|
return set(dstVal, srcVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.T {
|
||||||
|
case TupleTy:
|
||||||
|
if dstVal.Kind() != reflect.Struct {
|
||||||
|
return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
|
||||||
|
}
|
||||||
|
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, elem := range t.TupleElems {
|
||||||
|
fname := fieldmap[t.TupleRawNames[i]]
|
||||||
|
field := dstVal.FieldByName(fname)
|
||||||
|
if !field.IsValid() {
|
||||||
|
return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
|
||||||
|
}
|
||||||
|
if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case SliceTy:
|
||||||
|
if dstVal.Kind() != reflect.Slice {
|
||||||
|
return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
|
||||||
|
}
|
||||||
|
slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
|
||||||
|
for i := 0; i < slice.Len(); i++ {
|
||||||
|
if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dstVal.Set(slice)
|
||||||
|
case ArrayTy:
|
||||||
|
if dstVal.Kind() != reflect.Array {
|
||||||
|
return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
|
||||||
|
}
|
||||||
|
array := reflect.New(dstVal.Type()).Elem()
|
||||||
|
for i := 0; i < array.Len(); i++ {
|
||||||
|
if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dstVal.Set(array)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy performs the operation go format -> provided struct.
|
|
||||||
func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
|
||||||
// make sure the passed value is arguments pointer
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
return nil // Nothing to copy, return
|
|
||||||
}
|
|
||||||
if arguments.isTuple() {
|
|
||||||
return arguments.copyTuple(v, values)
|
|
||||||
}
|
|
||||||
return arguments.copyAtomic(v, values[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
||||||
func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
|
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
||||||
dst := reflect.ValueOf(v).Elem()
|
if arguments.LengthNonIndexed() == 0 {
|
||||||
src := reflect.ValueOf(marshalledValues)
|
return nil
|
||||||
|
|
||||||
if dst.Kind() == reflect.Struct {
|
|
||||||
return set(dst.Field(0), src)
|
|
||||||
}
|
}
|
||||||
return set(dst, src)
|
argument := arguments.NonIndexed()[0]
|
||||||
}
|
elem := reflect.ValueOf(v).Elem()
|
||||||
|
|
||||||
// copyTuple copies a batch of values from marshalledValues to v.
|
if elem.Kind() == reflect.Struct {
|
||||||
func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error {
|
fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
|
||||||
value := reflect.ValueOf(v).Elem()
|
|
||||||
nonIndexedArgs := arguments.NonIndexed()
|
|
||||||
|
|
||||||
switch value.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
argNames := make([]string, len(nonIndexedArgs))
|
|
||||||
for i, arg := range nonIndexedArgs {
|
|
||||||
argNames[i] = arg.Name
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
abi2struct, err := mapArgNamesToStructFields(argNames, value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i, arg := range nonIndexedArgs {
|
field := elem.FieldByName(fieldmap[argument.Name])
|
||||||
|
if !field.IsValid() {
|
||||||
|
return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
|
||||||
|
}
|
||||||
|
return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
|
||||||
|
}
|
||||||
|
return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
||||||
|
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
||||||
|
var (
|
||||||
|
value = reflect.ValueOf(v).Elem()
|
||||||
|
typ = value.Type()
|
||||||
|
kind = value.Kind()
|
||||||
|
)
|
||||||
|
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the interface is a struct, get of abi->struct_field mapping
|
||||||
|
var abi2struct map[string]string
|
||||||
|
if kind == reflect.Struct {
|
||||||
|
var (
|
||||||
|
argNames []string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for _, arg := range arguments.NonIndexed() {
|
||||||
|
argNames = append(argNames, arg.Name)
|
||||||
|
}
|
||||||
|
abi2struct, err = mapArgNamesToStructFields(argNames, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, arg := range arguments.NonIndexed() {
|
||||||
|
switch kind {
|
||||||
|
case reflect.Struct:
|
||||||
field := value.FieldByName(abi2struct[arg.Name])
|
field := value.FieldByName(abi2struct[arg.Name])
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
||||||
}
|
}
|
||||||
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
|
if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
if value.Len() < len(marshalledValues) {
|
if value.Len() < i {
|
||||||
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
||||||
}
|
}
|
||||||
for i := range nonIndexedArgs {
|
v := value.Index(i)
|
||||||
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
|
if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
|
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
|
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
|
||||||
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
||||||
// values. An atomic argument will be a list with one element.
|
// values. An atomic argument will be a list with one element.
|
||||||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||||
nonIndexedArgs := arguments.NonIndexed()
|
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
|
||||||
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, arg := range nonIndexedArgs {
|
for index, arg := range arguments.NonIndexed() {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
||||||
// If we have a static array, like [3]uint256, these are coded as
|
// If we have a static array, like [3]uint256, these are coded as
|
||||||
// just like uint256,uint256,uint256.
|
// just like uint256,uint256,uint256.
|
||||||
@@ -207,23 +262,26 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
|||||||
// coded as just like uint256,bool,uint256
|
// coded as just like uint256,bool,uint256
|
||||||
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
retval = append(retval, marshalledValue)
|
retval = append(retval, marshalledValue)
|
||||||
}
|
}
|
||||||
return retval, nil
|
return retval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackValues performs the operation Go format -> Hexdata.
|
// PackValues performs the operation Go format -> Hexdata
|
||||||
// It is the semantic opposite of UnpackValues.
|
// It is the semantic opposite of UnpackValues
|
||||||
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
|
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
|
||||||
return arguments.Pack(args...)
|
return arguments.Pack(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack performs the operation Go format -> Hexdata.
|
// Pack performs the operation Go format -> Hexdata
|
||||||
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||||
// Make sure arguments match up and pack them
|
// Make sure arguments match up and pack them
|
||||||
abiArgs := arguments
|
abiArgs := arguments
|
||||||
if len(args) != len(abiArgs) {
|
if len(args) != len(abiArgs) {
|
||||||
return nil, fmt.Errorf("argument count mismatch: got %d for %d", len(args), len(abiArgs))
|
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
|
||||||
}
|
}
|
||||||
// variable input is the output appended at the end of packed
|
// variable input is the output appended at the end of packed
|
||||||
// output. This is used for strings and bytes types input.
|
// output. This is used for strings and bytes types input.
|
||||||
|
|||||||
@@ -17,34 +17,21 @@
|
|||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/external"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoChainID is returned whenever the user failed to specify a chain id.
|
|
||||||
var ErrNoChainID = errors.New("no chain id specified")
|
|
||||||
|
|
||||||
// ErrNotAuthorized is returned when an account is not properly unlocked.
|
|
||||||
var ErrNotAuthorized = errors.New("not authorized to sign this account")
|
|
||||||
|
|
||||||
// NewTransactor is a utility method to easily create a transaction signer from
|
// NewTransactor is a utility method to easily create a transaction signer from
|
||||||
// an encrypted json key stream and the associated passphrase.
|
// an encrypted json key stream and the associated passphrase.
|
||||||
//
|
|
||||||
// Deprecated: Use NewTransactorWithChainID instead.
|
|
||||||
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
||||||
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
|
json, err := ioutil.ReadAll(keyin)
|
||||||
json, err := io.ReadAll(keyin)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -55,42 +42,15 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
|||||||
return NewKeyedTransactor(key.PrivateKey), nil
|
return NewKeyedTransactor(key.PrivateKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
|
|
||||||
// an decrypted key from a keystore.
|
|
||||||
//
|
|
||||||
// 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) {
|
|
||||||
if address != account.Address {
|
|
||||||
return nil, ErrNotAuthorized
|
|
||||||
}
|
|
||||||
signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return tx.WithSignature(signer, signature)
|
|
||||||
},
|
|
||||||
Context: context.Background(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeyedTransactor is a utility method to easily create a transaction signer
|
// NewKeyedTransactor is a utility method to easily create a transaction signer
|
||||||
// from a single private key.
|
// from a single private key.
|
||||||
//
|
|
||||||
// Deprecated: Use NewKeyedTransactorWithChainID instead.
|
|
||||||
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||||
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
|
|
||||||
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
signer := types.HomesteadSigner{}
|
|
||||||
return &TransactOpts{
|
return &TransactOpts{
|
||||||
From: keyAddr,
|
From: keyAddr,
|
||||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
if address != keyAddr {
|
if address != keyAddr {
|
||||||
return nil, ErrNotAuthorized
|
return nil, errors.New("not authorized to sign this account")
|
||||||
}
|
}
|
||||||
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
|
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,82 +58,5 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
|||||||
}
|
}
|
||||||
return tx.WithSignature(signer, signature)
|
return tx.WithSignature(signer, signature)
|
||||||
},
|
},
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTransactorWithChainID is a utility method to easily create a transaction signer from
|
|
||||||
// an encrypted json key stream and the associated passphrase.
|
|
||||||
func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
|
|
||||||
json, err := io.ReadAll(keyin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
key, err := keystore.DecryptKey(json, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from
|
|
||||||
// an decrypted key from a keystore.
|
|
||||||
func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) {
|
|
||||||
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) {
|
|
||||||
if address != account.Address {
|
|
||||||
return nil, ErrNotAuthorized
|
|
||||||
}
|
|
||||||
signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return tx.WithSignature(signer, signature)
|
|
||||||
},
|
|
||||||
Context: context.Background(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer
|
|
||||||
// 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) {
|
|
||||||
if address != keyAddr {
|
|
||||||
return nil, ErrNotAuthorized
|
|
||||||
}
|
|
||||||
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return tx.WithSignature(signer, signature)
|
|
||||||
},
|
|
||||||
Context: context.Background(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClefTransactor is a utility method to easily create a transaction signer
|
|
||||||
// with a clef backend.
|
|
||||||
func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts {
|
|
||||||
return &TransactOpts{
|
|
||||||
From: account.Address,
|
|
||||||
Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
|
|
||||||
if address != account.Address {
|
|
||||||
return nil, ErrNotAuthorized
|
|
||||||
}
|
|
||||||
return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
|
|
||||||
},
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,23 +32,22 @@ var (
|
|||||||
// have any code associated with it (i.e. suicided).
|
// have any code associated with it (i.e. suicided).
|
||||||
ErrNoCode = errors.New("no contract code at given address")
|
ErrNoCode = errors.New("no contract code at given address")
|
||||||
|
|
||||||
// ErrNoPendingState is raised when attempting to perform a pending state action
|
// This error is raised when attempting to perform a pending state action
|
||||||
// on a backend that doesn't implement PendingContractCaller.
|
// on a backend that doesn't implement PendingContractCaller.
|
||||||
ErrNoPendingState = errors.New("backend does not support pending state")
|
ErrNoPendingState = errors.New("backend does not support pending state")
|
||||||
|
|
||||||
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
// This error is returned by WaitDeployed if contract creation leaves an
|
||||||
// an empty contract behind.
|
// empty contract behind.
|
||||||
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContractCaller defines the methods needed to allow operating with a contract on a read
|
// ContractCaller defines the methods needed to allow operating with contract on a read
|
||||||
// only basis.
|
// only basis.
|
||||||
type ContractCaller interface {
|
type ContractCaller interface {
|
||||||
// CodeAt returns the code of the given account. This is needed to differentiate
|
// CodeAt returns the code of the given account. This is needed to differentiate
|
||||||
// between contract internal errors and the local chain being out of sync.
|
// between contract internal errors and the local chain being out of sync.
|
||||||
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
|
// ContractCall executes an Ethereum contract call with the specified data as the
|
||||||
// CallContract executes an Ethereum contract call with the specified data as the
|
|
||||||
// input.
|
// input.
|
||||||
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||||
}
|
}
|
||||||
@@ -59,41 +58,28 @@ type ContractCaller interface {
|
|||||||
type PendingContractCaller interface {
|
type PendingContractCaller interface {
|
||||||
// PendingCodeAt returns the code of the given account in the pending state.
|
// PendingCodeAt returns the code of the given account in the pending state.
|
||||||
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
||||||
|
|
||||||
// PendingCallContract executes an Ethereum contract call against the pending state.
|
// PendingCallContract executes an Ethereum contract call against the pending state.
|
||||||
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
|
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractTransactor defines the methods needed to allow operating with a contract
|
// ContractTransactor defines the methods needed to allow operating with contract
|
||||||
// on a write only basis. Besides the transacting method, the remainder are helpers
|
// on a write only basis. Beside the transacting method, the remainder are helpers
|
||||||
// used when the user does not provide some needed values, but rather leaves it up
|
// used when the user does not provide some needed values, but rather leaves it up
|
||||||
// to the transactor to decide.
|
// to the transactor to decide.
|
||||||
type ContractTransactor interface {
|
type ContractTransactor interface {
|
||||||
// HeaderByNumber returns a block header from the current canonical chain. If
|
|
||||||
// number is nil, the latest known header is returned.
|
|
||||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
|
||||||
|
|
||||||
// PendingCodeAt returns the code of the given account in the pending state.
|
// PendingCodeAt returns the code of the given account in the pending state.
|
||||||
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
||||||
|
|
||||||
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||||
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||||
|
|
||||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||||
// execution of a transaction.
|
// execution of a transaction.
|
||||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
||||||
|
|
||||||
// SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
|
|
||||||
// a timely execution of a transaction.
|
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
|
||||||
|
|
||||||
// EstimateGas tries to estimate the gas needed to execute a specific
|
// EstimateGas tries to estimate the gas needed to execute a specific
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
// transaction based on the current pending state of the backend blockchain.
|
||||||
// There is no guarantee that this is the true gas limit requirement as other
|
// There is no guarantee that this is the true gas limit requirement as other
|
||||||
// transactions may be added or removed by miners, but it should provide a basis
|
// transactions may be added or removed by miners, but it should provide a basis
|
||||||
// for setting a reasonable default.
|
// for setting a reasonable default.
|
||||||
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
|
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
|
||||||
|
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
// SendTransaction injects the transaction into the pending pool for execution.
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
@@ -40,94 +38,59 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
|
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
var (
|
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
|
||||||
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
||||||
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
|
||||||
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
|
||||||
)
|
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
// the background. Its main purpose is to allow for easy testing of contract bindings.
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
||||||
// Simulated backend implements the following interfaces:
|
|
||||||
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
|
|
||||||
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
|
|
||||||
type SimulatedBackend struct {
|
type SimulatedBackend struct {
|
||||||
database ethdb.Database // In memory database to store our testing data
|
database ethdb.Database // In memory database to store our testing data
|
||||||
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
pendingBlock *types.Block // Currently pending block that will be imported on request
|
||||||
pendingState *state.StateDB // Currently pending state that will be the active on request
|
pendingState *state.StateDB // Currently pending state that will be the active on on request
|
||||||
pendingReceipts types.Receipts // Currently receipts for the pending block
|
|
||||||
|
|
||||||
events *filters.EventSystem // for filtering log events live
|
events *filters.EventSystem // Event system for filtering log events live
|
||||||
filterSystem *filters.FilterSystem // for filtering database logs
|
|
||||||
|
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||||
// and uses a simulated blockchain for testing purposes.
|
// for testing purposes.
|
||||||
// A simulated backend always uses chainID 1337.
|
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
database := ethdb.NewMemDatabase()
|
||||||
genesis := core.Genesis{
|
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||||
Config: params.AllEthashProtocolChanges,
|
genesis.MustCommit(database)
|
||||||
GasLimit: gasLimit,
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
Alloc: alloc,
|
|
||||||
}
|
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
|
|
||||||
backend := &SimulatedBackend{
|
backend := &SimulatedBackend{
|
||||||
database: database,
|
database: database,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
config: genesis.Config,
|
config: genesis.Config,
|
||||||
|
events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
|
||||||
}
|
}
|
||||||
|
backend.rollback()
|
||||||
filterBackend := &filterBackend{database, blockchain, backend}
|
|
||||||
backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{})
|
|
||||||
backend.events = filters.NewEventSystem(backend.filterSystem, false)
|
|
||||||
|
|
||||||
backend.rollback(blockchain.CurrentBlock())
|
|
||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
|
||||||
// for testing purposes.
|
|
||||||
// A simulated backend always uses chainID 1337.
|
|
||||||
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
|
||||||
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close terminates the underlying blockchain's update loop.
|
|
||||||
func (b *SimulatedBackend) Close() error {
|
|
||||||
b.blockchain.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
// Commit imports all the pending transactions as a single block and starts a
|
||||||
// fresh new state.
|
// fresh new state.
|
||||||
func (b *SimulatedBackend) Commit() common.Hash {
|
func (b *SimulatedBackend) Commit() {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
||||||
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
||||||
}
|
}
|
||||||
blockHash := b.pendingBlock.Hash()
|
b.rollback()
|
||||||
|
|
||||||
// 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.
|
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||||
@@ -135,53 +98,15 @@ func (b *SimulatedBackend) Rollback() {
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
b.rollback(b.blockchain.CurrentBlock())
|
b.rollback()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SimulatedBackend) rollback(parent *types.Block) {
|
func (b *SimulatedBackend) rollback() {
|
||||||
blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
||||||
}
|
|
||||||
|
|
||||||
// Fork creates a side-chain that can be used to simulate reorgs.
|
|
||||||
//
|
|
||||||
// This function should be called with the ancestor block where the new side
|
|
||||||
// chain should be started. Transactions (old and new) can then be applied on
|
|
||||||
// top and Commit-ed.
|
|
||||||
//
|
|
||||||
// Note, the side-chain will only become canonical (and trigger the events) when
|
|
||||||
// it becomes longer. Until then CallContract will still operate on the current
|
|
||||||
// canonical chain.
|
|
||||||
//
|
|
||||||
// There is a % chance that the side chain becomes canonical at the same length
|
|
||||||
// to simulate live network behavior.
|
|
||||||
func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return b.blockchain.State()
|
|
||||||
}
|
|
||||||
block, err := b.blockByNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b.blockchain.StateAt(block.Root())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeAt returns the code associated with a certain account in the blockchain.
|
// CodeAt returns the code associated with a certain account in the blockchain.
|
||||||
@@ -189,12 +114,11 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
if err != nil {
|
return nil, errBlockNumberUnsupported
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
return stateDB.GetCode(contract), nil
|
return statedb.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
||||||
@@ -202,12 +126,11 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
if err != nil {
|
return nil, errBlockNumberUnsupported
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
return stateDB.GetBalance(contract), nil
|
return statedb.GetBalance(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NonceAt returns the nonce of a certain account in the blockchain.
|
// NonceAt returns the nonce of a certain account in the blockchain.
|
||||||
@@ -215,12 +138,11 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
if err != nil {
|
return 0, errBlockNumberUnsupported
|
||||||
return 0, err
|
|
||||||
}
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
return stateDB.GetNonce(contract), nil
|
return statedb.GetNonce(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageAt returns the value of key in the storage of an account in the blockchain.
|
// StorageAt returns the value of key in the storage of an account in the blockchain.
|
||||||
@@ -228,166 +150,20 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
if err != nil {
|
return nil, errBlockNumberUnsupported
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
val := stateDB.GetState(contract, key)
|
val := statedb.GetState(contract, key)
|
||||||
return val[:], nil
|
return val[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionReceipt returns the receipt of a transaction.
|
// TransactionReceipt returns the receipt of a transaction.
|
||||||
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
b.mu.Lock()
|
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash)
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
|
|
||||||
if receipt == nil {
|
|
||||||
return nil, ethereum.NotFound
|
|
||||||
}
|
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionByHash checks the pool of pending transactions in addition to the
|
|
||||||
// blockchain. The isPending return value indicates whether the transaction has been
|
|
||||||
// mined yet. Note that the transaction may not be part of the canonical chain even if
|
|
||||||
// it's not pending.
|
|
||||||
func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
tx := b.pendingBlock.Transaction(txHash)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockByHash retrieves a block based on the block hash.
|
|
||||||
func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.blockByHash(ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockByHash retrieves a block based on the block hash without Locking.
|
|
||||||
func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
|
||||||
if hash == b.pendingBlock.Hash() {
|
|
||||||
return b.pendingBlock, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(hash)
|
|
||||||
if block != nil {
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockByNumber retrieves a block from the database by number, caching it
|
|
||||||
// (associated with its hash) if found.
|
|
||||||
func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.blockByNumber(ctx, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockByNumber retrieves a block from the database by number, caching it
|
|
||||||
// (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.blockchain.CurrentBlock(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderByHash returns a block header from the current canonical chain.
|
|
||||||
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if hash == b.pendingBlock.Hash() {
|
|
||||||
return b.pendingBlock.Header(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := b.blockchain.GetHeaderByHash(hash)
|
|
||||||
if header == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderByNumber returns a block header from the current canonical chain. If number is
|
|
||||||
// nil, the latest known header is returned.
|
|
||||||
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
|
|
||||||
return b.blockchain.CurrentHeader(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionCount returns the number of transactions in a given block.
|
|
||||||
func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockHash == b.pendingBlock.Hash() {
|
|
||||||
return uint(b.pendingBlock.Transactions().Len()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(blockHash)
|
|
||||||
if block == nil {
|
|
||||||
return uint(0), errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint(block.Transactions().Len()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionInBlock returns the transaction for a specific block at a specific index.
|
|
||||||
func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockHash == b.pendingBlock.Hash() {
|
|
||||||
transactions := b.pendingBlock.Transactions()
|
|
||||||
if uint(len(transactions)) < index+1 {
|
|
||||||
return nil, errTransactionDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactions[index], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(blockHash)
|
|
||||||
if block == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
transactions := block.Transactions()
|
|
||||||
if uint(len(transactions)) < index+1 {
|
|
||||||
return nil, errTransactionDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactions[index], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingCodeAt returns the code associated with an account in the pending state.
|
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
@@ -396,36 +172,6 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
|
|||||||
return b.pendingState.GetCode(contract), nil
|
return b.pendingState.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// revertError is an API error that encompasses an EVM revert with JSON error
|
|
||||||
// code and a binary data blob.
|
|
||||||
type revertError struct {
|
|
||||||
error
|
|
||||||
reason string // revert reason hex encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorCode returns the JSON error code for a revert.
|
|
||||||
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
|
|
||||||
func (e *revertError) ErrorCode() int {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorData returns the hex encoded revert reason.
|
|
||||||
func (e *revertError) ErrorData() interface{} {
|
|
||||||
return e.reason
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallContract executes a contract call.
|
// CallContract executes a contract call.
|
||||||
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
@@ -434,19 +180,12 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
|||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
return nil, errBlockNumberUnsupported
|
return nil, errBlockNumberUnsupported
|
||||||
}
|
}
|
||||||
stateDB, err := b.blockchain.State()
|
state, err := b.blockchain.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
if err != nil {
|
return rval, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
|
||||||
if len(res.Revert()) > 0 {
|
|
||||||
return nil, newRevertError(res)
|
|
||||||
}
|
|
||||||
return res.Return(), res.Err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
@@ -455,15 +194,8 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
|||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
if err != nil {
|
return rval, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
|
||||||
if len(res.Revert()) > 0 {
|
|
||||||
return nil, newRevertError(res)
|
|
||||||
}
|
|
||||||
return res.Return(), res.Err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
@@ -478,18 +210,6 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
|
|||||||
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
||||||
// chain doesn't have miners, we just return a gas price of 1 for any call.
|
// chain doesn't have miners, we just return a gas price of 1 for any call.
|
||||||
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if b.pendingBlock.Header().BaseFee != nil {
|
|
||||||
return b.pendingBlock.Header().BaseFee, nil
|
|
||||||
}
|
|
||||||
return big.NewInt(1), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
|
|
||||||
// chain doesn't have miners, we just return a gas tip of 1 for any call.
|
|
||||||
func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
|
||||||
return big.NewInt(1), nil
|
return big.NewInt(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,68 +230,25 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
} else {
|
} else {
|
||||||
hi = b.pendingBlock.GasLimit()
|
hi = b.pendingBlock.GasLimit()
|
||||||
}
|
}
|
||||||
// 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 {
|
|
||||||
feeCap = call.GasPrice
|
|
||||||
} else if call.GasFeeCap != nil {
|
|
||||||
feeCap = call.GasFeeCap
|
|
||||||
} else {
|
|
||||||
feeCap = common.Big0
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
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
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
executable := func(gas uint64) bool {
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil || failed {
|
||||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
return false
|
||||||
return true, nil, nil // Special case, raise gas limit
|
|
||||||
}
|
}
|
||||||
return true, nil, err // Bail out
|
return true
|
||||||
}
|
|
||||||
return res.Failed(), res, nil
|
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
failed, _, err := executable(mid)
|
if !executable(mid) {
|
||||||
|
|
||||||
// If the error is not nil(consensus error), it means the provided message
|
|
||||||
// call or transaction will never be accepted no matter how much gas it is
|
|
||||||
// assigned. Return the error directly, don't struggle any more
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if failed {
|
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
@@ -579,19 +256,8 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
}
|
}
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
failed, result, err := executable(hi)
|
if !executable(hi) {
|
||||||
if err != nil {
|
return 0, errGasEstimationFailed
|
||||||
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
|
return hi, nil
|
||||||
@@ -599,39 +265,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// state is modified during execution, make sure to copy it if necessary.
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, 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
|
|
||||||
if call.GasPrice != nil {
|
|
||||||
// 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
|
|
||||||
if call.GasFeeCap == nil {
|
|
||||||
call.GasFeeCap = new(big.Int)
|
|
||||||
}
|
|
||||||
if call.GasTipCap == nil {
|
|
||||||
call.GasTipCap = new(big.Int)
|
|
||||||
}
|
|
||||||
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
|
||||||
call.GasPrice = new(big.Int)
|
|
||||||
if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
|
|
||||||
call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure message is initialized properly.
|
// Ensure message is initialized properly.
|
||||||
|
if call.GasPrice == nil {
|
||||||
|
call.GasPrice = big.NewInt(1)
|
||||||
|
}
|
||||||
if call.Gas == 0 {
|
if call.Gas == 0 {
|
||||||
call.Gas = 50000000
|
call.Gas = 50000000
|
||||||
}
|
}
|
||||||
@@ -639,53 +277,45 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
|||||||
call.Value = new(big.Int)
|
call.Value = new(big.Int)
|
||||||
}
|
}
|
||||||
// Set infinite balance to the fake caller account.
|
// Set infinite balance to the fake caller account.
|
||||||
from := stateDB.GetOrNewStateObject(call.From)
|
from := statedb.GetOrNewStateObject(call.From)
|
||||||
from.SetBalance(math.MaxBig256)
|
from.SetBalance(math.MaxBig256)
|
||||||
// Execute the call.
|
// Execute the call.
|
||||||
msg := callMsg{call}
|
msg := callmsg{call}
|
||||||
|
|
||||||
txContext := core.NewEVMTxContext(msg)
|
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
|
||||||
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
|
|
||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
|
|
||||||
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb()
|
return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
// It panics if the transaction is invalid.
|
||||||
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
// Get the last block
|
sender, err := types.Sender(types.HomesteadSigner{}, tx)
|
||||||
block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not fetch parent")
|
panic(fmt.Errorf("invalid transaction: %v", err))
|
||||||
}
|
|
||||||
// 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)
|
nonce := b.pendingState.GetNonce(sender)
|
||||||
if tx.Nonce() != nonce {
|
if tx.Nonce() != nonce {
|
||||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
||||||
}
|
}
|
||||||
// Include tx in chain
|
|
||||||
blocks, receipts := 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) {
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
block.AddTxWithChain(b.blockchain, tx)
|
||||||
}
|
}
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
block.AddTxWithChain(b.blockchain, tx)
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
||||||
b.pendingReceipts = receipts[0]
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,9 +327,9 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
|
|||||||
var filter *filters.Filter
|
var filter *filters.Filter
|
||||||
if query.BlockHash != nil {
|
if query.BlockHash != nil {
|
||||||
// Block filter requested, construct a single-shot filter
|
// Block filter requested, construct a single-shot filter
|
||||||
filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
|
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
|
||||||
} else {
|
} else {
|
||||||
// Initialize unset filter boundaries to run from genesis to chain head
|
// Initialize unset filter boundaried to run from genesis to chain head
|
||||||
from := int64(0)
|
from := int64(0)
|
||||||
if query.FromBlock != nil {
|
if query.FromBlock != nil {
|
||||||
from = query.FromBlock.Int64()
|
from = query.FromBlock.Int64()
|
||||||
@@ -709,7 +339,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
|
|||||||
to = query.ToBlock.Int64()
|
to = query.ToBlock.Int64()
|
||||||
}
|
}
|
||||||
// Construct the range filter
|
// 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
|
// Run the filter and return all the logs
|
||||||
logs, err := filter.Logs(ctx)
|
logs, err := filter.Logs(ctx)
|
||||||
@@ -717,8 +347,8 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := make([]types.Log, len(logs))
|
res := make([]types.Log, len(logs))
|
||||||
for i, nLog := range logs {
|
for i, log := range logs {
|
||||||
res[i] = *nLog
|
res[i] = *log
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@@ -739,9 +369,9 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case logs := <-sink:
|
case logs := <-sink:
|
||||||
for _, nlog := range logs {
|
for _, log := range logs {
|
||||||
select {
|
select {
|
||||||
case ch <- *nlog:
|
case ch <- *log:
|
||||||
case err := <-sub.Err():
|
case err := <-sub.Err():
|
||||||
return err
|
return err
|
||||||
case <-quit:
|
case <-quit:
|
||||||
@@ -757,182 +387,100 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeNewHead returns an event subscription for a new header.
|
|
||||||
func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
|
||||||
// subscribe to a new head
|
|
||||||
sink := make(chan *types.Header)
|
|
||||||
sub := b.events.SubscribeNewHeads(sink)
|
|
||||||
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case head := <-sink:
|
|
||||||
select {
|
|
||||||
case ch <- head:
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdjustTime adds a time shift to the simulated clock.
|
// AdjustTime adds a time shift to the simulated clock.
|
||||||
// It can only be called on empty blocks.
|
|
||||||
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
return errors.New("Could not adjust time on non-empty block")
|
block.AddTx(tx)
|
||||||
}
|
}
|
||||||
// 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) {
|
|
||||||
block.OffsetTime(int64(adjustment.Seconds()))
|
block.OffsetTime(int64(adjustment.Seconds()))
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blockchain returns the underlying blockchain.
|
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||||
func (b *SimulatedBackend) Blockchain() *core.BlockChain {
|
type callmsg struct {
|
||||||
return b.blockchain
|
|
||||||
}
|
|
||||||
|
|
||||||
// callMsg implements core.Message to allow passing it as a transaction simulator.
|
|
||||||
type callMsg struct {
|
|
||||||
ethereum.CallMsg
|
ethereum.CallMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
func (m callmsg) From() common.Address { return m.CallMsg.From }
|
||||||
func (m callMsg) Nonce() uint64 { return 0 }
|
func (m callmsg) Nonce() uint64 { return 0 }
|
||||||
func (m callMsg) IsFake() bool { return true }
|
func (m callmsg) CheckNonce() bool { return false }
|
||||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
func (m callmsg) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||||
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
|
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
||||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
|
||||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
|
||||||
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
|
||||||
|
|
||||||
// filterBackend implements filters.Backend to support filtering for logs without
|
// filterBackend implements filters.Backend to support filtering for logs without
|
||||||
// taking bloom-bits acceleration structures into account.
|
// taking bloom-bits acceleration structures into account.
|
||||||
type filterBackend struct {
|
type filterBackend struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
bc *core.BlockChain
|
bc *core.BlockChain
|
||||||
backend *SimulatedBackend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
||||||
|
|
||||||
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
|
||||||
switch number {
|
if block == rpc.LatestBlockNumber {
|
||||||
case rpc.PendingBlockNumber:
|
|
||||||
if block := fb.backend.pendingBlock; block != nil {
|
|
||||||
return block.Header(), nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
case rpc.LatestBlockNumber:
|
|
||||||
return fb.bc.CurrentHeader(), nil
|
return fb.bc.CurrentHeader(), nil
|
||||||
case rpc.FinalizedBlockNumber:
|
|
||||||
if block := fb.bc.CurrentFinalizedBlock(); block != nil {
|
|
||||||
return block.Header(), nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("finalized block not found")
|
|
||||||
case rpc.SafeBlockNumber:
|
|
||||||
if block := fb.bc.CurrentSafeBlock(); block != nil {
|
|
||||||
return block.Header(), nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("safe block not found")
|
|
||||||
default:
|
|
||||||
return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil
|
|
||||||
}
|
}
|
||||||
|
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
return fb.bc.GetHeaderByHash(hash), nil
|
return fb.bc.GetHeaderByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
|
||||||
if body := fb.bc.GetBody(hash); body != nil {
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("block body not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
|
||||||
return fb.backend.pendingBlock, fb.backend.pendingReceipts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
return rawdb.ReadReceipts(fb.db, hash, *number), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||||
logs := rawdb.ReadLogs(fb.db, hash, number, fb.bc.Config())
|
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||||
|
if number == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
receipts := rawdb.ReadReceipts(fb.db, hash, *number)
|
||||||
|
if receipts == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
logs := make([][]*types.Log, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
logs[i] = receipt.Logs
|
||||||
|
}
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return nullSubscription()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeChainEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
||||||
return fb.bc.SubscribeLogsEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
||||||
return nullSubscription()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
|
||||||
|
|
||||||
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
|
||||||
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 {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
<-quit
|
<-quit
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||||
|
return fb.bc.SubscribeChainEvent(ch)
|
||||||
|
}
|
||||||
|
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
||||||
|
}
|
||||||
|
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
return fb.bc.SubscribeLogsEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
||||||
|
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,8 +21,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
@@ -32,11 +30,9 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
const basefeeWiggleMultiplier = 2
|
|
||||||
|
|
||||||
// SignerFn is a signer function callback when a contract requires a method to
|
// SignerFn is a signer function callback when a contract requires a method to
|
||||||
// sign the transaction before submission.
|
// sign the transaction before submission.
|
||||||
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
|
type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
|
||||||
|
|
||||||
// CallOpts is the collection of options to fine tune a contract call request.
|
// CallOpts is the collection of options to fine tune a contract call request.
|
||||||
type CallOpts struct {
|
type CallOpts struct {
|
||||||
@@ -53,15 +49,11 @@ type TransactOpts struct {
|
|||||||
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||||
|
|
||||||
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
|
||||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
|
||||||
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
|
||||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||||
|
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
|
|
||||||
NoSend bool // Do all transact steps but do not send the transaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterOpts is the collection of options to fine tune filtering for events
|
// FilterOpts is the collection of options to fine tune filtering for events
|
||||||
@@ -80,29 +72,6 @@ type WatchOpts struct {
|
|||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetaData collects all metadata for a bound contract.
|
|
||||||
type MetaData struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
Sigs map[string]string
|
|
||||||
Bin string
|
|
||||||
ABI string
|
|
||||||
ab *abi.ABI
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoundContract is the base wrapper object that reflects a contract on the
|
// BoundContract is the base wrapper object that reflects a contract on the
|
||||||
// Ethereum network. It contains a collection of methods that are used by the
|
// Ethereum network. It contains a collection of methods that are used by the
|
||||||
// higher level contract bindings to operate.
|
// higher level contract bindings to operate.
|
||||||
@@ -148,14 +117,11 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
|
|||||||
// sets the output to result. The result type might be a single field for simple
|
// sets the output to result. The result type might be a single field for simple
|
||||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
// returns.
|
// returns.
|
||||||
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
|
func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
// Don't crash on a lazy user
|
// Don't crash on a lazy user
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(CallOpts)
|
opts = new(CallOpts)
|
||||||
}
|
}
|
||||||
if results == nil {
|
|
||||||
results = new([]interface{})
|
|
||||||
}
|
|
||||||
// Pack the input, call and unpack the results
|
// Pack the input, call and unpack the results
|
||||||
input, err := c.abi.Pack(method, params...)
|
input, err := c.abi.Pack(method, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,10 +139,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||||||
return ErrNoPendingState
|
return ErrNoPendingState
|
||||||
}
|
}
|
||||||
output, err = pb.PendingCallContract(ctx, msg)
|
output, err = pb.PendingCallContract(ctx, msg)
|
||||||
if err != nil {
|
if err == nil && len(output) == 0 {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(output) == 0 {
|
|
||||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -186,10 +149,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
|
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
|
||||||
if err != nil {
|
if err == nil && len(output) == 0 {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(output) == 0 {
|
|
||||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
|
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -198,14 +158,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
if len(*results) == 0 {
|
|
||||||
res, err := c.abi.Unpack(method, output)
|
|
||||||
*results = res
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res := *results
|
return c.abi.Unpack(result, method, output)
|
||||||
return c.abi.UnpackIntoInterface(res[0], method, output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transact invokes the (paid) contract method with params as input values.
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
@@ -215,191 +171,73 @@ func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// todo(rjl493456442) check the method is payable or not,
|
|
||||||
// reject invalid transaction at the first place
|
|
||||||
return c.transact(opts, &c.address, input)
|
return c.transact(opts, &c.address, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawTransact initiates a transaction with the given raw calldata as the input.
|
|
||||||
// It's usually used to initiate transactions for invoking **Fallback** function.
|
|
||||||
func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
|
|
||||||
// todo(rjl493456442) check the method is payable or not,
|
|
||||||
// reject invalid transaction at the first place
|
|
||||||
return c.transact(opts, &c.address, calldata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
// its default method if one is available.
|
// its default method if one is available.
|
||||||
func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
|
func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
|
||||||
// todo(rjl493456442) check the payable fallback or receive is defined
|
|
||||||
// or not, reject invalid transaction at the first place
|
|
||||||
return c.transact(opts, &c.address, nil)
|
return c.transact(opts, &c.address, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
|
|
||||||
// Normalize value
|
|
||||||
value := opts.Value
|
|
||||||
if value == nil {
|
|
||||||
value = new(big.Int)
|
|
||||||
}
|
|
||||||
// Estimate TipCap
|
|
||||||
gasTipCap := opts.GasTipCap
|
|
||||||
if gasTipCap == nil {
|
|
||||||
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
gasTipCap = tip
|
|
||||||
}
|
|
||||||
// Estimate FeeCap
|
|
||||||
gasFeeCap := opts.GasFeeCap
|
|
||||||
if gasFeeCap == nil {
|
|
||||||
gasFeeCap = new(big.Int).Add(
|
|
||||||
gasTipCap,
|
|
||||||
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create the transaction
|
|
||||||
nonce, err := c.getNonce(opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
baseTx := &types.DynamicFeeTx{
|
|
||||||
To: contract,
|
|
||||||
Nonce: nonce,
|
|
||||||
GasFeeCap: gasFeeCap,
|
|
||||||
GasTipCap: gasTipCap,
|
|
||||||
Gas: gasLimit,
|
|
||||||
Value: value,
|
|
||||||
Data: input,
|
|
||||||
}
|
|
||||||
return types.NewTx(baseTx), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
|
||||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
|
||||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
|
||||||
}
|
|
||||||
// Normalize value
|
|
||||||
value := opts.Value
|
|
||||||
if value == nil {
|
|
||||||
value = new(big.Int)
|
|
||||||
}
|
|
||||||
// Estimate GasPrice
|
|
||||||
gasPrice := opts.GasPrice
|
|
||||||
if gasPrice == nil {
|
|
||||||
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create the transaction
|
|
||||||
nonce, err := c.getNonce(opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
baseTx := &types.LegacyTx{
|
|
||||||
To: contract,
|
|
||||||
Nonce: nonce,
|
|
||||||
GasPrice: gasPrice,
|
|
||||||
Gas: gasLimit,
|
|
||||||
Value: value,
|
|
||||||
Data: input,
|
|
||||||
}
|
|
||||||
return types.NewTx(baseTx), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
|
|
||||||
if contract != nil {
|
|
||||||
// Gas estimation cannot succeed without code for method invocations.
|
|
||||||
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else if len(code) == 0 {
|
|
||||||
return 0, ErrNoCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg := ethereum.CallMsg{
|
|
||||||
From: opts.From,
|
|
||||||
To: contract,
|
|
||||||
GasPrice: gasPrice,
|
|
||||||
GasTipCap: gasTipCap,
|
|
||||||
GasFeeCap: gasFeeCap,
|
|
||||||
Value: value,
|
|
||||||
Data: input,
|
|
||||||
}
|
|
||||||
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
|
|
||||||
if opts.Nonce == nil {
|
|
||||||
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
|
||||||
} else {
|
|
||||||
return opts.Nonce.Uint64(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transact executes an actual transaction invocation, first deriving any missing
|
// transact executes an actual transaction invocation, first deriving any missing
|
||||||
// authorization fields, and then scheduling the transaction for execution.
|
// authorization fields, and then scheduling the transaction for execution.
|
||||||
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
|
var err error
|
||||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
|
||||||
}
|
// Ensure a valid value field and resolve the account nonce
|
||||||
// Create the transaction
|
value := opts.Value
|
||||||
var (
|
if value == nil {
|
||||||
rawTx *types.Transaction
|
value = new(big.Int)
|
||||||
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 {
|
|
||||||
return nil, errHead
|
|
||||||
} else if head.BaseFee != nil {
|
|
||||||
rawTx, err = c.createDynamicTx(opts, contract, input, head)
|
|
||||||
} else {
|
|
||||||
// Chain is not London ready -> use legacy transaction
|
|
||||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var nonce uint64
|
||||||
|
if opts.Nonce == nil {
|
||||||
|
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nonce = opts.Nonce.Uint64()
|
||||||
|
}
|
||||||
|
// Figure out the gas allowance and gas price values
|
||||||
|
gasPrice := opts.GasPrice
|
||||||
|
if gasPrice == nil {
|
||||||
|
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gasLimit := opts.GasLimit
|
||||||
|
if gasLimit == 0 {
|
||||||
|
// Gas estimation cannot succeed without code for method invocations
|
||||||
|
if contract != nil {
|
||||||
|
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(code) == 0 {
|
||||||
|
return nil, ErrNoCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the contract surely has code (or code is not needed), estimate the transaction
|
||||||
|
msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
|
||||||
|
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the transaction, sign it and schedule it for execution
|
||||||
|
var rawTx *types.Transaction
|
||||||
|
if contract == nil {
|
||||||
|
rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
|
||||||
|
} else {
|
||||||
|
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
|
||||||
}
|
}
|
||||||
// Sign the transaction and schedule it for execution
|
|
||||||
if opts.Signer == nil {
|
if opts.Signer == nil {
|
||||||
return nil, errors.New("no signer to authorize the transaction with")
|
return nil, errors.New("no signer to authorize the transaction with")
|
||||||
}
|
}
|
||||||
signedTx, err := opts.Signer(opts.From, rawTx)
|
signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if opts.NoSend {
|
|
||||||
return signedTx, nil
|
|
||||||
}
|
|
||||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -414,9 +252,9 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
|||||||
opts = new(FilterOpts)
|
opts = new(FilterOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
||||||
|
|
||||||
topics, err := abi.MakeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -463,9 +301,9 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
|||||||
opts = new(WatchOpts)
|
opts = new(WatchOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
||||||
|
|
||||||
topics, err := abi.MakeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -488,11 +326,8 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
|||||||
|
|
||||||
// UnpackLog unpacks a retrieved log into the provided output structure.
|
// UnpackLog unpacks a retrieved log into the provided output structure.
|
||||||
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
|
||||||
return fmt.Errorf("event signature mismatch")
|
|
||||||
}
|
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
if err := c.abi.Unpack(out, event, log.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,33 +337,14 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
|
|||||||
indexed = append(indexed, arg)
|
indexed = append(indexed, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return abi.ParseTopics(out, indexed, log.Topics[1:])
|
return 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 {
|
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
|
||||||
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:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureContext is a helper method to ensure a context is not nil, even if the
|
// ensureContext is a helper method to ensure a context is not nil, even if the
|
||||||
// user specified it as such.
|
// user specified it as such.
|
||||||
func ensureContext(ctx context.Context) context.Context {
|
func ensureContext(ctx context.Context) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return context.Background()
|
return context.TODO()
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,34 @@
|
|||||||
// 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
|
|
||||||
// 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 bind_test
|
package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
|
|
||||||
|
|
||||||
type mockTransactor struct {
|
|
||||||
baseFee *big.Int
|
|
||||||
gasTipCap *big.Int
|
|
||||||
gasPrice *big.Int
|
|
||||||
suggestGasTipCapCalled bool
|
|
||||||
suggestGasPriceCalled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
|
||||||
return &types.Header{BaseFee: mt.baseFee}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
|
||||||
return []byte{1}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
|
||||||
mt.suggestGasPriceCalled = true
|
|
||||||
return mt.gasPrice, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
|
||||||
mt.suggestGasTipCapCalled = true
|
|
||||||
return mt.gasTipCap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockCaller struct {
|
type mockCaller struct {
|
||||||
codeAtBlockNumber *big.Int
|
codeAtBlockNumber *big.Int
|
||||||
callContractBlockNumber *big.Int
|
callContractBlockNumber *big.Int
|
||||||
callContractBytes []byte
|
|
||||||
callContractErr error
|
|
||||||
codeAtBytes []byte
|
|
||||||
codeAtErr error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
mc.codeAtBlockNumber = blockNumber
|
mc.codeAtBlockNumber = blockNumber
|
||||||
return 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) {
|
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
mc.callContractBlockNumber = blockNumber
|
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) {
|
|
||||||
mc.pendingCodeAtCalled = true
|
|
||||||
return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
|
||||||
mc.pendingCallContractCalled = true
|
|
||||||
return mc.pendingCallContractBytes, mc.pendingCallContractErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPassingBlockNumber(t *testing.T) {
|
func TestPassingBlockNumber(t *testing.T) {
|
||||||
mc := &mockPendingCaller{
|
|
||||||
mockCaller: &mockCaller{
|
mc := &mockCaller{}
|
||||||
codeAtBytes: []byte{1, 2, 3},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||||
Methods: map[string]abi.Method{
|
Methods: map[string]abi.Method{
|
||||||
@@ -129,10 +38,11 @@ func TestPassingBlockNumber(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, mc, nil, nil)
|
}, mc, nil, nil)
|
||||||
|
var ret string
|
||||||
|
|
||||||
blockNumber := big.NewInt(42)
|
blockNumber := big.NewInt(42)
|
||||||
|
|
||||||
bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something")
|
bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something")
|
||||||
|
|
||||||
if mc.callContractBlockNumber != blockNumber {
|
if mc.callContractBlockNumber != blockNumber {
|
||||||
t.Fatalf("CallContract() was not passed the block number")
|
t.Fatalf("CallContract() was not passed the block number")
|
||||||
@@ -142,7 +52,7 @@ func TestPassingBlockNumber(t *testing.T) {
|
|||||||
t.Fatalf("CodeAt() was not passed the block number")
|
t.Fatalf("CodeAt() was not passed the block number")
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.Call(&bind.CallOpts{}, nil, "something")
|
bc.Call(&bind.CallOpts{}, &ret, "something")
|
||||||
|
|
||||||
if mc.callContractBlockNumber != nil {
|
if mc.callContractBlockNumber != nil {
|
||||||
t.Fatalf("CallContract() was passed a block number when it should not have been")
|
t.Fatalf("CallContract() was passed a block number when it should not have been")
|
||||||
@@ -151,347 +61,4 @@ func TestPassingBlockNumber(t *testing.T) {
|
|||||||
if mc.codeAtBlockNumber != nil {
|
if mc.codeAtBlockNumber != nil {
|
||||||
t.Fatalf("CodeAt() was passed a block number when it should not have been")
|
t.Fatalf("CodeAt() was passed a block number when it should not have been")
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something")
|
|
||||||
|
|
||||||
if !mc.pendingCallContractCalled {
|
|
||||||
t.Fatalf("CallContract() was not passed the block number")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mc.pendingCodeAtCalled {
|
|
||||||
t.Fatalf("CodeAt() was not passed the block number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
|
|
||||||
|
|
||||||
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
|
||||||
hash := crypto.Keccak256Hash([]byte("testName"))
|
|
||||||
topics := []common.Hash{
|
|
||||||
crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
|
||||||
"name": hash,
|
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
|
||||||
"amount": big.NewInt(1),
|
|
||||||
"memo": []byte{88},
|
|
||||||
}
|
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)")),
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
|
||||||
"names": hash,
|
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
|
||||||
"amount": big.NewInt(1),
|
|
||||||
"memo": []byte{88},
|
|
||||||
}
|
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
|
||||||
arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
hash := crypto.Keccak256Hash(arrBytes)
|
|
||||||
topics := []common.Hash{
|
|
||||||
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
|
||||||
"addresses": hash,
|
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
|
||||||
"amount": big.NewInt(1),
|
|
||||||
"memo": []byte{88},
|
|
||||||
}
|
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
|
||||||
mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
|
|
||||||
addrBytes := mockAddress.Bytes()
|
|
||||||
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)")),
|
|
||||||
common.BytesToHash(functionTyBytes),
|
|
||||||
}
|
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
|
||||||
"function": functionTy,
|
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
|
||||||
"amount": big.NewInt(1),
|
|
||||||
"memo": []byte{88},
|
|
||||||
}
|
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
|
||||||
bytes := []byte{1, 2, 3, 4, 5}
|
|
||||||
hash := crypto.Keccak256Hash(bytes)
|
|
||||||
topics := []common.Hash{
|
|
||||||
crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")),
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
|
||||||
"content": hash,
|
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
|
||||||
"amount": big.NewInt(1),
|
|
||||||
"memo": []byte{88},
|
|
||||||
}
|
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTransactGasFee(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
// GasTipCap and GasFeeCap
|
|
||||||
// When opts.GasTipCap and opts.GasFeeCap are nil
|
|
||||||
mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
|
|
||||||
bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
|
||||||
opts := &bind.TransactOpts{Signer: mockSign}
|
|
||||||
tx, err := bc.Transact(opts, "")
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(big.NewInt(5), tx.GasTipCap())
|
|
||||||
assert.Equal(big.NewInt(205), tx.GasFeeCap())
|
|
||||||
assert.Nil(opts.GasTipCap)
|
|
||||||
assert.Nil(opts.GasFeeCap)
|
|
||||||
assert.True(mt.suggestGasTipCapCalled)
|
|
||||||
|
|
||||||
// Second call to Transact should use latest suggested GasTipCap
|
|
||||||
mt.gasTipCap = big.NewInt(6)
|
|
||||||
mt.suggestGasTipCapCalled = false
|
|
||||||
tx, err = bc.Transact(opts, "")
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(big.NewInt(6), tx.GasTipCap())
|
|
||||||
assert.Equal(big.NewInt(206), tx.GasFeeCap())
|
|
||||||
assert.True(mt.suggestGasTipCapCalled)
|
|
||||||
|
|
||||||
// GasPrice
|
|
||||||
// When opts.GasPrice is nil
|
|
||||||
mt = &mockTransactor{gasPrice: big.NewInt(5)}
|
|
||||||
bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
|
||||||
opts = &bind.TransactOpts{Signer: mockSign}
|
|
||||||
tx, err = bc.Transact(opts, "")
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(big.NewInt(5), tx.GasPrice())
|
|
||||||
assert.Nil(opts.GasPrice)
|
|
||||||
assert.True(mt.suggestGasPriceCalled)
|
|
||||||
|
|
||||||
// Second call to Transact should use latest suggested GasPrice
|
|
||||||
mt.gasPrice = big.NewInt(6)
|
|
||||||
mt.suggestGasPriceCalled = false
|
|
||||||
tx, err = bc.Transact(opts, "")
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(big.NewInt(6), tx.GasPrice())
|
|
||||||
assert.True(mt.suggestGasPriceCalled)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
|
|
||||||
received := make(map[string]interface{})
|
|
||||||
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
|
||||||
return types.Log{
|
|
||||||
Address: common.HexToAddress("0x0"),
|
|
||||||
Topics: topics,
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: txHash,
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCall(t *testing.T) {
|
|
||||||
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) {
|
|
||||||
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"}]}]}]`))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lang is a target programming language selector to generate bindings for.
|
// Lang is a target programming language selector to generate bindings for.
|
||||||
@@ -38,60 +37,18 @@ type Lang int
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
LangGo Lang = iota
|
LangGo Lang = iota
|
||||||
|
LangJava
|
||||||
|
LangObjC
|
||||||
)
|
)
|
||||||
|
|
||||||
func isKeyWord(arg string) bool {
|
|
||||||
switch arg {
|
|
||||||
case "break":
|
|
||||||
case "case":
|
|
||||||
case "chan":
|
|
||||||
case "const":
|
|
||||||
case "continue":
|
|
||||||
case "default":
|
|
||||||
case "defer":
|
|
||||||
case "else":
|
|
||||||
case "fallthrough":
|
|
||||||
case "for":
|
|
||||||
case "func":
|
|
||||||
case "go":
|
|
||||||
case "goto":
|
|
||||||
case "if":
|
|
||||||
case "import":
|
|
||||||
case "interface":
|
|
||||||
case "iota":
|
|
||||||
case "map":
|
|
||||||
case "make":
|
|
||||||
case "new":
|
|
||||||
case "package":
|
|
||||||
case "range":
|
|
||||||
case "return":
|
|
||||||
case "select":
|
|
||||||
case "struct":
|
|
||||||
case "switch":
|
|
||||||
case "type":
|
|
||||||
case "var":
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
|
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
|
||||||
// to be used as is in client code, but rather as an intermediate struct which
|
// to be used as is in client code, but rather as an intermediate struct which
|
||||||
// enforces compile time type safety and naming convention opposed to having to
|
// enforces compile time type safety and naming convention opposed to having to
|
||||||
// manually maintain hard coded strings that break on runtime.
|
// manually maintain hard coded strings that break on runtime.
|
||||||
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
|
func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
|
||||||
var (
|
// Process each individual contract requested binding
|
||||||
// contracts is the map of each individual contract requested binding
|
contracts := make(map[string]*tmplContract)
|
||||||
contracts = make(map[string]*tmplContract)
|
|
||||||
|
|
||||||
// structs is the map of all redeclared structs shared by passed contracts.
|
|
||||||
structs = make(map[string]*tmplStruct)
|
|
||||||
|
|
||||||
// isLib is the map used to flag each encountered library as such
|
|
||||||
isLib = make(map[string]struct{})
|
|
||||||
)
|
|
||||||
for i := 0; i < len(types); i++ {
|
for i := 0; i < len(types); i++ {
|
||||||
// Parse the actual ABI to generate the binding for
|
// Parse the actual ABI to generate the binding for
|
||||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||||
@@ -106,54 +63,23 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||||||
return r
|
return r
|
||||||
}, abis[i])
|
}, abis[i])
|
||||||
|
|
||||||
// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
|
// Extract the call and transact methods; events; and sort them alphabetically
|
||||||
var (
|
var (
|
||||||
calls = make(map[string]*tmplMethod)
|
calls = make(map[string]*tmplMethod)
|
||||||
transacts = make(map[string]*tmplMethod)
|
transacts = make(map[string]*tmplMethod)
|
||||||
events = make(map[string]*tmplEvent)
|
events = make(map[string]*tmplEvent)
|
||||||
fallback *tmplMethod
|
|
||||||
receive *tmplMethod
|
|
||||||
|
|
||||||
// identifiers are used to detect duplicated identifiers of functions
|
|
||||||
// and events. For all calls, transacts and events, abigen will generate
|
|
||||||
// corresponding bindings. However we have to ensure there is no
|
|
||||||
// identifier collisions in the bindings of these categories.
|
|
||||||
callIdentifiers = make(map[string]bool)
|
|
||||||
transactIdentifiers = make(map[string]bool)
|
|
||||||
eventIdentifiers = make(map[string]bool)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, input := range evmABI.Constructor.Inputs {
|
|
||||||
if hasStruct(input.Type) {
|
|
||||||
bindStructType[lang](input.Type, structs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, original := range evmABI.Methods {
|
for _, original := range evmABI.Methods {
|
||||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
normalized.Name = methodNormalizer[lang](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))
|
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||||
copy(normalized.Inputs, original.Inputs)
|
copy(normalized.Inputs, original.Inputs)
|
||||||
for j, input := range normalized.Inputs {
|
for j, input := range normalized.Inputs {
|
||||||
if input.Name == "" || isKeyWord(input.Name) {
|
if input.Name == "" {
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
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))
|
normalized.Outputs = make([]abi.Argument, len(original.Outputs))
|
||||||
copy(normalized.Outputs, original.Outputs)
|
copy(normalized.Outputs, original.Outputs)
|
||||||
@@ -161,12 +87,9 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||||||
if output.Name != "" {
|
if output.Name != "" {
|
||||||
normalized.Outputs[j].Name = capitalise(output.Name)
|
normalized.Outputs[j].Name = capitalise(output.Name)
|
||||||
}
|
}
|
||||||
if hasStruct(output.Type) {
|
|
||||||
bindStructType[lang](output.Type, structs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Append the methods to the call or transact lists
|
// Append the methods to the call or transact lists
|
||||||
if original.IsConstant() {
|
if original.Const {
|
||||||
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||||
} else {
|
} else {
|
||||||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||||
@@ -179,88 +102,35 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||||||
}
|
}
|
||||||
// Normalize the event for capital cases and non-anonymous outputs
|
// Normalize the event for capital cases and non-anonymous outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
|
normalized.Name = methodNormalizer[lang](original.Name)
|
||||||
|
|
||||||
// Ensure there is no duplicated identifier
|
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
|
||||||
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))
|
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||||
copy(normalized.Inputs, original.Inputs)
|
copy(normalized.Inputs, original.Inputs)
|
||||||
for j, input := range normalized.Inputs {
|
for j, input := range normalized.Inputs {
|
||||||
if input.Name == "" || isKeyWord(input.Name) {
|
// Indexed fields are input, non-indexed ones are outputs
|
||||||
|
if input.Indexed {
|
||||||
|
if input.Name == "" {
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||||
}
|
}
|
||||||
// Event is a bit special, we need to define event struct in binding,
|
|
||||||
// ensure there is no camel-case-style name conflict.
|
|
||||||
for index := 0; ; index++ {
|
|
||||||
if !used[capitalise(normalized.Inputs[j].Name)] {
|
|
||||||
used[capitalise(normalized.Inputs[j].Name)] = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
|
|
||||||
}
|
|
||||||
if hasStruct(input.Type) {
|
|
||||||
bindStructType[lang](input.Type, structs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append the event to the accumulator list
|
// Append the event to the accumulator list
|
||||||
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
||||||
}
|
}
|
||||||
// Add two special fallback functions if they exist
|
|
||||||
if evmABI.HasFallback() {
|
|
||||||
fallback = &tmplMethod{Original: evmABI.Fallback}
|
|
||||||
}
|
|
||||||
if evmABI.HasReceive() {
|
|
||||||
receive = &tmplMethod{Original: evmABI.Receive}
|
|
||||||
}
|
|
||||||
contracts[types[i]] = &tmplContract{
|
contracts[types[i]] = &tmplContract{
|
||||||
Type: capitalise(types[i]),
|
Type: capitalise(types[i]),
|
||||||
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||||
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
InputBin: strings.TrimSpace(bytecodes[i]),
|
||||||
Constructor: evmABI.Constructor,
|
Constructor: evmABI.Constructor,
|
||||||
Calls: calls,
|
Calls: calls,
|
||||||
Transacts: transacts,
|
Transacts: transacts,
|
||||||
Fallback: fallback,
|
|
||||||
Receive: receive,
|
|
||||||
Events: events,
|
Events: events,
|
||||||
Libraries: make(map[string]string),
|
|
||||||
}
|
}
|
||||||
// Function 4-byte signatures are stored in the same sequence
|
|
||||||
// as types, if available.
|
|
||||||
if len(fsigs) > i {
|
|
||||||
contracts[types[i]].FuncSigs = fsigs[i]
|
|
||||||
}
|
|
||||||
// Parse library references.
|
|
||||||
for pattern, name := range libs {
|
|
||||||
matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
|
|
||||||
}
|
|
||||||
if matched {
|
|
||||||
contracts[types[i]].Libraries[pattern] = name
|
|
||||||
// keep track that this type is a library
|
|
||||||
if _, ok := isLib[name]; !ok {
|
|
||||||
isLib[name] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if that type has already been identified as a library
|
|
||||||
for i := 0; i < len(types); i++ {
|
|
||||||
_, ok := isLib[types[i]]
|
|
||||||
contracts[types[i]].Library = ok
|
|
||||||
}
|
}
|
||||||
// Generate the contract template data content and render it
|
// Generate the contract template data content and render it
|
||||||
data := &tmplData{
|
data := &tmplData{
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
Contracts: contracts,
|
Contracts: contracts,
|
||||||
Libraries: libs,
|
|
||||||
Structs: structs,
|
|
||||||
}
|
}
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
@@ -289,148 +159,236 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||||||
|
|
||||||
// bindType is a set of type binders that convert Solidity types to some supported
|
// bindType is a set of type binders that convert Solidity types to some supported
|
||||||
// programming language types.
|
// programming language types.
|
||||||
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
var bindType = map[Lang]func(kind abi.Type) string{
|
||||||
LangGo: bindTypeGo,
|
LangGo: bindTypeGo,
|
||||||
|
LangJava: bindTypeJava,
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
|
// Helper function for the binding generators.
|
||||||
func bindBasicTypeGo(kind abi.Type) string {
|
// It reads the unmatched characters after the inner type-match,
|
||||||
switch kind.T {
|
// (since the inner type is a prefix of the total type declaration),
|
||||||
case abi.AddressTy:
|
// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
|
||||||
return "common.Address"
|
// and returns the sizes of these arrays.
|
||||||
case abi.IntTy, abi.UintTy:
|
//
|
||||||
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
|
// Returned array sizes are in the same order as solidity signatures; inner array size first.
|
||||||
|
// Array sizes may also be "", indicating a dynamic array.
|
||||||
|
func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
|
||||||
|
remainder := stringKind[innerLen:]
|
||||||
|
//find all the sizes
|
||||||
|
matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
|
||||||
|
parts := make([]string, 0, len(matches))
|
||||||
|
for _, match := range matches {
|
||||||
|
//get group 1 from the regex match
|
||||||
|
parts = append(parts, match[1])
|
||||||
|
}
|
||||||
|
return innerMapping, parts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type.
|
||||||
|
// Simply returns the inner type if arraySizes is empty.
|
||||||
|
func arrayBindingGo(inner string, arraySizes []string) string {
|
||||||
|
out := ""
|
||||||
|
//prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes)
|
||||||
|
for i := len(arraySizes) - 1; i >= 0; i-- {
|
||||||
|
out += "[" + arraySizes[i] + "]"
|
||||||
|
}
|
||||||
|
out += inner
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
|
||||||
|
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
|
||||||
|
// mapped will use an upscaled type (e.g. *big.Int).
|
||||||
|
func bindTypeGo(kind abi.Type) string {
|
||||||
|
stringKind := kind.String()
|
||||||
|
innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
|
||||||
|
return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The inner function of bindTypeGo, this finds the inner type of stringKind.
|
||||||
|
// (Or just the type itself if it is not an array or slice)
|
||||||
|
// The length of the matched part is returned, with the translated type.
|
||||||
|
func bindUnnestedTypeGo(stringKind string) (int, string) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(stringKind, "address"):
|
||||||
|
return len("address"), "common.Address"
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "bytes"):
|
||||||
|
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
|
||||||
|
return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
|
||||||
|
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
|
||||||
switch parts[2] {
|
switch parts[2] {
|
||||||
case "8", "16", "32", "64":
|
case "8", "16", "32", "64":
|
||||||
return fmt.Sprintf("%sint%s", parts[1], parts[2])
|
return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
|
||||||
}
|
}
|
||||||
return "*big.Int"
|
return len(parts[0]), "*big.Int"
|
||||||
case abi.FixedBytesTy:
|
|
||||||
return fmt.Sprintf("[%d]byte", kind.Size)
|
case strings.HasPrefix(stringKind, "bool"):
|
||||||
case abi.BytesTy:
|
return len("bool"), "bool"
|
||||||
return "[]byte"
|
|
||||||
case abi.FunctionTy:
|
case strings.HasPrefix(stringKind, "string"):
|
||||||
return "[24]byte"
|
return len("string"), "string"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// string, bool types
|
return len(stringKind), stringKind
|
||||||
return kind.String()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
|
// Translates the array sizes to a Java declaration of a (nested) array of the inner type.
|
||||||
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
|
// Simply returns the inner type if arraySizes is empty.
|
||||||
|
func arrayBindingJava(inner string, arraySizes []string) string {
|
||||||
|
// Java array type declarations do not include the length.
|
||||||
|
return inner + strings.Repeat("[]", len(arraySizes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
// mapped will use an upscaled type (e.g. BigDecimal).
|
||||||
func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTypeJava(kind abi.Type) string {
|
||||||
switch kind.T {
|
stringKind := kind.String()
|
||||||
case abi.TupleTy:
|
innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
|
||||||
return structs[kind.TupleRawName+kind.String()].Name
|
return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
|
||||||
case abi.ArrayTy:
|
}
|
||||||
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
|
|
||||||
case abi.SliceTy:
|
// The inner function of bindTypeJava, this finds the inner type of stringKind.
|
||||||
return "[]" + bindTypeGo(*kind.Elem, structs)
|
// (Or just the type itself if it is not an array or slice)
|
||||||
|
// The length of the matched part is returned, with the translated type.
|
||||||
|
func bindUnnestedTypeJava(stringKind string) (int, string) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(stringKind, "address"):
|
||||||
|
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return len(stringKind), stringKind
|
||||||
|
}
|
||||||
|
if parts[1] == "" {
|
||||||
|
return len("address"), "Address"
|
||||||
|
}
|
||||||
|
return len(parts[0]), "Addresses"
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "bytes"):
|
||||||
|
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return len(stringKind), stringKind
|
||||||
|
}
|
||||||
|
return len(parts[0]), "byte[]"
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
|
||||||
|
//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(stringKind)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return len(stringKind), stringKind
|
||||||
|
}
|
||||||
|
|
||||||
|
namedSize := map[string]string{
|
||||||
|
"8": "byte",
|
||||||
|
"16": "short",
|
||||||
|
"32": "int",
|
||||||
|
"64": "long",
|
||||||
|
}[parts[2]]
|
||||||
|
|
||||||
|
//default to BigInt
|
||||||
|
if namedSize == "" {
|
||||||
|
namedSize = "BigInt"
|
||||||
|
}
|
||||||
|
return len(parts[0]), namedSize
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "bool"):
|
||||||
|
return len("bool"), "boolean"
|
||||||
|
|
||||||
|
case strings.HasPrefix(stringKind, "string"):
|
||||||
|
return len("string"), "String"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return bindBasicTypeGo(kind)
|
return len(stringKind), stringKind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindTopicType is a set of type binders that convert Solidity types to some
|
// bindTopicType is a set of type binders that convert Solidity types to some
|
||||||
// supported programming language topic types.
|
// supported programming language topic types.
|
||||||
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
var bindTopicType = map[Lang]func(kind abi.Type) string{
|
||||||
LangGo: bindTopicTypeGo,
|
LangGo: bindTopicTypeGo,
|
||||||
|
LangJava: bindTopicTypeJava,
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
// bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
||||||
// functionality as for simple types, but dynamic types get converted to hashes.
|
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||||
func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTopicTypeGo(kind abi.Type) string {
|
||||||
bound := bindTypeGo(kind, structs)
|
bound := bindTypeGo(kind)
|
||||||
|
|
||||||
// todo(rjl493456442) according solidity documentation, indexed event
|
|
||||||
// parameters that are not value types i.e. arrays and structs are not
|
|
||||||
// stored directly but instead a keccak256-hash of an encoding is stored.
|
|
||||||
//
|
|
||||||
// We only convert stringS and bytes to hash, still need to deal with
|
|
||||||
// array(both fixed-size and dynamic-size) and struct.
|
|
||||||
if bound == "string" || bound == "[]byte" {
|
if bound == "string" || bound == "[]byte" {
|
||||||
bound = "common.Hash"
|
bound = "common.Hash"
|
||||||
}
|
}
|
||||||
return bound
|
return bound
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindStructType is a set of type binders that convert Solidity tuple types to some supported
|
// bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
|
||||||
// programming language struct definition.
|
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||||
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
func bindTopicTypeJava(kind abi.Type) string {
|
||||||
LangGo: bindStructTypeGo,
|
bound := bindTypeJava(kind)
|
||||||
}
|
if bound == "String" || bound == "Bytes" {
|
||||||
|
bound = "Hash"
|
||||||
// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
|
|
||||||
// in the given map.
|
|
||||||
// Notably, this function will resolve and record nested struct recursively.
|
|
||||||
func bindStructTypeGo(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 (
|
|
||||||
names = make(map[string]bool)
|
|
||||||
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})
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
case abi.SliceTy:
|
|
||||||
return "[]" + bindStructTypeGo(*kind.Elem, structs)
|
|
||||||
default:
|
|
||||||
return bindBasicTypeGo(kind)
|
|
||||||
}
|
}
|
||||||
|
return bound
|
||||||
}
|
}
|
||||||
|
|
||||||
// namedType is a set of functions that transform language specific types to
|
// namedType is a set of functions that transform language specific types to
|
||||||
// named versions that may be used inside method names.
|
// named versions that my be used inside method names.
|
||||||
var namedType = map[Lang]func(string, abi.Type) string{
|
var namedType = map[Lang]func(string, abi.Type) string{
|
||||||
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
|
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
|
||||||
|
LangJava: namedTypeJava,
|
||||||
}
|
}
|
||||||
|
|
||||||
// alias returns an alias of the given string based on the aliasing rules
|
// namedTypeJava converts some primitive data types to named variants that can
|
||||||
// or returns itself if no rule is matched.
|
// be used as parts of method names.
|
||||||
func alias(aliases map[string]string, n string) string {
|
func namedTypeJava(javaKind string, solKind abi.Type) string {
|
||||||
if alias, exist := aliases[n]; exist {
|
switch javaKind {
|
||||||
return alias
|
case "byte[]":
|
||||||
|
return "Binary"
|
||||||
|
case "byte[][]":
|
||||||
|
return "Binaries"
|
||||||
|
case "string":
|
||||||
|
return "String"
|
||||||
|
case "string[]":
|
||||||
|
return "Strings"
|
||||||
|
case "boolean":
|
||||||
|
return "Bool"
|
||||||
|
case "boolean[]":
|
||||||
|
return "Bools"
|
||||||
|
case "BigInt[]":
|
||||||
|
return "BigInts"
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// methodNormalizer is a name transformer that modifies Solidity method names to
|
// methodNormalizer is a name transformer that modifies Solidity method names to
|
||||||
// conform to target language naming conventions.
|
// conform to target language naming concentions.
|
||||||
var methodNormalizer = map[Lang]func(string) string{
|
var methodNormalizer = map[Lang]func(string) string{
|
||||||
LangGo: abi.ToCamelCase,
|
LangGo: abi.ToCamelCase,
|
||||||
|
LangJava: decapitalise,
|
||||||
}
|
}
|
||||||
|
|
||||||
// capitalise makes a camel-case string which starts with an upper case character.
|
// capitalise makes a camel-case string which starts with an upper case character.
|
||||||
var capitalise = abi.ToCamelCase
|
func capitalise(input string) string {
|
||||||
|
return abi.ToCamelCase(input)
|
||||||
|
}
|
||||||
|
|
||||||
// decapitalise makes a camel-case string which starts with a lower case character.
|
// decapitalise makes a camel-case string which starts with a lower case character.
|
||||||
func decapitalise(input string) string {
|
func decapitalise(input string) string {
|
||||||
@@ -464,18 +422,3 @@ func structured(args abi.Arguments) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasStruct returns an indicator whether the given type is struct, struct slice
|
|
||||||
// or struct array.
|
|
||||||
func hasStruct(t abi.Type) bool {
|
|
||||||
switch t.T {
|
|
||||||
case abi.SliceTy:
|
|
||||||
return hasStruct(*t.Elem)
|
|
||||||
case abi.ArrayTy:
|
|
||||||
return hasStruct(*t.Elem)
|
|
||||||
case abi.TupleTy:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -22,24 +22,17 @@ import "github.com/ethereum/go-ethereum/accounts/abi"
|
|||||||
type tmplData struct {
|
type tmplData struct {
|
||||||
Package string // Name of the package to place the generated file in
|
Package string // Name of the package to place the generated file in
|
||||||
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
||||||
Libraries map[string]string // Map the bytecode's link pattern to the library name
|
|
||||||
Structs map[string]*tmplStruct // Contract struct type definitions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplContract contains the data needed to generate an individual contract binding.
|
// tmplContract contains the data needed to generate an individual contract binding.
|
||||||
type tmplContract struct {
|
type tmplContract struct {
|
||||||
Type string // Type name of the main contract binding
|
Type string // Type name of the main contract binding
|
||||||
InputABI string // JSON ABI used as the input to generate the binding from
|
InputABI string // JSON ABI used as the input to generate the binding from
|
||||||
InputBin string // Optional EVM bytecode used to generate deploy code from
|
InputBin string // Optional EVM bytecode used to denetare deploy code from
|
||||||
FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
|
|
||||||
Constructor abi.Method // Contract constructor for deploy parametrization
|
Constructor abi.Method // Contract constructor for deploy parametrization
|
||||||
Calls map[string]*tmplMethod // Contract calls that only read state data
|
Calls map[string]*tmplMethod // Contract calls that only read state data
|
||||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||||
Fallback *tmplMethod // Additional special fallback function
|
|
||||||
Receive *tmplMethod // Additional special receive function
|
|
||||||
Events map[string]*tmplEvent // Contract events accessors
|
Events map[string]*tmplEvent // Contract events accessors
|
||||||
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
|
||||||
Library bool // Indicator whether the contract is a library
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
||||||
@@ -50,36 +43,21 @@ type tmplMethod struct {
|
|||||||
Structured bool // Whether the returns should be accumulated into a struct
|
Structured bool // Whether the returns should be accumulated into a struct
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed
|
// tmplEvent is a wrapper around an a
|
||||||
// and cached data fields.
|
|
||||||
type tmplEvent struct {
|
type tmplEvent struct {
|
||||||
Original abi.Event // Original event as parsed by the abi package
|
Original abi.Event // Original event as parsed by the abi package
|
||||||
Normalized abi.Event // Normalized version of the parsed fields
|
Normalized abi.Event // Normalized version of the parsed fields
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplField is a wrapper around a struct field with binding language
|
|
||||||
// struct type definition and relative filed name.
|
|
||||||
type tmplField struct {
|
|
||||||
Type string // Field type representation depends on target binding language
|
|
||||||
Name string // Field name converted from the raw user-defined field name
|
|
||||||
SolKind abi.Type // Raw abi type information
|
|
||||||
}
|
|
||||||
|
|
||||||
// tmplStruct is a wrapper around an abi.tuple and contains an auto-generated
|
|
||||||
// struct name.
|
|
||||||
type tmplStruct struct {
|
|
||||||
Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
|
|
||||||
Fields []*tmplField // Struct fields definition depends on the binding language.
|
|
||||||
}
|
|
||||||
|
|
||||||
// tmplSource is language to template mapping containing all the supported
|
// tmplSource is language to template mapping containing all the supported
|
||||||
// programming languages the package can generate to.
|
// programming languages the package can generate to.
|
||||||
var tmplSource = map[Lang]string{
|
var tmplSource = map[Lang]string{
|
||||||
LangGo: tmplSourceGo,
|
LangGo: tmplSourceGo,
|
||||||
|
LangJava: tmplSourceJava,
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplSourceGo is the Go source template that the generated Go contract binding
|
// tmplSourceGo is the Go source template use to generate the contract binding
|
||||||
// is based on.
|
// based on.
|
||||||
const tmplSourceGo = `
|
const tmplSourceGo = `
|
||||||
// Code generated - DO NOT EDIT.
|
// Code generated - DO NOT EDIT.
|
||||||
// This file is a generated binding and any manual changes will be lost.
|
// This file is a generated binding and any manual changes will be lost.
|
||||||
@@ -89,7 +67,6 @@ package {{.Package}}
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"errors"
|
|
||||||
|
|
||||||
ethereum "github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
@@ -101,69 +78,31 @@ import (
|
|||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var (
|
var (
|
||||||
_ = errors.New
|
|
||||||
_ = big.NewInt
|
_ = big.NewInt
|
||||||
_ = strings.NewReader
|
_ = strings.NewReader
|
||||||
_ = ethereum.NotFound
|
_ = ethereum.NotFound
|
||||||
|
_ = abi.U256
|
||||||
_ = bind.Bind
|
_ = bind.Bind
|
||||||
_ = common.Big1
|
_ = common.Big1
|
||||||
_ = types.BloomLookup
|
_ = types.BloomLookup
|
||||||
_ = event.NewSubscription
|
_ = event.NewSubscription
|
||||||
_ = abi.ConvertType
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{{$structs := .Structs}}
|
|
||||||
{{range $structs}}
|
|
||||||
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type {{.Name}} struct {
|
|
||||||
{{range $field := .Fields}}
|
|
||||||
{{$field.Name}} {{$field.Type}}{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
|
|
||||||
var {{.Type}}MetaData = &bind.MetaData{
|
|
||||||
ABI: "{{.InputABI}}",
|
|
||||||
{{if $contract.FuncSigs -}}
|
|
||||||
Sigs: map[string]string{
|
|
||||||
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
|
||||||
{{end}}
|
|
||||||
},
|
|
||||||
{{end -}}
|
|
||||||
{{if .InputBin -}}
|
|
||||||
Bin: "0x{{.InputBin}}",
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||||
// Deprecated: Use {{.Type}}MetaData.ABI instead.
|
const {{.Type}}ABI = "{{.InputABI}}"
|
||||||
var {{.Type}}ABI = {{.Type}}MetaData.ABI
|
|
||||||
|
|
||||||
{{if $contract.FuncSigs}}
|
|
||||||
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
|
|
||||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
|
||||||
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .InputBin}}
|
{{if .InputBin}}
|
||||||
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
||||||
// Deprecated: Use {{.Type}}MetaData.Bin instead.
|
const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + `
|
||||||
var {{.Type}}Bin = {{.Type}}MetaData.Bin
|
|
||||||
|
|
||||||
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||||
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
||||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
if parsed == nil {
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||||
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
|
|
||||||
}
|
|
||||||
{{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:])
|
|
||||||
{{end}}
|
|
||||||
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -268,18 +207,18 @@ var (
|
|||||||
|
|
||||||
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
||||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
|
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call invokes the (constant) contract method with params as input values and
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
// sets the output to result. The result type might be a single field for simple
|
// sets the output to result. The result type might be a single field for simple
|
||||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
// returns.
|
// returns.
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
|
return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +237,7 @@ var (
|
|||||||
// sets the output to result. The result type might be a single field for simple
|
// sets the output to result. The result type might be a single field for simple
|
||||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
// returns.
|
// returns.
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
|
return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,116 +253,63 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
{{range .Calls}}
|
{{range .Calls}}
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}},{{end}}{{end}} error) {
|
||||||
var out []interface{}
|
{{if .Structured}}ret := new(struct{
|
||||||
err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
{{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}}
|
||||||
{{if .Structured}}
|
|
||||||
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
|
|
||||||
if err != nil {
|
|
||||||
return *outstruct, err
|
|
||||||
}
|
|
||||||
{{range $i, $t := .Normalized.Outputs}}
|
|
||||||
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
|
||||||
|
|
||||||
return *outstruct, err
|
|
||||||
{{else}}
|
|
||||||
if err != nil {
|
|
||||||
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
|
|
||||||
}
|
|
||||||
{{range $i, $t := .Normalized.Outputs}}
|
|
||||||
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
|
||||||
|
|
||||||
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
}){{else}}var (
|
||||||
|
{{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type}})
|
||||||
|
{{end}}
|
||||||
|
){{end}}
|
||||||
|
out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}&[]interface{}{
|
||||||
|
{{range $i, $_ := .Normalized.Outputs}}ret{{$i}},
|
||||||
|
{{end}}
|
||||||
|
}{{end}}{{end}}
|
||||||
|
err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range .Transacts}}
|
{{range .Transacts}}
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .Fallback}}
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Receive}}
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Events}}
|
{{range .Events}}
|
||||||
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
|
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
|
||||||
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
|
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
|
||||||
@@ -491,14 +377,14 @@ var (
|
|||||||
|
|
||||||
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
|
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
|
||||||
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
|
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
|
||||||
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
|
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type}}{{else}}{{bindtype .Type}}{{end}}; {{end}}
|
||||||
Raw types.Log // Blockchain specific contextual infos
|
Raw types.Log // Blockchain specific contextual infos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
||||||
{{range .Normalized.Inputs}}
|
{{range .Normalized.Inputs}}
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
for _, {{.Name}}Item := range {{.Name}} {
|
for _, {{.Name}}Item := range {{.Name}} {
|
||||||
@@ -512,10 +398,10 @@ var (
|
|||||||
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (event.Subscription, error) {
|
||||||
{{range .Normalized.Inputs}}
|
{{range .Normalized.Inputs}}
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
for _, {{.Name}}Item := range {{.Name}} {
|
for _, {{.Name}}Item := range {{.Name}} {
|
||||||
@@ -553,19 +439,108 @@ var (
|
|||||||
}
|
}
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
|
||||||
event := new({{$contract.Type}}{{.Normalized.Name}})
|
|
||||||
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
event.Raw = log
|
|
||||||
return event, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// tmplSourceJava is the Java source template use to generate the contract binding
|
||||||
|
// 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 org.ethereum.geth.internal.*;
|
||||||
|
|
||||||
|
{{range $contract := .Contracts}}
|
||||||
|
public class {{.Type}} {
|
||||||
|
// ABI is the input ABI used to generate the binding from.
|
||||||
|
public final static String ABI = "{{.InputABI}}";
|
||||||
|
|
||||||
|
{{if .InputBin}}
|
||||||
|
// BYTECODE is the compiled bytecode used for deploying new contracts.
|
||||||
|
public final static byte[] BYTECODE = "{{.InputBin}}".getBytes();
|
||||||
|
|
||||||
|
// 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}} {{.Name}}{{end}}) throws Exception {
|
||||||
|
Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
|
||||||
|
{{range $index, $element := .Constructor.Inputs}}
|
||||||
|
args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
|
||||||
|
{{end}}
|
||||||
|
return new {{.Type}}(Geth.deployContract(auth, ABI, 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}} {{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}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
|
||||||
|
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
||||||
|
{{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
|
||||||
|
{{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .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) .Type}}();
|
||||||
|
{{end}}
|
||||||
|
return result;
|
||||||
|
{{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .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}} {{.Name}}{{end}}) throws Exception {
|
||||||
|
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
||||||
|
{{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
`
|
||||||
|
|||||||
189
accounts/abi/bind/topics.go
Normal file
189
accounts/abi/bind/topics.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2018 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 bind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Try to generate the topic based on simple types
|
||||||
|
switch rule := rule.(type) {
|
||||||
|
case common.Hash:
|
||||||
|
copy(topic[:], rule[:])
|
||||||
|
case common.Address:
|
||||||
|
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
||||||
|
case *big.Int:
|
||||||
|
blob := rule.Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case bool:
|
||||||
|
if rule {
|
||||||
|
topic[common.HashLength-1] = 1
|
||||||
|
}
|
||||||
|
case int8:
|
||||||
|
blob := big.NewInt(int64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case int16:
|
||||||
|
blob := big.NewInt(int64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case int32:
|
||||||
|
blob := big.NewInt(int64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case int64:
|
||||||
|
blob := big.NewInt(rule).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint8:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint16:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint32:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint64:
|
||||||
|
blob := new(big.Int).SetUint64(rule).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case string:
|
||||||
|
hash := crypto.Keccak256Hash([]byte(rule))
|
||||||
|
copy(topic[:], hash[:])
|
||||||
|
case []byte:
|
||||||
|
hash := crypto.Keccak256Hash(rule)
|
||||||
|
copy(topic[:], hash[:])
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Attempt to generate the topic from funky types
|
||||||
|
val := reflect.ValueOf(rule)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
||||||
|
reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topics[i] = append(topics[i], topic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big batch of reflect types for topic reconstruction.
|
||||||
|
var (
|
||||||
|
reflectHash = reflect.TypeOf(common.Hash{})
|
||||||
|
reflectAddress = reflect.TypeOf(common.Address{})
|
||||||
|
reflectBigInt = reflect.TypeOf(new(big.Int))
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseTopics converts the indexed topic fields into actual log field values.
|
||||||
|
//
|
||||||
|
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
||||||
|
// hashes as the topic value!
|
||||||
|
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
|
||||||
|
// Sanity check that the fields and topics match up
|
||||||
|
if len(fields) != len(topics) {
|
||||||
|
return errors.New("topic/field count mismatch")
|
||||||
|
}
|
||||||
|
// Iterate over all the fields and reconstruct them from topics
|
||||||
|
for _, arg := range fields {
|
||||||
|
if !arg.Indexed {
|
||||||
|
return errors.New("non-indexed field in topic reconstruction")
|
||||||
|
}
|
||||||
|
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
|
||||||
|
|
||||||
|
// Try to parse the topic back into the fields based on primitive types
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
if topics[0][common.HashLength-1] == 1 {
|
||||||
|
field.Set(reflect.ValueOf(true))
|
||||||
|
}
|
||||||
|
case reflect.Int8:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(int8(num.Int64())))
|
||||||
|
|
||||||
|
case reflect.Int16:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(int16(num.Int64())))
|
||||||
|
|
||||||
|
case reflect.Int32:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(int32(num.Int64())))
|
||||||
|
|
||||||
|
case reflect.Int64:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(num.Int64()))
|
||||||
|
|
||||||
|
case reflect.Uint8:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(uint8(num.Uint64())))
|
||||||
|
|
||||||
|
case reflect.Uint16:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(uint16(num.Uint64())))
|
||||||
|
|
||||||
|
case reflect.Uint32:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(uint32(num.Uint64())))
|
||||||
|
|
||||||
|
case reflect.Uint64:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(num.Uint64()))
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Ran out of plain primitive types, try custom types
|
||||||
|
switch field.Type() {
|
||||||
|
case reflectHash: // Also covers all dynamic types
|
||||||
|
field.Set(reflect.ValueOf(topics[0]))
|
||||||
|
|
||||||
|
case reflectAddress:
|
||||||
|
var addr common.Address
|
||||||
|
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
|
||||||
|
field.Set(reflect.ValueOf(addr))
|
||||||
|
|
||||||
|
case reflectBigInt:
|
||||||
|
num := new(big.Int).SetBytes(topics[0][:])
|
||||||
|
field.Set(reflect.ValueOf(num))
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Ran out of custom types, try the crazies
|
||||||
|
switch {
|
||||||
|
case arg.Type.T == abi.FixedBytesTy:
|
||||||
|
reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:]))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topics = topics[1:]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -18,10 +18,9 @@ package bind
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
@@ -36,16 +35,14 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
|
|||||||
logger := log.New("hash", tx.Hash())
|
logger := log.New("hash", tx.Hash())
|
||||||
for {
|
for {
|
||||||
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
||||||
if err == nil {
|
if receipt != nil {
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
if errors.Is(err, ethereum.NotFound) {
|
|
||||||
logger.Trace("Transaction not yet mined")
|
|
||||||
} else {
|
|
||||||
logger.Trace("Receipt retrieval failed", "err", err)
|
logger.Trace("Receipt retrieval failed", "err", err)
|
||||||
|
} else {
|
||||||
|
logger.Trace("Transaction not yet mined")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the next round.
|
// Wait for the next round.
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -59,14 +56,14 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
|
|||||||
// contract address when it is mined. It stops waiting when ctx is canceled.
|
// contract address when it is mined. It stops waiting when ctx is canceled.
|
||||||
func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
|
func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
return common.Address{}, errors.New("tx is not contract creation")
|
return common.Address{}, fmt.Errorf("tx is not contract creation")
|
||||||
}
|
}
|
||||||
receipt, err := WaitMined(ctx, b, tx)
|
receipt, err := WaitMined(ctx, b, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
if receipt.ContractAddress == (common.Address{}) {
|
if receipt.ContractAddress == (common.Address{}) {
|
||||||
return common.Address{}, errors.New("zero address")
|
return common.Address{}, fmt.Errorf("zero address")
|
||||||
}
|
}
|
||||||
// Check that code has indeed been deployed at the address.
|
// Check that code has indeed been deployed at the address.
|
||||||
// This matters on pre-Homestead chains: OOG in the constructor
|
// This matters on pre-Homestead chains: OOG in the constructor
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package bind_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -56,17 +55,12 @@ func TestWaitDeployed(t *testing.T) {
|
|||||||
for name, test := range waitDeployedTests {
|
for name, test := range waitDeployedTests {
|
||||||
backend := backends.NewSimulatedBackend(
|
backend := backends.NewSimulatedBackend(
|
||||||
core.GenesisAlloc{
|
core.GenesisAlloc{
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||||
},
|
}, 10000000,
|
||||||
10000000,
|
|
||||||
)
|
)
|
||||||
defer backend.Close()
|
|
||||||
|
|
||||||
// Create the transaction
|
// Create the transaction.
|
||||||
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
|
||||||
|
|
||||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
|
|
||||||
// Wait for it to get mined in the background.
|
// Wait for it to get mined in the background.
|
||||||
@@ -88,7 +82,7 @@ func TestWaitDeployed(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case <-mined:
|
case <-mined:
|
||||||
if err != test.wantErr {
|
if err != test.wantErr {
|
||||||
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
|
t.Errorf("test %q: error mismatch: got %q, want %q", name, err, test.wantErr)
|
||||||
}
|
}
|
||||||
if address != test.wantAddress {
|
if address != test.wantAddress {
|
||||||
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
||||||
@@ -98,43 +92,3 @@ func TestWaitDeployed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitDeployedCornerCases(t *testing.T) {
|
|
||||||
backend := backends.NewSimulatedBackend(
|
|
||||||
core.GenesisAlloc{
|
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
|
||||||
},
|
|
||||||
10000000,
|
|
||||||
)
|
|
||||||
defer backend.Close()
|
|
||||||
|
|
||||||
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
|
||||||
|
|
||||||
// Create a transaction to an account.
|
|
||||||
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 missmatch: want %q, got %q, ", notContentCreation, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a transaction that is not mined.
|
|
||||||
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
contextCanceled := errors.New("context canceled")
|
|
||||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() {
|
|
||||||
t.Errorf("error missmatch: want %q, got %q, ", contextCanceled, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
backend.SendTransaction(ctx, tx)
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,77 +17,68 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error struct {
|
var (
|
||||||
Name string
|
errBadBool = errors.New("abi: improperly encoded boolean value")
|
||||||
Inputs Arguments
|
)
|
||||||
str string
|
|
||||||
|
|
||||||
// Sig contains the string signature according to the ABI spec.
|
// formatSliceString formats the reflection kind with the given slice size
|
||||||
// e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
|
// and returns a formatted string representation.
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
||||||
Sig string
|
if sliceSize == -1 {
|
||||||
|
return fmt.Sprintf("[]%v", kind)
|
||||||
// ID returns the canonical representation of the error's signature used by the
|
}
|
||||||
// abi definition to identify event names and types.
|
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
||||||
ID common.Hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(name string, inputs Arguments) Error {
|
// sliceTypeCheck checks that the given slice can by assigned to the reflection
|
||||||
// sanitize inputs to remove inputs without names
|
// type in t.
|
||||||
// and precompute string and sig representation.
|
func sliceTypeCheck(t Type, val reflect.Value) error {
|
||||||
names := make([]string, len(inputs))
|
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||||
types := make([]string, len(inputs))
|
return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
|
||||||
for i, input := range inputs {
|
|
||||||
if input.Name == "" {
|
|
||||||
inputs[i] = Argument{
|
|
||||||
Name: fmt.Sprintf("arg%d", i),
|
|
||||||
Indexed: input.Indexed,
|
|
||||||
Type: input.Type,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.T == ArrayTy && val.Len() != t.Size {
|
||||||
|
return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Elem.T == SliceTy {
|
||||||
|
if val.Len() > 0 {
|
||||||
|
return sliceTypeCheck(*t.Elem, val.Index(0))
|
||||||
|
}
|
||||||
|
} else if t.Elem.T == ArrayTy {
|
||||||
|
return sliceTypeCheck(*t.Elem, val.Index(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
|
||||||
|
return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeCheck checks that the given reflection value can be assigned to the reflection
|
||||||
|
// type in t.
|
||||||
|
func typeCheck(t Type, value reflect.Value) error {
|
||||||
|
if t.T == SliceTy || t.T == ArrayTy {
|
||||||
|
return sliceTypeCheck(t, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check base type validity. Element types will be checked later on.
|
||||||
|
if t.Kind != value.Kind() {
|
||||||
|
return typeErr(t.Kind, value.Kind())
|
||||||
|
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
||||||
|
return typeErr(t.Type, value.Type())
|
||||||
} else {
|
} else {
|
||||||
inputs[i] = input
|
return nil
|
||||||
}
|
|
||||||
// string representation
|
|
||||||
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
|
|
||||||
if input.Indexed {
|
|
||||||
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
|
|
||||||
}
|
|
||||||
// sig representation
|
|
||||||
types[i] = input.Type.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
|
|
||||||
sig := fmt.Sprintf("%v(%v)", name, strings.Join(types, ","))
|
|
||||||
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
|
|
||||||
|
|
||||||
return Error{
|
|
||||||
Name: name,
|
|
||||||
Inputs: inputs,
|
|
||||||
str: str,
|
|
||||||
Sig: sig,
|
|
||||||
ID: id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Error) String() string {
|
// typeErr returns a formatted type casting error.
|
||||||
return e.str
|
func typeErr(expected, got interface{}) error {
|
||||||
}
|
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
|
||||||
|
|
||||||
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:])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
// Copyright 2016 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 (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
// and returns a formatted string representation.
|
|
||||||
func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
|
||||||
if sliceSize == -1 {
|
|
||||||
return fmt.Sprintf("[]%v", kind)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sliceTypeCheck checks that the given slice can by assigned to the reflection
|
|
||||||
// type in t.
|
|
||||||
func sliceTypeCheck(t Type, val reflect.Value) error {
|
|
||||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
|
||||||
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.T == ArrayTy && val.Len() != t.Size {
|
|
||||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
|
|
||||||
if val.Len() > 0 {
|
|
||||||
return sliceTypeCheck(*t.Elem, val.Index(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
|
||||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeCheck checks that the given reflection value can be assigned to the reflection
|
|
||||||
// type in t.
|
|
||||||
func typeCheck(t Type, value reflect.Value) error {
|
|
||||||
if t.T == SliceTy || t.T == ArrayTy {
|
|
||||||
return sliceTypeCheck(t, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check base type validity. Element types will be checked later on.
|
|
||||||
if t.GetType().Kind() != value.Kind() {
|
|
||||||
return typeErr(t.GetType().Kind(), value.Kind())
|
|
||||||
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
|
||||||
return typeErr(t.GetType(), value.Type())
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeErr returns a formatted type casting error.
|
|
||||||
func typeErr(expected, got interface{}) error {
|
|
||||||
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
|
|
||||||
}
|
|
||||||
@@ -28,76 +28,30 @@ import (
|
|||||||
// holds type information (inputs) about the yielded output. Anonymous events
|
// holds type information (inputs) about the yielded output. Anonymous events
|
||||||
// don't get the signature canonical representation as the first LOG topic.
|
// don't get the signature canonical representation as the first LOG topic.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Name 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.
|
|
||||||
//
|
|
||||||
// e.g.
|
|
||||||
// These are two events that have the same name:
|
|
||||||
// * foo(int,int)
|
|
||||||
// * foo(uint,uint)
|
|
||||||
// The event name of the first one will be resolved as foo while the second one
|
|
||||||
// will be resolved as foo0.
|
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// RawName is the raw event name parsed from ABI.
|
|
||||||
RawName string
|
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Inputs Arguments
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEvent creates a new Event.
|
|
||||||
// It sanitizes the input arguments to remove unnamed arguments.
|
|
||||||
// It also precomputes the id, signature and string representation
|
|
||||||
// of the event.
|
|
||||||
func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
|
|
||||||
// sanitize inputs to remove inputs without names
|
|
||||||
// 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{
|
|
||||||
Name: fmt.Sprintf("arg%d", i),
|
|
||||||
Indexed: input.Indexed,
|
|
||||||
Type: input.Type,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inputs[i] = input
|
|
||||||
}
|
|
||||||
// string representation
|
|
||||||
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
|
|
||||||
if input.Indexed {
|
|
||||||
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
|
|
||||||
}
|
|
||||||
// sig representation
|
|
||||||
types[i] = input.Type.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
str := fmt.Sprintf("event %v(%v)", rawName, strings.Join(names, ", "))
|
|
||||||
sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
|
||||||
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
|
|
||||||
|
|
||||||
return Event{
|
|
||||||
Name: name,
|
|
||||||
RawName: rawName,
|
|
||||||
Anonymous: anonymous,
|
|
||||||
Inputs: inputs,
|
|
||||||
str: str,
|
|
||||||
Sig: sig,
|
|
||||||
ID: id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Event) String() string {
|
func (e Event) String() string {
|
||||||
return e.str
|
inputs := make([]string, len(e.Inputs))
|
||||||
|
for i, input := range e.Inputs {
|
||||||
|
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||||
|
if input.Indexed {
|
||||||
|
inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("event %v(%v)", e.Name, strings.Join(inputs, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id returns the canonical representation of the event's signature used by the
|
||||||
|
// abi definition to identify event names and types.
|
||||||
|
func (e Event) Id() common.Hash {
|
||||||
|
types := make([]string, len(e.Inputs))
|
||||||
|
i := 0
|
||||||
|
for _, input := range e.Inputs {
|
||||||
|
types[i] = input.Type.String()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ func TestEventId(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for name, event := range abi.Events {
|
for name, event := range abi.Events {
|
||||||
if event.ID != test.expectations[name] {
|
if event.Id() != test.expectations[name] {
|
||||||
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID)
|
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,6 +147,10 @@ func TestEventString(t *testing.T) {
|
|||||||
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
|
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
|
||||||
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
|
type testStruct struct {
|
||||||
|
Value1 [2]uint8
|
||||||
|
Value2 uint8
|
||||||
|
}
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
@@ -154,13 +158,14 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
|||||||
for ; i <= 3; i++ {
|
for ; i <= 3; i++ {
|
||||||
b.Write(packNum(reflect.ValueOf(i)))
|
b.Write(packNum(reflect.ValueOf(i)))
|
||||||
}
|
}
|
||||||
unpacked, err := abi.Unpack("test", b.Bytes())
|
var rst testStruct
|
||||||
require.NoError(t, err)
|
require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
|
||||||
require.Equal(t, [2]uint8{1, 2}, unpacked[0])
|
require.Equal(t, [2]uint8{1, 2}, rst.Value1)
|
||||||
require.Equal(t, uint8(3), unpacked[1])
|
require.Equal(t, uint8(3), rst.Value2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEventTupleUnpack(t *testing.T) {
|
func TestEventTupleUnpack(t *testing.T) {
|
||||||
|
|
||||||
type EventTransfer struct {
|
type EventTransfer struct {
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
}
|
}
|
||||||
@@ -168,7 +173,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||||||
type EventTransferWithTag struct {
|
type EventTransferWithTag struct {
|
||||||
// this is valid because `value` is not exportable,
|
// this is valid because `value` is not exportable,
|
||||||
// so value is only unmarshalled into `Value1`.
|
// so value is only unmarshalled into `Value1`.
|
||||||
value *big.Int //lint:ignore U1000 unused field is part of test
|
value *big.Int
|
||||||
Value1 *big.Int `abi:"value"`
|
Value1 *big.Int `abi:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,14 +312,14 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||||||
&[]interface{}{common.Address{}, new(big.Int)},
|
&[]interface{}{common.Address{}, new(big.Int)},
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
jsonEventPledge,
|
jsonEventPledge,
|
||||||
"abi: insufficient number of arguments for unpack, want 3, got 2",
|
"abi: insufficient number of elements in the list/array for unpack, want 3, got 2",
|
||||||
"Can not unpack Pledge event into too short slice",
|
"Can not unpack Pledge event into too short slice",
|
||||||
}, {
|
}, {
|
||||||
pledgeData1,
|
pledgeData1,
|
||||||
new(map[string]interface{}),
|
new(map[string]interface{}),
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
jsonEventPledge,
|
jsonEventPledge,
|
||||||
"abi:[2] cannot unmarshal tuple in to map[string]interface {}",
|
"abi: cannot unmarshal tuple into map[string]interface {}",
|
||||||
"Can not unpack Pledge event into map",
|
"Can not unpack Pledge event into map",
|
||||||
}, {
|
}, {
|
||||||
mixedCaseData1,
|
mixedCaseData1,
|
||||||
@@ -346,14 +351,48 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass
|
|||||||
var e Event
|
var e Event
|
||||||
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
|
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
|
||||||
a := ABI{Events: map[string]Event{"e": e}}
|
a := ABI{Events: map[string]Event{"e": e}}
|
||||||
return a.UnpackIntoInterface(dest, "e", data)
|
return a.Unpack(dest, "e", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Taken from
|
||||||
|
https://github.com/ethereum/go-ethereum/pull/15568
|
||||||
|
*/
|
||||||
|
|
||||||
|
type testResult struct {
|
||||||
|
Values [2]*big.Int
|
||||||
|
Value1 *big.Int
|
||||||
|
Value2 *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
definition string
|
||||||
|
want testResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc testCase) encoded(intType, arrayType Type) []byte {
|
||||||
|
var b bytes.Buffer
|
||||||
|
if tc.want.Value1 != nil {
|
||||||
|
val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
|
||||||
|
b.Write(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
|
||||||
|
val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
|
||||||
|
b.Write(val)
|
||||||
|
}
|
||||||
|
if tc.want.Value2 != nil {
|
||||||
|
val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
|
||||||
|
b.Write(val)
|
||||||
|
}
|
||||||
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
||||||
func TestEventUnpackIndexed(t *testing.T) {
|
func TestEventUnpackIndexed(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
type testStruct struct {
|
type testStruct struct {
|
||||||
Value1 uint8 // indexed
|
Value1 uint8
|
||||||
Value2 uint8
|
Value2 uint8
|
||||||
}
|
}
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
@@ -361,16 +400,16 @@ func TestEventUnpackIndexed(t *testing.T) {
|
|||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
b.Write(packNum(reflect.ValueOf(uint8(8))))
|
b.Write(packNum(reflect.ValueOf(uint8(8))))
|
||||||
var rst testStruct
|
var rst testStruct
|
||||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
|
||||||
require.Equal(t, uint8(0), rst.Value1)
|
require.Equal(t, uint8(0), rst.Value1)
|
||||||
require.Equal(t, uint8(8), rst.Value2)
|
require.Equal(t, uint8(8), rst.Value2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
|
// TestEventIndexedWithArrayUnpack verifies that decoder will not overlow when static array is indexed input.
|
||||||
func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
|
||||||
type testStruct struct {
|
type testStruct struct {
|
||||||
Value1 [2]uint8 // indexed
|
Value1 [2]uint8
|
||||||
Value2 string
|
Value2 string
|
||||||
}
|
}
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
@@ -383,7 +422,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
|||||||
b.Write(common.RightPadBytes([]byte(stringOut), 32))
|
b.Write(common.RightPadBytes([]byte(stringOut), 32))
|
||||||
|
|
||||||
var rst testStruct
|
var rst testStruct
|
||||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
|
||||||
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
|
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
|
||||||
require.Equal(t, stringOut, rst.Value2)
|
require.Equal(t, stringOut, rst.Value2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,145 +23,55 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FunctionType represents different types of functions a contract might have.
|
|
||||||
type FunctionType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Constructor represents the constructor of the contract.
|
|
||||||
// The constructor function is called while deploying a contract.
|
|
||||||
Constructor FunctionType = iota
|
|
||||||
// Fallback represents the fallback function.
|
|
||||||
// This function is executed if no other function matches the given function
|
|
||||||
// signature and no receive function is specified.
|
|
||||||
Fallback
|
|
||||||
// Receive represents the receive function.
|
|
||||||
// This function is executed on plain Ether transfers.
|
|
||||||
Receive
|
|
||||||
// Function represents a normal function.
|
|
||||||
Function
|
|
||||||
)
|
|
||||||
|
|
||||||
// Method represents a callable given a `Name` and whether the method is a constant.
|
// Method represents a callable given a `Name` and whether the method is a constant.
|
||||||
// If the method is `Const` no transaction needs to be created for this
|
// If the method is `Const` no transaction needs to be created for this
|
||||||
// particular Method call. It can easily be simulated using a local VM.
|
// particular Method call. It can easily be simulated using a local VM.
|
||||||
// For example a `Balance()` method only needs to retrieve something
|
// For example a `Balance()` method only needs to retrieve something
|
||||||
// from the storage and therefore requires no Tx to be sent to the
|
// from the storage and therefor requires no Tx to be send to the
|
||||||
// network. A method such as `Transact` does require a Tx and thus will
|
// network. A method such as `Transact` does require a Tx and thus will
|
||||||
// be flagged `false`.
|
// be flagged `true`.
|
||||||
// Input specifies the required input parameters for this gives method.
|
// Input specifies the required input parameters for this gives method.
|
||||||
type Method struct {
|
type Method struct {
|
||||||
// Name is the method name used for internal representation. It's derived from
|
|
||||||
// the raw name and a suffix will be added in the case of a function overload.
|
|
||||||
//
|
|
||||||
// e.g.
|
|
||||||
// These are two functions that have the same name:
|
|
||||||
// * foo(int,int)
|
|
||||||
// * foo(uint,uint)
|
|
||||||
// The method name of the first one will be resolved as foo while the second one
|
|
||||||
// will be resolved as foo0.
|
|
||||||
Name string
|
Name string
|
||||||
RawName string // RawName is the raw method name parsed from ABI
|
Const bool
|
||||||
|
|
||||||
// Type indicates whether the method is a
|
|
||||||
// special fallback introduced in solidity v0.6.0
|
|
||||||
Type FunctionType
|
|
||||||
|
|
||||||
// StateMutability indicates the mutability state of method,
|
|
||||||
// the default value is nonpayable. It can be empty if the abi
|
|
||||||
// is generated by legacy compiler.
|
|
||||||
StateMutability string
|
|
||||||
|
|
||||||
// Legacy indicators generated by compiler before v0.6.0
|
|
||||||
Constant bool
|
|
||||||
Payable bool
|
|
||||||
|
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
Outputs Arguments
|
Outputs Arguments
|
||||||
str string
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
|
||||||
// e.g. function 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 method's signature used by the
|
|
||||||
// abi definition to identify method names and types.
|
|
||||||
ID []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMethod creates a new Method.
|
// Sig returns the methods string signature according to the ABI spec.
|
||||||
// A method should always be created using NewMethod.
|
//
|
||||||
// It also precomputes the sig representation and the string representation
|
// Example
|
||||||
// of the method.
|
//
|
||||||
func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
|
// function foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
var (
|
//
|
||||||
types = make([]string, len(inputs))
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
inputNames = make([]string, len(inputs))
|
func (method Method) Sig() string {
|
||||||
outputNames = make([]string, len(outputs))
|
types := make([]string, len(method.Inputs))
|
||||||
)
|
for i, input := range method.Inputs {
|
||||||
for i, input := range inputs {
|
|
||||||
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
|
||||||
types[i] = input.Type.String()
|
types[i] = input.Type.String()
|
||||||
}
|
}
|
||||||
for i, output := range outputs {
|
return fmt.Sprintf("%v(%v)", method.Name, strings.Join(types, ","))
|
||||||
outputNames[i] = output.Type.String()
|
|
||||||
if len(output.Name) > 0 {
|
|
||||||
outputNames[i] += fmt.Sprintf(" %v", output.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// calculate the signature and method id. Note only function
|
|
||||||
// has meaningful signature and id.
|
|
||||||
var (
|
|
||||||
sig string
|
|
||||||
id []byte
|
|
||||||
)
|
|
||||||
if funType == Function {
|
|
||||||
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
|
||||||
id = crypto.Keccak256([]byte(sig))[:4]
|
|
||||||
}
|
|
||||||
// Extract meaningful state mutability of solidity method.
|
|
||||||
// If it's default value, never print it.
|
|
||||||
state := mutability
|
|
||||||
if state == "nonpayable" {
|
|
||||||
state = ""
|
|
||||||
}
|
|
||||||
if state != "" {
|
|
||||||
state = state + " "
|
|
||||||
}
|
|
||||||
identity := fmt.Sprintf("function %v", rawName)
|
|
||||||
if funType == Fallback {
|
|
||||||
identity = "fallback"
|
|
||||||
} else if funType == Receive {
|
|
||||||
identity = "receive"
|
|
||||||
} else if funType == Constructor {
|
|
||||||
identity = "constructor"
|
|
||||||
}
|
|
||||||
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
|
||||||
|
|
||||||
return Method{
|
|
||||||
Name: name,
|
|
||||||
RawName: rawName,
|
|
||||||
Type: funType,
|
|
||||||
StateMutability: mutability,
|
|
||||||
Constant: isConst,
|
|
||||||
Payable: isPayable,
|
|
||||||
Inputs: inputs,
|
|
||||||
Outputs: outputs,
|
|
||||||
str: str,
|
|
||||||
Sig: sig,
|
|
||||||
ID: id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (method Method) String() string {
|
func (method Method) String() string {
|
||||||
return method.str
|
inputs := make([]string, len(method.Inputs))
|
||||||
|
for i, input := range method.Inputs {
|
||||||
|
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||||
|
}
|
||||||
|
outputs := make([]string, len(method.Outputs))
|
||||||
|
for i, output := range method.Outputs {
|
||||||
|
outputs[i] = output.Type.String()
|
||||||
|
if len(output.Name) > 0 {
|
||||||
|
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constant := ""
|
||||||
|
if method.Const {
|
||||||
|
constant = "constant "
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsConstant returns the indicator whether the method is read-only.
|
func (method Method) Id() []byte {
|
||||||
func (method Method) IsConstant() bool {
|
return crypto.Keccak256([]byte(method.Sig()))[:4]
|
||||||
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPayable returns the indicator whether the method can process
|
|
||||||
// plain ether transfers.
|
|
||||||
func (method Method) IsPayable() bool {
|
|
||||||
return method.StateMutability == "payable" || method.Payable
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2016 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
@@ -23,15 +23,9 @@ import (
|
|||||||
|
|
||||||
const methoddata = `
|
const methoddata = `
|
||||||
[
|
[
|
||||||
{"type": "function", "name": "balance", "stateMutability": "view"},
|
{ "type" : "function", "name" : "balance", "constant" : true },
|
||||||
{"type": "function", "name": "send", "inputs": [{ "name": "amount", "type": "uint256" }]},
|
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
||||||
{"type": "function", "name": "transfer", "inputs": [{"name": "from", "type": "address"}, {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}], "outputs": [{"name": "success", "type": "bool"}]},
|
{ "type" : "function", "name" : "transfer", "constant" : false, "inputs" : [ { "name" : "from", "type" : "address" }, { "name" : "to", "type" : "address" }, { "name" : "value", "type" : "uint256" } ], "outputs" : [ { "name" : "success", "type" : "bool" } ] }
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple"}],"name":"tuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[]"}],"name":"tupleSlice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5]"}],"name":"tupleArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5][]"}],"name":"complexTuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
|
||||||
{"stateMutability":"nonpayable","type":"fallback"},
|
|
||||||
{"stateMutability":"payable","type":"receive"}
|
|
||||||
]`
|
]`
|
||||||
|
|
||||||
func TestMethodString(t *testing.T) {
|
func TestMethodString(t *testing.T) {
|
||||||
@@ -41,7 +35,7 @@ func TestMethodString(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
method: "balance",
|
method: "balance",
|
||||||
expectation: "function balance() view returns()",
|
expectation: "function balance() constant returns()",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: "send",
|
method: "send",
|
||||||
@@ -51,30 +45,6 @@ func TestMethodString(t *testing.T) {
|
|||||||
method: "transfer",
|
method: "transfer",
|
||||||
expectation: "function transfer(address from, address to, uint256 value) returns(bool success)",
|
expectation: "function transfer(address from, address to, uint256 value) returns(bool success)",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
method: "tuple",
|
|
||||||
expectation: "function tuple((uint256,uint256) a) returns()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "tupleArray",
|
|
||||||
expectation: "function tupleArray((uint256,uint256)[5] a) returns()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "tupleSlice",
|
|
||||||
expectation: "function tupleSlice((uint256,uint256)[] a) returns()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "complexTuple",
|
|
||||||
expectation: "function complexTuple((uint256,uint256)[5][] a) returns()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "fallback",
|
|
||||||
expectation: "fallback() returns()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "receive",
|
|
||||||
expectation: "receive() payable returns()",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(methoddata))
|
abi, err := JSON(strings.NewReader(methoddata))
|
||||||
@@ -83,63 +53,9 @@ func TestMethodString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
var got string
|
got := abi.Methods[test.method].String()
|
||||||
if test.method == "fallback" {
|
|
||||||
got = abi.Fallback.String()
|
|
||||||
} else if test.method == "receive" {
|
|
||||||
got = abi.Receive.String()
|
|
||||||
} else {
|
|
||||||
got = abi.Methods[test.method].String()
|
|
||||||
}
|
|
||||||
if got != test.expectation {
|
if got != test.expectation {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMethodSig(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
method string
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
method: "balance",
|
|
||||||
expect: "balance()",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "send",
|
|
||||||
expect: "send(uint256)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "transfer",
|
|
||||||
expect: "transfer(address,address,uint256)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "tuple",
|
|
||||||
expect: "tuple((uint256,uint256))",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "tupleArray",
|
|
||||||
expect: "tupleArray((uint256,uint256)[5])",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "tupleSlice",
|
|
||||||
expect: "tupleSlice((uint256,uint256)[])",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "complexTuple",
|
|
||||||
expect: "complexTuple((uint256,uint256)[5][])",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
abi, err := JSON(strings.NewReader(methoddata))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range cases {
|
|
||||||
got := abi.Methods[test.method].Sig
|
|
||||||
if got != test.expect {
|
|
||||||
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
44
accounts/abi/numbers.go
Normal file
44
accounts/abi/numbers.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2015 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 (
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bigT = reflect.TypeOf(&big.Int{})
|
||||||
|
derefbigT = reflect.TypeOf(big.Int{})
|
||||||
|
uint8T = reflect.TypeOf(uint8(0))
|
||||||
|
uint16T = reflect.TypeOf(uint16(0))
|
||||||
|
uint32T = reflect.TypeOf(uint32(0))
|
||||||
|
uint64T = reflect.TypeOf(uint64(0))
|
||||||
|
int8T = reflect.TypeOf(int8(0))
|
||||||
|
int16T = reflect.TypeOf(int16(0))
|
||||||
|
int32T = reflect.TypeOf(int32(0))
|
||||||
|
int64T = reflect.TypeOf(int64(0))
|
||||||
|
addressT = reflect.TypeOf(common.Address{})
|
||||||
|
)
|
||||||
|
|
||||||
|
// U256 converts a big Int into a 256bit EVM number.
|
||||||
|
func U256(n *big.Int) []byte {
|
||||||
|
return math.PaddedBigBytes(math.U256(n), 32)
|
||||||
|
}
|
||||||
33
accounts/abi/numbers_test.go
Normal file
33
accounts/abi/numbers_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2015 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 (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumberTypes(t *testing.T) {
|
||||||
|
ubytes := make([]byte, 32)
|
||||||
|
ubytes[31] = 1
|
||||||
|
|
||||||
|
unsigned := U256(big.NewInt(1))
|
||||||
|
if !bytes.Equal(unsigned, ubytes) {
|
||||||
|
t.Errorf("expected %x got %x", ubytes, unsigned)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@@ -27,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// packBytesSlice packs the given bytes as [L, V] as the canonical representation
|
// packBytesSlice packs the given bytes as [L, V] as the canonical representation
|
||||||
// bytes slice.
|
// bytes slice
|
||||||
func packBytesSlice(bytes []byte, l int) []byte {
|
func packBytesSlice(bytes []byte, l int) []byte {
|
||||||
len := packNum(reflect.ValueOf(l))
|
len := packNum(reflect.ValueOf(l))
|
||||||
return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
|
return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
|
||||||
@@ -35,51 +33,49 @@ func packBytesSlice(bytes []byte, l int) []byte {
|
|||||||
|
|
||||||
// packElement packs the given reflect value according to the abi specification in
|
// packElement packs the given reflect value according to the abi specification in
|
||||||
// t.
|
// t.
|
||||||
func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
func packElement(t Type, reflectValue reflect.Value) []byte {
|
||||||
switch t.T {
|
switch t.T {
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return packNum(reflectValue), nil
|
return packNum(reflectValue)
|
||||||
case StringTy:
|
case StringTy:
|
||||||
return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()), nil
|
return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len())
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.LeftPadBytes(reflectValue.Bytes(), 32), nil
|
return common.LeftPadBytes(reflectValue.Bytes(), 32)
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
if reflectValue.Bool() {
|
if reflectValue.Bool() {
|
||||||
return math.PaddedBigBytes(common.Big1, 32), nil
|
return math.PaddedBigBytes(common.Big1, 32)
|
||||||
}
|
}
|
||||||
return math.PaddedBigBytes(common.Big0, 32), nil
|
return math.PaddedBigBytes(common.Big0, 32)
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
|
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len())
|
||||||
return []byte{}, errors.New("Bytes type is neither slice nor array")
|
|
||||||
}
|
|
||||||
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
|
|
||||||
case FixedBytesTy, FunctionTy:
|
case FixedBytesTy, FunctionTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
return common.RightPadBytes(reflectValue.Bytes(), 32)
|
||||||
default:
|
|
||||||
return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation.
|
|
||||||
func packNum(value reflect.Value) []byte {
|
|
||||||
switch kind := value.Kind(); kind {
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return math.U256Bytes(new(big.Int).SetUint64(value.Uint()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return math.U256Bytes(big.NewInt(value.Int()))
|
|
||||||
case reflect.Ptr:
|
|
||||||
return math.U256Bytes(new(big.Int).Set(value.Interface().(*big.Int)))
|
|
||||||
default:
|
default:
|
||||||
panic("abi: fatal error")
|
panic("abi: fatal error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
|
||||||
|
func packNum(value reflect.Value) []byte {
|
||||||
|
switch kind := value.Kind(); kind {
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return U256(new(big.Int).SetUint64(value.Uint()))
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return U256(big.NewInt(value.Int()))
|
||||||
|
case reflect.Ptr:
|
||||||
|
return U256(value.Interface().(*big.Int))
|
||||||
|
default:
|
||||||
|
panic("abi: fatal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,51 +18,623 @@ package abi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestPack tests the general pack/unpack tests in packing_test.go
|
|
||||||
func TestPack(t *testing.T) {
|
func TestPack(t *testing.T) {
|
||||||
for i, test := range packUnpackTests {
|
for i, test := range []struct {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
typ string
|
||||||
encb, err := hex.DecodeString(test.packed)
|
components []ArgumentMarshaling
|
||||||
if err != nil {
|
input interface{}
|
||||||
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
output []byte
|
||||||
}
|
}{
|
||||||
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
{
|
||||||
inAbi, err := JSON(strings.NewReader(inDef))
|
"uint8",
|
||||||
if err != nil {
|
nil,
|
||||||
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
uint8(2),
|
||||||
}
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
var packed []byte
|
},
|
||||||
packed, err = inAbi.Pack("method", test.unpacked)
|
{
|
||||||
|
"uint8[]",
|
||||||
|
nil,
|
||||||
|
[]uint8{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint16",
|
||||||
|
nil,
|
||||||
|
uint16(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint16[]",
|
||||||
|
nil,
|
||||||
|
[]uint16{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint32",
|
||||||
|
nil,
|
||||||
|
uint32(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint32[]",
|
||||||
|
nil,
|
||||||
|
[]uint32{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint64",
|
||||||
|
nil,
|
||||||
|
uint64(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint64[]",
|
||||||
|
nil,
|
||||||
|
[]uint64{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint256",
|
||||||
|
nil,
|
||||||
|
big.NewInt(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint256[]",
|
||||||
|
nil,
|
||||||
|
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int8",
|
||||||
|
nil,
|
||||||
|
int8(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int8[]",
|
||||||
|
nil,
|
||||||
|
[]int8{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int16",
|
||||||
|
nil,
|
||||||
|
int16(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int16[]",
|
||||||
|
nil,
|
||||||
|
[]int16{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int32",
|
||||||
|
nil,
|
||||||
|
int32(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int32[]",
|
||||||
|
nil,
|
||||||
|
[]int32{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int64",
|
||||||
|
nil,
|
||||||
|
int64(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int64[]",
|
||||||
|
nil,
|
||||||
|
[]int64{1, 2},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int256",
|
||||||
|
nil,
|
||||||
|
big.NewInt(2),
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int256[]",
|
||||||
|
nil,
|
||||||
|
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes1",
|
||||||
|
nil,
|
||||||
|
[1]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes2",
|
||||||
|
nil,
|
||||||
|
[2]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes3",
|
||||||
|
nil,
|
||||||
|
[3]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes4",
|
||||||
|
nil,
|
||||||
|
[4]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes5",
|
||||||
|
nil,
|
||||||
|
[5]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes6",
|
||||||
|
nil,
|
||||||
|
[6]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes7",
|
||||||
|
nil,
|
||||||
|
[7]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes8",
|
||||||
|
nil,
|
||||||
|
[8]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes9",
|
||||||
|
nil,
|
||||||
|
[9]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes10",
|
||||||
|
nil,
|
||||||
|
[10]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes11",
|
||||||
|
nil,
|
||||||
|
[11]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes12",
|
||||||
|
nil,
|
||||||
|
[12]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes13",
|
||||||
|
nil,
|
||||||
|
[13]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes14",
|
||||||
|
nil,
|
||||||
|
[14]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes15",
|
||||||
|
nil,
|
||||||
|
[15]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes16",
|
||||||
|
nil,
|
||||||
|
[16]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes17",
|
||||||
|
nil,
|
||||||
|
[17]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes18",
|
||||||
|
nil,
|
||||||
|
[18]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes19",
|
||||||
|
nil,
|
||||||
|
[19]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes20",
|
||||||
|
nil,
|
||||||
|
[20]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes21",
|
||||||
|
nil,
|
||||||
|
[21]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes22",
|
||||||
|
nil,
|
||||||
|
[22]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes23",
|
||||||
|
nil,
|
||||||
|
[23]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes24",
|
||||||
|
nil,
|
||||||
|
[24]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes25",
|
||||||
|
nil,
|
||||||
|
[25]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes26",
|
||||||
|
nil,
|
||||||
|
[26]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes27",
|
||||||
|
nil,
|
||||||
|
[27]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes28",
|
||||||
|
nil,
|
||||||
|
[28]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes29",
|
||||||
|
nil,
|
||||||
|
[29]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes30",
|
||||||
|
nil,
|
||||||
|
[30]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes31",
|
||||||
|
nil,
|
||||||
|
[31]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes32",
|
||||||
|
nil,
|
||||||
|
[32]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uint32[2][3][4]",
|
||||||
|
nil,
|
||||||
|
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address[]",
|
||||||
|
nil,
|
||||||
|
[]common.Address{{1}, {2}},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes32[]",
|
||||||
|
nil,
|
||||||
|
[]common.Hash{{1}, {2}},
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function",
|
||||||
|
nil,
|
||||||
|
[24]byte{1},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"string",
|
||||||
|
nil,
|
||||||
|
"foobar",
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"string[]",
|
||||||
|
nil,
|
||||||
|
[]string{"hello", "foobar"},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"string[2]",
|
||||||
|
nil,
|
||||||
|
[]string{"hello", "foobar"},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bytes32[][]",
|
||||||
|
nil,
|
||||||
|
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"bytes32[][2]",
|
||||||
|
nil,
|
||||||
|
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"bytes32[3][2]",
|
||||||
|
nil,
|
||||||
|
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
||||||
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// static tuple
|
||||||
|
"tuple",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "int64"},
|
||||||
|
{Name: "b", Type: "int256"},
|
||||||
|
{Name: "c", Type: "int256"},
|
||||||
|
{Name: "d", Type: "bool"},
|
||||||
|
{Name: "e", Type: "bytes32[3][2]"},
|
||||||
|
},
|
||||||
|
struct {
|
||||||
|
A int64
|
||||||
|
B *big.Int
|
||||||
|
C *big.Int
|
||||||
|
D bool
|
||||||
|
E [][]common.Hash
|
||||||
|
}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// dynamic tuple
|
||||||
|
"tuple",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "string"},
|
||||||
|
{Name: "b", Type: "int64"},
|
||||||
|
{Name: "c", Type: "bytes"},
|
||||||
|
{Name: "d", Type: "string[]"},
|
||||||
|
{Name: "e", Type: "int256[]"},
|
||||||
|
{Name: "f", Type: "address[]"},
|
||||||
|
},
|
||||||
|
struct {
|
||||||
|
FieldA string `abi:"a"` // Test whether abi tag works
|
||||||
|
FieldB int64 `abi:"b"`
|
||||||
|
C []byte
|
||||||
|
D []string
|
||||||
|
E []*big.Int
|
||||||
|
F []common.Address
|
||||||
|
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
|
||||||
|
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
|
||||||
|
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
|
||||||
|
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
|
||||||
|
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
|
||||||
|
"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// nested tuple
|
||||||
|
"tuple",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
|
||||||
|
{Name: "b", Type: "int256[]"},
|
||||||
|
},
|
||||||
|
struct {
|
||||||
|
A struct {
|
||||||
|
FieldA *big.Int `abi:"a"`
|
||||||
|
B []*big.Int
|
||||||
|
}
|
||||||
|
B []*big.Int
|
||||||
|
}{
|
||||||
|
A: struct {
|
||||||
|
FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
|
||||||
|
B []*big.Int
|
||||||
|
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
||||||
|
B: []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // a offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" + // a.b[1] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000"), // b[1] value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// tuple slice
|
||||||
|
"tuple[]",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "int256"},
|
||||||
|
{Name: "b", Type: "int256[]"},
|
||||||
|
},
|
||||||
|
[]struct {
|
||||||
|
A *big.Int
|
||||||
|
B []*big.Int
|
||||||
|
}{
|
||||||
|
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
||||||
|
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
|
||||||
|
},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" + // tuple[0].B[1] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].B[1] value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// static tuple array
|
||||||
|
"tuple[2]",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "int256"},
|
||||||
|
{Name: "b", Type: "int256"},
|
||||||
|
},
|
||||||
|
[2]struct {
|
||||||
|
A *big.Int
|
||||||
|
B *big.Int
|
||||||
|
}{
|
||||||
|
{big.NewInt(-1), big.NewInt(1)},
|
||||||
|
{big.NewInt(1), big.NewInt(-1)},
|
||||||
|
},
|
||||||
|
common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].b
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// dynamic tuple array
|
||||||
|
"tuple[2]",
|
||||||
|
[]ArgumentMarshaling{
|
||||||
|
{Name: "a", Type: "int256[]"},
|
||||||
|
},
|
||||||
|
[2]struct {
|
||||||
|
A []*big.Int
|
||||||
|
}{
|
||||||
|
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
|
||||||
|
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
|
||||||
|
},
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
typ, err := NewType(test.typ, test.components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(packed[4:], encb) {
|
output, err := typ.pack(reflect.ValueOf(test.input))
|
||||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
if err != nil {
|
||||||
|
t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(output, test.output) {
|
||||||
|
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMethodPack(t *testing.T) {
|
func TestMethodPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := abi.Methods["slice"].ID
|
sig := abi.Methods["slice"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
@@ -76,7 +648,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var addrA, addrB = common.Address{1}, common.Address{2}
|
var addrA, addrB = common.Address{1}, common.Address{2}
|
||||||
sig = abi.Methods["sliceAddress"].ID
|
sig = abi.Methods["sliceAddress"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
||||||
@@ -91,7 +663,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var addrC, addrD = common.Address{3}, common.Address{4}
|
var addrC, addrD = common.Address{3}, common.Address{4}
|
||||||
sig = abi.Methods["sliceMultiAddress"].ID
|
sig = abi.Methods["sliceMultiAddress"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
@@ -109,7 +681,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["slice256"].ID
|
sig = abi.Methods["slice256"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
@@ -123,7 +695,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
||||||
sig = abi.Methods["nestedArray"].ID
|
sig = abi.Methods["nestedArray"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
@@ -140,7 +712,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedArray2"].ID
|
sig = abi.Methods["nestedArray2"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
||||||
@@ -156,7 +728,7 @@ func TestMethodPack(t *testing.T) {
|
|||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedSlice"].ID
|
sig = abi.Methods["nestedSlice"].Id()
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
|
|||||||
@@ -1,990 +0,0 @@
|
|||||||
// Copyright 2020 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 (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type packUnpackTest struct {
|
|
||||||
def string
|
|
||||||
unpacked interface{}
|
|
||||||
packed string
|
|
||||||
}
|
|
||||||
|
|
||||||
var packUnpackTests = []packUnpackTest{
|
|
||||||
// Booleans
|
|
||||||
{
|
|
||||||
def: `[{ "type": "bool" }]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{ "type": "bool" }]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: false,
|
|
||||||
},
|
|
||||||
// Integers
|
|
||||||
{
|
|
||||||
def: `[{ "type": "uint8" }]`,
|
|
||||||
unpacked: uint8(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{ "type": "uint8[]" }]`,
|
|
||||||
unpacked: []uint8{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{ "type": "uint16" }]`,
|
|
||||||
unpacked: uint16(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{ "type": "uint16[]" }]`,
|
|
||||||
unpacked: []uint16{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint17"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: big.NewInt(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: uint32(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[]"}]`,
|
|
||||||
unpacked: []uint32{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64"}]`,
|
|
||||||
unpacked: uint64(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64[]"}]`,
|
|
||||||
unpacked: []uint64{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256"}]`,
|
|
||||||
unpacked: big.NewInt(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[]"}]`,
|
|
||||||
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int8"}]`,
|
|
||||||
unpacked: int8(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int8[]"}]`,
|
|
||||||
unpacked: []int8{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16"}]`,
|
|
||||||
unpacked: int16(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16[]"}]`,
|
|
||||||
unpacked: []int16{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int17"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: big.NewInt(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32"}]`,
|
|
||||||
unpacked: int32(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: int32(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32[]"}]`,
|
|
||||||
unpacked: []int32{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64"}]`,
|
|
||||||
unpacked: int64(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64[]"}]`,
|
|
||||||
unpacked: []int64{1, 2},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256"}]`,
|
|
||||||
unpacked: big.NewInt(2),
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256"}]`,
|
|
||||||
packed: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
unpacked: big.NewInt(-1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256[]"}]`,
|
|
||||||
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
},
|
|
||||||
// Address
|
|
||||||
{
|
|
||||||
def: `[{"type": "address"}]`,
|
|
||||||
packed: "0000000000000000000000000100000000000000000000000000000000000000",
|
|
||||||
unpacked: common.Address{1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "address[]"}]`,
|
|
||||||
unpacked: []common.Address{{1}, {2}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000100000000000000000000000000000000000000" +
|
|
||||||
"0000000000000000000000000200000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
// Bytes
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes1"}]`,
|
|
||||||
unpacked: [1]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes2"}]`,
|
|
||||||
unpacked: [2]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes3"}]`,
|
|
||||||
unpacked: [3]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes4"}]`,
|
|
||||||
unpacked: [4]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes5"}]`,
|
|
||||||
unpacked: [5]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes6"}]`,
|
|
||||||
unpacked: [6]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes7"}]`,
|
|
||||||
unpacked: [7]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes8"}]`,
|
|
||||||
unpacked: [8]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes9"}]`,
|
|
||||||
unpacked: [9]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes10"}]`,
|
|
||||||
unpacked: [10]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes11"}]`,
|
|
||||||
unpacked: [11]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes12"}]`,
|
|
||||||
unpacked: [12]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes13"}]`,
|
|
||||||
unpacked: [13]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes14"}]`,
|
|
||||||
unpacked: [14]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes15"}]`,
|
|
||||||
unpacked: [15]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes16"}]`,
|
|
||||||
unpacked: [16]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes17"}]`,
|
|
||||||
unpacked: [17]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes18"}]`,
|
|
||||||
unpacked: [18]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes19"}]`,
|
|
||||||
unpacked: [19]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes20"}]`,
|
|
||||||
unpacked: [20]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes21"}]`,
|
|
||||||
unpacked: [21]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes22"}]`,
|
|
||||||
unpacked: [22]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes23"}]`,
|
|
||||||
unpacked: [23]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes24"}]`,
|
|
||||||
unpacked: [24]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes25"}]`,
|
|
||||||
unpacked: [25]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes26"}]`,
|
|
||||||
unpacked: [26]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes27"}]`,
|
|
||||||
unpacked: [27]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes28"}]`,
|
|
||||||
unpacked: [28]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes29"}]`,
|
|
||||||
unpacked: [29]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes30"}]`,
|
|
||||||
unpacked: [30]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes31"}]`,
|
|
||||||
unpacked: [31]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32"}]`,
|
|
||||||
unpacked: [32]byte{1},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32"}]`,
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32"}]`,
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
// Functions
|
|
||||||
{
|
|
||||||
def: `[{"type": "function"}]`,
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [24]byte{1},
|
|
||||||
},
|
|
||||||
// Slice and Array
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []uint8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: []uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: []*big.Int{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]uint8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int8[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]int8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []int16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]int16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []int32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]int32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []int64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]int64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256[3]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
unpacked: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
|
||||||
},
|
|
||||||
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [][]uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [][]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
unpacked: [][]uint8{{1, 2}, {1, 2, 3}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2][2]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000060" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [2][]uint8{{}, {}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: [2][]uint8{{1}, {1}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [][2]uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [][2]uint8{{1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [][2]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint16[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []uint16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint16[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]uint16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []uint32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[2][3][4]"}]`,
|
|
||||||
unpacked: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000004" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000007" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000008" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000009" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000a" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000b" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000c" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000d" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000e" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000f" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000010" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000011" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000012" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000013" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000014" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000015" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000016" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000017" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000018",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32[]"}]`,
|
|
||||||
unpacked: [][32]byte{{1}, {2}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" +
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]uint32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []uint64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64[2]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: [2]uint64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[3]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
unpacked: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[4]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c0" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000100" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000140" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" +
|
|
||||||
"48656c6c6f000000000000000000000000000000000000000000000000000000" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" +
|
|
||||||
"576f726c64000000000000000000000000000000000000000000000000000000" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000b" +
|
|
||||||
"476f2d657468657265756d000000000000000000000000000000000000000000" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000008" +
|
|
||||||
"457468657265756d000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [4]string{"Hello", "World", "Go-ethereum", "Ethereum"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000008" +
|
|
||||||
"457468657265756d000000000000000000000000000000000000000000000000" +
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000000b" +
|
|
||||||
"676f2d657468657265756d000000000000000000000000000000000000000000",
|
|
||||||
unpacked: []string{"Ethereum", "go-ethereum"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes[]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" +
|
|
||||||
"f0f0f00000000000000000000000000000000000000000000000000000000000" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" +
|
|
||||||
"f0f0f00000000000000000000000000000000000000000000000000000000000",
|
|
||||||
unpacked: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[2][][]"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000e0" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c8" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000003e8" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c8" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000003e8",
|
|
||||||
unpacked: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
|
|
||||||
},
|
|
||||||
// struct outputs
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}], "type":"tuple"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: struct {
|
|
||||||
Int1 *big.Int
|
|
||||||
Int2 *big.Int
|
|
||||||
}{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"int_one","type":"int256"}], "type":"tuple"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"int__one","type":"int256"}], "type":"tuple"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"int_one_","type":"int256"}], "type":"tuple"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
unpacked: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}], "type":"tuple"}]`,
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
unpacked: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
Intone *big.Int
|
|
||||||
}{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string"}]`,
|
|
||||||
unpacked: "foobar",
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" +
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[]"}]`,
|
|
||||||
unpacked: []string{"hello", "foobar"},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
|
||||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000", // str[1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[2]"}]`,
|
|
||||||
unpacked: [2]string{"hello", "foobar"},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
|
||||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000", // str[1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32[][]"}]`,
|
|
||||||
unpacked: [][][32]byte{{{1}, {2}}, {{3}, {4}, {5}}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32[][2]"}]`,
|
|
||||||
unpacked: [2][][32]byte{{{1}, {2}}, {{3}, {4}, {5}}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32[3][2]"}]`,
|
|
||||||
unpacked: [2][3][32]byte{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
|
||||||
packed: "0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// static tuple
|
|
||||||
def: `[{"components": [{"name":"a","type":"int64"},
|
|
||||||
{"name":"b","type":"int256"},
|
|
||||||
{"name":"c","type":"int256"},
|
|
||||||
{"name":"d","type":"bool"},
|
|
||||||
{"name":"e","type":"bytes32[3][2]"}], "type":"tuple"}]`,
|
|
||||||
unpacked: struct {
|
|
||||||
A int64
|
|
||||||
B *big.Int
|
|
||||||
C *big.Int
|
|
||||||
D bool
|
|
||||||
E [2][3][32]byte
|
|
||||||
}{1, big.NewInt(1), big.NewInt(-1), true, [2][3][32]byte{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000", // struct[e] array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name":"a","type":"string"},
|
|
||||||
{"name":"b","type":"int64"},
|
|
||||||
{"name":"c","type":"bytes"},
|
|
||||||
{"name":"d","type":"string[]"},
|
|
||||||
{"name":"e","type":"int256[]"},
|
|
||||||
{"name":"f","type":"address[]"}], "type":"tuple"}]`,
|
|
||||||
unpacked: struct {
|
|
||||||
A string
|
|
||||||
B int64
|
|
||||||
C []byte
|
|
||||||
D []string
|
|
||||||
E []*big.Int
|
|
||||||
F []common.Address
|
|
||||||
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
|
|
||||||
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
|
|
||||||
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
|
|
||||||
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
|
|
||||||
"0000000000000000000000000200000000000000000000000000000000000000", // common.Address{2}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{ "type": "tuple","components": [{"name": "a","type": "uint256"},
|
|
||||||
{"name": "b","type": "uint256[]"}],
|
|
||||||
"name": "a","type": "tuple"},
|
|
||||||
{"name": "b","type": "uint256[]"}], "type": "tuple"}]`,
|
|
||||||
unpacked: struct {
|
|
||||||
A struct {
|
|
||||||
A *big.Int
|
|
||||||
B []*big.Int
|
|
||||||
}
|
|
||||||
B []*big.Int
|
|
||||||
}{
|
|
||||||
A: struct {
|
|
||||||
A *big.Int
|
|
||||||
B []*big.Int
|
|
||||||
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(2)}},
|
|
||||||
B: []*big.Int{big.NewInt(1), big.NewInt(2)}},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // a offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b[1] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002", // b[1] value
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name": "a","type": "int256"},
|
|
||||||
{"name": "b","type": "int256[]"}],
|
|
||||||
"name": "a","type": "tuple[]"}]`,
|
|
||||||
unpacked: []struct {
|
|
||||||
A *big.Int
|
|
||||||
B []*big.Int
|
|
||||||
}{
|
|
||||||
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(3)}},
|
|
||||||
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
|
|
||||||
},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // tuple[0].B[1] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].B[1] value
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name": "a","type": "int256"},
|
|
||||||
{"name": "b","type": "int256"}],
|
|
||||||
"name": "a","type": "tuple[2]"}]`,
|
|
||||||
unpacked: [2]struct {
|
|
||||||
A *big.Int
|
|
||||||
B *big.Int
|
|
||||||
}{
|
|
||||||
{big.NewInt(-1), big.NewInt(1)},
|
|
||||||
{big.NewInt(1), big.NewInt(-1)},
|
|
||||||
},
|
|
||||||
packed: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].b
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"components": [{"name": "a","type": "int256[]"}],
|
|
||||||
"name": "a","type": "tuple[2]"}]`,
|
|
||||||
unpacked: [2]struct {
|
|
||||||
A []*big.Int
|
|
||||||
}{
|
|
||||||
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
|
|
||||||
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
|
|
||||||
},
|
|
||||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].A[1]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -17,77 +17,49 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertType converts an interface of a runtime type into a interface of the
|
|
||||||
// given type, e.g. turn this code:
|
|
||||||
//
|
|
||||||
// var fields []reflect.StructField
|
|
||||||
//
|
|
||||||
// fields = append(fields, reflect.StructField{
|
|
||||||
// Name: "X",
|
|
||||||
// Type: reflect.TypeOf(new(big.Int)),
|
|
||||||
// Tag: reflect.StructTag("json:\"" + "x" + "\""),
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// into:
|
|
||||||
//
|
|
||||||
// type TupleT struct { X *big.Int }
|
|
||||||
func ConvertType(in interface{}, proto interface{}) interface{} {
|
|
||||||
protoType := reflect.TypeOf(proto)
|
|
||||||
if reflect.TypeOf(in).ConvertibleTo(protoType) {
|
|
||||||
return reflect.ValueOf(in).Convert(protoType).Interface()
|
|
||||||
}
|
|
||||||
// Use set as a last ditch effort
|
|
||||||
if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return proto
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirect recursively dereferences the value until it either gets the value
|
// indirect recursively dereferences the value until it either gets the value
|
||||||
// or finds a big.Int
|
// or finds a big.Int
|
||||||
func indirect(v reflect.Value) reflect.Value {
|
func indirect(v reflect.Value) reflect.Value {
|
||||||
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT {
|
||||||
return indirect(v.Elem())
|
return indirect(v.Elem())
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflectIntType returns the reflect using the given size and
|
// reflectIntKind returns the reflect using the given size and
|
||||||
// unsignedness.
|
// unsignedness.
|
||||||
func reflectIntType(unsigned bool, size int) reflect.Type {
|
func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
|
||||||
|
switch size {
|
||||||
|
case 8:
|
||||||
if unsigned {
|
if unsigned {
|
||||||
switch size {
|
return reflect.Uint8, uint8T
|
||||||
case 8:
|
}
|
||||||
return reflect.TypeOf(uint8(0))
|
return reflect.Int8, int8T
|
||||||
case 16:
|
case 16:
|
||||||
return reflect.TypeOf(uint16(0))
|
if unsigned {
|
||||||
|
return reflect.Uint16, uint16T
|
||||||
|
}
|
||||||
|
return reflect.Int16, int16T
|
||||||
case 32:
|
case 32:
|
||||||
return reflect.TypeOf(uint32(0))
|
if unsigned {
|
||||||
|
return reflect.Uint32, uint32T
|
||||||
|
}
|
||||||
|
return reflect.Int32, int32T
|
||||||
case 64:
|
case 64:
|
||||||
return reflect.TypeOf(uint64(0))
|
if unsigned {
|
||||||
|
return reflect.Uint64, uint64T
|
||||||
}
|
}
|
||||||
|
return reflect.Int64, int64T
|
||||||
}
|
}
|
||||||
switch size {
|
return reflect.Ptr, bigT
|
||||||
case 8:
|
|
||||||
return reflect.TypeOf(int8(0))
|
|
||||||
case 16:
|
|
||||||
return reflect.TypeOf(int16(0))
|
|
||||||
case 32:
|
|
||||||
return reflect.TypeOf(int32(0))
|
|
||||||
case 64:
|
|
||||||
return reflect.TypeOf(int64(0))
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(&big.Int{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustArrayToByteSlice creates a new byte slice with the exact same size as value
|
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
|
||||||
// and copies the bytes in value to the new slice.
|
// and copies the bytes in value to the new slice.
|
||||||
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
||||||
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
|
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
|
||||||
@@ -102,18 +74,14 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
|||||||
func set(dst, src reflect.Value) error {
|
func set(dst, src reflect.Value) error {
|
||||||
dstType, srcType := dst.Type(), src.Type()
|
dstType, srcType := dst.Type(), src.Type()
|
||||||
switch {
|
switch {
|
||||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
|
case dstType.Kind() == reflect.Interface:
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case srcType.AssignableTo(dstType) && dst.CanSet():
|
case srcType.AssignableTo(dstType) && dst.CanSet():
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet():
|
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
|
||||||
return setSlice(dst, src)
|
return setSlice(dst, src)
|
||||||
case dstType.Kind() == reflect.Array:
|
|
||||||
return setArray(dst, src)
|
|
||||||
case dstType.Kind() == reflect.Struct:
|
|
||||||
return setStruct(dst, src)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
||||||
}
|
}
|
||||||
@@ -122,64 +90,48 @@ func set(dst, src reflect.Value) error {
|
|||||||
|
|
||||||
// setSlice attempts to assign src to dst when slices are not assignable by default
|
// setSlice attempts to assign src to dst when slices are not assignable by default
|
||||||
// e.g. src: [][]byte -> dst: [][15]byte
|
// e.g. src: [][]byte -> dst: [][15]byte
|
||||||
// setSlice ignores if we cannot copy all of src' elements.
|
|
||||||
func setSlice(dst, src reflect.Value) error {
|
func setSlice(dst, src reflect.Value) error {
|
||||||
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
||||||
for i := 0; i < src.Len(); i++ {
|
for i := 0; i < src.Len(); i++ {
|
||||||
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
v := src.Index(i)
|
||||||
return err
|
reflect.Copy(slice.Index(i), v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if dst.CanSet() {
|
|
||||||
dst.Set(slice)
|
dst.Set(slice)
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
return errors.New("Cannot set slice, destination not settable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setArray(dst, src reflect.Value) error {
|
// requireAssignable assures that `dest` is a pointer and it's not an interface.
|
||||||
if src.Kind() == reflect.Ptr {
|
func requireAssignable(dst, src reflect.Value) error {
|
||||||
return set(dst, indirect(src))
|
if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
|
||||||
|
return fmt.Errorf("abi: cannot unmarshal %v into %v", src.Type(), dst.Type())
|
||||||
}
|
}
|
||||||
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 nil
|
||||||
}
|
|
||||||
return errors.New("Cannot set array, destination not settable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setStruct(dst, src reflect.Value) error {
|
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
||||||
for i := 0; i < src.NumField(); i++ {
|
func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
||||||
srcField := src.Field(i)
|
args Arguments) error {
|
||||||
dstField := dst.Field(i)
|
|
||||||
if !dstField.IsValid() || !srcField.IsValid() {
|
switch k {
|
||||||
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
|
case reflect.Struct:
|
||||||
}
|
case reflect.Slice, reflect.Array:
|
||||||
if err := set(dstField, srcField); err != nil {
|
if minLen := args.LengthNonIndexed(); v.Len() < minLen {
|
||||||
return err
|
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
||||||
|
minLen, v.Len())
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapArgNamesToStructFields maps a slice of argument names to struct fields.
|
// mapArgNamesToStructFields maps a slice of argument names to struct fields.
|
||||||
//
|
// first round: for each Exportable field that contains a `abi:""` tag
|
||||||
// first round: for each Exportable field that contains a `abi:""` tag and this field name
|
// and this field name exists in the given argument name list, pair them together.
|
||||||
// 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
|
||||||
// second round: for each argument name that has not been already linked, find what
|
// used, pair them.
|
||||||
// variable is expected to be mapped into, if it exists and has not been used, pair them.
|
|
||||||
//
|
|
||||||
// Note this function assumes the given value is a struct value.
|
// Note this function assumes the given value is a struct value.
|
||||||
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
|
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
|
||||||
typ := value.Type()
|
typ := value.Type()
|
||||||
@@ -196,8 +148,9 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// skip fields that have no abi:"" tag.
|
// skip fields that have no abi:"" tag.
|
||||||
tagName, ok := typ.Field(i).Tag.Lookup("abi")
|
var ok bool
|
||||||
if !ok {
|
var tagName string
|
||||||
|
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if tag is empty.
|
// check if tag is empty.
|
||||||
@@ -225,6 +178,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||||||
|
|
||||||
// second round ~~~
|
// second round ~~~
|
||||||
for _, argName := range argNames {
|
for _, argName := range argNames {
|
||||||
|
|
||||||
structFieldName := ToCamelCase(argName)
|
structFieldName := ToCamelCase(argName)
|
||||||
|
|
||||||
if structFieldName == "" {
|
if structFieldName == "" {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -32,7 +31,7 @@ type reflectTest struct {
|
|||||||
|
|
||||||
var reflectTests = []reflectTest{
|
var reflectTests = []reflectTest{
|
||||||
{
|
{
|
||||||
name: "OneToOneCorrespondence",
|
name: "OneToOneCorrespondance",
|
||||||
args: []string{"fieldA"},
|
args: []string{"fieldA"},
|
||||||
struc: struct {
|
struc: struct {
|
||||||
FieldA int `abi:"fieldA"`
|
FieldA int `abi:"fieldA"`
|
||||||
@@ -190,72 +189,3 @@ func TestReflectNameToStruct(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertType(t *testing.T) {
|
|
||||||
// Test Basic Struct
|
|
||||||
type T struct {
|
|
||||||
X *big.Int
|
|
||||||
Y *big.Int
|
|
||||||
}
|
|
||||||
// Create on-the-fly structure
|
|
||||||
var fields []reflect.StructField
|
|
||||||
fields = append(fields, reflect.StructField{
|
|
||||||
Name: "X",
|
|
||||||
Type: reflect.TypeOf(new(big.Int)),
|
|
||||||
Tag: "json:\"" + "x" + "\"",
|
|
||||||
})
|
|
||||||
fields = append(fields, reflect.StructField{
|
|
||||||
Name: "Y",
|
|
||||||
Type: reflect.TypeOf(new(big.Int)),
|
|
||||||
Tag: "json:\"" + "y" + "\"",
|
|
||||||
})
|
|
||||||
val := reflect.New(reflect.StructOf(fields))
|
|
||||||
val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1)))
|
|
||||||
val.Elem().Field(1).Set(reflect.ValueOf(big.NewInt(2)))
|
|
||||||
// ConvertType
|
|
||||||
out := *ConvertType(val.Interface(), new(T)).(*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))
|
|
||||||
}
|
|
||||||
// Slice Type
|
|
||||||
val2 := reflect.MakeSlice(reflect.SliceOf(reflect.StructOf(fields)), 2, 2)
|
|
||||||
val2.Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1)))
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
// Array Type
|
|
||||||
val3 := reflect.New(reflect.ArrayOf(2, reflect.StructOf(fields)))
|
|
||||||
val3.Elem().Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1)))
|
|
||||||
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,176 +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"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SelectorMarshaling struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Inputs []ArgumentMarshaling `json:"inputs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(c byte) bool {
|
|
||||||
return c >= '0' && c <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAlpha(c byte) bool {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentifierSymbol(c byte) bool {
|
|
||||||
return c == '$' || c == '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseIdentifier(unescapedSelector string) (string, string, error) {
|
|
||||||
return parseToken(unescapedSelector, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseElementaryType(unescapedSelector string) (string, string, error) {
|
|
||||||
parsedType, rest, err := parseToken(unescapedSelector, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("failed to parse elementary type: %v", err)
|
|
||||||
}
|
|
||||||
// handle arrays
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseType(unescapedSelector string) (interface{}, string, error) {
|
|
||||||
if len(unescapedSelector) == 0 {
|
|
||||||
return nil, "", fmt.Errorf("empty type")
|
|
||||||
}
|
|
||||||
if unescapedSelector[0] == '(' {
|
|
||||||
return parseCompositeType(unescapedSelector)
|
|
||||||
} else {
|
|
||||||
return parseElementaryType(unescapedSelector)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if s, ok := arg.(string); ok {
|
|
||||||
arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false})
|
|
||||||
} else if components, ok := arg.([]interface{}); ok {
|
|
||||||
subArgs, err := assembleArgs(components)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to assemble components: %v", err)
|
|
||||||
}
|
|
||||||
tupleType := "tuple"
|
|
||||||
if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
|
|
||||||
subArgs = subArgs[:len(subArgs)-1]
|
|
||||||
tupleType = "tuple[]"
|
|
||||||
}
|
|
||||||
arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arguments, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSelector converts a method selector into a struct that can be JSON encoded
|
|
||||||
// and consumed by other functions in this package.
|
|
||||||
// Note, although uppercase letters are not part of the ABI spec, this function
|
|
||||||
// still accepts it as the general format is valid.
|
|
||||||
func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
|
||||||
name, rest, err := parseIdentifier(unescapedSelector)
|
|
||||||
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 {
|
|
||||||
args, rest, err = parseCompositeType(rest)
|
|
||||||
if err != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reassemble the fake ABI and construct the JSON
|
|
||||||
fakeArgs, err := assembleArgs(args)
|
|
||||||
if err != nil {
|
|
||||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return SelectorMarshaling{name, "function", fakeArgs}, nil
|
|
||||||
}
|
|
||||||
@@ -1,79 +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"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
args []ArgumentMarshaling
|
|
||||||
}{
|
|
||||||
{"noargs()", "noargs", []ArgumentMarshaling{}},
|
|
||||||
{"simple(uint256,uint256,uint256)", "simple", mkType("uint256", "uint256", "uint256")},
|
|
||||||
{"other(uint256,address)", "other", mkType("uint256", "address")},
|
|
||||||
{"withArray(uint256[],address[2],uint8[4][][5])", "withArray", mkType("uint256[]", "address[2]", "uint8[4][][5]")},
|
|
||||||
{"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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
// Copyright 2018 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 (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Try to generate the topic based on simple types
|
|
||||||
switch rule := rule.(type) {
|
|
||||||
case common.Hash:
|
|
||||||
copy(topic[:], rule[:])
|
|
||||||
case common.Address:
|
|
||||||
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
|
||||||
case *big.Int:
|
|
||||||
blob := rule.Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case bool:
|
|
||||||
if rule {
|
|
||||||
topic[common.HashLength-1] = 1
|
|
||||||
}
|
|
||||||
case int8:
|
|
||||||
copy(topic[:], genIntType(int64(rule), 1))
|
|
||||||
case int16:
|
|
||||||
copy(topic[:], genIntType(int64(rule), 2))
|
|
||||||
case int32:
|
|
||||||
copy(topic[:], genIntType(int64(rule), 4))
|
|
||||||
case int64:
|
|
||||||
copy(topic[:], genIntType(rule, 8))
|
|
||||||
case uint8:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint16:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint32:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint64:
|
|
||||||
blob := new(big.Int).SetUint64(rule).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case string:
|
|
||||||
hash := crypto.Keccak256Hash([]byte(rule))
|
|
||||||
copy(topic[:], hash[:])
|
|
||||||
case []byte:
|
|
||||||
hash := crypto.Keccak256Hash(rule)
|
|
||||||
copy(topic[:], hash[:])
|
|
||||||
|
|
||||||
default:
|
|
||||||
// todo(rjl493456442) according solidity documentation, indexed event
|
|
||||||
// parameters that are not value types i.e. arrays and structs are not
|
|
||||||
// stored directly but instead a keccak256-hash of an encoding is stored.
|
|
||||||
//
|
|
||||||
// We only convert stringS and bytes to hash, still need to deal with
|
|
||||||
// array(both fixed-size and dynamic-size) and struct.
|
|
||||||
|
|
||||||
// Attempt to generate the topic from funky types
|
|
||||||
val := reflect.ValueOf(rule)
|
|
||||||
switch {
|
|
||||||
// static byte array
|
|
||||||
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
|
||||||
reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
topics[i] = append(topics[i], topic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return topics, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func genIntType(rule int64, size uint) []byte {
|
|
||||||
var topic [common.HashLength]byte
|
|
||||||
if rule < 0 {
|
|
||||||
// if a rule is negative, we need to put it into two's complement.
|
|
||||||
// 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[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTopics converts the indexed topic fields into actual log field values.
|
|
||||||
func ParseTopics(out interface{}, fields Arguments, topics []common.Hash) error {
|
|
||||||
return parseTopicWithSetter(fields, topics,
|
|
||||||
func(arg Argument, reconstr interface{}) {
|
|
||||||
field := reflect.ValueOf(out).Elem().FieldByName(ToCamelCase(arg.Name))
|
|
||||||
field.Set(reflect.ValueOf(reconstr))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs.
|
|
||||||
func ParseTopicsIntoMap(out map[string]interface{}, fields Arguments, topics []common.Hash) error {
|
|
||||||
return parseTopicWithSetter(fields, topics,
|
|
||||||
func(arg Argument, reconstr interface{}) {
|
|
||||||
out[arg.Name] = reconstr
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
|
|
||||||
// provided set function.
|
|
||||||
//
|
|
||||||
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
|
||||||
// hashes as the topic value!
|
|
||||||
func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Argument, interface{})) error {
|
|
||||||
// Sanity check that the fields and topics match up
|
|
||||||
if len(fields) != len(topics) {
|
|
||||||
return errors.New("topic/field count mismatch")
|
|
||||||
}
|
|
||||||
// Iterate over all the fields and reconstruct them from topics
|
|
||||||
for i, arg := range fields {
|
|
||||||
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")
|
|
||||||
case StringTy, BytesTy, SliceTy, ArrayTy:
|
|
||||||
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
|
|
||||||
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
|
|
||||||
reconstr = topics[i]
|
|
||||||
case FunctionTy:
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Use the setter function to store the value
|
|
||||||
setter(arg, reconstr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,381 +0,0 @@
|
|||||||
// Copyright 2020 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 (
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMakeTopics(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
query [][]interface{}
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want [][]common.Hash
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"support fixed byte types, right padded to 32 bytes",
|
|
||||||
args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
|
|
||||||
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support common hash types in topics",
|
|
||||||
args{[][]interface{}{{common.Hash{1, 2, 3, 4, 5}}}},
|
|
||||||
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support address types in topics",
|
|
||||||
args{[][]interface{}{{common.Address{1, 2, 3, 4, 5}}}},
|
|
||||||
[][]common.Hash{{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5}}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support *big.Int types in topics",
|
|
||||||
args{[][]interface{}{{big.NewInt(1).Lsh(big.NewInt(2), 254)}}},
|
|
||||||
[][]common.Hash{{common.Hash{128}}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support boolean types in topics",
|
|
||||||
args{[][]interface{}{
|
|
||||||
{true},
|
|
||||||
{false},
|
|
||||||
}},
|
|
||||||
[][]common.Hash{
|
|
||||||
{common.Hash{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, 1}},
|
|
||||||
{common.Hash{0}},
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support int/uint(8/16/32/64) types in topics",
|
|
||||||
args{[][]interface{}{
|
|
||||||
{int8(-2)},
|
|
||||||
{int16(-3)},
|
|
||||||
{int32(-4)},
|
|
||||||
{int64(-5)},
|
|
||||||
{int8(1)},
|
|
||||||
{int16(256)},
|
|
||||||
{int32(65536)},
|
|
||||||
{int64(4294967296)},
|
|
||||||
{uint8(1)},
|
|
||||||
{uint16(256)},
|
|
||||||
{uint32(65536)},
|
|
||||||
{uint64(4294967296)},
|
|
||||||
}},
|
|
||||||
[][]common.Hash{
|
|
||||||
{common.Hash{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, 254}},
|
|
||||||
{common.Hash{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, 253}},
|
|
||||||
{common.Hash{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, 252}},
|
|
||||||
{common.Hash{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, 251}},
|
|
||||||
{common.Hash{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, 1}},
|
|
||||||
{common.Hash{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, 0}},
|
|
||||||
{common.Hash{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, 0, 0}},
|
|
||||||
{common.Hash{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, 0, 0, 0, 0}},
|
|
||||||
{common.Hash{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, 1}},
|
|
||||||
{common.Hash{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, 0}},
|
|
||||||
{common.Hash{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, 0, 0}},
|
|
||||||
{common.Hash{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, 0, 0, 0, 0}},
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support string types in topics",
|
|
||||||
args{[][]interface{}{{"hello world"}}},
|
|
||||||
[][]common.Hash{{crypto.Keccak256Hash([]byte("hello world"))}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"support byte slice types in topics",
|
|
||||||
args{[][]interface{}{{[]byte{1, 2, 3}}}},
|
|
||||||
[][]common.Hash{{crypto.Keccak256Hash([]byte{1, 2, 3})}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := MakeTopics(tt.args.query...)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
createObj func() interface{}
|
|
||||||
resultObj func() interface{}
|
|
||||||
resultMap func() map[string]interface{}
|
|
||||||
fields Arguments
|
|
||||||
topics []common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
type bytesStruct struct {
|
|
||||||
StaticBytes [5]byte
|
|
||||||
}
|
|
||||||
type int8Struct struct {
|
|
||||||
Int8Value int8
|
|
||||||
}
|
|
||||||
type int256Struct struct {
|
|
||||||
Int256Value *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type hashStruct struct {
|
|
||||||
HashValue common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
type funcStruct struct {
|
|
||||||
FuncValue [24]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type topicTest struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTopicsTests() []topicTest {
|
|
||||||
bytesType, _ := NewType("bytes5", "", nil)
|
|
||||||
int8Type, _ := NewType("int8", "", nil)
|
|
||||||
int256Type, _ := NewType("int256", "", nil)
|
|
||||||
tupleType, _ := NewType("tuple(int256,int8)", "", nil)
|
|
||||||
stringType, _ := NewType("string", "", nil)
|
|
||||||
funcType, _ := NewType("function", "", nil)
|
|
||||||
|
|
||||||
tests := []topicTest{
|
|
||||||
{
|
|
||||||
name: "support fixed byte types, right padded to 32 bytes",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &bytesStruct{} },
|
|
||||||
resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"staticBytes": [5]byte{1, 2, 3, 4, 5}}
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "staticBytes",
|
|
||||||
Type: bytesType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{1, 2, 3, 4, 5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int8 with negative value",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &int8Struct{} },
|
|
||||||
resultObj: func() interface{} { return &int8Struct{Int8Value: -1} },
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"int8Value": int8(-1)}
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "int8Value",
|
|
||||||
Type: int8Type,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{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},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int256 with negative value",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &int256Struct{} },
|
|
||||||
resultObj: func() interface{} { return &int256Struct{Int256Value: big.NewInt(-1)} },
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"int256Value": big.NewInt(-1)}
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "int256Value",
|
|
||||||
Type: int256Type,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{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},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "hash type",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &hashStruct{} },
|
|
||||||
resultObj: func() interface{} { return &hashStruct{crypto.Keccak256Hash([]byte("stringtopic"))} },
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"hashValue": crypto.Keccak256Hash([]byte("stringtopic"))}
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "hashValue",
|
|
||||||
Type: stringType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
crypto.Keccak256Hash([]byte("stringtopic")),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "function type",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &funcStruct{} },
|
|
||||||
resultObj: func() interface{} {
|
|
||||||
return &funcStruct{[24]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}}
|
|
||||||
},
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return map[string]interface{}{"funcValue": [24]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}}
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "funcValue",
|
|
||||||
Type: funcType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error on topic/field count mismatch",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return nil },
|
|
||||||
resultObj: func() interface{} { return nil },
|
|
||||||
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "tupletype",
|
|
||||||
Type: tupleType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error on unindexed arguments",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &int256Struct{} },
|
|
||||||
resultObj: func() interface{} { return &int256Struct{} },
|
|
||||||
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "int256Value",
|
|
||||||
Type: int256Type,
|
|
||||||
Indexed: false,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{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},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error on tuple in topic reconstruction",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &tupleType },
|
|
||||||
resultObj: func() interface{} { return &tupleType },
|
|
||||||
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "tupletype",
|
|
||||||
Type: tupleType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{{0}},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error on improper encoded function",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &funcStruct{} },
|
|
||||||
resultObj: func() interface{} { return &funcStruct{} },
|
|
||||||
resultMap: func() map[string]interface{} {
|
|
||||||
return make(map[string]interface{})
|
|
||||||
},
|
|
||||||
fields: Arguments{Argument{
|
|
||||||
Name: "funcValue",
|
|
||||||
Type: funcType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return tests
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTopics(t *testing.T) {
|
|
||||||
tests := setupTopicsTests()
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
createObj := tt.args.createObj()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTopicsIntoMap(t *testing.T) {
|
|
||||||
tests := setupTopicsTests()
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
outMap := make(map[string]interface{})
|
|
||||||
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,10 +23,6 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type enumerator
|
// Type enumerator
|
||||||
@@ -46,19 +42,19 @@ const (
|
|||||||
FunctionTy
|
FunctionTy
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type is the reflection of the supported argument type.
|
// Type is the reflection of the supported argument type
|
||||||
type Type struct {
|
type Type struct {
|
||||||
Elem *Type
|
Elem *Type
|
||||||
|
Kind reflect.Kind
|
||||||
|
Type reflect.Type
|
||||||
Size int
|
Size int
|
||||||
T byte // Our own type checking
|
T byte // Our own type checking
|
||||||
|
|
||||||
stringKind string // holds the unparsed string for deriving signatures
|
stringKind string // holds the unparsed string for deriving signatures
|
||||||
|
|
||||||
// Tuple relative fields
|
// Tuple relative fields
|
||||||
TupleRawName string // Raw struct name defined in source code, may be empty.
|
|
||||||
TupleElems []*Type // Type information of all tuple fields
|
TupleElems []*Type // Type information of all tuple fields
|
||||||
TupleRawNames []string // Raw field name of all tuple fields
|
TupleRawNames []string // Raw field name of all tuple fields
|
||||||
TupleType reflect.Type // Underlying struct of the tuple
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -67,24 +63,20 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewType creates a new reflection type of abi type given in t.
|
// NewType creates a new reflection type of abi type given in t.
|
||||||
func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
|
func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
// check that array brackets are equal if they exist
|
// check that array brackets are equal if they exist
|
||||||
if strings.Count(t, "[") != strings.Count(t, "]") {
|
if strings.Count(t, "[") != strings.Count(t, "]") {
|
||||||
return Type{}, fmt.Errorf("invalid arg type in abi")
|
return Type{}, fmt.Errorf("invalid arg type in abi")
|
||||||
}
|
}
|
||||||
|
|
||||||
typ.stringKind = t
|
typ.stringKind = t
|
||||||
|
|
||||||
// if there are brackets, get ready to go into slice/array mode and
|
// if there are brackets, get ready to go into slice/array mode and
|
||||||
// recursively create the type
|
// recursively create the type
|
||||||
if strings.Count(t, "[") != 0 {
|
if strings.Count(t, "[") != 0 {
|
||||||
// Note internalType can be empty here.
|
|
||||||
subInternal := internalType
|
|
||||||
if i := strings.LastIndex(internalType, "["); i != -1 {
|
|
||||||
subInternal = subInternal[:i]
|
|
||||||
}
|
|
||||||
// recursively embed the type
|
|
||||||
i := strings.LastIndex(t, "[")
|
i := strings.LastIndex(t, "[")
|
||||||
embeddedType, err := NewType(t[:i], subInternal, components)
|
// recursively embed the type
|
||||||
|
embeddedType, err := NewType(t[:i], components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
@@ -97,17 +89,25 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||||||
if len(intz) == 0 {
|
if len(intz) == 0 {
|
||||||
// is a slice
|
// is a slice
|
||||||
typ.T = SliceTy
|
typ.T = SliceTy
|
||||||
|
typ.Kind = reflect.Slice
|
||||||
typ.Elem = &embeddedType
|
typ.Elem = &embeddedType
|
||||||
|
typ.Type = reflect.SliceOf(embeddedType.Type)
|
||||||
|
if embeddedType.T == TupleTy {
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
|
}
|
||||||
} else if len(intz) == 1 {
|
} else if len(intz) == 1 {
|
||||||
// is an array
|
// is a array
|
||||||
typ.T = ArrayTy
|
typ.T = ArrayTy
|
||||||
|
typ.Kind = reflect.Array
|
||||||
typ.Elem = &embeddedType
|
typ.Elem = &embeddedType
|
||||||
typ.Size, err = strconv.Atoi(intz[0])
|
typ.Size, err = strconv.Atoi(intz[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||||
}
|
}
|
||||||
|
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
|
||||||
|
if embeddedType.T == TupleTy {
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Type{}, fmt.Errorf("invalid formatting of array type")
|
return Type{}, fmt.Errorf("invalid formatting of array type")
|
||||||
}
|
}
|
||||||
@@ -138,27 +138,36 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||||||
// varType is the parsed abi type
|
// varType is the parsed abi type
|
||||||
switch varType := parsedType[1]; varType {
|
switch varType := parsedType[1]; varType {
|
||||||
case "int":
|
case "int":
|
||||||
|
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
typ.T = IntTy
|
typ.T = IntTy
|
||||||
case "uint":
|
case "uint":
|
||||||
|
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
typ.T = UintTy
|
typ.T = UintTy
|
||||||
case "bool":
|
case "bool":
|
||||||
|
typ.Kind = reflect.Bool
|
||||||
typ.T = BoolTy
|
typ.T = BoolTy
|
||||||
|
typ.Type = reflect.TypeOf(bool(false))
|
||||||
case "address":
|
case "address":
|
||||||
|
typ.Kind = reflect.Array
|
||||||
|
typ.Type = addressT
|
||||||
typ.Size = 20
|
typ.Size = 20
|
||||||
typ.T = AddressTy
|
typ.T = AddressTy
|
||||||
case "string":
|
case "string":
|
||||||
|
typ.Kind = reflect.String
|
||||||
|
typ.Type = reflect.TypeOf("")
|
||||||
typ.T = StringTy
|
typ.T = StringTy
|
||||||
case "bytes":
|
case "bytes":
|
||||||
if varSize == 0 {
|
if varSize == 0 {
|
||||||
typ.T = BytesTy
|
typ.T = BytesTy
|
||||||
|
typ.Kind = reflect.Slice
|
||||||
|
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
|
||||||
} else {
|
} else {
|
||||||
if varSize > 32 {
|
|
||||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
|
||||||
}
|
|
||||||
typ.T = FixedBytesTy
|
typ.T = FixedBytesTy
|
||||||
|
typ.Kind = reflect.Array
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
|
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
|
||||||
}
|
}
|
||||||
case "tuple":
|
case "tuple":
|
||||||
var (
|
var (
|
||||||
@@ -166,30 +175,19 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||||||
elems []*Type
|
elems []*Type
|
||||||
names []string
|
names []string
|
||||||
expression string // canonical parameter expression
|
expression string // canonical parameter expression
|
||||||
used = make(map[string]bool)
|
|
||||||
)
|
)
|
||||||
expression += "("
|
expression += "("
|
||||||
for idx, c := range components {
|
for idx, c := range components {
|
||||||
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
cType, err := NewType(c.Type, c.Components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
name := ToCamelCase(c.Name)
|
if ToCamelCase(c.Name) == "" {
|
||||||
if name == "" {
|
|
||||||
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
|
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
|
||||||
}
|
}
|
||||||
fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
|
|
||||||
if err != nil {
|
|
||||||
return Type{}, err
|
|
||||||
}
|
|
||||||
used[fieldName] = true
|
|
||||||
if !isValidFieldName(fieldName) {
|
|
||||||
return Type{}, fmt.Errorf("field %d has invalid name", idx)
|
|
||||||
}
|
|
||||||
fields = append(fields, reflect.StructField{
|
fields = append(fields, reflect.StructField{
|
||||||
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
|
||||||
Type: cType.GetType(),
|
Type: cType.Type,
|
||||||
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
|
||||||
})
|
})
|
||||||
elems = append(elems, &cType)
|
elems = append(elems, &cType)
|
||||||
names = append(names, c.Name)
|
names = append(names, c.Name)
|
||||||
@@ -199,26 +197,17 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression += ")"
|
expression += ")"
|
||||||
|
typ.Kind = reflect.Struct
|
||||||
typ.TupleType = reflect.StructOf(fields)
|
typ.Type = reflect.StructOf(fields)
|
||||||
typ.TupleElems = elems
|
typ.TupleElems = elems
|
||||||
typ.TupleRawNames = names
|
typ.TupleRawNames = names
|
||||||
typ.T = TupleTy
|
typ.T = TupleTy
|
||||||
typ.stringKind = expression
|
typ.stringKind = expression
|
||||||
|
|
||||||
const structPrefix = "struct "
|
|
||||||
// After solidity 0.5.10, a new field of abi "internalType"
|
|
||||||
// is introduced. From that we can obtain the struct name
|
|
||||||
// user defined in the source code.
|
|
||||||
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
|
||||||
// Foo.Bar type definition is not allowed in golang,
|
|
||||||
// convert the format to FooBar
|
|
||||||
typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
case "function":
|
case "function":
|
||||||
|
typ.Kind = reflect.Array
|
||||||
typ.T = FunctionTy
|
typ.T = FunctionTy
|
||||||
typ.Size = 24
|
typ.Size = 24
|
||||||
|
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
|
||||||
default:
|
default:
|
||||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||||
}
|
}
|
||||||
@@ -226,43 +215,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType returns the reflection type of the ABI type.
|
// String implements Stringer
|
||||||
func (t Type) GetType() reflect.Type {
|
|
||||||
switch t.T {
|
|
||||||
case IntTy:
|
|
||||||
return reflectIntType(false, t.Size)
|
|
||||||
case UintTy:
|
|
||||||
return reflectIntType(true, t.Size)
|
|
||||||
case BoolTy:
|
|
||||||
return reflect.TypeOf(false)
|
|
||||||
case StringTy:
|
|
||||||
return reflect.TypeOf("")
|
|
||||||
case SliceTy:
|
|
||||||
return reflect.SliceOf(t.Elem.GetType())
|
|
||||||
case ArrayTy:
|
|
||||||
return reflect.ArrayOf(t.Size, t.Elem.GetType())
|
|
||||||
case TupleTy:
|
|
||||||
return t.TupleType
|
|
||||||
case AddressTy:
|
|
||||||
return reflect.TypeOf(common.Address{})
|
|
||||||
case FixedBytesTy:
|
|
||||||
return reflect.ArrayOf(t.Size, reflect.TypeOf(byte(0)))
|
|
||||||
case BytesTy:
|
|
||||||
return reflect.SliceOf(reflect.TypeOf(byte(0)))
|
|
||||||
case HashTy:
|
|
||||||
// hashtype currently not used
|
|
||||||
return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
|
|
||||||
case FixedPointTy:
|
|
||||||
// fixedpoint type currently not used
|
|
||||||
return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
|
|
||||||
case FunctionTy:
|
|
||||||
return reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
|
|
||||||
default:
|
|
||||||
panic("Invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements Stringer.
|
|
||||||
func (t Type) String() (out string) {
|
func (t Type) String() (out string) {
|
||||||
return t.stringKind
|
return t.stringKind
|
||||||
}
|
}
|
||||||
@@ -344,7 +297,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||||||
return append(ret, tail...), nil
|
return append(ret, tail...), nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return packElement(t, v)
|
return packElement(t, v), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,7 +337,7 @@ func isDynamicType(t Type) bool {
|
|||||||
func getTypeSize(t Type) int {
|
func getTypeSize(t Type) int {
|
||||||
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
|
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
|
||||||
// Recursively calculate type size if it is a nested array
|
// Recursively calculate type size if it is a nested array
|
||||||
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
|
if t.Elem.T == ArrayTy {
|
||||||
return t.Size * getTypeSize(*t.Elem)
|
return t.Size * getTypeSize(*t.Elem)
|
||||||
}
|
}
|
||||||
return t.Size * 32
|
return t.Size * 32
|
||||||
@@ -397,30 +350,3 @@ func getTypeSize(t Type) int {
|
|||||||
}
|
}
|
||||||
return 32
|
return 32
|
||||||
}
|
}
|
||||||
|
|
||||||
// isLetter reports whether a given 'rune' is classified as a Letter.
|
|
||||||
// This method is copied from reflect/type.go
|
|
||||||
func isLetter(ch rune) bool {
|
|
||||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValidFieldName checks if a string is a valid (struct) field name or not.
|
|
||||||
//
|
|
||||||
// According to the language spec, a field name should be an identifier.
|
|
||||||
//
|
|
||||||
// identifier = letter { letter | unicode_digit } .
|
|
||||||
// letter = unicode_letter | "_" .
|
|
||||||
// This method is copied from reflect/type.go
|
|
||||||
func isValidFieldName(fieldName string) bool {
|
|
||||||
for i, c := range fieldName {
|
|
||||||
if i == 0 && !isLetter(c) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(isLetter(c) || unicode.IsDigit(c)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(fieldName) > 0
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,58 +36,58 @@ func TestTypeRegexp(t *testing.T) {
|
|||||||
components []ArgumentMarshaling
|
components []ArgumentMarshaling
|
||||||
kind Type
|
kind Type
|
||||||
}{
|
}{
|
||||||
{"bool", nil, Type{T: BoolTy, stringKind: "bool"}},
|
{"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
|
||||||
{"bool[]", nil, Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
|
{"bool[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
|
||||||
{"bool[2]", nil, Type{Size: 2, T: ArrayTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}},
|
{"bool[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
|
||||||
{"bool[2][]", nil, Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
{"bool[2][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
||||||
{"bool[][]", nil, Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
{"bool[][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
||||||
{"bool[][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
|
{"bool[][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
|
||||||
{"bool[2][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
|
{"bool[2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
|
||||||
{"bool[2][][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
|
{"bool[2][][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
|
||||||
{"bool[2][2][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
|
{"bool[2][2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
|
||||||
{"bool[][][]", nil, Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
|
{"bool[][][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
|
||||||
{"bool[][2][]", nil, Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
|
{"bool[][2][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
|
||||||
{"int8", nil, Type{Size: 8, T: IntTy, stringKind: "int8"}},
|
{"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
|
||||||
{"int16", nil, Type{Size: 16, T: IntTy, stringKind: "int16"}},
|
{"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
|
||||||
{"int32", nil, Type{Size: 32, T: IntTy, stringKind: "int32"}},
|
{"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
|
||||||
{"int64", nil, Type{Size: 64, T: IntTy, stringKind: "int64"}},
|
{"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
|
||||||
{"int256", nil, Type{Size: 256, T: IntTy, stringKind: "int256"}},
|
{"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
|
||||||
{"int8[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
|
{"int8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
|
||||||
{"int8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
|
{"int8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
|
||||||
{"int16[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
|
{"int16[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
|
||||||
{"int16[2]", nil, Type{Size: 2, T: ArrayTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
|
{"int16[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
|
||||||
{"int32[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
|
{"int32[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
|
||||||
{"int32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
|
{"int32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
|
||||||
{"int64[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
|
{"int64[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
|
||||||
{"int64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
|
{"int64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
|
||||||
{"int256[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
|
{"int256[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
|
||||||
{"int256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
|
{"int256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
|
||||||
{"uint8", nil, Type{Size: 8, T: UintTy, stringKind: "uint8"}},
|
{"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
|
||||||
{"uint16", nil, Type{Size: 16, T: UintTy, stringKind: "uint16"}},
|
{"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
|
||||||
{"uint32", nil, Type{Size: 32, T: UintTy, stringKind: "uint32"}},
|
{"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
|
||||||
{"uint64", nil, Type{Size: 64, T: UintTy, stringKind: "uint64"}},
|
{"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
|
||||||
{"uint256", nil, Type{Size: 256, T: UintTy, stringKind: "uint256"}},
|
{"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
|
||||||
{"uint8[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
|
{"uint8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
|
||||||
{"uint8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
|
{"uint8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
|
||||||
{"uint16[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
|
{"uint16[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
|
||||||
{"uint16[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
|
{"uint16[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
|
||||||
{"uint32[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
|
{"uint32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
|
||||||
{"uint32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
|
{"uint32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
|
||||||
{"uint64[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
|
{"uint64[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
|
||||||
{"uint64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
|
{"uint64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
|
||||||
{"uint256[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
|
{"uint256[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
|
||||||
{"uint256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
|
{"uint256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
|
||||||
{"bytes32", nil, Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}},
|
{"bytes32", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
|
||||||
{"bytes[]", nil, Type{T: SliceTy, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
{"bytes[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
||||||
{"bytes[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
{"bytes[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
||||||
{"bytes32[]", nil, Type{T: SliceTy, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
{"bytes32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
||||||
{"bytes32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
{"bytes32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
||||||
{"string", nil, Type{T: StringTy, stringKind: "string"}},
|
{"string", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
|
||||||
{"string[]", nil, Type{T: SliceTy, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
{"string[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
||||||
{"string[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[2]"}},
|
{"string[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
|
||||||
{"address", nil, Type{Size: 20, T: AddressTy, stringKind: "address"}},
|
{"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
|
||||||
{"address[]", nil, Type{T: SliceTy, Elem: &Type{Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
|
{"address[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
|
||||||
{"address[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
|
{"address[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
|
||||||
// TODO when fixed types are implemented properly
|
// TODO when fixed types are implemented properly
|
||||||
// {"fixed", nil, Type{}},
|
// {"fixed", nil, Type{}},
|
||||||
// {"fixed128x128", nil, Type{}},
|
// {"fixed128x128", nil, Type{}},
|
||||||
@@ -95,18 +95,12 @@ func TestTypeRegexp(t *testing.T) {
|
|||||||
// {"fixed[2]", nil, Type{}},
|
// {"fixed[2]", nil, Type{}},
|
||||||
// {"fixed128x128[]", nil, Type{}},
|
// {"fixed128x128[]", nil, Type{}},
|
||||||
// {"fixed128x128[2]", nil, Type{}},
|
// {"fixed128x128[2]", nil, Type{}},
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct{ A int64 }{}), stringKind: "(int64)",
|
||||||
A int64 `json:"a"`
|
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
||||||
}{}), stringKind: "(int64)",
|
|
||||||
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
|
||||||
{"tuple with long name", []ArgumentMarshaling{{Name: "aTypicalParamName", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
|
||||||
ATypicalParamName int64 `json:"aTypicalParamName"`
|
|
||||||
}{}), stringKind: "(int64)",
|
|
||||||
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"aTypicalParamName"}}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
typ, err := NewType(tt.blob, "", tt.components)
|
typ, err := NewType(tt.blob, tt.components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
||||||
}
|
}
|
||||||
@@ -255,7 +249,7 @@ func TestTypeCheck(t *testing.T) {
|
|||||||
{"bytes", nil, [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
|
{"bytes", nil, [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
|
||||||
{"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"},
|
{"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"},
|
||||||
{"string", nil, "hello world", ""},
|
{"string", nil, "hello world", ""},
|
||||||
{"string", nil, "", ""},
|
{"string", nil, string(""), ""},
|
||||||
{"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
|
{"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
|
||||||
{"bytes32[]", nil, [][32]byte{{}}, ""},
|
{"bytes32[]", nil, [][32]byte{{}}, ""},
|
||||||
{"function", nil, [24]byte{}, ""},
|
{"function", nil, [24]byte{}, ""},
|
||||||
@@ -281,7 +275,7 @@ func TestTypeCheck(t *testing.T) {
|
|||||||
B *big.Int
|
B *big.Int
|
||||||
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
|
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
|
||||||
} {
|
} {
|
||||||
typ, err := NewType(test.typ, "", test.components)
|
typ, err := NewType(test.typ, test.components)
|
||||||
if err != nil && len(test.err) == 0 {
|
if err != nil && len(test.err) == 0 {
|
||||||
t.Fatal("unexpected parse error:", err)
|
t.Fatal("unexpected parse error:", err)
|
||||||
} else if err != nil && len(test.err) != 0 {
|
} else if err != nil && len(test.err) != 0 {
|
||||||
@@ -306,70 +300,3 @@ func TestTypeCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalType(t *testing.T) {
|
|
||||||
components := []ArgumentMarshaling{{Name: "a", Type: "int64"}}
|
|
||||||
internalType := "struct a.b[]"
|
|
||||||
kind := Type{
|
|
||||||
T: TupleTy,
|
|
||||||
TupleType: reflect.TypeOf(struct {
|
|
||||||
A int64 `json:"a"`
|
|
||||||
}{}),
|
|
||||||
stringKind: "(int64)",
|
|
||||||
TupleRawName: "ab[]",
|
|
||||||
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}},
|
|
||||||
TupleRawNames: []string{"a"},
|
|
||||||
}
|
|
||||||
|
|
||||||
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)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetTypeSize(t *testing.T) {
|
|
||||||
var testCases = []struct {
|
|
||||||
typ string
|
|
||||||
components []ArgumentMarshaling
|
|
||||||
typSize int
|
|
||||||
}{
|
|
||||||
// simple array
|
|
||||||
{"uint256[2]", nil, 32 * 2},
|
|
||||||
{"address[3]", nil, 32 * 3},
|
|
||||||
{"bytes32[4]", nil, 32 * 4},
|
|
||||||
// array array
|
|
||||||
{"uint256[2][3][4]", nil, 32 * (2 * 3 * 4)},
|
|
||||||
// array tuple
|
|
||||||
{"tuple[2]", []ArgumentMarshaling{{Name: "x", Type: "bytes32"}, {Name: "y", Type: "bytes32"}}, (32 * 2) * 2},
|
|
||||||
// simple tuple
|
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "uint256"}, {Name: "y", Type: "uint256"}}, 32 * 2},
|
|
||||||
// tuple array
|
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "bytes32[2]"}}, 32 * 2},
|
|
||||||
// tuple tuple
|
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "tuple", Components: []ArgumentMarshaling{{Name: "x", Type: "bytes32"}}}}, 32},
|
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "tuple", Components: []ArgumentMarshaling{{Name: "x", Type: "bytes32[2]"}, {Name: "y", Type: "uint256"}}}}, 32 * (2 + 1)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, data := range testCases {
|
|
||||||
typ, err := NewType(data.typ, "", data.components)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("type %q: failed to parse type string: %v", data.typ, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := getTypeSize(typ)
|
|
||||||
if result != data.typSize {
|
|
||||||
t.Errorf("case %d type %q: get type size error: actual: %d expected: %d", i, data.typ, result, data.typSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewFixedBytesOver32(t *testing.T) {
|
|
||||||
_, 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 (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@@ -27,83 +26,52 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// MaxUint256 is the maximum value that can be represented by a uint256.
|
maxUint256 = big.NewInt(0).Add(
|
||||||
MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
|
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
|
||||||
// MaxInt256 is the maximum value that can be represented by a int256.
|
big.NewInt(-1))
|
||||||
MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1)
|
maxInt256 = big.NewInt(0).Add(
|
||||||
|
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
|
||||||
|
big.NewInt(-1))
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadInteger reads the integer based on its kind and returns the appropriate value.
|
// reads the integer based on its kind
|
||||||
func ReadInteger(typ Type, b []byte) (interface{}, error) {
|
func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
|
||||||
ret := new(big.Int).SetBytes(b)
|
switch kind {
|
||||||
|
case reflect.Uint8:
|
||||||
if typ.T == UintTy {
|
return b[len(b)-1]
|
||||||
u64, isu64 := ret.Uint64(), ret.IsUint64()
|
case reflect.Uint16:
|
||||||
switch typ.Size {
|
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||||
case 8:
|
case reflect.Uint32:
|
||||||
if !isu64 || u64 > math.MaxUint8 {
|
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||||
return nil, errBadUint8
|
case reflect.Uint64:
|
||||||
}
|
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||||
return byte(u64), nil
|
case reflect.Int8:
|
||||||
case 16:
|
return int8(b[len(b)-1])
|
||||||
if !isu64 || u64 > math.MaxUint16 {
|
case reflect.Int16:
|
||||||
return nil, errBadUint16
|
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
||||||
}
|
case reflect.Int32:
|
||||||
return uint16(u64), nil
|
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
||||||
case 32:
|
case reflect.Int64:
|
||||||
if !isu64 || u64 > math.MaxUint32 {
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
return nil, errBadUint32
|
|
||||||
}
|
|
||||||
return uint32(u64), nil
|
|
||||||
case 64:
|
|
||||||
if !isu64 {
|
|
||||||
return nil, errBadUint64
|
|
||||||
}
|
|
||||||
return u64, nil
|
|
||||||
default:
|
default:
|
||||||
// the only case left for unsigned integer is uint256.
|
// the only case lefts for integer is int256/uint256.
|
||||||
return ret, nil
|
// big.SetBytes can't tell if a number is negative, positive on itself.
|
||||||
}
|
// On EVM, if the returned number > max int256, it is negative.
|
||||||
|
ret := new(big.Int).SetBytes(b)
|
||||||
|
if typ == UintTy {
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// big.SetBytes can't tell if a number is negative or positive in itself.
|
if ret.Cmp(maxInt256) > 0 {
|
||||||
// On EVM, if the returned number > max int256, it is negative.
|
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
|
||||||
// A number is > max int256 if the bit at position 255 is set.
|
ret.Add(ret, big.NewInt(1))
|
||||||
if ret.Bit(255) == 1 {
|
|
||||||
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
|
||||||
ret.Add(ret, common.Big1)
|
|
||||||
ret.Neg(ret)
|
ret.Neg(ret)
|
||||||
}
|
}
|
||||||
i64, isi64 := ret.Int64(), ret.IsInt64()
|
return ret
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readBool reads a bool.
|
// reads a bool
|
||||||
func readBool(word []byte) (bool, error) {
|
func readBool(word []byte) (bool, error) {
|
||||||
for _, b := range word[:31] {
|
for _, b := range word[:31] {
|
||||||
if b != 0 {
|
if b != 0 {
|
||||||
@@ -121,8 +89,7 @@ func readBool(word []byte) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A function type is simply the address with the function selection signature at the end.
|
// A function type is simply the address with the function selection signature at the end.
|
||||||
//
|
// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
|
||||||
// readFunctionType enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
|
|
||||||
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
if t.T != FunctionTy {
|
if t.T != FunctionTy {
|
||||||
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
|
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
|
||||||
@@ -135,25 +102,26 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFixedBytes uses reflection to create a fixed array to be read from.
|
// through reflection, creates a fixed array to be read from
|
||||||
func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
func readFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
if t.T != FixedBytesTy {
|
if t.T != FixedBytesTy {
|
||||||
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
|
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
|
||||||
}
|
}
|
||||||
// convert
|
// convert
|
||||||
array := reflect.New(t.GetType()).Elem()
|
array := reflect.New(t.Type).Elem()
|
||||||
|
|
||||||
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
||||||
return array.Interface(), nil
|
return array.Interface(), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forEachUnpack iteratively unpack elements.
|
// iteratively unpack elements
|
||||||
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
|
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
||||||
}
|
}
|
||||||
if start+32*size > len(output) {
|
if start+32*size > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this value will become our slice or our array, depending on the type
|
// this value will become our slice or our array, depending on the type
|
||||||
@@ -161,10 +129,10 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||||||
|
|
||||||
if t.T == SliceTy {
|
if t.T == SliceTy {
|
||||||
// declare our slice
|
// declare our slice
|
||||||
refSlice = reflect.MakeSlice(t.GetType(), size, size)
|
refSlice = reflect.MakeSlice(t.Type, size, size)
|
||||||
} else if t.T == ArrayTy {
|
} else if t.T == ArrayTy {
|
||||||
// declare our array
|
// declare our array
|
||||||
refSlice = reflect.New(t.GetType()).Elem()
|
refSlice = reflect.New(t.Type).Elem()
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
||||||
}
|
}
|
||||||
@@ -188,13 +156,10 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||||
retval := reflect.New(t.GetType()).Elem()
|
retval := reflect.New(t.Type).Elem()
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, elem := range t.TupleElems {
|
for index, elem := range t.TupleElems {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if elem.T == ArrayTy && !isDynamicType(*elem) {
|
if elem.T == ArrayTy && !isDynamicType(*elem) {
|
||||||
// If we have a static array, like [3]uint256, these are coded as
|
// If we have a static array, like [3]uint256, these are coded as
|
||||||
// just like uint256,uint256,uint256.
|
// just like uint256,uint256,uint256.
|
||||||
@@ -212,6 +177,9 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
|||||||
// coded as just like uint256,bool,uint256
|
// coded as just like uint256,bool,uint256
|
||||||
virtualArgs += getTypeSize(*elem)/32 - 1
|
virtualArgs += getTypeSize(*elem)/32 - 1
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
|
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
|
||||||
}
|
}
|
||||||
return retval.Interface(), nil
|
return retval.Interface(), nil
|
||||||
@@ -248,23 +216,21 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return forTupleUnpack(t, output[begin:])
|
return forTupleUnpack(t, output[begin:])
|
||||||
}
|
} else {
|
||||||
return forTupleUnpack(t, output[index:])
|
return forTupleUnpack(t, output[index:])
|
||||||
|
}
|
||||||
case SliceTy:
|
case SliceTy:
|
||||||
return forEachUnpack(t, output[begin:], 0, length)
|
return forEachUnpack(t, output[begin:], 0, length)
|
||||||
case ArrayTy:
|
case ArrayTy:
|
||||||
if isDynamicType(*t.Elem) {
|
if isDynamicType(*t.Elem) {
|
||||||
offset := binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:])
|
offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]))
|
||||||
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[offset:], 0, t.Size)
|
||||||
}
|
}
|
||||||
return forEachUnpack(t, output[index:], 0, t.Size)
|
return forEachUnpack(t, output[index:], 0, t.Size)
|
||||||
case StringTy: // variable arrays are written at the end of the return bytes
|
case StringTy: // variable arrays are written at the end of the return bytes
|
||||||
return string(output[begin : begin+length]), nil
|
return string(output[begin : begin+length]), nil
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return ReadInteger(t, returnOutput)
|
return readInteger(t.T, t.Kind, returnOutput), nil
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return readBool(returnOutput)
|
return readBool(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
@@ -274,7 +240,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
|||||||
case BytesTy:
|
case BytesTy:
|
||||||
return output[begin : begin+length], nil
|
return output[begin : begin+length], nil
|
||||||
case FixedBytesTy:
|
case FixedBytesTy:
|
||||||
return ReadFixedBytes(t, returnOutput)
|
return readFixedBytes(t, returnOutput)
|
||||||
case FunctionTy:
|
case FunctionTy:
|
||||||
return readFunctionType(t, returnOutput)
|
return readFunctionType(t, returnOutput)
|
||||||
default:
|
default:
|
||||||
@@ -282,9 +248,9 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
|
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
|
||||||
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
||||||
bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
|
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
|
||||||
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
||||||
outputLength := big.NewInt(int64(len(output)))
|
outputLength := big.NewInt(int64(len(output)))
|
||||||
|
|
||||||
@@ -297,11 +263,13 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
offsetEnd := int(bigOffsetEnd.Uint64())
|
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 {
|
if totalSize.BitLen() > 63 {
|
||||||
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
|
return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalSize.Cmp(outputLength) > 0 {
|
if totalSize.Cmp(outputLength) > 0 {
|
||||||
@@ -314,10 +282,10 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
|||||||
|
|
||||||
// tuplePointsTo resolves the location reference for dynamic tuple.
|
// tuplePointsTo resolves the location reference for dynamic tuple.
|
||||||
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
||||||
offset := new(big.Int).SetBytes(output[index : index+32])
|
offset := big.NewInt(0).SetBytes(output[index : index+32])
|
||||||
outputLen := big.NewInt(int64(len(output)))
|
outputLen := big.NewInt(int64(len(output)))
|
||||||
|
|
||||||
if offset.Cmp(outputLen) > 0 {
|
if offset.Cmp(big.NewInt(int64(len(output)))) > 0 {
|
||||||
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
|
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
|
||||||
}
|
}
|
||||||
if offset.BitLen() > 63 {
|
if offset.BitLen() > 63 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -31,32 +30,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestUnpack tests the general pack/unpack tests in packing_test.go
|
|
||||||
func TestUnpack(t *testing.T) {
|
|
||||||
for i, test := range packUnpackTests {
|
|
||||||
t.Run(strconv.Itoa(i)+" "+test.def, func(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])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type unpackTest struct {
|
type unpackTest struct {
|
||||||
def string // ABI definition JSON
|
def string // ABI definition JSON
|
||||||
enc string // evm return data
|
enc string // evm return data
|
||||||
@@ -78,7 +51,16 @@ func (test unpackTest) checkError(err error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var unpackTests = []unpackTest{
|
var unpackTests = []unpackTest{
|
||||||
// Bools
|
{
|
||||||
|
def: `[{ "type": "bool" }]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{ "type": "bool" }]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{ "type": "bool" }]`,
|
def: `[{ "type": "bool" }]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000001000000000001",
|
enc: "0000000000000000000000000000000000000000000000000001000000000001",
|
||||||
@@ -91,7 +73,11 @@ var unpackTests = []unpackTest{
|
|||||||
want: false,
|
want: false,
|
||||||
err: "abi: improperly encoded boolean value",
|
err: "abi: improperly encoded boolean value",
|
||||||
},
|
},
|
||||||
// Integers
|
{
|
||||||
|
def: `[{"type": "uint32"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: uint32(1),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "uint32"}]`,
|
def: `[{"type": "uint32"}]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
@@ -104,6 +90,16 @@ var unpackTests = []unpackTest{
|
|||||||
want: uint16(0),
|
want: uint16(0),
|
||||||
err: "abi: cannot unmarshal *big.Int in to uint16",
|
err: "abi: cannot unmarshal *big.Int in to uint16",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint17"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: big.NewInt(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: int32(1),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "int32"}]`,
|
def: `[{"type": "int32"}]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
@@ -116,10 +112,36 @@ var unpackTests = []unpackTest{
|
|||||||
want: int16(0),
|
want: int16(0),
|
||||||
err: "abi: cannot unmarshal *big.Int in to int16",
|
err: "abi: cannot unmarshal *big.Int in to int16",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int17"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: big.NewInt(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256"}]`,
|
||||||
|
enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
want: big.NewInt(-1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "address"}]`,
|
||||||
|
enc: "0000000000000000000000000100000000000000000000000000000000000000",
|
||||||
|
want: common.Address{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "bytes"}]`,
|
def: `[{"type": "bytes"}]`,
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
||||||
want: [32]byte{1},
|
want: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes"}]`,
|
||||||
|
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: [32]byte{},
|
||||||
|
err: "abi: cannot unmarshal []uint8 in to [32]uint8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "bytes32"}]`,
|
def: `[{"type": "bytes32"}]`,
|
||||||
@@ -127,13 +149,219 @@ var unpackTests = []unpackTest{
|
|||||||
want: []byte(nil),
|
want: []byte(nil),
|
||||||
err: "abi: cannot unmarshal [32]uint8 in to []uint8",
|
err: "abi: cannot unmarshal [32]uint8 in to []uint8",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "function"}]`,
|
||||||
|
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: [24]byte{1},
|
||||||
|
},
|
||||||
|
// slices
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []uint8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]uint8{1, 2},
|
||||||
|
},
|
||||||
|
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [][]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
want: [][]uint8{{1, 2}, {1, 2, 3}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][2]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2][2]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][2]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
want: [2][]uint8{{1}, {1}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [][2]uint8{{1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][]"}]`,
|
||||||
|
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [][2]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint16[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []uint16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint16[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]uint16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []uint32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]uint32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[2][3][4]"}]`,
|
||||||
|
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018",
|
||||||
|
want: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []uint64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]uint64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[3]"}]`,
|
||||||
|
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[4]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b476f2d657468657265756d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000",
|
||||||
|
want: [4]string{"Hello", "World", "Go-ethereum", "Ethereum"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000",
|
||||||
|
want: []string{"Ethereum", "go-ethereum"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes[]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003f0f0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f0f0f00000000000000000000000000000000000000000000000000000000000",
|
||||||
|
want: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[2][][]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e8",
|
||||||
|
want: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int8[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []int8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int8[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]int8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []int16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]int16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []int32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]int32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []int64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64[2]"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: [2]int64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256[]"}]`,
|
||||||
|
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256[3]"}]`,
|
||||||
|
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
||||||
|
},
|
||||||
|
// struct outputs
|
||||||
|
{
|
||||||
|
def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
Int1 *big.Int
|
||||||
|
Int2 *big.Int
|
||||||
|
}{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int__one","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one_","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
Intone *big.Int
|
||||||
|
}{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
def: `[{"name":"___","type":"int256"}]`,
|
def: `[{"name":"___","type":"int256"}]`,
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
want: struct {
|
want: struct {
|
||||||
IntOne *big.Int
|
IntOne *big.Int
|
||||||
Intone *big.Int
|
Intone *big.Int
|
||||||
}{IntOne: big.NewInt(1)},
|
}{},
|
||||||
|
err: "abi: purely underscored output cannot unpack to struct",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
|
def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
|
||||||
@@ -180,64 +408,22 @@ var unpackTests = []unpackTest{
|
|||||||
}{},
|
}{},
|
||||||
err: "abi: purely underscored output cannot unpack to struct",
|
err: "abi: purely underscored output cannot unpack to struct",
|
||||||
},
|
},
|
||||||
// Make sure only the first argument is consumed
|
|
||||||
{
|
|
||||||
def: `[{"name":"int_one","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int__one","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int_one_","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
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.
|
func TestUnpack(t *testing.T) {
|
||||||
// All test cases that can be used to test packing and unpacking should move to packing_test.go
|
|
||||||
func TestLocalUnpackTests(t *testing.T) {
|
|
||||||
for i, test := range unpackTests {
|
for i, test := range unpackTests {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
//Unpack
|
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
}
|
}
|
||||||
encb, err := hex.DecodeString(test.enc)
|
encb, err := hex.DecodeString(test.enc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid hex %s: %v", test.enc, err)
|
t.Fatalf("invalid hex: %s" + test.enc)
|
||||||
}
|
}
|
||||||
outptr := reflect.New(reflect.TypeOf(test.want))
|
outptr := reflect.New(reflect.TypeOf(test.want))
|
||||||
err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb)
|
err = abi.Unpack(outptr.Interface(), "method", encb)
|
||||||
if err := test.checkError(err); err != nil {
|
if err := test.checkError(err); err != nil {
|
||||||
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
return
|
return
|
||||||
@@ -250,7 +436,7 @@ func TestLocalUnpackTests(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
func TestUnpackSetDynamicArrayOutput(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`))
|
abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -265,7 +451,7 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// test 32
|
// test 32
|
||||||
err = abi.UnpackIntoInterface(&out32, "testDynamicFixedBytes32", marshalledReturn32)
|
err = abi.Unpack(&out32, "testDynamicFixedBytes32", marshalledReturn32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -282,7 +468,7 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// test 15
|
// test 15
|
||||||
err = abi.UnpackIntoInterface(&out15, "testDynamicFixedBytes32", marshalledReturn15)
|
err = abi.Unpack(&out15, "testDynamicFixedBytes32", marshalledReturn15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -306,7 +492,7 @@ type methodMultiOutput struct {
|
|||||||
|
|
||||||
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
@@ -326,11 +512,6 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||||||
Int *big.Int
|
Int *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
newInterfaceSlice := func(len int) interface{} {
|
|
||||||
slice := make([]interface{}, len)
|
|
||||||
return &slice
|
|
||||||
}
|
|
||||||
|
|
||||||
abi, data, expected := methodMultiReturn(require.New(t))
|
abi, data, expected := methodMultiReturn(require.New(t))
|
||||||
bigint := new(big.Int)
|
bigint := new(big.Int)
|
||||||
var testCases = []struct {
|
var testCases = []struct {
|
||||||
@@ -353,26 +534,11 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||||||
&[]interface{}{&expected.Int, &expected.String},
|
&[]interface{}{&expected.Int, &expected.String},
|
||||||
"",
|
"",
|
||||||
"Can unpack into a slice",
|
"Can unpack into a slice",
|
||||||
}, {
|
|
||||||
&[]interface{}{&bigint, ""},
|
|
||||||
&[]interface{}{&expected.Int, expected.String},
|
|
||||||
"",
|
|
||||||
"Can unpack into a slice without indirection",
|
|
||||||
}, {
|
}, {
|
||||||
&[2]interface{}{&bigint, new(string)},
|
&[2]interface{}{&bigint, new(string)},
|
||||||
&[2]interface{}{&expected.Int, &expected.String},
|
&[2]interface{}{&expected.Int, &expected.String},
|
||||||
"",
|
"",
|
||||||
"Can unpack into an array",
|
"Can unpack into an array",
|
||||||
}, {
|
|
||||||
&[2]interface{}{},
|
|
||||||
&[2]interface{}{expected.Int, expected.String},
|
|
||||||
"",
|
|
||||||
"Can unpack into interface array",
|
|
||||||
}, {
|
|
||||||
newInterfaceSlice(2),
|
|
||||||
&[]interface{}{expected.Int, expected.String},
|
|
||||||
"",
|
|
||||||
"Can unpack into interface slice",
|
|
||||||
}, {
|
}, {
|
||||||
&[]interface{}{new(int), new(int)},
|
&[]interface{}{new(int), new(int)},
|
||||||
&[]interface{}{&expected.Int, &expected.String},
|
&[]interface{}{&expected.Int, &expected.String},
|
||||||
@@ -381,14 +547,14 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||||||
}, {
|
}, {
|
||||||
&[]interface{}{new(int)},
|
&[]interface{}{new(int)},
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
"abi: insufficient number of arguments for unpack, want 2, got 1",
|
"abi: insufficient number of elements in the list/array for unpack, want 2, got 1",
|
||||||
"Can not unpack into a slice with wrong types",
|
"Can not unpack into a slice with wrong types",
|
||||||
}}
|
}}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
err := abi.UnpackIntoInterface(tc.dest, "multi", data)
|
err := abi.Unpack(tc.dest, "multi", data)
|
||||||
if tc.error == "" {
|
if tc.error == "" {
|
||||||
require.Nil(err, "Should be able to unpack method outputs.")
|
require.Nil(err, "Should be able to unpack method outputs.")
|
||||||
require.Equal(tc.expected, tc.dest)
|
require.Equal(tc.expected, tc.dest)
|
||||||
@@ -400,7 +566,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithArray(t *testing.T) {
|
func TestMultiReturnWithArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -411,7 +577,7 @@ func TestMultiReturnWithArray(t *testing.T) {
|
|||||||
|
|
||||||
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
|
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
|
||||||
ret2, ret2Exp := new(uint64), uint64(8)
|
ret2, ret2Exp := new(uint64), uint64(8)
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
@@ -423,19 +589,19 @@ func TestMultiReturnWithArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringArray(t *testing.T) {
|
func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
||||||
temp, _ := 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}
|
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
|
||||||
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
||||||
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
||||||
ret4, ret4Exp := new(bool), false
|
ret4, ret4Exp := new(bool), false
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
|
if err := abi.Unpack(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
@@ -453,7 +619,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringSlice(t *testing.T) {
|
func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -473,7 +639,7 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
|
||||||
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
|
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
|
||||||
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
|
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
@@ -489,7 +655,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
|||||||
// values of nested static arrays count towards the size as well, and any element following
|
// values of nested static arrays count towards the size as well, and any element following
|
||||||
// after such nested array argument should be read with the correct offset,
|
// after such nested array argument should be read with the correct offset,
|
||||||
// so that it does not read content from the previous array argument.
|
// so that it does not read content from the previous array argument.
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -513,7 +679,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
|||||||
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
||||||
}
|
}
|
||||||
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
@@ -526,15 +692,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
|||||||
|
|
||||||
func TestUnmarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "int", "type": "function", "outputs": [ { "type": "uint256" } ] },
|
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
|
||||||
{ "name" : "bool", "type": "function", "outputs": [ { "type": "bool" } ] },
|
{ "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] },
|
||||||
{ "name" : "bytes", "type": "function", "outputs": [ { "type": "bytes" } ] },
|
{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
|
||||||
{ "name" : "fixed", "type": "function", "outputs": [ { "type": "bytes32" } ] },
|
{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
|
||||||
{ "name" : "multi", "type": "function", "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
||||||
{ "name" : "intArraySingle", "type": "function", "outputs": [ { "type": "uint256[3]" } ] },
|
{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
|
||||||
{ "name" : "addressSliceSingle", "type": "function", "outputs": [ { "type": "address[]" } ] },
|
{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
|
||||||
{ "name" : "addressSliceDouble", "type": "function", "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
||||||
{ "name" : "mixedBytes", "type": "function", "stateMutability" : "view", "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -552,7 +718,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a"))
|
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a"))
|
||||||
buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000"))
|
buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&mixedBytes, "mixedBytes", buff.Bytes())
|
err = abi.Unpack(&mixedBytes, "mixedBytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else {
|
} else {
|
||||||
@@ -567,7 +733,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
|
|
||||||
// marshal int
|
// marshal int
|
||||||
var Int *big.Int
|
var Int *big.Int
|
||||||
err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -578,7 +744,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
|
|
||||||
// marshal bool
|
// marshal bool
|
||||||
var Bool bool
|
var Bool bool
|
||||||
err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -595,7 +761,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(bytesOut)
|
buff.Write(bytesOut)
|
||||||
|
|
||||||
var Bytes []byte
|
var Bytes []byte
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -611,7 +777,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
bytesOut = common.RightPadBytes([]byte("hello"), 64)
|
bytesOut = common.RightPadBytes([]byte("hello"), 64)
|
||||||
buff.Write(bytesOut)
|
buff.Write(bytesOut)
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -627,7 +793,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
bytesOut = common.RightPadBytes([]byte("hello"), 64)
|
bytesOut = common.RightPadBytes([]byte("hello"), 64)
|
||||||
buff.Write(bytesOut)
|
buff.Write(bytesOut)
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -637,7 +803,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// marshal dynamic bytes output empty
|
// marshal dynamic bytes output empty
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", nil)
|
err = abi.Unpack(&Bytes, "bytes", nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error")
|
t.Error("expected error")
|
||||||
}
|
}
|
||||||
@@ -648,7 +814,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
|
||||||
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -662,7 +828,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
||||||
|
|
||||||
var hash common.Hash
|
var hash common.Hash
|
||||||
err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes())
|
err = abi.Unpack(&hash, "fixed", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -675,12 +841,12 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
// marshal error
|
// marshal error
|
||||||
buff.Reset()
|
buff.Reset()
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error")
|
t.Error("expected error")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "multi", make([]byte, 64))
|
err = abi.Unpack(&Bytes, "multi", make([]byte, 64))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error")
|
t.Error("expected error")
|
||||||
}
|
}
|
||||||
@@ -691,7 +857,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
// marshal int array
|
// marshal int array
|
||||||
var intArray [3]*big.Int
|
var intArray [3]*big.Int
|
||||||
err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes())
|
err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -712,7 +878,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
|
||||||
|
|
||||||
var outAddr []common.Address
|
var outAddr []common.Address
|
||||||
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
|
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("didn't expect error:", err)
|
t.Fatal("didn't expect error:", err)
|
||||||
}
|
}
|
||||||
@@ -739,7 +905,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
A []common.Address
|
A []common.Address
|
||||||
B []common.Address
|
B []common.Address
|
||||||
}
|
}
|
||||||
err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes())
|
err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("didn't expect error:", err)
|
t.Fatal("didn't expect error:", err)
|
||||||
}
|
}
|
||||||
@@ -767,14 +933,14 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
buff.Reset()
|
buff.Reset()
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
|
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error:", err)
|
t.Fatal("expected error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackTuple(t *testing.T) {
|
func TestUnpackTuple(t *testing.T) {
|
||||||
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
||||||
abi, err := JSON(strings.NewReader(simpleTuple))
|
abi, err := JSON(strings.NewReader(simpleTuple))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -784,30 +950,30 @@ func TestUnpackTuple(t *testing.T) {
|
|||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
|
||||||
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
|
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
|
||||||
|
|
||||||
// If the result is single tuple, use struct as return value container directly.
|
v := struct {
|
||||||
type v struct {
|
Ret struct {
|
||||||
A *big.Int
|
A *big.Int
|
||||||
B *big.Int
|
B *big.Int
|
||||||
}
|
}
|
||||||
type r struct {
|
}{Ret: struct {
|
||||||
Result v
|
A *big.Int
|
||||||
}
|
B *big.Int
|
||||||
var ret0 = new(r)
|
}{new(big.Int), new(big.Int)}}
|
||||||
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
|
||||||
|
|
||||||
|
err = abi.Unpack(&v, "tuple", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else {
|
} else {
|
||||||
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
|
if v.Ret.A.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
|
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.Ret.A)
|
||||||
}
|
}
|
||||||
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
|
if v.Ret.B.Cmp(big.NewInt(-1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
|
t.Errorf("unexpected value unpacked: want %x, got %x", v.Ret.B, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nested tuple
|
// Test nested tuple
|
||||||
const nestedTuple = `[{"name":"tuple","type":"function","outputs":[
|
const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
|
||||||
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
|
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
|
||||||
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
|
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
|
||||||
{"type":"uint256","name":"a"}
|
{"type":"uint256","name":"a"}
|
||||||
@@ -866,7 +1032,7 @@ func TestUnpackTuple(t *testing.T) {
|
|||||||
A: big.NewInt(1),
|
A: big.NewInt(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&ret, "tuple", buff.Bytes())
|
err = abi.Unpack(&ret, "tuple", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -929,7 +1095,7 @@ func TestOOMMaliciousInput(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range oomTests {
|
for i, test := range oomTests {
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
@@ -944,164 +1110,3 @@ func TestOOMMaliciousInput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
|
|
||||||
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,40 +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
|
|
||||||
}
|
|
||||||
@@ -18,14 +18,12 @@
|
|||||||
package accounts
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account represents an Ethereum account located at a specific location defined
|
// Account represents an Ethereum account located at a specific location defined
|
||||||
@@ -35,18 +33,11 @@ type Account struct {
|
|||||||
URL URL `json:"url"` // Optional resource locator within a backend
|
URL URL `json:"url"` // Optional resource locator within a backend
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
MimetypeDataWithValidator = "data/validator"
|
|
||||||
MimetypeTypedData = "data/typed"
|
|
||||||
MimetypeClique = "application/x-clique-header"
|
|
||||||
MimetypeTextPlain = "text/plain"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wallet represents a software or hardware wallet that might contain one or more
|
// Wallet represents a software or hardware wallet that might contain one or more
|
||||||
// accounts (derived from the same seed).
|
// accounts (derived from the same seed).
|
||||||
type Wallet interface {
|
type Wallet interface {
|
||||||
// URL retrieves the canonical path under which this wallet is reachable. It is
|
// URL retrieves the canonical path under which this wallet is reachable. It is
|
||||||
// used by upper layers to define a sorting order over all wallets from multiple
|
// user by upper layers to define a sorting order over all wallets from multiple
|
||||||
// backends.
|
// backends.
|
||||||
URL() URL
|
URL() URL
|
||||||
|
|
||||||
@@ -88,53 +79,26 @@ type Wallet interface {
|
|||||||
// to discover non zero accounts and automatically add them to list of tracked
|
// to discover non zero accounts and automatically add them to list of tracked
|
||||||
// accounts.
|
// accounts.
|
||||||
//
|
//
|
||||||
// Note, self derivation will increment the last component of the specified path
|
// Note, self derivaton will increment the last component of the specified path
|
||||||
// opposed to descending into a child path to allow discovering accounts starting
|
// opposed to decending into a child path to allow discovering accounts starting
|
||||||
// from non zero components.
|
// from non zero components.
|
||||||
//
|
//
|
||||||
// Some hardware wallets switched derivation paths through their evolution, so
|
|
||||||
// this method supports providing multiple bases to discover old user accounts
|
|
||||||
// too. Only the last base will be used to derive the next empty account.
|
|
||||||
//
|
|
||||||
// You can disable automatic account discovery by calling SelfDerive with a nil
|
// You can disable automatic account discovery by calling SelfDerive with a nil
|
||||||
// chain state reader.
|
// chain state reader.
|
||||||
SelfDerive(bases []DerivationPath, chain ethereum.ChainStateReader)
|
SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
|
||||||
|
|
||||||
// SignData requests the wallet to sign the hash of the given data
|
// SignHash requests the wallet to sign the given hash.
|
||||||
|
//
|
||||||
// It looks up the account specified either solely via its address contained within,
|
// It looks up the account specified either solely via its address contained within,
|
||||||
// or optionally with the aid of any location metadata from the embedded URL field.
|
// or optionally with the aid of any location metadata from the embedded URL field.
|
||||||
//
|
//
|
||||||
// If the wallet requires additional authentication to sign the request (e.g.
|
// If the wallet requires additional authentication to sign the request (e.g.
|
||||||
// a password to decrypt the account, or a PIN code to verify the transaction),
|
// a password to decrypt the account, or a PIN code o verify the transaction),
|
||||||
// an AuthNeededError instance will be returned, containing infos for the user
|
// an AuthNeededError instance will be returned, containing infos for the user
|
||||||
// about which fields or actions are needed. The user may retry by providing
|
// about which fields or actions are needed. The user may retry by providing
|
||||||
// the needed details via SignDataWithPassphrase, or by other means (e.g. unlock
|
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
|
||||||
// the account in a keystore).
|
// the account in a keystore).
|
||||||
SignData(account Account, mimeType string, data []byte) ([]byte, error)
|
SignHash(account Account, hash []byte) ([]byte, error)
|
||||||
|
|
||||||
// SignDataWithPassphrase is identical to SignData, but also takes a password
|
|
||||||
// NOTE: there's a chance that an erroneous call might mistake the two strings, and
|
|
||||||
// supply password in the mimetype field, or vice versa. Thus, an implementation
|
|
||||||
// should never echo the mimetype or return the mimetype in the error-response
|
|
||||||
SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error)
|
|
||||||
|
|
||||||
// SignText requests the wallet to sign the hash of a given piece of data, prefixed
|
|
||||||
// by the Ethereum prefix scheme
|
|
||||||
// It looks up the account specified either solely via its address contained within,
|
|
||||||
// or optionally with the aid of any location metadata from the embedded URL field.
|
|
||||||
//
|
|
||||||
// If the wallet requires additional authentication to sign the request (e.g.
|
|
||||||
// a password to decrypt the account, or a PIN code to verify the transaction),
|
|
||||||
// an AuthNeededError instance will be returned, containing infos for the user
|
|
||||||
// about which fields or actions are needed. The user may retry by providing
|
|
||||||
// the needed details via SignTextWithPassphrase, or by other means (e.g. unlock
|
|
||||||
// the account in a keystore).
|
|
||||||
//
|
|
||||||
// This method should return the signature in 'canonical' format, with v 0 or 1.
|
|
||||||
SignText(account Account, text []byte) ([]byte, error)
|
|
||||||
|
|
||||||
// SignTextWithPassphrase is identical to Signtext, but also takes a password
|
|
||||||
SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
|
|
||||||
|
|
||||||
// SignTx requests the wallet to sign the given transaction.
|
// SignTx requests the wallet to sign the given transaction.
|
||||||
//
|
//
|
||||||
@@ -149,7 +113,18 @@ type Wallet interface {
|
|||||||
// the account in a keystore).
|
// the account in a keystore).
|
||||||
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
||||||
|
|
||||||
// SignTxWithPassphrase is identical to SignTx, but also takes a password
|
// SignHashWithPassphrase requests the wallet to sign the given hash with the
|
||||||
|
// given passphrase as extra authentication information.
|
||||||
|
//
|
||||||
|
// It looks up the account specified either solely via its address contained within,
|
||||||
|
// or optionally with the aid of any location metadata from the embedded URL field.
|
||||||
|
SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
|
||||||
|
|
||||||
|
// SignTxWithPassphrase requests the wallet to sign the given transaction, with the
|
||||||
|
// given passphrase as extra authentication information.
|
||||||
|
//
|
||||||
|
// It looks up the account specified either solely via its address contained within,
|
||||||
|
// or optionally with the aid of any location metadata from the embedded URL field.
|
||||||
SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,34 +148,6 @@ type Backend interface {
|
|||||||
Subscribe(sink chan<- WalletEvent) event.Subscription
|
Subscribe(sink chan<- WalletEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextHash is a helper function that calculates a hash for the given message that can be
|
|
||||||
// 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.
|
|
||||||
func TextHash(data []byte) []byte {
|
|
||||||
hash, _ := TextAndHash(data)
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextAndHash is a helper function that calculates a hash for the given message that can be
|
|
||||||
// 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.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalletEventType represents the different event types that can be fired by
|
// WalletEventType represents the different event types that can be fired by
|
||||||
// the wallet subscription subsystem.
|
// the wallet subscription subsystem.
|
||||||
type WalletEventType int
|
type WalletEventType int
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
// Copyright 2015 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 accounts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,13 +35,14 @@ var ErrNotSupported = errors.New("not supported")
|
|||||||
|
|
||||||
// ErrInvalidPassphrase is returned when a decryption operation receives a bad
|
// ErrInvalidPassphrase is returned when a decryption operation receives a bad
|
||||||
// passphrase.
|
// passphrase.
|
||||||
var ErrInvalidPassphrase = errors.New("invalid password")
|
var ErrInvalidPassphrase = errors.New("invalid passphrase")
|
||||||
|
|
||||||
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
|
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
|
||||||
// second time.
|
// second time.
|
||||||
var ErrWalletAlreadyOpen = errors.New("wallet already open")
|
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
|
||||||
|
// secodn time.
|
||||||
var ErrWalletClosed = errors.New("wallet closed")
|
var ErrWalletClosed = errors.New("wallet closed")
|
||||||
|
|
||||||
// AuthNeededError is returned by backends for signing requests where the user
|
// AuthNeededError is returned by backends for signing requests where the user
|
||||||
|
|||||||
269
accounts/external/backend.go
vendored
269
accounts/external/backend.go
vendored
@@ -1,269 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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 external
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"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/event"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ExternalBackend struct {
|
|
||||||
signers []accounts.Wallet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (eb *ExternalBackend) Wallets() []accounts.Wallet {
|
|
||||||
return eb.signers
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
|
|
||||||
signer, err := NewExternalSigner(endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ExternalBackend{
|
|
||||||
signers: []accounts.Wallet{signer},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (eb *ExternalBackend) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
<-quit
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExternalSigner provides an API to interact with an external signer (clef)
|
|
||||||
// It proxies request to the external signer while forwarding relevant
|
|
||||||
// request headers
|
|
||||||
type ExternalSigner struct {
|
|
||||||
client *rpc.Client
|
|
||||||
endpoint string
|
|
||||||
status string
|
|
||||||
cacheMu sync.RWMutex
|
|
||||||
cache []accounts.Account
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
|
|
||||||
client, err := rpc.Dial(endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
extsigner := &ExternalSigner{
|
|
||||||
client: client,
|
|
||||||
endpoint: endpoint,
|
|
||||||
}
|
|
||||||
// Check if reachable
|
|
||||||
version, err := extsigner.pingVersion()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
extsigner.status = fmt.Sprintf("ok [version=%v]", version)
|
|
||||||
return extsigner, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) URL() accounts.URL {
|
|
||||||
return accounts.URL{
|
|
||||||
Scheme: "extapi",
|
|
||||||
Path: api.endpoint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) Status() (string, error) {
|
|
||||||
return api.status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) Open(passphrase string) error {
|
|
||||||
return fmt.Errorf("operation not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) Close() error {
|
|
||||||
return fmt.Errorf("operation not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
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{
|
|
||||||
Scheme: "extapi",
|
|
||||||
Path: api.endpoint,
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
|
|
||||||
return accounts.Account{}, fmt.Errorf("operation not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
|
||||||
log.Error("operation SelfDerive 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,
|
|
||||||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
|
||||||
hexutil.Encode(data)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If V is on 27/28-form, convert to 0/1 for Clique
|
|
||||||
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,
|
|
||||||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// signTransactionResult represents the signinig result returned by clef.
|
|
||||||
type signTransactionResult struct {
|
|
||||||
Raw hexutil.Bytes `json:"raw"`
|
|
||||||
Tx *types.Transaction `json:"tx"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignTx sends the transaction to the external signer.
|
|
||||||
// If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned
|
|
||||||
// by the external signer. For non-legacy transactions, the chain ID of the
|
|
||||||
// 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()),
|
|
||||||
Value: hexutil.Big(*tx.Value()),
|
|
||||||
Gas: hexutil.Uint64(tx.Gas()),
|
|
||||||
To: to,
|
|
||||||
From: common.NewMixedcaseAddress(account.Address),
|
|
||||||
}
|
|
||||||
switch tx.Type() {
|
|
||||||
case types.LegacyTxType, types.AccessListTxType:
|
|
||||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
|
||||||
case types.DynamicFeeTxType:
|
|
||||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
|
||||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
|
|
||||||
}
|
|
||||||
// We should request the default chain id that we're operating with
|
|
||||||
// (the chain we're executing on)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
|
||||||
return []byte{}, fmt.Errorf("password-operations not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
|
||||||
return nil, fmt.Errorf("password-operations not supported on external signers")
|
|
||||||
}
|
|
||||||
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("password-operations not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
|
||||||
var res []common.Address
|
|
||||||
if err := api.client.Call(&res, "account_list"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ExternalSigner) pingVersion() (string, error) {
|
|
||||||
var v string
|
|
||||||
if err := api.client.Call(&v, "account_version"); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
package accounts
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@@ -35,13 +34,13 @@ var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60,
|
|||||||
// at m/44'/60'/0'/0/1, etc.
|
// at m/44'/60'/0'/0/1, etc.
|
||||||
var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
|
var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
|
||||||
|
|
||||||
// LegacyLedgerBaseDerivationPath is the legacy base path from which custom derivation
|
// DefaultLedgerBaseDerivationPath is the base path from which custom derivation endpoints
|
||||||
// endpoints are incremented. As such, the first account will be at m/44'/60'/0'/0, the
|
// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second
|
||||||
// second at m/44'/60'/0'/1, etc.
|
// at m/44'/60'/0'/1, etc.
|
||||||
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
var DefaultLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
||||||
|
|
||||||
// DerivationPath represents the computer friendly version of a hierarchical
|
// DerivationPath represents the computer friendly version of a hierarchical
|
||||||
// deterministic wallet account derivation path.
|
// deterministic wallet account derivaion path.
|
||||||
//
|
//
|
||||||
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
// defines derivation paths to be of the form:
|
// defines derivation paths to be of the form:
|
||||||
@@ -134,47 +133,3 @@ func (path DerivationPath) String() string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON turns a derivation path into its json-serialized string
|
|
||||||
func (path DerivationPath) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(path.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultIterator creates a BIP-32 path iterator, which progresses by increasing the last component:
|
|
||||||
// i.e. m/44'/60'/0'/0/0, m/44'/60'/0'/0/1, m/44'/60'/0'/0/2, ... m/44'/60'/0'/0/N.
|
|
||||||
func DefaultIterator(base DerivationPath) func() DerivationPath {
|
|
||||||
path := make(DerivationPath, len(base))
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LedgerLiveIterator creates a bip44 path iterator for Ledger Live.
|
|
||||||
// Ledger Live increments the third component rather than the fifth component
|
|
||||||
// i.e. m/44'/60'/0'/0/0, m/44'/60'/1'/0/0, m/44'/60'/2'/0/0, ... m/44'/60'/N'/0/0.
|
|
||||||
func LedgerLiveIterator(base DerivationPath) func() DerivationPath {
|
|
||||||
path := make(DerivationPath, len(base))
|
|
||||||
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]++
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package accounts
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -62,7 +61,7 @@ func TestHDPathParsing(t *testing.T) {
|
|||||||
// Weird inputs just to ensure they work
|
// Weird inputs just to ensure they work
|
||||||
{" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
|
{" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
|
||||||
|
|
||||||
// Invalid derivation paths
|
// Invaid derivation paths
|
||||||
{"", nil}, // Empty relative derivation path
|
{"", nil}, // Empty relative derivation path
|
||||||
{"m", nil}, // Empty absolute derivation path
|
{"m", nil}, // Empty absolute derivation path
|
||||||
{"m/", nil}, // Missing last derivation component
|
{"m/", nil}, // Missing last derivation component
|
||||||
@@ -78,41 +77,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHdPathIteration(t *testing.T) {
|
|
||||||
testDerive(t, DefaultIterator(DefaultBaseDerivationPath),
|
|
||||||
[]string{
|
|
||||||
"m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1",
|
|
||||||
"m/44'/60'/0'/0/2", "m/44'/60'/0'/0/3",
|
|
||||||
"m/44'/60'/0'/0/4", "m/44'/60'/0'/0/5",
|
|
||||||
"m/44'/60'/0'/0/6", "m/44'/60'/0'/0/7",
|
|
||||||
"m/44'/60'/0'/0/8", "m/44'/60'/0'/0/9",
|
|
||||||
})
|
|
||||||
|
|
||||||
testDerive(t, DefaultIterator(LegacyLedgerBaseDerivationPath),
|
|
||||||
[]string{
|
|
||||||
"m/44'/60'/0'/0", "m/44'/60'/0'/1",
|
|
||||||
"m/44'/60'/0'/2", "m/44'/60'/0'/3",
|
|
||||||
"m/44'/60'/0'/4", "m/44'/60'/0'/5",
|
|
||||||
"m/44'/60'/0'/6", "m/44'/60'/0'/7",
|
|
||||||
"m/44'/60'/0'/8", "m/44'/60'/0'/9",
|
|
||||||
})
|
|
||||||
|
|
||||||
testDerive(t, LedgerLiveIterator(DefaultBaseDerivationPath),
|
|
||||||
[]string{
|
|
||||||
"m/44'/60'/0'/0/0", "m/44'/60'/1'/0/0",
|
|
||||||
"m/44'/60'/2'/0/0", "m/44'/60'/3'/0/0",
|
|
||||||
"m/44'/60'/4'/0/0", "m/44'/60'/5'/0/0",
|
|
||||||
"m/44'/60'/6'/0/0", "m/44'/60'/7'/0/0",
|
|
||||||
"m/44'/60'/8'/0/0", "m/44'/60'/9'/0/0",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
@@ -79,7 +79,7 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
|
|||||||
keydir: keydir,
|
keydir: keydir,
|
||||||
byAddr: make(map[common.Address][]accounts.Account),
|
byAddr: make(map[common.Address][]accounts.Account),
|
||||||
notify: make(chan struct{}, 1),
|
notify: make(chan struct{}, 1),
|
||||||
fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
|
fileC: fileCache{all: mapset.NewThreadUnsafeSet()},
|
||||||
}
|
}
|
||||||
ac.watcher = newWatcher(ac)
|
ac.watcher = newWatcher(ac)
|
||||||
return ac, ac.notify
|
return ac, ac.notify
|
||||||
@@ -146,14 +146,6 @@ func (ac *accountCache) deleteByFile(path string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// watcherStarted returns true if the watcher loop started running (even if it
|
|
||||||
// has since also ended).
|
|
||||||
func (ac *accountCache) watcherStarted() bool {
|
|
||||||
ac.mu.Lock()
|
|
||||||
defer ac.mu.Unlock()
|
|
||||||
return ac.watcher.running || ac.watcher.runEnded
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
|
func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
|
||||||
for i := range slice {
|
for i := range slice {
|
||||||
if slice[i] == elem {
|
if slice[i] == elem {
|
||||||
@@ -270,7 +262,7 @@ func (ac *accountCache) scanAccounts() error {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Debug("Failed to decode keystore key", "path", path, "err", err)
|
log.Debug("Failed to decode keystore key", "path", path, "err", err)
|
||||||
case addr == common.Address{}:
|
case (addr == common.Address{}):
|
||||||
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
||||||
default:
|
default:
|
||||||
return &accounts.Account{
|
return &accounts.Account{
|
||||||
@@ -283,15 +275,16 @@ func (ac *accountCache) scanAccounts() error {
|
|||||||
// Process all the file diffs
|
// Process all the file diffs
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
for _, path := range creates.ToSlice() {
|
for _, p := range creates.ToSlice() {
|
||||||
if a := readAccount(path); a != nil {
|
if a := readAccount(p.(string)); a != nil {
|
||||||
ac.add(*a)
|
ac.add(*a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, path := range deletes.ToSlice() {
|
for _, p := range deletes.ToSlice() {
|
||||||
ac.deleteByFile(path)
|
ac.deleteByFile(p.(string))
|
||||||
}
|
}
|
||||||
for _, path := range updates.ToSlice() {
|
for _, p := range updates.ToSlice() {
|
||||||
|
path := p.(string)
|
||||||
ac.deleteByFile(path)
|
ac.deleteByFile(path)
|
||||||
if a := readAccount(path); a != nil {
|
if a := readAccount(path); a != nil {
|
||||||
ac.add(*a)
|
ac.add(*a)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package keystore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -50,48 +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) {
|
func TestWatchNewFile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dir, ks := tmpKeyStore(t, false)
|
dir, ks := tmpKeyStore(t, false)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// Ensure the watcher is started before adding any files.
|
// Ensure the watcher is started before adding any files.
|
||||||
ks.Accounts()
|
ks.Accounts()
|
||||||
if !waitWatcherStart(ks) {
|
time.Sleep(1000 * time.Millisecond)
|
||||||
t.Fatal("keystore watcher didn't start in time")
|
|
||||||
}
|
|
||||||
// Move in the files.
|
// Move in the files.
|
||||||
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
|
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
|
||||||
for i := range cachetestAccounts {
|
for i := range cachetestAccounts {
|
||||||
@@ -105,24 +74,37 @@ func TestWatchNewFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ks should see the accounts.
|
// ks should see the accounts.
|
||||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
var list []accounts.Account
|
||||||
t.Error(err)
|
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) {
|
func TestWatchNoDir(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create ks but not the directory that it watches.
|
// Create ks but not the directory that it watches.
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
list := ks.Accounts()
|
list := ks.Accounts()
|
||||||
if len(list) > 0 {
|
if len(list) > 0 {
|
||||||
t.Error("initial account list not empty:", list)
|
t.Error("initial account list not empty:", list)
|
||||||
}
|
}
|
||||||
// The watcher should start, and then exit.
|
time.Sleep(100 * time.Millisecond)
|
||||||
if !waitWatcherStart(ks) {
|
|
||||||
t.Fatal("keystore watcher didn't start in time")
|
|
||||||
}
|
|
||||||
// Create the directory and copy a key file into it.
|
// Create the directory and copy a key file into it.
|
||||||
os.MkdirAll(dir, 0700)
|
os.MkdirAll(dir, 0700)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
@@ -315,22 +297,40 @@ func TestCacheFind(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
||||||
|
var list []accounts.Account
|
||||||
|
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
|
||||||
|
list = ks.Accounts()
|
||||||
|
if reflect.DeepEqual(list, wantAccounts) {
|
||||||
|
// ks should have also received change notifications
|
||||||
|
select {
|
||||||
|
case <-ks.changes:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("wasn't notified of new accounts")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(d)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
// TestUpdatedKeyfileContents tests that updating the contents of a keystore file
|
// TestUpdatedKeyfileContents tests that updating the contents of a keystore file
|
||||||
// is noticed by the watcher, and the account cache is updated accordingly
|
// is noticed by the watcher, and the account cache is updated accordingly
|
||||||
func TestUpdatedKeyfileContents(t *testing.T) {
|
func TestUpdatedKeyfileContents(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create a temporary keystore to test with
|
// Create a temporary kesytore to test with
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
list := ks.Accounts()
|
list := ks.Accounts()
|
||||||
if len(list) > 0 {
|
if len(list) > 0 {
|
||||||
t.Error("initial account list not empty:", list)
|
t.Error("initial account list not empty:", list)
|
||||||
}
|
}
|
||||||
if !waitWatcherStart(ks) {
|
time.Sleep(100 * time.Millisecond)
|
||||||
t.Fatal("keystore watcher didn't start in time")
|
|
||||||
}
|
|
||||||
// Create the directory and copy a key file into it.
|
// Create the directory and copy a key file into it.
|
||||||
os.MkdirAll(dir, 0700)
|
os.MkdirAll(dir, 0700)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
@@ -348,8 +348,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||||
time.Sleep(time.Second)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
|
||||||
// Now replace file contents
|
// Now replace file contents
|
||||||
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
||||||
@@ -365,7 +366,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||||
time.Sleep(time.Second)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
|
||||||
// Now replace file contents again
|
// Now replace file contents again
|
||||||
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
||||||
@@ -380,11 +381,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after os.WriteFile
|
// needed so that modTime of `file` is different to its current value after ioutil.WriteFile
|
||||||
time.Sleep(time.Second)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
|
||||||
// Now replace file contents with crap
|
// 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)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -397,9 +398,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||||||
|
|
||||||
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
||||||
func forceCopyFile(dst, src string) error {
|
func forceCopyFile(dst, src string) error {
|
||||||
data, err := os.ReadFile(src)
|
data, err := ioutil.ReadFile(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(dst, data, 0644)
|
return ioutil.WriteFile(dst, data, 0644)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,30 +17,31 @@
|
|||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
mapset "github.com/deckarep/golang-set"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileCache is a cache of files seen during scan of keystore.
|
// fileCache is a cache of files seen during scan of keystore.
|
||||||
type fileCache struct {
|
type fileCache struct {
|
||||||
all mapset.Set[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
|
lastMod time.Time // Last time instance when a file was modified
|
||||||
mu sync.Mutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// scan performs a new scan on the given directory, compares against the already
|
// scan performs a new scan on the given directory, compares against the already
|
||||||
// cached filenames, and returns file sets: creates, deletes, updates.
|
// cached filenames, and returns file sets: creates, deletes, updates.
|
||||||
func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) {
|
func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
// List all the files from the keystore folder
|
// List all the failes from the keystore folder
|
||||||
files, err := os.ReadDir(keyDir)
|
files, err := ioutil.ReadDir(keyDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -50,8 +51,8 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
|
|||||||
defer fc.mu.Unlock()
|
defer fc.mu.Unlock()
|
||||||
|
|
||||||
// Iterate all the files and gather their metadata
|
// Iterate all the files and gather their metadata
|
||||||
all := mapset.NewThreadUnsafeSet[string]()
|
all := mapset.NewThreadUnsafeSet()
|
||||||
mods := mapset.NewThreadUnsafeSet[string]()
|
mods := mapset.NewThreadUnsafeSet()
|
||||||
|
|
||||||
var newLastMod time.Time
|
var newLastMod time.Time
|
||||||
for _, fi := range files {
|
for _, fi := range files {
|
||||||
@@ -61,14 +62,10 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
|
|||||||
log.Trace("Ignoring file on account scan", "path", path)
|
log.Trace("Ignoring file on account scan", "path", path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Gather the set of all and freshly modified files
|
// Gather the set of all and fresly modified files
|
||||||
all.Add(path)
|
all.Add(path)
|
||||||
|
|
||||||
info, err := fi.Info()
|
modified := fi.ModTime()
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
modified := info.ModTime()
|
|
||||||
if modified.After(fc.lastMod) {
|
if modified.After(fc.lastMod) {
|
||||||
mods.Add(path)
|
mods.Add(path)
|
||||||
}
|
}
|
||||||
@@ -92,13 +89,13 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
|
// 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.
|
// Skip editor backups and UNIX-style hidden files.
|
||||||
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Skip misc special files, directories (yes, symlinks too).
|
// Skip misc special files, directories (yes, symlinks too).
|
||||||
if fi.IsDir() || !fi.Type().IsRegular() {
|
if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -31,7 +32,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/google/uuid"
|
"github.com/pborman/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -109,10 +110,7 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u := new(uuid.UUID)
|
u := new(uuid.UUID)
|
||||||
*u, err = uuid.Parse(keyJSON.Id)
|
*u = uuid.Parse(keyJSON.Id)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k.Id = *u
|
k.Id = *u
|
||||||
addr, err := hex.DecodeString(keyJSON.Address)
|
addr, err := hex.DecodeString(keyJSON.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,10 +128,7 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
||||||
id, err := uuid.NewRandom()
|
id := uuid.NewRandom()
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Could not create random uuid: %v", err))
|
|
||||||
}
|
|
||||||
key := &Key{
|
key := &Key{
|
||||||
Id: id,
|
Id: id,
|
||||||
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||||
@@ -196,7 +191,7 @@ func writeTemporaryKeyFile(file string, content []byte) (string, error) {
|
|||||||
}
|
}
|
||||||
// Atomic write: create a temporary hidden file first
|
// Atomic write: create a temporary hidden file first
|
||||||
// then move it into place. TempFile assigns mode 0600.
|
// then move it into place. TempFile assigns mode 0600.
|
||||||
f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -42,11 +43,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrLocked = accounts.NewAuthNeededError("password or unlock")
|
ErrLocked = accounts.NewAuthNeededError("password or unlock")
|
||||||
ErrNoMatch = errors.New("no key for given address or file")
|
ErrNoMatch = errors.New("no key for given address or file")
|
||||||
ErrDecrypt = errors.New("could not decrypt key with given password")
|
ErrDecrypt = errors.New("could not decrypt key with given passphrase")
|
||||||
|
|
||||||
// ErrAccountAlreadyExists is returned if an account attempted to import is
|
|
||||||
// already present in the keystore.
|
|
||||||
ErrAccountAlreadyExists = errors.New("account already exists")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyStoreType is the reflect type of a keystore backend.
|
// KeyStoreType is the reflect type of a keystore backend.
|
||||||
@@ -71,7 +68,6 @@ type KeyStore struct {
|
|||||||
updating bool // Whether the event notification loop is running
|
updating bool // Whether the event notification loop is running
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type unlocked struct {
|
type unlocked struct {
|
||||||
@@ -141,10 +137,8 @@ func (ks *KeyStore) refreshWallets() {
|
|||||||
accs := ks.cache.accounts()
|
accs := ks.cache.accounts()
|
||||||
|
|
||||||
// Transform the current list of wallets into the new one
|
// Transform the current list of wallets into the new one
|
||||||
var (
|
wallets := make([]accounts.Wallet, 0, len(accs))
|
||||||
wallets = make([]accounts.Wallet, 0, len(accs))
|
events := []accounts.WalletEvent{}
|
||||||
events []accounts.WalletEvent
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, account := range accs {
|
for _, account := range accs {
|
||||||
// Drop wallets while they were in front of the next account
|
// Drop wallets while they were in front of the next account
|
||||||
@@ -283,9 +277,11 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
|
|||||||
if !found {
|
if !found {
|
||||||
return nil, ErrLocked
|
return nil, ErrLocked
|
||||||
}
|
}
|
||||||
// Depending on the presence of the chain ID, sign with 2718 or homestead
|
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
if chainID != nil {
|
||||||
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
|
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
|
||||||
|
}
|
||||||
|
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignHashWithPassphrase signs hash if the private key matching the given address
|
// SignHashWithPassphrase signs hash if the private key matching the given address
|
||||||
@@ -308,9 +304,12 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer zeroKey(key.PrivateKey)
|
defer zeroKey(key.PrivateKey)
|
||||||
// Depending on the presence of the chain ID, sign with or without replay protection.
|
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||||
return types.SignTx(tx, signer, key.PrivateKey)
|
if chainID != nil {
|
||||||
|
return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
|
||||||
|
}
|
||||||
|
return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock unlocks the given account indefinitely.
|
// Unlock unlocks the given account indefinitely.
|
||||||
@@ -442,27 +441,14 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
ks.importMu.Lock()
|
|
||||||
defer ks.importMu.Unlock()
|
|
||||||
|
|
||||||
if ks.cache.hasAddress(key.Address) {
|
|
||||||
return accounts.Account{
|
|
||||||
Address: key.Address,
|
|
||||||
}, ErrAccountAlreadyExists
|
|
||||||
}
|
|
||||||
return ks.importKey(key, newPassphrase)
|
return ks.importKey(key, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
||||||
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
||||||
ks.importMu.Lock()
|
|
||||||
defer ks.importMu.Unlock()
|
|
||||||
|
|
||||||
key := newKeyFromECDSA(priv)
|
key := newKeyFromECDSA(priv)
|
||||||
if ks.cache.hasAddress(key.Address) {
|
if ks.cache.hasAddress(key.Address) {
|
||||||
return accounts.Account{
|
return accounts.Account{}, fmt.Errorf("account already exists")
|
||||||
Address: key.Address,
|
|
||||||
}, ErrAccountAlreadyExists
|
|
||||||
}
|
}
|
||||||
return ks.importKey(key, passphrase)
|
return ks.importKey(key, passphrase)
|
||||||
}
|
}
|
||||||
@@ -498,14 +484,6 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isUpdating returns whether the event notification loop is running.
|
|
||||||
// This method is mainly meant for tests.
|
|
||||||
func (ks *KeyStore) isUpdating() bool {
|
|
||||||
ks.mu.RLock()
|
|
||||||
defer ks.mu.RUnlock()
|
|
||||||
return ks.updating
|
|
||||||
}
|
|
||||||
|
|
||||||
// zeroKey zeroes a private key in memory.
|
// zeroKey zeroes a private key in memory.
|
||||||
func zeroKey(k *ecdsa.PrivateKey) {
|
func zeroKey(k *ecdsa.PrivateKey) {
|
||||||
b := k.D.Bits()
|
b := k.D.Bits()
|
||||||
|
|||||||
@@ -17,19 +17,17 @@
|
|||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,6 +35,7 @@ var testSigData = make([]byte, 32)
|
|||||||
|
|
||||||
func TestKeyStore(t *testing.T) {
|
func TestKeyStore(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
a, err := ks.NewAccount("foo")
|
a, err := ks.NewAccount("foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,7 +69,8 @@ func TestKeyStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
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
|
pass := "" // not used but required by API
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
@@ -86,7 +86,8 @@ func TestSign(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSignWithPassphrase(t *testing.T) {
|
func TestSignWithPassphrase(t *testing.T) {
|
||||||
_, ks := tmpKeyStore(t, true)
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
pass := "passwd"
|
pass := "passwd"
|
||||||
acc, err := ks.NewAccount(pass)
|
acc, err := ks.NewAccount(pass)
|
||||||
@@ -113,8 +114,8 @@ func TestSignWithPassphrase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimedUnlock(t *testing.T) {
|
func TestTimedUnlock(t *testing.T) {
|
||||||
t.Parallel()
|
dir, ks := tmpKeyStore(t, true)
|
||||||
_, ks := tmpKeyStore(t, true)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
@@ -148,8 +149,8 @@ func TestTimedUnlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOverrideUnlock(t *testing.T) {
|
func TestOverrideUnlock(t *testing.T) {
|
||||||
t.Parallel()
|
dir, ks := tmpKeyStore(t, false)
|
||||||
_, ks := tmpKeyStore(t, false)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
@@ -189,8 +190,8 @@ func TestOverrideUnlock(t *testing.T) {
|
|||||||
|
|
||||||
// This test should fail under -race if signing races the expiration goroutine.
|
// This test should fail under -race if signing races the expiration goroutine.
|
||||||
func TestSignRace(t *testing.T) {
|
func TestSignRace(t *testing.T) {
|
||||||
t.Parallel()
|
dir, ks := tmpKeyStore(t, false)
|
||||||
_, ks := tmpKeyStore(t, false)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// Create a test account.
|
// Create a test account.
|
||||||
a1, err := ks.NewAccount("")
|
a1, err := ks.NewAccount("")
|
||||||
@@ -214,33 +215,20 @@ func TestSignRace(t *testing.T) {
|
|||||||
t.Errorf("Account did not lock within the timeout")
|
t.Errorf("Account did not lock within the timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitForKsUpdating waits until the updating-status of the ks reaches the
|
|
||||||
// desired wantStatus.
|
|
||||||
// It waits for a maximum time of maxTime, and returns false if it does not
|
|
||||||
// finish in time
|
|
||||||
func waitForKsUpdating(t *testing.T, ks *KeyStore, wantStatus bool, maxTime time.Duration) bool {
|
|
||||||
t.Helper()
|
|
||||||
// Wait max 250 ms, then return false
|
|
||||||
for t0 := time.Now(); time.Since(t0) < maxTime; {
|
|
||||||
if ks.isUpdating() == wantStatus {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
time.Sleep(25 * time.Millisecond)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that the wallet notifier loop starts and stops correctly based on the
|
// Tests that the wallet notifier loop starts and stops correctly based on the
|
||||||
// addition and removal of wallet event subscriptions.
|
// addition and removal of wallet event subscriptions.
|
||||||
func TestWalletNotifierLifecycle(t *testing.T) {
|
func TestWalletNotifierLifecycle(t *testing.T) {
|
||||||
t.Parallel()
|
// Create a temporary kesytore to test with
|
||||||
// Create a temporary keystore to test with
|
dir, ks := tmpKeyStore(t, false)
|
||||||
_, ks := tmpKeyStore(t, false)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// Ensure that the notification updater is not running yet
|
// Ensure that the notification updater is not running yet
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
ks.mu.RLock()
|
||||||
|
updating := ks.updating
|
||||||
|
ks.mu.RUnlock()
|
||||||
|
|
||||||
if ks.isUpdating() {
|
if updating {
|
||||||
t.Errorf("wallet notifier running without subscribers")
|
t.Errorf("wallet notifier running without subscribers")
|
||||||
}
|
}
|
||||||
// Subscribe to the wallet feed and ensure the updater boots up
|
// Subscribe to the wallet feed and ensure the updater boots up
|
||||||
@@ -250,26 +238,38 @@ func TestWalletNotifierLifecycle(t *testing.T) {
|
|||||||
for i := 0; i < len(subs); i++ {
|
for i := 0; i < len(subs); i++ {
|
||||||
// Create a new subscription
|
// Create a new subscription
|
||||||
subs[i] = ks.Subscribe(updates)
|
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)
|
t.Errorf("sub %d: wallet notifier not running after subscription", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Close all but one sub
|
// Unsubscribe and ensure the updater terminates eventually
|
||||||
for i := 0; i < len(subs)-1; i++ {
|
for i := 0; i < len(subs); i++ {
|
||||||
// Close an existing subscription
|
// Close an existing subscription
|
||||||
subs[i].Unsubscribe()
|
subs[i].Unsubscribe()
|
||||||
}
|
|
||||||
// Check that it is still running
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
|
|
||||||
if !ks.isUpdating() {
|
// Ensure the notifier shuts down at and only at the last close
|
||||||
t.Fatal("event notifier stopped prematurely")
|
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
|
||||||
|
}
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Unsubscribe the last one and ensure the updater terminates eventually.
|
|
||||||
subs[len(subs)-1].Unsubscribe()
|
|
||||||
if !waitForKsUpdating(t, ks, false, 4*time.Second) {
|
|
||||||
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type walletEvent struct {
|
type walletEvent struct {
|
||||||
@@ -280,7 +280,8 @@ type walletEvent struct {
|
|||||||
// Tests that wallet notifications and correctly fired when accounts are added
|
// Tests that wallet notifications and correctly fired when accounts are added
|
||||||
// or deleted from the keystore.
|
// or deleted from the keystore.
|
||||||
func TestWalletNotifications(t *testing.T) {
|
func TestWalletNotifications(t *testing.T) {
|
||||||
_, ks := tmpKeyStore(t, false)
|
dir, ks := tmpKeyStore(t, false)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// Subscribe to the wallet feed and collect events.
|
// Subscribe to the wallet feed and collect events.
|
||||||
var (
|
var (
|
||||||
@@ -332,88 +333,11 @@ func TestWalletNotifications(t *testing.T) {
|
|||||||
|
|
||||||
// Shut down the event collector and check events.
|
// Shut down the event collector and check events.
|
||||||
sub.Unsubscribe()
|
sub.Unsubscribe()
|
||||||
for ev := range updates {
|
<-updates
|
||||||
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
|
|
||||||
}
|
|
||||||
checkAccounts(t, live, ks.Wallets())
|
checkAccounts(t, live, ks.Wallets())
|
||||||
checkEvents(t, wantEvents, events)
|
checkEvents(t, wantEvents, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestImportExport tests the import functionality of a keystore.
|
|
||||||
func TestImportECDSA(t *testing.T) {
|
|
||||||
_, ks := tmpKeyStore(t, true)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
|
||||||
func TestImportExport(t *testing.T) {
|
|
||||||
_, ks := tmpKeyStore(t, true)
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkAccounts checks that all known live accounts are present in the wallet list.
|
// checkAccounts checks that all known live accounts are present in the wallet list.
|
||||||
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
||||||
if len(live) != len(wallets) {
|
if len(live) != len(wallets) {
|
||||||
@@ -451,10 +375,13 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
||||||
d := t.TempDir()
|
d, err := ioutil.TempDir("", "eth-keystore-test")
|
||||||
newKs := NewPlaintextKeyStore
|
if err != nil {
|
||||||
if encrypted {
|
t.Fatal(err)
|
||||||
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
|
||||||
}
|
}
|
||||||
return d, newKs(d)
|
new := NewPlaintextKeyStore
|
||||||
|
if encrypted {
|
||||||
|
new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
||||||
|
}
|
||||||
|
return d, new(d)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/google/uuid"
|
"github.com/pborman/uuid"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"golang.org/x/crypto/scrypt"
|
"golang.org/x/crypto/scrypt"
|
||||||
)
|
)
|
||||||
@@ -81,7 +81,7 @@ type keyStorePassphrase struct {
|
|||||||
|
|
||||||
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
||||||
// Load the key from the keystore and decrypt its contents
|
// Load the key from the keystore and decrypt its contents
|
||||||
keyjson, err := os.ReadFile(filename)
|
keyjson, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,9 +97,9 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
|
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
|
||||||
func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
|
func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
|
||||||
_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
|
_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
|
||||||
return a, err
|
return a.Address, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
|
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
|
||||||
@@ -122,7 +122,6 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
|||||||
"Please file a ticket at:\n\n" +
|
"Please file a ticket at:\n\n" +
|
||||||
"https://github.com/ethereum/go-ethereum/issues." +
|
"https://github.com/ethereum/go-ethereum/issues." +
|
||||||
"The error was : %s"
|
"The error was : %s"
|
||||||
//lint:ignore ST1005 This is a message for the user
|
|
||||||
return fmt.Errorf(msg, tmpName, err)
|
return fmt.Errorf(msg, tmpName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +137,7 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
|
|||||||
|
|
||||||
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
||||||
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
||||||
|
|
||||||
salt := make([]byte, 32)
|
salt := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||||
panic("reading from crypto/rand failed: " + err.Error())
|
panic("reading from crypto/rand failed: " + err.Error())
|
||||||
@@ -226,12 +226,9 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
key := crypto.ToECDSAUnsafe(keyBytes)
|
||||||
id, err := uuid.FromBytes(keyId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Key{
|
return &Key{
|
||||||
Id: id,
|
Id: uuid.UUID(keyId),
|
||||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||||
PrivateKey: key,
|
PrivateKey: key,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -239,7 +236,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||||||
|
|
||||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||||
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
||||||
}
|
}
|
||||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -275,13 +272,9 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
|||||||
|
|
||||||
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
if keyProtected.Version != version {
|
if keyProtected.Version != version {
|
||||||
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||||
}
|
}
|
||||||
keyUUID, err := uuid.Parse(keyProtected.Id)
|
keyId = uuid.Parse(keyProtected.Id)
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
keyId = keyUUID[:]
|
|
||||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -290,11 +283,7 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
|
func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
keyUUID, err := uuid.Parse(keyProtected.Id)
|
keyId = uuid.Parse(keyProtected.Id)
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
keyId = keyUUID[:]
|
|
||||||
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -340,17 +329,18 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
|||||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||||
|
|
||||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||||
if prf != "hmac-sha256" {
|
if prf != "hmac-sha256" {
|
||||||
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
|
||||||
}
|
}
|
||||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
|
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can we do without this when unmarshalling dynamic JSON?
|
// TODO: can we do without this when unmarshalling dynamic JSON?
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@@ -30,7 +30,7 @@ const (
|
|||||||
|
|
||||||
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
||||||
func TestKeyEncryptDecrypt(t *testing.T) {
|
func TestKeyEncryptDecrypt(t *testing.T) {
|
||||||
keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
|
keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
|
|||||||
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
|
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
|
||||||
}
|
}
|
||||||
// Recrypt with a new password and start over
|
// Recrypt with a new password and start over
|
||||||
password += "new data appended" // nolint: gosec
|
password += "new data appended"
|
||||||
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
||||||
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -30,7 +32,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
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 {
|
if encrypted {
|
||||||
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
||||||
} else {
|
} else {
|
||||||
@@ -40,7 +45,8 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePlain(t *testing.T) {
|
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
|
pass := "" // not used but required by API
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
@@ -60,7 +66,8 @@ func TestKeyStorePlain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePassphrase(t *testing.T) {
|
func TestKeyStorePassphrase(t *testing.T) {
|
||||||
_, ks := tmpKeyStoreIface(t, true)
|
dir, ks := tmpKeyStoreIface(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
@@ -80,7 +87,8 @@ func TestKeyStorePassphrase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
_, ks := tmpKeyStoreIface(t, true)
|
dir, ks := tmpKeyStoreIface(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
@@ -88,12 +96,13 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
|
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
|
||||||
t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
|
t.Fatalf("wrong error for invalid passphrase\ngot %q\nwant %q", err, ErrDecrypt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportPreSaleKey(t *testing.T) {
|
func TestImportPreSaleKey(t *testing.T) {
|
||||||
dir, ks := tmpKeyStoreIface(t, true)
|
dir, ks := tmpKeyStoreIface(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// file content of a presale key file generated with:
|
// file content of a presale key file generated with:
|
||||||
// python pyethsaletool.py genwallet
|
// python pyethsaletool.py genwallet
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/google/uuid"
|
"github.com/pborman/uuid"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,10 +37,7 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, nil, err
|
return accounts.Account{}, nil, err
|
||||||
}
|
}
|
||||||
key.Id, err = uuid.NewRandom()
|
key.Id = uuid.NewRandom()
|
||||||
if err != nil {
|
|
||||||
return accounts.Account{}, nil, err
|
|
||||||
}
|
|
||||||
a := accounts.Account{
|
a := accounts.Account{
|
||||||
Address: key.Address,
|
Address: key.Address,
|
||||||
URL: accounts.URL{
|
URL: accounts.URL{
|
||||||
@@ -89,7 +86,7 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
|||||||
ecKey := crypto.ToECDSAUnsafe(ethPriv)
|
ecKey := crypto.ToECDSAUnsafe(ethPriv)
|
||||||
|
|
||||||
key = &Key{
|
key = &Key{
|
||||||
Id: uuid.UUID{},
|
Id: nil,
|
||||||
Address: crypto.PubkeyToAddress(ecKey.PublicKey),
|
Address: crypto.PubkeyToAddress(ecKey.PublicKey),
|
||||||
PrivateKey: ecKey,
|
PrivateKey: ecKey,
|
||||||
}
|
}
|
||||||
|
|||||||
2
accounts/keystore/testdata/keystore/README
vendored
2
accounts/keystore/testdata/keystore/README
vendored
@@ -1,5 +1,5 @@
|
|||||||
This directory contains accounts for testing.
|
This directory contains accounts for testing.
|
||||||
The password that unlocks them is "foobar".
|
The passphrase that unlocks them is "foobar".
|
||||||
|
|
||||||
The "good" key files which are supposed to be loadable are:
|
The "good" key files which are supposed to be loadable are:
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,9 @@ package keystore
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// keystoreWallet implements the accounts.Wallet interface for the original
|
// keystoreWallet implements the accounts.Wallet interface for the original
|
||||||
@@ -58,7 +57,7 @@ func (w *keystoreWallet) Open(passphrase string) error { return nil }
|
|||||||
func (w *keystoreWallet) Close() error { return nil }
|
func (w *keystoreWallet) Close() error { return nil }
|
||||||
|
|
||||||
// Accounts implements accounts.Wallet, returning an account list consisting of
|
// Accounts implements accounts.Wallet, returning an account list consisting of
|
||||||
// a single account that the plain keystore wallet contains.
|
// a single account that the plain kestore wallet contains.
|
||||||
func (w *keystoreWallet) Accounts() []accounts.Account {
|
func (w *keystoreWallet) Accounts() []accounts.Account {
|
||||||
return []accounts.Account{w.account}
|
return []accounts.Account{w.account}
|
||||||
}
|
}
|
||||||
@@ -77,14 +76,13 @@ func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (account
|
|||||||
|
|
||||||
// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
|
// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
|
||||||
// there is no notion of hierarchical account derivation for plain keystore accounts.
|
// there is no notion of hierarchical account derivation for plain keystore accounts.
|
||||||
func (w *keystoreWallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {}
|
||||||
}
|
|
||||||
|
|
||||||
// signHash attempts to sign the given hash with
|
// SignHash implements accounts.Wallet, attempting to sign the given hash with
|
||||||
// the given account. If the wallet does not wrap this particular account, an
|
// the given account. If the wallet does not wrap this particular account, an
|
||||||
// error is returned to avoid account leakage (even though in theory we may be
|
// error is returned to avoid account leakage (even though in theory we may be
|
||||||
// able to sign via our shared keystore backend).
|
// able to sign via our shared keystore backend).
|
||||||
func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
|
func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
|
||||||
// Make sure the requested account is contained within
|
// Make sure the requested account is contained within
|
||||||
if !w.Contains(account) {
|
if !w.Contains(account) {
|
||||||
return nil, accounts.ErrUnknownAccount
|
return nil, accounts.ErrUnknownAccount
|
||||||
@@ -93,38 +91,6 @@ func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte
|
|||||||
return w.keystore.SignHash(account, hash)
|
return w.keystore.SignHash(account, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed.
|
|
||||||
func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
|
||||||
return w.signHash(account, crypto.Keccak256(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed.
|
|
||||||
func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
|
||||||
// Make sure the requested account is contained within
|
|
||||||
if !w.Contains(account) {
|
|
||||||
return nil, accounts.ErrUnknownAccount
|
|
||||||
}
|
|
||||||
// Account seems valid, request the keystore to sign
|
|
||||||
return w.keystore.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignText implements accounts.Wallet, attempting to sign the hash of
|
|
||||||
// the given text with the given account.
|
|
||||||
func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
|
||||||
return w.signHash(account, accounts.TextHash(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the
|
|
||||||
// hash of the given text with the given account using passphrase as extra authentication.
|
|
||||||
func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
|
||||||
// Make sure the requested account is contained within
|
|
||||||
if !w.Contains(account) {
|
|
||||||
return nil, accounts.ErrUnknownAccount
|
|
||||||
}
|
|
||||||
// Account seems valid, request the keystore to sign
|
|
||||||
return w.keystore.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignTx implements accounts.Wallet, attempting to sign the given transaction
|
// SignTx implements accounts.Wallet, attempting to sign the given transaction
|
||||||
// with the given account. If the wallet does not wrap this particular account,
|
// with the given account. If the wallet does not wrap this particular account,
|
||||||
// an error is returned to avoid account leakage (even though in theory we may
|
// an error is returned to avoid account leakage (even though in theory we may
|
||||||
@@ -138,6 +104,17 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
|
|||||||
return w.keystore.SignTx(account, tx, chainID)
|
return w.keystore.SignTx(account, tx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
|
||||||
|
// given hash with the given account using passphrase as extra authentication.
|
||||||
|
func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
|
||||||
|
// Make sure the requested account is contained within
|
||||||
|
if !w.Contains(account) {
|
||||||
|
return nil, accounts.ErrUnknownAccount
|
||||||
|
}
|
||||||
|
// Account seems valid, request the keystore to sign
|
||||||
|
return w.keystore.SignHashWithPassphrase(account, passphrase, hash)
|
||||||
|
}
|
||||||
|
|
||||||
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
|
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
|
||||||
// transaction with the given account using passphrase as extra authentication.
|
// transaction with the given account using passphrase as extra authentication.
|
||||||
func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
|
|||||||
@@ -14,8 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
|
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
|
||||||
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
@@ -23,27 +22,25 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/rjeczalik/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
ac *accountCache
|
ac *accountCache
|
||||||
running bool // set to true when runloop begins
|
starting bool
|
||||||
runEnded bool // set to true when runloop ends
|
running bool
|
||||||
starting bool // set to true prior to runloop starting
|
ev chan notify.EventInfo
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWatcher(ac *accountCache) *watcher {
|
func newWatcher(ac *accountCache) *watcher {
|
||||||
return &watcher{
|
return &watcher{
|
||||||
ac: ac,
|
ac: ac,
|
||||||
|
ev: make(chan notify.EventInfo, 10),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enabled returns false on systems not supported.
|
|
||||||
func (*watcher) enabled() bool { return true }
|
|
||||||
|
|
||||||
// starts the watcher loop in the background.
|
// starts the watcher loop in the background.
|
||||||
// Start a watcher in the background if that's not already in progress.
|
// Start a watcher in the background if that's not already in progress.
|
||||||
// The caller must hold w.ac.mu.
|
// The caller must hold w.ac.mu.
|
||||||
@@ -64,24 +61,16 @@ func (w *watcher) loop() {
|
|||||||
w.ac.mu.Lock()
|
w.ac.mu.Lock()
|
||||||
w.running = false
|
w.running = false
|
||||||
w.starting = false
|
w.starting = false
|
||||||
w.runEnded = true
|
|
||||||
w.ac.mu.Unlock()
|
w.ac.mu.Unlock()
|
||||||
}()
|
}()
|
||||||
logger := log.New("path", w.ac.keydir)
|
logger := log.New("path", w.ac.keydir)
|
||||||
|
|
||||||
// Create new watcher.
|
if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
logger.Trace("Failed to watch keystore folder", "err", err)
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to start filesystem watcher", "err", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer watcher.Close()
|
defer notify.Stop(w.ev)
|
||||||
if err := watcher.Add(w.ac.keydir); err != nil {
|
logger.Trace("Started watching keystore folder")
|
||||||
logger.Warn("Failed to watch keystore folder", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Trace("Started watching keystore folder", "folder", w.ac.keydir)
|
|
||||||
defer logger.Trace("Stopped watching keystore folder")
|
defer logger.Trace("Stopped watching keystore folder")
|
||||||
|
|
||||||
w.ac.mu.Lock()
|
w.ac.mu.Lock()
|
||||||
@@ -105,24 +94,12 @@ func (w *watcher) loop() {
|
|||||||
select {
|
select {
|
||||||
case <-w.quit:
|
case <-w.quit:
|
||||||
return
|
return
|
||||||
case _, ok := <-watcher.Events:
|
case <-w.ev:
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Trigger the scan (with delay), if not already triggered
|
// Trigger the scan (with delay), if not already triggered
|
||||||
if !rescanTriggered {
|
if !rescanTriggered {
|
||||||
debounce.Reset(debounceDuration)
|
debounce.Reset(debounceDuration)
|
||||||
rescanTriggered = true
|
rescanTriggered = true
|
||||||
}
|
}
|
||||||
// The fsnotify library does provide more granular event-info, it
|
|
||||||
// would be possible to refresh individual affected files instead
|
|
||||||
// of scheduling a full rescan. For most cases though, the
|
|
||||||
// full rescan is quick and obviously simplest.
|
|
||||||
case err, ok := <-watcher.Errors:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("Filsystem watcher error", "err", err)
|
|
||||||
case <-debounce.C:
|
case <-debounce.C:
|
||||||
w.ac.scanAccounts()
|
w.ac.scanAccounts()
|
||||||
rescanTriggered = false
|
rescanTriggered = false
|
||||||
|
|||||||
@@ -14,22 +14,15 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
|
// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||||
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
|
||||||
|
|
||||||
// This is the fallback implementation of directory watching.
|
// This is the fallback implementation of directory watching.
|
||||||
// It is used on unsupported platforms.
|
// It is used on unsupported platforms.
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct{ running bool }
|
||||||
running bool
|
|
||||||
runEnded bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWatcher(*accountCache) *watcher { return new(watcher) }
|
func newWatcher(*accountCache) *watcher { return new(watcher) }
|
||||||
func (*watcher) start() {}
|
func (*watcher) start() {}
|
||||||
func (*watcher) close() {}
|
func (*watcher) close() {}
|
||||||
|
|
||||||
// enabled returns false on systems not supported.
|
|
||||||
func (*watcher) enabled() bool { return false }
|
|
||||||
|
|||||||
@@ -21,56 +21,33 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
// managerSubBufferSize determines how many incoming wallet events
|
|
||||||
// the manager will buffer in its channel.
|
|
||||||
const managerSubBufferSize = 50
|
|
||||||
|
|
||||||
// Config contains the settings of the global account manager.
|
|
||||||
//
|
|
||||||
// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
|
|
||||||
// is removed in favor of Clef.
|
|
||||||
type Config struct {
|
|
||||||
InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBackendEvent lets the manager know it should
|
|
||||||
// track the given backend for wallet updates.
|
|
||||||
type newBackendEvent struct {
|
|
||||||
backend Backend
|
|
||||||
processed chan struct{} // Informs event emitter that backend has been integrated
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manager is an overarching account manager that can communicate with various
|
// Manager is an overarching account manager that can communicate with various
|
||||||
// backends for signing transactions.
|
// backends for signing transactions.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
config *Config // Global account manager configurations
|
|
||||||
backends map[reflect.Type][]Backend // Index of backends currently registered
|
backends map[reflect.Type][]Backend // Index of backends currently registered
|
||||||
updaters []event.Subscription // Wallet update subscriptions for all backends
|
updaters []event.Subscription // Wallet update subscriptions for all backends
|
||||||
updates chan WalletEvent // Subscription sink for backend wallet changes
|
updates chan WalletEvent // Subscription sink for backend wallet changes
|
||||||
newBackends chan newBackendEvent // Incoming backends to be tracked by the manager
|
|
||||||
wallets []Wallet // Cache of all wallets from all registered backends
|
wallets []Wallet // Cache of all wallets from all registered backends
|
||||||
|
|
||||||
feed event.Feed // Wallet feed notifying of arrivals/departures
|
feed event.Feed // Wallet feed notifying of arrivals/departures
|
||||||
|
|
||||||
quit chan chan error
|
quit chan chan error
|
||||||
term chan struct{} // Channel is closed upon termination of the update loop
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a generic account manager to sign transaction via various
|
// NewManager creates a generic account manager to sign transaction via various
|
||||||
// supported backends.
|
// supported backends.
|
||||||
func NewManager(config *Config, backends ...Backend) *Manager {
|
func NewManager(backends ...Backend) *Manager {
|
||||||
// Retrieve the initial list of wallets from the backends and sort by URL
|
// Retrieve the initial list of wallets from the backends and sort by URL
|
||||||
var wallets []Wallet
|
var wallets []Wallet
|
||||||
for _, backend := range backends {
|
for _, backend := range backends {
|
||||||
wallets = merge(wallets, backend.Wallets()...)
|
wallets = merge(wallets, backend.Wallets()...)
|
||||||
}
|
}
|
||||||
// Subscribe to wallet notifications from all backends
|
// Subscribe to wallet notifications from all backends
|
||||||
updates := make(chan WalletEvent, managerSubBufferSize)
|
updates := make(chan WalletEvent, 4*len(backends))
|
||||||
|
|
||||||
subs := make([]event.Subscription, len(backends))
|
subs := make([]event.Subscription, len(backends))
|
||||||
for i, backend := range backends {
|
for i, backend := range backends {
|
||||||
@@ -78,14 +55,11 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
|||||||
}
|
}
|
||||||
// Assemble the account manager and return
|
// Assemble the account manager and return
|
||||||
am := &Manager{
|
am := &Manager{
|
||||||
config: config,
|
|
||||||
backends: make(map[reflect.Type][]Backend),
|
backends: make(map[reflect.Type][]Backend),
|
||||||
updaters: subs,
|
updaters: subs,
|
||||||
updates: updates,
|
updates: updates,
|
||||||
newBackends: make(chan newBackendEvent),
|
|
||||||
wallets: wallets,
|
wallets: wallets,
|
||||||
quit: make(chan chan error),
|
quit: make(chan chan error),
|
||||||
term: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
for _, backend := range backends {
|
for _, backend := range backends {
|
||||||
kind := reflect.TypeOf(backend)
|
kind := reflect.TypeOf(backend)
|
||||||
@@ -103,19 +77,6 @@ func (am *Manager) Close() error {
|
|||||||
return <-errc
|
return <-errc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config returns the configuration of account manager.
|
|
||||||
func (am *Manager) Config() *Config {
|
|
||||||
return am.config
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBackend starts the tracking of an additional backend for wallet updates.
|
|
||||||
// cmd/geth assumes once this func returns the backends have been already integrated.
|
|
||||||
func (am *Manager) AddBackend(backend Backend) {
|
|
||||||
done := make(chan struct{})
|
|
||||||
am.newBackends <- newBackendEvent{backend, done}
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
// update is the wallet event loop listening for notifications from the backends
|
// update is the wallet event loop listening for notifications from the backends
|
||||||
// and updating the cache of wallets.
|
// and updating the cache of wallets.
|
||||||
func (am *Manager) update() {
|
func (am *Manager) update() {
|
||||||
@@ -145,22 +106,10 @@ func (am *Manager) update() {
|
|||||||
|
|
||||||
// Notify any listeners of the event
|
// Notify any listeners of the event
|
||||||
am.feed.Send(event)
|
am.feed.Send(event)
|
||||||
case event := <-am.newBackends:
|
|
||||||
am.lock.Lock()
|
|
||||||
// Update caches
|
|
||||||
backend := event.backend
|
|
||||||
am.wallets = merge(am.wallets, backend.Wallets()...)
|
|
||||||
am.updaters = append(am.updaters, backend.Subscribe(am.updates))
|
|
||||||
kind := reflect.TypeOf(backend)
|
|
||||||
am.backends[kind] = append(am.backends[kind], backend)
|
|
||||||
am.lock.Unlock()
|
|
||||||
close(event.processed)
|
|
||||||
case errc := <-am.quit:
|
case errc := <-am.quit:
|
||||||
// Manager terminating, return
|
// Manager terminating, return
|
||||||
errc <- nil
|
errc <- nil
|
||||||
// Signals event emitters the loop is not receiving values
|
|
||||||
// to prevent them from getting stuck.
|
|
||||||
close(am.term)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,9 +117,6 @@ func (am *Manager) update() {
|
|||||||
|
|
||||||
// Backends retrieves the backend(s) with the given type from the account manager.
|
// Backends retrieves the backend(s) with the given type from the account manager.
|
||||||
func (am *Manager) Backends(kind reflect.Type) []Backend {
|
func (am *Manager) Backends(kind reflect.Type) []Backend {
|
||||||
am.lock.RLock()
|
|
||||||
defer am.lock.RUnlock()
|
|
||||||
|
|
||||||
return am.backends[kind]
|
return am.backends[kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +125,6 @@ func (am *Manager) Wallets() []Wallet {
|
|||||||
am.lock.RLock()
|
am.lock.RLock()
|
||||||
defer am.lock.RUnlock()
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
return am.walletsNoLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// walletsNoLock returns all registered wallets. Callers must hold am.lock.
|
|
||||||
func (am *Manager) walletsNoLock() []Wallet {
|
|
||||||
cpy := make([]Wallet, len(am.wallets))
|
cpy := make([]Wallet, len(am.wallets))
|
||||||
copy(cpy, am.wallets)
|
copy(cpy, am.wallets)
|
||||||
return cpy
|
return cpy
|
||||||
@@ -198,7 +139,7 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, wallet := range am.walletsNoLock() {
|
for _, wallet := range am.Wallets() {
|
||||||
if wallet.URL() == parsed {
|
if wallet.URL() == parsed {
|
||||||
return wallet, nil
|
return wallet, nil
|
||||||
}
|
}
|
||||||
@@ -206,20 +147,6 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
|||||||
return nil, ErrUnknownWallet
|
return nil, ErrUnknownWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accounts returns all account addresses of all wallets within the account manager
|
|
||||||
func (am *Manager) Accounts() []common.Address {
|
|
||||||
am.lock.RLock()
|
|
||||||
defer am.lock.RUnlock()
|
|
||||||
|
|
||||||
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
|
||||||
for _, wallet := range am.wallets {
|
|
||||||
for _, account := range wallet.Accounts() {
|
|
||||||
addresses = append(addresses, account.Address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addresses
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find attempts to locate the wallet corresponding to a specific account. Since
|
// Find attempts to locate the wallet corresponding to a specific account. Since
|
||||||
// accounts can be dynamically added to and removed from wallets, this method has
|
// accounts can be dynamically added to and removed from wallets, this method has
|
||||||
// a linear runtime in the number of wallets.
|
// a linear runtime in the number of wallets.
|
||||||
@@ -257,7 +184,7 @@ func merge(slice []Wallet, wallets ...Wallet) []Wallet {
|
|||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop is the counterpart of merge, which looks up wallets from within the sorted
|
// drop is the couterpart of merge, which looks up wallets from within the sorted
|
||||||
// cache and removes the ones specified.
|
// cache and removes the ones specified.
|
||||||
func drop(slice []Wallet, wallets ...Wallet) []Wallet {
|
func drop(slice []Wallet, wallets ...Wallet) []Wallet {
|
||||||
for _, wallet := range wallets {
|
for _, wallet := range wallets {
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
# Using the smartcard wallet
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
* A USB smartcard reader
|
|
||||||
* A keycard that supports the status app
|
|
||||||
* PCSCD version 4.3 running on your system **Only version 4.3 is currently supported**
|
|
||||||
|
|
||||||
## Preparing the smartcard
|
|
||||||
|
|
||||||
**WARNING: FOILLOWING THESE INSTRUCTIONS WILL DESTROY THE MASTER KEY ON YOUR CARD. ONLY PROCEED IF NO FUNDS ARE ASSOCIATED WITH THESE ACCOUNTS**
|
|
||||||
|
|
||||||
You can use status' [keycard-cli](https://github.com/status-im/keycard-cli) and you should get _at least_ version 2.1.1 of their [smartcard application](https://github.com/status-im/status-keycard/releases/download/2.2.1/keycard_v2.2.1.cap)
|
|
||||||
|
|
||||||
You also need to make sure that the PCSC daemon is running on your system.
|
|
||||||
|
|
||||||
Then, you can install the application to the card by typing:
|
|
||||||
|
|
||||||
```
|
|
||||||
keycard install -a keycard_v2.2.1.cap && keycard init
|
|
||||||
```
|
|
||||||
|
|
||||||
At the end of this process, you will be provided with a PIN, a PUK and a pairing password. Write them down, you'll need them shortly.
|
|
||||||
|
|
||||||
Start `geth` with the `console` command. You will notice the following warning:
|
|
||||||
|
|
||||||
```
|
|
||||||
WARN [04-09|16:58:38.898] Failed to open wallet url=keycard://044def09 err="smartcard: pairing password needed"
|
|
||||||
```
|
|
||||||
|
|
||||||
Write down the URL (`keycard://044def09` in this example). Then ask `geth` to open the wallet:
|
|
||||||
|
|
||||||
```
|
|
||||||
> personal.openWallet("keycard://044def09", "pairing password")
|
|
||||||
```
|
|
||||||
|
|
||||||
The pairing password has been generated during the card initialization process.
|
|
||||||
|
|
||||||
The process needs to be repeated once more with the PIN:
|
|
||||||
|
|
||||||
```
|
|
||||||
> personal.openWallet("keycard://044def09", "PIN number")
|
|
||||||
```
|
|
||||||
|
|
||||||
If everything goes well, you should see your new account when typing `personal` on the console:
|
|
||||||
|
|
||||||
```
|
|
||||||
> personal
|
|
||||||
WARN [04-09|17:02:07.330] Smartcard wallet account derivation failed url=keycard://044def09 err="Unexpected response status Cla=0x80, Ins=0xd1, Sw=0x6985"
|
|
||||||
{
|
|
||||||
listAccounts: [],
|
|
||||||
listWallets: [{
|
|
||||||
status: "Empty, waiting for initialization",
|
|
||||||
url: "keycard://044def09"
|
|
||||||
}],
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
So the communication with the card is working, but there is no key associated with this wallet. Let's create it:
|
|
||||||
|
|
||||||
```
|
|
||||||
> personal.initializeWallet("keycard://044def09")
|
|
||||||
"tilt ... impact"
|
|
||||||
```
|
|
||||||
|
|
||||||
You should get a list of words, this is your seed so write them down. Your wallet should now be initialized:
|
|
||||||
|
|
||||||
```
|
|
||||||
> personal.listWallets
|
|
||||||
[{
|
|
||||||
accounts: [{
|
|
||||||
address: "0x678b7cd55c61917defb23546a41803c5bfefbc7a",
|
|
||||||
url: "keycard://044d/m/44'/60'/0'/0/0"
|
|
||||||
}],
|
|
||||||
status: "Online",
|
|
||||||
url: "keycard://044def09"
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
You're all set!
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. Start `geth` with the `console` command
|
|
||||||
2. Check the card's URL by checking `personal.listWallets`:
|
|
||||||
|
|
||||||
```
|
|
||||||
listWallets: [{
|
|
||||||
status: "Online, can derive public keys",
|
|
||||||
url: "keycard://a4d73015"
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Open the wallet, you will be prompted for your pairing password, then PIN:
|
|
||||||
|
|
||||||
```
|
|
||||||
personal.openWallet("keycard://a4d73015")
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Check that creation was successful by typing e.g. `personal`. Then use it like a regular wallet.
|
|
||||||
|
|
||||||
## Known issues
|
|
||||||
|
|
||||||
* Starting geth with a valid card seems to make firefox crash.
|
|
||||||
* PCSC version 4.4 should work, but is currently untested
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
// Copyright 2018 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 scwallet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// commandAPDU represents an application data unit sent to a smartcard.
|
|
||||||
type commandAPDU struct {
|
|
||||||
Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2
|
|
||||||
Data []byte // Command data
|
|
||||||
Le uint8 // Command data length
|
|
||||||
}
|
|
||||||
|
|
||||||
// serialize serializes a command APDU.
|
|
||||||
func (ca commandAPDU) serialize() ([]byte, error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// responseAPDU represents an application data unit received from a smart card.
|
|
||||||
type responseAPDU struct {
|
|
||||||
Data []byte // response data
|
|
||||||
Sw1, Sw2 uint8 // status words 1 and 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// deserialize deserializes a response APDU.
|
|
||||||
func (ra *responseAPDU) deserialize(data []byte) error {
|
|
||||||
if len(data) < 2 {
|
|
||||||
return fmt.Errorf("can not deserialize data: payload too short (%d < 2)", len(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
ra.Data = make([]byte, len(data)-2)
|
|
||||||
|
|
||||||
buf := bytes.NewReader(data)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
// Copyright 2018 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/>.
|
|
||||||
|
|
||||||
// This package implements support for smartcard-based hardware wallets such as
|
|
||||||
// the one written by Status: https://github.com/status-im/hardware-wallet
|
|
||||||
//
|
|
||||||
// This implementation of smartcard wallets have a different interaction process
|
|
||||||
// to other types of hardware wallet. The process works like this:
|
|
||||||
//
|
|
||||||
// 1. (First use with a given client) Establish a pairing between hardware
|
|
||||||
// wallet and client. This requires a secret value called a 'pairing password'.
|
|
||||||
// You can pair with an unpaired wallet with `personal.openWallet(URI, pairing password)`.
|
|
||||||
// 2. (First use only) Initialize the wallet, which generates a keypair, stores
|
|
||||||
// it on the wallet, and returns it so the user can back it up. You can
|
|
||||||
// initialize a wallet with `personal.initializeWallet(URI)`.
|
|
||||||
// 3. Connect to the wallet using the pairing information established in step 1.
|
|
||||||
// You can connect to a paired wallet with `personal.openWallet(URI, PIN)`.
|
|
||||||
// 4. Interact with the wallet as normal.
|
|
||||||
|
|
||||||
package scwallet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
pcsc "github.com/gballet/go-libpcsclite"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scheme is the URI prefix for smartcard wallets.
|
|
||||||
const Scheme = "keycard"
|
|
||||||
|
|
||||||
// refreshCycle is the maximum time between wallet refreshes (if USB hotplug
|
|
||||||
// notifications don't work).
|
|
||||||
const refreshCycle = time.Second
|
|
||||||
|
|
||||||
// refreshThrottling is the minimum time between wallet refreshes to avoid thrashing.
|
|
||||||
const refreshThrottling = 500 * time.Millisecond
|
|
||||||
|
|
||||||
// smartcardPairing contains information about a smart card we have paired with
|
|
||||||
// or might pair with the hub.
|
|
||||||
type smartcardPairing struct {
|
|
||||||
PublicKey []byte `json:"publicKey"`
|
|
||||||
PairingIndex uint8 `json:"pairingIndex"`
|
|
||||||
PairingKey []byte `json:"pairingKey"`
|
|
||||||
Accounts map[common.Address]accounts.DerivationPath `json:"accounts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hub is a accounts.Backend that can find and handle generic PC/SC hardware wallets.
|
|
||||||
type Hub struct {
|
|
||||||
scheme string // Protocol scheme prefixing account and wallet URLs.
|
|
||||||
|
|
||||||
context *pcsc.Client
|
|
||||||
datadir string
|
|
||||||
pairings map[string]smartcardPairing
|
|
||||||
|
|
||||||
refreshed time.Time // Time instance when the list of wallets was last refreshed
|
|
||||||
wallets map[string]*Wallet // Mapping from reader names to wallet instances
|
|
||||||
updateFeed event.Feed // Event feed to notify wallet additions/removals
|
|
||||||
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
|
||||||
updating bool // Whether the event notification loop is running
|
|
||||||
|
|
||||||
quit chan chan error
|
|
||||||
|
|
||||||
stateLock sync.RWMutex // Protects the internals of the hub from racey access
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pairings []smartcardPairing
|
|
||||||
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pairing := range pairings {
|
|
||||||
hub.pairings[string(pairing.PublicKey)] = pairing
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hub *Hub) writePairings() error {
|
|
||||||
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer pairingFile.Close()
|
|
||||||
|
|
||||||
pairings := make([]smartcardPairing, 0, len(hub.pairings))
|
|
||||||
for _, pairing := range hub.pairings {
|
|
||||||
pairings = append(pairings, pairing)
|
|
||||||
}
|
|
||||||
|
|
||||||
pairingData, err := json.Marshal(pairings)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := pairingFile.Write(pairingData); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
|
||||||
if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
|
|
||||||
return &pairing
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
|
|
||||||
if pairing == nil {
|
|
||||||
delete(hub.pairings, string(wallet.PublicKey))
|
|
||||||
} else {
|
|
||||||
hub.pairings[string(wallet.PublicKey)] = *pairing
|
|
||||||
}
|
|
||||||
return hub.writePairings()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHub creates a new hardware wallet manager for smartcards.
|
|
||||||
func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
|
|
||||||
context, err := pcsc.EstablishContext(daemonPath, pcsc.ScopeSystem)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hub := &Hub{
|
|
||||||
scheme: scheme,
|
|
||||||
context: context,
|
|
||||||
datadir: datadir,
|
|
||||||
wallets: make(map[string]*Wallet),
|
|
||||||
quit: make(chan chan error),
|
|
||||||
}
|
|
||||||
if err := hub.readPairings(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hub.refreshWallets()
|
|
||||||
return hub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wallets implements accounts.Backend, returning all the currently tracked smart
|
|
||||||
// cards that appear to be hardware wallets.
|
|
||||||
func (hub *Hub) Wallets() []accounts.Wallet {
|
|
||||||
// Make sure the list of wallets is up to date
|
|
||||||
hub.refreshWallets()
|
|
||||||
|
|
||||||
hub.stateLock.RLock()
|
|
||||||
defer hub.stateLock.RUnlock()
|
|
||||||
|
|
||||||
cpy := make([]accounts.Wallet, 0, len(hub.wallets))
|
|
||||||
for _, wallet := range hub.wallets {
|
|
||||||
cpy = append(cpy, wallet)
|
|
||||||
}
|
|
||||||
sort.Sort(accounts.WalletsByURL(cpy))
|
|
||||||
return cpy
|
|
||||||
}
|
|
||||||
|
|
||||||
// refreshWallets scans the devices attached to the machine and updates the
|
|
||||||
// list of wallets based on the found devices.
|
|
||||||
func (hub *Hub) refreshWallets() {
|
|
||||||
// Don't scan the USB like crazy it the user fetches wallets in a loop
|
|
||||||
hub.stateLock.RLock()
|
|
||||||
elapsed := time.Since(hub.refreshed)
|
|
||||||
hub.stateLock.RUnlock()
|
|
||||||
|
|
||||||
if elapsed < refreshThrottling {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Retrieve all the smart card reader to check for cards
|
|
||||||
readers, err := hub.context.ListReaders()
|
|
||||||
if err != nil {
|
|
||||||
// This is a perverted hack, the scard library returns an error if no card
|
|
||||||
// readers are present instead of simply returning an empty list. We don't
|
|
||||||
// want to fill the user's log with errors, so filter those out.
|
|
||||||
if err.Error() != "scard: Cannot find a smart card reader." {
|
|
||||||
log.Error("Failed to enumerate smart card readers", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Transform the current list of wallets into the new one
|
|
||||||
hub.stateLock.Lock()
|
|
||||||
|
|
||||||
events := []accounts.WalletEvent{}
|
|
||||||
seen := make(map[string]struct{})
|
|
||||||
|
|
||||||
for _, reader := range readers {
|
|
||||||
// Mark the reader as present
|
|
||||||
seen[reader] = struct{}{}
|
|
||||||
|
|
||||||
// If we already know about this card, skip to the next reader, otherwise clean up
|
|
||||||
if wallet, ok := hub.wallets[reader]; ok {
|
|
||||||
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
|
|
||||||
card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny)
|
|
||||||
if err != nil {
|
|
||||||
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
|
|
||||||
hub.wallets[reader] = wallet
|
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
|
||||||
}
|
|
||||||
// Remove any wallets no longer present
|
|
||||||
for reader, wallet := range hub.wallets {
|
|
||||||
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()
|
|
||||||
|
|
||||||
for _, event := range events {
|
|
||||||
hub.updateFeed.Send(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe implements accounts.Backend, creating an async subscription to
|
|
||||||
// receive notifications on the addition or removal of smart card wallets.
|
|
||||||
func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
|
||||||
// We need the mutex to reliably start/stop the update loop
|
|
||||||
hub.stateLock.Lock()
|
|
||||||
defer hub.stateLock.Unlock()
|
|
||||||
|
|
||||||
// Subscribe the caller and track the subscriber count
|
|
||||||
sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
|
|
||||||
|
|
||||||
// Subscribers require an active notification loop, start it
|
|
||||||
if !hub.updating {
|
|
||||||
hub.updating = true
|
|
||||||
go hub.updater()
|
|
||||||
}
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
|
|
||||||
// updater is responsible for maintaining an up-to-date list of wallets managed
|
|
||||||
// by the smart card hub, and for firing wallet addition/removal events.
|
|
||||||
func (hub *Hub) updater() {
|
|
||||||
for {
|
|
||||||
// TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout
|
|
||||||
// <-hub.changes
|
|
||||||
time.Sleep(refreshCycle)
|
|
||||||
|
|
||||||
// Run the wallet refresher
|
|
||||||
hub.refreshWallets()
|
|
||||||
|
|
||||||
// If all our subscribers left, stop the updater
|
|
||||||
hub.stateLock.Lock()
|
|
||||||
if hub.updateScope.Count() == 0 {
|
|
||||||
hub.updating = false
|
|
||||||
hub.stateLock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hub.stateLock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
// Copyright 2018 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 scwallet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
pcsc "github.com/gballet/go-libpcsclite"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxPayloadSize = 223
|
|
||||||
pairP1FirstStep = 0
|
|
||||||
pairP1LastStep = 1
|
|
||||||
|
|
||||||
scSecretLength = 32
|
|
||||||
scBlockSize = 16
|
|
||||||
|
|
||||||
insOpenSecureChannel = 0x10
|
|
||||||
insMutuallyAuthenticate = 0x11
|
|
||||||
insPair = 0x12
|
|
||||||
insUnpair = 0x13
|
|
||||||
|
|
||||||
pairingSalt = "Keycard Pairing Password Salt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SecureChannelSession enables secure communication with a hardware wallet.
|
|
||||||
type SecureChannelSession struct {
|
|
||||||
card *pcsc.Card // A handle to the smartcard for communication
|
|
||||||
secret []byte // A shared secret generated from our ECDSA keys
|
|
||||||
publicKey []byte // Our own ephemeral public key
|
|
||||||
PairingKey []byte // A permanent shared secret for a pairing, if present
|
|
||||||
sessionEncKey []byte // The current session encryption key
|
|
||||||
sessionMacKey []byte // The current session MAC key
|
|
||||||
iv []byte // The current IV
|
|
||||||
PairingIndex uint8 // The pairing index
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSecureChannelSession creates a new secure channel for the given card and public key.
|
|
||||||
func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) {
|
|
||||||
// Generate an ECDSA keypair for ourselves
|
|
||||||
key, err := crypto.GenerateKey()
|
|
||||||
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(),
|
|
||||||
publicKey: elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pair establishes a new pairing with the smartcard.
|
|
||||||
func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
|
||||||
secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New)
|
|
||||||
|
|
||||||
challenge := make([]byte, 32)
|
|
||||||
if _, err := rand.Read(challenge); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := s.pair(pairP1FirstStep, challenge)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
md := sha256.New()
|
|
||||||
md.Write(secretHash[:])
|
|
||||||
md.Write(challenge)
|
|
||||||
|
|
||||||
expectedCryptogram := md.Sum(nil)
|
|
||||||
cardCryptogram := response.Data[:32]
|
|
||||||
cardChallenge := response.Data[32:64]
|
|
||||||
|
|
||||||
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
|
|
||||||
return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
|
|
||||||
}
|
|
||||||
|
|
||||||
md.Reset()
|
|
||||||
md.Write(secretHash[:])
|
|
||||||
md.Write(cardChallenge)
|
|
||||||
response, err = s.pair(pairP1LastStep, md.Sum(nil))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
md.Reset()
|
|
||||||
md.Write(secretHash[:])
|
|
||||||
md.Write(response.Data[1:])
|
|
||||||
s.PairingKey = md.Sum(nil)
|
|
||||||
s.PairingIndex = response.Data[0]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpair disestablishes an existing pairing.
|
|
||||||
func (s *SecureChannelSession) Unpair() error {
|
|
||||||
if s.PairingKey == nil {
|
|
||||||
return fmt.Errorf("cannot unpair: not paired")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.PairingKey = nil
|
|
||||||
// Close channel
|
|
||||||
s.iv = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open initializes the secure channel.
|
|
||||||
func (s *SecureChannelSession) Open() error {
|
|
||||||
if s.iv != nil {
|
|
||||||
return fmt.Errorf("session already opened")
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := s.open()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the encryption/mac key by hashing our shared secret,
|
|
||||||
// pairing key, and the first bytes returned from the Open APDU.
|
|
||||||
md := sha512.New()
|
|
||||||
md.Write(s.secret)
|
|
||||||
md.Write(s.PairingKey)
|
|
||||||
md.Write(response.Data[:scSecretLength])
|
|
||||||
keyData := md.Sum(nil)
|
|
||||||
s.sessionEncKey = keyData[:scSecretLength]
|
|
||||||
s.sessionMacKey = keyData[scSecretLength : scSecretLength*2]
|
|
||||||
|
|
||||||
// The IV is the last bytes returned from the Open APDU.
|
|
||||||
s.iv = response.Data[scSecretLength:]
|
|
||||||
|
|
||||||
return s.mutuallyAuthenticate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// mutuallyAuthenticate is an internal method to authenticate both ends of the
|
|
||||||
// connection.
|
|
||||||
func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
|
||||||
data := make([]byte, scSecretLength)
|
|
||||||
if _, err := rand.Read(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(response.Data) != scSecretLength {
|
|
||||||
return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// open is an internal method that sends an open APDU.
|
|
||||||
func (s *SecureChannelSession) open() (*responseAPDU, error) {
|
|
||||||
return transmit(s.card, &commandAPDU{
|
|
||||||
Cla: claSCWallet,
|
|
||||||
Ins: insOpenSecureChannel,
|
|
||||||
P1: s.PairingIndex,
|
|
||||||
P2: 0,
|
|
||||||
Data: s.publicKey,
|
|
||||||
Le: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// pair is an internal method that sends a pair APDU.
|
|
||||||
func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) {
|
|
||||||
return transmit(s.card, &commandAPDU{
|
|
||||||
Cla: claSCWallet,
|
|
||||||
Ins: insPair,
|
|
||||||
P1: p1,
|
|
||||||
P2: 0,
|
|
||||||
Data: data,
|
|
||||||
Le: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
|
||||||
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
|
||||||
if s.iv == nil {
|
|
||||||
return nil, fmt.Errorf("channel not open")
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := s.encryptAPDU(data)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
fulldata := make([]byte, len(s.iv)+len(data))
|
|
||||||
copy(fulldata, s.iv)
|
|
||||||
copy(fulldata[len(s.iv):], data)
|
|
||||||
|
|
||||||
response, err := transmit(s.card, &commandAPDU{
|
|
||||||
Cla: cla,
|
|
||||||
Ins: ins,
|
|
||||||
P1: p1,
|
|
||||||
P2: p2,
|
|
||||||
Data: fulldata,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
rapdu := &responseAPDU{}
|
|
||||||
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 rapdu, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
|
||||||
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))
|
|
||||||
|
|
||||||
a, err := aes.NewCipher(s.sessionEncKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
crypter := cipher.NewCBCEncrypter(a, s.iv)
|
|
||||||
crypter.CryptBlocks(ret, data)
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad applies message padding to a 16 byte boundary.
|
|
||||||
func pad(data []byte, terminator byte) []byte {
|
|
||||||
padded := make([]byte, (len(data)/16+1)*16)
|
|
||||||
copy(padded, data)
|
|
||||||
padded[len(data)] = terminator
|
|
||||||
return padded
|
|
||||||
}
|
|
||||||
|
|
||||||
// decryptAPDU is an internal method that decrypts and deserializes an APDU.
|
|
||||||
func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
|
||||||
a, err := aes.NewCipher(s.sessionEncKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]byte, len(data))
|
|
||||||
|
|
||||||
crypter := cipher.NewCBCDecrypter(a, s.iv)
|
|
||||||
crypter.CryptBlocks(ret, data)
|
|
||||||
return unpad(ret, 0x80)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpad strips padding from a message.
|
|
||||||
func unpad(data []byte, terminator byte) ([]byte, error) {
|
|
||||||
for i := 1; i <= 16; i++ {
|
|
||||||
switch data[len(data)-i] {
|
|
||||||
case 0:
|
|
||||||
continue
|
|
||||||
case terminator:
|
|
||||||
return data[:len(data)-i], nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("expected end of padding, got 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateIV is an internal method that updates the initialization vector after
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
|||||||
// Copyright 2018 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 accounts
|
|
||||||
|
|
||||||
// AccountsByURL implements sort.Interface for []Account based on the URL field.
|
|
||||||
type AccountsByURL []Account
|
|
||||||
|
|
||||||
func (a AccountsByURL) Len() int { return len(a) }
|
|
||||||
func (a AccountsByURL) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a AccountsByURL) Less(i, j int) bool { return a[i].URL.Cmp(a[j].URL) < 0 }
|
|
||||||
|
|
||||||
// WalletsByURL implements sort.Interface for []Wallet based on the URL field.
|
|
||||||
type WalletsByURL []Wallet
|
|
||||||
|
|
||||||
func (w WalletsByURL) Len() int { return len(w) }
|
|
||||||
func (w WalletsByURL) Swap(i, j int) { w[i], w[j] = w[j], w[i] }
|
|
||||||
func (w WalletsByURL) Less(i, j int) bool { return w[i].URL().Cmp(w[j].URL()) < 0 }
|
|
||||||
@@ -64,7 +64,7 @@ func (u URL) String() string {
|
|||||||
func (u URL) TerminalString() string {
|
func (u URL) TerminalString() string {
|
||||||
url := u.String()
|
url := u.String()
|
||||||
if len(url) > 32 {
|
if len(url) > 32 {
|
||||||
return url[:31] + ".."
|
return url[:31] + "…"
|
||||||
}
|
}
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
@@ -95,6 +95,7 @@ func (u *URL) UnmarshalJSON(input []byte) error {
|
|||||||
// -1 if x < y
|
// -1 if x < y
|
||||||
// 0 if x == y
|
// 0 if x == y
|
||||||
// +1 if x > y
|
// +1 if x > y
|
||||||
|
//
|
||||||
func (u URL) Cmp(url URL) int {
|
func (u URL) Cmp(url URL) int {
|
||||||
if u.Scheme == url.Scheme {
|
if u.Scheme == url.Scheme {
|
||||||
return strings.Compare(u.Path, url.Path)
|
return strings.Compare(u.Path, url.Path)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
@@ -32,10 +32,9 @@ func TestURLParsing(t *testing.T) {
|
|||||||
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range []string{"ethereum.org", ""} {
|
_, err = parseURL("ethereum.org")
|
||||||
if _, err = parseURL(u); err == nil {
|
if err == nil {
|
||||||
t.Errorf("input %v, expected err, got: nil", u)
|
t.Error("expected err, got: nil")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/karalabe/usb"
|
"github.com/karalabe/hid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
||||||
@@ -65,51 +64,21 @@ type Hub struct {
|
|||||||
// TODO(karalabe): remove if hotplug lands on Windows
|
// TODO(karalabe): remove if hotplug lands on Windows
|
||||||
commsPend int // Number of operations blocking enumeration
|
commsPend int // Number of operations blocking enumeration
|
||||||
commsLock sync.Mutex // Lock protecting the pending counter and enumeration
|
commsLock sync.Mutex // Lock protecting the pending counter and enumeration
|
||||||
enumFails uint32 // Number of times enumeration has failed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||||
func NewLedgerHub() (*Hub, error) {
|
func NewLedgerHub() (*Hub, error) {
|
||||||
return newHub(LedgerScheme, 0x2c97, []uint16{
|
return newHub(LedgerScheme, 0x2c97, []uint16{0x0000 /* Ledger Blue */, 0x0001 /* Ledger Nano S */}, 0xffa0, 0, newLedgerDriver)
|
||||||
|
|
||||||
// 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 */
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
|
// NewTrezorHub creates a new hardware wallet manager for Trezor devices.
|
||||||
func NewTrezorHubWithHID() (*Hub, error) {
|
func NewTrezorHub() (*Hub, error) {
|
||||||
return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor HID */}, 0xff00, 0, newTrezorDriver)
|
return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver)
|
||||||
}
|
|
||||||
|
|
||||||
// NewTrezorHubWithWebUSB creates a new hardware wallet manager for Trezor devices with
|
|
||||||
// firmware version > 1.8.0
|
|
||||||
func NewTrezorHubWithWebUSB() (*Hub, error) {
|
|
||||||
return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor WebUSB */}, 0xffff /* No usage id on webusb, don't match unset (0) */, 0, newTrezorDriver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHub creates a new hardware wallet manager for generic USB devices.
|
// newHub creates a new hardware wallet manager for generic USB devices.
|
||||||
func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
|
func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
|
||||||
if !usb.Supported() {
|
if !hid.Supported() {
|
||||||
return nil, errors.New("unsupported platform")
|
return nil, errors.New("unsupported platform")
|
||||||
}
|
}
|
||||||
hub := &Hub{
|
hub := &Hub{
|
||||||
@@ -150,12 +119,8 @@ func (hub *Hub) refreshWallets() {
|
|||||||
if elapsed < refreshThrottling {
|
if elapsed < refreshThrottling {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// If USB enumeration is continually failing, don't keep trying indefinitely
|
|
||||||
if atomic.LoadUint32(&hub.enumFails) > 2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Retrieve the current list of USB wallet devices
|
// Retrieve the current list of USB wallet devices
|
||||||
var devices []usb.DeviceInfo
|
var devices []hid.DeviceInfo
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// hidapi on Linux opens the device during enumeration to retrieve some infos,
|
// hidapi on Linux opens the device during enumeration to retrieve some infos,
|
||||||
@@ -170,22 +135,8 @@ func (hub *Hub) refreshWallets() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infos, err := usb.Enumerate(hub.vendorID, 0)
|
for _, info := range hid.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 {
|
|
||||||
for _, id := range hub.productIDs {
|
for _, id := range hub.productIDs {
|
||||||
// Windows and Macos use UsageID matching, Linux uses Interface matching
|
|
||||||
if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
|
if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
|
||||||
devices = append(devices, info)
|
devices = append(devices, info)
|
||||||
break
|
break
|
||||||
@@ -199,10 +150,8 @@ func (hub *Hub) refreshWallets() {
|
|||||||
// Transform the current list of wallets into the new one
|
// Transform the current list of wallets into the new one
|
||||||
hub.stateLock.Lock()
|
hub.stateLock.Lock()
|
||||||
|
|
||||||
var (
|
wallets := make([]accounts.Wallet, 0, len(devices))
|
||||||
wallets = make([]accounts.Wallet, 0, len(devices))
|
events := []accounts.WalletEvent{}
|
||||||
events []accounts.WalletEvent
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, device := range devices {
|
for _, device := range devices {
|
||||||
url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
|
url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
|
||||||
|
|||||||
3081
accounts/usbwallet/internal/trezor/messages.pb.go
Normal file
3081
accounts/usbwallet/internal/trezor/messages.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
905
accounts/usbwallet/internal/trezor/messages.proto
Normal file
905
accounts/usbwallet/internal/trezor/messages.proto
Normal file
@@ -0,0 +1,905 @@
|
|||||||
|
// This file originates from the SatoshiLabs Trezor `common` repository at:
|
||||||
|
// https://github.com/trezor/trezor-common/blob/master/protob/messages.proto
|
||||||
|
// dated 28.07.2017, commit dd8ec3231fb5f7992360aff9bdfe30bb58130f4b.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messages for TREZOR communication
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sugar for easier handling in Java
|
||||||
|
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
||||||
|
option java_outer_classname = "TrezorMessage";
|
||||||
|
|
||||||
|
import "types.proto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping between Trezor wire identifier (uint) and a protobuf message
|
||||||
|
*/
|
||||||
|
enum MessageType {
|
||||||
|
MessageType_Initialize = 0 [(wire_in) = true];
|
||||||
|
MessageType_Ping = 1 [(wire_in) = true];
|
||||||
|
MessageType_Success = 2 [(wire_out) = true];
|
||||||
|
MessageType_Failure = 3 [(wire_out) = true];
|
||||||
|
MessageType_ChangePin = 4 [(wire_in) = true];
|
||||||
|
MessageType_WipeDevice = 5 [(wire_in) = true];
|
||||||
|
MessageType_FirmwareErase = 6 [(wire_in) = true, (wire_bootloader) = true];
|
||||||
|
MessageType_FirmwareUpload = 7 [(wire_in) = true, (wire_bootloader) = true];
|
||||||
|
MessageType_FirmwareRequest = 8 [(wire_out) = true, (wire_bootloader) = true];
|
||||||
|
MessageType_GetEntropy = 9 [(wire_in) = true];
|
||||||
|
MessageType_Entropy = 10 [(wire_out) = true];
|
||||||
|
MessageType_GetPublicKey = 11 [(wire_in) = true];
|
||||||
|
MessageType_PublicKey = 12 [(wire_out) = true];
|
||||||
|
MessageType_LoadDevice = 13 [(wire_in) = true];
|
||||||
|
MessageType_ResetDevice = 14 [(wire_in) = true];
|
||||||
|
MessageType_SignTx = 15 [(wire_in) = true];
|
||||||
|
MessageType_SimpleSignTx = 16 [(wire_in) = true, deprecated = true];
|
||||||
|
MessageType_Features = 17 [(wire_out) = true];
|
||||||
|
MessageType_PinMatrixRequest = 18 [(wire_out) = true];
|
||||||
|
MessageType_PinMatrixAck = 19 [(wire_in) = true, (wire_tiny) = true];
|
||||||
|
MessageType_Cancel = 20 [(wire_in) = true];
|
||||||
|
MessageType_TxRequest = 21 [(wire_out) = true];
|
||||||
|
MessageType_TxAck = 22 [(wire_in) = true];
|
||||||
|
MessageType_CipherKeyValue = 23 [(wire_in) = true];
|
||||||
|
MessageType_ClearSession = 24 [(wire_in) = true];
|
||||||
|
MessageType_ApplySettings = 25 [(wire_in) = true];
|
||||||
|
MessageType_ButtonRequest = 26 [(wire_out) = true];
|
||||||
|
MessageType_ButtonAck = 27 [(wire_in) = true, (wire_tiny) = true];
|
||||||
|
MessageType_ApplyFlags = 28 [(wire_in) = true];
|
||||||
|
MessageType_GetAddress = 29 [(wire_in) = true];
|
||||||
|
MessageType_Address = 30 [(wire_out) = true];
|
||||||
|
MessageType_SelfTest = 32 [(wire_in) = true, (wire_bootloader) = true];
|
||||||
|
MessageType_BackupDevice = 34 [(wire_in) = true];
|
||||||
|
MessageType_EntropyRequest = 35 [(wire_out) = true];
|
||||||
|
MessageType_EntropyAck = 36 [(wire_in) = true];
|
||||||
|
MessageType_SignMessage = 38 [(wire_in) = true];
|
||||||
|
MessageType_VerifyMessage = 39 [(wire_in) = true];
|
||||||
|
MessageType_MessageSignature = 40 [(wire_out) = true];
|
||||||
|
MessageType_PassphraseRequest = 41 [(wire_out) = true];
|
||||||
|
MessageType_PassphraseAck = 42 [(wire_in) = true, (wire_tiny) = true];
|
||||||
|
MessageType_EstimateTxSize = 43 [(wire_in) = true, deprecated = true];
|
||||||
|
MessageType_TxSize = 44 [(wire_out) = true, deprecated = true];
|
||||||
|
MessageType_RecoveryDevice = 45 [(wire_in) = true];
|
||||||
|
MessageType_WordRequest = 46 [(wire_out) = true];
|
||||||
|
MessageType_WordAck = 47 [(wire_in) = true];
|
||||||
|
MessageType_CipheredKeyValue = 48 [(wire_out) = true];
|
||||||
|
MessageType_EncryptMessage = 49 [(wire_in) = true, deprecated = true];
|
||||||
|
MessageType_EncryptedMessage = 50 [(wire_out) = true, deprecated = true];
|
||||||
|
MessageType_DecryptMessage = 51 [(wire_in) = true, deprecated = true];
|
||||||
|
MessageType_DecryptedMessage = 52 [(wire_out) = true, deprecated = true];
|
||||||
|
MessageType_SignIdentity = 53 [(wire_in) = true];
|
||||||
|
MessageType_SignedIdentity = 54 [(wire_out) = true];
|
||||||
|
MessageType_GetFeatures = 55 [(wire_in) = true];
|
||||||
|
MessageType_EthereumGetAddress = 56 [(wire_in) = true];
|
||||||
|
MessageType_EthereumAddress = 57 [(wire_out) = true];
|
||||||
|
MessageType_EthereumSignTx = 58 [(wire_in) = true];
|
||||||
|
MessageType_EthereumTxRequest = 59 [(wire_out) = true];
|
||||||
|
MessageType_EthereumTxAck = 60 [(wire_in) = true];
|
||||||
|
MessageType_GetECDHSessionKey = 61 [(wire_in) = true];
|
||||||
|
MessageType_ECDHSessionKey = 62 [(wire_out) = true];
|
||||||
|
MessageType_SetU2FCounter = 63 [(wire_in) = true];
|
||||||
|
MessageType_EthereumSignMessage = 64 [(wire_in) = true];
|
||||||
|
MessageType_EthereumVerifyMessage = 65 [(wire_in) = true];
|
||||||
|
MessageType_EthereumMessageSignature = 66 [(wire_out) = true];
|
||||||
|
MessageType_DebugLinkDecision = 100 [(wire_debug_in) = true, (wire_tiny) = true];
|
||||||
|
MessageType_DebugLinkGetState = 101 [(wire_debug_in) = true];
|
||||||
|
MessageType_DebugLinkState = 102 [(wire_debug_out) = true];
|
||||||
|
MessageType_DebugLinkStop = 103 [(wire_debug_in) = true];
|
||||||
|
MessageType_DebugLinkLog = 104 [(wire_debug_out) = true];
|
||||||
|
MessageType_DebugLinkMemoryRead = 110 [(wire_debug_in) = true];
|
||||||
|
MessageType_DebugLinkMemory = 111 [(wire_debug_out) = true];
|
||||||
|
MessageType_DebugLinkMemoryWrite = 112 [(wire_debug_in) = true];
|
||||||
|
MessageType_DebugLinkFlashErase = 113 [(wire_debug_in) = true];
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Basic messages //
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Reset device to default state and ask for device details
|
||||||
|
* @next Features
|
||||||
|
*/
|
||||||
|
message Initialize {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask for device details (no device reset)
|
||||||
|
* @next Features
|
||||||
|
*/
|
||||||
|
message GetFeatures {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Reports various information about the device
|
||||||
|
* @prev Initialize
|
||||||
|
* @prev GetFeatures
|
||||||
|
*/
|
||||||
|
message Features {
|
||||||
|
optional string vendor = 1; // name of the manufacturer, e.g. "bitcointrezor.com"
|
||||||
|
optional uint32 major_version = 2; // major version of the device, e.g. 1
|
||||||
|
optional uint32 minor_version = 3; // minor version of the device, e.g. 0
|
||||||
|
optional uint32 patch_version = 4; // patch version of the device, e.g. 0
|
||||||
|
optional bool bootloader_mode = 5; // is device in bootloader mode?
|
||||||
|
optional string device_id = 6; // device's unique identifier
|
||||||
|
optional bool pin_protection = 7; // is device protected by PIN?
|
||||||
|
optional bool passphrase_protection = 8; // is node/mnemonic encrypted using passphrase?
|
||||||
|
optional string language = 9; // device language
|
||||||
|
optional string label = 10; // device description label
|
||||||
|
repeated CoinType coins = 11; // supported coins
|
||||||
|
optional bool initialized = 12; // does device contain seed?
|
||||||
|
optional bytes revision = 13; // SCM revision of firmware
|
||||||
|
optional bytes bootloader_hash = 14; // hash of the bootloader
|
||||||
|
optional bool imported = 15; // was storage imported from an external source?
|
||||||
|
optional bool pin_cached = 16; // is PIN already cached in session?
|
||||||
|
optional bool passphrase_cached = 17; // is passphrase already cached in session?
|
||||||
|
optional bool firmware_present = 18; // is valid firmware loaded?
|
||||||
|
optional bool needs_backup = 19; // does storage need backup? (equals to Storage.needs_backup)
|
||||||
|
optional uint32 flags = 20; // device flags (equals to Storage.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: clear session (removes cached PIN, passphrase, etc).
|
||||||
|
* @next Success
|
||||||
|
*/
|
||||||
|
message ClearSession {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: change language and/or label of the device
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
* @next ButtonRequest
|
||||||
|
* @next PinMatrixRequest
|
||||||
|
*/
|
||||||
|
message ApplySettings {
|
||||||
|
optional string language = 1;
|
||||||
|
optional string label = 2;
|
||||||
|
optional bool use_passphrase = 3;
|
||||||
|
optional bytes homescreen = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: set flags of the device
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message ApplyFlags {
|
||||||
|
optional uint32 flags = 1; // bitmask, can only set bits, not unset
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Starts workflow for setting/changing/removing the PIN
|
||||||
|
* @next ButtonRequest
|
||||||
|
* @next PinMatrixRequest
|
||||||
|
*/
|
||||||
|
message ChangePin {
|
||||||
|
optional bool remove = 1; // is PIN removal requested?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Test if the device is alive, device sends back the message in Success response
|
||||||
|
* @next Success
|
||||||
|
*/
|
||||||
|
message Ping {
|
||||||
|
optional string message = 1; // message to send back in Success message
|
||||||
|
optional bool button_protection = 2; // ask for button press
|
||||||
|
optional bool pin_protection = 3; // ask for PIN if set in device
|
||||||
|
optional bool passphrase_protection = 4; // ask for passphrase if set in device
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Success of the previous request
|
||||||
|
*/
|
||||||
|
message Success {
|
||||||
|
optional string message = 1; // human readable description of action or request-specific payload
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Failure of the previous request
|
||||||
|
*/
|
||||||
|
message Failure {
|
||||||
|
optional FailureType code = 1; // computer-readable definition of the error state
|
||||||
|
optional string message = 2; // human-readable message of the error state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device is waiting for HW button press.
|
||||||
|
* @next ButtonAck
|
||||||
|
* @next Cancel
|
||||||
|
*/
|
||||||
|
message ButtonRequest {
|
||||||
|
optional ButtonRequestType code = 1;
|
||||||
|
optional string data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Computer agrees to wait for HW button press
|
||||||
|
* @prev ButtonRequest
|
||||||
|
*/
|
||||||
|
message ButtonAck {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
|
||||||
|
* @next PinMatrixAck
|
||||||
|
* @next Cancel
|
||||||
|
*/
|
||||||
|
message PinMatrixRequest {
|
||||||
|
optional PinMatrixRequestType type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Computer responds with encoded PIN
|
||||||
|
* @prev PinMatrixRequest
|
||||||
|
*/
|
||||||
|
message PinMatrixAck {
|
||||||
|
required string pin = 1; // matrix encoded PIN entered by user
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Abort last operation that required user interaction
|
||||||
|
* @prev ButtonRequest
|
||||||
|
* @prev PinMatrixRequest
|
||||||
|
* @prev PassphraseRequest
|
||||||
|
*/
|
||||||
|
message Cancel {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device awaits encryption passphrase
|
||||||
|
* @next PassphraseAck
|
||||||
|
* @next Cancel
|
||||||
|
*/
|
||||||
|
message PassphraseRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Send passphrase back
|
||||||
|
* @prev PassphraseRequest
|
||||||
|
*/
|
||||||
|
message PassphraseAck {
|
||||||
|
required string passphrase = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Request a sample of random data generated by hardware RNG. May be used for testing.
|
||||||
|
* @next ButtonRequest
|
||||||
|
* @next Entropy
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message GetEntropy {
|
||||||
|
required uint32 size = 1; // size of requested entropy
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Reply with random data generated by internal RNG
|
||||||
|
* @prev GetEntropy
|
||||||
|
*/
|
||||||
|
message Entropy {
|
||||||
|
required bytes entropy = 1; // stream of random generated bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device for public key corresponding to address_n path
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next PublicKey
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message GetPublicKey {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
optional string ecdsa_curve_name = 2; // ECDSA curve name to use
|
||||||
|
optional bool show_display = 3; // optionally show on display before sending the result
|
||||||
|
optional string coin_name = 4 [default='Bitcoin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Contains public key derived from device private seed
|
||||||
|
* @prev GetPublicKey
|
||||||
|
*/
|
||||||
|
message PublicKey {
|
||||||
|
required HDNodeType node = 1; // BIP32 public node
|
||||||
|
optional string xpub = 2; // serialized form of public node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device for address corresponding to address_n path
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next Address
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message GetAddress {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
optional string coin_name = 2 [default='Bitcoin'];
|
||||||
|
optional bool show_display = 3 ; // optionally show on display before sending the result
|
||||||
|
optional MultisigRedeemScriptType multisig = 4; // filled if we are showing a multisig address
|
||||||
|
optional InputScriptType script_type = 5 [default=SPENDADDRESS]; // used to distinguish between various address formats (non-segwit, segwit, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device for Ethereum address corresponding to address_n path
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next EthereumAddress
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EthereumGetAddress {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
optional bool show_display = 2; // optionally show on display before sending the result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Contains address derived from device private seed
|
||||||
|
* @prev GetAddress
|
||||||
|
*/
|
||||||
|
message Address {
|
||||||
|
required string address = 1; // Coin address in Base58 encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Contains an Ethereum address derived from device private seed
|
||||||
|
* @prev EthereumGetAddress
|
||||||
|
*/
|
||||||
|
message EthereumAddress {
|
||||||
|
required bytes address = 1; // Coin address as an Ethereum 160 bit hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Request device to wipe all sensitive data and settings
|
||||||
|
* @next ButtonRequest
|
||||||
|
*/
|
||||||
|
message WipeDevice {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Load seed and related internal settings from the computer
|
||||||
|
* @next ButtonRequest
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message LoadDevice {
|
||||||
|
optional string mnemonic = 1; // seed encoded as BIP-39 mnemonic (12, 18 or 24 words)
|
||||||
|
optional HDNodeType node = 2; // BIP-32 node
|
||||||
|
optional string pin = 3; // set PIN protection
|
||||||
|
optional bool passphrase_protection = 4; // enable master node encryption using passphrase
|
||||||
|
optional string language = 5 [default='english']; // device language
|
||||||
|
optional string label = 6; // device label
|
||||||
|
optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum
|
||||||
|
optional uint32 u2f_counter = 8; // U2F counter
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to do initialization involving user interaction
|
||||||
|
* @next EntropyRequest
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message ResetDevice {
|
||||||
|
optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy
|
||||||
|
optional uint32 strength = 2 [default=256]; // strength of seed in bits
|
||||||
|
optional bool passphrase_protection = 3; // enable master node encryption using passphrase
|
||||||
|
optional bool pin_protection = 4; // enable PIN protection
|
||||||
|
optional string language = 5 [default='english']; // device language
|
||||||
|
optional string label = 6; // device label
|
||||||
|
optional uint32 u2f_counter = 7; // U2F counter
|
||||||
|
optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Perform backup of the device seed if not backed up using ResetDevice
|
||||||
|
* @next ButtonRequest
|
||||||
|
*/
|
||||||
|
message BackupDevice {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Ask for additional entropy from host computer
|
||||||
|
* @prev ResetDevice
|
||||||
|
* @next EntropyAck
|
||||||
|
*/
|
||||||
|
message EntropyRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Provide additional entropy for seed generation function
|
||||||
|
* @prev EntropyRequest
|
||||||
|
* @next ButtonRequest
|
||||||
|
*/
|
||||||
|
message EntropyAck {
|
||||||
|
optional bytes entropy = 1; // 256 bits (32 bytes) of random data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Start recovery workflow asking user for specific words of mnemonic
|
||||||
|
* Used to recovery device safely even on untrusted computer.
|
||||||
|
* @next WordRequest
|
||||||
|
*/
|
||||||
|
message RecoveryDevice {
|
||||||
|
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic
|
||||||
|
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
|
||||||
|
optional bool pin_protection = 3; // enable PIN protection
|
||||||
|
optional string language = 4 [default='english']; // device language
|
||||||
|
optional string label = 5; // device label
|
||||||
|
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process
|
||||||
|
// 7 reserved for unused recovery method
|
||||||
|
optional uint32 type = 8; // supported recovery type (see RecoveryType)
|
||||||
|
optional uint32 u2f_counter = 9; // U2F counter
|
||||||
|
optional bool dry_run = 10; // perform dry-run recovery workflow (for safe mnemonic validation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device is waiting for user to enter word of the mnemonic
|
||||||
|
* Its position is shown only on device's internal display.
|
||||||
|
* @prev RecoveryDevice
|
||||||
|
* @prev WordAck
|
||||||
|
*/
|
||||||
|
message WordRequest {
|
||||||
|
optional WordRequestType type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Computer replies with word from the mnemonic
|
||||||
|
* @prev WordRequest
|
||||||
|
* @next WordRequest
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message WordAck {
|
||||||
|
required string word = 1; // one word of mnemonic on asked position
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Message signing messages //
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign message
|
||||||
|
* @next MessageSignature
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message SignMessage {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
required bytes message = 2; // message to be signed
|
||||||
|
optional string coin_name = 3 [default='Bitcoin']; // coin to use for signing
|
||||||
|
optional InputScriptType script_type = 4 [default=SPENDADDRESS]; // used to distinguish between various address formats (non-segwit, segwit, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to verify message
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message VerifyMessage {
|
||||||
|
optional string address = 1; // address to verify
|
||||||
|
optional bytes signature = 2; // signature to verify
|
||||||
|
optional bytes message = 3; // message to verify
|
||||||
|
optional string coin_name = 4 [default='Bitcoin']; // coin to use for verifying
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Signed message
|
||||||
|
* @prev SignMessage
|
||||||
|
*/
|
||||||
|
message MessageSignature {
|
||||||
|
optional string address = 1; // address used to sign the message
|
||||||
|
optional bytes signature = 2; // signature of the message
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// Encryption/decryption //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to encrypt message
|
||||||
|
* @next EncryptedMessage
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EncryptMessage {
|
||||||
|
optional bytes pubkey = 1; // public key
|
||||||
|
optional bytes message = 2; // message to encrypt
|
||||||
|
optional bool display_only = 3; // show just on display? (don't send back via wire)
|
||||||
|
repeated uint32 address_n = 4; // BIP-32 path to derive the signing key from master node
|
||||||
|
optional string coin_name = 5 [default='Bitcoin']; // coin to use for signing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Encrypted message
|
||||||
|
* @prev EncryptMessage
|
||||||
|
*/
|
||||||
|
message EncryptedMessage {
|
||||||
|
optional bytes nonce = 1; // nonce used during encryption
|
||||||
|
optional bytes message = 2; // encrypted message
|
||||||
|
optional bytes hmac = 3; // message hmac
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to decrypt message
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message DecryptMessage {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the decryption key from master node
|
||||||
|
optional bytes nonce = 2; // nonce used during encryption
|
||||||
|
optional bytes message = 3; // message to decrypt
|
||||||
|
optional bytes hmac = 4; // message hmac
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Decrypted message
|
||||||
|
* @prev DecryptedMessage
|
||||||
|
*/
|
||||||
|
message DecryptedMessage {
|
||||||
|
optional bytes message = 1; // decrypted message
|
||||||
|
optional string address = 2; // address used to sign the message (if used)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to encrypt or decrypt value of given key
|
||||||
|
* @next CipheredKeyValue
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message CipherKeyValue {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
optional string key = 2; // key component of key:value
|
||||||
|
optional bytes value = 3; // value component of key:value
|
||||||
|
optional bool encrypt = 4; // are we encrypting (True) or decrypting (False)?
|
||||||
|
optional bool ask_on_encrypt = 5; // should we ask on encrypt operation?
|
||||||
|
optional bool ask_on_decrypt = 6; // should we ask on decrypt operation?
|
||||||
|
optional bytes iv = 7; // initialization vector (will be computed if not set)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Return ciphered/deciphered value
|
||||||
|
* @prev CipherKeyValue
|
||||||
|
*/
|
||||||
|
message CipheredKeyValue {
|
||||||
|
optional bytes value = 1; // ciphered/deciphered value
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// Transaction signing messages //
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Estimated size of the transaction
|
||||||
|
* This behaves exactly like SignTx, which means that it can ask using TxRequest
|
||||||
|
* This call is non-blocking (except possible PassphraseRequest to unlock the seed)
|
||||||
|
* @next TxSize
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EstimateTxSize {
|
||||||
|
required uint32 outputs_count = 1; // number of transaction outputs
|
||||||
|
required uint32 inputs_count = 2; // number of transaction inputs
|
||||||
|
optional string coin_name = 3 [default='Bitcoin']; // coin to use
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Estimated size of the transaction
|
||||||
|
* @prev EstimateTxSize
|
||||||
|
*/
|
||||||
|
message TxSize {
|
||||||
|
optional uint32 tx_size = 1; // estimated size of transaction in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign transaction
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next PinMatrixRequest
|
||||||
|
* @next TxRequest
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message SignTx {
|
||||||
|
required uint32 outputs_count = 1; // number of transaction outputs
|
||||||
|
required uint32 inputs_count = 2; // number of transaction inputs
|
||||||
|
optional string coin_name = 3 [default='Bitcoin']; // coin to use
|
||||||
|
optional uint32 version = 4 [default=1]; // transaction version
|
||||||
|
optional uint32 lock_time = 5 [default=0]; // transaction lock_time
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Simplified transaction signing
|
||||||
|
* This method doesn't support streaming, so there are hardware limits in number of inputs and outputs.
|
||||||
|
* In case of success, the result is returned using TxRequest message.
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next PinMatrixRequest
|
||||||
|
* @next TxRequest
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message SimpleSignTx {
|
||||||
|
repeated TxInputType inputs = 1; // transaction inputs
|
||||||
|
repeated TxOutputType outputs = 2; // transaction outputs
|
||||||
|
repeated TransactionType transactions = 3; // transactions whose outputs are used to build current inputs
|
||||||
|
optional string coin_name = 4 [default='Bitcoin']; // coin to use
|
||||||
|
optional uint32 version = 5 [default=1]; // transaction version
|
||||||
|
optional uint32 lock_time = 6 [default=0]; // transaction lock_time
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device asks for information for signing transaction or returns the last result
|
||||||
|
* If request_index is set, device awaits TxAck message (with fields filled in according to request_type)
|
||||||
|
* If signature_index is set, 'signature' contains signed input of signature_index's input
|
||||||
|
* @prev SignTx
|
||||||
|
* @prev SimpleSignTx
|
||||||
|
* @prev TxAck
|
||||||
|
*/
|
||||||
|
message TxRequest {
|
||||||
|
optional RequestType request_type = 1; // what should be filled in TxAck message?
|
||||||
|
optional TxRequestDetailsType details = 2; // request for tx details
|
||||||
|
optional TxRequestSerializedType serialized = 3; // serialized data and request for next
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Reported transaction data
|
||||||
|
* @prev TxRequest
|
||||||
|
* @next TxRequest
|
||||||
|
*/
|
||||||
|
message TxAck {
|
||||||
|
optional TransactionType tx = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign transaction
|
||||||
|
* All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
|
||||||
|
* Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
|
||||||
|
* @next PassphraseRequest
|
||||||
|
* @next PinMatrixRequest
|
||||||
|
* @next EthereumTxRequest
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EthereumSignTx {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
optional bytes nonce = 2; // <=256 bit unsigned big endian
|
||||||
|
optional bytes gas_price = 3; // <=256 bit unsigned big endian (in wei)
|
||||||
|
optional bytes gas_limit = 4; // <=256 bit unsigned big endian
|
||||||
|
optional bytes to = 5; // 160 bit address hash
|
||||||
|
optional bytes value = 6; // <=256 bit unsigned big endian (in wei)
|
||||||
|
optional bytes data_initial_chunk = 7; // The initial data chunk (<= 1024 bytes)
|
||||||
|
optional uint32 data_length = 8; // Length of transaction payload
|
||||||
|
optional uint32 chain_id = 9; // Chain Id for EIP 155
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device asks for more data from transaction payload, or returns the signature.
|
||||||
|
* If data_length is set, device awaits that many more bytes of payload.
|
||||||
|
* Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
|
||||||
|
* @prev EthereumSignTx
|
||||||
|
* @next EthereumTxAck
|
||||||
|
*/
|
||||||
|
message EthereumTxRequest {
|
||||||
|
optional uint32 data_length = 1; // Number of bytes being requested (<= 1024)
|
||||||
|
optional uint32 signature_v = 2; // Computed signature (recovery parameter, limited to 27 or 28)
|
||||||
|
optional bytes signature_r = 3; // Computed signature R component (256 bit)
|
||||||
|
optional bytes signature_s = 4; // Computed signature S component (256 bit)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Transaction payload data.
|
||||||
|
* @prev EthereumTxRequest
|
||||||
|
* @next EthereumTxRequest
|
||||||
|
*/
|
||||||
|
message EthereumTxAck {
|
||||||
|
optional bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// Ethereum: Message signing messages //
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign message
|
||||||
|
* @next EthereumMessageSignature
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EthereumSignMessage {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
required bytes message = 2; // message to be signed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to verify message
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message EthereumVerifyMessage {
|
||||||
|
optional bytes address = 1; // address to verify
|
||||||
|
optional bytes signature = 2; // signature to verify
|
||||||
|
optional bytes message = 3; // message to verify
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Signed message
|
||||||
|
* @prev EthereumSignMessage
|
||||||
|
*/
|
||||||
|
message EthereumMessageSignature {
|
||||||
|
optional bytes address = 1; // address used to sign the message
|
||||||
|
optional bytes signature = 2; // signature of the message
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// Identity messages //
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to sign identity
|
||||||
|
* @next SignedIdentity
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message SignIdentity {
|
||||||
|
optional IdentityType identity = 1; // identity
|
||||||
|
optional bytes challenge_hidden = 2; // non-visible challenge
|
||||||
|
optional string challenge_visual = 3; // challenge shown on display (e.g. date+time)
|
||||||
|
optional string ecdsa_curve_name = 4; // ECDSA curve name to use
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device provides signed identity
|
||||||
|
* @prev SignIdentity
|
||||||
|
*/
|
||||||
|
message SignedIdentity {
|
||||||
|
optional string address = 1; // identity address
|
||||||
|
optional bytes public_key = 2; // identity public key
|
||||||
|
optional bytes signature = 3; // signature of the identity data
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
// ECDH messages //
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to generate ECDH session key
|
||||||
|
* @next ECDHSessionKey
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message GetECDHSessionKey {
|
||||||
|
optional IdentityType identity = 1; // identity
|
||||||
|
optional bytes peer_public_key = 2; // peer's public key
|
||||||
|
optional string ecdsa_curve_name = 3; // ECDSA curve name to use
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device provides ECDH session key
|
||||||
|
* @prev GetECDHSessionKey
|
||||||
|
*/
|
||||||
|
message ECDHSessionKey {
|
||||||
|
optional bytes session_key = 1; // ECDH session key
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
// U2F messages //
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Set U2F counter
|
||||||
|
* @next Success
|
||||||
|
*/
|
||||||
|
message SetU2FCounter {
|
||||||
|
optional uint32 u2f_counter = 1; // counter
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// Bootloader messages //
|
||||||
|
/////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to erase its firmware (so it can be replaced via FirmwareUpload)
|
||||||
|
* @next Success
|
||||||
|
* @next FirmwareRequest
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message FirmwareErase {
|
||||||
|
optional uint32 length = 1; // length of new firmware
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Ask for firmware chunk
|
||||||
|
* @next FirmwareUpload
|
||||||
|
*/
|
||||||
|
message FirmwareRequest {
|
||||||
|
optional uint32 offset = 1; // offset of requested firmware chunk
|
||||||
|
optional uint32 length = 2; // length of requested firmware chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Send firmware in binary form to the device
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message FirmwareUpload {
|
||||||
|
required bytes payload = 1; // firmware to be loaded into device
|
||||||
|
optional bytes hash = 2; // hash of the payload
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Perform a device self-test
|
||||||
|
* @next Success
|
||||||
|
* @next Failure
|
||||||
|
*/
|
||||||
|
message SelfTest {
|
||||||
|
optional bytes payload = 1; // payload to be used in self-test
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// Debug messages (only available if DebugLink is enabled) //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: "Press" the button on the device
|
||||||
|
* @next Success
|
||||||
|
*/
|
||||||
|
message DebugLinkDecision {
|
||||||
|
required bool yes_no = 1; // true for "Confirm", false for "Cancel"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Computer asks for device state
|
||||||
|
* @next DebugLinkState
|
||||||
|
*/
|
||||||
|
message DebugLinkGetState {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device current state
|
||||||
|
* @prev DebugLinkGetState
|
||||||
|
*/
|
||||||
|
message DebugLinkState {
|
||||||
|
optional bytes layout = 1; // raw buffer of display
|
||||||
|
optional string pin = 2; // current PIN, blank if PIN is not set/enabled
|
||||||
|
optional string matrix = 3; // current PIN matrix
|
||||||
|
optional string mnemonic = 4; // current BIP-39 mnemonic
|
||||||
|
optional HDNodeType node = 5; // current BIP-32 node
|
||||||
|
optional bool passphrase_protection = 6; // is node/mnemonic encrypted using passphrase?
|
||||||
|
optional string reset_word = 7; // word on device display during ResetDevice workflow
|
||||||
|
optional bytes reset_entropy = 8; // current entropy during ResetDevice workflow
|
||||||
|
optional string recovery_fake_word = 9; // (fake) word on display during RecoveryDevice workflow
|
||||||
|
optional uint32 recovery_word_pos = 10; // index of mnemonic word the device is expecting during RecoveryDevice workflow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Ask device to restart
|
||||||
|
*/
|
||||||
|
message DebugLinkStop {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device wants host to log event
|
||||||
|
*/
|
||||||
|
message DebugLinkLog {
|
||||||
|
optional uint32 level = 1;
|
||||||
|
optional string bucket = 2;
|
||||||
|
optional string text = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Read memory from device
|
||||||
|
* @next DebugLinkMemory
|
||||||
|
*/
|
||||||
|
message DebugLinkMemoryRead {
|
||||||
|
optional uint32 address = 1;
|
||||||
|
optional uint32 length = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response: Device sends memory back
|
||||||
|
* @prev DebugLinkMemoryRead
|
||||||
|
*/
|
||||||
|
message DebugLinkMemory {
|
||||||
|
optional bytes memory = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Write memory to device.
|
||||||
|
* WARNING: Writing to the wrong location can irreparably break the device.
|
||||||
|
*/
|
||||||
|
message DebugLinkMemoryWrite {
|
||||||
|
optional uint32 address = 1;
|
||||||
|
optional bytes memory = 2;
|
||||||
|
optional bool flash = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request: Erase block of flash on device
|
||||||
|
* WARNING: Writing to the wrong location can irreparably break the device.
|
||||||
|
*/
|
||||||
|
message DebugLinkFlashErase {
|
||||||
|
optional uint32 sector = 1;
|
||||||
|
}
|
||||||
46
accounts/usbwallet/internal/trezor/trezor.go
Normal file
46
accounts/usbwallet/internal/trezor/trezor.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// This file contains the implementation for interacting with the Trezor hardware
|
||||||
|
// wallets. The wire protocol spec can be found on the SatoshiLabs website:
|
||||||
|
// https://doc.satoshilabs.com/trezor-tech/api-protobuf.html
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=import_path=trezor:. types.proto messages.proto
|
||||||
|
|
||||||
|
// Package trezor contains the wire protocol wrapper in Go.
|
||||||
|
package trezor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type returns the protocol buffer type number of a specific message. If the
|
||||||
|
// message is nil, this method panics!
|
||||||
|
func Type(msg proto.Message) uint16 {
|
||||||
|
return uint16(MessageType_value["MessageType_"+reflect.TypeOf(msg).Elem().Name()])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the friendly message type name of a specific protocol buffer
|
||||||
|
// type number.
|
||||||
|
func Name(kind uint16) string {
|
||||||
|
name := MessageType_name[int32(kind)]
|
||||||
|
if len(name) < 12 {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return name[12:]
|
||||||
|
}
|
||||||
1333
accounts/usbwallet/internal/trezor/types.pb.go
Normal file
1333
accounts/usbwallet/internal/trezor/types.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
278
accounts/usbwallet/internal/trezor/types.proto
Normal file
278
accounts/usbwallet/internal/trezor/types.proto
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
// This file originates from the SatoshiLabs Trezor `common` repository at:
|
||||||
|
// https://github.com/trezor/trezor-common/blob/master/protob/types.proto
|
||||||
|
// dated 28.07.2017, commit dd8ec3231fb5f7992360aff9bdfe30bb58130f4b.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types for TREZOR communication
|
||||||
|
*
|
||||||
|
* @author Marek Palatinus <slush@satoshilabs.com>
|
||||||
|
* @version 1.2
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sugar for easier handling in Java
|
||||||
|
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
||||||
|
option java_outer_classname = "TrezorType";
|
||||||
|
|
||||||
|
import "google/protobuf/descriptor.proto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for specifying message direction and type of wire (normal/debug)
|
||||||
|
*/
|
||||||
|
extend google.protobuf.EnumValueOptions {
|
||||||
|
optional bool wire_in = 50002; // message can be transmitted via wire from PC to TREZOR
|
||||||
|
optional bool wire_out = 50003; // message can be transmitted via wire from TREZOR to PC
|
||||||
|
optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to TREZOR
|
||||||
|
optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from TREZOR to PC
|
||||||
|
optional bool wire_tiny = 50006; // message is handled by TREZOR when the USB stack is in tiny mode
|
||||||
|
optional bool wire_bootloader = 50007; // message is only handled by TREZOR Bootloader
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of failures returned by Failure message
|
||||||
|
* @used_in Failure
|
||||||
|
*/
|
||||||
|
enum FailureType {
|
||||||
|
Failure_UnexpectedMessage = 1;
|
||||||
|
Failure_ButtonExpected = 2;
|
||||||
|
Failure_DataError = 3;
|
||||||
|
Failure_ActionCancelled = 4;
|
||||||
|
Failure_PinExpected = 5;
|
||||||
|
Failure_PinCancelled = 6;
|
||||||
|
Failure_PinInvalid = 7;
|
||||||
|
Failure_InvalidSignature = 8;
|
||||||
|
Failure_ProcessError = 9;
|
||||||
|
Failure_NotEnoughFunds = 10;
|
||||||
|
Failure_NotInitialized = 11;
|
||||||
|
Failure_FirmwareError = 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of script which will be used for transaction output
|
||||||
|
* @used_in TxOutputType
|
||||||
|
*/
|
||||||
|
enum OutputScriptType {
|
||||||
|
PAYTOADDRESS = 0; // used for all addresses (bitcoin, p2sh, witness)
|
||||||
|
PAYTOSCRIPTHASH = 1; // p2sh address (deprecated; use PAYTOADDRESS)
|
||||||
|
PAYTOMULTISIG = 2; // only for change output
|
||||||
|
PAYTOOPRETURN = 3; // op_return
|
||||||
|
PAYTOWITNESS = 4; // only for change output
|
||||||
|
PAYTOP2SHWITNESS = 5; // only for change output
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of script which will be used for transaction output
|
||||||
|
* @used_in TxInputType
|
||||||
|
*/
|
||||||
|
enum InputScriptType {
|
||||||
|
SPENDADDRESS = 0; // standard p2pkh address
|
||||||
|
SPENDMULTISIG = 1; // p2sh multisig address
|
||||||
|
EXTERNAL = 2; // reserved for external inputs (coinjoin)
|
||||||
|
SPENDWITNESS = 3; // native segwit
|
||||||
|
SPENDP2SHWITNESS = 4; // segwit over p2sh (backward compatible)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of information required by transaction signing process
|
||||||
|
* @used_in TxRequest
|
||||||
|
*/
|
||||||
|
enum RequestType {
|
||||||
|
TXINPUT = 0;
|
||||||
|
TXOUTPUT = 1;
|
||||||
|
TXMETA = 2;
|
||||||
|
TXFINISHED = 3;
|
||||||
|
TXEXTRADATA = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of button request
|
||||||
|
* @used_in ButtonRequest
|
||||||
|
*/
|
||||||
|
enum ButtonRequestType {
|
||||||
|
ButtonRequest_Other = 1;
|
||||||
|
ButtonRequest_FeeOverThreshold = 2;
|
||||||
|
ButtonRequest_ConfirmOutput = 3;
|
||||||
|
ButtonRequest_ResetDevice = 4;
|
||||||
|
ButtonRequest_ConfirmWord = 5;
|
||||||
|
ButtonRequest_WipeDevice = 6;
|
||||||
|
ButtonRequest_ProtectCall = 7;
|
||||||
|
ButtonRequest_SignTx = 8;
|
||||||
|
ButtonRequest_FirmwareCheck = 9;
|
||||||
|
ButtonRequest_Address = 10;
|
||||||
|
ButtonRequest_PublicKey = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of PIN request
|
||||||
|
* @used_in PinMatrixRequest
|
||||||
|
*/
|
||||||
|
enum PinMatrixRequestType {
|
||||||
|
PinMatrixRequestType_Current = 1;
|
||||||
|
PinMatrixRequestType_NewFirst = 2;
|
||||||
|
PinMatrixRequestType_NewSecond = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of recovery procedure. These should be used as bitmask, e.g.,
|
||||||
|
* `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
|
||||||
|
* listing every method supported by the host computer.
|
||||||
|
*
|
||||||
|
* Note that ScrambledWords must be supported by every implementation
|
||||||
|
* for backward compatibility; there is no way to not support it.
|
||||||
|
*
|
||||||
|
* @used_in RecoveryDevice
|
||||||
|
*/
|
||||||
|
enum RecoveryDeviceType {
|
||||||
|
// use powers of two when extending this field
|
||||||
|
RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
|
||||||
|
RecoveryDeviceType_Matrix = 1; // matrix recovery type
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of Recovery Word request
|
||||||
|
* @used_in WordRequest
|
||||||
|
*/
|
||||||
|
enum WordRequestType {
|
||||||
|
WordRequestType_Plain = 0;
|
||||||
|
WordRequestType_Matrix9 = 1;
|
||||||
|
WordRequestType_Matrix6 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing BIP32 (hierarchical deterministic) node
|
||||||
|
* Used for imports of private key into the device and exporting public key out of device
|
||||||
|
* @used_in PublicKey
|
||||||
|
* @used_in LoadDevice
|
||||||
|
* @used_in DebugLinkState
|
||||||
|
* @used_in Storage
|
||||||
|
*/
|
||||||
|
message HDNodeType {
|
||||||
|
required uint32 depth = 1;
|
||||||
|
required uint32 fingerprint = 2;
|
||||||
|
required uint32 child_num = 3;
|
||||||
|
required bytes chain_code = 4;
|
||||||
|
optional bytes private_key = 5;
|
||||||
|
optional bytes public_key = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HDNodePathType {
|
||||||
|
required HDNodeType node = 1; // BIP-32 node in deserialized form
|
||||||
|
repeated uint32 address_n = 2; // BIP-32 path to derive the key from node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing Coin
|
||||||
|
* @used_in Features
|
||||||
|
*/
|
||||||
|
message CoinType {
|
||||||
|
optional string coin_name = 1;
|
||||||
|
optional string coin_shortcut = 2;
|
||||||
|
optional uint32 address_type = 3 [default=0];
|
||||||
|
optional uint64 maxfee_kb = 4;
|
||||||
|
optional uint32 address_type_p2sh = 5 [default=5];
|
||||||
|
optional string signed_message_header = 8;
|
||||||
|
optional uint32 xpub_magic = 9 [default=76067358]; // default=0x0488b21e
|
||||||
|
optional uint32 xprv_magic = 10 [default=76066276]; // default=0x0488ade4
|
||||||
|
optional bool segwit = 11;
|
||||||
|
optional uint32 forkid = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of redeem script used in input
|
||||||
|
* @used_in TxInputType
|
||||||
|
*/
|
||||||
|
message MultisigRedeemScriptType {
|
||||||
|
repeated HDNodePathType pubkeys = 1; // pubkeys from multisig address (sorted lexicographically)
|
||||||
|
repeated bytes signatures = 2; // existing signatures for partially signed input
|
||||||
|
optional uint32 m = 3; // "m" from n, how many valid signatures is necessary for spending
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing transaction input
|
||||||
|
* @used_in SimpleSignTx
|
||||||
|
* @used_in TransactionType
|
||||||
|
*/
|
||||||
|
message TxInputType {
|
||||||
|
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
||||||
|
required bytes prev_hash = 2; // hash of previous transaction output to spend by this input
|
||||||
|
required uint32 prev_index = 3; // index of previous output to spend
|
||||||
|
optional bytes script_sig = 4; // script signature, unset for tx to sign
|
||||||
|
optional uint32 sequence = 5 [default=4294967295]; // sequence (default=0xffffffff)
|
||||||
|
optional InputScriptType script_type = 6 [default=SPENDADDRESS]; // defines template of input script
|
||||||
|
optional MultisigRedeemScriptType multisig = 7; // Filled if input is going to spend multisig tx
|
||||||
|
optional uint64 amount = 8; // amount of previous transaction output (for segwit only)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing transaction output
|
||||||
|
* @used_in SimpleSignTx
|
||||||
|
* @used_in TransactionType
|
||||||
|
*/
|
||||||
|
message TxOutputType {
|
||||||
|
optional string address = 1; // target coin address in Base58 encoding
|
||||||
|
repeated uint32 address_n = 2; // BIP-32 path to derive the key from master node; has higher priority than "address"
|
||||||
|
required uint64 amount = 3; // amount to spend in satoshis
|
||||||
|
required OutputScriptType script_type = 4; // output script type
|
||||||
|
optional MultisigRedeemScriptType multisig = 5; // defines multisig address; script_type must be PAYTOMULTISIG
|
||||||
|
optional bytes op_return_data = 6; // defines op_return data; script_type must be PAYTOOPRETURN, amount must be 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing compiled transaction output
|
||||||
|
* @used_in TransactionType
|
||||||
|
*/
|
||||||
|
message TxOutputBinType {
|
||||||
|
required uint64 amount = 1;
|
||||||
|
required bytes script_pubkey = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing transaction
|
||||||
|
* @used_in SimpleSignTx
|
||||||
|
*/
|
||||||
|
message TransactionType {
|
||||||
|
optional uint32 version = 1;
|
||||||
|
repeated TxInputType inputs = 2;
|
||||||
|
repeated TxOutputBinType bin_outputs = 3;
|
||||||
|
repeated TxOutputType outputs = 5;
|
||||||
|
optional uint32 lock_time = 4;
|
||||||
|
optional uint32 inputs_cnt = 6;
|
||||||
|
optional uint32 outputs_cnt = 7;
|
||||||
|
optional bytes extra_data = 8;
|
||||||
|
optional uint32 extra_data_len = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing request details
|
||||||
|
* @used_in TxRequest
|
||||||
|
*/
|
||||||
|
message TxRequestDetailsType {
|
||||||
|
optional uint32 request_index = 1; // device expects TxAck message from the computer
|
||||||
|
optional bytes tx_hash = 2; // tx_hash of requested transaction
|
||||||
|
optional uint32 extra_data_len = 3; // length of requested extra data
|
||||||
|
optional uint32 extra_data_offset = 4; // offset of requested extra data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing serialized data
|
||||||
|
* @used_in TxRequest
|
||||||
|
*/
|
||||||
|
message TxRequestSerializedType {
|
||||||
|
optional uint32 signature_index = 1; // 'signature' field contains signed input of this index
|
||||||
|
optional bytes signature = 2; // signature of the signature_index input
|
||||||
|
optional bytes serialized_tx = 3; // part of serialized and signed transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure representing identity data
|
||||||
|
* @used_in IdentityType
|
||||||
|
*/
|
||||||
|
message IdentityType {
|
||||||
|
optional string proto = 1; // proto part of URI
|
||||||
|
optional string user = 2; // user part of URI
|
||||||
|
optional string host = 3; // host part of URI
|
||||||
|
optional string port = 4; // port part of URI
|
||||||
|
optional string path = 5; // path part of URI
|
||||||
|
optional uint32 index = 6 [default=0]; // identity index
|
||||||
|
}
|
||||||
@@ -32,7 +32,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
@@ -52,10 +51,8 @@ const (
|
|||||||
ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
|
ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
|
||||||
ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
|
ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
|
||||||
ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
|
ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
|
||||||
ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs an Ethereum message following the EIP 712 specification
|
|
||||||
|
|
||||||
ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
|
ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
|
||||||
ledgerP1InitTypedMessageData ledgerParam1 = 0x00 // First chunk of Typed Message data
|
|
||||||
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
|
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
|
||||||
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
|
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
|
||||||
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
|
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
|
||||||
@@ -165,31 +162,12 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
|||||||
}
|
}
|
||||||
// Ensure the wallet is capable of signing the given transaction
|
// Ensure the wallet is capable of signing the given transaction
|
||||||
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
|
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
|
||||||
//lint:ignore ST1005 brand name displayed on the console
|
|
||||||
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
||||||
}
|
}
|
||||||
// All infos gathered and metadata checks out, request signing
|
// All infos gathered and metadata checks out, request signing
|
||||||
return w.ledgerSign(path, tx, chainID)
|
return w.ledgerSign(path, tx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTypedMessage implements usbwallet.driver, sending the message to the Ledger and
|
|
||||||
// waiting for the user to sign or deny the transaction.
|
|
||||||
//
|
|
||||||
// Note: this was introduced in the ledger 1.5.0 firmware
|
|
||||||
func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
|
|
||||||
// If the Ethereum app doesn't run, abort
|
|
||||||
if w.offline() {
|
|
||||||
return nil, accounts.ErrWalletClosed
|
|
||||||
}
|
|
||||||
// Ensure the wallet is capable of signing the given transaction
|
|
||||||
if w.version[0] < 1 && w.version[1] < 5 {
|
|
||||||
//lint:ignore ST1005 brand name displayed on the console
|
|
||||||
return nil, fmt.Errorf("Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)", w.version[0], w.version[1], w.version[2])
|
|
||||||
}
|
|
||||||
// All infos gathered and metadata checks out, request signing
|
|
||||||
return w.ledgerSignTypedMessage(path, domainHash, messageHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ledgerVersion retrieves the current version of the Ethereum wallet app running
|
// ledgerVersion retrieves the current version of the Ethereum wallet app running
|
||||||
// on the Ledger wallet.
|
// on the Ledger wallet.
|
||||||
//
|
//
|
||||||
@@ -363,7 +341,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||||||
op = ledgerP1ContTransactionData
|
op = ledgerP1ContTransactionData
|
||||||
}
|
}
|
||||||
// Extract the Ethereum signature and do a sanity validation
|
// Extract the Ethereum signature and do a sanity validation
|
||||||
if len(reply) != crypto.SignatureLength {
|
if len(reply) != 65 {
|
||||||
return common.Address{}, nil, errors.New("reply lacks signature")
|
return common.Address{}, nil, errors.New("reply lacks signature")
|
||||||
}
|
}
|
||||||
signature := append(reply[1:], reply[0])
|
signature := append(reply[1:], reply[0])
|
||||||
@@ -387,66 +365,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||||||
return sender, signed, nil
|
return sender, signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ledgerSignTypedMessage sends the transaction to the Ledger wallet, and waits for the user
|
|
||||||
// to confirm or deny the transaction.
|
|
||||||
//
|
|
||||||
// The signing protocol is defined as follows:
|
|
||||||
//
|
|
||||||
// CLA | INS | P1 | P2 | Lc | Le
|
|
||||||
// ----+-----+----+-----------------------------+-----+---
|
|
||||||
// E0 | 0C | 00 | implementation version : 00 | variable | variable
|
|
||||||
//
|
|
||||||
// Where the input is:
|
|
||||||
//
|
|
||||||
// Description | Length
|
|
||||||
// -------------------------------------------------+----------
|
|
||||||
// Number of BIP 32 derivations to perform (max 10) | 1 byte
|
|
||||||
// First derivation index (big endian) | 4 bytes
|
|
||||||
// ... | 4 bytes
|
|
||||||
// Last derivation index (big endian) | 4 bytes
|
|
||||||
// domain hash | 32 bytes
|
|
||||||
// message hash | 32 bytes
|
|
||||||
//
|
|
||||||
// And the output data is:
|
|
||||||
//
|
|
||||||
// Description | Length
|
|
||||||
// ------------+---------
|
|
||||||
// signature V | 1 byte
|
|
||||||
// signature R | 32 bytes
|
|
||||||
// signature S | 32 bytes
|
|
||||||
func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) {
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
// Create the 712 message
|
|
||||||
payload := append(path, domainHash...)
|
|
||||||
payload = append(payload, messageHash...)
|
|
||||||
|
|
||||||
// Send the request and wait for the response
|
|
||||||
var (
|
|
||||||
op = ledgerP1InitTypedMessageData
|
|
||||||
reply []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send the message over, ensuring it's processed correctly
|
|
||||||
reply, err = w.ledgerExchange(ledgerOpSignTypedMessage, op, 0, payload)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the Ethereum signature and do a sanity validation
|
|
||||||
if len(reply) != crypto.SignatureLength {
|
|
||||||
return nil, errors.New("reply lacks signature")
|
|
||||||
}
|
|
||||||
signature := append(reply[1:], reply[0])
|
|
||||||
return signature, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
|
// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
|
||||||
// message and retrieving the response.
|
// message and retrieving the response.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
|
"github.com/ethereum/go-ethereum/accounts/usbwallet/internal/trezor"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@@ -41,9 +41,6 @@ import (
|
|||||||
// encoded passphrase.
|
// encoded passphrase.
|
||||||
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
||||||
|
|
||||||
// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
|
|
||||||
var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
|
|
||||||
|
|
||||||
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
||||||
// if the device replies with a mismatching header. This usually means the device
|
// if the device replies with a mismatching header. This usually means the device
|
||||||
// is in browser mode.
|
// is in browser mode.
|
||||||
@@ -55,7 +52,6 @@ type trezorDriver struct {
|
|||||||
version [3]uint32 // Current version of the Trezor firmware
|
version [3]uint32 // Current version of the Trezor firmware
|
||||||
label string // Current textual label of the Trezor device
|
label string // Current textual label of the Trezor device
|
||||||
pinwait bool // Flags whether the device is waiting for PIN entry
|
pinwait bool // Flags whether the device is waiting for PIN entry
|
||||||
passphrasewait bool // Flags whether the device is waiting for passphrase entry
|
|
||||||
failure error // Any failure that would make the device unusable
|
failure error // Any failure that would make the device unusable
|
||||||
log log.Logger // Contextual logger to tag the trezor with its id
|
log log.Logger // Contextual logger to tag the trezor with its id
|
||||||
}
|
}
|
||||||
@@ -83,21 +79,19 @@ func (w *trezorDriver) Status() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open implements usbwallet.driver, attempting to initialize the connection to
|
// Open implements usbwallet.driver, attempting to initialize the connection to
|
||||||
// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
|
// the Trezor hardware wallet. Initializing the Trezor is a two phase operation:
|
||||||
// - The first phase is to initialize the connection and read the wallet's
|
// * The first phase is to initialize the connection and read the wallet's
|
||||||
// features. This phase is invoked if the provided passphrase is empty. The
|
// features. This phase is invoked is the provided passphrase is empty. The
|
||||||
// device will display the pinpad as a result and will return an appropriate
|
// device will display the pinpad as a result and will return an appropriate
|
||||||
// error to notify the user that a second open phase is needed.
|
// error to notify the user that a second open phase is needed.
|
||||||
// - The second phase is to unlock access to the Trezor, which is done by the
|
// * The second phase is to unlock access to the Trezor, which is done by the
|
||||||
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
||||||
// number of the user (shuffled according to the pinpad displayed).
|
// number of the user (shuffled according to the pinpad displayed).
|
||||||
// - If needed the device will ask for passphrase which will require calling
|
|
||||||
// open again with the actual passphrase (3rd phase)
|
|
||||||
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
w.device, w.failure = device, nil
|
w.device, w.failure = device, nil
|
||||||
|
|
||||||
// If phase 1 is requested, init the connection and wait for user callback
|
// If phase 1 is requested, init the connection and wait for user callback
|
||||||
if passphrase == "" && !w.passphrasewait {
|
if passphrase == "" {
|
||||||
// If we're already waiting for a PIN entry, insta-return
|
// If we're already waiting for a PIN entry, insta-return
|
||||||
if w.pinwait {
|
if w.pinwait {
|
||||||
return ErrTrezorPINNeeded
|
return ErrTrezorPINNeeded
|
||||||
@@ -110,46 +104,26 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|||||||
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
||||||
w.label = features.GetLabel()
|
w.label = features.GetLabel()
|
||||||
|
|
||||||
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
// Do a manual ping, forcing the device to ask for its PIN
|
||||||
askPin := true
|
askPin := true
|
||||||
askPassphrase := true
|
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin}, new(trezor.PinMatrixRequest), new(trezor.Success))
|
||||||
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Only return the PIN request if the device wasn't unlocked until now
|
// Only return the PIN request if the device wasn't unlocked until now
|
||||||
switch res {
|
if res == 1 {
|
||||||
case 0:
|
return nil // Device responded with trezor.Success
|
||||||
|
}
|
||||||
w.pinwait = true
|
w.pinwait = true
|
||||||
return ErrTrezorPINNeeded
|
return ErrTrezorPINNeeded
|
||||||
case 1:
|
|
||||||
w.pinwait = false
|
|
||||||
w.passphrasewait = true
|
|
||||||
return ErrTrezorPassphraseNeeded
|
|
||||||
case 2:
|
|
||||||
return nil // responded with trezor.Success
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Phase 2 requested with actual PIN entry
|
// Phase 2 requested with actual PIN entry
|
||||||
if w.pinwait {
|
|
||||||
w.pinwait = false
|
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
|
|
||||||
}
|
|
||||||
} else if w.passphrasewait {
|
|
||||||
w.passphrasewait = false
|
|
||||||
if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
|
|
||||||
w.failure = err
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil {
|
||||||
|
w.failure = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,10 +159,6 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
|||||||
return w.trezorSign(path, tx, chainID)
|
return w.trezorSign(path, tx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *trezorDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
|
|
||||||
return nil, accounts.ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// trezorDerive sends a derivation request to the Trezor device and returns the
|
// trezorDerive sends a derivation request to the Trezor device and returns the
|
||||||
// Ethereum address located on that path.
|
// Ethereum address located on that path.
|
||||||
func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) {
|
func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) {
|
||||||
@@ -196,13 +166,7 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
|
|||||||
if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
|
if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary formats
|
return common.BytesToAddress(address.GetAddress()), nil
|
||||||
return common.BytesToAddress(addr), nil
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trezorSign sends the transaction to the Trezor wallet, and waits for the user
|
// trezorSign sends the transaction to the Trezor wallet, and waits for the user
|
||||||
@@ -221,10 +185,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||||||
DataLength: &length,
|
DataLength: &length,
|
||||||
}
|
}
|
||||||
if to := tx.To(); to != nil {
|
if to := tx.To(); to != nil {
|
||||||
// Non contract deploy, set recipient explicitly
|
request.To = (*to)[:] // 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
|
if length > 1024 { // Send the data chunked if that was requested
|
||||||
request.DataInitialChunk, data = data[:1024], data[1024:]
|
request.DataInitialChunk, data = data[:1024], data[1024:]
|
||||||
@@ -259,11 +220,9 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
signer = new(types.HomesteadSigner)
|
signer = new(types.HomesteadSigner)
|
||||||
} else {
|
} else {
|
||||||
// Trezor backend does not support typed transactions yet.
|
|
||||||
signer = types.NewEIP155Signer(chainID)
|
signer = types.NewEIP155Signer(chainID)
|
||||||
signature[64] -= byte(chainID.Uint64()*2 + 35)
|
signature[64] -= byte(chainID.Uint64()*2 + 35)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject the final signature into the transaction and sanity check the sender
|
// Inject the final signature into the transaction and sanity check the sender
|
||||||
signed, err := tx.WithSignature(signer, signature)
|
signed, err := tx.WithSignature(signer, signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,811 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// source: messages-common.proto
|
|
||||||
|
|
||||||
package trezor
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
math "math"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
type Failure_FailureType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
Failure_Failure_UnexpectedMessage Failure_FailureType = 1
|
|
||||||
Failure_Failure_ButtonExpected Failure_FailureType = 2
|
|
||||||
Failure_Failure_DataError Failure_FailureType = 3
|
|
||||||
Failure_Failure_ActionCancelled Failure_FailureType = 4
|
|
||||||
Failure_Failure_PinExpected Failure_FailureType = 5
|
|
||||||
Failure_Failure_PinCancelled Failure_FailureType = 6
|
|
||||||
Failure_Failure_PinInvalid Failure_FailureType = 7
|
|
||||||
Failure_Failure_InvalidSignature Failure_FailureType = 8
|
|
||||||
Failure_Failure_ProcessError Failure_FailureType = 9
|
|
||||||
Failure_Failure_NotEnoughFunds Failure_FailureType = 10
|
|
||||||
Failure_Failure_NotInitialized Failure_FailureType = 11
|
|
||||||
Failure_Failure_PinMismatch Failure_FailureType = 12
|
|
||||||
Failure_Failure_FirmwareError Failure_FailureType = 99
|
|
||||||
)
|
|
||||||
|
|
||||||
var Failure_FailureType_name = map[int32]string{
|
|
||||||
1: "Failure_UnexpectedMessage",
|
|
||||||
2: "Failure_ButtonExpected",
|
|
||||||
3: "Failure_DataError",
|
|
||||||
4: "Failure_ActionCancelled",
|
|
||||||
5: "Failure_PinExpected",
|
|
||||||
6: "Failure_PinCancelled",
|
|
||||||
7: "Failure_PinInvalid",
|
|
||||||
8: "Failure_InvalidSignature",
|
|
||||||
9: "Failure_ProcessError",
|
|
||||||
10: "Failure_NotEnoughFunds",
|
|
||||||
11: "Failure_NotInitialized",
|
|
||||||
12: "Failure_PinMismatch",
|
|
||||||
99: "Failure_FirmwareError",
|
|
||||||
}
|
|
||||||
|
|
||||||
var Failure_FailureType_value = map[string]int32{
|
|
||||||
"Failure_UnexpectedMessage": 1,
|
|
||||||
"Failure_ButtonExpected": 2,
|
|
||||||
"Failure_DataError": 3,
|
|
||||||
"Failure_ActionCancelled": 4,
|
|
||||||
"Failure_PinExpected": 5,
|
|
||||||
"Failure_PinCancelled": 6,
|
|
||||||
"Failure_PinInvalid": 7,
|
|
||||||
"Failure_InvalidSignature": 8,
|
|
||||||
"Failure_ProcessError": 9,
|
|
||||||
"Failure_NotEnoughFunds": 10,
|
|
||||||
"Failure_NotInitialized": 11,
|
|
||||||
"Failure_PinMismatch": 12,
|
|
||||||
"Failure_FirmwareError": 99,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x Failure_FailureType) Enum() *Failure_FailureType {
|
|
||||||
p := new(Failure_FailureType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x Failure_FailureType) String() string {
|
|
||||||
return proto.EnumName(Failure_FailureType_name, int32(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Failure_FailureType) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(Failure_FailureType_value, data, "Failure_FailureType")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = Failure_FailureType(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Failure_FailureType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{1, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Type of button request
|
|
||||||
type ButtonRequest_ButtonRequestType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
ButtonRequest_ButtonRequest_Other ButtonRequest_ButtonRequestType = 1
|
|
||||||
ButtonRequest_ButtonRequest_FeeOverThreshold ButtonRequest_ButtonRequestType = 2
|
|
||||||
ButtonRequest_ButtonRequest_ConfirmOutput ButtonRequest_ButtonRequestType = 3
|
|
||||||
ButtonRequest_ButtonRequest_ResetDevice ButtonRequest_ButtonRequestType = 4
|
|
||||||
ButtonRequest_ButtonRequest_ConfirmWord ButtonRequest_ButtonRequestType = 5
|
|
||||||
ButtonRequest_ButtonRequest_WipeDevice ButtonRequest_ButtonRequestType = 6
|
|
||||||
ButtonRequest_ButtonRequest_ProtectCall ButtonRequest_ButtonRequestType = 7
|
|
||||||
ButtonRequest_ButtonRequest_SignTx ButtonRequest_ButtonRequestType = 8
|
|
||||||
ButtonRequest_ButtonRequest_FirmwareCheck ButtonRequest_ButtonRequestType = 9
|
|
||||||
ButtonRequest_ButtonRequest_Address ButtonRequest_ButtonRequestType = 10
|
|
||||||
ButtonRequest_ButtonRequest_PublicKey ButtonRequest_ButtonRequestType = 11
|
|
||||||
ButtonRequest_ButtonRequest_MnemonicWordCount ButtonRequest_ButtonRequestType = 12
|
|
||||||
ButtonRequest_ButtonRequest_MnemonicInput ButtonRequest_ButtonRequestType = 13
|
|
||||||
ButtonRequest_ButtonRequest_PassphraseType ButtonRequest_ButtonRequestType = 14
|
|
||||||
ButtonRequest_ButtonRequest_UnknownDerivationPath ButtonRequest_ButtonRequestType = 15
|
|
||||||
)
|
|
||||||
|
|
||||||
var ButtonRequest_ButtonRequestType_name = map[int32]string{
|
|
||||||
1: "ButtonRequest_Other",
|
|
||||||
2: "ButtonRequest_FeeOverThreshold",
|
|
||||||
3: "ButtonRequest_ConfirmOutput",
|
|
||||||
4: "ButtonRequest_ResetDevice",
|
|
||||||
5: "ButtonRequest_ConfirmWord",
|
|
||||||
6: "ButtonRequest_WipeDevice",
|
|
||||||
7: "ButtonRequest_ProtectCall",
|
|
||||||
8: "ButtonRequest_SignTx",
|
|
||||||
9: "ButtonRequest_FirmwareCheck",
|
|
||||||
10: "ButtonRequest_Address",
|
|
||||||
11: "ButtonRequest_PublicKey",
|
|
||||||
12: "ButtonRequest_MnemonicWordCount",
|
|
||||||
13: "ButtonRequest_MnemonicInput",
|
|
||||||
14: "ButtonRequest_PassphraseType",
|
|
||||||
15: "ButtonRequest_UnknownDerivationPath",
|
|
||||||
}
|
|
||||||
|
|
||||||
var ButtonRequest_ButtonRequestType_value = map[string]int32{
|
|
||||||
"ButtonRequest_Other": 1,
|
|
||||||
"ButtonRequest_FeeOverThreshold": 2,
|
|
||||||
"ButtonRequest_ConfirmOutput": 3,
|
|
||||||
"ButtonRequest_ResetDevice": 4,
|
|
||||||
"ButtonRequest_ConfirmWord": 5,
|
|
||||||
"ButtonRequest_WipeDevice": 6,
|
|
||||||
"ButtonRequest_ProtectCall": 7,
|
|
||||||
"ButtonRequest_SignTx": 8,
|
|
||||||
"ButtonRequest_FirmwareCheck": 9,
|
|
||||||
"ButtonRequest_Address": 10,
|
|
||||||
"ButtonRequest_PublicKey": 11,
|
|
||||||
"ButtonRequest_MnemonicWordCount": 12,
|
|
||||||
"ButtonRequest_MnemonicInput": 13,
|
|
||||||
"ButtonRequest_PassphraseType": 14,
|
|
||||||
"ButtonRequest_UnknownDerivationPath": 15,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
|
|
||||||
p := new(ButtonRequest_ButtonRequestType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ButtonRequest_ButtonRequestType) String() string {
|
|
||||||
return proto.EnumName(ButtonRequest_ButtonRequestType_name, int32(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(ButtonRequest_ButtonRequestType_value, data, "ButtonRequest_ButtonRequestType")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = ButtonRequest_ButtonRequestType(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ButtonRequest_ButtonRequestType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{2, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Type of PIN request
|
|
||||||
type PinMatrixRequest_PinMatrixRequestType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
PinMatrixRequest_PinMatrixRequestType_Current PinMatrixRequest_PinMatrixRequestType = 1
|
|
||||||
PinMatrixRequest_PinMatrixRequestType_NewFirst PinMatrixRequest_PinMatrixRequestType = 2
|
|
||||||
PinMatrixRequest_PinMatrixRequestType_NewSecond PinMatrixRequest_PinMatrixRequestType = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var PinMatrixRequest_PinMatrixRequestType_name = map[int32]string{
|
|
||||||
1: "PinMatrixRequestType_Current",
|
|
||||||
2: "PinMatrixRequestType_NewFirst",
|
|
||||||
3: "PinMatrixRequestType_NewSecond",
|
|
||||||
}
|
|
||||||
|
|
||||||
var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{
|
|
||||||
"PinMatrixRequestType_Current": 1,
|
|
||||||
"PinMatrixRequestType_NewFirst": 2,
|
|
||||||
"PinMatrixRequestType_NewSecond": 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
|
|
||||||
p := new(PinMatrixRequest_PinMatrixRequestType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x PinMatrixRequest_PinMatrixRequestType) String() string {
|
|
||||||
return proto.EnumName(PinMatrixRequest_PinMatrixRequestType_name, int32(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(PinMatrixRequest_PinMatrixRequestType_value, data, "PinMatrixRequest_PinMatrixRequestType")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = PinMatrixRequest_PinMatrixRequestType(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (PinMatrixRequest_PinMatrixRequestType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{4, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Success of the previous request
|
|
||||||
// @end
|
|
||||||
type Success struct {
|
|
||||||
Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Success) Reset() { *m = Success{} }
|
|
||||||
func (m *Success) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Success) ProtoMessage() {}
|
|
||||||
func (*Success) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Success) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_Success.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *Success) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_Success.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *Success) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_Success.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *Success) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_Success.Size(m)
|
|
||||||
}
|
|
||||||
func (m *Success) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_Success.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_Success proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *Success) GetMessage() string {
|
|
||||||
if m != nil && m.Message != nil {
|
|
||||||
return *m.Message
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Failure of the previous request
|
|
||||||
// @end
|
|
||||||
type Failure struct {
|
|
||||||
Code *Failure_FailureType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.Failure_FailureType" json:"code,omitempty"`
|
|
||||||
Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Failure) Reset() { *m = Failure{} }
|
|
||||||
func (m *Failure) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Failure) ProtoMessage() {}
|
|
||||||
func (*Failure) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Failure) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_Failure.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *Failure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_Failure.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *Failure) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_Failure.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *Failure) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_Failure.Size(m)
|
|
||||||
}
|
|
||||||
func (m *Failure) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_Failure.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_Failure proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *Failure) GetCode() Failure_FailureType {
|
|
||||||
if m != nil && m.Code != nil {
|
|
||||||
return *m.Code
|
|
||||||
}
|
|
||||||
return Failure_Failure_UnexpectedMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Failure) GetMessage() string {
|
|
||||||
if m != nil && m.Message != nil {
|
|
||||||
return *m.Message
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Device is waiting for HW button press.
|
|
||||||
// @auxstart
|
|
||||||
// @next ButtonAck
|
|
||||||
type ButtonRequest struct {
|
|
||||||
Code *ButtonRequest_ButtonRequestType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.ButtonRequest_ButtonRequestType" json:"code,omitempty"`
|
|
||||||
Data *string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ButtonRequest) Reset() { *m = ButtonRequest{} }
|
|
||||||
func (m *ButtonRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*ButtonRequest) ProtoMessage() {}
|
|
||||||
func (*ButtonRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ButtonRequest) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_ButtonRequest.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *ButtonRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_ButtonRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *ButtonRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_ButtonRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *ButtonRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_ButtonRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *ButtonRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_ButtonRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_ButtonRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType {
|
|
||||||
if m != nil && m.Code != nil {
|
|
||||||
return *m.Code
|
|
||||||
}
|
|
||||||
return ButtonRequest_ButtonRequest_Other
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ButtonRequest) GetData() string {
|
|
||||||
if m != nil && m.Data != nil {
|
|
||||||
return *m.Data
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Computer agrees to wait for HW button press
|
|
||||||
// @auxend
|
|
||||||
type ButtonAck struct {
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ButtonAck) Reset() { *m = ButtonAck{} }
|
|
||||||
func (m *ButtonAck) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*ButtonAck) ProtoMessage() {}
|
|
||||||
func (*ButtonAck) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ButtonAck) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_ButtonAck.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *ButtonAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_ButtonAck.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *ButtonAck) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_ButtonAck.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *ButtonAck) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_ButtonAck.Size(m)
|
|
||||||
}
|
|
||||||
func (m *ButtonAck) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_ButtonAck.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_ButtonAck proto.InternalMessageInfo
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
|
|
||||||
// @auxstart
|
|
||||||
// @next PinMatrixAck
|
|
||||||
type PinMatrixRequest struct {
|
|
||||||
Type *PinMatrixRequest_PinMatrixRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType" json:"type,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PinMatrixRequest) Reset() { *m = PinMatrixRequest{} }
|
|
||||||
func (m *PinMatrixRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PinMatrixRequest) ProtoMessage() {}
|
|
||||||
func (*PinMatrixRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PinMatrixRequest) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PinMatrixRequest.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PinMatrixRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PinMatrixRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PinMatrixRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PinMatrixRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PinMatrixRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
|
|
||||||
if m != nil && m.Type != nil {
|
|
||||||
return *m.Type
|
|
||||||
}
|
|
||||||
return PinMatrixRequest_PinMatrixRequestType_Current
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Computer responds with encoded PIN
|
|
||||||
// @auxend
|
|
||||||
type PinMatrixAck struct {
|
|
||||||
Pin *string `protobuf:"bytes,1,req,name=pin" json:"pin,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PinMatrixAck) Reset() { *m = PinMatrixAck{} }
|
|
||||||
func (m *PinMatrixAck) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PinMatrixAck) ProtoMessage() {}
|
|
||||||
func (*PinMatrixAck) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{5}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PinMatrixAck) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PinMatrixAck.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PinMatrixAck.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixAck) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PinMatrixAck.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixAck) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PinMatrixAck.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PinMatrixAck) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PinMatrixAck.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PinMatrixAck proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *PinMatrixAck) GetPin() string {
|
|
||||||
if m != nil && m.Pin != nil {
|
|
||||||
return *m.Pin
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Device awaits encryption passphrase
|
|
||||||
// @auxstart
|
|
||||||
// @next PassphraseAck
|
|
||||||
type PassphraseRequest struct {
|
|
||||||
OnDevice *bool `protobuf:"varint,1,opt,name=on_device,json=onDevice" json:"on_device,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseRequest) Reset() { *m = PassphraseRequest{} }
|
|
||||||
func (m *PassphraseRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PassphraseRequest) ProtoMessage() {}
|
|
||||||
func (*PassphraseRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{6}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseRequest) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PassphraseRequest.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PassphraseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PassphraseRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PassphraseRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PassphraseRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PassphraseRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PassphraseRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PassphraseRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PassphraseRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PassphraseRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *PassphraseRequest) GetOnDevice() bool {
|
|
||||||
if m != nil && m.OnDevice != nil {
|
|
||||||
return *m.OnDevice
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Send passphrase back
|
|
||||||
// @next PassphraseStateRequest
|
|
||||||
type PassphraseAck struct {
|
|
||||||
Passphrase *string `protobuf:"bytes,1,opt,name=passphrase" json:"passphrase,omitempty"`
|
|
||||||
State []byte `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseAck) Reset() { *m = PassphraseAck{} }
|
|
||||||
func (m *PassphraseAck) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PassphraseAck) ProtoMessage() {}
|
|
||||||
func (*PassphraseAck) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{7}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseAck) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PassphraseAck.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PassphraseAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PassphraseAck.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PassphraseAck) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PassphraseAck.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PassphraseAck) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PassphraseAck.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PassphraseAck) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PassphraseAck.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PassphraseAck proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *PassphraseAck) GetPassphrase() string {
|
|
||||||
if m != nil && m.Passphrase != nil {
|
|
||||||
return *m.Passphrase
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseAck) GetState() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.State
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Device awaits passphrase state
|
|
||||||
// @next PassphraseStateAck
|
|
||||||
type PassphraseStateRequest struct {
|
|
||||||
State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseStateRequest) Reset() { *m = PassphraseStateRequest{} }
|
|
||||||
func (m *PassphraseStateRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PassphraseStateRequest) ProtoMessage() {}
|
|
||||||
func (*PassphraseStateRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{8}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseStateRequest) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PassphraseStateRequest.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PassphraseStateRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PassphraseStateRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PassphraseStateRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PassphraseStateRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PassphraseStateRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *PassphraseStateRequest) GetState() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.State
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Send passphrase state back
|
|
||||||
// @auxend
|
|
||||||
type PassphraseStateAck struct {
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseStateAck) Reset() { *m = PassphraseStateAck{} }
|
|
||||||
func (m *PassphraseStateAck) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*PassphraseStateAck) ProtoMessage() {}
|
|
||||||
func (*PassphraseStateAck) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{9}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PassphraseStateAck) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_PassphraseStateAck.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_PassphraseStateAck.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateAck) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_PassphraseStateAck.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateAck) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_PassphraseStateAck.Size(m)
|
|
||||||
}
|
|
||||||
func (m *PassphraseStateAck) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_PassphraseStateAck.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_PassphraseStateAck proto.InternalMessageInfo
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Structure representing BIP32 (hierarchical deterministic) node
|
|
||||||
// Used for imports of private key into the device and exporting public key out of device
|
|
||||||
// @embed
|
|
||||||
type HDNodeType struct {
|
|
||||||
Depth *uint32 `protobuf:"varint,1,req,name=depth" json:"depth,omitempty"`
|
|
||||||
Fingerprint *uint32 `protobuf:"varint,2,req,name=fingerprint" json:"fingerprint,omitempty"`
|
|
||||||
ChildNum *uint32 `protobuf:"varint,3,req,name=child_num,json=childNum" json:"child_num,omitempty"`
|
|
||||||
ChainCode []byte `protobuf:"bytes,4,req,name=chain_code,json=chainCode" json:"chain_code,omitempty"`
|
|
||||||
PrivateKey []byte `protobuf:"bytes,5,opt,name=private_key,json=privateKey" json:"private_key,omitempty"`
|
|
||||||
PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey" json:"public_key,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) Reset() { *m = HDNodeType{} }
|
|
||||||
func (m *HDNodeType) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*HDNodeType) ProtoMessage() {}
|
|
||||||
func (*HDNodeType) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_aaf30d059fdbc38d, []int{10}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_HDNodeType.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *HDNodeType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_HDNodeType.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *HDNodeType) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_HDNodeType.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *HDNodeType) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_HDNodeType.Size(m)
|
|
||||||
}
|
|
||||||
func (m *HDNodeType) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_HDNodeType.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_HDNodeType proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetDepth() uint32 {
|
|
||||||
if m != nil && m.Depth != nil {
|
|
||||||
return *m.Depth
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetFingerprint() uint32 {
|
|
||||||
if m != nil && m.Fingerprint != nil {
|
|
||||||
return *m.Fingerprint
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetChildNum() uint32 {
|
|
||||||
if m != nil && m.ChildNum != nil {
|
|
||||||
return *m.ChildNum
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetChainCode() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.ChainCode
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetPrivateKey() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.PrivateKey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HDNodeType) GetPublicKey() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.PublicKey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterEnum("hw.trezor.messages.common.Failure_FailureType", Failure_FailureType_name, Failure_FailureType_value)
|
|
||||||
proto.RegisterEnum("hw.trezor.messages.common.ButtonRequest_ButtonRequestType", ButtonRequest_ButtonRequestType_name, ButtonRequest_ButtonRequestType_value)
|
|
||||||
proto.RegisterEnum("hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType", PinMatrixRequest_PinMatrixRequestType_name, PinMatrixRequest_PinMatrixRequestType_value)
|
|
||||||
proto.RegisterType((*Success)(nil), "hw.trezor.messages.common.Success")
|
|
||||||
proto.RegisterType((*Failure)(nil), "hw.trezor.messages.common.Failure")
|
|
||||||
proto.RegisterType((*ButtonRequest)(nil), "hw.trezor.messages.common.ButtonRequest")
|
|
||||||
proto.RegisterType((*ButtonAck)(nil), "hw.trezor.messages.common.ButtonAck")
|
|
||||||
proto.RegisterType((*PinMatrixRequest)(nil), "hw.trezor.messages.common.PinMatrixRequest")
|
|
||||||
proto.RegisterType((*PinMatrixAck)(nil), "hw.trezor.messages.common.PinMatrixAck")
|
|
||||||
proto.RegisterType((*PassphraseRequest)(nil), "hw.trezor.messages.common.PassphraseRequest")
|
|
||||||
proto.RegisterType((*PassphraseAck)(nil), "hw.trezor.messages.common.PassphraseAck")
|
|
||||||
proto.RegisterType((*PassphraseStateRequest)(nil), "hw.trezor.messages.common.PassphraseStateRequest")
|
|
||||||
proto.RegisterType((*PassphraseStateAck)(nil), "hw.trezor.messages.common.PassphraseStateAck")
|
|
||||||
proto.RegisterType((*HDNodeType)(nil), "hw.trezor.messages.common.HDNodeType")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("messages-common.proto", fileDescriptor_aaf30d059fdbc38d) }
|
|
||||||
|
|
||||||
var fileDescriptor_aaf30d059fdbc38d = []byte{
|
|
||||||
// 846 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xcd, 0x52, 0x23, 0x37,
|
|
||||||
0x10, 0x2e, 0xff, 0x80, 0xed, 0xb6, 0xd9, 0x08, 0xc5, 0x80, 0x09, 0xb0, 0x38, 0xc3, 0x21, 0x5c,
|
|
||||||
0xe2, 0x4a, 0xe5, 0x98, 0x53, 0x58, 0x83, 0x2b, 0xd4, 0x16, 0x86, 0x1a, 0xd8, 0xda, 0xa3, 0x4b,
|
|
||||||
0xd1, 0xf4, 0x32, 0x2a, 0xcf, 0x48, 0x13, 0x8d, 0x06, 0xf0, 0x5e, 0xf2, 0x6a, 0x79, 0x89, 0xbc,
|
|
||||||
0x42, 0xaa, 0x52, 0xb9, 0xe4, 0x11, 0xb6, 0x34, 0x3f, 0x78, 0xc6, 0x66, 0x39, 0xcd, 0xe8, 0xfb,
|
|
||||||
0xbe, 0xee, 0x96, 0xba, 0x3f, 0x09, 0x76, 0x42, 0x8c, 0x63, 0x76, 0x8f, 0xf1, 0x8f, 0x5c, 0x85,
|
|
||||||
0xa1, 0x92, 0xa3, 0x48, 0x2b, 0xa3, 0xe8, 0xbe, 0xff, 0x38, 0x32, 0x1a, 0x3f, 0x2b, 0x3d, 0x2a,
|
|
||||||
0x04, 0xa3, 0x4c, 0xe0, 0x9c, 0x40, 0xeb, 0x36, 0xe1, 0x1c, 0xe3, 0x98, 0x0e, 0xa0, 0x95, 0xb3,
|
|
||||||
0x83, 0xda, 0xb0, 0x76, 0xda, 0x71, 0x8b, 0xa5, 0xf3, 0x77, 0x03, 0x5a, 0x13, 0x26, 0x82, 0x44,
|
|
||||||
0x23, 0x7d, 0x07, 0x4d, 0xae, 0xbc, 0x4c, 0xf2, 0xe6, 0xe7, 0xd1, 0xe8, 0xab, 0xa9, 0x47, 0x79,
|
|
||||||
0x44, 0xf1, 0xbd, 0x5b, 0x44, 0xe8, 0xa6, 0xb1, 0xe5, 0x4a, 0xf5, 0x6a, 0xa5, 0xff, 0xea, 0xd0,
|
|
||||||
0x2d, 0xe9, 0xe9, 0x11, 0xec, 0xe7, 0xcb, 0xd9, 0x07, 0x89, 0x4f, 0x11, 0x72, 0x83, 0xde, 0x55,
|
|
||||||
0x26, 0x26, 0x35, 0xfa, 0x1d, 0xec, 0x16, 0xf4, 0xbb, 0xc4, 0x18, 0x25, 0x2f, 0x72, 0x09, 0xa9,
|
|
||||||
0xd3, 0x1d, 0xd8, 0x2e, 0xb8, 0x73, 0x66, 0xd8, 0x85, 0xd6, 0x4a, 0x93, 0x06, 0x3d, 0x80, 0xbd,
|
|
||||||
0x02, 0x3e, 0xe3, 0x46, 0x28, 0x39, 0x66, 0x92, 0x63, 0x10, 0xa0, 0x47, 0x9a, 0x74, 0x0f, 0xbe,
|
|
||||||
0x2d, 0xc8, 0x1b, 0xb1, 0x4c, 0xb6, 0x41, 0x07, 0xd0, 0x2f, 0x11, 0xcb, 0x90, 0x4d, 0xba, 0x0b,
|
|
||||||
0xb4, 0xc4, 0x5c, 0xca, 0x07, 0x16, 0x08, 0x8f, 0xb4, 0xe8, 0x21, 0x0c, 0x0a, 0x3c, 0x07, 0x6f,
|
|
||||||
0xc5, 0xbd, 0x64, 0x26, 0xd1, 0x48, 0xda, 0x95, 0x7c, 0x5a, 0xd9, 0xf6, 0x67, 0xfb, 0xeb, 0x94,
|
|
||||||
0x8f, 0x34, 0x55, 0xe6, 0x42, 0xaa, 0xe4, 0xde, 0x9f, 0x24, 0xd2, 0x8b, 0x09, 0xac, 0x70, 0x97,
|
|
||||||
0x52, 0x18, 0xc1, 0x02, 0xf1, 0x19, 0x3d, 0xd2, 0x5d, 0xd9, 0xfa, 0x95, 0x88, 0x43, 0x66, 0xb8,
|
|
||||||
0x4f, 0x7a, 0x74, 0x1f, 0x76, 0x0a, 0x62, 0x22, 0x74, 0xf8, 0xc8, 0x34, 0x66, 0xb5, 0xb8, 0xf3,
|
|
||||||
0x4f, 0x13, 0xb6, 0xb2, 0xbe, 0xb9, 0xf8, 0x47, 0x82, 0xb1, 0xa1, 0xd3, 0xca, 0x74, 0x7f, 0x79,
|
|
||||||
0x65, 0xba, 0x95, 0xb8, 0xea, 0xaa, 0x34, 0x69, 0x0a, 0x4d, 0x8f, 0x19, 0x96, 0x8f, 0x39, 0xfd,
|
|
||||||
0x77, 0xfe, 0x6f, 0xc0, 0xf6, 0x9a, 0xde, 0xee, 0xbf, 0x02, 0xce, 0xae, 0x8d, 0x8f, 0x9a, 0xd4,
|
|
||||||
0xa8, 0x03, 0x6f, 0xab, 0xc4, 0x04, 0xf1, 0xfa, 0x01, 0xf5, 0x9d, 0xaf, 0x31, 0xf6, 0x55, 0x60,
|
|
||||||
0x67, 0x7d, 0x0c, 0x07, 0x55, 0xcd, 0x58, 0xc9, 0x4f, 0x42, 0x87, 0xd7, 0x89, 0x89, 0x12, 0x43,
|
|
||||||
0x1a, 0xd6, 0x47, 0x55, 0x81, 0x8b, 0x31, 0x9a, 0x73, 0x7c, 0x10, 0x1c, 0x49, 0x73, 0x9d, 0xce,
|
|
||||||
0xe3, 0x3f, 0x2a, 0x6d, 0xa7, 0x7f, 0x08, 0x83, 0x2a, 0xfd, 0x51, 0x44, 0x98, 0x07, 0x6f, 0xae,
|
|
||||||
0x07, 0xdf, 0x68, 0x65, 0x90, 0x9b, 0x31, 0x0b, 0x02, 0xd2, 0xb2, 0xa3, 0xae, 0xd2, 0xd6, 0x07,
|
|
||||||
0x77, 0x4f, 0xa4, 0xbd, 0xbe, 0xeb, 0x62, 0x3e, 0x63, 0x1f, 0xf9, 0x9c, 0x74, 0xec, 0xe8, 0xaa,
|
|
||||||
0x82, 0x33, 0xcf, 0xd3, 0x18, 0x5b, 0x2b, 0x1c, 0xc0, 0xde, 0x4a, 0xd1, 0xe4, 0xf7, 0x40, 0xf0,
|
|
||||||
0xf7, 0xb8, 0x20, 0x5d, 0x7a, 0x02, 0xc7, 0x55, 0xf2, 0x4a, 0x62, 0xa8, 0xa4, 0xe0, 0xf6, 0x3c,
|
|
||||||
0x63, 0x95, 0x48, 0x43, 0x7a, 0xeb, 0xd5, 0x0b, 0xd1, 0xa5, 0xb4, 0x3d, 0xdb, 0xa2, 0x43, 0x38,
|
|
||||||
0x5c, 0x29, 0xc1, 0xe2, 0x38, 0xf2, 0x35, 0x8b, 0xd3, 0xbb, 0x49, 0xde, 0xd0, 0x1f, 0xe0, 0xa4,
|
|
||||||
0xaa, 0xf8, 0x20, 0xe7, 0x52, 0x3d, 0xca, 0x73, 0xd4, 0xe2, 0x81, 0xd9, 0xcb, 0x75, 0xc3, 0x8c,
|
|
||||||
0x4f, 0xbe, 0x71, 0xba, 0xd0, 0xc9, 0x84, 0x67, 0x7c, 0xee, 0xfc, 0x5b, 0x03, 0x62, 0x2d, 0xca,
|
|
||||||
0x8c, 0x16, 0x4f, 0x85, 0xf1, 0xee, 0xa0, 0x69, 0x16, 0x51, 0x61, 0xbc, 0x5f, 0x5f, 0x31, 0xde,
|
|
||||||
0x6a, 0xe8, 0x1a, 0x90, 0xd9, 0xcf, 0x66, 0x73, 0xfe, 0x84, 0xfe, 0x4b, 0xac, 0x3d, 0xda, 0x4b,
|
|
||||||
0xf8, 0x6c, 0x9c, 0x68, 0x8d, 0xd2, 0x90, 0x1a, 0xfd, 0x1e, 0x8e, 0x5e, 0x54, 0x4c, 0xf1, 0x71,
|
|
||||||
0x22, 0x74, 0x6c, 0x48, 0xdd, 0x1a, 0xf3, 0x6b, 0x92, 0x5b, 0xe4, 0x4a, 0x7a, 0xa4, 0xe1, 0x0c,
|
|
||||||
0xa1, 0xf7, 0xac, 0x39, 0xe3, 0x73, 0x4a, 0xa0, 0x11, 0x09, 0x39, 0xa8, 0x0d, 0xeb, 0xa7, 0x1d,
|
|
||||||
0xd7, 0xfe, 0x3a, 0x3f, 0xc1, 0xf6, 0xb2, 0xaf, 0x45, 0x37, 0x0e, 0xa0, 0xa3, 0xe4, 0xcc, 0x4b,
|
|
||||||
0x1d, 0x96, 0xb6, 0xa4, 0xed, 0xb6, 0x95, 0xcc, 0x1c, 0xe7, 0x5c, 0xc0, 0xd6, 0x32, 0xc2, 0x26,
|
|
||||||
0x7d, 0x0b, 0x10, 0x3d, 0x03, 0xf9, 0xdb, 0x5d, 0x42, 0x68, 0x1f, 0x36, 0x62, 0xc3, 0x4c, 0xf6,
|
|
||||||
0xd8, 0xf6, 0xdc, 0x6c, 0xe1, 0x8c, 0x60, 0x77, 0x99, 0xe6, 0xd6, 0x42, 0x45, 0xf5, 0x67, 0x7d,
|
|
||||||
0xad, 0xac, 0xef, 0x03, 0x5d, 0xd1, 0xdb, 0x61, 0xfe, 0x55, 0x03, 0xf8, 0xed, 0x7c, 0xaa, 0xbc,
|
|
||||||
0xec, 0xbd, 0xee, 0xc3, 0x86, 0x87, 0x91, 0xf1, 0xd3, 0x13, 0x6e, 0xb9, 0xd9, 0x82, 0x0e, 0xa1,
|
|
||||||
0xfb, 0x49, 0xc8, 0x7b, 0xd4, 0x91, 0x16, 0xd2, 0x0c, 0xea, 0x29, 0x57, 0x86, 0xec, 0x81, 0xb9,
|
|
||||||
0x2f, 0x02, 0x6f, 0x26, 0x93, 0x70, 0xd0, 0x48, 0xf9, 0x76, 0x0a, 0x4c, 0x93, 0x90, 0x1e, 0x01,
|
|
||||||
0x70, 0x9f, 0x09, 0x39, 0x4b, 0x9f, 0xa6, 0xe6, 0xb0, 0x7e, 0xda, 0x73, 0x3b, 0x29, 0x32, 0xb6,
|
|
||||||
0x6f, 0xcc, 0x31, 0x74, 0xa3, 0xd4, 0x6f, 0x38, 0x9b, 0xe3, 0x62, 0xb0, 0x91, 0x6e, 0x1a, 0x72,
|
|
||||||
0xe8, 0x3d, 0x2e, 0x6c, 0x7c, 0x94, 0xde, 0x8e, 0x94, 0xdf, 0x4c, 0xf9, 0x4e, 0x54, 0xdc, 0x97,
|
|
||||||
0x2f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x7d, 0x20, 0xa6, 0x35, 0x07, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
// This file originates from the SatoshiLabs Trezor `common` repository at:
|
|
||||||
// https://github.com/trezor/trezor-common/blob/master/protob/messages-common.proto
|
|
||||||
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
package hw.trezor.messages.common;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Success of the previous request
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message Success {
|
|
||||||
optional string message = 1; // human readable description of action or request-specific payload
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Failure of the previous request
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message Failure {
|
|
||||||
optional FailureType code = 1; // computer-readable definition of the error state
|
|
||||||
optional string message = 2; // human-readable message of the error state
|
|
||||||
enum FailureType {
|
|
||||||
Failure_UnexpectedMessage = 1;
|
|
||||||
Failure_ButtonExpected = 2;
|
|
||||||
Failure_DataError = 3;
|
|
||||||
Failure_ActionCancelled = 4;
|
|
||||||
Failure_PinExpected = 5;
|
|
||||||
Failure_PinCancelled = 6;
|
|
||||||
Failure_PinInvalid = 7;
|
|
||||||
Failure_InvalidSignature = 8;
|
|
||||||
Failure_ProcessError = 9;
|
|
||||||
Failure_NotEnoughFunds = 10;
|
|
||||||
Failure_NotInitialized = 11;
|
|
||||||
Failure_PinMismatch = 12;
|
|
||||||
Failure_FirmwareError = 99;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device is waiting for HW button press.
|
|
||||||
* @auxstart
|
|
||||||
* @next ButtonAck
|
|
||||||
*/
|
|
||||||
message ButtonRequest {
|
|
||||||
optional ButtonRequestType code = 1;
|
|
||||||
optional string data = 2;
|
|
||||||
/**
|
|
||||||
* Type of button request
|
|
||||||
*/
|
|
||||||
enum ButtonRequestType {
|
|
||||||
ButtonRequest_Other = 1;
|
|
||||||
ButtonRequest_FeeOverThreshold = 2;
|
|
||||||
ButtonRequest_ConfirmOutput = 3;
|
|
||||||
ButtonRequest_ResetDevice = 4;
|
|
||||||
ButtonRequest_ConfirmWord = 5;
|
|
||||||
ButtonRequest_WipeDevice = 6;
|
|
||||||
ButtonRequest_ProtectCall = 7;
|
|
||||||
ButtonRequest_SignTx = 8;
|
|
||||||
ButtonRequest_FirmwareCheck = 9;
|
|
||||||
ButtonRequest_Address = 10;
|
|
||||||
ButtonRequest_PublicKey = 11;
|
|
||||||
ButtonRequest_MnemonicWordCount = 12;
|
|
||||||
ButtonRequest_MnemonicInput = 13;
|
|
||||||
ButtonRequest_PassphraseType = 14;
|
|
||||||
ButtonRequest_UnknownDerivationPath = 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Computer agrees to wait for HW button press
|
|
||||||
* @auxend
|
|
||||||
*/
|
|
||||||
message ButtonAck {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
|
|
||||||
* @auxstart
|
|
||||||
* @next PinMatrixAck
|
|
||||||
*/
|
|
||||||
message PinMatrixRequest {
|
|
||||||
optional PinMatrixRequestType type = 1;
|
|
||||||
/**
|
|
||||||
* Type of PIN request
|
|
||||||
*/
|
|
||||||
enum PinMatrixRequestType {
|
|
||||||
PinMatrixRequestType_Current = 1;
|
|
||||||
PinMatrixRequestType_NewFirst = 2;
|
|
||||||
PinMatrixRequestType_NewSecond = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Computer responds with encoded PIN
|
|
||||||
* @auxend
|
|
||||||
*/
|
|
||||||
message PinMatrixAck {
|
|
||||||
required string pin = 1; // matrix encoded PIN entered by user
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device awaits encryption passphrase
|
|
||||||
* @auxstart
|
|
||||||
* @next PassphraseAck
|
|
||||||
*/
|
|
||||||
message PassphraseRequest {
|
|
||||||
optional bool on_device = 1; // passphrase is being entered on the device
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Send passphrase back
|
|
||||||
* @next PassphraseStateRequest
|
|
||||||
*/
|
|
||||||
message PassphraseAck {
|
|
||||||
optional string passphrase = 1;
|
|
||||||
optional bytes state = 2; // expected device state
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device awaits passphrase state
|
|
||||||
* @next PassphraseStateAck
|
|
||||||
*/
|
|
||||||
message PassphraseStateRequest {
|
|
||||||
optional bytes state = 1; // actual device state
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Send passphrase state back
|
|
||||||
* @auxend
|
|
||||||
*/
|
|
||||||
message PassphraseStateAck {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structure representing BIP32 (hierarchical deterministic) node
|
|
||||||
* Used for imports of private key into the device and exporting public key out of device
|
|
||||||
* @embed
|
|
||||||
*/
|
|
||||||
message HDNodeType {
|
|
||||||
required uint32 depth = 1;
|
|
||||||
required uint32 fingerprint = 2;
|
|
||||||
required uint32 child_num = 3;
|
|
||||||
required bytes chain_code = 4;
|
|
||||||
optional bytes private_key = 5;
|
|
||||||
optional bytes public_key = 6;
|
|
||||||
}
|
|
||||||
@@ -1,698 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// source: messages-ethereum.proto
|
|
||||||
|
|
||||||
package trezor
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
math "math"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Ask device for public key corresponding to address_n path
|
|
||||||
// @start
|
|
||||||
// @next EthereumPublicKey
|
|
||||||
// @next Failure
|
|
||||||
type EthereumGetPublicKey struct {
|
|
||||||
AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
|
|
||||||
ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetPublicKey) Reset() { *m = EthereumGetPublicKey{} }
|
|
||||||
func (m *EthereumGetPublicKey) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumGetPublicKey) ProtoMessage() {}
|
|
||||||
func (*EthereumGetPublicKey) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetPublicKey) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumGetPublicKey.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumGetPublicKey.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetPublicKey) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumGetPublicKey.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetPublicKey) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumGetPublicKey.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetPublicKey) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumGetPublicKey.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumGetPublicKey proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumGetPublicKey) GetAddressN() []uint32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressN
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetPublicKey) GetShowDisplay() bool {
|
|
||||||
if m != nil && m.ShowDisplay != nil {
|
|
||||||
return *m.ShowDisplay
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Contains public key derived from device private seed
|
|
||||||
// @end
|
|
||||||
type EthereumPublicKey struct {
|
|
||||||
Node *HDNodeType `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"`
|
|
||||||
Xpub *string `protobuf:"bytes,2,opt,name=xpub" json:"xpub,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumPublicKey) Reset() { *m = EthereumPublicKey{} }
|
|
||||||
func (m *EthereumPublicKey) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumPublicKey) ProtoMessage() {}
|
|
||||||
func (*EthereumPublicKey) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumPublicKey) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumPublicKey.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumPublicKey.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumPublicKey) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumPublicKey.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumPublicKey) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumPublicKey.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumPublicKey) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumPublicKey.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumPublicKey proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumPublicKey) GetNode() *HDNodeType {
|
|
||||||
if m != nil {
|
|
||||||
return m.Node
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumPublicKey) GetXpub() string {
|
|
||||||
if m != nil && m.Xpub != nil {
|
|
||||||
return *m.Xpub
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Ask device for Ethereum address corresponding to address_n path
|
|
||||||
// @start
|
|
||||||
// @next EthereumAddress
|
|
||||||
// @next Failure
|
|
||||||
type EthereumGetAddress struct {
|
|
||||||
AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
|
|
||||||
ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetAddress) Reset() { *m = EthereumGetAddress{} }
|
|
||||||
func (m *EthereumGetAddress) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumGetAddress) ProtoMessage() {}
|
|
||||||
func (*EthereumGetAddress) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetAddress) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumGetAddress.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumGetAddress.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetAddress) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumGetAddress.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetAddress) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumGetAddress.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumGetAddress) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumGetAddress.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumGetAddress proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumGetAddress) GetAddressN() []uint32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressN
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumGetAddress) GetShowDisplay() bool {
|
|
||||||
if m != nil && m.ShowDisplay != nil {
|
|
||||||
return *m.ShowDisplay
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Contains an Ethereum address derived from device private seed
|
|
||||||
// @end
|
|
||||||
type EthereumAddress struct {
|
|
||||||
AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
|
|
||||||
AddressHex *string `protobuf:"bytes,2,opt,name=addressHex" json:"addressHex,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumAddress) Reset() { *m = EthereumAddress{} }
|
|
||||||
func (m *EthereumAddress) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumAddress) ProtoMessage() {}
|
|
||||||
func (*EthereumAddress) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumAddress) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumAddress.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumAddress.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumAddress) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumAddress.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumAddress) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumAddress.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumAddress) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumAddress.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumAddress proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumAddress) GetAddressBin() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressBin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumAddress) GetAddressHex() string {
|
|
||||||
if m != nil && m.AddressHex != nil {
|
|
||||||
return *m.AddressHex
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Ask device to sign transaction
|
|
||||||
// All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
|
|
||||||
// Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
|
|
||||||
// @start
|
|
||||||
// @next EthereumTxRequest
|
|
||||||
// @next Failure
|
|
||||||
type EthereumSignTx struct {
|
|
||||||
AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
|
|
||||||
Nonce []byte `protobuf:"bytes,2,opt,name=nonce" json:"nonce,omitempty"`
|
|
||||||
GasPrice []byte `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice" json:"gas_price,omitempty"`
|
|
||||||
GasLimit []byte `protobuf:"bytes,4,opt,name=gas_limit,json=gasLimit" json:"gas_limit,omitempty"`
|
|
||||||
ToBin []byte `protobuf:"bytes,5,opt,name=toBin" json:"toBin,omitempty"`
|
|
||||||
ToHex *string `protobuf:"bytes,11,opt,name=toHex" json:"toHex,omitempty"`
|
|
||||||
Value []byte `protobuf:"bytes,6,opt,name=value" json:"value,omitempty"`
|
|
||||||
DataInitialChunk []byte `protobuf:"bytes,7,opt,name=data_initial_chunk,json=dataInitialChunk" json:"data_initial_chunk,omitempty"`
|
|
||||||
DataLength *uint32 `protobuf:"varint,8,opt,name=data_length,json=dataLength" json:"data_length,omitempty"`
|
|
||||||
ChainId *uint32 `protobuf:"varint,9,opt,name=chain_id,json=chainId" json:"chain_id,omitempty"`
|
|
||||||
TxType *uint32 `protobuf:"varint,10,opt,name=tx_type,json=txType" json:"tx_type,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) Reset() { *m = EthereumSignTx{} }
|
|
||||||
func (m *EthereumSignTx) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumSignTx) ProtoMessage() {}
|
|
||||||
func (*EthereumSignTx) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumSignTx.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumSignTx.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignTx) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumSignTx.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignTx) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumSignTx.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignTx) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumSignTx.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumSignTx proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetAddressN() []uint32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressN
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetNonce() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Nonce
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetGasPrice() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.GasPrice
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetGasLimit() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.GasLimit
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetToBin() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.ToBin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetToHex() string {
|
|
||||||
if m != nil && m.ToHex != nil {
|
|
||||||
return *m.ToHex
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetValue() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetDataInitialChunk() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.DataInitialChunk
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetDataLength() uint32 {
|
|
||||||
if m != nil && m.DataLength != nil {
|
|
||||||
return *m.DataLength
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetChainId() uint32 {
|
|
||||||
if m != nil && m.ChainId != nil {
|
|
||||||
return *m.ChainId
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignTx) GetTxType() uint32 {
|
|
||||||
if m != nil && m.TxType != nil {
|
|
||||||
return *m.TxType
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Device asks for more data from transaction payload, or returns the signature.
|
|
||||||
// If data_length is set, device awaits that many more bytes of payload.
|
|
||||||
// Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
|
|
||||||
// @end
|
|
||||||
// @next EthereumTxAck
|
|
||||||
type EthereumTxRequest struct {
|
|
||||||
DataLength *uint32 `protobuf:"varint,1,opt,name=data_length,json=dataLength" json:"data_length,omitempty"`
|
|
||||||
SignatureV *uint32 `protobuf:"varint,2,opt,name=signature_v,json=signatureV" json:"signature_v,omitempty"`
|
|
||||||
SignatureR []byte `protobuf:"bytes,3,opt,name=signature_r,json=signatureR" json:"signature_r,omitempty"`
|
|
||||||
SignatureS []byte `protobuf:"bytes,4,opt,name=signature_s,json=signatureS" json:"signature_s,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) Reset() { *m = EthereumTxRequest{} }
|
|
||||||
func (m *EthereumTxRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumTxRequest) ProtoMessage() {}
|
|
||||||
func (*EthereumTxRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{5}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumTxRequest.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumTxRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumTxRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumTxRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumTxRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumTxRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) GetDataLength() uint32 {
|
|
||||||
if m != nil && m.DataLength != nil {
|
|
||||||
return *m.DataLength
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) GetSignatureV() uint32 {
|
|
||||||
if m != nil && m.SignatureV != nil {
|
|
||||||
return *m.SignatureV
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) GetSignatureR() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.SignatureR
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxRequest) GetSignatureS() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.SignatureS
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Transaction payload data.
|
|
||||||
// @next EthereumTxRequest
|
|
||||||
type EthereumTxAck struct {
|
|
||||||
DataChunk []byte `protobuf:"bytes,1,opt,name=data_chunk,json=dataChunk" json:"data_chunk,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxAck) Reset() { *m = EthereumTxAck{} }
|
|
||||||
func (m *EthereumTxAck) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumTxAck) ProtoMessage() {}
|
|
||||||
func (*EthereumTxAck) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{6}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumTxAck) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumTxAck.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumTxAck.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxAck) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumTxAck.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxAck) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumTxAck.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumTxAck) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumTxAck.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumTxAck proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumTxAck) GetDataChunk() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.DataChunk
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Ask device to sign message
|
|
||||||
// @start
|
|
||||||
// @next EthereumMessageSignature
|
|
||||||
// @next Failure
|
|
||||||
type EthereumSignMessage struct {
|
|
||||||
AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
|
|
||||||
Message []byte `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignMessage) Reset() { *m = EthereumSignMessage{} }
|
|
||||||
func (m *EthereumSignMessage) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumSignMessage) ProtoMessage() {}
|
|
||||||
func (*EthereumSignMessage) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{7}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignMessage) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumSignMessage.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumSignMessage.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignMessage) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumSignMessage.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignMessage) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumSignMessage.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumSignMessage) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumSignMessage.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumSignMessage proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumSignMessage) GetAddressN() []uint32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressN
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumSignMessage) GetMessage() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Message
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Response: Signed message
|
|
||||||
// @end
|
|
||||||
type EthereumMessageSignature struct {
|
|
||||||
AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
|
|
||||||
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
|
|
||||||
AddressHex *string `protobuf:"bytes,3,opt,name=addressHex" json:"addressHex,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumMessageSignature) Reset() { *m = EthereumMessageSignature{} }
|
|
||||||
func (m *EthereumMessageSignature) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumMessageSignature) ProtoMessage() {}
|
|
||||||
func (*EthereumMessageSignature) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{8}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumMessageSignature) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumMessageSignature.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumMessageSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumMessageSignature.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumMessageSignature) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumMessageSignature.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumMessageSignature) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumMessageSignature.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumMessageSignature) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumMessageSignature.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumMessageSignature proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumMessageSignature) GetAddressBin() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressBin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumMessageSignature) GetSignature() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Signature
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumMessageSignature) GetAddressHex() string {
|
|
||||||
if m != nil && m.AddressHex != nil {
|
|
||||||
return *m.AddressHex
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Request: Ask device to verify message
|
|
||||||
// @start
|
|
||||||
// @next Success
|
|
||||||
// @next Failure
|
|
||||||
type EthereumVerifyMessage struct {
|
|
||||||
AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
|
|
||||||
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
|
|
||||||
Message []byte `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"`
|
|
||||||
AddressHex *string `protobuf:"bytes,4,opt,name=addressHex" json:"addressHex,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) Reset() { *m = EthereumVerifyMessage{} }
|
|
||||||
func (m *EthereumVerifyMessage) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*EthereumVerifyMessage) ProtoMessage() {}
|
|
||||||
func (*EthereumVerifyMessage) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_cb33f46ba915f15c, []int{9}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_EthereumVerifyMessage.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *EthereumVerifyMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_EthereumVerifyMessage.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *EthereumVerifyMessage) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_EthereumVerifyMessage.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *EthereumVerifyMessage) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_EthereumVerifyMessage.Size(m)
|
|
||||||
}
|
|
||||||
func (m *EthereumVerifyMessage) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_EthereumVerifyMessage.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_EthereumVerifyMessage proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) GetAddressBin() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.AddressBin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) GetSignature() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Signature
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) GetMessage() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Message
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *EthereumVerifyMessage) GetAddressHex() string {
|
|
||||||
if m != nil && m.AddressHex != nil {
|
|
||||||
return *m.AddressHex
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*EthereumGetPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumGetPublicKey")
|
|
||||||
proto.RegisterType((*EthereumPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumPublicKey")
|
|
||||||
proto.RegisterType((*EthereumGetAddress)(nil), "hw.trezor.messages.ethereum.EthereumGetAddress")
|
|
||||||
proto.RegisterType((*EthereumAddress)(nil), "hw.trezor.messages.ethereum.EthereumAddress")
|
|
||||||
proto.RegisterType((*EthereumSignTx)(nil), "hw.trezor.messages.ethereum.EthereumSignTx")
|
|
||||||
proto.RegisterType((*EthereumTxRequest)(nil), "hw.trezor.messages.ethereum.EthereumTxRequest")
|
|
||||||
proto.RegisterType((*EthereumTxAck)(nil), "hw.trezor.messages.ethereum.EthereumTxAck")
|
|
||||||
proto.RegisterType((*EthereumSignMessage)(nil), "hw.trezor.messages.ethereum.EthereumSignMessage")
|
|
||||||
proto.RegisterType((*EthereumMessageSignature)(nil), "hw.trezor.messages.ethereum.EthereumMessageSignature")
|
|
||||||
proto.RegisterType((*EthereumVerifyMessage)(nil), "hw.trezor.messages.ethereum.EthereumVerifyMessage")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("messages-ethereum.proto", fileDescriptor_cb33f46ba915f15c) }
|
|
||||||
|
|
||||||
var fileDescriptor_cb33f46ba915f15c = []byte{
|
|
||||||
// 593 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
|
|
||||||
0x10, 0x95, 0x9b, 0xb4, 0x49, 0x26, 0x0d, 0x1f, 0xa6, 0x55, 0x17, 0x0a, 0x34, 0x18, 0x21, 0xe5,
|
|
||||||
0x00, 0x3e, 0x70, 0x43, 0xe2, 0xd2, 0x52, 0x44, 0x2b, 0x4a, 0x55, 0xdc, 0xa8, 0x57, 0x6b, 0x63,
|
|
||||||
0x6f, 0xe3, 0x55, 0x9d, 0xdd, 0xe0, 0x5d, 0xb7, 0x0e, 0x7f, 0x82, 0x23, 0xff, 0x87, 0x5f, 0x86,
|
|
||||||
0xf6, 0x2b, 0x71, 0x52, 0x54, 0x0e, 0xbd, 0x65, 0xde, 0xbc, 0x7d, 0xf3, 0x66, 0xf4, 0x62, 0xd8,
|
|
||||||
0x99, 0x10, 0x21, 0xf0, 0x98, 0x88, 0x77, 0x44, 0x66, 0xa4, 0x20, 0xe5, 0x24, 0x9c, 0x16, 0x5c,
|
|
||||||
0x72, 0x7f, 0x37, 0xbb, 0x09, 0x65, 0x41, 0x7e, 0xf2, 0x22, 0x74, 0x94, 0xd0, 0x51, 0x9e, 0x6d,
|
|
||||||
0xcf, 0x5f, 0x25, 0x7c, 0x32, 0xe1, 0xcc, 0xbc, 0x09, 0x2e, 0x60, 0xeb, 0xb3, 0xa5, 0x7c, 0x21,
|
|
||||||
0xf2, 0xac, 0x1c, 0xe5, 0x34, 0xf9, 0x4a, 0x66, 0xfe, 0x2e, 0x74, 0x70, 0x9a, 0x16, 0x44, 0x88,
|
|
||||||
0x98, 0x21, 0xaf, 0xdf, 0x18, 0xf4, 0xa2, 0xb6, 0x05, 0x4e, 0xfd, 0x57, 0xb0, 0x29, 0x32, 0x7e,
|
|
||||||
0x13, 0xa7, 0x54, 0x4c, 0x73, 0x3c, 0x43, 0x6b, 0x7d, 0x6f, 0xd0, 0x8e, 0xba, 0x0a, 0x3b, 0x34,
|
|
||||||
0x50, 0x30, 0x82, 0xc7, 0x4e, 0x77, 0x21, 0xfa, 0x01, 0x9a, 0x8c, 0xa7, 0x04, 0x79, 0x7d, 0x6f,
|
|
||||||
0xd0, 0x7d, 0xff, 0x26, 0xfc, 0x87, 0x5f, 0x6b, 0xee, 0xe8, 0xf0, 0x94, 0xa7, 0x64, 0x38, 0x9b,
|
|
||||||
0x92, 0x48, 0x3f, 0xf1, 0x7d, 0x68, 0x56, 0xd3, 0x72, 0xa4, 0x47, 0x75, 0x22, 0xfd, 0x3b, 0x18,
|
|
||||||
0x82, 0x5f, 0xf3, 0xbe, 0x6f, 0xdc, 0xdd, 0xdb, 0xf9, 0x77, 0x78, 0xe8, 0x54, 0x9d, 0xe4, 0x4b,
|
|
||||||
0x00, 0xab, 0x70, 0x40, 0x99, 0x76, 0xbf, 0x19, 0xd5, 0x90, 0x5a, 0xff, 0x88, 0x54, 0xd6, 0x62,
|
|
||||||
0x0d, 0x09, 0xfe, 0xac, 0xc1, 0x03, 0xa7, 0x79, 0x4e, 0xc7, 0x6c, 0x58, 0xdd, 0xed, 0x72, 0x0b,
|
|
||||||
0xd6, 0x19, 0x67, 0x09, 0xd1, 0x52, 0x9b, 0x91, 0x29, 0xd4, 0x93, 0x31, 0x16, 0xf1, 0xb4, 0xa0,
|
|
||||||
0x09, 0x41, 0x0d, 0xdd, 0x69, 0x8f, 0xb1, 0x38, 0x53, 0xb5, 0x6b, 0xe6, 0x74, 0x42, 0x25, 0x6a,
|
|
||||||
0xce, 0x9b, 0x27, 0xaa, 0x56, 0x7a, 0x92, 0x2b, 0xeb, 0xeb, 0x46, 0x4f, 0x17, 0x06, 0x55, 0x86,
|
|
||||||
0xbb, 0xda, 0xb0, 0x29, 0x14, 0x7a, 0x8d, 0xf3, 0x92, 0xa0, 0x0d, 0xc3, 0xd5, 0x85, 0xff, 0x16,
|
|
||||||
0xfc, 0x14, 0x4b, 0x1c, 0x53, 0x46, 0x25, 0xc5, 0x79, 0x9c, 0x64, 0x25, 0xbb, 0x42, 0x2d, 0x4d,
|
|
||||||
0x79, 0xa4, 0x3a, 0xc7, 0xa6, 0xf1, 0x49, 0xe1, 0xfe, 0x1e, 0x74, 0x35, 0x3b, 0x27, 0x6c, 0x2c,
|
|
||||||
0x33, 0xd4, 0xee, 0x7b, 0x83, 0x5e, 0x04, 0x0a, 0x3a, 0xd1, 0x88, 0xff, 0x14, 0xda, 0x49, 0x86,
|
|
||||||
0x29, 0x8b, 0x69, 0x8a, 0x3a, 0xba, 0xdb, 0xd2, 0xf5, 0x71, 0xea, 0xef, 0x40, 0x4b, 0x56, 0xb1,
|
|
||||||
0x9c, 0x4d, 0x09, 0x02, 0xdd, 0xd9, 0x90, 0x95, 0xca, 0x41, 0xf0, 0xdb, 0x5b, 0x44, 0x6a, 0x58,
|
|
||||||
0x45, 0xe4, 0x47, 0x49, 0x84, 0x5c, 0x1d, 0xe5, 0xdd, 0x1a, 0xb5, 0x07, 0x5d, 0x41, 0xc7, 0x0c,
|
|
||||||
0xcb, 0xb2, 0x20, 0xf1, 0xb5, 0xbe, 0x68, 0x2f, 0x82, 0x39, 0x74, 0xb1, 0x4c, 0x28, 0xec, 0x61,
|
|
||||||
0x17, 0x84, 0x68, 0x99, 0x20, 0xec, 0x71, 0x17, 0x84, 0xf3, 0x20, 0x84, 0xde, 0xc2, 0xd8, 0x7e,
|
|
||||||
0x72, 0xe5, 0xbf, 0x00, 0xed, 0xc0, 0x5e, 0xc9, 0xe4, 0xa5, 0xa3, 0x10, 0x7d, 0x9e, 0xe0, 0x04,
|
|
||||||
0x9e, 0xd4, 0xd3, 0xf0, 0xcd, 0x64, 0xff, 0xee, 0x48, 0x20, 0x68, 0xd9, 0xff, 0x88, 0x0d, 0x85,
|
|
||||||
0x2b, 0x83, 0x0a, 0x90, 0x53, 0xb3, 0x4a, 0xe7, 0xce, 0xda, 0x7f, 0x83, 0xfb, 0x1c, 0x3a, 0xf3,
|
|
||||||
0x3d, 0xac, 0xee, 0x02, 0x58, 0x89, 0x75, 0xe3, 0x56, 0xac, 0x7f, 0x79, 0xb0, 0xed, 0x46, 0x5f,
|
|
||||||
0x90, 0x82, 0x5e, 0xce, 0xdc, 0x2a, 0xf7, 0x9b, 0x5b, 0xdb, 0xb5, 0xb1, 0xb4, 0xeb, 0x8a, 0xa3,
|
|
||||||
0xe6, 0xaa, 0xa3, 0x83, 0x8f, 0xf0, 0x3a, 0xe1, 0x93, 0x50, 0x60, 0xc9, 0x45, 0x46, 0x73, 0x3c,
|
|
||||||
0x12, 0xee, 0x03, 0x93, 0xd3, 0x91, 0xf9, 0xe2, 0x8d, 0xca, 0xcb, 0x83, 0xed, 0xa1, 0x06, 0xad,
|
|
||||||
0x5b, 0xb7, 0xc2, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xce, 0x81, 0xc8, 0x59, 0x05, 0x00,
|
|
||||||
0x00,
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
// This file originates from the SatoshiLabs Trezor `common` repository at:
|
|
||||||
// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum.proto
|
|
||||||
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
package hw.trezor.messages.ethereum;
|
|
||||||
|
|
||||||
// Sugar for easier handling in Java
|
|
||||||
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
|
||||||
option java_outer_classname = "TrezorMessageEthereum";
|
|
||||||
|
|
||||||
import "messages-common.proto";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device for public key corresponding to address_n path
|
|
||||||
* @start
|
|
||||||
* @next EthereumPublicKey
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message EthereumGetPublicKey {
|
|
||||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
|
||||||
optional bool show_display = 2; // optionally show on display before sending the result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Contains public key derived from device private seed
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message EthereumPublicKey {
|
|
||||||
optional hw.trezor.messages.common.HDNodeType node = 1; // BIP32 public node
|
|
||||||
optional string xpub = 2; // serialized form of public node
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device for Ethereum address corresponding to address_n path
|
|
||||||
* @start
|
|
||||||
* @next EthereumAddress
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message EthereumGetAddress {
|
|
||||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
|
||||||
optional bool show_display = 2; // optionally show on display before sending the result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Contains an Ethereum address derived from device private seed
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message EthereumAddress {
|
|
||||||
optional bytes addressBin = 1; // Ethereum address as 20 bytes (legacy firmwares)
|
|
||||||
optional string addressHex = 2; // Ethereum address as hex string (newer firmwares)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device to sign transaction
|
|
||||||
* All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
|
|
||||||
* Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
|
|
||||||
* @start
|
|
||||||
* @next EthereumTxRequest
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message EthereumSignTx {
|
|
||||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
|
||||||
optional bytes nonce = 2; // <=256 bit unsigned big endian
|
|
||||||
optional bytes gas_price = 3; // <=256 bit unsigned big endian (in wei)
|
|
||||||
optional bytes gas_limit = 4; // <=256 bit unsigned big endian
|
|
||||||
optional bytes toBin = 5; // recipient address (20 bytes, legacy firmware)
|
|
||||||
optional string toHex = 11; // recipient address (hex string, newer firmware)
|
|
||||||
optional bytes value = 6; // <=256 bit unsigned big endian (in wei)
|
|
||||||
optional bytes data_initial_chunk = 7; // The initial data chunk (<= 1024 bytes)
|
|
||||||
optional uint32 data_length = 8; // Length of transaction payload
|
|
||||||
optional uint32 chain_id = 9; // Chain Id for EIP 155
|
|
||||||
optional uint32 tx_type = 10; // (only for Wanchain)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device asks for more data from transaction payload, or returns the signature.
|
|
||||||
* If data_length is set, device awaits that many more bytes of payload.
|
|
||||||
* Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
|
|
||||||
* @end
|
|
||||||
* @next EthereumTxAck
|
|
||||||
*/
|
|
||||||
message EthereumTxRequest {
|
|
||||||
optional uint32 data_length = 1; // Number of bytes being requested (<= 1024)
|
|
||||||
optional uint32 signature_v = 2; // Computed signature (recovery parameter, limited to 27 or 28)
|
|
||||||
optional bytes signature_r = 3; // Computed signature R component (256 bit)
|
|
||||||
optional bytes signature_s = 4; // Computed signature S component (256 bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Transaction payload data.
|
|
||||||
* @next EthereumTxRequest
|
|
||||||
*/
|
|
||||||
message EthereumTxAck {
|
|
||||||
optional bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device to sign message
|
|
||||||
* @start
|
|
||||||
* @next EthereumMessageSignature
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message EthereumSignMessage {
|
|
||||||
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
|
|
||||||
optional bytes message = 2; // message to be signed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Signed message
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message EthereumMessageSignature {
|
|
||||||
optional bytes addressBin = 1; // address used to sign the message (20 bytes, legacy firmware)
|
|
||||||
optional bytes signature = 2; // signature of the message
|
|
||||||
optional string addressHex = 3; // address used to sign the message (hex string, newer firmware)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device to verify message
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message EthereumVerifyMessage {
|
|
||||||
optional bytes addressBin = 1; // address to verify (20 bytes, legacy firmware)
|
|
||||||
optional bytes signature = 2; // signature to verify
|
|
||||||
optional bytes message = 3; // message to verify
|
|
||||||
optional string addressHex = 4; // address to verify (hex string, newer firmware)
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,289 +0,0 @@
|
|||||||
// This file originates from the SatoshiLabs Trezor `common` repository at:
|
|
||||||
// https://github.com/trezor/trezor-common/blob/master/protob/messages-management.proto
|
|
||||||
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
package hw.trezor.messages.management;
|
|
||||||
|
|
||||||
// Sugar for easier handling in Java
|
|
||||||
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
|
||||||
option java_outer_classname = "TrezorMessageManagement";
|
|
||||||
|
|
||||||
import "messages-common.proto";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Reset device to default state and ask for device details
|
|
||||||
* @start
|
|
||||||
* @next Features
|
|
||||||
*/
|
|
||||||
message Initialize {
|
|
||||||
optional bytes state = 1; // assumed device state, clear session if set and different
|
|
||||||
optional bool skip_passphrase = 2; // this session should always assume empty passphrase
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask for device details (no device reset)
|
|
||||||
* @start
|
|
||||||
* @next Features
|
|
||||||
*/
|
|
||||||
message GetFeatures {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Reports various information about the device
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message Features {
|
|
||||||
optional string vendor = 1; // name of the manufacturer, e.g. "trezor.io"
|
|
||||||
optional uint32 major_version = 2; // major version of the firmware/bootloader, e.g. 1
|
|
||||||
optional uint32 minor_version = 3; // minor version of the firmware/bootloader, e.g. 0
|
|
||||||
optional uint32 patch_version = 4; // patch version of the firmware/bootloader, e.g. 0
|
|
||||||
optional bool bootloader_mode = 5; // is device in bootloader mode?
|
|
||||||
optional string device_id = 6; // device's unique identifier
|
|
||||||
optional bool pin_protection = 7; // is device protected by PIN?
|
|
||||||
optional bool passphrase_protection = 8; // is node/mnemonic encrypted using passphrase?
|
|
||||||
optional string language = 9; // device language
|
|
||||||
optional string label = 10; // device description label
|
|
||||||
optional bool initialized = 12; // does device contain seed?
|
|
||||||
optional bytes revision = 13; // SCM revision of firmware
|
|
||||||
optional bytes bootloader_hash = 14; // hash of the bootloader
|
|
||||||
optional bool imported = 15; // was storage imported from an external source?
|
|
||||||
optional bool pin_cached = 16; // is PIN already cached in session?
|
|
||||||
optional bool passphrase_cached = 17; // is passphrase already cached in session?
|
|
||||||
optional bool firmware_present = 18; // is valid firmware loaded?
|
|
||||||
optional bool needs_backup = 19; // does storage need backup? (equals to Storage.needs_backup)
|
|
||||||
optional uint32 flags = 20; // device flags (equals to Storage.flags)
|
|
||||||
optional string model = 21; // device hardware model
|
|
||||||
optional uint32 fw_major = 22; // reported firmware version if in bootloader mode
|
|
||||||
optional uint32 fw_minor = 23; // reported firmware version if in bootloader mode
|
|
||||||
optional uint32 fw_patch = 24; // reported firmware version if in bootloader mode
|
|
||||||
optional string fw_vendor = 25; // reported firmware vendor if in bootloader mode
|
|
||||||
optional bytes fw_vendor_keys = 26; // reported firmware vendor keys (their hash)
|
|
||||||
optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
|
|
||||||
optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: clear session (removes cached PIN, passphrase, etc).
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
*/
|
|
||||||
message ClearSession {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: change language and/or label of the device
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message ApplySettings {
|
|
||||||
optional string language = 1;
|
|
||||||
optional string label = 2;
|
|
||||||
optional bool use_passphrase = 3;
|
|
||||||
optional bytes homescreen = 4;
|
|
||||||
optional PassphraseSourceType passphrase_source = 5;
|
|
||||||
optional uint32 auto_lock_delay_ms = 6;
|
|
||||||
optional uint32 display_rotation = 7; // in degrees from North
|
|
||||||
/**
|
|
||||||
* Structure representing passphrase source
|
|
||||||
*/
|
|
||||||
enum PassphraseSourceType {
|
|
||||||
ASK = 0;
|
|
||||||
DEVICE = 1;
|
|
||||||
HOST = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: set flags of the device
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message ApplyFlags {
|
|
||||||
optional uint32 flags = 1; // bitmask, can only set bits, not unset
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Starts workflow for setting/changing/removing the PIN
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message ChangePin {
|
|
||||||
optional bool remove = 1; // is PIN removal requested?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Test if the device is alive, device sends back the message in Success response
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
*/
|
|
||||||
message Ping {
|
|
||||||
optional string message = 1; // message to send back in Success message
|
|
||||||
optional bool button_protection = 2; // ask for button press
|
|
||||||
optional bool pin_protection = 3; // ask for PIN if set in device
|
|
||||||
optional bool passphrase_protection = 4; // ask for passphrase if set in device
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Abort last operation that required user interaction
|
|
||||||
* @start
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message Cancel {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Request a sample of random data generated by hardware RNG. May be used for testing.
|
|
||||||
* @start
|
|
||||||
* @next Entropy
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message GetEntropy {
|
|
||||||
required uint32 size = 1; // size of requested entropy
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Reply with random data generated by internal RNG
|
|
||||||
* @end
|
|
||||||
*/
|
|
||||||
message Entropy {
|
|
||||||
required bytes entropy = 1; // chunk of random generated bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Request device to wipe all sensitive data and settings
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message WipeDevice {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Load seed and related internal settings from the computer
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message LoadDevice {
|
|
||||||
optional string mnemonic = 1; // seed encoded as BIP-39 mnemonic (12, 18 or 24 words)
|
|
||||||
optional hw.trezor.messages.common.HDNodeType node = 2; // BIP-32 node
|
|
||||||
optional string pin = 3; // set PIN protection
|
|
||||||
optional bool passphrase_protection = 4; // enable master node encryption using passphrase
|
|
||||||
optional string language = 5 [default='english']; // device language
|
|
||||||
optional string label = 6; // device label
|
|
||||||
optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum
|
|
||||||
optional uint32 u2f_counter = 8; // U2F counter
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Ask device to do initialization involving user interaction
|
|
||||||
* @start
|
|
||||||
* @next EntropyRequest
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message ResetDevice {
|
|
||||||
optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy
|
|
||||||
optional uint32 strength = 2 [default=256]; // strength of seed in bits
|
|
||||||
optional bool passphrase_protection = 3; // enable master node encryption using passphrase
|
|
||||||
optional bool pin_protection = 4; // enable PIN protection
|
|
||||||
optional string language = 5 [default='english']; // device language
|
|
||||||
optional string label = 6; // device label
|
|
||||||
optional uint32 u2f_counter = 7; // U2F counter
|
|
||||||
optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow
|
|
||||||
optional bool no_backup = 9; // indicate that no backup is going to be made
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Perform backup of the device seed if not backed up using ResetDevice
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
*/
|
|
||||||
message BackupDevice {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Ask for additional entropy from host computer
|
|
||||||
* @next EntropyAck
|
|
||||||
*/
|
|
||||||
message EntropyRequest {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Provide additional entropy for seed generation function
|
|
||||||
* @next Success
|
|
||||||
*/
|
|
||||||
message EntropyAck {
|
|
||||||
optional bytes entropy = 1; // 256 bits (32 bytes) of random data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Start recovery workflow asking user for specific words of mnemonic
|
|
||||||
* Used to recovery device safely even on untrusted computer.
|
|
||||||
* @start
|
|
||||||
* @next WordRequest
|
|
||||||
*/
|
|
||||||
message RecoveryDevice {
|
|
||||||
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic
|
|
||||||
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
|
|
||||||
optional bool pin_protection = 3; // enable PIN protection
|
|
||||||
optional string language = 4 [default='english']; // device language
|
|
||||||
optional string label = 5; // device label
|
|
||||||
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process
|
|
||||||
// 7 reserved for unused recovery method
|
|
||||||
optional RecoveryDeviceType type = 8; // supported recovery type
|
|
||||||
optional uint32 u2f_counter = 9; // U2F counter
|
|
||||||
optional bool dry_run = 10; // perform dry-run recovery workflow (for safe mnemonic validation)
|
|
||||||
/**
|
|
||||||
* Type of recovery procedure. These should be used as bitmask, e.g.,
|
|
||||||
* `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
|
|
||||||
* listing every method supported by the host computer.
|
|
||||||
*
|
|
||||||
* Note that ScrambledWords must be supported by every implementation
|
|
||||||
* for backward compatibility; there is no way to not support it.
|
|
||||||
*/
|
|
||||||
enum RecoveryDeviceType {
|
|
||||||
// use powers of two when extending this field
|
|
||||||
RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
|
|
||||||
RecoveryDeviceType_Matrix = 1; // matrix recovery type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response: Device is waiting for user to enter word of the mnemonic
|
|
||||||
* Its position is shown only on device's internal display.
|
|
||||||
* @next WordAck
|
|
||||||
*/
|
|
||||||
message WordRequest {
|
|
||||||
optional WordRequestType type = 1;
|
|
||||||
/**
|
|
||||||
* Type of Recovery Word request
|
|
||||||
*/
|
|
||||||
enum WordRequestType {
|
|
||||||
WordRequestType_Plain = 0;
|
|
||||||
WordRequestType_Matrix9 = 1;
|
|
||||||
WordRequestType_Matrix6 = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Computer replies with word from the mnemonic
|
|
||||||
* @next WordRequest
|
|
||||||
* @next Success
|
|
||||||
* @next Failure
|
|
||||||
*/
|
|
||||||
message WordAck {
|
|
||||||
required string word = 1; // one word of mnemonic on asked position
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request: Set U2F counter
|
|
||||||
* @start
|
|
||||||
* @next Success
|
|
||||||
*/
|
|
||||||
message SetU2FCounter {
|
|
||||||
optional uint32 u2f_counter = 1; // counter
|
|
||||||
}
|
|
||||||
@@ -1,889 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// source: messages.proto
|
|
||||||
|
|
||||||
package trezor
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
math "math"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
// *
|
|
||||||
// Mapping between TREZOR wire identifier (uint) and a protobuf message
|
|
||||||
type MessageType int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Management
|
|
||||||
MessageType_MessageType_Initialize MessageType = 0
|
|
||||||
MessageType_MessageType_Ping MessageType = 1
|
|
||||||
MessageType_MessageType_Success MessageType = 2
|
|
||||||
MessageType_MessageType_Failure MessageType = 3
|
|
||||||
MessageType_MessageType_ChangePin MessageType = 4
|
|
||||||
MessageType_MessageType_WipeDevice MessageType = 5
|
|
||||||
MessageType_MessageType_GetEntropy MessageType = 9
|
|
||||||
MessageType_MessageType_Entropy MessageType = 10
|
|
||||||
MessageType_MessageType_LoadDevice MessageType = 13
|
|
||||||
MessageType_MessageType_ResetDevice MessageType = 14
|
|
||||||
MessageType_MessageType_Features MessageType = 17
|
|
||||||
MessageType_MessageType_PinMatrixRequest MessageType = 18
|
|
||||||
MessageType_MessageType_PinMatrixAck MessageType = 19
|
|
||||||
MessageType_MessageType_Cancel MessageType = 20
|
|
||||||
MessageType_MessageType_ClearSession MessageType = 24
|
|
||||||
MessageType_MessageType_ApplySettings MessageType = 25
|
|
||||||
MessageType_MessageType_ButtonRequest MessageType = 26
|
|
||||||
MessageType_MessageType_ButtonAck MessageType = 27
|
|
||||||
MessageType_MessageType_ApplyFlags MessageType = 28
|
|
||||||
MessageType_MessageType_BackupDevice MessageType = 34
|
|
||||||
MessageType_MessageType_EntropyRequest MessageType = 35
|
|
||||||
MessageType_MessageType_EntropyAck MessageType = 36
|
|
||||||
MessageType_MessageType_PassphraseRequest MessageType = 41
|
|
||||||
MessageType_MessageType_PassphraseAck MessageType = 42
|
|
||||||
MessageType_MessageType_PassphraseStateRequest MessageType = 77
|
|
||||||
MessageType_MessageType_PassphraseStateAck MessageType = 78
|
|
||||||
MessageType_MessageType_RecoveryDevice MessageType = 45
|
|
||||||
MessageType_MessageType_WordRequest MessageType = 46
|
|
||||||
MessageType_MessageType_WordAck MessageType = 47
|
|
||||||
MessageType_MessageType_GetFeatures MessageType = 55
|
|
||||||
MessageType_MessageType_SetU2FCounter MessageType = 63
|
|
||||||
// Bootloader
|
|
||||||
MessageType_MessageType_FirmwareErase MessageType = 6
|
|
||||||
MessageType_MessageType_FirmwareUpload MessageType = 7
|
|
||||||
MessageType_MessageType_FirmwareRequest MessageType = 8
|
|
||||||
MessageType_MessageType_SelfTest MessageType = 32
|
|
||||||
// Bitcoin
|
|
||||||
MessageType_MessageType_GetPublicKey MessageType = 11
|
|
||||||
MessageType_MessageType_PublicKey MessageType = 12
|
|
||||||
MessageType_MessageType_SignTx MessageType = 15
|
|
||||||
MessageType_MessageType_TxRequest MessageType = 21
|
|
||||||
MessageType_MessageType_TxAck MessageType = 22
|
|
||||||
MessageType_MessageType_GetAddress MessageType = 29
|
|
||||||
MessageType_MessageType_Address MessageType = 30
|
|
||||||
MessageType_MessageType_SignMessage MessageType = 38
|
|
||||||
MessageType_MessageType_VerifyMessage MessageType = 39
|
|
||||||
MessageType_MessageType_MessageSignature MessageType = 40
|
|
||||||
// Crypto
|
|
||||||
MessageType_MessageType_CipherKeyValue MessageType = 23
|
|
||||||
MessageType_MessageType_CipheredKeyValue MessageType = 48
|
|
||||||
MessageType_MessageType_SignIdentity MessageType = 53
|
|
||||||
MessageType_MessageType_SignedIdentity MessageType = 54
|
|
||||||
MessageType_MessageType_GetECDHSessionKey MessageType = 61
|
|
||||||
MessageType_MessageType_ECDHSessionKey MessageType = 62
|
|
||||||
MessageType_MessageType_CosiCommit MessageType = 71
|
|
||||||
MessageType_MessageType_CosiCommitment MessageType = 72
|
|
||||||
MessageType_MessageType_CosiSign MessageType = 73
|
|
||||||
MessageType_MessageType_CosiSignature MessageType = 74
|
|
||||||
// Debug
|
|
||||||
MessageType_MessageType_DebugLinkDecision MessageType = 100
|
|
||||||
MessageType_MessageType_DebugLinkGetState MessageType = 101
|
|
||||||
MessageType_MessageType_DebugLinkState MessageType = 102
|
|
||||||
MessageType_MessageType_DebugLinkStop MessageType = 103
|
|
||||||
MessageType_MessageType_DebugLinkLog MessageType = 104
|
|
||||||
MessageType_MessageType_DebugLinkMemoryRead MessageType = 110
|
|
||||||
MessageType_MessageType_DebugLinkMemory MessageType = 111
|
|
||||||
MessageType_MessageType_DebugLinkMemoryWrite MessageType = 112
|
|
||||||
MessageType_MessageType_DebugLinkFlashErase MessageType = 113
|
|
||||||
// Ethereum
|
|
||||||
MessageType_MessageType_EthereumGetPublicKey MessageType = 450
|
|
||||||
MessageType_MessageType_EthereumPublicKey MessageType = 451
|
|
||||||
MessageType_MessageType_EthereumGetAddress MessageType = 56
|
|
||||||
MessageType_MessageType_EthereumAddress MessageType = 57
|
|
||||||
MessageType_MessageType_EthereumSignTx MessageType = 58
|
|
||||||
MessageType_MessageType_EthereumTxRequest MessageType = 59
|
|
||||||
MessageType_MessageType_EthereumTxAck MessageType = 60
|
|
||||||
MessageType_MessageType_EthereumSignMessage MessageType = 64
|
|
||||||
MessageType_MessageType_EthereumVerifyMessage MessageType = 65
|
|
||||||
MessageType_MessageType_EthereumMessageSignature MessageType = 66
|
|
||||||
// NEM
|
|
||||||
MessageType_MessageType_NEMGetAddress MessageType = 67
|
|
||||||
MessageType_MessageType_NEMAddress MessageType = 68
|
|
||||||
MessageType_MessageType_NEMSignTx MessageType = 69
|
|
||||||
MessageType_MessageType_NEMSignedTx MessageType = 70
|
|
||||||
MessageType_MessageType_NEMDecryptMessage MessageType = 75
|
|
||||||
MessageType_MessageType_NEMDecryptedMessage MessageType = 76
|
|
||||||
// Lisk
|
|
||||||
MessageType_MessageType_LiskGetAddress MessageType = 114
|
|
||||||
MessageType_MessageType_LiskAddress MessageType = 115
|
|
||||||
MessageType_MessageType_LiskSignTx MessageType = 116
|
|
||||||
MessageType_MessageType_LiskSignedTx MessageType = 117
|
|
||||||
MessageType_MessageType_LiskSignMessage MessageType = 118
|
|
||||||
MessageType_MessageType_LiskMessageSignature MessageType = 119
|
|
||||||
MessageType_MessageType_LiskVerifyMessage MessageType = 120
|
|
||||||
MessageType_MessageType_LiskGetPublicKey MessageType = 121
|
|
||||||
MessageType_MessageType_LiskPublicKey MessageType = 122
|
|
||||||
// Tezos
|
|
||||||
MessageType_MessageType_TezosGetAddress MessageType = 150
|
|
||||||
MessageType_MessageType_TezosAddress MessageType = 151
|
|
||||||
MessageType_MessageType_TezosSignTx MessageType = 152
|
|
||||||
MessageType_MessageType_TezosSignedTx MessageType = 153
|
|
||||||
MessageType_MessageType_TezosGetPublicKey MessageType = 154
|
|
||||||
MessageType_MessageType_TezosPublicKey MessageType = 155
|
|
||||||
// Stellar
|
|
||||||
MessageType_MessageType_StellarSignTx MessageType = 202
|
|
||||||
MessageType_MessageType_StellarTxOpRequest MessageType = 203
|
|
||||||
MessageType_MessageType_StellarGetAddress MessageType = 207
|
|
||||||
MessageType_MessageType_StellarAddress MessageType = 208
|
|
||||||
MessageType_MessageType_StellarCreateAccountOp MessageType = 210
|
|
||||||
MessageType_MessageType_StellarPaymentOp MessageType = 211
|
|
||||||
MessageType_MessageType_StellarPathPaymentOp MessageType = 212
|
|
||||||
MessageType_MessageType_StellarManageOfferOp MessageType = 213
|
|
||||||
MessageType_MessageType_StellarCreatePassiveOfferOp MessageType = 214
|
|
||||||
MessageType_MessageType_StellarSetOptionsOp MessageType = 215
|
|
||||||
MessageType_MessageType_StellarChangeTrustOp MessageType = 216
|
|
||||||
MessageType_MessageType_StellarAllowTrustOp MessageType = 217
|
|
||||||
MessageType_MessageType_StellarAccountMergeOp MessageType = 218
|
|
||||||
// omitted: StellarInflationOp is not a supported operation, would be 219
|
|
||||||
MessageType_MessageType_StellarManageDataOp MessageType = 220
|
|
||||||
MessageType_MessageType_StellarBumpSequenceOp MessageType = 221
|
|
||||||
MessageType_MessageType_StellarSignedTx MessageType = 230
|
|
||||||
// TRON
|
|
||||||
MessageType_MessageType_TronGetAddress MessageType = 250
|
|
||||||
MessageType_MessageType_TronAddress MessageType = 251
|
|
||||||
MessageType_MessageType_TronSignTx MessageType = 252
|
|
||||||
MessageType_MessageType_TronSignedTx MessageType = 253
|
|
||||||
// Cardano
|
|
||||||
// dropped Sign/VerifyMessage ids 300-302
|
|
||||||
MessageType_MessageType_CardanoSignTx MessageType = 303
|
|
||||||
MessageType_MessageType_CardanoTxRequest MessageType = 304
|
|
||||||
MessageType_MessageType_CardanoGetPublicKey MessageType = 305
|
|
||||||
MessageType_MessageType_CardanoPublicKey MessageType = 306
|
|
||||||
MessageType_MessageType_CardanoGetAddress MessageType = 307
|
|
||||||
MessageType_MessageType_CardanoAddress MessageType = 308
|
|
||||||
MessageType_MessageType_CardanoTxAck MessageType = 309
|
|
||||||
MessageType_MessageType_CardanoSignedTx MessageType = 310
|
|
||||||
// Ontology
|
|
||||||
MessageType_MessageType_OntologyGetAddress MessageType = 350
|
|
||||||
MessageType_MessageType_OntologyAddress MessageType = 351
|
|
||||||
MessageType_MessageType_OntologyGetPublicKey MessageType = 352
|
|
||||||
MessageType_MessageType_OntologyPublicKey MessageType = 353
|
|
||||||
MessageType_MessageType_OntologySignTransfer MessageType = 354
|
|
||||||
MessageType_MessageType_OntologySignedTransfer MessageType = 355
|
|
||||||
MessageType_MessageType_OntologySignWithdrawOng MessageType = 356
|
|
||||||
MessageType_MessageType_OntologySignedWithdrawOng MessageType = 357
|
|
||||||
MessageType_MessageType_OntologySignOntIdRegister MessageType = 358
|
|
||||||
MessageType_MessageType_OntologySignedOntIdRegister MessageType = 359
|
|
||||||
MessageType_MessageType_OntologySignOntIdAddAttributes MessageType = 360
|
|
||||||
MessageType_MessageType_OntologySignedOntIdAddAttributes MessageType = 361
|
|
||||||
// Ripple
|
|
||||||
MessageType_MessageType_RippleGetAddress MessageType = 400
|
|
||||||
MessageType_MessageType_RippleAddress MessageType = 401
|
|
||||||
MessageType_MessageType_RippleSignTx MessageType = 402
|
|
||||||
MessageType_MessageType_RippleSignedTx MessageType = 403
|
|
||||||
// Monero
|
|
||||||
MessageType_MessageType_MoneroTransactionInitRequest MessageType = 501
|
|
||||||
MessageType_MessageType_MoneroTransactionInitAck MessageType = 502
|
|
||||||
MessageType_MessageType_MoneroTransactionSetInputRequest MessageType = 503
|
|
||||||
MessageType_MessageType_MoneroTransactionSetInputAck MessageType = 504
|
|
||||||
MessageType_MessageType_MoneroTransactionInputsPermutationRequest MessageType = 505
|
|
||||||
MessageType_MessageType_MoneroTransactionInputsPermutationAck MessageType = 506
|
|
||||||
MessageType_MessageType_MoneroTransactionInputViniRequest MessageType = 507
|
|
||||||
MessageType_MessageType_MoneroTransactionInputViniAck MessageType = 508
|
|
||||||
MessageType_MessageType_MoneroTransactionAllInputsSetRequest MessageType = 509
|
|
||||||
MessageType_MessageType_MoneroTransactionAllInputsSetAck MessageType = 510
|
|
||||||
MessageType_MessageType_MoneroTransactionSetOutputRequest MessageType = 511
|
|
||||||
MessageType_MessageType_MoneroTransactionSetOutputAck MessageType = 512
|
|
||||||
MessageType_MessageType_MoneroTransactionAllOutSetRequest MessageType = 513
|
|
||||||
MessageType_MessageType_MoneroTransactionAllOutSetAck MessageType = 514
|
|
||||||
MessageType_MessageType_MoneroTransactionSignInputRequest MessageType = 515
|
|
||||||
MessageType_MessageType_MoneroTransactionSignInputAck MessageType = 516
|
|
||||||
MessageType_MessageType_MoneroTransactionFinalRequest MessageType = 517
|
|
||||||
MessageType_MessageType_MoneroTransactionFinalAck MessageType = 518
|
|
||||||
MessageType_MessageType_MoneroKeyImageExportInitRequest MessageType = 530
|
|
||||||
MessageType_MessageType_MoneroKeyImageExportInitAck MessageType = 531
|
|
||||||
MessageType_MessageType_MoneroKeyImageSyncStepRequest MessageType = 532
|
|
||||||
MessageType_MessageType_MoneroKeyImageSyncStepAck MessageType = 533
|
|
||||||
MessageType_MessageType_MoneroKeyImageSyncFinalRequest MessageType = 534
|
|
||||||
MessageType_MessageType_MoneroKeyImageSyncFinalAck MessageType = 535
|
|
||||||
MessageType_MessageType_MoneroGetAddress MessageType = 540
|
|
||||||
MessageType_MessageType_MoneroAddress MessageType = 541
|
|
||||||
MessageType_MessageType_MoneroGetWatchKey MessageType = 542
|
|
||||||
MessageType_MessageType_MoneroWatchKey MessageType = 543
|
|
||||||
MessageType_MessageType_DebugMoneroDiagRequest MessageType = 546
|
|
||||||
MessageType_MessageType_DebugMoneroDiagAck MessageType = 547
|
|
||||||
MessageType_MessageType_MoneroGetTxKeyRequest MessageType = 550
|
|
||||||
MessageType_MessageType_MoneroGetTxKeyAck MessageType = 551
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshStartRequest MessageType = 552
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshStartAck MessageType = 553
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshStepRequest MessageType = 554
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshStepAck MessageType = 555
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshFinalRequest MessageType = 556
|
|
||||||
MessageType_MessageType_MoneroLiveRefreshFinalAck MessageType = 557
|
|
||||||
// EOS
|
|
||||||
MessageType_MessageType_EosGetPublicKey MessageType = 600
|
|
||||||
MessageType_MessageType_EosPublicKey MessageType = 601
|
|
||||||
MessageType_MessageType_EosSignTx MessageType = 602
|
|
||||||
MessageType_MessageType_EosTxActionRequest MessageType = 603
|
|
||||||
MessageType_MessageType_EosTxActionAck MessageType = 604
|
|
||||||
MessageType_MessageType_EosSignedTx MessageType = 605
|
|
||||||
// Binance
|
|
||||||
MessageType_MessageType_BinanceGetAddress MessageType = 700
|
|
||||||
MessageType_MessageType_BinanceAddress MessageType = 701
|
|
||||||
MessageType_MessageType_BinanceGetPublicKey MessageType = 702
|
|
||||||
MessageType_MessageType_BinancePublicKey MessageType = 703
|
|
||||||
MessageType_MessageType_BinanceSignTx MessageType = 704
|
|
||||||
MessageType_MessageType_BinanceTxRequest MessageType = 705
|
|
||||||
MessageType_MessageType_BinanceTransferMsg MessageType = 706
|
|
||||||
MessageType_MessageType_BinanceOrderMsg MessageType = 707
|
|
||||||
MessageType_MessageType_BinanceCancelMsg MessageType = 708
|
|
||||||
MessageType_MessageType_BinanceSignedTx MessageType = 709
|
|
||||||
)
|
|
||||||
|
|
||||||
var MessageType_name = map[int32]string{
|
|
||||||
0: "MessageType_Initialize",
|
|
||||||
1: "MessageType_Ping",
|
|
||||||
2: "MessageType_Success",
|
|
||||||
3: "MessageType_Failure",
|
|
||||||
4: "MessageType_ChangePin",
|
|
||||||
5: "MessageType_WipeDevice",
|
|
||||||
9: "MessageType_GetEntropy",
|
|
||||||
10: "MessageType_Entropy",
|
|
||||||
13: "MessageType_LoadDevice",
|
|
||||||
14: "MessageType_ResetDevice",
|
|
||||||
17: "MessageType_Features",
|
|
||||||
18: "MessageType_PinMatrixRequest",
|
|
||||||
19: "MessageType_PinMatrixAck",
|
|
||||||
20: "MessageType_Cancel",
|
|
||||||
24: "MessageType_ClearSession",
|
|
||||||
25: "MessageType_ApplySettings",
|
|
||||||
26: "MessageType_ButtonRequest",
|
|
||||||
27: "MessageType_ButtonAck",
|
|
||||||
28: "MessageType_ApplyFlags",
|
|
||||||
34: "MessageType_BackupDevice",
|
|
||||||
35: "MessageType_EntropyRequest",
|
|
||||||
36: "MessageType_EntropyAck",
|
|
||||||
41: "MessageType_PassphraseRequest",
|
|
||||||
42: "MessageType_PassphraseAck",
|
|
||||||
77: "MessageType_PassphraseStateRequest",
|
|
||||||
78: "MessageType_PassphraseStateAck",
|
|
||||||
45: "MessageType_RecoveryDevice",
|
|
||||||
46: "MessageType_WordRequest",
|
|
||||||
47: "MessageType_WordAck",
|
|
||||||
55: "MessageType_GetFeatures",
|
|
||||||
63: "MessageType_SetU2FCounter",
|
|
||||||
6: "MessageType_FirmwareErase",
|
|
||||||
7: "MessageType_FirmwareUpload",
|
|
||||||
8: "MessageType_FirmwareRequest",
|
|
||||||
32: "MessageType_SelfTest",
|
|
||||||
11: "MessageType_GetPublicKey",
|
|
||||||
12: "MessageType_PublicKey",
|
|
||||||
15: "MessageType_SignTx",
|
|
||||||
21: "MessageType_TxRequest",
|
|
||||||
22: "MessageType_TxAck",
|
|
||||||
29: "MessageType_GetAddress",
|
|
||||||
30: "MessageType_Address",
|
|
||||||
38: "MessageType_SignMessage",
|
|
||||||
39: "MessageType_VerifyMessage",
|
|
||||||
40: "MessageType_MessageSignature",
|
|
||||||
23: "MessageType_CipherKeyValue",
|
|
||||||
48: "MessageType_CipheredKeyValue",
|
|
||||||
53: "MessageType_SignIdentity",
|
|
||||||
54: "MessageType_SignedIdentity",
|
|
||||||
61: "MessageType_GetECDHSessionKey",
|
|
||||||
62: "MessageType_ECDHSessionKey",
|
|
||||||
71: "MessageType_CosiCommit",
|
|
||||||
72: "MessageType_CosiCommitment",
|
|
||||||
73: "MessageType_CosiSign",
|
|
||||||
74: "MessageType_CosiSignature",
|
|
||||||
100: "MessageType_DebugLinkDecision",
|
|
||||||
101: "MessageType_DebugLinkGetState",
|
|
||||||
102: "MessageType_DebugLinkState",
|
|
||||||
103: "MessageType_DebugLinkStop",
|
|
||||||
104: "MessageType_DebugLinkLog",
|
|
||||||
110: "MessageType_DebugLinkMemoryRead",
|
|
||||||
111: "MessageType_DebugLinkMemory",
|
|
||||||
112: "MessageType_DebugLinkMemoryWrite",
|
|
||||||
113: "MessageType_DebugLinkFlashErase",
|
|
||||||
450: "MessageType_EthereumGetPublicKey",
|
|
||||||
451: "MessageType_EthereumPublicKey",
|
|
||||||
56: "MessageType_EthereumGetAddress",
|
|
||||||
57: "MessageType_EthereumAddress",
|
|
||||||
58: "MessageType_EthereumSignTx",
|
|
||||||
59: "MessageType_EthereumTxRequest",
|
|
||||||
60: "MessageType_EthereumTxAck",
|
|
||||||
64: "MessageType_EthereumSignMessage",
|
|
||||||
65: "MessageType_EthereumVerifyMessage",
|
|
||||||
66: "MessageType_EthereumMessageSignature",
|
|
||||||
67: "MessageType_NEMGetAddress",
|
|
||||||
68: "MessageType_NEMAddress",
|
|
||||||
69: "MessageType_NEMSignTx",
|
|
||||||
70: "MessageType_NEMSignedTx",
|
|
||||||
75: "MessageType_NEMDecryptMessage",
|
|
||||||
76: "MessageType_NEMDecryptedMessage",
|
|
||||||
114: "MessageType_LiskGetAddress",
|
|
||||||
115: "MessageType_LiskAddress",
|
|
||||||
116: "MessageType_LiskSignTx",
|
|
||||||
117: "MessageType_LiskSignedTx",
|
|
||||||
118: "MessageType_LiskSignMessage",
|
|
||||||
119: "MessageType_LiskMessageSignature",
|
|
||||||
120: "MessageType_LiskVerifyMessage",
|
|
||||||
121: "MessageType_LiskGetPublicKey",
|
|
||||||
122: "MessageType_LiskPublicKey",
|
|
||||||
150: "MessageType_TezosGetAddress",
|
|
||||||
151: "MessageType_TezosAddress",
|
|
||||||
152: "MessageType_TezosSignTx",
|
|
||||||
153: "MessageType_TezosSignedTx",
|
|
||||||
154: "MessageType_TezosGetPublicKey",
|
|
||||||
155: "MessageType_TezosPublicKey",
|
|
||||||
202: "MessageType_StellarSignTx",
|
|
||||||
203: "MessageType_StellarTxOpRequest",
|
|
||||||
207: "MessageType_StellarGetAddress",
|
|
||||||
208: "MessageType_StellarAddress",
|
|
||||||
210: "MessageType_StellarCreateAccountOp",
|
|
||||||
211: "MessageType_StellarPaymentOp",
|
|
||||||
212: "MessageType_StellarPathPaymentOp",
|
|
||||||
213: "MessageType_StellarManageOfferOp",
|
|
||||||
214: "MessageType_StellarCreatePassiveOfferOp",
|
|
||||||
215: "MessageType_StellarSetOptionsOp",
|
|
||||||
216: "MessageType_StellarChangeTrustOp",
|
|
||||||
217: "MessageType_StellarAllowTrustOp",
|
|
||||||
218: "MessageType_StellarAccountMergeOp",
|
|
||||||
220: "MessageType_StellarManageDataOp",
|
|
||||||
221: "MessageType_StellarBumpSequenceOp",
|
|
||||||
230: "MessageType_StellarSignedTx",
|
|
||||||
250: "MessageType_TronGetAddress",
|
|
||||||
251: "MessageType_TronAddress",
|
|
||||||
252: "MessageType_TronSignTx",
|
|
||||||
253: "MessageType_TronSignedTx",
|
|
||||||
303: "MessageType_CardanoSignTx",
|
|
||||||
304: "MessageType_CardanoTxRequest",
|
|
||||||
305: "MessageType_CardanoGetPublicKey",
|
|
||||||
306: "MessageType_CardanoPublicKey",
|
|
||||||
307: "MessageType_CardanoGetAddress",
|
|
||||||
308: "MessageType_CardanoAddress",
|
|
||||||
309: "MessageType_CardanoTxAck",
|
|
||||||
310: "MessageType_CardanoSignedTx",
|
|
||||||
350: "MessageType_OntologyGetAddress",
|
|
||||||
351: "MessageType_OntologyAddress",
|
|
||||||
352: "MessageType_OntologyGetPublicKey",
|
|
||||||
353: "MessageType_OntologyPublicKey",
|
|
||||||
354: "MessageType_OntologySignTransfer",
|
|
||||||
355: "MessageType_OntologySignedTransfer",
|
|
||||||
356: "MessageType_OntologySignWithdrawOng",
|
|
||||||
357: "MessageType_OntologySignedWithdrawOng",
|
|
||||||
358: "MessageType_OntologySignOntIdRegister",
|
|
||||||
359: "MessageType_OntologySignedOntIdRegister",
|
|
||||||
360: "MessageType_OntologySignOntIdAddAttributes",
|
|
||||||
361: "MessageType_OntologySignedOntIdAddAttributes",
|
|
||||||
400: "MessageType_RippleGetAddress",
|
|
||||||
401: "MessageType_RippleAddress",
|
|
||||||
402: "MessageType_RippleSignTx",
|
|
||||||
403: "MessageType_RippleSignedTx",
|
|
||||||
501: "MessageType_MoneroTransactionInitRequest",
|
|
||||||
502: "MessageType_MoneroTransactionInitAck",
|
|
||||||
503: "MessageType_MoneroTransactionSetInputRequest",
|
|
||||||
504: "MessageType_MoneroTransactionSetInputAck",
|
|
||||||
505: "MessageType_MoneroTransactionInputsPermutationRequest",
|
|
||||||
506: "MessageType_MoneroTransactionInputsPermutationAck",
|
|
||||||
507: "MessageType_MoneroTransactionInputViniRequest",
|
|
||||||
508: "MessageType_MoneroTransactionInputViniAck",
|
|
||||||
509: "MessageType_MoneroTransactionAllInputsSetRequest",
|
|
||||||
510: "MessageType_MoneroTransactionAllInputsSetAck",
|
|
||||||
511: "MessageType_MoneroTransactionSetOutputRequest",
|
|
||||||
512: "MessageType_MoneroTransactionSetOutputAck",
|
|
||||||
513: "MessageType_MoneroTransactionAllOutSetRequest",
|
|
||||||
514: "MessageType_MoneroTransactionAllOutSetAck",
|
|
||||||
515: "MessageType_MoneroTransactionSignInputRequest",
|
|
||||||
516: "MessageType_MoneroTransactionSignInputAck",
|
|
||||||
517: "MessageType_MoneroTransactionFinalRequest",
|
|
||||||
518: "MessageType_MoneroTransactionFinalAck",
|
|
||||||
530: "MessageType_MoneroKeyImageExportInitRequest",
|
|
||||||
531: "MessageType_MoneroKeyImageExportInitAck",
|
|
||||||
532: "MessageType_MoneroKeyImageSyncStepRequest",
|
|
||||||
533: "MessageType_MoneroKeyImageSyncStepAck",
|
|
||||||
534: "MessageType_MoneroKeyImageSyncFinalRequest",
|
|
||||||
535: "MessageType_MoneroKeyImageSyncFinalAck",
|
|
||||||
540: "MessageType_MoneroGetAddress",
|
|
||||||
541: "MessageType_MoneroAddress",
|
|
||||||
542: "MessageType_MoneroGetWatchKey",
|
|
||||||
543: "MessageType_MoneroWatchKey",
|
|
||||||
546: "MessageType_DebugMoneroDiagRequest",
|
|
||||||
547: "MessageType_DebugMoneroDiagAck",
|
|
||||||
550: "MessageType_MoneroGetTxKeyRequest",
|
|
||||||
551: "MessageType_MoneroGetTxKeyAck",
|
|
||||||
552: "MessageType_MoneroLiveRefreshStartRequest",
|
|
||||||
553: "MessageType_MoneroLiveRefreshStartAck",
|
|
||||||
554: "MessageType_MoneroLiveRefreshStepRequest",
|
|
||||||
555: "MessageType_MoneroLiveRefreshStepAck",
|
|
||||||
556: "MessageType_MoneroLiveRefreshFinalRequest",
|
|
||||||
557: "MessageType_MoneroLiveRefreshFinalAck",
|
|
||||||
600: "MessageType_EosGetPublicKey",
|
|
||||||
601: "MessageType_EosPublicKey",
|
|
||||||
602: "MessageType_EosSignTx",
|
|
||||||
603: "MessageType_EosTxActionRequest",
|
|
||||||
604: "MessageType_EosTxActionAck",
|
|
||||||
605: "MessageType_EosSignedTx",
|
|
||||||
700: "MessageType_BinanceGetAddress",
|
|
||||||
701: "MessageType_BinanceAddress",
|
|
||||||
702: "MessageType_BinanceGetPublicKey",
|
|
||||||
703: "MessageType_BinancePublicKey",
|
|
||||||
704: "MessageType_BinanceSignTx",
|
|
||||||
705: "MessageType_BinanceTxRequest",
|
|
||||||
706: "MessageType_BinanceTransferMsg",
|
|
||||||
707: "MessageType_BinanceOrderMsg",
|
|
||||||
708: "MessageType_BinanceCancelMsg",
|
|
||||||
709: "MessageType_BinanceSignedTx",
|
|
||||||
}
|
|
||||||
|
|
||||||
var MessageType_value = map[string]int32{
|
|
||||||
"MessageType_Initialize": 0,
|
|
||||||
"MessageType_Ping": 1,
|
|
||||||
"MessageType_Success": 2,
|
|
||||||
"MessageType_Failure": 3,
|
|
||||||
"MessageType_ChangePin": 4,
|
|
||||||
"MessageType_WipeDevice": 5,
|
|
||||||
"MessageType_GetEntropy": 9,
|
|
||||||
"MessageType_Entropy": 10,
|
|
||||||
"MessageType_LoadDevice": 13,
|
|
||||||
"MessageType_ResetDevice": 14,
|
|
||||||
"MessageType_Features": 17,
|
|
||||||
"MessageType_PinMatrixRequest": 18,
|
|
||||||
"MessageType_PinMatrixAck": 19,
|
|
||||||
"MessageType_Cancel": 20,
|
|
||||||
"MessageType_ClearSession": 24,
|
|
||||||
"MessageType_ApplySettings": 25,
|
|
||||||
"MessageType_ButtonRequest": 26,
|
|
||||||
"MessageType_ButtonAck": 27,
|
|
||||||
"MessageType_ApplyFlags": 28,
|
|
||||||
"MessageType_BackupDevice": 34,
|
|
||||||
"MessageType_EntropyRequest": 35,
|
|
||||||
"MessageType_EntropyAck": 36,
|
|
||||||
"MessageType_PassphraseRequest": 41,
|
|
||||||
"MessageType_PassphraseAck": 42,
|
|
||||||
"MessageType_PassphraseStateRequest": 77,
|
|
||||||
"MessageType_PassphraseStateAck": 78,
|
|
||||||
"MessageType_RecoveryDevice": 45,
|
|
||||||
"MessageType_WordRequest": 46,
|
|
||||||
"MessageType_WordAck": 47,
|
|
||||||
"MessageType_GetFeatures": 55,
|
|
||||||
"MessageType_SetU2FCounter": 63,
|
|
||||||
"MessageType_FirmwareErase": 6,
|
|
||||||
"MessageType_FirmwareUpload": 7,
|
|
||||||
"MessageType_FirmwareRequest": 8,
|
|
||||||
"MessageType_SelfTest": 32,
|
|
||||||
"MessageType_GetPublicKey": 11,
|
|
||||||
"MessageType_PublicKey": 12,
|
|
||||||
"MessageType_SignTx": 15,
|
|
||||||
"MessageType_TxRequest": 21,
|
|
||||||
"MessageType_TxAck": 22,
|
|
||||||
"MessageType_GetAddress": 29,
|
|
||||||
"MessageType_Address": 30,
|
|
||||||
"MessageType_SignMessage": 38,
|
|
||||||
"MessageType_VerifyMessage": 39,
|
|
||||||
"MessageType_MessageSignature": 40,
|
|
||||||
"MessageType_CipherKeyValue": 23,
|
|
||||||
"MessageType_CipheredKeyValue": 48,
|
|
||||||
"MessageType_SignIdentity": 53,
|
|
||||||
"MessageType_SignedIdentity": 54,
|
|
||||||
"MessageType_GetECDHSessionKey": 61,
|
|
||||||
"MessageType_ECDHSessionKey": 62,
|
|
||||||
"MessageType_CosiCommit": 71,
|
|
||||||
"MessageType_CosiCommitment": 72,
|
|
||||||
"MessageType_CosiSign": 73,
|
|
||||||
"MessageType_CosiSignature": 74,
|
|
||||||
"MessageType_DebugLinkDecision": 100,
|
|
||||||
"MessageType_DebugLinkGetState": 101,
|
|
||||||
"MessageType_DebugLinkState": 102,
|
|
||||||
"MessageType_DebugLinkStop": 103,
|
|
||||||
"MessageType_DebugLinkLog": 104,
|
|
||||||
"MessageType_DebugLinkMemoryRead": 110,
|
|
||||||
"MessageType_DebugLinkMemory": 111,
|
|
||||||
"MessageType_DebugLinkMemoryWrite": 112,
|
|
||||||
"MessageType_DebugLinkFlashErase": 113,
|
|
||||||
"MessageType_EthereumGetPublicKey": 450,
|
|
||||||
"MessageType_EthereumPublicKey": 451,
|
|
||||||
"MessageType_EthereumGetAddress": 56,
|
|
||||||
"MessageType_EthereumAddress": 57,
|
|
||||||
"MessageType_EthereumSignTx": 58,
|
|
||||||
"MessageType_EthereumTxRequest": 59,
|
|
||||||
"MessageType_EthereumTxAck": 60,
|
|
||||||
"MessageType_EthereumSignMessage": 64,
|
|
||||||
"MessageType_EthereumVerifyMessage": 65,
|
|
||||||
"MessageType_EthereumMessageSignature": 66,
|
|
||||||
"MessageType_NEMGetAddress": 67,
|
|
||||||
"MessageType_NEMAddress": 68,
|
|
||||||
"MessageType_NEMSignTx": 69,
|
|
||||||
"MessageType_NEMSignedTx": 70,
|
|
||||||
"MessageType_NEMDecryptMessage": 75,
|
|
||||||
"MessageType_NEMDecryptedMessage": 76,
|
|
||||||
"MessageType_LiskGetAddress": 114,
|
|
||||||
"MessageType_LiskAddress": 115,
|
|
||||||
"MessageType_LiskSignTx": 116,
|
|
||||||
"MessageType_LiskSignedTx": 117,
|
|
||||||
"MessageType_LiskSignMessage": 118,
|
|
||||||
"MessageType_LiskMessageSignature": 119,
|
|
||||||
"MessageType_LiskVerifyMessage": 120,
|
|
||||||
"MessageType_LiskGetPublicKey": 121,
|
|
||||||
"MessageType_LiskPublicKey": 122,
|
|
||||||
"MessageType_TezosGetAddress": 150,
|
|
||||||
"MessageType_TezosAddress": 151,
|
|
||||||
"MessageType_TezosSignTx": 152,
|
|
||||||
"MessageType_TezosSignedTx": 153,
|
|
||||||
"MessageType_TezosGetPublicKey": 154,
|
|
||||||
"MessageType_TezosPublicKey": 155,
|
|
||||||
"MessageType_StellarSignTx": 202,
|
|
||||||
"MessageType_StellarTxOpRequest": 203,
|
|
||||||
"MessageType_StellarGetAddress": 207,
|
|
||||||
"MessageType_StellarAddress": 208,
|
|
||||||
"MessageType_StellarCreateAccountOp": 210,
|
|
||||||
"MessageType_StellarPaymentOp": 211,
|
|
||||||
"MessageType_StellarPathPaymentOp": 212,
|
|
||||||
"MessageType_StellarManageOfferOp": 213,
|
|
||||||
"MessageType_StellarCreatePassiveOfferOp": 214,
|
|
||||||
"MessageType_StellarSetOptionsOp": 215,
|
|
||||||
"MessageType_StellarChangeTrustOp": 216,
|
|
||||||
"MessageType_StellarAllowTrustOp": 217,
|
|
||||||
"MessageType_StellarAccountMergeOp": 218,
|
|
||||||
"MessageType_StellarManageDataOp": 220,
|
|
||||||
"MessageType_StellarBumpSequenceOp": 221,
|
|
||||||
"MessageType_StellarSignedTx": 230,
|
|
||||||
"MessageType_TronGetAddress": 250,
|
|
||||||
"MessageType_TronAddress": 251,
|
|
||||||
"MessageType_TronSignTx": 252,
|
|
||||||
"MessageType_TronSignedTx": 253,
|
|
||||||
"MessageType_CardanoSignTx": 303,
|
|
||||||
"MessageType_CardanoTxRequest": 304,
|
|
||||||
"MessageType_CardanoGetPublicKey": 305,
|
|
||||||
"MessageType_CardanoPublicKey": 306,
|
|
||||||
"MessageType_CardanoGetAddress": 307,
|
|
||||||
"MessageType_CardanoAddress": 308,
|
|
||||||
"MessageType_CardanoTxAck": 309,
|
|
||||||
"MessageType_CardanoSignedTx": 310,
|
|
||||||
"MessageType_OntologyGetAddress": 350,
|
|
||||||
"MessageType_OntologyAddress": 351,
|
|
||||||
"MessageType_OntologyGetPublicKey": 352,
|
|
||||||
"MessageType_OntologyPublicKey": 353,
|
|
||||||
"MessageType_OntologySignTransfer": 354,
|
|
||||||
"MessageType_OntologySignedTransfer": 355,
|
|
||||||
"MessageType_OntologySignWithdrawOng": 356,
|
|
||||||
"MessageType_OntologySignedWithdrawOng": 357,
|
|
||||||
"MessageType_OntologySignOntIdRegister": 358,
|
|
||||||
"MessageType_OntologySignedOntIdRegister": 359,
|
|
||||||
"MessageType_OntologySignOntIdAddAttributes": 360,
|
|
||||||
"MessageType_OntologySignedOntIdAddAttributes": 361,
|
|
||||||
"MessageType_RippleGetAddress": 400,
|
|
||||||
"MessageType_RippleAddress": 401,
|
|
||||||
"MessageType_RippleSignTx": 402,
|
|
||||||
"MessageType_RippleSignedTx": 403,
|
|
||||||
"MessageType_MoneroTransactionInitRequest": 501,
|
|
||||||
"MessageType_MoneroTransactionInitAck": 502,
|
|
||||||
"MessageType_MoneroTransactionSetInputRequest": 503,
|
|
||||||
"MessageType_MoneroTransactionSetInputAck": 504,
|
|
||||||
"MessageType_MoneroTransactionInputsPermutationRequest": 505,
|
|
||||||
"MessageType_MoneroTransactionInputsPermutationAck": 506,
|
|
||||||
"MessageType_MoneroTransactionInputViniRequest": 507,
|
|
||||||
"MessageType_MoneroTransactionInputViniAck": 508,
|
|
||||||
"MessageType_MoneroTransactionAllInputsSetRequest": 509,
|
|
||||||
"MessageType_MoneroTransactionAllInputsSetAck": 510,
|
|
||||||
"MessageType_MoneroTransactionSetOutputRequest": 511,
|
|
||||||
"MessageType_MoneroTransactionSetOutputAck": 512,
|
|
||||||
"MessageType_MoneroTransactionAllOutSetRequest": 513,
|
|
||||||
"MessageType_MoneroTransactionAllOutSetAck": 514,
|
|
||||||
"MessageType_MoneroTransactionSignInputRequest": 515,
|
|
||||||
"MessageType_MoneroTransactionSignInputAck": 516,
|
|
||||||
"MessageType_MoneroTransactionFinalRequest": 517,
|
|
||||||
"MessageType_MoneroTransactionFinalAck": 518,
|
|
||||||
"MessageType_MoneroKeyImageExportInitRequest": 530,
|
|
||||||
"MessageType_MoneroKeyImageExportInitAck": 531,
|
|
||||||
"MessageType_MoneroKeyImageSyncStepRequest": 532,
|
|
||||||
"MessageType_MoneroKeyImageSyncStepAck": 533,
|
|
||||||
"MessageType_MoneroKeyImageSyncFinalRequest": 534,
|
|
||||||
"MessageType_MoneroKeyImageSyncFinalAck": 535,
|
|
||||||
"MessageType_MoneroGetAddress": 540,
|
|
||||||
"MessageType_MoneroAddress": 541,
|
|
||||||
"MessageType_MoneroGetWatchKey": 542,
|
|
||||||
"MessageType_MoneroWatchKey": 543,
|
|
||||||
"MessageType_DebugMoneroDiagRequest": 546,
|
|
||||||
"MessageType_DebugMoneroDiagAck": 547,
|
|
||||||
"MessageType_MoneroGetTxKeyRequest": 550,
|
|
||||||
"MessageType_MoneroGetTxKeyAck": 551,
|
|
||||||
"MessageType_MoneroLiveRefreshStartRequest": 552,
|
|
||||||
"MessageType_MoneroLiveRefreshStartAck": 553,
|
|
||||||
"MessageType_MoneroLiveRefreshStepRequest": 554,
|
|
||||||
"MessageType_MoneroLiveRefreshStepAck": 555,
|
|
||||||
"MessageType_MoneroLiveRefreshFinalRequest": 556,
|
|
||||||
"MessageType_MoneroLiveRefreshFinalAck": 557,
|
|
||||||
"MessageType_EosGetPublicKey": 600,
|
|
||||||
"MessageType_EosPublicKey": 601,
|
|
||||||
"MessageType_EosSignTx": 602,
|
|
||||||
"MessageType_EosTxActionRequest": 603,
|
|
||||||
"MessageType_EosTxActionAck": 604,
|
|
||||||
"MessageType_EosSignedTx": 605,
|
|
||||||
"MessageType_BinanceGetAddress": 700,
|
|
||||||
"MessageType_BinanceAddress": 701,
|
|
||||||
"MessageType_BinanceGetPublicKey": 702,
|
|
||||||
"MessageType_BinancePublicKey": 703,
|
|
||||||
"MessageType_BinanceSignTx": 704,
|
|
||||||
"MessageType_BinanceTxRequest": 705,
|
|
||||||
"MessageType_BinanceTransferMsg": 706,
|
|
||||||
"MessageType_BinanceOrderMsg": 707,
|
|
||||||
"MessageType_BinanceCancelMsg": 708,
|
|
||||||
"MessageType_BinanceSignedTx": 709,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x MessageType) Enum() *MessageType {
|
|
||||||
p := new(MessageType)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x MessageType) String() string {
|
|
||||||
return proto.EnumName(MessageType_name, int32(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *MessageType) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(MessageType_value, data, "MessageType")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = MessageType(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (MessageType) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_4dc296cbfe5ffcd5, []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireIn = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50002,
|
|
||||||
Name: "hw.trezor.messages.wire_in",
|
|
||||||
Tag: "varint,50002,opt,name=wire_in",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireOut = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50003,
|
|
||||||
Name: "hw.trezor.messages.wire_out",
|
|
||||||
Tag: "varint,50003,opt,name=wire_out",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireDebugIn = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50004,
|
|
||||||
Name: "hw.trezor.messages.wire_debug_in",
|
|
||||||
Tag: "varint,50004,opt,name=wire_debug_in",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireDebugOut = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50005,
|
|
||||||
Name: "hw.trezor.messages.wire_debug_out",
|
|
||||||
Tag: "varint,50005,opt,name=wire_debug_out",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireTiny = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50006,
|
|
||||||
Name: "hw.trezor.messages.wire_tiny",
|
|
||||||
Tag: "varint,50006,opt,name=wire_tiny",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireBootloader = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50007,
|
|
||||||
Name: "hw.trezor.messages.wire_bootloader",
|
|
||||||
Tag: "varint,50007,opt,name=wire_bootloader",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
var E_WireNoFsm = &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*descriptor.EnumValueOptions)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 50008,
|
|
||||||
Name: "hw.trezor.messages.wire_no_fsm",
|
|
||||||
Tag: "varint,50008,opt,name=wire_no_fsm",
|
|
||||||
Filename: "messages.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterEnum("hw.trezor.messages.MessageType", MessageType_name, MessageType_value)
|
|
||||||
proto.RegisterExtension(E_WireIn)
|
|
||||||
proto.RegisterExtension(E_WireOut)
|
|
||||||
proto.RegisterExtension(E_WireDebugIn)
|
|
||||||
proto.RegisterExtension(E_WireDebugOut)
|
|
||||||
proto.RegisterExtension(E_WireTiny)
|
|
||||||
proto.RegisterExtension(E_WireBootloader)
|
|
||||||
proto.RegisterExtension(E_WireNoFsm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("messages.proto", fileDescriptor_4dc296cbfe5ffcd5) }
|
|
||||||
|
|
||||||
var fileDescriptor_4dc296cbfe5ffcd5 = []byte{
|
|
||||||
// 2430 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x9a, 0xd9, 0x73, 0x1c, 0xc5,
|
|
||||||
0x1d, 0xc7, 0xb3, 0xab, 0x11, 0x88, 0xf6, 0x41, 0x23, 0xb0, 0x2d, 0xaf, 0x2f, 0xf9, 0xc0, 0x96,
|
|
||||||
0x2f, 0xd9, 0x10, 0x0c, 0x44, 0x38, 0x60, 0x69, 0xb5, 0x12, 0x8a, 0xb5, 0x5a, 0x97, 0x76, 0xb1,
|
|
||||||
0x1f, 0x5d, 0xa3, 0x9d, 0xd6, 0x6e, 0x97, 0x67, 0x67, 0x86, 0x9e, 0x1e, 0x49, 0xeb, 0xa7, 0x9c,
|
|
||||||
0x3c, 0x13, 0x48, 0xc0, 0xb9, 0xa9, 0xa4, 0x2a, 0x21, 0x57, 0x85, 0x1c, 0x4e, 0x25, 0x55, 0x39,
|
|
||||||
0x08, 0x24, 0x2f, 0xc9, 0x43, 0x52, 0x9c, 0x86, 0x40, 0xee, 0x90, 0xe4, 0x0f, 0xc8, 0xc5, 0x91,
|
|
||||||
0xa4, 0x7a, 0xa6, 0xbb, 0xe7, 0xd8, 0xdf, 0xae, 0x36, 0x6f, 0x58, 0xf3, 0xf9, 0x7d, 0x7f, 0x47,
|
|
||||||
0xff, 0xfa, 0x37, 0xdd, 0xb3, 0xa0, 0xcd, 0x2d, 0xe2, 0xfb, 0x66, 0x83, 0xf8, 0xe3, 0x1e, 0x73,
|
|
||||||
0xb9, 0x3b, 0x3c, 0xdc, 0x5c, 0x1d, 0xe7, 0x8c, 0x5c, 0x76, 0xd9, 0xb8, 0x7a, 0x52, 0x18, 0x6d,
|
|
||||||
0xb8, 0x6e, 0xc3, 0x26, 0x27, 0x42, 0x62, 0x29, 0x58, 0x3e, 0x61, 0x11, 0xbf, 0xce, 0xa8, 0xc7,
|
|
||||||
0x5d, 0x16, 0x59, 0x1d, 0xf9, 0xfe, 0x7d, 0x68, 0x43, 0x39, 0xc2, 0x6b, 0x6d, 0x8f, 0x0c, 0x1f,
|
|
||||||
0x40, 0x5b, 0x13, 0xff, 0xbc, 0x38, 0xe7, 0x50, 0x4e, 0x4d, 0x9b, 0x5e, 0x26, 0xf8, 0x5d, 0x85,
|
|
||||||
0xa1, 0x87, 0xaf, 0x8e, 0xe4, 0x9e, 0xba, 0x3a, 0x92, 0x1b, 0x2e, 0x20, 0x9c, 0xa4, 0xce, 0x51,
|
|
||||||
0xa7, 0x81, 0x73, 0x05, 0x43, 0x3c, 0x1f, 0xde, 0x85, 0x6e, 0x4e, 0x3e, 0xab, 0x06, 0xf5, 0x3a,
|
|
||||||
0xf1, 0x7d, 0x9c, 0x2f, 0x18, 0x57, 0x80, 0xc7, 0x33, 0x26, 0xb5, 0x03, 0x46, 0xf0, 0x80, 0x7c,
|
|
||||||
0xbc, 0x07, 0x6d, 0x49, 0x3e, 0x2e, 0x36, 0x4d, 0xa7, 0x41, 0xce, 0x51, 0x07, 0x1b, 0x52, 0x7e,
|
|
||||||
0x34, 0x1d, 0xe0, 0x05, 0xea, 0x91, 0x69, 0xb2, 0x42, 0xeb, 0x04, 0x0f, 0xc2, 0xc4, 0x2c, 0xe1,
|
|
||||||
0x25, 0x87, 0x33, 0xd7, 0x6b, 0xe3, 0x1b, 0xe0, 0x10, 0xd5, 0x63, 0x24, 0x63, 0xc8, 0x08, 0xcc,
|
|
||||||
0xbb, 0xa6, 0x25, 0x5d, 0x6c, 0x92, 0x02, 0x7b, 0xd1, 0xb6, 0x24, 0xb1, 0x48, 0x7c, 0xc2, 0x25,
|
|
||||||
0xb2, 0x59, 0x22, 0xbb, 0xd1, 0x2d, 0xa9, 0x3c, 0x89, 0xc9, 0x03, 0x46, 0x7c, 0x7c, 0x93, 0x74,
|
|
||||||
0x72, 0x10, 0xed, 0xcc, 0x94, 0xb0, 0x6c, 0x72, 0x46, 0xd7, 0x16, 0xc9, 0x83, 0x01, 0xf1, 0x39,
|
|
||||||
0x1e, 0x96, 0xdc, 0x11, 0x34, 0x02, 0x72, 0x93, 0xf5, 0x4b, 0xf8, 0xe6, 0xc2, 0x46, 0xb5, 0x24,
|
|
||||||
0x4f, 0x47, 0x81, 0x0f, 0xa7, 0x8a, 0x67, 0x3a, 0x75, 0x62, 0xe3, 0x5b, 0x12, 0x0b, 0xb7, 0x2f,
|
|
||||||
0xad, 0x56, 0xb4, 0x89, 0xc9, 0xaa, 0xc4, 0xf7, 0xa9, 0xeb, 0xe0, 0x11, 0x19, 0xf9, 0x7e, 0xb4,
|
|
||||||
0x3d, 0xc9, 0x4c, 0x7a, 0x9e, 0xdd, 0xae, 0x12, 0xce, 0xa9, 0xd3, 0xf0, 0xf1, 0x76, 0x18, 0x9a,
|
|
||||||
0x0a, 0x38, 0x77, 0x1d, 0x15, 0x7b, 0x41, 0xc6, 0x7e, 0x28, 0xbd, 0x98, 0x11, 0x24, 0x02, 0xdf,
|
|
||||||
0xd1, 0x11, 0xf8, 0xd6, 0x0e, 0x97, 0x33, 0xb6, 0xd9, 0xf0, 0xf1, 0x4e, 0xe9, 0x2f, 0x13, 0xf8,
|
|
||||||
0x94, 0x59, 0xbf, 0x14, 0x78, 0xb2, 0xe4, 0xfb, 0x24, 0x73, 0x00, 0x15, 0x80, 0x65, 0x55, 0x41,
|
|
||||||
0xed, 0x87, 0x57, 0x57, 0x52, 0x22, 0xaa, 0x03, 0x52, 0xe7, 0x10, 0xda, 0x95, 0x2a, 0xb9, 0xe9,
|
|
||||||
0xfb, 0x5e, 0x93, 0x99, 0x3e, 0x51, 0x52, 0x87, 0xa5, 0xd4, 0xd1, 0x74, 0x11, 0x62, 0x50, 0xa8,
|
|
||||||
0x1d, 0xc9, 0xe4, 0x78, 0x0c, 0xed, 0x83, 0xe1, 0x2a, 0x37, 0xb9, 0x96, 0x2e, 0x4b, 0xe9, 0x93,
|
|
||||||
0x68, 0x77, 0x0f, 0x5a, 0xe8, 0x2f, 0x64, 0xf4, 0x33, 0xd9, 0x2f, 0x92, 0xba, 0xbb, 0x42, 0x58,
|
|
||||||
0x5b, 0xd6, 0xe8, 0x38, 0xdc, 0xb9, 0x17, 0x5c, 0x66, 0x29, 0xd7, 0xe3, 0xf0, 0x0e, 0x15, 0x88,
|
|
||||||
0xf0, 0x77, 0x02, 0x56, 0x98, 0x25, 0x5c, 0xf7, 0xf6, 0x5d, 0x70, 0x73, 0x54, 0x09, 0x7f, 0xe0,
|
|
||||||
0xf6, 0x99, 0xa2, 0x1b, 0x38, 0x9c, 0x30, 0x7c, 0x9f, 0xae, 0x72, 0x0a, 0x9a, 0xa1, 0xac, 0xb5,
|
|
||||||
0x6a, 0x32, 0x52, 0x12, 0x49, 0xe2, 0xeb, 0xa2, 0x9e, 0xfd, 0x9e, 0x00, 0xc7, 0xd2, 0x89, 0x29,
|
|
||||||
0xf0, 0x01, 0xcf, 0x76, 0x4d, 0x0b, 0x5f, 0x9f, 0x20, 0x0f, 0xa3, 0x1d, 0x10, 0xa9, 0x12, 0x1c,
|
|
||||||
0x2a, 0x0c, 0x5d, 0x51, 0xe8, 0xbe, 0xf4, 0xf6, 0xac, 0x12, 0x7b, 0xb9, 0x26, 0x98, 0xd1, 0x84,
|
|
||||||
0x5c, 0xa6, 0xe7, 0x66, 0x09, 0x3f, 0x17, 0x2c, 0xd9, 0xb4, 0x7e, 0x96, 0xb4, 0xf1, 0x06, 0x99,
|
|
||||||
0x45, 0x66, 0x5e, 0xc5, 0xc0, 0x46, 0x59, 0xcd, 0x9d, 0xe9, 0x3d, 0x59, 0xa5, 0x0d, 0xa7, 0xb6,
|
|
||||||
0x86, 0x6f, 0x84, 0xcd, 0x6b, 0x7a, 0xfb, 0x6f, 0x91, 0xe6, 0x3b, 0xd0, 0x4d, 0x69, 0x40, 0x2c,
|
|
||||||
0xc5, 0xd6, 0xae, 0x93, 0x6e, 0xd2, 0xb2, 0x98, 0x98, 0xb6, 0xbb, 0xe0, 0x49, 0xa7, 0x1e, 0xef,
|
|
||||||
0x96, 0xea, 0x99, 0xb5, 0x14, 0xc1, 0xc9, 0x7f, 0xe3, 0x83, 0xf0, 0x5a, 0x9e, 0x27, 0x8c, 0x2e,
|
|
||||||
0xb7, 0x15, 0x74, 0x48, 0x42, 0x99, 0x61, 0x26, 0xff, 0x5b, 0xc8, 0x85, 0x9d, 0x81, 0xc7, 0xa4,
|
|
||||||
0xbf, 0x4c, 0x8f, 0x16, 0xa9, 0xd7, 0x24, 0xec, 0x2c, 0x69, 0x9f, 0x37, 0xed, 0x80, 0xe0, 0x6d,
|
|
||||||
0xb0, 0x5a, 0x44, 0x11, 0x4b, 0x73, 0x27, 0xa5, 0x5a, 0x66, 0x7d, 0x84, 0xbb, 0x39, 0x8b, 0x38,
|
|
||||||
0x9c, 0xf2, 0x36, 0x3e, 0x05, 0xcf, 0x04, 0xc1, 0x10, 0x4b, 0x53, 0x77, 0xea, 0x41, 0xb5, 0x2b,
|
|
||||||
0xfb, 0xca, 0x28, 0x4e, 0xdf, 0x2f, 0x07, 0xa3, 0x58, 0xcd, 0xf7, 0x76, 0x19, 0x31, 0x69, 0xea,
|
|
||||||
0x5e, 0x78, 0xc4, 0x14, 0x5d, 0x9f, 0x16, 0xdd, 0x56, 0x8b, 0x72, 0x3c, 0x0b, 0xeb, 0xc4, 0x44,
|
|
||||||
0x8b, 0x38, 0x1c, 0xdf, 0x2f, 0x75, 0x32, 0xef, 0x10, 0x41, 0x89, 0x04, 0xf0, 0x1c, 0xbc, 0x36,
|
|
||||||
0xea, 0x79, 0x54, 0xf3, 0xf7, 0x49, 0x91, 0x13, 0xe9, 0xdc, 0xa6, 0xc9, 0x52, 0xd0, 0x98, 0xa7,
|
|
||||||
0xce, 0xa5, 0x69, 0x52, 0xa7, 0xe1, 0xdc, 0xb7, 0x0a, 0x1b, 0x9f, 0x48, 0x0e, 0x92, 0xa3, 0x5d,
|
|
||||||
0x0c, 0x66, 0x09, 0x0f, 0x87, 0x0f, 0x26, 0x85, 0x21, 0x65, 0x90, 0x4d, 0x44, 0xc3, 0x11, 0xb9,
|
|
||||||
0x5c, 0x30, 0x9e, 0x04, 0x02, 0x4d, 0x50, 0xae, 0x87, 0x1b, 0x05, 0xe3, 0x09, 0x60, 0x39, 0x35,
|
|
||||||
0x34, 0xef, 0x36, 0x70, 0x53, 0x0a, 0x1d, 0x46, 0x7b, 0x40, 0xa6, 0x4c, 0x5a, 0x2e, 0x6b, 0x2f,
|
|
||||||
0x12, 0xd3, 0xc2, 0x8e, 0x94, 0xbb, 0x35, 0x3d, 0x0c, 0x32, 0x28, 0x76, 0xa5, 0xe2, 0x11, 0x34,
|
|
||||||
0xda, 0x03, 0xbb, 0xc0, 0x28, 0x27, 0xd8, 0x93, 0x92, 0xdd, 0xbc, 0xcf, 0xd8, 0xa6, 0xdf, 0x8c,
|
|
||||||
0x06, 0xd7, 0x83, 0x12, 0x3d, 0x9a, 0x96, 0x2d, 0x71, 0xd1, 0xc2, 0x41, 0x2b, 0x35, 0x43, 0x9e,
|
|
||||||
0x19, 0x90, 0xeb, 0x38, 0x96, 0xae, 0xb8, 0x82, 0x63, 0xf2, 0x59, 0x75, 0x3c, 0x1a, 0x4b, 0xbf,
|
|
||||||
0x16, 0x12, 0xb2, 0x6a, 0x6b, 0xdf, 0x2d, 0x35, 0x33, 0xe9, 0x2b, 0x52, 0x61, 0xef, 0x81, 0x77,
|
|
||||||
0xa4, 0xc2, 0xe4, 0x98, 0x9a, 0x80, 0xdf, 0x88, 0x8a, 0x8a, 0xc7, 0xd5, 0x3d, 0x52, 0x2e, 0xb3,
|
|
||||||
0xd0, 0x31, 0x28, 0xc6, 0xd6, 0x69, 0xa9, 0x96, 0x29, 0x63, 0xd2, 0xa7, 0x1a, 0x2c, 0x67, 0x24,
|
|
||||||
0x7a, 0x14, 0xed, 0x85, 0xd0, 0xf4, 0x14, 0x9a, 0x94, 0xf0, 0x38, 0x3a, 0x00, 0xc1, 0x1d, 0xd3,
|
|
||||||
0x68, 0x0a, 0x0e, 0x76, 0xa1, 0x54, 0x4e, 0xd4, 0xb1, 0x08, 0xcf, 0xd8, 0x85, 0x52, 0x59, 0x11,
|
|
||||||
0xd3, 0xf0, 0x91, 0x75, 0xa1, 0x54, 0x96, 0xd5, 0x2b, 0xc1, 0x6f, 0x4c, 0x09, 0x10, 0xab, 0xb6,
|
|
||||||
0x86, 0x67, 0xe0, 0x01, 0xb4, 0x50, 0x2a, 0x4f, 0x93, 0x3a, 0x6b, 0x7b, 0x5c, 0xe5, 0x78, 0x16,
|
|
||||||
0xae, 0x5d, 0x0c, 0x12, 0x4b, 0xa1, 0xf3, 0xf0, 0xd2, 0xce, 0x53, 0xff, 0x52, 0x22, 0x3f, 0x06,
|
|
||||||
0x07, 0x27, 0x28, 0x85, 0xf8, 0x5d, 0xce, 0xc3, 0xd4, 0xbf, 0x24, 0x33, 0xe4, 0xf0, 0xe9, 0x4c,
|
|
||||||
0x11, 0x61, 0x8a, 0x81, 0x54, 0xc9, 0x34, 0xa4, 0x62, 0x54, 0xd4, 0x2b, 0x52, 0x2a, 0xb3, 0x1f,
|
|
||||||
0x05, 0xd6, 0xb1, 0x80, 0xab, 0x70, 0xd5, 0x04, 0x9b, 0xee, 0x8c, 0x35, 0xf8, 0x8d, 0x22, 0x4b,
|
|
||||||
0x11, 0xef, 0xaf, 0x36, 0x3c, 0x50, 0x05, 0x17, 0x43, 0x97, 0xf5, 0xc9, 0x3d, 0x95, 0x48, 0x8d,
|
|
||||||
0x5c, 0x76, 0xfd, 0x44, 0x61, 0x1f, 0xcb, 0x69, 0xb1, 0x91, 0x0e, 0x4e, 0x41, 0x8f, 0xe7, 0xf4,
|
|
||||||
0x3b, 0x6c, 0x5b, 0x07, 0x24, 0x8b, 0x7b, 0x25, 0xa7, 0x5f, 0x16, 0xdb, 0x41, 0x26, 0x2c, 0xef,
|
|
||||||
0x27, 0x72, 0x7a, 0x34, 0xec, 0x82, 0xc2, 0x8a, 0xe3, 0xff, 0x64, 0x4e, 0x8f, 0x86, 0x42, 0x07,
|
|
||||||
0x19, 0x63, 0x9f, 0xca, 0xe9, 0xfe, 0x49, 0x9f, 0xe2, 0x38, 0xb1, 0x6d, 0x93, 0xc9, 0xe0, 0x7e,
|
|
||||||
0x9e, 0xd3, 0x0d, 0xb9, 0x1b, 0xa0, 0x6a, 0x6b, 0x15, 0x4f, 0xcd, 0x86, 0x5f, 0x74, 0x89, 0x50,
|
|
||||||
0xa2, 0x89, 0xd2, 0xfd, 0xb2, 0x4b, 0x84, 0x92, 0x54, 0xd8, 0xaf, 0x94, 0xe0, 0xf1, 0xf4, 0x91,
|
|
||||||
0x5a, 0x62, 0x45, 0x46, 0xc2, 0x23, 0x72, 0x5d, 0x1c, 0x38, 0x2b, 0x1e, 0x7e, 0x2e, 0xa7, 0xa7,
|
|
||||||
0xd8, 0x4e, 0x00, 0x3f, 0x67, 0xb6, 0xc5, 0x4b, 0xb7, 0xe2, 0xe1, 0xe7, 0x73, 0x7a, 0xea, 0x8c,
|
|
||||||
0x82, 0x20, 0x6f, 0xc6, 0xf0, 0x0b, 0xbd, 0xe1, 0xb2, 0xe9, 0x98, 0x0d, 0x52, 0x59, 0x5e, 0x26,
|
|
||||||
0xac, 0xe2, 0xe1, 0x17, 0x15, 0x7c, 0x3b, 0x3a, 0xd4, 0x35, 0x62, 0x71, 0xc6, 0xa7, 0x2b, 0xda,
|
|
||||||
0xe6, 0xa5, 0x9c, 0xde, 0x11, 0x7b, 0xa0, 0x75, 0x20, 0xbc, 0xe2, 0x71, 0xea, 0x3a, 0x7e, 0xc5,
|
|
||||||
0xc3, 0x2f, 0xf7, 0x0e, 0x26, 0xba, 0x45, 0xd7, 0x58, 0xe0, 0x8b, 0xc8, 0xaf, 0xf5, 0x16, 0x9e,
|
|
||||||
0xb4, 0x6d, 0x77, 0x55, 0xb1, 0xaf, 0x28, 0xf6, 0x58, 0x7a, 0x10, 0x2b, 0x36, 0x2a, 0x72, 0x99,
|
|
||||||
0xb0, 0x06, 0xa9, 0x78, 0xf8, 0xd5, 0xde, 0xca, 0x51, 0x4d, 0xa6, 0x4d, 0x6e, 0x56, 0x3c, 0xfc,
|
|
||||||
0x5a, 0x6f, 0xe5, 0xa9, 0xa0, 0xe5, 0x55, 0x45, 0x03, 0x39, 0x75, 0xa1, 0xfc, 0x7a, 0x4e, 0xef,
|
|
||||||
0xe4, 0x1d, 0x5d, 0x9a, 0x32, 0xdc, 0x0d, 0x6f, 0xe4, 0xf4, 0xb4, 0x49, 0xf7, 0x38, 0x73, 0x9d,
|
|
||||||
0x44, 0xa3, 0xbd, 0x99, 0xd3, 0x83, 0x6b, 0x5b, 0x16, 0x53, 0xcc, 0x5b, 0x39, 0x7d, 0x48, 0xde,
|
|
||||||
0x9a, 0x65, 0xe4, 0x26, 0x78, 0xbb, 0xdb, 0x56, 0x97, 0x48, 0x18, 0xd2, 0x3b, 0x5d, 0xf6, 0x53,
|
|
||||||
0xd1, 0x64, 0x96, 0xe9, 0xb8, 0x52, 0xea, 0x1b, 0x79, 0xb8, 0x49, 0x25, 0x15, 0xbf, 0x69, 0x9f,
|
|
||||||
0xca, 0xeb, 0x0f, 0x03, 0x7b, 0x00, 0x30, 0xb5, 0xe3, 0xbf, 0xd9, 0x5b, 0x34, 0x06, 0xbf, 0x95,
|
|
||||||
0x87, 0xb7, 0x68, 0x2c, 0xaa, 0xaa, 0xf2, 0xed, 0x3c, 0xbc, 0x45, 0x25, 0xa9, 0xb0, 0xef, 0xe4,
|
|
||||||
0xf5, 0x3b, 0x76, 0x04, 0x4c, 0x47, 0x9c, 0x07, 0xae, 0xe6, 0xe1, 0x45, 0x4d, 0x54, 0x26, 0xac,
|
|
||||||
0xe0, 0x77, 0x95, 0x58, 0x66, 0xd6, 0x54, 0x1c, 0xee, 0xda, 0x6e, 0xa3, 0x9d, 0x08, 0xef, 0x37,
|
|
||||||
0x5d, 0x24, 0x15, 0xaa, 0xb8, 0xdf, 0xe6, 0xf5, 0x15, 0x7e, 0xb4, 0x8b, 0x64, 0x5c, 0x9d, 0xdf,
|
|
||||||
0xe5, 0xe1, 0x73, 0x9a, 0x82, 0x63, 0xf2, 0xf7, 0xeb, 0xc8, 0x86, 0x8b, 0xcd, 0x4c, 0xc7, 0x5f,
|
|
||||||
0x26, 0x0c, 0xff, 0x41, 0xc9, 0x66, 0xc6, 0x58, 0x12, 0x26, 0x96, 0xc6, 0xff, 0xa8, 0xb4, 0xc7,
|
|
||||||
0xd1, 0xfe, 0x6e, 0xf8, 0x05, 0xca, 0x9b, 0x16, 0x33, 0x57, 0x2b, 0x4e, 0x03, 0xff, 0x49, 0xc9,
|
|
||||||
0x9f, 0x44, 0xb7, 0x76, 0x97, 0x4f, 0x5a, 0xfc, 0x39, 0xaf, 0x3f, 0x3e, 0x74, 0xb5, 0xa8, 0x38,
|
|
||||||
0x7c, 0xce, 0x5a, 0x24, 0x0d, 0xea, 0x8b, 0xbb, 0xfc, 0x1b, 0x79, 0x78, 0xae, 0xa5, 0x7d, 0xa4,
|
|
||||||
0x6d, 0xfe, 0xa2, 0xbc, 0x9c, 0x42, 0x47, 0x7a, 0x7a, 0x99, 0xb4, 0xac, 0x49, 0xce, 0x19, 0x5d,
|
|
||||||
0x0a, 0x38, 0xf1, 0xf1, 0x5f, 0x95, 0xab, 0xbb, 0xd0, 0xb1, 0x75, 0x5c, 0xa5, 0x0d, 0xff, 0x96,
|
|
||||||
0xd7, 0xa7, 0x85, 0xd4, 0x26, 0x58, 0xa4, 0x9e, 0x67, 0x93, 0x44, 0xef, 0x3c, 0x3c, 0x00, 0xbf,
|
|
||||||
0x6f, 0x23, 0x50, 0x51, 0x1f, 0x1d, 0x80, 0x3b, 0x3b, 0xa2, 0xe4, 0x6e, 0x7e, 0x64, 0x00, 0xde,
|
|
||||||
0x25, 0x31, 0x14, 0x36, 0xf6, 0xa3, 0x0a, 0x7b, 0x37, 0x1a, 0x4b, 0xdd, 0x9f, 0x5d, 0x87, 0x30,
|
|
||||||
0x37, 0x5c, 0x79, 0xb3, 0x2e, 0x66, 0xfc, 0x9c, 0x43, 0xb9, 0x1a, 0x00, 0x7f, 0x1f, 0xd0, 0x17,
|
|
||||||
0xbb, 0x03, 0xeb, 0x1a, 0x89, 0x6d, 0xf6, 0x0f, 0x65, 0x90, 0xa9, 0x5c, 0x87, 0x41, 0x95, 0xf0,
|
|
||||||
0x39, 0xc7, 0x0b, 0xb4, 0xa7, 0x7f, 0x2a, 0xc3, 0xf5, 0xc2, 0x53, 0x86, 0xc2, 0xdb, 0xbf, 0x94,
|
|
||||||
0xd1, 0x19, 0x74, 0x6a, 0x9d, 0xf0, 0xbc, 0x80, 0xfb, 0xe7, 0x08, 0x6b, 0x05, 0xdc, 0x14, 0x7f,
|
|
||||||
0x50, 0x6e, 0xff, 0xad, 0x14, 0x4e, 0xa3, 0xdb, 0xfe, 0x3f, 0x05, 0xe1, 0xff, 0x4d, 0x65, 0x7d,
|
|
||||||
0x37, 0x3a, 0xbe, 0xbe, 0xf5, 0x79, 0xea, 0x50, 0xe5, 0xf7, 0x2d, 0x65, 0x79, 0x07, 0x3a, 0xdc,
|
|
||||||
0x9f, 0xa5, 0xf0, 0xf7, 0xb6, 0xb2, 0xba, 0x07, 0x9d, 0xec, 0x69, 0x35, 0x69, 0xdb, 0x51, 0xc0,
|
|
||||||
0x55, 0xa2, 0x2b, 0xfc, 0x4e, 0xbf, 0x4b, 0x93, 0x34, 0x16, 0x5e, 0xff, 0xd3, 0x6f, 0x96, 0xe2,
|
|
||||||
0x98, 0x10, 0xf0, 0xc4, 0xa2, 0xfe, 0xb7, 0xdf, 0x2c, 0xb5, 0xa5, 0xf0, 0xf7, 0x7e, 0xa3, 0x4f,
|
|
||||||
0x7f, 0x93, 0xb6, 0x5d, 0x09, 0x78, 0x22, 0xc5, 0x0f, 0x18, 0x7d, 0xfa, 0xd3, 0x96, 0xc2, 0xdf,
|
|
||||||
0x07, 0xfb, 0xf5, 0x17, 0x7e, 0xf4, 0x49, 0x36, 0xed, 0x87, 0xfa, 0xf5, 0xa7, 0x2d, 0x85, 0xbf,
|
|
||||||
0x0f, 0xf7, 0x6b, 0x35, 0x43, 0x1d, 0xd3, 0x56, 0xbe, 0x3e, 0x62, 0xc0, 0x03, 0x13, 0xb6, 0x12,
|
|
||||||
0x7e, 0x1e, 0x52, 0x16, 0x77, 0xa2, 0xa3, 0x9d, 0x16, 0x67, 0x49, 0x7b, 0xae, 0x65, 0x36, 0x48,
|
|
||||||
0x69, 0xcd, 0x73, 0x19, 0x4f, 0x6e, 0xfa, 0x47, 0x94, 0x5d, 0x66, 0xd0, 0x76, 0xb3, 0x13, 0xbe,
|
|
||||||
0x1e, 0xed, 0x99, 0x93, 0xb2, 0xa9, 0xb6, 0x9d, 0x7a, 0x95, 0x13, 0x7d, 0x5a, 0xff, 0x58, 0xcf,
|
|
||||||
0x9c, 0xb2, 0x56, 0xc2, 0xcf, 0xc7, 0x0d, 0x78, 0xa0, 0x77, 0x5a, 0xa4, 0x8a, 0xf7, 0x98, 0x32,
|
|
||||||
0xbb, 0x0d, 0x1d, 0xec, 0xc3, 0x4c, 0x78, 0x7a, 0xdc, 0x80, 0x47, 0x79, 0x64, 0x92, 0x18, 0xe5,
|
|
||||||
0x9f, 0x36, 0xe0, 0x51, 0x1e, 0x81, 0x8a, 0xfa, 0x8c, 0x01, 0x9f, 0x7a, 0xb4, 0xdc, 0x05, 0x93,
|
|
||||||
0xd7, 0x9b, 0xe2, 0xbd, 0xfe, 0x59, 0x03, 0x9e, 0xe7, 0x11, 0xa9, 0xb1, 0xcf, 0x19, 0xf0, 0xc5,
|
|
||||||
0x24, 0xfc, 0x50, 0x14, 0xb1, 0xd3, 0xd4, 0x6c, 0xa8, 0x0a, 0x7c, 0xde, 0x80, 0xef, 0x50, 0x19,
|
|
||||||
0x5c, 0x64, 0xfe, 0x05, 0xa5, 0x9c, 0x39, 0x2d, 0xeb, 0x50, 0x6b, 0x6b, 0x67, 0x89, 0xfe, 0xa9,
|
|
||||||
0xe3, 0x8b, 0x06, 0x7c, 0x60, 0x49, 0xd3, 0x42, 0xf7, 0x4b, 0x3d, 0x7b, 0x64, 0x9e, 0xae, 0x90,
|
|
||||||
0x45, 0xb2, 0xcc, 0x88, 0xdf, 0xac, 0x72, 0x93, 0xe9, 0x6e, 0x7c, 0xd2, 0x80, 0x8f, 0x16, 0xb0,
|
|
||||||
0x95, 0xf0, 0xf3, 0x65, 0xa3, 0xd7, 0xab, 0x24, 0x65, 0x11, 0xb7, 0xe2, 0x57, 0x94, 0x1b, 0xf0,
|
|
||||||
0x4d, 0x97, 0x31, 0x12, 0x5e, 0xbe, 0xda, 0x6f, 0x36, 0xa9, 0x46, 0xfc, 0x5a, 0xbf, 0xd9, 0xe8,
|
|
||||||
0x3e, 0xfc, 0xba, 0x01, 0x7f, 0x0a, 0x28, 0x65, 0x6e, 0xdc, 0xd7, 0x0c, 0xf8, 0x7e, 0x50, 0x4a,
|
|
||||||
0xde, 0xb7, 0x5f, 0x31, 0xf4, 0x67, 0x96, 0x2d, 0x19, 0x48, 0x9e, 0x26, 0x5e, 0xed, 0xd2, 0x27,
|
|
||||||
0x25, 0xd7, 0x17, 0x07, 0xe9, 0xe4, 0xbb, 0xf3, 0xd7, 0x06, 0x7c, 0xff, 0x49, 0xa0, 0x22, 0x81,
|
|
||||||
0xd7, 0x0c, 0xf8, 0xfe, 0x53, 0x4a, 0x7c, 0x58, 0x78, 0xbd, 0xcb, 0xee, 0x98, 0xa2, 0x8e, 0xe9,
|
|
||||||
0xd4, 0x93, 0x07, 0xa7, 0x1f, 0x0c, 0xc2, 0xbb, 0x43, 0x92, 0x0a, 0xfb, 0xe1, 0x20, 0x7c, 0x73,
|
|
||||||
0x89, 0x05, 0xe3, 0xa2, 0xfc, 0x68, 0x10, 0xbe, 0xb9, 0x48, 0x36, 0x06, 0x7f, 0x3c, 0x08, 0xdf,
|
|
||||||
0xae, 0x24, 0x28, 0x2b, 0xf8, 0x74, 0x6f, 0xb9, 0xf8, 0x76, 0xf5, 0x93, 0x41, 0xf8, 0xaa, 0xa1,
|
|
||||||
0x40, 0x79, 0x18, 0x2f, 0xfb, 0x0d, 0xfc, 0xcc, 0x20, 0x7c, 0xd5, 0x90, 0x68, 0x85, 0x59, 0x11,
|
|
||||||
0xf7, 0x6c, 0x6f, 0xdf, 0xd1, 0x8f, 0xb4, 0x02, 0xfc, 0x69, 0x6f, 0x41, 0xbd, 0x30, 0x3f, 0x93,
|
|
||||||
0x31, 0x4e, 0x9c, 0x46, 0xd7, 0xaf, 0x52, 0x46, 0x2e, 0x52, 0x67, 0x78, 0xef, 0x78, 0xf4, 0x4b,
|
|
||||||
0xff, 0xb8, 0xfa, 0xa5, 0x7f, 0xbc, 0xe4, 0x04, 0xad, 0xf0, 0xe7, 0x12, 0xf9, 0x95, 0x60, 0xe4,
|
|
||||||
0xb9, 0x87, 0x06, 0x46, 0x73, 0x63, 0x43, 0x8b, 0xd7, 0x09, 0x9b, 0x39, 0x67, 0xe2, 0x5e, 0x34,
|
|
||||||
0x14, 0x5a, 0xbb, 0x01, 0xef, 0xc7, 0xfc, 0x79, 0x69, 0x1e, 0xba, 0xac, 0x04, 0x7c, 0x62, 0x16,
|
|
||||||
0x6d, 0x0a, 0xed, 0x2d, 0x31, 0xad, 0xfa, 0x8c, 0xe1, 0x05, 0x29, 0xb2, 0x41, 0x58, 0x86, 0x63,
|
|
||||||
0x6e, 0xce, 0x99, 0x98, 0x43, 0x9b, 0x13, 0x42, 0x7d, 0x86, 0xf3, 0xa2, 0x54, 0xda, 0xa8, 0x95,
|
|
||||||
0x44, 0x4c, 0x67, 0xd0, 0x0d, 0xa1, 0x14, 0xa7, 0x4e, 0xbb, 0x1f, 0x95, 0x97, 0xa4, 0x4a, 0x58,
|
|
||||||
0x89, 0x1a, 0x75, 0xda, 0x13, 0xf3, 0xe8, 0xc6, 0x50, 0x61, 0xc9, 0x75, 0xb9, 0xed, 0x9a, 0x16,
|
|
||||||
0x61, 0xfd, 0xe8, 0xbc, 0x2c, 0x75, 0xc2, 0x44, 0xa6, 0xb4, 0xe9, 0x44, 0x11, 0x85, 0x99, 0x5e,
|
|
||||||
0x74, 0xdc, 0x8b, 0xcb, 0x7e, 0xab, 0x1f, 0xa5, 0x6b, 0x52, 0x29, 0xcc, 0x63, 0xc1, 0x9d, 0xf1,
|
|
||||||
0x5b, 0x53, 0x77, 0xa0, 0xfd, 0x75, 0xb7, 0x35, 0xee, 0x9b, 0xdc, 0xf5, 0x9b, 0xd4, 0x36, 0x97,
|
|
||||||
0x7c, 0xf5, 0xff, 0x79, 0xd8, 0x74, 0x49, 0x4b, 0x4d, 0x6d, 0xaa, 0x85, 0x7f, 0x94, 0x9d, 0xf3,
|
|
||||||
0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x69, 0x67, 0x5d, 0x1f, 0x22, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user