mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' into lean_reindex
This commit is contained in:
commit
87b1ffaf36
216 changed files with 11661 additions and 3554 deletions
46
.travis.yml
46
.travis.yml
|
|
@ -5,7 +5,7 @@ jobs:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- stage: build
|
- stage: build
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
- stage: lint
|
- stage: lint
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- lint
|
- lint
|
||||||
git:
|
git:
|
||||||
|
|
@ -44,12 +44,24 @@ jobs:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
|
- stage: build
|
||||||
|
os: linux
|
||||||
|
dist: xenial
|
||||||
|
go: 1.13.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
script:
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
# These are the latest Go versions.
|
# These are the latest Go versions.
|
||||||
- stage: build
|
- stage: build
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
@ -59,7 +71,9 @@ jobs:
|
||||||
os: linux
|
os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
@ -67,7 +81,9 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode11.3
|
osx_image: xcode11.3
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- echo "Increase the maximum number of open file descriptors on macOS"
|
- echo "Increase the maximum number of open file descriptors on macOS"
|
||||||
- NOFILE=20480
|
- NOFILE=20480
|
||||||
|
|
@ -86,9 +102,10 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.13.x
|
go: 1.14.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:
|
||||||
|
|
@ -102,7 +119,7 @@ jobs:
|
||||||
- python-paramiko
|
- python-paramiko
|
||||||
script:
|
script:
|
||||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||||
- go run build/ci.go debsrc -goversion 1.13.8 -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
- go run build/ci.go debsrc -goversion 1.14.2 -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
|
- stage: build
|
||||||
|
|
@ -110,9 +127,10 @@ jobs:
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.13.x
|
go: 1.14.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:
|
||||||
|
|
@ -146,9 +164,10 @@ jobs:
|
||||||
dist: xenial
|
dist: xenial
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-linux-mips
|
- azure-linux-mips
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
|
@ -189,10 +208,11 @@ jobs:
|
||||||
env:
|
env:
|
||||||
- azure-android
|
- azure-android
|
||||||
- maven-android
|
- maven-android
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
before_install:
|
before_install:
|
||||||
- curl https://dl.google.com/go/go1.13.8.linux-amd64.tar.gz | tar -xz
|
- curl https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz | tar -xz
|
||||||
- export PATH=`pwd`/go/bin:$PATH
|
- export PATH=`pwd`/go/bin:$PATH
|
||||||
- export GOROOT=`pwd`/go
|
- export GOROOT=`pwd`/go
|
||||||
- export GOPATH=$HOME/go
|
- export GOPATH=$HOME/go
|
||||||
|
|
@ -210,11 +230,12 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.13.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
- cocoapods-ios
|
- cocoapods-ios
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
|
@ -241,9 +262,10 @@ jobs:
|
||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.13.x
|
go: 1.14.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:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.13-alpine as builder
|
FROM golang:1.14-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.13-alpine as builder
|
FROM golang:1.14-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
||||||
42
README.md
42
README.md
|
|
@ -16,7 +16,7 @@ archives are published at https://geth.ethereum.org/downloads/.
|
||||||
|
|
||||||
For prerequisites and detailed build instructions please read the [Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum) on the wiki.
|
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.10 or later) and a C compiler. You can install
|
Building `geth` requires both a Go (version 1.13 or later) and a C compiler. You can install
|
||||||
them using your favourite package manager. Once the dependencies are installed, run
|
them using your favourite package manager. Once the dependencies are installed, run
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
@ -72,7 +72,7 @@ This command will:
|
||||||
This tool is optional and if you leave it out you can always attach to an already running
|
This tool is optional and if you leave it out you can always attach to an already running
|
||||||
`geth` instance with `geth attach`.
|
`geth` instance with `geth attach`.
|
||||||
|
|
||||||
### A Full node on the Ethereum test network
|
### A Full node on the Görli test network
|
||||||
|
|
||||||
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
||||||
contracts, you almost certainly would like to do that without any real money involved until
|
contracts, you almost certainly would like to do that without any real money involved until
|
||||||
|
|
@ -81,23 +81,24 @@ network, you want to join the **test** network with your node, which is fully eq
|
||||||
the main network, but with play-Ether only.
|
the main network, but with play-Ether only.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ geth --testnet console
|
$ geth --goerli console
|
||||||
```
|
```
|
||||||
|
|
||||||
The `console` subcommand has the exact same meaning as above and they are equally
|
The `console` subcommand has the exact same meaning as above and they are equally
|
||||||
useful on the testnet too. Please see above for their explanations if you've skipped here.
|
useful on the testnet too. Please, see above for their explanations if you've skipped here.
|
||||||
|
|
||||||
Specifying the `--testnet` flag, however, will reconfigure your `geth` instance a bit:
|
Specifying the `--goerli` flag, however, will reconfigure your `geth` instance a bit:
|
||||||
|
|
||||||
|
* Instead of connecting the main Ethereum network, the client will connect to the Görli
|
||||||
|
test network, which uses different P2P bootnodes, different network IDs and genesis
|
||||||
|
states.
|
||||||
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
* Instead of using the default data directory (`~/.ethereum` on Linux for example), `geth`
|
||||||
will nest itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on
|
will nest itself one level deeper into a `goerli` subfolder (`~/.ethereum/goerli` on
|
||||||
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
|
Linux). Note, on OSX and Linux this also means that attaching to a running testnet node
|
||||||
requires the use of a custom endpoint since `geth attach` will try to attach to a
|
requires the use of a custom endpoint since `geth attach` will try to attach to a
|
||||||
production node endpoint by default. E.g.
|
production node endpoint by default, e.g.,
|
||||||
`geth attach <datadir>/testnet/geth.ipc`. Windows users are not affected by
|
`geth attach <datadir>/goerli/geth.ipc`. Windows users are not affected by
|
||||||
this.
|
this.
|
||||||
* Instead of connecting the main Ethereum network, the client will connect to the test
|
|
||||||
network, which uses different P2P bootnodes, different network IDs and genesis states.
|
|
||||||
|
|
||||||
*Note: Although there are some internal protective measures to prevent transactions from
|
*Note: Although there are some internal protective measures to prevent transactions from
|
||||||
crossing over between the main network and test network, you should make sure to always
|
crossing over between the main network and test network, you should make sure to always
|
||||||
|
|
@ -107,17 +108,26 @@ accounts available between them.*
|
||||||
|
|
||||||
### Full node on the Rinkeby test network
|
### Full node on the Rinkeby test network
|
||||||
|
|
||||||
The above test network is a cross-client one based on the ethash proof-of-work consensus
|
Go Ethereum also supports connecting to the older proof-of-authority based test network
|
||||||
algorithm. As such, it has certain extra overhead and is more susceptible to reorganization
|
called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community.
|
||||||
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.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ geth --rinkeby console
|
$ geth --rinkeby console
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Full node on the Ropsten test network
|
||||||
|
|
||||||
|
In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The
|
||||||
|
Ropsten test network is 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.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ geth --ropsten console
|
||||||
|
```
|
||||||
|
|
||||||
|
*Note: Older Geth configurations store the Ropsten database in the `testnet` subdirectory.*
|
||||||
|
|
||||||
### 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
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ 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/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
|
||||||
|
|
@ -32,6 +34,12 @@ type ABI struct {
|
||||||
Constructor Method
|
Constructor Method
|
||||||
Methods map[string]Method
|
Methods map[string]Method
|
||||||
Events map[string]Event
|
Events map[string]Event
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
@ -42,7 +50,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +77,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Pack up the method ID too if not a constructor and return
|
// Pack up the method ID too if not a constructor and return
|
||||||
return append(method.ID(), arguments...), nil
|
return append(method.ID, arguments...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack output in v according to the abi specification
|
// Unpack output in v according to the abi specification
|
||||||
|
|
@ -108,13 +115,22 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface
|
// 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
|
Inputs []Argument
|
||||||
|
Outputs []Argument
|
||||||
|
|
||||||
|
// Status indicator which can be: "pure", "view",
|
||||||
|
// "nonpayable" or "payable".
|
||||||
StateMutability string
|
StateMutability string
|
||||||
Anonymous bool
|
|
||||||
Inputs []Argument
|
// Deprecated Status indicators, but removed in v0.6.0.
|
||||||
Outputs []Argument
|
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
|
||||||
|
|
@ -124,44 +140,67 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = Method{
|
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||||
Inputs: field.Inputs,
|
case "function":
|
||||||
|
name := abi.overloadedMethodName(field.Name)
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
// empty defaults to function according to the abi spec
|
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "function", "":
|
case "receive":
|
||||||
name := field.Name
|
// New introduced function type in v0.6.0, check more detail
|
||||||
_, ok := abi.Methods[name]
|
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
||||||
for idx := 0; ok; idx++ {
|
if abi.HasReceive() {
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
return errors.New("only single receive is allowed")
|
||||||
_, ok = abi.Methods[name]
|
|
||||||
}
|
}
|
||||||
isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
|
if field.StateMutability != "payable" {
|
||||||
abi.Methods[name] = Method{
|
return errors.New("the statemutability of receive can only be payable")
|
||||||
Name: name,
|
|
||||||
RawName: field.Name,
|
|
||||||
Const: isConst,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
Outputs: field.Outputs,
|
|
||||||
}
|
}
|
||||||
|
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "event":
|
case "event":
|
||||||
name := field.Name
|
name := abi.overloadedEventName(field.Name)
|
||||||
_, ok := abi.Events[name]
|
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||||
for idx := 0; ok; idx++ {
|
default:
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||||
_, ok = abi.Events[name]
|
|
||||||
}
|
|
||||||
abi.Events[name] = Event{
|
|
||||||
Name: name,
|
|
||||||
RawName: field.Name,
|
|
||||||
Anonymous: field.Anonymous,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overloadedMethodName returns the next available name for a given function.
|
||||||
|
// Needed since solidity allows for function overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains Methods send, send1
|
||||||
|
// overloadedMethodName would return send2 for input send.
|
||||||
|
func (abi *ABI) overloadedMethodName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Methods[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Methods[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// overloadedEventName returns the next available name for a given event.
|
||||||
|
// Needed since solidity allows for event overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains events received, received1
|
||||||
|
// overloadedEventName would return received2 for input received.
|
||||||
|
func (abi *ABI) overloadedEventName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Events[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Events[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|
@ -169,7 +208,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
||||||
}
|
}
|
||||||
for _, method := range abi.Methods {
|
for _, method := range abi.Methods {
|
||||||
if bytes.Equal(method.ID(), sigdata[:4]) {
|
if bytes.Equal(method.ID, sigdata[:4]) {
|
||||||
return &method, nil
|
return &method, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,9 +219,41 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
// ABI and returns nil if none found.
|
// ABI and returns nil if none found.
|
||||||
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
for _, event := range abi.Events {
|
for _, event := range abi.Events {
|
||||||
if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
|
if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
|
||||||
return &event, nil
|
return &event, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
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")
|
||||||
|
}
|
||||||
|
var reason string
|
||||||
|
typ, _ := NewType("string", "", nil)
|
||||||
|
if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return reason, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package abi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -26,57 +27,99 @@ import (
|
||||||
"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" : "balance", "constant" : true },
|
{ "type" : "function", "name" : "", "stateMutability" : "view" },
|
||||||
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
|
{ "type" : "function", "name" : "balance", "stateMutability" : "view" },
|
||||||
|
{ "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" : "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]" } ] }
|
||||||
]`
|
]`
|
||||||
|
|
||||||
const jsondata2 = `
|
var (
|
||||||
[
|
Uint256, _ = NewType("uint256", "", nil)
|
||||||
{ "type" : "function", "name" : "balance", "constant" : true },
|
Uint32, _ = NewType("uint32", "", nil)
|
||||||
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
Uint16, _ = NewType("uint16", "", nil)
|
||||||
{ "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
|
String, _ = NewType("string", "", nil)
|
||||||
{ "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
|
Bool, _ = NewType("bool", "", nil)
|
||||||
{ "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
|
Bytes, _ = NewType("bytes", "", 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)
|
||||||
|
)
|
||||||
|
|
||||||
|
var methods = map[string]Method{
|
||||||
|
"": NewMethod("", "", Function, "view", 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),
|
||||||
|
"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),
|
||||||
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
Uint256, _ := NewType("uint256", "", nil)
|
abi := ABI{
|
||||||
exp := ABI{
|
Methods: methods,
|
||||||
Methods: map[string]Method{
|
|
||||||
"balance": {
|
|
||||||
"balance", "balance", true, nil, nil,
|
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"send", "send", false, []Argument{
|
|
||||||
{"amount", Uint256, false},
|
|
||||||
}, nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
exp, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(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 {
|
||||||
|
|
@ -98,8 +141,58 @@ 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)
|
||||||
|
}
|
||||||
|
v := struct {
|
||||||
|
A *big.Int
|
||||||
|
B *big.Int
|
||||||
|
}{new(big.Int), new(big.Int)}
|
||||||
|
//abi.Unpack(&v, "", packed)
|
||||||
|
if err := abi.Constructor.Inputs.Unpack(&v, packed); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(v.A, big.NewInt(1)) {
|
||||||
|
t.Error("Unable to pack/unpack from constructor")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(v.B, 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(jsondata2))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -135,60 +228,22 @@ func TestTestNumbers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTestString(t *testing.T) {
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Fatal(err)
|
|
||||||
}
|
|
||||||
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) {
|
||||||
String, _ := NewType("string", "", nil)
|
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
||||||
m := Method{"foo", "foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
|
|
||||||
exp := "foo(string,string)"
|
exp := "foo(string,string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
idexp := crypto.Keccak256([]byte(exp))[:4]
|
idexp := crypto.Keccak256([]byte(exp))[:4]
|
||||||
if !bytes.Equal(m.ID(), idexp) {
|
if !bytes.Equal(m.ID, idexp) {
|
||||||
t.Errorf("expected ids to match %x != %x", m.ID(), idexp)
|
t.Errorf("expected ids to match %x != %x", m.ID, idexp)
|
||||||
}
|
}
|
||||||
|
|
||||||
uintt, _ := NewType("uint256", "", nil)
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
|
||||||
m = Method{"foo", "foo", false, []Argument{{"bar", uintt, false}}, nil}
|
|
||||||
exp = "foo(uint256)"
|
exp = "foo(uint256)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method with tuple arguments
|
// Method with tuple arguments
|
||||||
|
|
@ -204,10 +259,10 @@ func TestMethodSignature(t *testing.T) {
|
||||||
{Name: "y", Type: "int256"},
|
{Name: "y", Type: "int256"},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
m = Method{"foo", "foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
|
||||||
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,12 +274,12 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
check := func(name string, expect string, method bool) {
|
check := func(name string, expect string, method bool) {
|
||||||
if method {
|
if method {
|
||||||
if abi.Methods[name].Sig() != expect {
|
if abi.Methods[name].Sig != expect {
|
||||||
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig())
|
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if abi.Events[name].Sig() != expect {
|
if abi.Events[name].Sig != expect {
|
||||||
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig())
|
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +290,7 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiPack(t *testing.T) {
|
func TestMultiPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -400,15 +455,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
const definition = `[
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
{ "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)
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +602,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 = U256(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
|
dynarroffset = math.U256Bytes(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)
|
||||||
|
|
@ -582,7 +629,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultFunctionParsing(t *testing.T) {
|
func TestDefaultFunctionParsing(t *testing.T) {
|
||||||
const definition = `[{ "name" : "balance" }]`
|
const definition = `[{ "name" : "balance", "type" : "function" }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -602,8 +649,6 @@ func TestBareEvents(t *testing.T) {
|
||||||
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
||||||
]`
|
]`
|
||||||
|
|
||||||
arg0, _ := NewType("uint256", "", nil)
|
|
||||||
arg1, _ := NewType("address", "", nil)
|
|
||||||
tuple, _ := NewType("tuple", "", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
tuple, _ := NewType("tuple", "", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
||||||
|
|
||||||
expectedEvents := map[string]struct {
|
expectedEvents := map[string]struct {
|
||||||
|
|
@ -613,12 +658,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: arg0, Indexed: false},
|
{Name: "arg0", Type: Uint256, Indexed: false},
|
||||||
{Name: "arg1", Type: arg1, Indexed: true},
|
{Name: "arg1", Type: Address, Indexed: true},
|
||||||
}},
|
}},
|
||||||
"tuple": {false, []Argument{
|
"tuple": {false, []Argument{
|
||||||
{Name: "t", Type: tuple, Indexed: false},
|
{Name: "t", Type: tuple, Indexed: false},
|
||||||
{Name: "arg1", Type: arg1, Indexed: true},
|
{Name: "arg1", Type: Address, Indexed: true},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,45 +936,25 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABI_MethodById(t *testing.T) {
|
func TestABI_MethodById(t *testing.T) {
|
||||||
const abiJSON = `[
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
{"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 %x) not 'findable' by id in ABI", name, 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")
|
||||||
|
|
@ -995,8 +1020,8 @@ func TestABI_EventById(t *testing.T) {
|
||||||
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.ID() != topicID {
|
if event.ID != topicID {
|
||||||
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID().Hex(), topicID.Hex(), testnum)
|
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
||||||
|
|
@ -1010,26 +1035,6 @@ func TestABI_EventById(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDuplicateMethodNames(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":"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":"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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
||||||
// conflict and that the second transfer method will be renamed transfer1.
|
// conflict and that the second transfer method will be renamed transfer1.
|
||||||
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
|
|
@ -1051,3 +1056,87 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
t.Fatalf("Should not have found extra method")
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,18 +59,6 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
|
|
@ -92,9 +80,8 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
} else {
|
|
||||||
return nil // Nothing to unmarshal, return
|
|
||||||
}
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
}
|
}
|
||||||
// make sure the passed value is arguments pointer
|
// make sure the passed value is arguments pointer
|
||||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||||
|
|
@ -104,6 +91,9 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(marshalledValues) == 0 {
|
||||||
|
return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v)
|
||||||
|
}
|
||||||
if arguments.isTuple() {
|
if arguments.isTuple() {
|
||||||
return arguments.unpackTuple(v, marshalledValues)
|
return arguments.unpackTuple(v, marshalledValues)
|
||||||
}
|
}
|
||||||
|
|
@ -112,18 +102,24 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
|
|
||||||
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
||||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||||
|
// Make sure map is not nil
|
||||||
|
if v == nil {
|
||||||
|
return fmt.Errorf("abi: cannot unpack into a nil map")
|
||||||
|
}
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
} else {
|
|
||||||
return nil // Nothing to unmarshal, return
|
|
||||||
}
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
}
|
}
|
||||||
marshalledValues, err := arguments.UnpackValues(data)
|
marshalledValues, err := arguments.UnpackValues(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return arguments.unpackIntoMap(v, marshalledValues)
|
for i, arg := range arguments.NonIndexed() {
|
||||||
|
v[arg.Name] = marshalledValues[i]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpack sets the unmarshalled value to go format.
|
// unpack sets the unmarshalled value to go format.
|
||||||
|
|
@ -195,25 +191,13 @@ func unpack(t *Type, dst interface{}, src interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpackIntoMap unpacks marshalledValues into the provided map[string]interface{}
|
|
||||||
func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledValues []interface{}) error {
|
|
||||||
// Make sure map is not nil
|
|
||||||
if v == nil {
|
|
||||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, arg := range arguments.NonIndexed() {
|
|
||||||
v[arg.Name] = marshalledValues[i]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
||||||
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
||||||
if arguments.LengthNonIndexed() == 0 {
|
nonIndexedArgs := arguments.NonIndexed()
|
||||||
|
if len(nonIndexedArgs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
argument := arguments.NonIndexed()[0]
|
argument := nonIndexedArgs[0]
|
||||||
elem := reflect.ValueOf(v).Elem()
|
elem := reflect.ValueOf(v).Elem()
|
||||||
|
|
||||||
if elem.Kind() == reflect.Struct && argument.Type.T != TupleTy {
|
if elem.Kind() == reflect.Struct && argument.Type.T != TupleTy {
|
||||||
|
|
@ -233,30 +217,28 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interfac
|
||||||
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
||||||
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
||||||
var (
|
var (
|
||||||
value = reflect.ValueOf(v).Elem()
|
value = reflect.ValueOf(v).Elem()
|
||||||
typ = value.Type()
|
typ = value.Type()
|
||||||
kind = value.Kind()
|
kind = value.Kind()
|
||||||
|
nonIndexedArgs = arguments.NonIndexed()
|
||||||
)
|
)
|
||||||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
if err := requireUnpackKind(value, len(nonIndexedArgs), arguments); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the interface is a struct, get of abi->struct_field mapping
|
// If the interface is a struct, get of abi->struct_field mapping
|
||||||
var abi2struct map[string]string
|
var abi2struct map[string]string
|
||||||
if kind == reflect.Struct {
|
if kind == reflect.Struct {
|
||||||
var (
|
argNames := make([]string, len(nonIndexedArgs))
|
||||||
argNames []string
|
for i, arg := range nonIndexedArgs {
|
||||||
err error
|
argNames[i] = arg.Name
|
||||||
)
|
|
||||||
for _, arg := range arguments.NonIndexed() {
|
|
||||||
argNames = append(argNames, arg.Name)
|
|
||||||
}
|
}
|
||||||
abi2struct, err = mapArgNamesToStructFields(argNames, value)
|
var err error
|
||||||
if err != nil {
|
if abi2struct, err = mapArgNamesToStructFields(argNames, value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, arg := range arguments.NonIndexed() {
|
for i, arg := range nonIndexedArgs {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
field := value.FieldByName(abi2struct[arg.Name])
|
field := value.FieldByName(abi2struct[arg.Name])
|
||||||
|
|
@ -289,10 +271,11 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
||||||
// 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) {
|
||||||
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
|
nonIndexedArgs := arguments.NonIndexed()
|
||||||
|
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, arg := range arguments.NonIndexed() {
|
for index, arg := range nonIndexedArgs {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
marshalledValue, err := ToGoType((index+virtualArgs)*32, arg.Type, data)
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ 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/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -49,7 +50,6 @@ 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")
|
||||||
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
||||||
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
||||||
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
|
|
@ -349,8 +349,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
|
|
@ -359,8 +362,11 @@ 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())
|
||||||
|
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
|
|
@ -398,22 +404,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
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 {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil || failed {
|
if err != nil {
|
||||||
return false
|
if err == core.ErrIntrinsicGas {
|
||||||
|
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
|
||||||
if !executable(mid) {
|
failed, _, err := 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
|
||||||
|
|
@ -421,8 +438,25 @@ 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 {
|
||||||
if !executable(hi) {
|
failed, result, err := executable(hi)
|
||||||
return 0, errGasEstimationFailed
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
|
errMsg := fmt.Sprintf("always failing transaction (%v)", result.Err)
|
||||||
|
if len(result.Revert()) > 0 {
|
||||||
|
ret, err := abi.UnpackRevert(result.Revert())
|
||||||
|
if err != nil {
|
||||||
|
errMsg += fmt.Sprintf(" (%#x)", result.Revert())
|
||||||
|
} else {
|
||||||
|
errMsg += fmt.Sprintf(" (%s)", ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
// Otherwise, the specified gas cap is too low
|
||||||
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hi, nil
|
return hi, nil
|
||||||
|
|
@ -430,7 +464,7 @@ 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) ([]byte, uint64, bool, error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
|
||||||
// Ensure message is initialized properly.
|
// Ensure message is initialized properly.
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = big.NewInt(1)
|
call.GasPrice = big.NewInt(1)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package backends
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -356,25 +357,112 @@ func TestSimulatedBackend_TransactionByHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
||||||
sim := NewSimulatedBackend(
|
/*
|
||||||
core.GenesisAlloc{}, 10000000,
|
pragma solidity ^0.6.4;
|
||||||
)
|
contract GasEstimation {
|
||||||
|
function PureRevert() public { revert(); }
|
||||||
|
function Revert() public { revert("revert reason");}
|
||||||
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||||
|
function Assert() public { assert(false);}
|
||||||
|
function Valid() public {}
|
||||||
|
}*/
|
||||||
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
|
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
||||||
|
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
opts := bind.NewKeyedTransactor(key)
|
||||||
|
|
||||||
|
sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
bgCtx := context.Background()
|
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
||||||
|
|
||||||
gas, err := sim.EstimateGas(bgCtx, ethereum.CallMsg{
|
parsed, _ := abi.JSON(strings.NewReader(contractAbi))
|
||||||
From: testAddr,
|
contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
|
||||||
To: &testAddr,
|
sim.Commit()
|
||||||
Value: big.NewInt(1000),
|
|
||||||
Data: []byte{},
|
var cases = []struct {
|
||||||
})
|
name string
|
||||||
if err != nil {
|
message ethereum.CallMsg
|
||||||
t.Errorf("could not estimate gas: %v", err)
|
expect uint64
|
||||||
|
expectError error
|
||||||
|
}{
|
||||||
|
{"plain transfer(valid)", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &addr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, params.TxGas, nil},
|
||||||
|
|
||||||
|
{"plain transfer(invalid)", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"Revert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("d8b98391"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted) (revert reason)")},
|
||||||
|
|
||||||
|
{"PureRevert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("aa8b1d30"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"OOG", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("50f6fe34"),
|
||||||
|
}, 0, errors.New("gas required exceeds allowance (100000)")},
|
||||||
|
|
||||||
|
{"Assert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("b9b046f9"),
|
||||||
|
}, 0, errors.New("always failing transaction (invalid opcode: opcode 0xfe not defined)")},
|
||||||
|
|
||||||
|
{"Valid", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("e09fface"),
|
||||||
|
}, 21275, nil},
|
||||||
}
|
}
|
||||||
|
for _, c := range cases {
|
||||||
if gas != params.TxGas {
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
t.Errorf("expected 21000 gas cost for a transaction got %v", gas)
|
if c.expectError != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expect error, got nil")
|
||||||
|
}
|
||||||
|
if c.expectError.Error() != err.Error() {
|
||||||
|
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got != c.expect {
|
||||||
|
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,24 @@ 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 initiates transaction 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +264,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
||||||
opts = new(FilterOpts)
|
opts = new(FilterOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -301,7 +313,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
||||||
opts = new(WatchOpts)
|
opts = new(WatchOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
package bind_test
|
package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -88,51 +88,23 @@ const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16
|
||||||
|
|
||||||
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
||||||
hash := crypto.Keccak256Hash([]byte("testName"))
|
hash := crypto.Keccak256Hash([]byte("testName"))
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
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"}]`
|
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))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"name": hash,
|
"name": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["name"] != expectedReceivedMap["name"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -141,51 +113,23 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(sliceBytes)
|
hash := crypto.Keccak256Hash(sliceBytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
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"}]`
|
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))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"names": hash,
|
"names": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["names"] != expectedReceivedMap["names"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -194,51 +138,23 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(arrBytes)
|
hash := crypto.Keccak256Hash(arrBytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
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"}]`
|
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))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"addresses": hash,
|
"addresses": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["addresses"] != expectedReceivedMap["addresses"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -249,99 +165,72 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
||||||
functionTyBytes := append(addrBytes, functionSelector...)
|
functionTyBytes := append(addrBytes, functionSelector...)
|
||||||
var functionTy [24]byte
|
var functionTy [24]byte
|
||||||
copy(functionTy[:], functionTyBytes[0:24])
|
copy(functionTy[:], functionTyBytes[0:24])
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
||||||
Topics: []common.Hash{
|
common.BytesToHash(functionTyBytes),
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
|
||||||
common.BytesToHash(functionTyBytes),
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
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"}]`
|
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))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"function": functionTy,
|
"function": functionTy,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["function"] != expectedReceivedMap["function"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
||||||
byts := []byte{1, 2, 3, 4, 5}
|
bytes := []byte{1, 2, 3, 4, 5}
|
||||||
hash := crypto.Keccak256Hash(byts)
|
hash := crypto.Keccak256Hash(bytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
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"}]`
|
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))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"content": hash,
|
"content": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
if len(received) != len(expected) {
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
|
||||||
}
|
}
|
||||||
if receivedMap["content"] != expectedReceivedMap["content"] {
|
for name, elem := range expected {
|
||||||
t.Error("unpacked map does not match expected map")
|
if !reflect.DeepEqual(elem, received[name]) {
|
||||||
}
|
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
}
|
||||||
t.Error("unpacked map does not match expected map")
|
}
|
||||||
}
|
}
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
||||||
}
|
return types.Log{
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
Address: common.HexToAddress("0x0"),
|
||||||
t.Error("unpacked map does not match expected map")
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
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 identifier of function
|
// identifiers are used to detect duplicated identifier of function
|
||||||
// and event. For all calls, transacts and events, abigen will generate
|
// and event. For all calls, transacts and events, abigen will generate
|
||||||
|
|
@ -92,7 +94,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
// Ensure there is no duplicated identifier
|
// Ensure there is no duplicated identifier
|
||||||
var identifiers = callIdentifiers
|
var identifiers = callIdentifiers
|
||||||
if !original.Const {
|
if !original.IsConstant() {
|
||||||
identifiers = transactIdentifiers
|
identifiers = transactIdentifiers
|
||||||
}
|
}
|
||||||
if identifiers[normalizedName] {
|
if identifiers[normalizedName] {
|
||||||
|
|
@ -121,7 +123,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append the methods to the call or transact lists
|
// Append the methods to the call or transact lists
|
||||||
if original.Const {
|
if original.IsConstant() {
|
||||||
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)}
|
||||||
|
|
@ -156,7 +158,13 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
// 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}
|
||||||
|
}
|
||||||
// There is no easy way to pass arbitrary java objects to the Go side.
|
// There is no easy way to pass arbitrary java objects to the Go side.
|
||||||
if len(structs) > 0 && lang == LangJava {
|
if len(structs) > 0 && lang == LangJava {
|
||||||
return "", errors.New("java binding for tuple arguments is not supported yet")
|
return "", errors.New("java binding for tuple arguments is not supported yet")
|
||||||
|
|
@ -169,6 +177,8 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
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),
|
Libraries: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
@ -619,11 +629,22 @@ func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
|
||||||
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constant := ""
|
// Extract meaningful state mutability of solidity method.
|
||||||
if method.Const {
|
// If it's default value, never print it.
|
||||||
constant = "constant "
|
state := method.StateMutability
|
||||||
|
if state == "nonpayable" {
|
||||||
|
state = ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
|
if state != "" {
|
||||||
|
state = state + " "
|
||||||
|
}
|
||||||
|
identity := fmt.Sprintf("function %v", method.RawName)
|
||||||
|
if method.Type == abi.Fallback {
|
||||||
|
identity = "fallback"
|
||||||
|
} else if method.Type == abi.Receive {
|
||||||
|
identity = "receive"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatEvent transforms raw event representation into a user friendly one.
|
// formatEvent transforms raw event representation into a user friendly one.
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -35,6 +35,8 @@ type tmplContract struct {
|
||||||
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
|
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
|
Library bool // Indicator whether the contract is a library
|
||||||
|
|
@ -101,7 +103,6 @@ var (
|
||||||
_ = 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
|
||||||
|
|
@ -351,6 +352,52 @@ var (
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Fallback}}
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{formatmethod .Fallback.Original $structs}}
|
||||||
|
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: {{formatmethod .Fallback.Original $structs}}
|
||||||
|
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: {{formatmethod .Fallback.Original $structs}}
|
||||||
|
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: {{formatmethod .Receive.Original $structs}}
|
||||||
|
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: {{formatmethod .Receive.Original $structs}}
|
||||||
|
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: {{formatmethod .Receive.Original $structs}}
|
||||||
|
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 {
|
||||||
|
|
@ -577,7 +624,7 @@ import java.util.*;
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
||||||
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
||||||
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -611,6 +658,24 @@ import java.util.*;
|
||||||
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Fallback}}
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{formatmethod .Fallback.Original $structs}}
|
||||||
|
public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
|
||||||
|
return this.Contract.rawTransact(opts, calldata);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Receive}}
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{formatmethod .Receive.Original $structs}}
|
||||||
|
public Transaction Receive(TransactOpts opts) throws Exception {
|
||||||
|
return this.Contract.rawTransact(opts, null);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,13 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
topic[common.HashLength-1] = 1
|
topic[common.HashLength-1] = 1
|
||||||
}
|
}
|
||||||
case int8:
|
case int8:
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
copy(topic[:], genIntType(int64(rule), 1))
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int16:
|
case int16:
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
copy(topic[:], genIntType(int64(rule), 2))
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int32:
|
case int32:
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
copy(topic[:], genIntType(int64(rule), 4))
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int64:
|
case int64:
|
||||||
blob := big.NewInt(rule).Bytes()
|
copy(topic[:], genIntType(rule, 8))
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint8:
|
case uint8:
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
|
@ -103,151 +99,75 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
return topics, nil
|
return topics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big batch of reflect types for topic reconstruction.
|
func genIntType(rule int64, size uint) []byte {
|
||||||
var (
|
var topic [common.HashLength]byte
|
||||||
reflectHash = reflect.TypeOf(common.Hash{})
|
if rule < 0 {
|
||||||
reflectAddress = reflect.TypeOf(common.Address{})
|
// if a rule is negative, we need to put it into two's complement.
|
||||||
reflectBigInt = reflect.TypeOf(new(big.Int))
|
// 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.
|
// 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 {
|
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
|
||||||
// Sanity check that the fields and topics match up
|
return parseTopicWithSetter(fields, topics,
|
||||||
if len(fields) != len(topics) {
|
func(arg abi.Argument, reconstr interface{}) {
|
||||||
return errors.New("topic/field count mismatch")
|
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
|
||||||
}
|
field.Set(reflect.ValueOf(reconstr))
|
||||||
// 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][:])
|
|
||||||
if arg.Type.T == abi.IntTy {
|
|
||||||
if num.Cmp(abi.MaxInt256) > 0 {
|
|
||||||
num.Add(abi.MaxUint256, big.NewInt(0).Neg(num))
|
|
||||||
num.Add(num, big.NewInt(1))
|
|
||||||
num.Neg(num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
field.Set(reflect.ValueOf(num))
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Ran out of custom types, try the crazies
|
|
||||||
switch {
|
|
||||||
// static byte array
|
|
||||||
case arg.Type.T == abi.FixedBytesTy:
|
|
||||||
reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
topics = topics[1:]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
|
// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
|
||||||
func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
|
func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
|
||||||
|
return parseTopicWithSetter(fields, topics,
|
||||||
|
func(arg abi.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 abi.Arguments, topics []common.Hash, setter func(abi.Argument, interface{})) error {
|
||||||
// Sanity check that the fields and topics match up
|
// Sanity check that the fields and topics match up
|
||||||
if len(fields) != len(topics) {
|
if len(fields) != len(topics) {
|
||||||
return errors.New("topic/field count mismatch")
|
return errors.New("topic/field count mismatch")
|
||||||
}
|
}
|
||||||
// Iterate over all the fields and reconstruct them from topics
|
// Iterate over all the fields and reconstruct them from topics
|
||||||
for _, arg := range fields {
|
for i, arg := range fields {
|
||||||
if !arg.Indexed {
|
if !arg.Indexed {
|
||||||
return errors.New("non-indexed field in topic reconstruction")
|
return errors.New("non-indexed field in topic reconstruction")
|
||||||
}
|
}
|
||||||
|
var reconstr interface{}
|
||||||
switch arg.Type.T {
|
switch arg.Type.T {
|
||||||
case abi.BoolTy:
|
case abi.TupleTy:
|
||||||
out[arg.Name] = topics[0][common.HashLength-1] == 1
|
return errors.New("tuple type in topic reconstruction")
|
||||||
case abi.IntTy, abi.UintTy:
|
|
||||||
out[arg.Name] = abi.ReadInteger(arg.Type.T, arg.Type.Kind, topics[0].Bytes())
|
|
||||||
case abi.AddressTy:
|
|
||||||
var addr common.Address
|
|
||||||
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
|
|
||||||
out[arg.Name] = addr
|
|
||||||
case abi.HashTy:
|
|
||||||
out[arg.Name] = topics[0]
|
|
||||||
case abi.FixedBytesTy:
|
|
||||||
array, err := abi.ReadFixedBytes(arg.Type, topics[0].Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out[arg.Name] = array
|
|
||||||
case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
|
case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
|
||||||
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
|
// 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
|
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
|
||||||
out[arg.Name] = topics[0]
|
reconstr = topics[i]
|
||||||
case abi.FunctionTy:
|
case abi.FunctionTy:
|
||||||
if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 {
|
if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
|
||||||
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes())
|
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
|
||||||
}
|
}
|
||||||
var tmp [24]byte
|
var tmp [24]byte
|
||||||
copy(tmp[:], topics[0][8:32])
|
copy(tmp[:], topics[i][8:32])
|
||||||
out[arg.Name] = tmp
|
reconstr = tmp
|
||||||
default: // Not handling tuples
|
default:
|
||||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
var err error
|
||||||
|
reconstr, err = abi.ToGoType(0, arg.Type, topics[i].Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Use the setter function to store the value
|
||||||
topics = topics[1:]
|
setter(arg, reconstr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeTopics(t *testing.T) {
|
func TestMakeTopics(t *testing.T) {
|
||||||
|
|
@ -41,6 +42,80 @@ func TestMakeTopics(t *testing.T) {
|
||||||
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
||||||
false,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
@ -84,6 +159,7 @@ func setupTopicsTests() []topicTest {
|
||||||
bytesType, _ := abi.NewType("bytes5", "", nil)
|
bytesType, _ := abi.NewType("bytes5", "", nil)
|
||||||
int8Type, _ := abi.NewType("int8", "", nil)
|
int8Type, _ := abi.NewType("int8", "", nil)
|
||||||
int256Type, _ := abi.NewType("int256", "", nil)
|
int256Type, _ := abi.NewType("int256", "", nil)
|
||||||
|
tupleType, _ := abi.NewType("tuple(int256,int8)", "", nil)
|
||||||
|
|
||||||
tests := []topicTest{
|
tests := []topicTest{
|
||||||
{
|
{
|
||||||
|
|
@ -145,6 +221,21 @@ func setupTopicsTests() []topicTest {
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "tuple(int256, int8)",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return nil },
|
||||||
|
resultObj: func() interface{} { return nil },
|
||||||
|
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
||||||
|
fields: abi.Arguments{abi.Argument{
|
||||||
|
Name: "tupletype",
|
||||||
|
Type: tupleType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return tests
|
return tests
|
||||||
|
|
|
||||||
|
|
@ -39,23 +39,21 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
||||||
// type in t.
|
// type in t.
|
||||||
func sliceTypeCheck(t Type, val reflect.Value) error {
|
func sliceTypeCheck(t Type, val reflect.Value) error {
|
||||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||||
return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
|
return typeErr(formatSliceString(t.getType().Kind(), t.Size), val.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.T == ArrayTy && val.Len() != t.Size {
|
if t.T == ArrayTy && val.Len() != t.Size {
|
||||||
return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
return typeErr(formatSliceString(t.Elem.getType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Elem.T == SliceTy {
|
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
|
||||||
if val.Len() > 0 {
|
if val.Len() > 0 {
|
||||||
return sliceTypeCheck(*t.Elem, val.Index(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 {
|
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.getType().Kind() {
|
||||||
return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
|
return typeErr(formatSliceString(t.Elem.getType().Kind(), t.Size), val.Type())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +66,10 @@ func typeCheck(t Type, value reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check base type validity. Element types will be checked later on.
|
// Check base type validity. Element types will be checked later on.
|
||||||
if t.Kind != value.Kind() {
|
if t.getType().Kind() != value.Kind() {
|
||||||
return typeErr(t.Kind, value.Kind())
|
return typeErr(t.getType().Kind(), value.Kind())
|
||||||
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
||||||
return typeErr(t.Type, value.Type())
|
return typeErr(t.getType(), value.Type())
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,36 +42,59 @@ type Event struct {
|
||||||
RawName string
|
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 {
|
||||||
inputs := make([]string, len(e.Inputs))
|
return e.str
|
||||||
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.RawName, strings.Join(inputs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sig returns the event string signature according to the ABI spec.
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
//
|
|
||||||
// event foo(uint32 a, int b) = "foo(uint32,int256)"
|
|
||||||
//
|
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
|
||||||
func (e Event) Sig() string {
|
|
||||||
types := make([]string, len(e.Inputs))
|
|
||||||
for i, input := range e.Inputs {
|
|
||||||
types[i] = input.Type.String()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v(%v)", e.RawName, strings.Join(types, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns the canonical representation of the event's signature used by the
|
|
||||||
// abi definition to identify event names and types.
|
|
||||||
func (e Event) ID() common.Hash {
|
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(e.Sig())))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ func TestEventId(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, event := range abi.Events {
|
for name, event := range abi.Events {
|
||||||
if event.ID() != test.expectations[name] {
|
if event.ID != test.expectations[name] {
|
||||||
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID())
|
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,24 @@ 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.
|
||||||
|
|
@ -41,50 +59,109 @@ type Method struct {
|
||||||
// * foo(uint,uint)
|
// * foo(uint,uint)
|
||||||
// The method name of the first one will be resolved as foo while the second one
|
// The method name of the first one will be resolved as foo while the second one
|
||||||
// will be resolved as foo0.
|
// will be resolved as foo0.
|
||||||
Name string
|
Name string
|
||||||
// RawName is the raw method name parsed from ABI.
|
RawName string // RawName is the raw method name parsed from ABI
|
||||||
RawName string
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
// NewMethod creates a new Method.
|
||||||
//
|
// A method should always be created using NewMethod.
|
||||||
// Example
|
// It also precomputes the sig representation and the string representation
|
||||||
//
|
// of the method.
|
||||||
// function foo(uint32 a, int b) = "foo(uint32,int256)"
|
func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
|
||||||
//
|
var (
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
types = make([]string, len(inputs))
|
||||||
func (method Method) Sig() string {
|
inputNames = make([]string, len(inputs))
|
||||||
types := make([]string, len(method.Inputs))
|
outputNames = make([]string, len(outputs))
|
||||||
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()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
|
for i, output := range outputs {
|
||||||
|
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 {
|
||||||
inputs := make([]string, len(method.Inputs))
|
return method.str
|
||||||
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.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the canonical representation of the method's signature used by the
|
// IsConstant returns the indicator whether the method is read-only.
|
||||||
// abi definition to identify method names and types.
|
func (method Method) IsConstant() bool {
|
||||||
func (method Method) ID() []byte {
|
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
||||||
return crypto.Keccak256([]byte(method.Sig()))[:4]
|
}
|
||||||
|
|
||||||
|
// IsPayable returns the indicator whether the method can process
|
||||||
|
// plain ether transfers.
|
||||||
|
func (method Method) IsPayable() bool {
|
||||||
|
return method.StateMutability == "payable" || method.Payable
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,15 @@ import (
|
||||||
|
|
||||||
const methoddata = `
|
const methoddata = `
|
||||||
[
|
[
|
||||||
{"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": "transfer", "constant": false, "inputs": [{"name": "from", "type": "address"}, {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}], "outputs": [{"name": "success", "type": "bool"}]},
|
{"type": "function", "name": "transfer", "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":"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[]"}],"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":"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"}
|
{"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) {
|
||||||
|
|
@ -39,7 +41,7 @@ func TestMethodString(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
method: "balance",
|
method: "balance",
|
||||||
expectation: "function balance() constant returns()",
|
expectation: "function balance() view returns()",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: "send",
|
method: "send",
|
||||||
|
|
@ -65,6 +67,14 @@ func TestMethodString(t *testing.T) {
|
||||||
method: "complexTuple",
|
method: "complexTuple",
|
||||||
expectation: "function complexTuple((uint256,uint256)[5][] a) returns()",
|
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))
|
||||||
|
|
@ -73,7 +83,14 @@ func TestMethodString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
got := abi.Methods[test.method].String()
|
var got 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)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +137,7 @@ func TestMethodSig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
got := abi.Methods[test.method].Sig()
|
got := abi.Methods[test.method].Sig
|
||||||
if got != test.expect {
|
if got != test.expect {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +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 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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +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 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -69,11 +69,11 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
|
||||||
func packNum(value reflect.Value) []byte {
|
func packNum(value reflect.Value) []byte {
|
||||||
switch kind := value.Kind(); kind {
|
switch kind := value.Kind(); kind {
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
return U256(new(big.Int).SetUint64(value.Uint()))
|
return math.U256Bytes(new(big.Int).SetUint64(value.Uint()))
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return U256(big.NewInt(value.Int()))
|
return math.U256Bytes(big.NewInt(value.Int()))
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return U256(new(big.Int).Set(value.Interface().(*big.Int)))
|
return math.U256Bytes(new(big.Int).Set(value.Interface().(*big.Int)))
|
||||||
default:
|
default:
|
||||||
panic("abi: fatal error")
|
panic("abi: fatal error")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,623 +18,62 @@ 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 []struct {
|
for i, test := range packUnpackTests {
|
||||||
typ string
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
components []ArgumentMarshaling
|
encb, err := hex.DecodeString(test.packed)
|
||||||
input interface{}
|
if err != nil {
|
||||||
output []byte
|
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||||
}{
|
}
|
||||||
{
|
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
||||||
"uint8",
|
inAbi, err := JSON(strings.NewReader(inDef))
|
||||||
nil,
|
if err != nil {
|
||||||
uint8(2),
|
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
}
|
||||||
},
|
var packed []byte
|
||||||
{
|
if reflect.TypeOf(test.unpacked).Kind() != reflect.Struct {
|
||||||
"uint8[]",
|
packed, err = inAbi.Pack("method", test.unpacked)
|
||||||
nil,
|
} else {
|
||||||
[]uint8{1, 2},
|
// if want is a struct we need to use the components.
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
elem := reflect.ValueOf(test.unpacked)
|
||||||
},
|
var values []interface{}
|
||||||
{
|
for i := 0; i < elem.NumField(); i++ {
|
||||||
"uint16",
|
field := elem.Field(i)
|
||||||
nil,
|
values = append(values, field.Interface())
|
||||||
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
|
packed, err = inAbi.Pack("method", values...)
|
||||||
}{
|
}
|
||||||
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 {
|
|
||||||
t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
|
|
||||||
}
|
|
||||||
output, err := typ.pack(reflect.ValueOf(test.input))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(output, test.output) {
|
if err != nil {
|
||||||
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
|
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
}
|
}
|
||||||
|
if !reflect.DeepEqual(packed[4:], encb) {
|
||||||
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMethodPack(t *testing.T) {
|
func TestMethodPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
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)...)
|
||||||
|
|
||||||
|
|
@ -648,7 +87,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrA, addrB = common.Address{1}, common.Address{2}
|
var addrA, addrB = common.Address{1}, common.Address{2}
|
||||||
sig = abi.Methods["sliceAddress"].ID()
|
sig = abi.Methods["sliceAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
||||||
|
|
@ -663,7 +102,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrC, addrD = common.Address{3}, common.Address{4}
|
var addrC, addrD = common.Address{3}, common.Address{4}
|
||||||
sig = abi.Methods["sliceMultiAddress"].ID()
|
sig = abi.Methods["sliceMultiAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -681,7 +120,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["slice256"].ID()
|
sig = abi.Methods["slice256"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
|
|
@ -695,7 +134,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
||||||
sig = abi.Methods["nestedArray"].ID()
|
sig = abi.Methods["nestedArray"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -712,7 +151,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedArray2"].ID()
|
sig = abi.Methods["nestedArray2"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
||||||
|
|
@ -728,7 +167,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)...)
|
||||||
|
|
|
||||||
988
accounts/abi/packing_test.go
Normal file
988
accounts/abi/packing_test.go
Normal file
|
|
@ -0,0 +1,988 @@
|
||||||
|
// 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: []common.Hash{{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: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: struct {
|
||||||
|
Int1 *big.Int
|
||||||
|
Int2 *big.Int
|
||||||
|
}{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int__one","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one_","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
|
||||||
|
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: `[{"name":"a","type":"int64"},
|
||||||
|
{"name":"b","type":"int256"},
|
||||||
|
{"name":"c","type":"int256"},
|
||||||
|
{"name":"d","type":"bool"},
|
||||||
|
{"name":"e","type":"bytes32[3][2]"}]`,
|
||||||
|
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: `[{"name":"a","type":"string"},
|
||||||
|
{"name":"b","type":"int64"},
|
||||||
|
{"name":"c","type":"bytes"},
|
||||||
|
{"name":"d","type":"string[]"},
|
||||||
|
{"name":"e","type":"int256[]"},
|
||||||
|
{"name":"f","type":"address[]"}]`,
|
||||||
|
unpacked: 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}}},
|
||||||
|
packed: "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": [{"name": "a","type": "uint256"},
|
||||||
|
{"name": "b","type": "uint256[]"}],
|
||||||
|
"name": "a","type": "tuple"},
|
||||||
|
{"name": "b","type": "uint256[]"}]`,
|
||||||
|
unpacked: 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(2)}},
|
||||||
|
B: []*big.Int{big.NewInt(1), big.NewInt(2)}},
|
||||||
|
packed: "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]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -25,7 +26,7 @@ import (
|
||||||
// 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() != derefbigT {
|
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
||||||
return indirect(v.Elem())
|
return indirect(v.Elem())
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
|
|
@ -39,32 +40,32 @@ func indirectInterfaceOrPtr(v reflect.Value) reflect.Value {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflectIntKind returns the reflect using the given size and
|
// reflectIntType returns the reflect using the given size and
|
||||||
// unsignedness.
|
// unsignedness.
|
||||||
func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
|
func reflectIntType(unsigned bool, size int) reflect.Type {
|
||||||
|
if unsigned {
|
||||||
|
switch size {
|
||||||
|
case 8:
|
||||||
|
return reflect.TypeOf(uint8(0))
|
||||||
|
case 16:
|
||||||
|
return reflect.TypeOf(uint16(0))
|
||||||
|
case 32:
|
||||||
|
return reflect.TypeOf(uint32(0))
|
||||||
|
case 64:
|
||||||
|
return reflect.TypeOf(uint64(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
switch size {
|
switch size {
|
||||||
case 8:
|
case 8:
|
||||||
if unsigned {
|
return reflect.TypeOf(int8(0))
|
||||||
return reflect.Uint8, uint8T
|
|
||||||
}
|
|
||||||
return reflect.Int8, int8T
|
|
||||||
case 16:
|
case 16:
|
||||||
if unsigned {
|
return reflect.TypeOf(int16(0))
|
||||||
return reflect.Uint16, uint16T
|
|
||||||
}
|
|
||||||
return reflect.Int16, int16T
|
|
||||||
case 32:
|
case 32:
|
||||||
if unsigned {
|
return reflect.TypeOf(int32(0))
|
||||||
return reflect.Uint32, uint32T
|
|
||||||
}
|
|
||||||
return reflect.Int32, int32T
|
|
||||||
case 64:
|
case 64:
|
||||||
if unsigned {
|
return reflect.TypeOf(int64(0))
|
||||||
return reflect.Uint64, uint64T
|
|
||||||
}
|
|
||||||
return reflect.Int64, int64T
|
|
||||||
}
|
}
|
||||||
return reflect.Ptr, bigT
|
return reflect.TypeOf(&big.Int{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustArrayToBytesSlice 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
|
||||||
|
|
@ -84,12 +85,12 @@ func set(dst, src reflect.Value) error {
|
||||||
switch {
|
switch {
|
||||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
|
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
||||||
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:
|
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet():
|
||||||
return setSlice(dst, src)
|
setSlice(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())
|
||||||
}
|
}
|
||||||
|
|
@ -98,15 +99,13 @@ 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
|
||||||
func setSlice(dst, src reflect.Value) error {
|
// setSlice ignores if we cannot copy all of src' elements.
|
||||||
|
func setSlice(dst, src reflect.Value) {
|
||||||
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++ {
|
||||||
v := src.Index(i)
|
reflect.Copy(slice.Index(i), src.Index(i))
|
||||||
reflect.Copy(slice.Index(i), v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.Set(slice)
|
dst.Set(slice)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireAssignable assures that `dest` is a pointer and it's not an interface.
|
// requireAssignable assures that `dest` is a pointer and it's not an interface.
|
||||||
|
|
@ -118,18 +117,16 @@ func requireAssignable(dst, src reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
||||||
func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
func requireUnpackKind(v reflect.Value, minLength int, args Arguments) error {
|
||||||
args Arguments) error {
|
switch v.Kind() {
|
||||||
|
|
||||||
switch k {
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
if minLen := args.LengthNonIndexed(); v.Len() < minLen {
|
if v.Len() < minLength {
|
||||||
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
||||||
minLen, v.Len())
|
minLength, v.Len())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
|
return fmt.Errorf("abi: cannot unmarshal tuple into %v", v.Type())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -156,9 +153,8 @@ 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.
|
||||||
var ok bool
|
tagName, ok := typ.Field(i).Tag.Lookup("abi")
|
||||||
var tagName string
|
if !ok {
|
||||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if tag is empty.
|
// check if tag is empty.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type enumerator
|
// Type enumerator
|
||||||
|
|
@ -45,17 +47,16 @@ const (
|
||||||
// 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.
|
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 (
|
||||||
|
|
@ -94,20 +95,16 @@ 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)
|
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
} else if len(intz) == 1 {
|
} else if len(intz) == 1 {
|
||||||
// is a 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)
|
|
||||||
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")
|
||||||
|
|
@ -139,36 +136,24 @@ 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 {
|
||||||
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 (
|
||||||
|
|
@ -188,7 +173,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
}
|
}
|
||||||
fields = append(fields, reflect.StructField{
|
fields = append(fields, reflect.StructField{
|
||||||
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
|
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
|
||||||
Type: cType.Type,
|
Type: cType.getType(),
|
||||||
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
||||||
})
|
})
|
||||||
elems = append(elems, &cType)
|
elems = append(elems, &cType)
|
||||||
|
|
@ -199,8 +184,8 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression += ")"
|
expression += ")"
|
||||||
typ.Kind = reflect.Struct
|
|
||||||
typ.Type = reflect.StructOf(fields)
|
typ.TupleType = reflect.StructOf(fields)
|
||||||
typ.TupleElems = elems
|
typ.TupleElems = elems
|
||||||
typ.TupleRawNames = names
|
typ.TupleRawNames = names
|
||||||
typ.T = TupleTy
|
typ.T = TupleTy
|
||||||
|
|
@ -217,10 +202,8 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -228,6 +211,41 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// String implements Stringer
|
||||||
func (t Type) String() (out string) {
|
func (t Type) String() (out string) {
|
||||||
return t.stringKind
|
return t.stringKind
|
||||||
|
|
|
||||||
|
|
@ -36,58 +36,58 @@ func TestTypeRegexp(t *testing.T) {
|
||||||
components []ArgumentMarshaling
|
components []ArgumentMarshaling
|
||||||
kind Type
|
kind Type
|
||||||
}{
|
}{
|
||||||
{"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
|
{"bool", nil, Type{T: BoolTy, 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[]", nil, Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
|
||||||
{"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{Size: 2, T: ArrayTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, 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[2][]", nil, Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
||||||
{"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[][]", nil, Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
||||||
{"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]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][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: 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.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]", 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][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[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[][][]", 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[][][]", 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[][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][]"}},
|
{"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][]"}},
|
||||||
{"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
|
{"int8", nil, Type{Size: 8, T: IntTy, stringKind: "int8"}},
|
||||||
{"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
|
{"int16", nil, Type{Size: 16, T: IntTy, stringKind: "int16"}},
|
||||||
{"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
|
{"int32", nil, Type{Size: 32, T: IntTy, stringKind: "int32"}},
|
||||||
{"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
|
{"int64", nil, Type{Size: 64, T: IntTy, stringKind: "int64"}},
|
||||||
{"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
|
{"int256", nil, Type{Size: 256, T: IntTy, stringKind: "int256"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
|
||||||
{"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]"}},
|
{"int8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
|
||||||
{"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]"}},
|
{"int16[2]", nil, Type{Size: 2, T: ArrayTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
|
||||||
{"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]"}},
|
{"int32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
|
||||||
{"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]"}},
|
{"int64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
|
||||||
{"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]"}},
|
{"int256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
|
||||||
{"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
|
{"uint8", nil, Type{Size: 8, T: UintTy, stringKind: "uint8"}},
|
||||||
{"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
|
{"uint16", nil, Type{Size: 16, T: UintTy, stringKind: "uint16"}},
|
||||||
{"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
|
{"uint32", nil, Type{Size: 32, T: UintTy, stringKind: "uint32"}},
|
||||||
{"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
|
{"uint64", nil, Type{Size: 64, T: UintTy, stringKind: "uint64"}},
|
||||||
{"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
|
{"uint256", nil, Type{Size: 256, T: UintTy, stringKind: "uint256"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
|
||||||
{"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]"}},
|
{"uint8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
|
||||||
{"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]"}},
|
{"uint16[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
|
||||||
{"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]"}},
|
{"uint32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
|
||||||
{"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]"}},
|
{"uint64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
|
||||||
{"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]"}},
|
{"uint256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
|
||||||
{"bytes32", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
|
{"bytes32", nil, Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
||||||
{"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]"}},
|
{"bytes[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
||||||
{"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[]", nil, Type{T: SliceTy, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
||||||
{"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]"}},
|
{"bytes32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
||||||
{"string", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
|
{"string", nil, Type{T: StringTy, 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[]", nil, Type{T: SliceTy, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
||||||
{"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]"}},
|
{"string[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[2]"}},
|
||||||
{"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
|
{"address", nil, Type{Size: 20, T: AddressTy, 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[]", nil, Type{T: SliceTy, Elem: &Type{Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
|
||||||
{"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]"}},
|
{"address[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{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,14 +95,14 @@ 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{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct {
|
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
||||||
A int64 `json:"a"`
|
A int64 `json:"a"`
|
||||||
}{}), stringKind: "(int64)",
|
}{}), stringKind: "(int64)",
|
||||||
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
||||||
{"tuple with long name", []ArgumentMarshaling{{Name: "aTypicalParamName", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct {
|
{"tuple with long name", []ArgumentMarshaling{{Name: "aTypicalParamName", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
||||||
ATypicalParamName int64 `json:"aTypicalParamName"`
|
ATypicalParamName int64 `json:"aTypicalParamName"`
|
||||||
}{}), stringKind: "(int64)",
|
}{}), stringKind: "(int64)",
|
||||||
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"aTypicalParamName"}}},
|
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"aTypicalParamName"}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
@ -306,3 +306,27 @@ 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,33 +33,37 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadInteger reads the integer based on its kind and returns the appropriate value
|
// ReadInteger reads the integer based on its kind and returns the appropriate value
|
||||||
func ReadInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
|
func ReadInteger(typ Type, b []byte) interface{} {
|
||||||
switch kind {
|
if typ.T == UintTy {
|
||||||
case reflect.Uint8:
|
switch typ.Size {
|
||||||
return b[len(b)-1]
|
case 8:
|
||||||
case reflect.Uint16:
|
return b[len(b)-1]
|
||||||
return binary.BigEndian.Uint16(b[len(b)-2:])
|
case 16:
|
||||||
case reflect.Uint32:
|
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||||
return binary.BigEndian.Uint32(b[len(b)-4:])
|
case 32:
|
||||||
case reflect.Uint64:
|
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||||
return binary.BigEndian.Uint64(b[len(b)-8:])
|
case 64:
|
||||||
case reflect.Int8:
|
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||||
|
default:
|
||||||
|
// the only case left for unsigned integer is uint256.
|
||||||
|
return new(big.Int).SetBytes(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch typ.Size {
|
||||||
|
case 8:
|
||||||
return int8(b[len(b)-1])
|
return int8(b[len(b)-1])
|
||||||
case reflect.Int16:
|
case 16:
|
||||||
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
||||||
case reflect.Int32:
|
case 32:
|
||||||
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
||||||
case reflect.Int64:
|
case 64:
|
||||||
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
default:
|
default:
|
||||||
// the only case left for integer is int256/uint256.
|
// the only case left for integer is int256
|
||||||
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.
|
// big.SetBytes can't tell if a number is negative or positive in itself.
|
||||||
// On EVM, if the returned number > max int256, it is negative.
|
// On EVM, if the returned number > max int256, it is negative.
|
||||||
// A number is > max int256 if the bit at position 255 is set.
|
// A number is > max int256 if the bit at position 255 is set.
|
||||||
|
ret := new(big.Int).SetBytes(b)
|
||||||
if ret.Bit(255) == 1 {
|
if ret.Bit(255) == 1 {
|
||||||
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
||||||
ret.Add(ret, common.Big1)
|
ret.Add(ret, common.Big1)
|
||||||
|
|
@ -106,7 +110,7 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
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.Type).Elem()
|
array := reflect.New(t.getType()).Elem()
|
||||||
|
|
||||||
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
||||||
return array.Interface(), nil
|
return array.Interface(), nil
|
||||||
|
|
@ -127,10 +131,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.Type, size, size)
|
refSlice = reflect.MakeSlice(t.getType(), size, size)
|
||||||
} else if t.T == ArrayTy {
|
} else if t.T == ArrayTy {
|
||||||
// declare our array
|
// declare our array
|
||||||
refSlice = reflect.New(t.Type).Elem()
|
refSlice = reflect.New(t.getType()).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")
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +144,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
elemSize := getTypeSize(*t.Elem)
|
elemSize := getTypeSize(*t.Elem)
|
||||||
|
|
||||||
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
|
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
|
||||||
inter, err := toGoType(i, *t.Elem, output)
|
inter, err := ToGoType(i, *t.Elem, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -154,10 +158,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.Type).Elem()
|
retval := reflect.New(t.getType()).Elem()
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, elem := range t.TupleElems {
|
for index, elem := range t.TupleElems {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
marshalledValue, err := ToGoType((index+virtualArgs)*32, *elem, output)
|
||||||
if 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.
|
||||||
|
|
@ -183,9 +187,9 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||||
return retval.Interface(), nil
|
return retval.Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// toGoType parses the output bytes and recursively assigns the value of these bytes
|
// ToGoType parses the output bytes and recursively assigns the value of these bytes
|
||||||
// into a go type with accordance with the ABI spec.
|
// into a go type with accordance with the ABI spec.
|
||||||
func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
func ToGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
if index+32 > len(output) {
|
if index+32 > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +232,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
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.T, t.Kind, returnOutput), nil
|
return ReadInteger(t, returnOutput), nil
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return readBool(returnOutput)
|
return readBool(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,34 @@ 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)
|
||||||
|
}
|
||||||
|
outptr := reflect.New(reflect.TypeOf(test.unpacked))
|
||||||
|
err = abi.Unpack(outptr.Interface(), "method", encb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := outptr.Elem().Interface()
|
||||||
|
if !reflect.DeepEqual(test.unpacked, out) {
|
||||||
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -52,16 +80,6 @@ func (test unpackTest) checkError(err error) error {
|
||||||
|
|
||||||
var unpackTests = []unpackTest{
|
var unpackTests = []unpackTest{
|
||||||
// Bools
|
// 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",
|
||||||
|
|
@ -75,11 +93,6 @@ var unpackTests = []unpackTest{
|
||||||
err: "abi: improperly encoded boolean value",
|
err: "abi: improperly encoded boolean value",
|
||||||
},
|
},
|
||||||
// Integers
|
// Integers
|
||||||
{
|
|
||||||
def: `[{"type": "uint32"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: uint32(1),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"type": "uint32"}]`,
|
def: `[{"type": "uint32"}]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
|
@ -92,16 +105,6 @@ 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",
|
||||||
|
|
@ -114,33 +117,6 @@ 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),
|
|
||||||
},
|
|
||||||
// Address
|
|
||||||
{
|
|
||||||
def: `[{"type": "address"}]`,
|
|
||||||
enc: "0000000000000000000000000100000000000000000000000000000000000000",
|
|
||||||
want: common.Address{1},
|
|
||||||
},
|
|
||||||
// Bytes
|
|
||||||
{
|
|
||||||
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"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"type": "bytes"}]`,
|
def: `[{"type": "bytes"}]`,
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
|
@ -153,237 +129,6 @@ 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},
|
|
||||||
},
|
|
||||||
// Functions
|
|
||||||
{
|
|
||||||
def: `[{"type": "function"}]`,
|
|
||||||
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [24]byte{1},
|
|
||||||
},
|
|
||||||
// Slice and Array
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []uint8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: []uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: []*big.Int{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [][]uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [2][]uint8{{}, {}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][2]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: [2][]uint8{{1}, {1}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [][2]uint8{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
||||||
|
|
@ -438,12 +183,37 @@ 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)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpack(t *testing.T) {
|
// TestLocalUnpackTests runs test specially designed only for unpacking.
|
||||||
|
// 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) {
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
//Unpack
|
||||||
|
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)
|
||||||
|
|
@ -522,7 +292,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", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
|
|
@ -611,7 +381,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithArray(t *testing.T) {
|
func TestMultiReturnWithArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -634,7 +404,7 @@ func TestMultiReturnWithArray(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringArray(t *testing.T) {
|
func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -664,7 +434,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringSlice(t *testing.T) {
|
func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -700,7 +470,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", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -737,15 +507,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||||
|
|
||||||
func TestUnmarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
|
{ "name" : "int", "type": "function", "outputs": [ { "type": "uint256" } ] },
|
||||||
{ "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] },
|
{ "name" : "bool", "type": "function", "outputs": [ { "type": "bool" } ] },
|
||||||
{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
|
{ "name" : "bytes", "type": "function", "outputs": [ { "type": "bytes" } ] },
|
||||||
{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
|
{ "name" : "fixed", "type": "function", "outputs": [ { "type": "bytes32" } ] },
|
||||||
{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
{ "name" : "multi", "type": "function", "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
||||||
{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
|
{ "name" : "intArraySingle", "type": "function", "outputs": [ { "type": "uint256[3]" } ] },
|
||||||
{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
|
{ "name" : "addressSliceSingle", "type": "function", "outputs": [ { "type": "address[]" } ] },
|
||||||
{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
{ "name" : "addressSliceDouble", "type": "function", "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
||||||
{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
{ "name" : "mixedBytes", "type": "function", "stateMutability" : "view", "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 {
|
||||||
|
|
@ -985,7 +755,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackTuple(t *testing.T) {
|
func TestUnpackTuple(t *testing.T) {
|
||||||
const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
||||||
abi, err := JSON(strings.NewReader(simpleTuple))
|
abi, err := JSON(strings.NewReader(simpleTuple))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -1014,7 +784,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nested tuple
|
// Test nested tuple
|
||||||
const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
|
const nestedTuple = `[{"name":"tuple","type":"function","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"}
|
||||||
|
|
@ -1136,7 +906,7 @@ func TestOOMMaliciousInput(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range oomTests {
|
for i, test := range oomTests {
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,8 @@ type Wallet interface {
|
||||||
// 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 SignHashWithPassphrase, 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).
|
||||||
|
//
|
||||||
|
// This method should return the signature in 'canonical' format, with v 0 or 1
|
||||||
SignText(account Account, text []byte) ([]byte, error)
|
SignText(account Account, text []byte) ([]byte, error)
|
||||||
|
|
||||||
// SignTextWithPassphrase is identical to Signtext, but also takes a password
|
// SignTextWithPassphrase is identical to Signtext, but also takes a password
|
||||||
|
|
|
||||||
17
accounts/external/backend.go
vendored
17
accounts/external/backend.go
vendored
|
|
@ -131,6 +131,12 @@ func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||||
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
||||||
api.cacheMu.RLock()
|
api.cacheMu.RLock()
|
||||||
defer api.cacheMu.RUnlock()
|
defer api.cacheMu.RUnlock()
|
||||||
|
if api.cache == nil {
|
||||||
|
// If 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 {
|
for _, a := range api.cache {
|
||||||
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
||||||
return true
|
return true
|
||||||
|
|
@ -169,15 +175,20 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
||||||
var res hexutil.Bytes
|
var signature hexutil.Bytes
|
||||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||||
if err := api.client.Call(&res, "account_signData",
|
if err := api.client.Call(&signature, "account_signData",
|
||||||
accounts.MimetypeTextPlain,
|
accounts.MimetypeTextPlain,
|
||||||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
||||||
hexutil.Encode(text)); err != nil {
|
hexutil.Encode(text)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return res, nil
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -67,7 +66,8 @@ type KeyStore struct {
|
||||||
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
||||||
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 {
|
||||||
|
|
@ -443,14 +443,21 @@ 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{}, errors.New("account already exists")
|
||||||
|
}
|
||||||
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) {
|
||||||
key := newKeyFromECDSA(priv)
|
key := newKeyFromECDSA(priv)
|
||||||
|
ks.importMu.Lock()
|
||||||
|
defer ks.importMu.Unlock()
|
||||||
if ks.cache.hasAddress(key.Address) {
|
if ks.cache.hasAddress(key.Address) {
|
||||||
return accounts.Account{}, fmt.Errorf("account already exists")
|
return accounts.Account{}, errors.New("account already exists")
|
||||||
}
|
}
|
||||||
return ks.importKey(key, passphrase)
|
return ks.importKey(key, passphrase)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ import (
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -338,6 +341,88 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
checkEvents(t, wantEvents, events)
|
checkEvents(t, wantEvents, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestImportExport tests the import functionality of a keystore.
|
||||||
|
func TestImportECDSA(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate key: %v", key)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||||
|
func TestImportExport(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||||
|
t.Errorf("importing with invalid password succeeded")
|
||||||
|
}
|
||||||
|
acc2, err := ks2.Import(json, "new", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if acc.Address != acc2.Address {
|
||||||
|
t.Error("imported account does not match exported account")
|
||||||
|
}
|
||||||
|
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
||||||
|
t.Errorf("importing a key twice succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportRace tests the keystore on races.
|
||||||
|
// This test should fail under -race if importing races.
|
||||||
|
func TestImportRace(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
var atom uint32
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||||
|
atomic.AddUint32(&atom, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
if atom != 1 {
|
||||||
|
t.Errorf("Import is racy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|
|
||||||
|
|
@ -312,15 +312,15 @@ func (w *Wallet) Status() (string, error) {
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
||||||
return fmt.Sprintf("Bricked, waiting for full wipe"), nil
|
return "Bricked, waiting for full wipe", nil
|
||||||
case !w.session.verified && status.PinRetryCount == 0:
|
case !w.session.verified && status.PinRetryCount == 0:
|
||||||
return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil
|
return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil
|
||||||
case !w.session.verified:
|
case !w.session.verified:
|
||||||
return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil
|
return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil
|
||||||
case !status.Initialized:
|
case !status.Initialized:
|
||||||
return fmt.Sprintf("Empty, waiting for initialization"), nil
|
return "Empty, waiting for initialization", nil
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Online"), nil
|
return "Online", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ clone_depth: 5
|
||||||
version: "{branch}.{build}"
|
version: "{branch}.{build}"
|
||||||
environment:
|
environment:
|
||||||
global:
|
global:
|
||||||
|
GO111MODULE: on
|
||||||
GOPATH: C:\gopath
|
GOPATH: C:\gopath
|
||||||
CC: gcc.exe
|
CC: gcc.exe
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -23,8 +24,8 @@ environment:
|
||||||
install:
|
install:
|
||||||
- git submodule update --init
|
- git submodule update --init
|
||||||
- rmdir C:\go /s /q
|
- rmdir C:\go /s /q
|
||||||
- appveyor DownloadFile https://dl.google.com/go/go1.13.8.windows-%GETH_ARCH%.zip
|
- appveyor DownloadFile https://dl.google.com/go/go1.14.2.windows-%GETH_ARCH%.zip
|
||||||
- 7z x go1.13.8.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
- 7z x go1.14.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||||
- go version
|
- go version
|
||||||
- gcc --version
|
- gcc --version
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
b13bf04633d4d8cf53226ebeaace8d4d2fd07ae6fa676d0844a688339debec34 go1.13.8.src.tar.gz
|
98de84e69726a66da7b4e58eac41b99cbe274d7e8906eeb8a5b7eb0aadee7f7c go1.14.2.src.tar.gz
|
||||||
|
|
||||||
478994633b0f5121a7a8d4f368078093e21014fdc7fb2c0ceeae63668c13c5b6 golangci-lint-1.22.2-freebsd-amd64.tar.gz
|
aeaa5498682246b87d0b77ece283897348ea03d98e816760a074058bfca60b2a golangci-lint-1.24.0-windows-amd64.zip
|
||||||
fcf80824c21567eb0871055711bf9bdca91cf9a081122e2a45f1d11fed754600 golangci-lint-1.22.2-darwin-amd64.tar.gz
|
7e854a70d449fe77b7a91583ec88c8603eb3bf96c45d52797dc4ba3f2f278dbe golangci-lint-1.24.0-darwin-386.tar.gz
|
||||||
cda85c72fc128b2ea0ae05baea7b91172c63aea34064829f65285f1dd536f1e0 golangci-lint-1.22.2-windows-386.zip
|
835101fae192c3a2e7a51cb19d5ac3e1a40b0e311955e89bc21d61de78635979 golangci-lint-1.24.0-linux-armv6.tar.gz
|
||||||
94f04899f620aadc9c1524e5482e415efdbd993fa2b2918c4fec2798f030ac1c golangci-lint-1.22.2-linux-armv7.tar.gz
|
a041a6e6a61c9ff3dbe58673af13ea00c76bcd462abede0ade645808e97cdd6d golangci-lint-1.24.0-windows-386.zip
|
||||||
0e72a87d71edde00b6e37e84a99841833ad55fee83e20d21130a7a622b2860bb golangci-lint-1.22.2-freebsd-386.tar.gz
|
7cc73eb9ca02b7a766c72b913f8080401862b10e7bb90c09b085415a81f21609 golangci-lint-1.24.0-freebsd-armv6.tar.gz
|
||||||
86def2f31fe8fd7c05674104ed2a4bef3e44b7132b93c6ad2f52f198b3d01801 golangci-lint-1.22.2-linux-s390x.tar.gz
|
537bb2186987b5e68ad4e8829230557f26087c3028eb736dea1662a851bad73d golangci-lint-1.24.0-linux-armv7.tar.gz
|
||||||
b0df4546d36be94e8107733ba290b98dd9b7e41a42d3fb202e87fc7e4ee800c3 golangci-lint-1.22.2-freebsd-armv6.tar.gz
|
8cb1bc1e63d8f0d9b71fcb10b38887e1646a6b8a120ded2e0cd7c3284528f633 golangci-lint-1.24.0-linux-mips64.tar.gz
|
||||||
3d45958dcf6a8d195086d2fced1a21db42a90815dfd156d180efa62dbdda6724 golangci-lint-1.22.2-darwin-386.tar.gz
|
095d3f8bf7fc431739861574d0b58d411a617df2ed5698ce5ae5ecc66d23d44d golangci-lint-1.24.0-freebsd-armv7.tar.gz
|
||||||
7ee29f35c74fab017a454237990c74d984ce3855960f2c10509238992bb781f9 golangci-lint-1.22.2-linux-arm64.tar.gz
|
e245df27cec3827aef9e7afbac59e92816978ee3b64f84f7b88562ff4b2ac225 golangci-lint-1.24.0-linux-arm64.tar.gz
|
||||||
52086ac52a502b68578e58e35d3964f127c16d7a90b9ffcb399a004d055ded51 golangci-lint-1.22.2-linux-386.tar.gz
|
35d6d5927e19f0577cf527f0e4441dbb37701d87e8cf729c98a510fce397fbf7 golangci-lint-1.24.0-linux-ppc64le.tar.gz
|
||||||
c2e4df1fab2ae53762f9baac6041503eeeaa968ce38ea41779f7cb526751c667 golangci-lint-1.22.2-windows-amd64.zip
|
a1ed66353b8ceb575d78db3051491bce3ac1560e469a9bc87e8554486fec7dfe golangci-lint-1.24.0-freebsd-386.tar.gz
|
||||||
109d38cdc89f271392f5a138d6782657157f9f496fd4801956efa2d0428e0cbe golangci-lint-1.22.2-linux-amd64.tar.gz
|
241ca454102e909de04957ff8a5754c757cefa255758b3e1fba8a4533d19d179 golangci-lint-1.24.0-linux-amd64.tar.gz
|
||||||
f08aae4868d4828c8f07deb0dcd941a1da695b97e58d15e9f3d1d07dcc7a0c84 golangci-lint-1.22.2-linux-armv6.tar.gz
|
ff488423db01a0ec8ffbe4e1d65ef1be6a8d5e6d7930cf380ce8aaf714125470 golangci-lint-1.24.0-linux-386.tar.gz
|
||||||
37af03d9c144d527cb15c46a07e6a22d3f62b5491e34ad6f3bfe6bb0b0b597d4 golangci-lint-1.22.2-linux-ppc64le.tar.gz
|
f05af56f15ebbcf77663a8955d1e39009b584ce8ea4c5583669369d80353a113 golangci-lint-1.24.0-darwin-amd64.tar.gz
|
||||||
251a1081d53944f1d5f86216d752837b23079f90605c9d1cc628da1ffcd2e749 golangci-lint-1.22.2-freebsd-armv7.tar.gz
|
b0096796c0ffcd6c350a2ec006100e7ef5f0597b43a204349d4f997273fb32a7 golangci-lint-1.24.0-freebsd-amd64.tar.gz
|
||||||
|
c9c2867380e85628813f1f7d1c3cfc6c6f7931e89bea86f567ff451b8cdb6654 golangci-lint-1.24.0-linux-mips64le.tar.gz
|
||||||
|
2feb97fa61c934aa3eba9bc104ab5dd8fb946791d58e64060e8857e800eeae0b golangci-lint-1.24.0-linux-s390x.tar.gz
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ func doTest(cmdline []string) {
|
||||||
// Test a single package at a time. CI builders are slow
|
// Test a single package at a time. CI builders are slow
|
||||||
// and some tests run into timeouts under load.
|
// and some tests run into timeouts under load.
|
||||||
gotest := goTool("test", buildFlags(env)...)
|
gotest := goTool("test", buildFlags(env)...)
|
||||||
gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m")
|
gotest.Args = append(gotest.Args, "-p", "1")
|
||||||
if *coverage {
|
if *coverage {
|
||||||
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +356,7 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
const version = "1.22.2"
|
const version = "1.24.0"
|
||||||
|
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
|
||||||
|
|
@ -222,11 +222,11 @@ func init() {
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
rpcPortFlag,
|
rpcPortFlag,
|
||||||
signerSecretFlag,
|
signerSecretFlag,
|
||||||
customDBFlag,
|
customDBFlag,
|
||||||
|
|
@ -579,21 +579,29 @@ func signer(c *cli.Context) error {
|
||||||
Service: api,
|
Service: api,
|
||||||
Version: "1.0"},
|
Version: "1.0"},
|
||||||
}
|
}
|
||||||
if c.GlobalBool(utils.RPCEnabledFlag.Name) {
|
if c.GlobalBool(utils.HTTPEnabledFlag.Name) {
|
||||||
vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name))
|
vhosts := splitAndTrim(c.GlobalString(utils.HTTPVirtualHostsFlag.Name))
|
||||||
cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
|
cors := splitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
|
srv := rpc.NewServer()
|
||||||
|
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"account"}, srv, false)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Could not register API: %w", err)
|
||||||
|
}
|
||||||
|
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
||||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.DefaultHTTPTimeouts)
|
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
extapiURL = fmt.Sprintf("http://%v/", listener.Addr())
|
extapiURL = fmt.Sprintf("http://%v/", addr)
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
|
httpServer.Shutdown(context.Background())
|
||||||
log.Info("HTTP endpoint closed", "url", extapiURL)
|
log.Info("HTTP endpoint closed", "url", extapiURL)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type crawler struct {
|
type crawler struct {
|
||||||
input nodeSet
|
input nodeSet
|
||||||
output nodeSet
|
output nodeSet
|
||||||
disc *discover.UDPv4
|
disc resolver
|
||||||
iters []enode.Iterator
|
iters []enode.Iterator
|
||||||
inputIter enode.Iterator
|
inputIter enode.Iterator
|
||||||
ch chan *enode.Node
|
ch chan *enode.Node
|
||||||
|
|
@ -37,7 +36,11 @@ type crawler struct {
|
||||||
revalidateInterval time.Duration
|
revalidateInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCrawler(input nodeSet, disc *discover.UDPv4, iters ...enode.Iterator) *crawler {
|
type resolver interface {
|
||||||
|
RequestENR(*enode.Node) (*enode.Node, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCrawler(input nodeSet, disc resolver, iters ...enode.Iterator) *crawler {
|
||||||
c := &crawler{
|
c := &crawler{
|
||||||
input: input,
|
input: input,
|
||||||
output: make(nodeSet, len(input)),
|
output: make(nodeSet, len(input)),
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,18 @@ var (
|
||||||
Name: "bootnodes",
|
Name: "bootnodes",
|
||||||
Usage: "Comma separated nodes used for bootstrapping",
|
Usage: "Comma separated nodes used for bootstrapping",
|
||||||
}
|
}
|
||||||
|
nodekeyFlag = cli.StringFlag{
|
||||||
|
Name: "nodekey",
|
||||||
|
Usage: "Hex-encoded node key",
|
||||||
|
}
|
||||||
|
nodedbFlag = cli.StringFlag{
|
||||||
|
Name: "nodedb",
|
||||||
|
Usage: "Nodes database location",
|
||||||
|
}
|
||||||
|
listenAddrFlag = cli.StringFlag{
|
||||||
|
Name: "addr",
|
||||||
|
Usage: "Listening address",
|
||||||
|
}
|
||||||
crawlTimeoutFlag = cli.DurationFlag{
|
crawlTimeoutFlag = cli.DurationFlag{
|
||||||
Name: "timeout",
|
Name: "timeout",
|
||||||
Usage: "Time limit for the crawl.",
|
Usage: "Time limit for the crawl.",
|
||||||
|
|
@ -172,6 +184,62 @@ func discv4Crawl(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startV4 starts an ephemeral discovery V4 node.
|
||||||
|
func startV4(ctx *cli.Context) *discover.UDPv4 {
|
||||||
|
ln, config := makeDiscoveryConfig(ctx)
|
||||||
|
socket := listen(ln, ctx.String(listenAddrFlag.Name))
|
||||||
|
disc, err := discover.ListenV4(socket, ln, config)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
return disc
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
|
||||||
|
var cfg discover.Config
|
||||||
|
|
||||||
|
if ctx.IsSet(nodekeyFlag.Name) {
|
||||||
|
key, err := crypto.HexToECDSA(ctx.String(nodekeyFlag.Name))
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err))
|
||||||
|
}
|
||||||
|
cfg.PrivateKey = key
|
||||||
|
} else {
|
||||||
|
cfg.PrivateKey, _ = crypto.GenerateKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandHasFlag(ctx, bootnodesFlag) {
|
||||||
|
bn, err := parseBootnodes(ctx)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
cfg.Bootnodes = bn
|
||||||
|
}
|
||||||
|
|
||||||
|
dbpath := ctx.String(nodedbFlag.Name)
|
||||||
|
db, err := enode.OpenDB(dbpath)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
ln := enode.NewLocalNode(db, cfg.PrivateKey)
|
||||||
|
return ln, cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func listen(ln *enode.LocalNode, addr string) *net.UDPConn {
|
||||||
|
if addr == "" {
|
||||||
|
addr = "0.0.0.0:0"
|
||||||
|
}
|
||||||
|
socket, err := net.ListenPacket("udp4", addr)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
usocket := socket.(*net.UDPConn)
|
||||||
|
uaddr := socket.LocalAddr().(*net.UDPAddr)
|
||||||
|
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
|
||||||
|
ln.SetFallbackUDP(uaddr.Port)
|
||||||
|
return usocket
|
||||||
|
}
|
||||||
|
|
||||||
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
||||||
s := params.RinkebyBootnodes
|
s := params.RinkebyBootnodes
|
||||||
if ctx.IsSet(bootnodesFlag.Name) {
|
if ctx.IsSet(bootnodesFlag.Name) {
|
||||||
|
|
@ -187,40 +255,3 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
||||||
}
|
}
|
||||||
return nodes, nil
|
return nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// startV4 starts an ephemeral discovery V4 node.
|
|
||||||
func startV4(ctx *cli.Context) *discover.UDPv4 {
|
|
||||||
socket, ln, cfg, err := listen()
|
|
||||||
if err != nil {
|
|
||||||
exit(err)
|
|
||||||
}
|
|
||||||
if commandHasFlag(ctx, bootnodesFlag) {
|
|
||||||
bn, err := parseBootnodes(ctx)
|
|
||||||
if err != nil {
|
|
||||||
exit(err)
|
|
||||||
}
|
|
||||||
cfg.Bootnodes = bn
|
|
||||||
}
|
|
||||||
disc, err := discover.ListenV4(socket, ln, cfg)
|
|
||||||
if err != nil {
|
|
||||||
exit(err)
|
|
||||||
}
|
|
||||||
return disc
|
|
||||||
}
|
|
||||||
|
|
||||||
func listen() (*net.UDPConn, *enode.LocalNode, discover.Config, error) {
|
|
||||||
var cfg discover.Config
|
|
||||||
cfg.PrivateKey, _ = crypto.GenerateKey()
|
|
||||||
db, _ := enode.OpenDB("")
|
|
||||||
ln := enode.NewLocalNode(db, cfg.PrivateKey)
|
|
||||||
|
|
||||||
socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{0, 0, 0, 0}})
|
|
||||||
if err != nil {
|
|
||||||
db.Close()
|
|
||||||
return nil, nil, cfg, err
|
|
||||||
}
|
|
||||||
addr := socket.LocalAddr().(*net.UDPAddr)
|
|
||||||
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
|
|
||||||
ln.SetFallbackUDP(addr.Port)
|
|
||||||
return socket, ln, cfg, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
123
cmd/devp2p/discv5cmd.go
Normal file
123
cmd/devp2p/discv5cmd.go
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
discv5Command = cli.Command{
|
||||||
|
Name: "discv5",
|
||||||
|
Usage: "Node Discovery v5 tools",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
discv5PingCommand,
|
||||||
|
discv5ResolveCommand,
|
||||||
|
discv5CrawlCommand,
|
||||||
|
discv5ListenCommand,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
discv5PingCommand = cli.Command{
|
||||||
|
Name: "ping",
|
||||||
|
Usage: "Sends ping to a node",
|
||||||
|
Action: discv5Ping,
|
||||||
|
}
|
||||||
|
discv5ResolveCommand = cli.Command{
|
||||||
|
Name: "resolve",
|
||||||
|
Usage: "Finds a node in the DHT",
|
||||||
|
Action: discv5Resolve,
|
||||||
|
Flags: []cli.Flag{bootnodesFlag},
|
||||||
|
}
|
||||||
|
discv5CrawlCommand = cli.Command{
|
||||||
|
Name: "crawl",
|
||||||
|
Usage: "Updates a nodes.json file with random nodes found in the DHT",
|
||||||
|
Action: discv5Crawl,
|
||||||
|
Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag},
|
||||||
|
}
|
||||||
|
discv5ListenCommand = cli.Command{
|
||||||
|
Name: "listen",
|
||||||
|
Usage: "Runs a node",
|
||||||
|
Action: discv5Listen,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
bootnodesFlag,
|
||||||
|
nodekeyFlag,
|
||||||
|
nodedbFlag,
|
||||||
|
listenAddrFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func discv5Ping(ctx *cli.Context) error {
|
||||||
|
n := getNodeArg(ctx)
|
||||||
|
disc := startV5(ctx)
|
||||||
|
defer disc.Close()
|
||||||
|
|
||||||
|
fmt.Println(disc.Ping(n))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func discv5Resolve(ctx *cli.Context) error {
|
||||||
|
n := getNodeArg(ctx)
|
||||||
|
disc := startV5(ctx)
|
||||||
|
defer disc.Close()
|
||||||
|
|
||||||
|
fmt.Println(disc.Resolve(n))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func discv5Crawl(ctx *cli.Context) error {
|
||||||
|
if ctx.NArg() < 1 {
|
||||||
|
return fmt.Errorf("need nodes file as argument")
|
||||||
|
}
|
||||||
|
nodesFile := ctx.Args().First()
|
||||||
|
var inputSet nodeSet
|
||||||
|
if common.FileExist(nodesFile) {
|
||||||
|
inputSet = loadNodesJSON(nodesFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
disc := startV5(ctx)
|
||||||
|
defer disc.Close()
|
||||||
|
c := newCrawler(inputSet, disc, disc.RandomNodes())
|
||||||
|
c.revalidateInterval = 10 * time.Minute
|
||||||
|
output := c.run(ctx.Duration(crawlTimeoutFlag.Name))
|
||||||
|
writeNodesJSON(nodesFile, output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func discv5Listen(ctx *cli.Context) error {
|
||||||
|
disc := startV5(ctx)
|
||||||
|
defer disc.Close()
|
||||||
|
|
||||||
|
fmt.Println(disc.Self())
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// startV5 starts an ephemeral discovery v5 node.
|
||||||
|
func startV5(ctx *cli.Context) *discover.UDPv5 {
|
||||||
|
ln, config := makeDiscoveryConfig(ctx)
|
||||||
|
socket := listen(ln, ctx.String(listenAddrFlag.Name))
|
||||||
|
disc, err := discover.ListenV5(socket, ln, config)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
return disc
|
||||||
|
}
|
||||||
|
|
@ -59,6 +59,7 @@ func init() {
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
enrdumpCommand,
|
enrdumpCommand,
|
||||||
discv4Command,
|
discv4Command,
|
||||||
|
discv5Command,
|
||||||
dnsCommand,
|
dnsCommand,
|
||||||
nodesetCommand,
|
nodesetCommand,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ func ethFilter(args []string) (nodeFilter, error) {
|
||||||
case "goerli":
|
case "goerli":
|
||||||
filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash)
|
filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash)
|
||||||
case "ropsten":
|
case "ropsten":
|
||||||
filter = forkid.NewStaticFilter(params.TestnetChainConfig, params.TestnetGenesisHash)
|
filter = forkid.NewStaticFilter(params.RopstenChainConfig, params.RopstenGenesisHash)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown network %q", args[0])
|
return nil, fmt.Errorf("unknown network %q", args[0])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
for _, st := range test.Subtests() {
|
for _, st := range test.Subtests() {
|
||||||
// Run the test and aggregate the result
|
// Run the test and aggregate the result
|
||||||
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
||||||
state, err := test.Run(st, cfg, false)
|
_, state, err := test.Run(st, cfg, false)
|
||||||
// print state root for evmlab tracing
|
// print state root for evmlab tracing
|
||||||
if ctx.GlobalBool(MachineFlag.Name) && state != nil {
|
if ctx.GlobalBool(MachineFlag.Name) && state != nil {
|
||||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false))
|
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false))
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,42 @@ Path of the secret key file: .*UTC--.+--[0-9a-f]{40}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccountImport(t *testing.T) {
|
||||||
|
tests := []struct{ name, key, output string }{
|
||||||
|
{
|
||||||
|
name: "correct account",
|
||||||
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||||
|
output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid character",
|
||||||
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
|
||||||
|
output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
importAccountWithExpect(t, test.key, test.output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func importAccountWithExpect(t *testing.T, key string, expected string) {
|
||||||
|
dir := tmpdir(t)
|
||||||
|
keyfile := filepath.Join(dir, "key.prv")
|
||||||
|
if err := ioutil.WriteFile(keyfile, []byte(key), 0600); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
passwordFile := filepath.Join(dir, "password.txt")
|
||||||
|
if err := ioutil.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
geth := runGeth(t, "account", "import", keyfile, "-password", passwordFile)
|
||||||
|
defer geth.ExpectExit()
|
||||||
|
geth.Expect(expected)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccountNewBadRepeat(t *testing.T) {
|
func TestAccountNewBadRepeat(t *testing.T) {
|
||||||
geth := runGeth(t, "account", "new", "--lightkdf")
|
geth := runGeth(t, "account", "new", "--lightkdf")
|
||||||
defer geth.ExpectExit()
|
defer geth.ExpectExit()
|
||||||
|
|
|
||||||
|
|
@ -157,9 +157,11 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
utils.TestnetFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
|
utils.GoerliFlag,
|
||||||
|
utils.LegacyTestnetFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
|
|
@ -205,9 +207,10 @@ Use "ethereum dump 0" to dump the genesis block.`,
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.AncientFlag,
|
utils.AncientFlag,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.TestnetFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
|
utils.LegacyTestnetFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
@ -147,12 +146,6 @@ func enableWhisper(ctx *cli.Context) bool {
|
||||||
|
|
||||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
stack, cfg := makeConfigNode(ctx)
|
stack, cfg := makeConfigNode(ctx)
|
||||||
if ctx.GlobalIsSet(utils.OverrideIstanbulFlag.Name) {
|
|
||||||
cfg.Eth.OverrideIstanbul = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideIstanbulFlag.Name))
|
|
||||||
}
|
|
||||||
if ctx.GlobalIsSet(utils.OverrideMuirGlacierFlag.Name) {
|
|
||||||
cfg.Eth.OverrideMuirGlacier = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideMuirGlacierFlag.Name))
|
|
||||||
}
|
|
||||||
utils.RegisterEthService(stack, &cfg.Eth)
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
||||||
|
|
|
||||||
|
|
@ -123,10 +123,19 @@ func remoteConsole(ctx *cli.Context) error {
|
||||||
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
||||||
}
|
}
|
||||||
if path != "" {
|
if path != "" {
|
||||||
if ctx.GlobalBool(utils.TestnetFlag.Name) {
|
if ctx.GlobalBool(utils.LegacyTestnetFlag.Name) || ctx.GlobalBool(utils.RopstenFlag.Name) {
|
||||||
path = filepath.Join(path, "testnet")
|
// Maintain compatibility with older Geth configurations storing the
|
||||||
|
// Ropsten database in `testnet` instead of `ropsten`.
|
||||||
|
legacyPath := filepath.Join(path, "testnet")
|
||||||
|
if _, err := os.Stat(legacyPath); !os.IsNotExist(err) {
|
||||||
|
path = legacyPath
|
||||||
|
} else {
|
||||||
|
path = filepath.Join(path, "ropsten")
|
||||||
|
}
|
||||||
} else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
|
} else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
|
||||||
path = filepath.Join(path, "rinkeby")
|
path = filepath.Join(path, "rinkeby")
|
||||||
|
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||||
|
path = filepath.Join(path, "goerli")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,6 @@ var (
|
||||||
utils.ExternalSignerFlag,
|
utils.ExternalSignerFlag,
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.OverrideIstanbulFlag,
|
|
||||||
utils.OverrideMuirGlacierFlag,
|
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
|
@ -96,11 +94,11 @@ var (
|
||||||
utils.SnapshotFlag,
|
utils.SnapshotFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
utils.LightServeFlag,
|
utils.LightServeFlag,
|
||||||
utils.LightLegacyServFlag,
|
utils.LegacyLightServFlag,
|
||||||
utils.LightIngressFlag,
|
utils.LightIngressFlag,
|
||||||
utils.LightEgressFlag,
|
utils.LightEgressFlag,
|
||||||
utils.LightMaxPeersFlag,
|
utils.LightMaxPeersFlag,
|
||||||
utils.LightLegacyPeersFlag,
|
utils.LegacyLightPeersFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.UltraLightServersFlag,
|
utils.UltraLightServersFlag,
|
||||||
utils.UltraLightFractionFlag,
|
utils.UltraLightFractionFlag,
|
||||||
|
|
@ -117,17 +115,17 @@ var (
|
||||||
utils.MaxPendingPeersFlag,
|
utils.MaxPendingPeersFlag,
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MinerLegacyThreadsFlag,
|
utils.LegacyMinerThreadsFlag,
|
||||||
utils.MinerNotifyFlag,
|
utils.MinerNotifyFlag,
|
||||||
utils.MinerGasTargetFlag,
|
utils.MinerGasTargetFlag,
|
||||||
utils.MinerLegacyGasTargetFlag,
|
utils.LegacyMinerGasTargetFlag,
|
||||||
utils.MinerGasLimitFlag,
|
utils.MinerGasLimitFlag,
|
||||||
utils.MinerGasPriceFlag,
|
utils.MinerGasPriceFlag,
|
||||||
utils.MinerLegacyGasPriceFlag,
|
utils.LegacyMinerGasPriceFlag,
|
||||||
utils.MinerEtherbaseFlag,
|
utils.MinerEtherbaseFlag,
|
||||||
utils.MinerLegacyEtherbaseFlag,
|
utils.LegacyMinerEtherbaseFlag,
|
||||||
utils.MinerExtraDataFlag,
|
utils.MinerExtraDataFlag,
|
||||||
utils.MinerLegacyExtraDataFlag,
|
utils.LegacyMinerExtraDataFlag,
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNoVerfiyFlag,
|
utils.MinerNoVerfiyFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
|
|
@ -139,7 +137,8 @@ var (
|
||||||
utils.DNSDiscoveryFlag,
|
utils.DNSDiscoveryFlag,
|
||||||
utils.DeveloperFlag,
|
utils.DeveloperFlag,
|
||||||
utils.DeveloperPeriodFlag,
|
utils.DeveloperPeriodFlag,
|
||||||
utils.TestnetFlag,
|
utils.LegacyTestnetFlag,
|
||||||
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
|
|
@ -148,29 +147,41 @@ var (
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
utils.NoCompactionFlag,
|
utils.NoCompactionFlag,
|
||||||
utils.GpoBlocksFlag,
|
utils.GpoBlocksFlag,
|
||||||
|
utils.LegacyGpoBlocksFlag,
|
||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
|
utils.LegacyGpoPercentileFlag,
|
||||||
utils.EWASMInterpreterFlag,
|
utils.EWASMInterpreterFlag,
|
||||||
utils.EVMInterpreterFlag,
|
utils.EVMInterpreterFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.HTTPPortFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
|
utils.LegacyRPCEnabledFlag,
|
||||||
|
utils.LegacyRPCListenAddrFlag,
|
||||||
|
utils.LegacyRPCPortFlag,
|
||||||
|
utils.LegacyRPCCORSDomainFlag,
|
||||||
|
utils.LegacyRPCVirtualHostsFlag,
|
||||||
utils.GraphQLEnabledFlag,
|
utils.GraphQLEnabledFlag,
|
||||||
utils.GraphQLListenAddrFlag,
|
utils.GraphQLListenAddrFlag,
|
||||||
utils.GraphQLPortFlag,
|
utils.GraphQLPortFlag,
|
||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
utils.RPCApiFlag,
|
utils.HTTPApiFlag,
|
||||||
|
utils.LegacyRPCApiFlag,
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
|
utils.LegacyWSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
|
utils.LegacyWSPortFlag,
|
||||||
utils.WSApiFlag,
|
utils.WSApiFlag,
|
||||||
|
utils.LegacyWSApiFlag,
|
||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
|
utils.LegacyWSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.InsecureUnlockAllowedFlag,
|
utils.InsecureUnlockAllowedFlag,
|
||||||
|
|
@ -229,6 +240,8 @@ func init() {
|
||||||
dumpConfigCommand,
|
dumpConfigCommand,
|
||||||
// See retesteth.go
|
// See retesteth.go
|
||||||
retestethCommand,
|
retestethCommand,
|
||||||
|
// See cmd/utils/flags_legacy.go
|
||||||
|
utils.ShowDeprecated,
|
||||||
}
|
}
|
||||||
sort.Sort(cli.CommandsByName(app.Commands))
|
sort.Sort(cli.CommandsByName(app.Commands))
|
||||||
|
|
||||||
|
|
@ -236,6 +249,7 @@ func init() {
|
||||||
app.Flags = append(app.Flags, rpcFlags...)
|
app.Flags = append(app.Flags, rpcFlags...)
|
||||||
app.Flags = append(app.Flags, consoleFlags...)
|
app.Flags = append(app.Flags, consoleFlags...)
|
||||||
app.Flags = append(app.Flags, debug.Flags...)
|
app.Flags = append(app.Flags, debug.Flags...)
|
||||||
|
app.Flags = append(app.Flags, debug.DeprecatedFlags...)
|
||||||
app.Flags = append(app.Flags, whisperFlags...)
|
app.Flags = append(app.Flags, whisperFlags...)
|
||||||
app.Flags = append(app.Flags, metricsFlags...)
|
app.Flags = append(app.Flags, metricsFlags...)
|
||||||
|
|
||||||
|
|
@ -259,10 +273,32 @@ func main() {
|
||||||
// prepare manipulates memory cache allowance and setups metric system.
|
// prepare manipulates memory cache allowance and setups metric system.
|
||||||
// This function should be called before launching devp2p stack.
|
// This function should be called before launching devp2p stack.
|
||||||
func prepare(ctx *cli.Context) {
|
func prepare(ctx *cli.Context) {
|
||||||
|
// If we're running a known preset, log it for convenience.
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ropsten testnet...")
|
||||||
|
log.Warn("The --testnet flag is ambiguous! Please specify one of --goerli, --rinkeby, or --ropsten.")
|
||||||
|
log.Warn("The generic --testnet flag is deprecated and will be removed in the future!")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.RopstenFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ropsten testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.RinkebyFlag.Name):
|
||||||
|
log.Info("Starting Geth on Rinkeby testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.GoerliFlag.Name):
|
||||||
|
log.Info("Starting Geth on Görli testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
|
||||||
|
log.Info("Starting Geth in ephemeral dev mode...")
|
||||||
|
|
||||||
|
case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ethereum mainnet...")
|
||||||
|
}
|
||||||
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
||||||
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
|
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
|
||||||
// Make sure we're not on any supported preconfigured testnet either
|
// Make sure we're not on any supported preconfigured testnet either
|
||||||
if !ctx.GlobalIsSet(utils.TestnetFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
|
if !ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name) && !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
|
||||||
// Nope, we're really on mainnet. Bump that cache up!
|
// Nope, we're really on mainnet. Bump that cache up!
|
||||||
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
|
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
|
||||||
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
|
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
|
||||||
|
|
@ -279,6 +315,10 @@ func prepare(ctx *cli.Context) {
|
||||||
// Check https://github.com/elastic/gosigar#supported-platforms
|
// Check https://github.com/elastic/gosigar#supported-platforms
|
||||||
if runtime.GOOS != "openbsd" {
|
if runtime.GOOS != "openbsd" {
|
||||||
if err := mem.Get(); err == nil {
|
if err := mem.Get(); err == nil {
|
||||||
|
if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
|
||||||
|
log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
|
||||||
|
mem.Total = 2 * 1024 * 1024 * 1024
|
||||||
|
}
|
||||||
allowance := int(mem.Total / 1024 / 1024 / 3)
|
allowance := int(mem.Total / 1024 / 1024 / 3)
|
||||||
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
|
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
|
||||||
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
|
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
|
||||||
|
|
@ -340,7 +380,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
|
|
||||||
// Set contract backend for ethereum service if local node
|
// Set contract backend for ethereum service if local node
|
||||||
// is serving LES requests.
|
// is serving LES requests.
|
||||||
if ctx.GlobalInt(utils.LightLegacyServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
|
if ctx.GlobalInt(utils.LegacyLightServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
|
||||||
var ethService *eth.Ethereum
|
var ethService *eth.Ethereum
|
||||||
if err := stack.Service(ðService); err != nil {
|
if err := stack.Service(ðService); err != nil {
|
||||||
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
||||||
|
|
@ -425,16 +465,18 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
utils.Fatalf("Ethereum service not running: %v", err)
|
utils.Fatalf("Ethereum service not running: %v", err)
|
||||||
}
|
}
|
||||||
// Set the gas price to the limits from the CLI and start mining
|
// Set the gas price to the limits from the CLI and start mining
|
||||||
gasprice := utils.GlobalBig(ctx, utils.MinerLegacyGasPriceFlag.Name)
|
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
||||||
if ctx.IsSet(utils.MinerGasPriceFlag.Name) {
|
if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) {
|
||||||
gasprice = utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name)
|
||||||
}
|
}
|
||||||
ethereum.TxPool().SetGasPrice(gasprice)
|
ethereum.TxPool().SetGasPrice(gasprice)
|
||||||
|
|
||||||
threads := ctx.GlobalInt(utils.MinerLegacyThreadsFlag.Name)
|
threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
|
||||||
if ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
|
if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
|
||||||
threads = ctx.GlobalInt(utils.MinerThreadsFlag.Name)
|
threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name)
|
||||||
|
log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ethereum.StartMining(threads); err != nil {
|
if err := ethereum.StartMining(threads); err != nil {
|
||||||
utils.Fatalf("Failed to start mining: %v", err)
|
utils.Fatalf("Failed to start mining: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -678,7 +678,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
@ -788,7 +788,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
@ -887,8 +887,16 @@ func retesteth(ctx *cli.Context) error {
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
vhosts := splitAndTrim(ctx.GlobalString(utils.RPCVirtualHostsFlag.Name))
|
vhosts := splitAndTrim(ctx.GlobalString(utils.HTTPVirtualHostsFlag.Name))
|
||||||
cors := splitAndTrim(ctx.GlobalString(utils.RPCCORSDomainFlag.Name))
|
cors := splitAndTrim(ctx.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
|
// register apis and create handler stack
|
||||||
|
srv := rpc.NewServer()
|
||||||
|
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"test", "eth", "debug", "web3"}, srv, false)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Could not register RPC apis: %w", err)
|
||||||
|
}
|
||||||
|
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
var RetestethHTTPTimeouts = rpc.HTTPTimeouts{
|
var RetestethHTTPTimeouts = rpc.HTTPTimeouts{
|
||||||
|
|
@ -896,8 +904,8 @@ func retesteth(ctx *cli.Context) error {
|
||||||
WriteTimeout: 120 * time.Second,
|
WriteTimeout: 120 * time.Second,
|
||||||
IdleTimeout: 120 * time.Second,
|
IdleTimeout: 120 * time.Second,
|
||||||
}
|
}
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.HTTPListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
||||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"test", "eth", "debug", "web3"}, cors, vhosts, RetestethHTTPTimeouts)
|
httpServer, _, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -905,7 +913,8 @@ func retesteth(ctx *cli.Context) error {
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
|
httpServer.Shutdown(context.Background())
|
||||||
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,9 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.TestnetFlag,
|
|
||||||
utils.RinkebyFlag,
|
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
|
utils.RinkebyFlag,
|
||||||
|
utils.RopstenFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
|
|
@ -158,13 +158,12 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.HTTPPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.HTTPApiFlag,
|
||||||
utils.RPCGlobalGasCap,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
|
|
@ -175,6 +174,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.GraphQLPortFlag,
|
utils.GraphQLPortFlag,
|
||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
|
utils.RPCGlobalGasCap,
|
||||||
utils.JSpathFlag,
|
utils.JSpathFlag,
|
||||||
utils.ExecFlag,
|
utils.ExecFlag,
|
||||||
utils.PreloadJSFlag,
|
utils.PreloadJSFlag,
|
||||||
|
|
@ -244,19 +244,28 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Flags: whisperFlags,
|
Flags: whisperFlags,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "DEPRECATED",
|
Name: "ALIASED (deprecated)",
|
||||||
Flags: []cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
utils.LightLegacyServFlag,
|
utils.LegacyRPCEnabledFlag,
|
||||||
utils.LightLegacyPeersFlag,
|
utils.LegacyRPCListenAddrFlag,
|
||||||
utils.MinerLegacyThreadsFlag,
|
utils.LegacyRPCPortFlag,
|
||||||
utils.MinerLegacyGasTargetFlag,
|
utils.LegacyRPCCORSDomainFlag,
|
||||||
utils.MinerLegacyGasPriceFlag,
|
utils.LegacyRPCVirtualHostsFlag,
|
||||||
utils.MinerLegacyEtherbaseFlag,
|
utils.LegacyRPCApiFlag,
|
||||||
utils.MinerLegacyExtraDataFlag,
|
utils.LegacyWSListenAddrFlag,
|
||||||
},
|
utils.LegacyWSPortFlag,
|
||||||
|
utils.LegacyWSAllowedOriginsFlag,
|
||||||
|
utils.LegacyWSApiFlag,
|
||||||
|
utils.LegacyGpoBlocksFlag,
|
||||||
|
utils.LegacyGpoPercentileFlag,
|
||||||
|
}, debug.DeprecatedFlags...),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "MISC",
|
Name: "MISC",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
utils.SnapshotFlag,
|
||||||
|
cli.HelpFlag,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,10 +323,17 @@ func init() {
|
||||||
categorized[flag.String()] = struct{}{}
|
categorized[flag.String()] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
deprecated := make(map[string]struct{})
|
||||||
|
for _, flag := range utils.DeprecatedFlags {
|
||||||
|
deprecated[flag.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
// Only add uncategorized flags if they are not deprecated
|
||||||
var uncategorized []cli.Flag
|
var uncategorized []cli.Flag
|
||||||
for _, flag := range data.(*cli.App).Flags {
|
for _, flag := range data.(*cli.App).Flags {
|
||||||
if _, ok := categorized[flag.String()]; !ok {
|
if _, ok := categorized[flag.String()]; !ok {
|
||||||
uncategorized = append(uncategorized, flag)
|
if _, ok := deprecated[flag.String()]; !ok {
|
||||||
|
uncategorized = append(uncategorized, flag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(uncategorized) > 0 {
|
if len(uncategorized) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -182,11 +182,11 @@ func checkWallet(client *sshClient, network string) (*walletInfos, error) {
|
||||||
// Run a sanity check to see if the devp2p and RPC ports are reachable
|
// Run a sanity check to see if the devp2p and RPC ports are reachable
|
||||||
nodePort := infos.portmap[infos.envvars["NODE_PORT"]]
|
nodePort := infos.portmap[infos.envvars["NODE_PORT"]]
|
||||||
if err = checkPort(client.server, nodePort); err != nil {
|
if err = checkPort(client.server, nodePort); err != nil {
|
||||||
log.Warn(fmt.Sprintf("Wallet devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err)
|
log.Warn("Wallet devp2p port seems unreachable", "server", client.server, "port", nodePort, "err", err)
|
||||||
}
|
}
|
||||||
rpcPort := infos.portmap["8545/tcp"]
|
rpcPort := infos.portmap["8545/tcp"]
|
||||||
if err = checkPort(client.server, rpcPort); err != nil {
|
if err = checkPort(client.server, rpcPort); err != nil {
|
||||||
log.Warn(fmt.Sprintf("Wallet RPC port seems unreachable"), "server", client.server, "port", rpcPort, "err", err)
|
log.Warn("Wallet RPC port seems unreachable", "server", client.server, "port", rpcPort, "err", err)
|
||||||
}
|
}
|
||||||
// Assemble and return the useful infos
|
// Assemble and return the useful infos
|
||||||
stats := &walletInfos{
|
stats := &walletInfos{
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ func ExportPreimages(db ethdb.Database, fn string) error {
|
||||||
defer writer.(*gzip.Writer).Close()
|
defer writer.(*gzip.Writer).Close()
|
||||||
}
|
}
|
||||||
// Iterate over the preimages and export them
|
// Iterate over the preimages and export them
|
||||||
it := db.NewIteratorWithPrefix([]byte("secure-key-"))
|
it := db.NewIterator([]byte("secure-key-"), nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
|
||||||
|
|
@ -162,20 +162,20 @@ var (
|
||||||
}
|
}
|
||||||
NetworkIdFlag = cli.Uint64Flag{
|
NetworkIdFlag = cli.Uint64Flag{
|
||||||
Name: "networkid",
|
Name: "networkid",
|
||||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
|
Usage: "Network identifier (integer, 1=Frontier, 3=Ropsten, 4=Rinkeby, 5=Görli)",
|
||||||
Value: eth.DefaultConfig.NetworkId,
|
Value: eth.DefaultConfig.NetworkId,
|
||||||
}
|
}
|
||||||
TestnetFlag = cli.BoolFlag{
|
GoerliFlag = cli.BoolFlag{
|
||||||
Name: "testnet",
|
Name: "goerli",
|
||||||
Usage: "Ropsten network: pre-configured proof-of-work test network",
|
Usage: "Görli network: pre-configured proof-of-authority test network",
|
||||||
}
|
}
|
||||||
RinkebyFlag = cli.BoolFlag{
|
RinkebyFlag = cli.BoolFlag{
|
||||||
Name: "rinkeby",
|
Name: "rinkeby",
|
||||||
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
||||||
}
|
}
|
||||||
GoerliFlag = cli.BoolFlag{
|
RopstenFlag = cli.BoolFlag{
|
||||||
Name: "goerli",
|
Name: "ropsten",
|
||||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
Usage: "Ropsten network: pre-configured proof-of-work test network",
|
||||||
}
|
}
|
||||||
DeveloperFlag = cli.BoolFlag{
|
DeveloperFlag = cli.BoolFlag{
|
||||||
Name: "dev",
|
Name: "dev",
|
||||||
|
|
@ -242,20 +242,7 @@ var (
|
||||||
Name: "whitelist",
|
Name: "whitelist",
|
||||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
||||||
}
|
}
|
||||||
OverrideIstanbulFlag = cli.Uint64Flag{
|
|
||||||
Name: "override.istanbul",
|
|
||||||
Usage: "Manually specify Istanbul fork-block, overriding the bundled setting",
|
|
||||||
}
|
|
||||||
OverrideMuirGlacierFlag = cli.Uint64Flag{
|
|
||||||
Name: "override.muirglacier",
|
|
||||||
Usage: "Manually specify Muir Glacier fork-block, overriding the bundled setting",
|
|
||||||
}
|
|
||||||
// Light server and client settings
|
// Light server and client settings
|
||||||
LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021
|
|
||||||
Name: "lightserv",
|
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)",
|
|
||||||
Value: eth.DefaultConfig.LightServ,
|
|
||||||
}
|
|
||||||
LightServeFlag = cli.IntFlag{
|
LightServeFlag = cli.IntFlag{
|
||||||
Name: "light.serve",
|
Name: "light.serve",
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)",
|
Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)",
|
||||||
|
|
@ -271,11 +258,6 @@ var (
|
||||||
Usage: "Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
|
Usage: "Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
|
||||||
Value: eth.DefaultConfig.LightEgress,
|
Value: eth.DefaultConfig.LightEgress,
|
||||||
}
|
}
|
||||||
LightLegacyPeersFlag = cli.IntFlag{ // Deprecated in favor of light.maxpeers, remove in 2021
|
|
||||||
Name: "lightpeers",
|
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)",
|
|
||||||
Value: eth.DefaultConfig.LightPeers,
|
|
||||||
}
|
|
||||||
LightMaxPeersFlag = cli.IntFlag{
|
LightMaxPeersFlag = cli.IntFlag{
|
||||||
Name: "light.maxpeers",
|
Name: "light.maxpeers",
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to",
|
Usage: "Maximum number of light clients to serve, or light servers to attach to",
|
||||||
|
|
@ -427,11 +409,6 @@ var (
|
||||||
Usage: "Number of CPU threads to use for mining",
|
Usage: "Number of CPU threads to use for mining",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
}
|
}
|
||||||
MinerLegacyThreadsFlag = cli.IntFlag{
|
|
||||||
Name: "minerthreads",
|
|
||||||
Usage: "Number of CPU threads to use for mining (deprecated, use --miner.threads)",
|
|
||||||
Value: 0,
|
|
||||||
}
|
|
||||||
MinerNotifyFlag = cli.StringFlag{
|
MinerNotifyFlag = cli.StringFlag{
|
||||||
Name: "miner.notify",
|
Name: "miner.notify",
|
||||||
Usage: "Comma separated HTTP URL list to notify of new work packages",
|
Usage: "Comma separated HTTP URL list to notify of new work packages",
|
||||||
|
|
@ -441,11 +418,6 @@ var (
|
||||||
Usage: "Target gas floor for mined blocks",
|
Usage: "Target gas floor for mined blocks",
|
||||||
Value: eth.DefaultConfig.Miner.GasFloor,
|
Value: eth.DefaultConfig.Miner.GasFloor,
|
||||||
}
|
}
|
||||||
MinerLegacyGasTargetFlag = cli.Uint64Flag{
|
|
||||||
Name: "targetgaslimit",
|
|
||||||
Usage: "Target gas floor for mined blocks (deprecated, use --miner.gastarget)",
|
|
||||||
Value: eth.DefaultConfig.Miner.GasFloor,
|
|
||||||
}
|
|
||||||
MinerGasLimitFlag = cli.Uint64Flag{
|
MinerGasLimitFlag = cli.Uint64Flag{
|
||||||
Name: "miner.gaslimit",
|
Name: "miner.gaslimit",
|
||||||
Usage: "Target gas ceiling for mined blocks",
|
Usage: "Target gas ceiling for mined blocks",
|
||||||
|
|
@ -456,29 +428,15 @@ var (
|
||||||
Usage: "Minimum gas price for mining a transaction",
|
Usage: "Minimum gas price for mining a transaction",
|
||||||
Value: eth.DefaultConfig.Miner.GasPrice,
|
Value: eth.DefaultConfig.Miner.GasPrice,
|
||||||
}
|
}
|
||||||
MinerLegacyGasPriceFlag = BigFlag{
|
|
||||||
Name: "gasprice",
|
|
||||||
Usage: "Minimum gas price for mining a transaction (deprecated, use --miner.gasprice)",
|
|
||||||
Value: eth.DefaultConfig.Miner.GasPrice,
|
|
||||||
}
|
|
||||||
MinerEtherbaseFlag = cli.StringFlag{
|
MinerEtherbaseFlag = cli.StringFlag{
|
||||||
Name: "miner.etherbase",
|
Name: "miner.etherbase",
|
||||||
Usage: "Public address for block mining rewards (default = first account)",
|
Usage: "Public address for block mining rewards (default = first account)",
|
||||||
Value: "0",
|
Value: "0",
|
||||||
}
|
}
|
||||||
MinerLegacyEtherbaseFlag = cli.StringFlag{
|
|
||||||
Name: "etherbase",
|
|
||||||
Usage: "Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase)",
|
|
||||||
Value: "0",
|
|
||||||
}
|
|
||||||
MinerExtraDataFlag = cli.StringFlag{
|
MinerExtraDataFlag = cli.StringFlag{
|
||||||
Name: "miner.extradata",
|
Name: "miner.extradata",
|
||||||
Usage: "Block extra data set by the miner (default = client version)",
|
Usage: "Block extra data set by the miner (default = client version)",
|
||||||
}
|
}
|
||||||
MinerLegacyExtraDataFlag = cli.StringFlag{
|
|
||||||
Name: "extradata",
|
|
||||||
Usage: "Block extra data set by the miner (default = client version, deprecated, use --miner.extradata)",
|
|
||||||
}
|
|
||||||
MinerRecommitIntervalFlag = cli.DurationFlag{
|
MinerRecommitIntervalFlag = cli.DurationFlag{
|
||||||
Name: "miner.recommit",
|
Name: "miner.recommit",
|
||||||
Usage: "Time interval to recreate the block being mined",
|
Usage: "Time interval to recreate the block being mined",
|
||||||
|
|
@ -538,32 +496,32 @@ var (
|
||||||
Name: "ipcpath",
|
Name: "ipcpath",
|
||||||
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
||||||
}
|
}
|
||||||
RPCEnabledFlag = cli.BoolFlag{
|
HTTPEnabledFlag = cli.BoolFlag{
|
||||||
Name: "rpc",
|
Name: "http",
|
||||||
Usage: "Enable the HTTP-RPC server",
|
Usage: "Enable the HTTP-RPC server",
|
||||||
}
|
}
|
||||||
RPCListenAddrFlag = cli.StringFlag{
|
HTTPListenAddrFlag = cli.StringFlag{
|
||||||
Name: "rpcaddr",
|
Name: "http.addr",
|
||||||
Usage: "HTTP-RPC server listening interface",
|
Usage: "HTTP-RPC server listening interface",
|
||||||
Value: node.DefaultHTTPHost,
|
Value: node.DefaultHTTPHost,
|
||||||
}
|
}
|
||||||
RPCPortFlag = cli.IntFlag{
|
HTTPPortFlag = cli.IntFlag{
|
||||||
Name: "rpcport",
|
Name: "http.port",
|
||||||
Usage: "HTTP-RPC server listening port",
|
Usage: "HTTP-RPC server listening port",
|
||||||
Value: node.DefaultHTTPPort,
|
Value: node.DefaultHTTPPort,
|
||||||
}
|
}
|
||||||
RPCCORSDomainFlag = cli.StringFlag{
|
HTTPCORSDomainFlag = cli.StringFlag{
|
||||||
Name: "rpccorsdomain",
|
Name: "http.corsdomain",
|
||||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
RPCVirtualHostsFlag = cli.StringFlag{
|
HTTPVirtualHostsFlag = cli.StringFlag{
|
||||||
Name: "rpcvhosts",
|
Name: "http.vhosts",
|
||||||
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
||||||
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
|
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
|
||||||
}
|
}
|
||||||
RPCApiFlag = cli.StringFlag{
|
HTTPApiFlag = cli.StringFlag{
|
||||||
Name: "rpcapi",
|
Name: "http.api",
|
||||||
Usage: "API's offered over the HTTP-RPC interface",
|
Usage: "API's offered over the HTTP-RPC interface",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
|
|
@ -572,22 +530,22 @@ var (
|
||||||
Usage: "Enable the WS-RPC server",
|
Usage: "Enable the WS-RPC server",
|
||||||
}
|
}
|
||||||
WSListenAddrFlag = cli.StringFlag{
|
WSListenAddrFlag = cli.StringFlag{
|
||||||
Name: "wsaddr",
|
Name: "ws.addr",
|
||||||
Usage: "WS-RPC server listening interface",
|
Usage: "WS-RPC server listening interface",
|
||||||
Value: node.DefaultWSHost,
|
Value: node.DefaultWSHost,
|
||||||
}
|
}
|
||||||
WSPortFlag = cli.IntFlag{
|
WSPortFlag = cli.IntFlag{
|
||||||
Name: "wsport",
|
Name: "ws.port",
|
||||||
Usage: "WS-RPC server listening port",
|
Usage: "WS-RPC server listening port",
|
||||||
Value: node.DefaultWSPort,
|
Value: node.DefaultWSPort,
|
||||||
}
|
}
|
||||||
WSApiFlag = cli.StringFlag{
|
WSApiFlag = cli.StringFlag{
|
||||||
Name: "wsapi",
|
Name: "ws.api",
|
||||||
Usage: "API's offered over the WS-RPC interface",
|
Usage: "API's offered over the WS-RPC interface",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
WSAllowedOriginsFlag = cli.StringFlag{
|
WSAllowedOriginsFlag = cli.StringFlag{
|
||||||
Name: "wsorigins",
|
Name: "ws.origins",
|
||||||
Usage: "Origins from which to accept websockets requests",
|
Usage: "Origins from which to accept websockets requests",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
|
|
@ -694,12 +652,12 @@ var (
|
||||||
|
|
||||||
// Gas price oracle settings
|
// Gas price oracle settings
|
||||||
GpoBlocksFlag = cli.IntFlag{
|
GpoBlocksFlag = cli.IntFlag{
|
||||||
Name: "gpoblocks",
|
Name: "gpo.blocks",
|
||||||
Usage: "Number of recent blocks to check for gas prices",
|
Usage: "Number of recent blocks to check for gas prices",
|
||||||
Value: eth.DefaultConfig.GPO.Blocks,
|
Value: eth.DefaultConfig.GPO.Blocks,
|
||||||
}
|
}
|
||||||
GpoPercentileFlag = cli.IntFlag{
|
GpoPercentileFlag = cli.IntFlag{
|
||||||
Name: "gpopercentile",
|
Name: "gpo.percentile",
|
||||||
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
|
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
|
||||||
Value: eth.DefaultConfig.GPO.Percentile,
|
Value: eth.DefaultConfig.GPO.Percentile,
|
||||||
}
|
}
|
||||||
|
|
@ -764,7 +722,6 @@ var (
|
||||||
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
||||||
Value: "host=localhost",
|
Value: "host=localhost",
|
||||||
}
|
}
|
||||||
|
|
||||||
EWASMInterpreterFlag = cli.StringFlag{
|
EWASMInterpreterFlag = cli.StringFlag{
|
||||||
Name: "vm.ewasm",
|
Name: "vm.ewasm",
|
||||||
Usage: "External ewasm configuration (default = built-in interpreter)",
|
Usage: "External ewasm configuration (default = built-in interpreter)",
|
||||||
|
|
@ -779,11 +736,17 @@ var (
|
||||||
|
|
||||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||||
// if none (or the empty string) is specified. If the node is starting a testnet,
|
// if none (or the empty string) is specified. If the node is starting a testnet,
|
||||||
// the a subdirectory of the specified datadir will be used.
|
// then a subdirectory of the specified datadir will be used.
|
||||||
func MakeDataDir(ctx *cli.Context) string {
|
func MakeDataDir(ctx *cli.Context) string {
|
||||||
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
|
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
|
||||||
if ctx.GlobalBool(TestnetFlag.Name) {
|
if ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name) {
|
||||||
return filepath.Join(path, "testnet")
|
// Maintain compatibility with older Geth configurations storing the
|
||||||
|
// Ropsten database in `testnet` instead of `ropsten`.
|
||||||
|
legacyPath := filepath.Join(path, "testnet")
|
||||||
|
if _, err := os.Stat(legacyPath); !os.IsNotExist(err) {
|
||||||
|
return legacyPath
|
||||||
|
}
|
||||||
|
return filepath.Join(path, "ropsten")
|
||||||
}
|
}
|
||||||
if ctx.GlobalBool(RinkebyFlag.Name) {
|
if ctx.GlobalBool(RinkebyFlag.Name) {
|
||||||
return filepath.Join(path, "rinkeby")
|
return filepath.Join(path, "rinkeby")
|
||||||
|
|
@ -841,8 +804,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
} else {
|
} else {
|
||||||
urls = splitAndTrim(ctx.GlobalString(BootnodesFlag.Name))
|
urls = splitAndTrim(ctx.GlobalString(BootnodesFlag.Name))
|
||||||
}
|
}
|
||||||
case ctx.GlobalBool(TestnetFlag.Name):
|
case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name):
|
||||||
urls = params.TestnetBootnodes
|
urls = params.RopstenBootnodes
|
||||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||||
urls = params.RinkebyBootnodes
|
urls = params.RinkebyBootnodes
|
||||||
case ctx.GlobalBool(GoerliFlag.Name):
|
case ctx.GlobalBool(GoerliFlag.Name):
|
||||||
|
|
@ -928,23 +891,51 @@ func splitAndTrim(input string) []string {
|
||||||
// setHTTP creates the HTTP RPC listener interface string from the set
|
// setHTTP creates the HTTP RPC listener interface string from the set
|
||||||
// command line flags, returning empty if the HTTP endpoint is disabled.
|
// command line flags, returning empty if the HTTP endpoint is disabled.
|
||||||
func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
||||||
if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" {
|
if ctx.GlobalBool(LegacyRPCEnabledFlag.Name) && cfg.HTTPHost == "" {
|
||||||
|
log.Warn("The flag --rpc is deprecated and will be removed in the future, please use --http")
|
||||||
cfg.HTTPHost = "127.0.0.1"
|
cfg.HTTPHost = "127.0.0.1"
|
||||||
if ctx.GlobalIsSet(RPCListenAddrFlag.Name) {
|
if ctx.GlobalIsSet(LegacyRPCListenAddrFlag.Name) {
|
||||||
cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
|
cfg.HTTPHost = ctx.GlobalString(LegacyRPCListenAddrFlag.Name)
|
||||||
|
log.Warn("The flag --rpcaddr is deprecated and will be removed in the future, please use --http.addr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
if ctx.GlobalBool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" {
|
||||||
cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
|
cfg.HTTPHost = "127.0.0.1"
|
||||||
|
if ctx.GlobalIsSet(HTTPListenAddrFlag.Name) {
|
||||||
|
cfg.HTTPHost = ctx.GlobalString(HTTPListenAddrFlag.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
|
|
||||||
cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name))
|
if ctx.GlobalIsSet(LegacyRPCPortFlag.Name) {
|
||||||
|
cfg.HTTPPort = ctx.GlobalInt(LegacyRPCPortFlag.Name)
|
||||||
|
log.Warn("The flag --rpcport is deprecated and will be removed in the future, please use --http.port")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(RPCApiFlag.Name) {
|
if ctx.GlobalIsSet(HTTPPortFlag.Name) {
|
||||||
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
|
cfg.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) {
|
|
||||||
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
|
if ctx.GlobalIsSet(LegacyRPCCORSDomainFlag.Name) {
|
||||||
|
cfg.HTTPCors = splitAndTrim(ctx.GlobalString(LegacyRPCCORSDomainFlag.Name))
|
||||||
|
log.Warn("The flag --rpccorsdomain is deprecated and will be removed in the future, please use --http.corsdomain")
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(HTTPCORSDomainFlag.Name) {
|
||||||
|
cfg.HTTPCors = splitAndTrim(ctx.GlobalString(HTTPCORSDomainFlag.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(LegacyRPCApiFlag.Name) {
|
||||||
|
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(LegacyRPCApiFlag.Name))
|
||||||
|
log.Warn("The flag --rpcapi is deprecated and will be removed in the future, please use --http.api")
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(HTTPApiFlag.Name) {
|
||||||
|
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(HTTPApiFlag.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(LegacyRPCVirtualHostsFlag.Name) {
|
||||||
|
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(LegacyRPCVirtualHostsFlag.Name))
|
||||||
|
log.Warn("The flag --rpcvhosts is deprecated and will be removed in the future, please use --http.vhosts")
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(HTTPVirtualHostsFlag.Name) {
|
||||||
|
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(HTTPVirtualHostsFlag.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -971,16 +962,34 @@ func setGraphQL(ctx *cli.Context, cfg *node.Config) {
|
||||||
func setWS(ctx *cli.Context, cfg *node.Config) {
|
func setWS(ctx *cli.Context, cfg *node.Config) {
|
||||||
if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" {
|
if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" {
|
||||||
cfg.WSHost = "127.0.0.1"
|
cfg.WSHost = "127.0.0.1"
|
||||||
|
if ctx.GlobalIsSet(LegacyWSListenAddrFlag.Name) {
|
||||||
|
cfg.WSHost = ctx.GlobalString(LegacyWSListenAddrFlag.Name)
|
||||||
|
log.Warn("The flag --wsaddr is deprecated and will be removed in the future, please use --ws.addr")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(WSListenAddrFlag.Name) {
|
if ctx.GlobalIsSet(WSListenAddrFlag.Name) {
|
||||||
cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
|
cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(LegacyWSPortFlag.Name) {
|
||||||
|
cfg.WSPort = ctx.GlobalInt(LegacyWSPortFlag.Name)
|
||||||
|
log.Warn("The flag --wsport is deprecated and will be removed in the future, please use --ws.port")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
||||||
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(LegacyWSAllowedOriginsFlag.Name) {
|
||||||
|
cfg.WSOrigins = splitAndTrim(ctx.GlobalString(LegacyWSAllowedOriginsFlag.Name))
|
||||||
|
log.Warn("The flag --wsorigins is deprecated and will be removed in the future, please use --ws.origins")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) {
|
if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) {
|
||||||
cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name))
|
cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(LegacyWSApiFlag.Name) {
|
||||||
|
cfg.WSModules = splitAndTrim(ctx.GlobalString(LegacyWSApiFlag.Name))
|
||||||
|
log.Warn("The flag --wsapi is deprecated and will be removed in the future, please use --ws.api")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(WSApiFlag.Name) {
|
if ctx.GlobalIsSet(WSApiFlag.Name) {
|
||||||
cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name))
|
cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
@ -1000,8 +1009,9 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
|
|
||||||
// setLes configures the les server and ultra light client settings from the command line flags.
|
// setLes configures the les server and ultra light client settings from the command line flags.
|
||||||
func setLes(ctx *cli.Context, cfg *eth.Config) {
|
func setLes(ctx *cli.Context, cfg *eth.Config) {
|
||||||
if ctx.GlobalIsSet(LightLegacyServFlag.Name) {
|
if ctx.GlobalIsSet(LegacyLightServFlag.Name) {
|
||||||
cfg.LightServ = ctx.GlobalInt(LightLegacyServFlag.Name)
|
cfg.LightServ = ctx.GlobalInt(LegacyLightServFlag.Name)
|
||||||
|
log.Warn("The flag --lightserv is deprecated and will be removed in the future, please use --light.serve")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(LightServeFlag.Name) {
|
if ctx.GlobalIsSet(LightServeFlag.Name) {
|
||||||
cfg.LightServ = ctx.GlobalInt(LightServeFlag.Name)
|
cfg.LightServ = ctx.GlobalInt(LightServeFlag.Name)
|
||||||
|
|
@ -1012,8 +1022,9 @@ func setLes(ctx *cli.Context, cfg *eth.Config) {
|
||||||
if ctx.GlobalIsSet(LightEgressFlag.Name) {
|
if ctx.GlobalIsSet(LightEgressFlag.Name) {
|
||||||
cfg.LightEgress = ctx.GlobalInt(LightEgressFlag.Name)
|
cfg.LightEgress = ctx.GlobalInt(LightEgressFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(LightLegacyPeersFlag.Name) {
|
if ctx.GlobalIsSet(LegacyLightPeersFlag.Name) {
|
||||||
cfg.LightPeers = ctx.GlobalInt(LightLegacyPeersFlag.Name)
|
cfg.LightPeers = ctx.GlobalInt(LegacyLightPeersFlag.Name)
|
||||||
|
log.Warn("The flag --lightpeers is deprecated and will be removed in the future, please use --light.maxpeers")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
||||||
cfg.LightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
|
cfg.LightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
|
||||||
|
|
@ -1077,8 +1088,10 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
|
||||||
func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
|
func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
|
||||||
// Extract the current etherbase, new flag overriding legacy one
|
// Extract the current etherbase, new flag overriding legacy one
|
||||||
var etherbase string
|
var etherbase string
|
||||||
if ctx.GlobalIsSet(MinerLegacyEtherbaseFlag.Name) {
|
if ctx.GlobalIsSet(LegacyMinerEtherbaseFlag.Name) {
|
||||||
etherbase = ctx.GlobalString(MinerLegacyEtherbaseFlag.Name)
|
etherbase = ctx.GlobalString(LegacyMinerEtherbaseFlag.Name)
|
||||||
|
log.Warn("The flag --etherbase is deprecated and will be removed in the future, please use --miner.etherbase")
|
||||||
|
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerEtherbaseFlag.Name) {
|
if ctx.GlobalIsSet(MinerEtherbaseFlag.Name) {
|
||||||
etherbase = ctx.GlobalString(MinerEtherbaseFlag.Name)
|
etherbase = ctx.GlobalString(MinerEtherbaseFlag.Name)
|
||||||
|
|
@ -1123,27 +1136,27 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
setBootstrapNodesV5(ctx, cfg)
|
setBootstrapNodesV5(ctx, cfg)
|
||||||
|
|
||||||
lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light"
|
lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light"
|
||||||
lightServer := (ctx.GlobalInt(LightLegacyServFlag.Name) != 0 || ctx.GlobalInt(LightServeFlag.Name) != 0)
|
lightServer := (ctx.GlobalInt(LegacyLightServFlag.Name) != 0 || ctx.GlobalInt(LightServeFlag.Name) != 0)
|
||||||
|
|
||||||
lightPeers := ctx.GlobalInt(LightLegacyPeersFlag.Name)
|
lightPeers := ctx.GlobalInt(LegacyLightPeersFlag.Name)
|
||||||
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
||||||
lightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
|
lightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
|
||||||
}
|
}
|
||||||
if lightClient && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
if lightClient && !ctx.GlobalIsSet(LegacyLightPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
||||||
// dynamic default - for clients we use 1/10th of the default for servers
|
// dynamic default - for clients we use 1/10th of the default for servers
|
||||||
lightPeers /= 10
|
lightPeers /= 10
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.GlobalIsSet(MaxPeersFlag.Name) {
|
if ctx.GlobalIsSet(MaxPeersFlag.Name) {
|
||||||
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
|
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
|
||||||
if lightServer && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
if lightServer && !ctx.GlobalIsSet(LegacyLightPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
|
||||||
cfg.MaxPeers += lightPeers
|
cfg.MaxPeers += lightPeers
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if lightServer {
|
if lightServer {
|
||||||
cfg.MaxPeers += lightPeers
|
cfg.MaxPeers += lightPeers
|
||||||
}
|
}
|
||||||
if lightClient && (ctx.GlobalIsSet(LightLegacyPeersFlag.Name) || ctx.GlobalIsSet(LightMaxPeersFlag.Name)) && cfg.MaxPeers < lightPeers {
|
if lightClient && (ctx.GlobalIsSet(LegacyLightPeersFlag.Name) || ctx.GlobalIsSet(LightMaxPeersFlag.Name)) && cfg.MaxPeers < lightPeers {
|
||||||
cfg.MaxPeers = lightPeers
|
cfg.MaxPeers = lightPeers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1245,8 +1258,16 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||||
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
|
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
|
||||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||||
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
||||||
case ctx.GlobalBool(TestnetFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case (ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name)) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
|
// Maintain compatibility with older Geth configurations storing the
|
||||||
|
// Ropsten database in `testnet` instead of `ropsten`.
|
||||||
|
legacyPath := filepath.Join(node.DefaultDataDir(), "testnet")
|
||||||
|
if _, err := os.Stat(legacyPath); !os.IsNotExist(err) {
|
||||||
|
log.Warn("Using the deprecated `testnet` datadir. Future versions will store the Ropsten chain in `ropsten`.")
|
||||||
|
cfg.DataDir = legacyPath
|
||||||
|
} else {
|
||||||
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ropsten")
|
||||||
|
}
|
||||||
case ctx.GlobalBool(RinkebyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.GlobalBool(RinkebyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||||
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
|
|
@ -1255,9 +1276,18 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
|
func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
|
||||||
|
if ctx.GlobalIsSet(LegacyGpoBlocksFlag.Name) {
|
||||||
|
cfg.Blocks = ctx.GlobalInt(LegacyGpoBlocksFlag.Name)
|
||||||
|
log.Warn("The flag --gpoblocks is deprecated and will be removed in the future, please use --gpo.blocks")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
||||||
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
|
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(LegacyGpoPercentileFlag.Name) {
|
||||||
|
cfg.Percentile = ctx.GlobalInt(LegacyGpoPercentileFlag.Name)
|
||||||
|
log.Warn("The flag --gpopercentile is deprecated and will be removed in the future, please use --gpo.percentile")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(GpoPercentileFlag.Name) {
|
if ctx.GlobalIsSet(GpoPercentileFlag.Name) {
|
||||||
cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name)
|
cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -1337,14 +1367,16 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||||
if ctx.GlobalIsSet(MinerNotifyFlag.Name) {
|
if ctx.GlobalIsSet(MinerNotifyFlag.Name) {
|
||||||
cfg.Notify = strings.Split(ctx.GlobalString(MinerNotifyFlag.Name), ",")
|
cfg.Notify = strings.Split(ctx.GlobalString(MinerNotifyFlag.Name), ",")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerLegacyExtraDataFlag.Name) {
|
if ctx.GlobalIsSet(LegacyMinerExtraDataFlag.Name) {
|
||||||
cfg.ExtraData = []byte(ctx.GlobalString(MinerLegacyExtraDataFlag.Name))
|
cfg.ExtraData = []byte(ctx.GlobalString(LegacyMinerExtraDataFlag.Name))
|
||||||
|
log.Warn("The flag --extradata is deprecated and will be removed in the future, please use --miner.extradata")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerExtraDataFlag.Name) {
|
if ctx.GlobalIsSet(MinerExtraDataFlag.Name) {
|
||||||
cfg.ExtraData = []byte(ctx.GlobalString(MinerExtraDataFlag.Name))
|
cfg.ExtraData = []byte(ctx.GlobalString(MinerExtraDataFlag.Name))
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerLegacyGasTargetFlag.Name) {
|
if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) {
|
||||||
cfg.GasFloor = ctx.GlobalUint64(MinerLegacyGasTargetFlag.Name)
|
cfg.GasFloor = ctx.GlobalUint64(LegacyMinerGasTargetFlag.Name)
|
||||||
|
log.Warn("The flag --targetgaslimit is deprecated and will be removed in the future, please use --miner.gastarget")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerGasTargetFlag.Name) {
|
if ctx.GlobalIsSet(MinerGasTargetFlag.Name) {
|
||||||
cfg.GasFloor = ctx.GlobalUint64(MinerGasTargetFlag.Name)
|
cfg.GasFloor = ctx.GlobalUint64(MinerGasTargetFlag.Name)
|
||||||
|
|
@ -1352,8 +1384,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||||
if ctx.GlobalIsSet(MinerGasLimitFlag.Name) {
|
if ctx.GlobalIsSet(MinerGasLimitFlag.Name) {
|
||||||
cfg.GasCeil = ctx.GlobalUint64(MinerGasLimitFlag.Name)
|
cfg.GasCeil = ctx.GlobalUint64(MinerGasLimitFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
|
if ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) {
|
||||||
cfg.GasPrice = GlobalBig(ctx, MinerLegacyGasPriceFlag.Name)
|
cfg.GasPrice = GlobalBig(ctx, LegacyMinerGasPriceFlag.Name)
|
||||||
|
log.Warn("The flag --gasprice is deprecated and will be removed in the future, please use --miner.gasprice")
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(MinerGasPriceFlag.Name) {
|
if ctx.GlobalIsSet(MinerGasPriceFlag.Name) {
|
||||||
cfg.GasPrice = GlobalBig(ctx, MinerGasPriceFlag.Name)
|
cfg.GasPrice = GlobalBig(ctx, MinerGasPriceFlag.Name)
|
||||||
|
|
@ -1446,8 +1479,8 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
|
||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags
|
||||||
CheckExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
|
CheckExclusive(ctx, DeveloperFlag, LegacyTestnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag)
|
||||||
CheckExclusive(ctx, LightLegacyServFlag, LightServeFlag, SyncModeFlag, "light")
|
CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light")
|
||||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||||
CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag)
|
CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag)
|
||||||
// todo(rjl493456442) make it available for les server
|
// todo(rjl493456442) make it available for les server
|
||||||
|
|
@ -1533,12 +1566,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
|
|
||||||
// Override any default configs for hard coded networks.
|
// Override any default configs for hard coded networks.
|
||||||
switch {
|
switch {
|
||||||
case ctx.GlobalBool(TestnetFlag.Name):
|
case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 3
|
cfg.NetworkId = 3
|
||||||
}
|
}
|
||||||
cfg.Genesis = core.DefaultTestnetGenesisBlock()
|
cfg.Genesis = core.DefaultRopstenGenesisBlock()
|
||||||
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.TestnetGenesisHash])
|
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.RopstenGenesisHash])
|
||||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 4
|
cfg.NetworkId = 4
|
||||||
|
|
@ -1574,7 +1607,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
log.Info("Using developer account", "address", developer.Address)
|
log.Info("Using developer account", "address", developer.Address)
|
||||||
|
|
||||||
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
|
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
|
||||||
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
|
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) {
|
||||||
cfg.Miner.GasPrice = big.NewInt(1)
|
cfg.Miner.GasPrice = big.NewInt(1)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -1720,8 +1753,8 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||||
var genesis *core.Genesis
|
var genesis *core.Genesis
|
||||||
switch {
|
switch {
|
||||||
case ctx.GlobalBool(TestnetFlag.Name):
|
case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name):
|
||||||
genesis = core.DefaultTestnetGenesisBlock()
|
genesis = core.DefaultRopstenGenesisBlock()
|
||||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||||
genesis = core.DefaultRinkebyGenesisBlock()
|
genesis = core.DefaultRinkebyGenesisBlock()
|
||||||
case ctx.GlobalBool(GoerliFlag.Name):
|
case ctx.GlobalBool(GoerliFlag.Name):
|
||||||
|
|
|
||||||
163
cmd/utils/flags_legacy.go
Normal file
163
cmd/utils/flags_legacy.go
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright 2020 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ShowDeprecated = cli.Command{
|
||||||
|
Action: showDeprecated,
|
||||||
|
Name: "show-deprecated-flags",
|
||||||
|
Usage: "Show flags that have been deprecated",
|
||||||
|
ArgsUsage: " ",
|
||||||
|
Category: "MISCELLANEOUS COMMANDS",
|
||||||
|
Description: "Show flags that have been deprecated and will soon be removed",
|
||||||
|
}
|
||||||
|
|
||||||
|
var DeprecatedFlags = []cli.Flag{
|
||||||
|
LegacyTestnetFlag,
|
||||||
|
LegacyLightServFlag,
|
||||||
|
LegacyLightPeersFlag,
|
||||||
|
LegacyMinerThreadsFlag,
|
||||||
|
LegacyMinerGasTargetFlag,
|
||||||
|
LegacyMinerGasPriceFlag,
|
||||||
|
LegacyMinerEtherbaseFlag,
|
||||||
|
LegacyMinerExtraDataFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// (Deprecated April 2018)
|
||||||
|
LegacyMinerThreadsFlag = cli.IntFlag{
|
||||||
|
Name: "minerthreads",
|
||||||
|
Usage: "Number of CPU threads to use for mining (deprecated, use --miner.threads)",
|
||||||
|
Value: 0,
|
||||||
|
}
|
||||||
|
LegacyMinerGasTargetFlag = cli.Uint64Flag{
|
||||||
|
Name: "targetgaslimit",
|
||||||
|
Usage: "Target gas floor for mined blocks (deprecated, use --miner.gastarget)",
|
||||||
|
Value: eth.DefaultConfig.Miner.GasFloor,
|
||||||
|
}
|
||||||
|
LegacyMinerGasPriceFlag = BigFlag{
|
||||||
|
Name: "gasprice",
|
||||||
|
Usage: "Minimum gas price for mining a transaction (deprecated, use --miner.gasprice)",
|
||||||
|
Value: eth.DefaultConfig.Miner.GasPrice,
|
||||||
|
}
|
||||||
|
LegacyMinerEtherbaseFlag = cli.StringFlag{
|
||||||
|
Name: "etherbase",
|
||||||
|
Usage: "Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase)",
|
||||||
|
Value: "0",
|
||||||
|
}
|
||||||
|
LegacyMinerExtraDataFlag = cli.StringFlag{
|
||||||
|
Name: "extradata",
|
||||||
|
Usage: "Block extra data set by the miner (default = client version, deprecated, use --miner.extradata)",
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Deprecated June 2019)
|
||||||
|
LegacyLightServFlag = cli.IntFlag{
|
||||||
|
Name: "lightserv",
|
||||||
|
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)",
|
||||||
|
Value: eth.DefaultConfig.LightServ,
|
||||||
|
}
|
||||||
|
LegacyLightPeersFlag = cli.IntFlag{
|
||||||
|
Name: "lightpeers",
|
||||||
|
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)",
|
||||||
|
Value: eth.DefaultConfig.LightPeers,
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Deprecated April 2020)
|
||||||
|
LegacyTestnetFlag = cli.BoolFlag{ // TODO(q9f): Remove after Ropsten is discontinued.
|
||||||
|
Name: "testnet",
|
||||||
|
Usage: "Pre-configured test network (Deprecated: Please choose one of --goerli, --rinkeby, or --ropsten.)",
|
||||||
|
}
|
||||||
|
LegacyRPCEnabledFlag = cli.BoolFlag{
|
||||||
|
Name: "rpc",
|
||||||
|
Usage: "Enable the HTTP-RPC server (deprecated, use --http)",
|
||||||
|
}
|
||||||
|
LegacyRPCListenAddrFlag = cli.StringFlag{
|
||||||
|
Name: "rpcaddr",
|
||||||
|
Usage: "HTTP-RPC server listening interface (deprecated, use --http.addr)",
|
||||||
|
Value: node.DefaultHTTPHost,
|
||||||
|
}
|
||||||
|
LegacyRPCPortFlag = cli.IntFlag{
|
||||||
|
Name: "rpcport",
|
||||||
|
Usage: "HTTP-RPC server listening port (deprecated, use --http.port)",
|
||||||
|
Value: node.DefaultHTTPPort,
|
||||||
|
}
|
||||||
|
LegacyRPCCORSDomainFlag = cli.StringFlag{
|
||||||
|
Name: "rpccorsdomain",
|
||||||
|
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced) (deprecated, use --http.corsdomain)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
LegacyRPCVirtualHostsFlag = cli.StringFlag{
|
||||||
|
Name: "rpcvhosts",
|
||||||
|
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (deprecated, use --http.vhosts)",
|
||||||
|
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
|
||||||
|
}
|
||||||
|
LegacyRPCApiFlag = cli.StringFlag{
|
||||||
|
Name: "rpcapi",
|
||||||
|
Usage: "API's offered over the HTTP-RPC interface (deprecated, use --http.api)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
LegacyWSListenAddrFlag = cli.StringFlag{
|
||||||
|
Name: "wsaddr",
|
||||||
|
Usage: "WS-RPC server listening interface (deprecated, use --ws.addr)",
|
||||||
|
Value: node.DefaultWSHost,
|
||||||
|
}
|
||||||
|
LegacyWSPortFlag = cli.IntFlag{
|
||||||
|
Name: "wsport",
|
||||||
|
Usage: "WS-RPC server listening port (deprecated, use --ws.port)",
|
||||||
|
Value: node.DefaultWSPort,
|
||||||
|
}
|
||||||
|
LegacyWSApiFlag = cli.StringFlag{
|
||||||
|
Name: "wsapi",
|
||||||
|
Usage: "API's offered over the WS-RPC interface (deprecated, use --ws.api)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
LegacyWSAllowedOriginsFlag = cli.StringFlag{
|
||||||
|
Name: "wsorigins",
|
||||||
|
Usage: "Origins from which to accept websockets requests (deprecated, use --ws.origins)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
LegacyGpoBlocksFlag = cli.IntFlag{
|
||||||
|
Name: "gpoblocks",
|
||||||
|
Usage: "Number of recent blocks to check for gas prices (deprecated, use --gpo.blocks)",
|
||||||
|
Value: eth.DefaultConfig.GPO.Blocks,
|
||||||
|
}
|
||||||
|
LegacyGpoPercentileFlag = cli.IntFlag{
|
||||||
|
Name: "gpopercentile",
|
||||||
|
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices (deprecated, use --gpo.percentile)",
|
||||||
|
Value: eth.DefaultConfig.GPO.Percentile,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
|
||||||
|
func showDeprecated(*cli.Context) {
|
||||||
|
fmt.Println("--------------------------------------------------------------------")
|
||||||
|
fmt.Println("The following flags are deprecated and will be removed in the future!")
|
||||||
|
fmt.Println("--------------------------------------------------------------------")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
for _, flag := range DeprecatedFlags {
|
||||||
|
fmt.Println(flag.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -145,3 +145,14 @@ func TrimLeftZeroes(s []byte) []byte {
|
||||||
}
|
}
|
||||||
return s[idx:]
|
return s[idx:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TrimRightZeroes returns a subslice of s without trailing zeroes
|
||||||
|
func TrimRightZeroes(s []byte) []byte {
|
||||||
|
idx := len(s)
|
||||||
|
for ; idx > 0; idx-- {
|
||||||
|
if s[idx-1] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[:idx]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,3 +105,22 @@ func TestNoPrefixShortHexOddLength(t *testing.T) {
|
||||||
t.Errorf("Expected %x got %x", expected, result)
|
t.Errorf("Expected %x got %x", expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrimRightZeroes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
arr []byte
|
||||||
|
exp []byte
|
||||||
|
}{
|
||||||
|
{FromHex("0x00ffff00ff0000"), FromHex("0x00ffff00ff")},
|
||||||
|
{FromHex("0x00000000000000"), []byte{}},
|
||||||
|
{FromHex("0xff"), FromHex("0xff")},
|
||||||
|
{[]byte{}, []byte{}},
|
||||||
|
{FromHex("0x00ffffffffffff"), FromHex("0x00ffffffffffff")},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
got := TrimRightZeroes(test.arr)
|
||||||
|
if !bytes.Equal(got, test.exp) {
|
||||||
|
t.Errorf("test %d, got %x exp %x", i, got, test.exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,12 @@ func U256(x *big.Int) *big.Int {
|
||||||
return x.And(x, tt256m1)
|
return x.And(x, tt256m1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// U256Bytes converts a big Int into a 256bit EVM number.
|
||||||
|
// This operation is destructive.
|
||||||
|
func U256Bytes(n *big.Int) []byte {
|
||||||
|
return PaddedBigBytes(U256(n), 32)
|
||||||
|
}
|
||||||
|
|
||||||
// S256 interprets x as a two's complement number.
|
// S256 interprets x as a two's complement number.
|
||||||
// x must not exceed 256 bits (the result is undefined if it does) and is not modified.
|
// x must not exceed 256 bits (the result is undefined if it does) and is not modified.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,16 @@ func TestU256(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestU256Bytes(t *testing.T) {
|
||||||
|
ubytes := make([]byte, 32)
|
||||||
|
ubytes[31] = 1
|
||||||
|
|
||||||
|
unsigned := U256Bytes(big.NewInt(1))
|
||||||
|
if !bytes.Equal(unsigned, ubytes) {
|
||||||
|
t.Errorf("expected %x got %x", ubytes, unsigned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBigEndianByteAt(t *testing.T) {
|
func TestBigEndianByteAt(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
x string
|
x string
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ var (
|
||||||
_ = 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
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -981,189 +980,108 @@ func TestLogReorgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This EVM code generates a log when the contract is created.
|
||||||
|
var logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||||
|
|
||||||
|
// This test checks that log events and RemovedLogsEvent are sent
|
||||||
|
// when the chain reorganizes.
|
||||||
func TestLogRebirth(t *testing.T) {
|
func TestLogRebirth(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||||
// this code generates a log
|
genesis = gspec.MustCommit(db)
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
engine = ethash.NewFaker()
|
||||||
genesis = gspec.MustCommit(db)
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
|
||||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
||||||
newLogCh = make(chan bool)
|
|
||||||
removeLogCh = make(chan bool)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// validateLogEvent checks whether the received logs number is equal with expected.
|
|
||||||
validateLogEvent := func(sink interface{}, result chan bool, expect int) {
|
|
||||||
chanval := reflect.ValueOf(sink)
|
|
||||||
chantyp := chanval.Type()
|
|
||||||
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
|
|
||||||
t.Fatalf("invalid channel, given type %v", chantyp)
|
|
||||||
}
|
|
||||||
cnt := 0
|
|
||||||
var recv []reflect.Value
|
|
||||||
timeout := time.After(1 * time.Second)
|
|
||||||
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
|
|
||||||
for {
|
|
||||||
chose, v, _ := reflect.Select(cases)
|
|
||||||
if chose == 1 {
|
|
||||||
// Not enough event received
|
|
||||||
result <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cnt += 1
|
|
||||||
recv = append(recv, v)
|
|
||||||
if cnt == expect {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done := time.After(50 * time.Millisecond)
|
|
||||||
cases = cases[:1]
|
|
||||||
cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
|
|
||||||
chose, _, _ := reflect.Select(cases)
|
|
||||||
// If chose equal 0, it means receiving redundant events.
|
|
||||||
if chose == 1 {
|
|
||||||
result <- true
|
|
||||||
} else {
|
|
||||||
result <- false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
logsCh := make(chan []*types.Log)
|
// The event channels.
|
||||||
blockchain.SubscribeLogsEvent(logsCh)
|
newLogCh := make(chan []*types.Log, 10)
|
||||||
|
rmLogsCh := make(chan RemovedLogsEvent, 10)
|
||||||
|
blockchain.SubscribeLogsEvent(newLogCh)
|
||||||
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
||||||
|
|
||||||
rmLogsCh := make(chan RemovedLogsEvent)
|
// This chain contains a single log.
|
||||||
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) {
|
||||||
|
if i == 1 {
|
||||||
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tx: %v", err)
|
||||||
|
}
|
||||||
|
gen.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
|
t.Fatalf("failed to insert chain: %v", err)
|
||||||
|
}
|
||||||
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
||||||
|
|
||||||
|
// Generate long reorg chain containing another log. Inserting the
|
||||||
|
// chain removes one log and adds one.
|
||||||
|
forkChain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) {
|
||||||
|
if i == 1 {
|
||||||
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tx: %v", err)
|
||||||
|
}
|
||||||
|
gen.AddTx(tx)
|
||||||
|
gen.OffsetTime(-9) // higher block difficulty
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
||||||
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
|
}
|
||||||
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
|
||||||
|
|
||||||
|
// This chain segment is rooted in the original chain, but doesn't contain any logs.
|
||||||
|
// When inserting it, the canonical chain switches away from forkChain and re-emits
|
||||||
|
// the log event for the old chain, as well as a RemovedLogsEvent for forkChain.
|
||||||
|
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], engine, db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
||||||
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
|
}
|
||||||
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is a variation of TestLogRebirth. It verifies that log events are emitted
|
||||||
|
// when a side chain containing log events overtakes the canonical chain.
|
||||||
|
func TestSideLogRebirth(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||||
|
genesis = gspec.MustCommit(db)
|
||||||
|
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||||
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
defer blockchain.Stop()
|
||||||
|
|
||||||
|
newLogCh := make(chan []*types.Log, 10)
|
||||||
|
rmLogsCh := make(chan RemovedLogsEvent, 10)
|
||||||
|
blockchain.SubscribeLogsEvent(newLogCh)
|
||||||
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
||||||
|
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
gen.OffsetTime(-9) // higher block difficulty
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tx: %v", err)
|
|
||||||
}
|
|
||||||
gen.AddTx(tx)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Spawn a goroutine to receive log events
|
|
||||||
go validateLogEvent(logsCh, newLogCh, 1)
|
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
||||||
t.Fatalf("failed to insert chain: %v", err)
|
|
||||||
}
|
|
||||||
if !<-newLogCh {
|
|
||||||
t.Fatal("failed to receive new log event")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate long reorg chain
|
|
||||||
forkChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
||||||
if i == 1 {
|
|
||||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tx: %v", err)
|
|
||||||
}
|
|
||||||
gen.AddTx(tx)
|
|
||||||
// Higher block difficulty
|
|
||||||
gen.OffsetTime(-9)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Spawn a goroutine to receive log events
|
|
||||||
go validateLogEvent(logsCh, newLogCh, 1)
|
|
||||||
go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
|
||||||
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
|
||||||
}
|
|
||||||
if !<-newLogCh {
|
|
||||||
t.Fatal("failed to receive new log event")
|
|
||||||
}
|
|
||||||
if !<-removeLogCh {
|
|
||||||
t.Fatal("failed to receive removed log event")
|
|
||||||
}
|
|
||||||
|
|
||||||
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
|
||||||
go validateLogEvent(logsCh, newLogCh, 1)
|
|
||||||
go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
|
||||||
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
|
||||||
}
|
|
||||||
// Rebirth logs should omit a newLogEvent
|
|
||||||
if !<-newLogCh {
|
|
||||||
t.Fatal("failed to receive new log event")
|
|
||||||
}
|
|
||||||
// Ensure removedLog events received
|
|
||||||
if !<-removeLogCh {
|
|
||||||
t.Fatal("failed to receive removed log event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSideLogRebirth(t *testing.T) {
|
|
||||||
var (
|
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
|
||||||
|
|
||||||
// this code generates a log
|
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
|
||||||
genesis = gspec.MustCommit(db)
|
|
||||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
||||||
newLogCh = make(chan bool)
|
|
||||||
)
|
|
||||||
|
|
||||||
// listenNewLog checks whether the received logs number is equal with expected.
|
|
||||||
listenNewLog := func(sink chan []*types.Log, expect int) {
|
|
||||||
cnt := 0
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case logs := <-sink:
|
|
||||||
cnt += len(logs)
|
|
||||||
case <-time.NewTimer(5 * time.Second).C:
|
|
||||||
// new logs timeout
|
|
||||||
newLogCh <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cnt == expect {
|
|
||||||
break
|
|
||||||
} else if cnt > expect {
|
|
||||||
// redundant logs received
|
|
||||||
newLogCh <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-sink:
|
|
||||||
// redundant logs received
|
|
||||||
newLogCh <- false
|
|
||||||
case <-time.NewTimer(100 * time.Millisecond).C:
|
|
||||||
newLogCh <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
defer blockchain.Stop()
|
|
||||||
|
|
||||||
logsCh := make(chan []*types.Log)
|
|
||||||
blockchain.SubscribeLogsEvent(logsCh)
|
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
||||||
if i == 1 {
|
|
||||||
// Higher block difficulty
|
|
||||||
gen.OffsetTime(-9)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||||
|
|
||||||
// Generate side chain with lower difficulty
|
// Generate side chain with lower difficulty
|
||||||
sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tx: %v", err)
|
t.Fatalf("failed to create tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1173,16 +1091,31 @@ func TestSideLogRebirth(t *testing.T) {
|
||||||
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||||
|
|
||||||
// Generate a new block based on side chain
|
// Generate a new block based on side chain.
|
||||||
newBlocks, _ := GenerateChain(params.TestChainConfig, sideChain[len(sideChain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
newBlocks, _ := GenerateChain(params.TestChainConfig, sideChain[len(sideChain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
go listenNewLog(logsCh, 1)
|
|
||||||
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
// Rebirth logs should omit a newLogEvent
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
||||||
if !<-newLogCh {
|
}
|
||||||
t.Fatalf("failed to receive new log event")
|
|
||||||
|
func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if len(logsCh) != wantNew {
|
||||||
|
t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
|
||||||
|
}
|
||||||
|
if len(rmLogsCh) != wantRemoved {
|
||||||
|
t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
|
||||||
|
}
|
||||||
|
// Drain events.
|
||||||
|
for i := 0; i < len(logsCh); i++ {
|
||||||
|
<-logsCh
|
||||||
|
}
|
||||||
|
for i := 0; i < len(rmLogsCh); i++ {
|
||||||
|
<-rmLogsCh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,9 @@ func (c *ChainIndexer) Sections() (uint64, uint64, common.Hash) {
|
||||||
|
|
||||||
// AddChildIndexer adds a child ChainIndexer that can use the output of this one
|
// AddChildIndexer adds a child ChainIndexer that can use the output of this one
|
||||||
func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
|
func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
|
||||||
|
if indexer == c {
|
||||||
|
panic("can't add indexer as a child of itself")
|
||||||
|
}
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,45 @@ var (
|
||||||
// ErrKnownBlock is returned when a block to import is already known locally.
|
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||||
ErrKnownBlock = errors.New("block already known")
|
ErrKnownBlock = errors.New("block already known")
|
||||||
|
|
||||||
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
|
||||||
// by a transaction is higher than what's left in the block.
|
|
||||||
ErrGasLimitReached = errors.New("gas limit reached")
|
|
||||||
|
|
||||||
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
ErrBlacklistedHash = errors.New("blacklisted hash")
|
||||||
|
|
||||||
|
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||||
|
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||||
|
)
|
||||||
|
|
||||||
|
// List of evm-call-message pre-checking errors. All state transtion messages will
|
||||||
|
// be pre-checked before execution. If any invalidation detected, the corresponding
|
||||||
|
// error should be returned which is defined here.
|
||||||
|
//
|
||||||
|
// - If the pre-checking happens in the miner, then the transaction won't be packed.
|
||||||
|
// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk"
|
||||||
|
// error should be emitted.
|
||||||
|
var (
|
||||||
|
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
||||||
|
// one present in the local chain.
|
||||||
|
ErrNonceTooLow = errors.New("nonce too low")
|
||||||
|
|
||||||
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||||
// next one expected based on the local chain.
|
// next one expected based on the local chain.
|
||||||
ErrNonceTooHigh = errors.New("nonce too high")
|
ErrNonceTooHigh = errors.New("nonce too high")
|
||||||
|
|
||||||
// ErrNoGenesis is returned when there is no Genesis Block.
|
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
||||||
ErrNoGenesis = errors.New("genesis not found in chain")
|
// by a transaction is higher than what's left in the block.
|
||||||
|
ErrGasLimitReached = errors.New("gas limit reached")
|
||||||
|
|
||||||
|
// ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
|
||||||
|
// have enough funds for transfer(topmost call only).
|
||||||
|
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||||
|
|
||||||
|
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||||
|
// is higher than the balance of the user's account.
|
||||||
|
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||||
|
|
||||||
|
// ErrGasUintOverflow is returned when calculating gas usage.
|
||||||
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
|
|
||||||
|
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
||||||
|
// than required to start the invocation.
|
||||||
|
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,8 @@ func TestCreation(t *testing.T) {
|
||||||
},
|
},
|
||||||
// Ropsten test cases
|
// Ropsten test cases
|
||||||
{
|
{
|
||||||
params.TestnetChainConfig,
|
params.RopstenChainConfig,
|
||||||
params.TestnetGenesisHash,
|
params.RopstenGenesisHash,
|
||||||
[]testcase{
|
[]testcase{
|
||||||
{0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block
|
{0, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block
|
||||||
{9, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Last Tangerine block
|
{9, ID{Hash: checksumToBytes(0x30c7ddbc), Next: 10}}, // Last Tangerine block
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,6 @@ func (e *GenesisMismatchError) Error() string {
|
||||||
//
|
//
|
||||||
// The returned chain configuration is never nil.
|
// The returned chain configuration is never nil.
|
||||||
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||||
return SetupGenesisBlockWithOverride(db, genesis, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul, overrideMuirGlacier *big.Int) (*params.ChainConfig, common.Hash, error) {
|
|
||||||
if genesis != nil && genesis.Config == nil {
|
if genesis != nil && genesis.Config == nil {
|
||||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||||
}
|
}
|
||||||
|
|
@ -204,12 +200,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
|
||||||
|
|
||||||
// Get the existing chain configuration.
|
// Get the existing chain configuration.
|
||||||
newcfg := genesis.configOrDefault(stored)
|
newcfg := genesis.configOrDefault(stored)
|
||||||
if overrideIstanbul != nil {
|
|
||||||
newcfg.IstanbulBlock = overrideIstanbul
|
|
||||||
}
|
|
||||||
if overrideMuirGlacier != nil {
|
|
||||||
newcfg.MuirGlacierBlock = overrideMuirGlacier
|
|
||||||
}
|
|
||||||
if err := newcfg.CheckConfigForkOrder(); err != nil {
|
if err := newcfg.CheckConfigForkOrder(); err != nil {
|
||||||
return newcfg, common.Hash{}, err
|
return newcfg, common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -246,8 +236,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||||
return g.Config
|
return g.Config
|
||||||
case ghash == params.MainnetGenesisHash:
|
case ghash == params.MainnetGenesisHash:
|
||||||
return params.MainnetChainConfig
|
return params.MainnetChainConfig
|
||||||
case ghash == params.TestnetGenesisHash:
|
case ghash == params.RopstenGenesisHash:
|
||||||
return params.TestnetChainConfig
|
return params.RopstenChainConfig
|
||||||
|
case ghash == params.RinkebyGenesisHash:
|
||||||
|
return params.RinkebyChainConfig
|
||||||
|
case ghash == params.GoerliGenesisHash:
|
||||||
|
return params.GoerliChainConfig
|
||||||
default:
|
default:
|
||||||
return params.AllEthashProtocolChanges
|
return params.AllEthashProtocolChanges
|
||||||
}
|
}
|
||||||
|
|
@ -347,15 +341,15 @@ func DefaultGenesisBlock() *Genesis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultTestnetGenesisBlock returns the Ropsten network genesis block.
|
// DefaultRopstenGenesisBlock returns the Ropsten network genesis block.
|
||||||
func DefaultTestnetGenesisBlock() *Genesis {
|
func DefaultRopstenGenesisBlock() *Genesis {
|
||||||
return &Genesis{
|
return &Genesis{
|
||||||
Config: params.TestnetChainConfig,
|
Config: params.RopstenChainConfig,
|
||||||
Nonce: 66,
|
Nonce: 66,
|
||||||
ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"),
|
ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"),
|
||||||
GasLimit: 16777216,
|
GasLimit: 16777216,
|
||||||
Difficulty: big.NewInt(1048576),
|
Difficulty: big.NewInt(1048576),
|
||||||
Alloc: decodePrealloc(testnetAllocData),
|
Alloc: decodePrealloc(ropstenAllocData),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -35,9 +35,9 @@ func TestDefaultGenesisBlock(t *testing.T) {
|
||||||
if block.Hash() != params.MainnetGenesisHash {
|
if block.Hash() != params.MainnetGenesisHash {
|
||||||
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
|
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
|
||||||
}
|
}
|
||||||
block = DefaultTestnetGenesisBlock().ToBlock(nil)
|
block = DefaultRopstenGenesisBlock().ToBlock(nil)
|
||||||
if block.Hash() != params.TestnetGenesisHash {
|
if block.Hash() != params.RopstenGenesisHash {
|
||||||
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash)
|
t.Errorf("wrong ropsten genesis hash, got %v, want %v", block.Hash(), params.RopstenGenesisHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,14 +95,14 @@ func TestSetupGenesis(t *testing.T) {
|
||||||
wantConfig: customg.Config,
|
wantConfig: customg.Config,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "custom block in DB, genesis == testnet",
|
name: "custom block in DB, genesis == ropsten",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
customg.MustCommit(db)
|
customg.MustCommit(db)
|
||||||
return SetupGenesisBlock(db, DefaultTestnetGenesisBlock())
|
return SetupGenesisBlock(db, DefaultRopstenGenesisBlock())
|
||||||
},
|
},
|
||||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestnetGenesisHash},
|
wantErr: &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash},
|
||||||
wantHash: params.TestnetGenesisHash,
|
wantHash: params.RopstenGenesisHash,
|
||||||
wantConfig: params.TestnetChainConfig,
|
wantConfig: params.RopstenChainConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "compatible config in DB",
|
name: "compatible config in DB",
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
if ptd == nil {
|
if ptd == nil {
|
||||||
return NonStatTy, consensus.ErrUnknownAncestor
|
return NonStatTy, consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
|
head := hc.CurrentHeader().Number.Uint64()
|
||||||
|
localTd := hc.GetTd(hc.currentHeaderHash, head)
|
||||||
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
||||||
|
|
||||||
// Irrelevant of the canonical status, write the td and header to the database
|
// Irrelevant of the canonical status, write the td and header to the database
|
||||||
|
|
@ -165,7 +166,15 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||||
if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
|
reorg := externTd.Cmp(localTd) > 0
|
||||||
|
if !reorg && externTd.Cmp(localTd) == 0 {
|
||||||
|
if header.Number.Uint64() < head {
|
||||||
|
reorg = true
|
||||||
|
} else if header.Number.Uint64() == head {
|
||||||
|
reorg = mrand.Float64() < 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reorg {
|
||||||
// If the header can be added into canonical chain, adjust the
|
// If the header can be added into canonical chain, adjust the
|
||||||
// header chain markers(canonical indexes and head header flag).
|
// header chain markers(canonical indexes and head header flag).
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
|
||||||
prefix := headerKeyPrefix(number)
|
prefix := headerKeyPrefix(number)
|
||||||
|
|
||||||
hashes := make([]common.Hash, 0, 1)
|
hashes := make([]common.Hash, 0, 1)
|
||||||
it := db.NewIteratorWithPrefix(prefix)
|
it := db.NewIterator(prefix, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
|
||||||
// IterateStorageSnapshots returns an iterator for walking the entire storage
|
// IterateStorageSnapshots returns an iterator for walking the entire storage
|
||||||
// space of a specific account.
|
// space of a specific account.
|
||||||
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
|
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
|
||||||
return db.NewIteratorWithPrefix(storageSnapshotsKey(accountHash))
|
return db.NewIterator(storageSnapshotsKey(accountHash), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadSnapshotJournal retrieves the serialized in-memory diff layers saved at
|
// ReadSnapshotJournal retrieves the serialized in-memory diff layers saved at
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer
|
||||||
// InspectDatabase traverses the entire database and checks the size
|
// InspectDatabase traverses the entire database and checks the size
|
||||||
// of all different categories of data.
|
// of all different categories of data.
|
||||||
func InspectDatabase(db ethdb.Database) error {
|
func InspectDatabase(db ethdb.Database) error {
|
||||||
it := db.NewIterator()
|
it := db.NewIterator(nil, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -541,20 +541,22 @@ func (t *freezerTable) getBounds(item uint64) (uint32, uint32, uint32, error) {
|
||||||
// Retrieve looks up the data offset of an item with the given number and retrieves
|
// Retrieve looks up the data offset of an item with the given number and retrieves
|
||||||
// the raw binary blob from the data file.
|
// the raw binary blob from the data file.
|
||||||
func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
|
func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
|
||||||
|
t.lock.RLock()
|
||||||
// Ensure the table and the item is accessible
|
// Ensure the table and the item is accessible
|
||||||
if t.index == nil || t.head == nil {
|
if t.index == nil || t.head == nil {
|
||||||
|
t.lock.RUnlock()
|
||||||
return nil, errClosed
|
return nil, errClosed
|
||||||
}
|
}
|
||||||
if atomic.LoadUint64(&t.items) <= item {
|
if atomic.LoadUint64(&t.items) <= item {
|
||||||
|
t.lock.RUnlock()
|
||||||
return nil, errOutOfBounds
|
return nil, errOutOfBounds
|
||||||
}
|
}
|
||||||
// Ensure the item was not deleted from the tail either
|
// Ensure the item was not deleted from the tail either
|
||||||
offset := atomic.LoadUint32(&t.itemOffset)
|
if uint64(t.itemOffset) > item {
|
||||||
if uint64(offset) > item {
|
t.lock.RUnlock()
|
||||||
return nil, errOutOfBounds
|
return nil, errOutOfBounds
|
||||||
}
|
}
|
||||||
t.lock.RLock()
|
startOffset, endOffset, filenum, err := t.getBounds(item - uint64(t.itemOffset))
|
||||||
startOffset, endOffset, filenum, err := t.getBounds(item - uint64(offset))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.lock.RUnlock()
|
t.lock.RUnlock()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -103,27 +103,12 @@ func (t *table) Delete(key []byte) error {
|
||||||
return t.db.Delete(append([]byte(t.prefix), key...))
|
return t.db.Delete(append([]byte(t.prefix), key...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
|
// NewIterator creates a binary-alphabetical iterator over a subset
|
||||||
// contained within the database.
|
// of database content with a particular key prefix, starting at a particular
|
||||||
func (t *table) NewIterator() ethdb.Iterator {
|
// initial key (or after, if it does not exist).
|
||||||
return t.NewIteratorWithPrefix(nil)
|
func (t *table) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
|
||||||
}
|
innerPrefix := append([]byte(t.prefix), prefix...)
|
||||||
|
iter := t.db.NewIterator(innerPrefix, start)
|
||||||
// NewIteratorWithStart creates a binary-alphabetical iterator over a subset of
|
|
||||||
// database content starting at a particular initial key (or after, if it does
|
|
||||||
// not exist).
|
|
||||||
func (t *table) NewIteratorWithStart(start []byte) ethdb.Iterator {
|
|
||||||
iter := t.db.NewIteratorWithStart(append([]byte(t.prefix), start...))
|
|
||||||
return &tableIterator{
|
|
||||||
iter: iter,
|
|
||||||
prefix: t.prefix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
|
|
||||||
// of database content with a particular key prefix.
|
|
||||||
func (t *table) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
|
|
||||||
iter := t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
|
|
||||||
return &tableIterator{
|
return &tableIterator{
|
||||||
iter: iter,
|
iter: iter,
|
||||||
prefix: t.prefix,
|
prefix: t.prefix,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package rawdb
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") }
|
func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") }
|
||||||
|
|
@ -96,48 +98,31 @@ func testTableDatabase(t *testing.T, prefix string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check := func(iter ethdb.Iterator, expCount, index int) {
|
||||||
|
count := 0
|
||||||
|
for iter.Next() {
|
||||||
|
key, value := iter.Key(), iter.Value()
|
||||||
|
if !bytes.Equal(key, entries[index].key) {
|
||||||
|
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(value, entries[index].value) {
|
||||||
|
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if count != expCount {
|
||||||
|
t.Fatalf("Wrong number of elems, exp %d got %d", expCount, count)
|
||||||
|
}
|
||||||
|
iter.Release()
|
||||||
|
}
|
||||||
// Test iterators
|
// Test iterators
|
||||||
iter := db.NewIterator()
|
check(db.NewIterator(nil, nil), 6, 0)
|
||||||
var index int
|
|
||||||
for iter.Next() {
|
|
||||||
key, value := iter.Key(), iter.Value()
|
|
||||||
if !bytes.Equal(key, entries[index].key) {
|
|
||||||
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(value, entries[index].value) {
|
|
||||||
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
|
|
||||||
}
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
iter.Release()
|
|
||||||
|
|
||||||
// Test iterators with prefix
|
// Test iterators with prefix
|
||||||
iter = db.NewIteratorWithPrefix([]byte{0xff, 0xff})
|
check(db.NewIterator([]byte{0xff, 0xff}, nil), 3, 3)
|
||||||
index = 3
|
|
||||||
for iter.Next() {
|
|
||||||
key, value := iter.Key(), iter.Value()
|
|
||||||
if !bytes.Equal(key, entries[index].key) {
|
|
||||||
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(value, entries[index].value) {
|
|
||||||
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
|
|
||||||
}
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
iter.Release()
|
|
||||||
|
|
||||||
// Test iterators with start point
|
// Test iterators with start point
|
||||||
iter = db.NewIteratorWithStart([]byte{0xff, 0xff, 0x02})
|
check(db.NewIterator(nil, []byte{0xff, 0xff, 0x02}), 2, 4)
|
||||||
index = 4
|
// Test iterators with prefix and start point
|
||||||
for iter.Next() {
|
check(db.NewIterator([]byte{0xee}, nil), 0, 0)
|
||||||
key, value := iter.Key(), iter.Value()
|
check(db.NewIterator(nil, []byte{0x00}), 6, 0)
|
||||||
if !bytes.Equal(key, entries[index].key) {
|
|
||||||
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(value, entries[index].value) {
|
|
||||||
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
|
|
||||||
}
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
iter.Release()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,6 @@ func (d iterativeDump) onRoot(root common.Hash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
|
func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
|
||||||
emptyAddress := (common.Address{})
|
|
||||||
missingPreimages := 0
|
missingPreimages := 0
|
||||||
c.onRoot(s.trie.Hash())
|
c.onRoot(s.trie.Hash())
|
||||||
|
|
||||||
|
|
@ -114,15 +113,14 @@ func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingP
|
||||||
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
addr := common.BytesToAddress(s.trie.GetKey(it.Key))
|
|
||||||
obj := newObject(nil, addr, data)
|
|
||||||
account := DumpAccount{
|
account := DumpAccount{
|
||||||
Balance: data.Balance.String(),
|
Balance: data.Balance.String(),
|
||||||
Nonce: data.Nonce,
|
Nonce: data.Nonce,
|
||||||
Root: common.Bytes2Hex(data.Root[:]),
|
Root: common.Bytes2Hex(data.Root[:]),
|
||||||
CodeHash: common.Bytes2Hex(data.CodeHash),
|
CodeHash: common.Bytes2Hex(data.CodeHash),
|
||||||
}
|
}
|
||||||
if emptyAddress == addr {
|
addrBytes := s.trie.GetKey(it.Key)
|
||||||
|
if addrBytes == nil {
|
||||||
// Preimage missing
|
// Preimage missing
|
||||||
missingPreimages++
|
missingPreimages++
|
||||||
if excludeMissingPreimages {
|
if excludeMissingPreimages {
|
||||||
|
|
@ -130,6 +128,8 @@ func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingP
|
||||||
}
|
}
|
||||||
account.SecureKey = it.Key
|
account.SecureKey = it.Key
|
||||||
}
|
}
|
||||||
|
addr := common.BytesToAddress(addrBytes)
|
||||||
|
obj := newObject(nil, addr, data)
|
||||||
if !excludeCode {
|
if !excludeCode {
|
||||||
account.Code = common.Bytes2Hex(obj.Code(s.db))
|
account.Code = common.Bytes2Hex(obj.Code(s.db))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
t.Errorf("state entry not reported %x", hash)
|
t.Errorf("state entry not reported %x", hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator()
|
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account is a slim version of a state.Account, where the root and code hash
|
// Account is a modified version of a state.Account, where the root is replaced
|
||||||
// are replaced with a nil byte slice for empty accounts.
|
// with a byte slice. This format can be used to represent full-consensus format
|
||||||
|
// or slim-snapshot format which replaces the empty root and code hash as nil
|
||||||
|
// byte slice.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
Balance *big.Int
|
Balance *big.Int
|
||||||
|
|
@ -33,9 +35,8 @@ type Account struct {
|
||||||
CodeHash []byte
|
CodeHash []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountRLP converts a state.Account content into a slim snapshot version RLP
|
// SlimAccount converts a state.Account content into a slim snapshot account
|
||||||
// encoded.
|
func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) Account {
|
||||||
func AccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) []byte {
|
|
||||||
slim := Account{
|
slim := Account{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
|
|
@ -46,9 +47,40 @@ func AccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byt
|
||||||
if !bytes.Equal(codehash, emptyCode[:]) {
|
if !bytes.Equal(codehash, emptyCode[:]) {
|
||||||
slim.CodeHash = codehash
|
slim.CodeHash = codehash
|
||||||
}
|
}
|
||||||
data, err := rlp.EncodeToBytes(slim)
|
return slim
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlimAccountRLP converts a state.Account content into a slim snapshot
|
||||||
|
// version RLP encoded.
|
||||||
|
func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) []byte {
|
||||||
|
data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, codehash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FullAccount decodes the data on the 'slim RLP' format and return
|
||||||
|
// the consensus format account.
|
||||||
|
func FullAccount(data []byte) (Account, error) {
|
||||||
|
var account Account
|
||||||
|
if err := rlp.DecodeBytes(data, &account); err != nil {
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
if len(account.Root) == 0 {
|
||||||
|
account.Root = emptyRoot[:]
|
||||||
|
}
|
||||||
|
if len(account.CodeHash) == 0 {
|
||||||
|
account.CodeHash = emptyCode[:]
|
||||||
|
}
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullAccountRLP converts data on the 'slim RLP' format into the full RLP-format.
|
||||||
|
func FullAccountRLP(data []byte) ([]byte, error) {
|
||||||
|
account, err := FullAccount(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rlp.EncodeToBytes(account)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package snapshot
|
package snapshot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -27,80 +29,239 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
// conversionAccount is used for converting between full and slim format. When
|
|
||||||
// doing this, we can consider 'balance' as a byte array, as it has already
|
|
||||||
// been converted from big.Int into an rlp-byteslice.
|
|
||||||
type conversionAccount struct {
|
|
||||||
Nonce uint64
|
|
||||||
Balance []byte
|
|
||||||
Root []byte
|
|
||||||
CodeHash []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// SlimToFull converts data on the 'slim RLP' format into the full RLP-format
|
|
||||||
func SlimToFull(data []byte) ([]byte, error) {
|
|
||||||
acc := &conversionAccount{}
|
|
||||||
if err := rlp.DecodeBytes(data, acc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(acc.Root) == 0 {
|
|
||||||
acc.Root = emptyRoot[:]
|
|
||||||
}
|
|
||||||
if len(acc.CodeHash) == 0 {
|
|
||||||
acc.CodeHash = emptyCode[:]
|
|
||||||
}
|
|
||||||
fullData, err := rlp.EncodeToBytes(acc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return fullData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// trieKV represents a trie key-value pair
|
// trieKV represents a trie key-value pair
|
||||||
type trieKV struct {
|
type trieKV struct {
|
||||||
key common.Hash
|
key common.Hash
|
||||||
value []byte
|
value []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type trieGeneratorFn func(in chan (trieKV), out chan (common.Hash))
|
type (
|
||||||
|
// trieGeneratorFn is the interface of trie generation which can
|
||||||
|
// be implemented by different trie algorithm.
|
||||||
|
trieGeneratorFn func(in chan (trieKV), out chan (common.Hash))
|
||||||
|
|
||||||
// GenerateTrieRoot takes an account iterator and reproduces the root hash.
|
// leafCallbackFn is the callback invoked at the leaves of the trie,
|
||||||
func GenerateTrieRoot(it AccountIterator) common.Hash {
|
// returns the subtrie root with the specified subtrie identifier.
|
||||||
return generateTrieRoot(it, stdGenerate)
|
leafCallbackFn func(hash common.Hash, stat *generateStats) common.Hash
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateAccountTrieRoot takes an account iterator and reproduces the root hash.
|
||||||
|
func GenerateAccountTrieRoot(it AccountIterator) (common.Hash, error) {
|
||||||
|
return generateTrieRoot(it, common.Hash{}, stdGenerate, nil, &generateStats{start: time.Now()}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTrieRoot(it AccountIterator, generatorFn trieGeneratorFn) common.Hash {
|
// GenerateStorageTrieRoot takes a storage iterator and reproduces the root hash.
|
||||||
|
func GenerateStorageTrieRoot(account common.Hash, it StorageIterator) (common.Hash, error) {
|
||||||
|
return generateTrieRoot(it, account, stdGenerate, nil, &generateStats{start: time.Now()}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyState takes the whole snapshot tree as the input, traverses all the accounts
|
||||||
|
// as well as the corresponding storages and compares the re-computed hash with the
|
||||||
|
// original one(state root and the storage root).
|
||||||
|
func VerifyState(snaptree *Tree, root common.Hash) error {
|
||||||
|
acctIt, err := snaptree.AccountIterator(root, common.Hash{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer acctIt.Release()
|
||||||
|
|
||||||
|
got, err := generateTrieRoot(acctIt, common.Hash{}, stdGenerate, func(account common.Hash, stat *generateStats) common.Hash {
|
||||||
|
storageIt, err := snaptree.StorageIterator(root, account, common.Hash{})
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
defer storageIt.Release()
|
||||||
|
|
||||||
|
hash, err := generateTrieRoot(storageIt, account, stdGenerate, nil, stat, false)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}, &generateStats{start: time.Now()}, true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if got != root {
|
||||||
|
return fmt.Errorf("state root hash mismatch: got %x, want %x", got, root)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateStats is a collection of statistics gathered by the trie generator
|
||||||
|
// for logging purposes.
|
||||||
|
type generateStats struct {
|
||||||
|
accounts uint64
|
||||||
|
slots uint64
|
||||||
|
curAccount common.Hash
|
||||||
|
curSlot common.Hash
|
||||||
|
start time.Time
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// progress records the progress trie generator made recently.
|
||||||
|
func (stat *generateStats) progress(accounts, slots uint64, curAccount common.Hash, curSlot common.Hash) {
|
||||||
|
stat.lock.Lock()
|
||||||
|
defer stat.lock.Unlock()
|
||||||
|
|
||||||
|
stat.accounts += accounts
|
||||||
|
stat.slots += slots
|
||||||
|
stat.curAccount = curAccount
|
||||||
|
stat.curSlot = curSlot
|
||||||
|
}
|
||||||
|
|
||||||
|
// report prints the cumulative progress statistic smartly.
|
||||||
|
func (stat *generateStats) report() {
|
||||||
|
stat.lock.RLock()
|
||||||
|
defer stat.lock.RUnlock()
|
||||||
|
|
||||||
|
var ctx []interface{}
|
||||||
|
if stat.curSlot != (common.Hash{}) {
|
||||||
|
ctx = append(ctx, []interface{}{
|
||||||
|
"in", stat.curAccount,
|
||||||
|
"at", stat.curSlot,
|
||||||
|
}...)
|
||||||
|
} else {
|
||||||
|
ctx = append(ctx, []interface{}{"at", stat.curAccount}...)
|
||||||
|
}
|
||||||
|
// Add the usual measurements
|
||||||
|
ctx = append(ctx, []interface{}{"accounts", stat.accounts}...)
|
||||||
|
if stat.slots != 0 {
|
||||||
|
ctx = append(ctx, []interface{}{"slots", stat.slots}...)
|
||||||
|
}
|
||||||
|
ctx = append(ctx, []interface{}{"elapsed", common.PrettyDuration(time.Since(stat.start))}...)
|
||||||
|
log.Info("Generating trie hash from snapshot", ctx...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reportDone prints the last log when the whole generation is finished.
|
||||||
|
func (stat *generateStats) reportDone() {
|
||||||
|
stat.lock.RLock()
|
||||||
|
defer stat.lock.RUnlock()
|
||||||
|
|
||||||
|
var ctx []interface{}
|
||||||
|
ctx = append(ctx, []interface{}{"accounts", stat.accounts}...)
|
||||||
|
if stat.slots != 0 {
|
||||||
|
ctx = append(ctx, []interface{}{"slots", stat.slots}...)
|
||||||
|
}
|
||||||
|
ctx = append(ctx, []interface{}{"elapsed", common.PrettyDuration(time.Since(stat.start))}...)
|
||||||
|
log.Info("Generated trie hash from snapshot", ctx...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateTrieRoot generates the trie hash based on the snapshot iterator.
|
||||||
|
// It can be used for generating account trie, storage trie or even the
|
||||||
|
// whole state which connects the accounts and the corresponding storages.
|
||||||
|
func generateTrieRoot(it Iterator, account common.Hash, generatorFn trieGeneratorFn, leafCallback leafCallbackFn, stats *generateStats, report bool) (common.Hash, error) {
|
||||||
var (
|
var (
|
||||||
in = make(chan trieKV) // chan to pass leaves
|
in = make(chan trieKV) // chan to pass leaves
|
||||||
out = make(chan common.Hash) // chan to collect result
|
out = make(chan common.Hash, 1) // chan to collect result
|
||||||
wg sync.WaitGroup
|
stoplog = make(chan bool, 1) // 1-size buffer, works when logging is not enabled
|
||||||
|
wg sync.WaitGroup
|
||||||
)
|
)
|
||||||
|
// Spin up a go-routine for trie hash re-generation
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
generatorFn(in, out)
|
generatorFn(in, out)
|
||||||
wg.Done()
|
|
||||||
}()
|
}()
|
||||||
// Feed leaves
|
|
||||||
start := time.Now()
|
// Spin up a go-routine for progress logging
|
||||||
logged := time.Now()
|
if report && stats != nil {
|
||||||
accounts := 0
|
wg.Add(1)
|
||||||
for it.Next() {
|
go func() {
|
||||||
slimData := it.Account()
|
defer wg.Done()
|
||||||
fullData, _ := SlimToFull(slimData)
|
|
||||||
l := trieKV{it.Hash(), fullData}
|
timer := time.NewTimer(0)
|
||||||
in <- l
|
defer timer.Stop()
|
||||||
if time.Since(logged) > 8*time.Second {
|
|
||||||
log.Info("Generating trie hash from snapshot",
|
for {
|
||||||
"at", l.key, "accounts", accounts, "elapsed", time.Since(start))
|
select {
|
||||||
logged = time.Now()
|
case <-timer.C:
|
||||||
}
|
stats.report()
|
||||||
accounts++
|
timer.Reset(time.Second * 8)
|
||||||
|
case success := <-stoplog:
|
||||||
|
if success {
|
||||||
|
stats.reportDone()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
close(in)
|
// stop is a helper function to shutdown the background threads
|
||||||
result := <-out
|
// and return the re-generated trie hash.
|
||||||
log.Info("Generated trie hash from snapshot", "accounts", accounts, "elapsed", time.Since(start))
|
stop := func(success bool) common.Hash {
|
||||||
wg.Wait()
|
close(in)
|
||||||
return result
|
result := <-out
|
||||||
|
stoplog <- success
|
||||||
|
wg.Wait()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
logged = time.Now()
|
||||||
|
processed = uint64(0)
|
||||||
|
leaf trieKV
|
||||||
|
last common.Hash
|
||||||
|
)
|
||||||
|
// Start to feed leaves
|
||||||
|
for it.Next() {
|
||||||
|
if account == (common.Hash{}) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
fullData []byte
|
||||||
|
)
|
||||||
|
if leafCallback == nil {
|
||||||
|
fullData, err = FullAccountRLP(it.(AccountIterator).Account())
|
||||||
|
if err != nil {
|
||||||
|
stop(false)
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
account, err := FullAccount(it.(AccountIterator).Account())
|
||||||
|
if err != nil {
|
||||||
|
stop(false)
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
// Apply the leaf callback. Normally the callback is used to traverse
|
||||||
|
// the storage trie and re-generate the subtrie root.
|
||||||
|
subroot := leafCallback(it.Hash(), stats)
|
||||||
|
if !bytes.Equal(account.Root, subroot.Bytes()) {
|
||||||
|
stop(false)
|
||||||
|
return common.Hash{}, fmt.Errorf("invalid subroot(%x), want %x, got %x", it.Hash(), account.Root, subroot)
|
||||||
|
}
|
||||||
|
fullData, err = rlp.EncodeToBytes(account)
|
||||||
|
if err != nil {
|
||||||
|
stop(false)
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf = trieKV{it.Hash(), fullData}
|
||||||
|
} else {
|
||||||
|
leaf = trieKV{it.Hash(), common.CopyBytes(it.(StorageIterator).Slot())}
|
||||||
|
}
|
||||||
|
in <- leaf
|
||||||
|
|
||||||
|
// Accumulate the generaation statistic if it's required.
|
||||||
|
processed++
|
||||||
|
if time.Since(logged) > 3*time.Second && stats != nil {
|
||||||
|
if account == (common.Hash{}) {
|
||||||
|
stats.progress(processed, 0, it.Hash(), common.Hash{})
|
||||||
|
} else {
|
||||||
|
stats.progress(0, processed, account, it.Hash())
|
||||||
|
}
|
||||||
|
logged, processed = time.Now(), 0
|
||||||
|
}
|
||||||
|
last = it.Hash()
|
||||||
|
}
|
||||||
|
// Commit the last part statistic.
|
||||||
|
if processed > 0 && stats != nil {
|
||||||
|
if account == (common.Hash{}) {
|
||||||
|
stats.progress(processed, 0, last, common.Hash{})
|
||||||
|
} else {
|
||||||
|
stats.progress(0, processed, account, last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := stop(true)
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stdGenerate is a very basic hexary trie builder which uses the same Trie
|
// stdGenerate is a very basic hexary trie builder which uses the same Trie
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,13 @@ type diffLayer struct {
|
||||||
root common.Hash // Root hash to which this snapshot diff belongs to
|
root common.Hash // Root hash to which this snapshot diff belongs to
|
||||||
stale uint32 // Signals that the layer became stale (state progressed)
|
stale uint32 // Signals that the layer became stale (state progressed)
|
||||||
|
|
||||||
|
// destructSet is a very special helper marker. If an account is marked as
|
||||||
|
// deleted, then it's recorded in this set. However it's allowed that an account
|
||||||
|
// is included here but still available in other sets(e.g. storageData). The
|
||||||
|
// reason is the diff layer includes all the changes in a *block*. It can
|
||||||
|
// happen that in the tx_1, account A is self-destructed while in the tx_2
|
||||||
|
// it's recreated. But we still need this marker to indicate the "old" A is
|
||||||
|
// deleted, all data in other set belongs to the "new" A.
|
||||||
destructSet map[common.Hash]struct{} // Keyed markers for deleted (and potentially) recreated accounts
|
destructSet map[common.Hash]struct{} // Keyed markers for deleted (and potentially) recreated accounts
|
||||||
accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil
|
accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil
|
||||||
accountData map[common.Hash][]byte // Keyed accounts for direct retrival (nil means deleted)
|
accountData map[common.Hash][]byte // Keyed accounts for direct retrival (nil means deleted)
|
||||||
|
|
@ -169,6 +176,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
|
||||||
destructSet: destructs,
|
destructSet: destructs,
|
||||||
accountData: accounts,
|
accountData: accounts,
|
||||||
storageData: storage,
|
storageData: storage,
|
||||||
|
storageList: make(map[common.Hash][]common.Hash),
|
||||||
}
|
}
|
||||||
switch parent := parent.(type) {
|
switch parent := parent.(type) {
|
||||||
case *diskLayer:
|
case *diskLayer:
|
||||||
|
|
@ -194,11 +202,6 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
|
||||||
dl.memory += uint64(common.HashLength + len(data))
|
dl.memory += uint64(common.HashLength + len(data))
|
||||||
snapshotDirtyAccountWriteMeter.Mark(int64(len(data)))
|
snapshotDirtyAccountWriteMeter.Mark(int64(len(data)))
|
||||||
}
|
}
|
||||||
// Fill the storage hashes and sort them for the iterator
|
|
||||||
dl.storageList = make(map[common.Hash][]common.Hash)
|
|
||||||
for accountHash := range destructs {
|
|
||||||
dl.storageList[accountHash] = nil
|
|
||||||
}
|
|
||||||
// Determine memory size and track the dirty writes
|
// Determine memory size and track the dirty writes
|
||||||
for _, slots := range storage {
|
for _, slots := range storage {
|
||||||
for _, data := range slots {
|
for _, data := range slots {
|
||||||
|
|
@ -206,7 +209,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
|
||||||
snapshotDirtyStorageWriteMeter.Mark(int64(len(data)))
|
snapshotDirtyStorageWriteMeter.Mark(int64(len(data)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dl.memory += uint64(len(dl.storageList) * common.HashLength)
|
dl.memory += uint64(len(destructs) * common.HashLength)
|
||||||
return dl
|
return dl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,6 +290,8 @@ func (dl *diffLayer) Account(hash common.Hash) (*Account, error) {
|
||||||
|
|
||||||
// AccountRLP directly retrieves the account RLP associated with a particular
|
// AccountRLP directly retrieves the account RLP associated with a particular
|
||||||
// hash in the snapshot slim data format.
|
// hash in the snapshot slim data format.
|
||||||
|
//
|
||||||
|
// Note the returned account is not a copy, please don't modify it.
|
||||||
func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
|
func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
|
||||||
// Check the bloom filter first whether there's even a point in reaching into
|
// Check the bloom filter first whether there's even a point in reaching into
|
||||||
// all the maps in all the layers below
|
// all the maps in all the layers below
|
||||||
|
|
@ -347,6 +352,8 @@ func (dl *diffLayer) accountRLP(hash common.Hash, depth int) ([]byte, error) {
|
||||||
// Storage directly retrieves the storage data associated with a particular hash,
|
// Storage directly retrieves the storage data associated with a particular hash,
|
||||||
// within a particular account. If the slot is unknown to this diff, it's parent
|
// within a particular account. If the slot is unknown to this diff, it's parent
|
||||||
// is consulted.
|
// is consulted.
|
||||||
|
//
|
||||||
|
// Note the returned slot is not a copy, please don't modify it.
|
||||||
func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error) {
|
func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error) {
|
||||||
// Check the bloom filter first whether there's even a point in reaching into
|
// Check the bloom filter first whether there's even a point in reaching into
|
||||||
// all the maps in all the layers below
|
// all the maps in all the layers below
|
||||||
|
|
@ -502,22 +509,34 @@ func (dl *diffLayer) AccountList() []common.Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(hashes(dl.accountList))
|
sort.Sort(hashes(dl.accountList))
|
||||||
|
dl.memory += uint64(len(dl.accountList) * common.HashLength)
|
||||||
return dl.accountList
|
return dl.accountList
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageList returns a sorted list of all storage slot hashes in this difflayer
|
// StorageList returns a sorted list of all storage slot hashes in this difflayer
|
||||||
// for the given account.
|
// for the given account. If the whole storage is destructed in this layer, then
|
||||||
|
// an additional flag *destructed = true* will be returned, otherwise the flag is
|
||||||
|
// false. Besides, the returned list will include the hash of deleted storage slot.
|
||||||
|
// Note a special case is an account is deleted in a prior tx but is recreated in
|
||||||
|
// the following tx with some storage slots set. In this case the returned list is
|
||||||
|
// not empty but the flag is true.
|
||||||
//
|
//
|
||||||
// Note, the returned slice is not a copy, so do not modify it.
|
// Note, the returned slice is not a copy, so do not modify it.
|
||||||
func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash {
|
func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool) {
|
||||||
// If an old list already exists, return it
|
|
||||||
dl.lock.RLock()
|
dl.lock.RLock()
|
||||||
list := dl.storageList[accountHash]
|
_, destructed := dl.destructSet[accountHash]
|
||||||
|
if _, ok := dl.storageData[accountHash]; !ok {
|
||||||
|
// Account not tracked by this layer
|
||||||
|
dl.lock.RUnlock()
|
||||||
|
return nil, destructed
|
||||||
|
}
|
||||||
|
// If an old list already exists, return it
|
||||||
|
if list, exist := dl.storageList[accountHash]; exist {
|
||||||
|
dl.lock.RUnlock()
|
||||||
|
return list, destructed // the cached list can't be nil
|
||||||
|
}
|
||||||
dl.lock.RUnlock()
|
dl.lock.RUnlock()
|
||||||
|
|
||||||
if list != nil {
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
// No old sorted account list exists, generate a new one
|
// No old sorted account list exists, generate a new one
|
||||||
dl.lock.Lock()
|
dl.lock.Lock()
|
||||||
defer dl.lock.Unlock()
|
defer dl.lock.Unlock()
|
||||||
|
|
@ -529,5 +548,6 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash {
|
||||||
}
|
}
|
||||||
sort.Sort(hashes(storageList))
|
sort.Sort(hashes(storageList))
|
||||||
dl.storageList[accountHash] = storageList
|
dl.storageList[accountHash] = storageList
|
||||||
return storageList
|
dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength)
|
||||||
|
return storageList, destructed
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,8 @@ func TestMergeBasics(t *testing.T) {
|
||||||
if have, want := len(merged.storageList), i; have != want {
|
if have, want := len(merged.storageList), i; have != want {
|
||||||
t.Errorf("[1] storageList wrong: have %v, want %v", have, want)
|
t.Errorf("[1] storageList wrong: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
if have, want := len(merged.StorageList(aHash)), len(sMap); have != want {
|
list, _ := merged.StorageList(aHash)
|
||||||
|
if have, want := len(list), len(sMap); have != want {
|
||||||
t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want)
|
t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
if have, want := len(merged.storageList[aHash]), len(sMap); have != want {
|
if have, want := len(merged.storageList[aHash]), len(sMap); have != want {
|
||||||
|
|
@ -131,7 +132,7 @@ func TestMergeDelete(t *testing.T) {
|
||||||
|
|
||||||
flipDrops := func() map[common.Hash]struct{} {
|
flipDrops := func() map[common.Hash]struct{} {
|
||||||
return map[common.Hash]struct{}{
|
return map[common.Hash]struct{}{
|
||||||
h2: struct{}{},
|
h2: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flipAccs := func() map[common.Hash][]byte {
|
flipAccs := func() map[common.Hash][]byte {
|
||||||
|
|
@ -141,7 +142,7 @@ func TestMergeDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
flopDrops := func() map[common.Hash]struct{} {
|
flopDrops := func() map[common.Hash]struct{} {
|
||||||
return map[common.Hash]struct{}{
|
return map[common.Hash]struct{}{
|
||||||
h1: struct{}{},
|
h1: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flopAccs := func() map[common.Hash][]byte {
|
flopAccs := func() map[common.Hash][]byte {
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,15 @@ package snapshot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -117,10 +121,10 @@ func TestDiskMerge(t *testing.T) {
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
||||||
accDelNoCache: struct{}{},
|
accDelNoCache: {},
|
||||||
accDelCache: struct{}{},
|
accDelCache: {},
|
||||||
conNukeNoCache: struct{}{},
|
conNukeNoCache: {},
|
||||||
conNukeCache: struct{}{},
|
conNukeCache: {},
|
||||||
}, map[common.Hash][]byte{
|
}, map[common.Hash][]byte{
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
|
|
@ -340,10 +344,10 @@ func TestDiskPartialMerge(t *testing.T) {
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
||||||
accDelNoCache: struct{}{},
|
accDelNoCache: {},
|
||||||
accDelCache: struct{}{},
|
accDelCache: {},
|
||||||
conNukeNoCache: struct{}{},
|
conNukeNoCache: {},
|
||||||
conNukeCache: struct{}{},
|
conNukeCache: {},
|
||||||
}, map[common.Hash][]byte{
|
}, map[common.Hash][]byte{
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
|
|
@ -432,4 +436,76 @@ func TestDiskPartialMerge(t *testing.T) {
|
||||||
// This test case is a tiny specialized case of TestDiskPartialMerge, which tests
|
// This test case is a tiny specialized case of TestDiskPartialMerge, which tests
|
||||||
// some very specific cornercases that random tests won't ever trigger.
|
// some very specific cornercases that random tests won't ever trigger.
|
||||||
func TestDiskMidAccountPartialMerge(t *testing.T) {
|
func TestDiskMidAccountPartialMerge(t *testing.T) {
|
||||||
|
// TODO(@karalabe) ?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDiskSeek tests that seek-operations work on the disk layer
|
||||||
|
func TestDiskSeek(t *testing.T) {
|
||||||
|
// Create some accounts in the disk layer
|
||||||
|
var db ethdb.Database
|
||||||
|
|
||||||
|
if dir, err := ioutil.TempDir("", "disklayer-test"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
diskdb, err := leveldb.New(dir, 256, 0, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
db = rawdb.NewDatabase(diskdb)
|
||||||
|
}
|
||||||
|
// Fill even keys [0,2,4...]
|
||||||
|
for i := 0; i < 0xff; i += 2 {
|
||||||
|
acc := common.Hash{byte(i)}
|
||||||
|
rawdb.WriteAccountSnapshot(db, acc, acc[:])
|
||||||
|
}
|
||||||
|
// Add an 'higher' key, with incorrect (higher) prefix
|
||||||
|
highKey := []byte{rawdb.SnapshotAccountPrefix[0] + 1}
|
||||||
|
db.Put(highKey, []byte{0xff, 0xff})
|
||||||
|
|
||||||
|
baseRoot := randomHash()
|
||||||
|
rawdb.WriteSnapshotRoot(db, baseRoot)
|
||||||
|
|
||||||
|
snaps := &Tree{
|
||||||
|
layers: map[common.Hash]snapshot{
|
||||||
|
baseRoot: &diskLayer{
|
||||||
|
diskdb: db,
|
||||||
|
cache: fastcache.New(500 * 1024),
|
||||||
|
root: baseRoot,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Test some different seek positions
|
||||||
|
type testcase struct {
|
||||||
|
pos byte
|
||||||
|
expkey byte
|
||||||
|
}
|
||||||
|
var cases = []testcase{
|
||||||
|
{0xff, 0x55}, // this should exit immediately without checking key
|
||||||
|
{0x01, 0x02},
|
||||||
|
{0xfe, 0xfe},
|
||||||
|
{0xfd, 0xfe},
|
||||||
|
{0x00, 0x00},
|
||||||
|
}
|
||||||
|
for i, tc := range cases {
|
||||||
|
it, err := snaps.AccountIterator(baseRoot, common.Hash{tc.pos})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("case %d, error: %v", i, err)
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for it.Next() {
|
||||||
|
k, v, err := it.Hash()[0], it.Account()[0], it.Error()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d, item %d, error: %v", i, count, err)
|
||||||
|
}
|
||||||
|
// First item in iterator should have the expected key
|
||||||
|
if count == 0 && k != tc.expkey {
|
||||||
|
t.Fatalf("test %d, item %d, got %v exp %v", i, count, k, tc.expkey)
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if v != k {
|
||||||
|
t.Fatalf("test %d, item %d, value wrong, got %v exp %v", i, count, v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// generatorStats is a collection of statistics gathered by the snapshot generator
|
// generatorStats is a collection of statistics gathered by the snapshot generator
|
||||||
// for logging purposes.
|
// for logging purposes.
|
||||||
type generatorStats struct {
|
type generatorStats struct {
|
||||||
wiping chan struct{} // Notification channel if wiping is in progress
|
wiping chan struct{} // Notification channel if wiping is in progress
|
||||||
origin uint64 // Origin prefix where generation started
|
origin uint64 // Origin prefix where generation started
|
||||||
|
|
@ -167,7 +167,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
|
||||||
if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil {
|
if err := rlp.DecodeBytes(accIt.Value, &acc); err != nil {
|
||||||
log.Crit("Invalid account encountered during snapshot creation", "err", err)
|
log.Crit("Invalid account encountered during snapshot creation", "err", err)
|
||||||
}
|
}
|
||||||
data := AccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash)
|
data := SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash)
|
||||||
|
|
||||||
// If the account is not yet in-progress, write it out
|
// If the account is not yet in-progress, write it out
|
||||||
if accMarker == nil || !bytes.Equal(accountHash[:], accMarker) {
|
if accMarker == nil || !bytes.Equal(accountHash[:], accMarker) {
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountIterator is an iterator to step over all the accounts in a snapshot,
|
// Iterator is a iterator to step over all the accounts or the specific
|
||||||
// which may or may npt be composed of multiple layers.
|
// storage in a snapshot which may or may not be composed of multiple layers.
|
||||||
type AccountIterator interface {
|
type Iterator interface {
|
||||||
// Next steps the iterator forward one element, returning false if exhausted,
|
// Next steps the iterator forward one element, returning false if exhausted,
|
||||||
// or an error if iteration failed for some reason (e.g. root being iterated
|
// or an error if iteration failed for some reason (e.g. root being iterated
|
||||||
// becomes stale and garbage collected).
|
// becomes stale and garbage collected).
|
||||||
|
|
@ -38,18 +38,35 @@ type AccountIterator interface {
|
||||||
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
||||||
Error() error
|
Error() error
|
||||||
|
|
||||||
// Hash returns the hash of the account the iterator is currently at.
|
// Hash returns the hash of the account or storage slot the iterator is
|
||||||
|
// currently at.
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
|
|
||||||
// Account returns the RLP encoded slim account the iterator is currently at.
|
|
||||||
// An error will be returned if the iterator becomes invalid (e.g. snaph
|
|
||||||
Account() []byte
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and
|
// Release releases associated resources. Release should always succeed and
|
||||||
// can be called multiple times without causing error.
|
// can be called multiple times without causing error.
|
||||||
Release()
|
Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccountIterator is a iterator to step over all the accounts in a snapshot,
|
||||||
|
// which may or may not be composed of multiple layers.
|
||||||
|
type AccountIterator interface {
|
||||||
|
Iterator
|
||||||
|
|
||||||
|
// Account returns the RLP encoded slim account the iterator is currently at.
|
||||||
|
// An error will be returned if the iterator becomes invalid
|
||||||
|
Account() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageIterator is a iterator to step over the specific storage in a snapshot,
|
||||||
|
// which may or may not be composed of multiple layers.
|
||||||
|
type StorageIterator interface {
|
||||||
|
Iterator
|
||||||
|
|
||||||
|
// Slot returns the storage slot the iterator is currently at. An error will
|
||||||
|
// be returned if the iterator becomes invalid
|
||||||
|
Slot() []byte
|
||||||
|
}
|
||||||
|
|
||||||
// diffAccountIterator is an account iterator that steps over the accounts (both
|
// diffAccountIterator is an account iterator that steps over the accounts (both
|
||||||
// live and deleted) contained within a single diff layer. Higher order iterators
|
// live and deleted) contained within a single diff layer. Higher order iterators
|
||||||
// will use the deleted accounts to skip deeper iterators.
|
// will use the deleted accounts to skip deeper iterators.
|
||||||
|
|
@ -70,7 +87,7 @@ func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator {
|
||||||
// Seek out the requested starting account
|
// Seek out the requested starting account
|
||||||
hashes := dl.AccountList()
|
hashes := dl.AccountList()
|
||||||
index := sort.Search(len(hashes), func(i int) bool {
|
index := sort.Search(len(hashes), func(i int) bool {
|
||||||
return bytes.Compare(seek[:], hashes[i][:]) < 0
|
return bytes.Compare(seek[:], hashes[i][:]) <= 0
|
||||||
})
|
})
|
||||||
// Assemble and returned the already seeked iterator
|
// Assemble and returned the already seeked iterator
|
||||||
return &diffAccountIterator{
|
return &diffAccountIterator{
|
||||||
|
|
@ -120,11 +137,14 @@ func (it *diffAccountIterator) Hash() common.Hash {
|
||||||
// This method assumes that flattening does not delete elements from
|
// This method assumes that flattening does not delete elements from
|
||||||
// the accountdata mapping (writing nil into it is fine though), and will panic
|
// the accountdata mapping (writing nil into it is fine though), and will panic
|
||||||
// if elements have been deleted.
|
// if elements have been deleted.
|
||||||
|
//
|
||||||
|
// Note the returned account is not a copy, please don't modify it.
|
||||||
func (it *diffAccountIterator) Account() []byte {
|
func (it *diffAccountIterator) Account() []byte {
|
||||||
it.layer.lock.RLock()
|
it.layer.lock.RLock()
|
||||||
blob, ok := it.layer.accountData[it.curHash]
|
blob, ok := it.layer.accountData[it.curHash]
|
||||||
if !ok {
|
if !ok {
|
||||||
if _, ok := it.layer.destructSet[it.curHash]; ok {
|
if _, ok := it.layer.destructSet[it.curHash]; ok {
|
||||||
|
it.layer.lock.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
||||||
|
|
@ -148,10 +168,10 @@ type diskAccountIterator struct {
|
||||||
|
|
||||||
// AccountIterator creates an account iterator over a disk layer.
|
// AccountIterator creates an account iterator over a disk layer.
|
||||||
func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator {
|
func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator {
|
||||||
// TODO: Fix seek position, or remove seek parameter
|
pos := common.TrimRightZeroes(seek[:])
|
||||||
return &diskAccountIterator{
|
return &diskAccountIterator{
|
||||||
layer: dl,
|
layer: dl,
|
||||||
it: dl.diskdb.NewIteratorWithPrefix(rawdb.SnapshotAccountPrefix),
|
it: dl.diskdb.NewIterator(rawdb.SnapshotAccountPrefix, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +183,7 @@ func (it *diskAccountIterator) Next() bool {
|
||||||
}
|
}
|
||||||
// Try to advance the iterator and release it if we reached the end
|
// Try to advance the iterator and release it if we reached the end
|
||||||
for {
|
for {
|
||||||
if !it.it.Next() || !bytes.HasPrefix(it.it.Key(), rawdb.SnapshotAccountPrefix) {
|
if !it.it.Next() {
|
||||||
it.it.Release()
|
it.it.Release()
|
||||||
it.it = nil
|
it.it = nil
|
||||||
return false
|
return false
|
||||||
|
|
@ -181,12 +201,15 @@ func (it *diskAccountIterator) Next() bool {
|
||||||
// A diff layer is immutable after creation content wise and can always be fully
|
// A diff layer is immutable after creation content wise and can always be fully
|
||||||
// iterated without error, so this method always returns nil.
|
// iterated without error, so this method always returns nil.
|
||||||
func (it *diskAccountIterator) Error() error {
|
func (it *diskAccountIterator) Error() error {
|
||||||
|
if it.it == nil {
|
||||||
|
return nil // Iterator is exhausted and released
|
||||||
|
}
|
||||||
return it.it.Error()
|
return it.it.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash of the account the iterator is currently at.
|
// Hash returns the hash of the account the iterator is currently at.
|
||||||
func (it *diskAccountIterator) Hash() common.Hash {
|
func (it *diskAccountIterator) Hash() common.Hash {
|
||||||
return common.BytesToHash(it.it.Key())
|
return common.BytesToHash(it.it.Key()) // The prefix will be truncated
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account returns the RLP encoded slim account the iterator is currently at.
|
// Account returns the RLP encoded slim account the iterator is currently at.
|
||||||
|
|
@ -202,3 +225,176 @@ func (it *diskAccountIterator) Release() {
|
||||||
it.it = nil
|
it.it = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diffStorageIterator is a storage iterator that steps over the specific storage
|
||||||
|
// (both live and deleted) contained within a single diff layer. Higher order
|
||||||
|
// iterators will use the deleted slot to skip deeper iterators.
|
||||||
|
type diffStorageIterator struct {
|
||||||
|
// curHash is the current hash the iterator is positioned on. The field is
|
||||||
|
// explicitly tracked since the referenced diff layer might go stale after
|
||||||
|
// the iterator was positioned and we don't want to fail accessing the old
|
||||||
|
// hash as long as the iterator is not touched any more.
|
||||||
|
curHash common.Hash
|
||||||
|
account common.Hash
|
||||||
|
|
||||||
|
layer *diffLayer // Live layer to retrieve values from
|
||||||
|
keys []common.Hash // Keys left in the layer to iterate
|
||||||
|
fail error // Any failures encountered (stale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageIterator creates a storage iterator over a single diff layer.
|
||||||
|
// Execept the storage iterator is returned, there is an additional flag
|
||||||
|
// "destructed" returned. If it's true then it means the whole storage is
|
||||||
|
// destructed in this layer(maybe recreated too), don't bother deeper layer
|
||||||
|
// for storage retrieval.
|
||||||
|
func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) {
|
||||||
|
// Create the storage for this account even it's marked
|
||||||
|
// as destructed. The iterator is for the new one which
|
||||||
|
// just has the same adddress as the deleted one.
|
||||||
|
hashes, destructed := dl.StorageList(account)
|
||||||
|
index := sort.Search(len(hashes), func(i int) bool {
|
||||||
|
return bytes.Compare(seek[:], hashes[i][:]) <= 0
|
||||||
|
})
|
||||||
|
// Assemble and returned the already seeked iterator
|
||||||
|
return &diffStorageIterator{
|
||||||
|
layer: dl,
|
||||||
|
account: account,
|
||||||
|
keys: hashes[index:],
|
||||||
|
}, destructed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next steps the iterator forward one element, returning false if exhausted.
|
||||||
|
func (it *diffStorageIterator) Next() bool {
|
||||||
|
// If the iterator was already stale, consider it a programmer error. Although
|
||||||
|
// we could just return false here, triggering this path would probably mean
|
||||||
|
// somebody forgot to check for Error, so lets blow up instead of undefined
|
||||||
|
// behavior that's hard to debug.
|
||||||
|
if it.fail != nil {
|
||||||
|
panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail))
|
||||||
|
}
|
||||||
|
// Stop iterating if all keys were exhausted
|
||||||
|
if len(it.keys) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if it.layer.Stale() {
|
||||||
|
it.fail, it.keys = ErrSnapshotStale, nil
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Iterator seems to be still alive, retrieve and cache the live hash
|
||||||
|
it.curHash = it.keys[0]
|
||||||
|
// key cached, shift the iterator and notify the user of success
|
||||||
|
it.keys = it.keys[1:]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns any failure that occurred during iteration, which might have
|
||||||
|
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
||||||
|
func (it *diffStorageIterator) Error() error {
|
||||||
|
return it.fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the storage slot the iterator is currently at.
|
||||||
|
func (it *diffStorageIterator) Hash() common.Hash {
|
||||||
|
return it.curHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot returns the raw storage slot value the iterator is currently at.
|
||||||
|
// This method may _fail_, if the underlying layer has been flattened between
|
||||||
|
// the call to Next and Value. That type of error will set it.Err.
|
||||||
|
// This method assumes that flattening does not delete elements from
|
||||||
|
// the storage mapping (writing nil into it is fine though), and will panic
|
||||||
|
// if elements have been deleted.
|
||||||
|
//
|
||||||
|
// Note the returned slot is not a copy, please don't modify it.
|
||||||
|
func (it *diffStorageIterator) Slot() []byte {
|
||||||
|
it.layer.lock.RLock()
|
||||||
|
storage, ok := it.layer.storageData[it.account]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("iterator referenced non-existent account storage: %x", it.account))
|
||||||
|
}
|
||||||
|
// Storage slot might be nil(deleted), but it must exist
|
||||||
|
blob, ok := storage[it.curHash]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("iterator referenced non-existent storage slot: %x", it.curHash))
|
||||||
|
}
|
||||||
|
it.layer.lock.RUnlock()
|
||||||
|
if it.layer.Stale() {
|
||||||
|
it.fail, it.keys = ErrSnapshotStale, nil
|
||||||
|
}
|
||||||
|
return blob
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release is a noop for diff account iterators as there are no held resources.
|
||||||
|
func (it *diffStorageIterator) Release() {}
|
||||||
|
|
||||||
|
// diskStorageIterator is a storage iterator that steps over the live storage
|
||||||
|
// contained within a disk layer.
|
||||||
|
type diskStorageIterator struct {
|
||||||
|
layer *diskLayer
|
||||||
|
account common.Hash
|
||||||
|
it ethdb.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageIterator creates a storage iterator over a disk layer.
|
||||||
|
// If the whole storage is destructed, then all entries in the disk
|
||||||
|
// layer are deleted already. So the "destructed" flag returned here
|
||||||
|
// is always false.
|
||||||
|
func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) {
|
||||||
|
pos := common.TrimRightZeroes(seek[:])
|
||||||
|
return &diskStorageIterator{
|
||||||
|
layer: dl,
|
||||||
|
account: account,
|
||||||
|
it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos),
|
||||||
|
}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next steps the iterator forward one element, returning false if exhausted.
|
||||||
|
func (it *diskStorageIterator) Next() bool {
|
||||||
|
// If the iterator was already exhausted, don't bother
|
||||||
|
if it.it == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Try to advance the iterator and release it if we reached the end
|
||||||
|
for {
|
||||||
|
if !it.it.Next() {
|
||||||
|
it.it.Release()
|
||||||
|
it.it = nil
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(it.it.Key()) == len(rawdb.SnapshotStoragePrefix)+common.HashLength+common.HashLength {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns any failure that occurred during iteration, which might have
|
||||||
|
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
||||||
|
//
|
||||||
|
// A diff layer is immutable after creation content wise and can always be fully
|
||||||
|
// iterated without error, so this method always returns nil.
|
||||||
|
func (it *diskStorageIterator) Error() error {
|
||||||
|
if it.it == nil {
|
||||||
|
return nil // Iterator is exhausted and released
|
||||||
|
}
|
||||||
|
return it.it.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the storage slot the iterator is currently at.
|
||||||
|
func (it *diskStorageIterator) Hash() common.Hash {
|
||||||
|
return common.BytesToHash(it.it.Key()) // The prefix will be truncated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot returns the raw strorage slot content the iterator is currently at.
|
||||||
|
func (it *diskStorageIterator) Slot() []byte {
|
||||||
|
return it.it.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases the database snapshot held during iteration.
|
||||||
|
func (it *diskStorageIterator) Release() {
|
||||||
|
// The iterator is auto-released on exhaustion, so make sure it's still alive
|
||||||
|
if it.it != nil {
|
||||||
|
it.it.Release()
|
||||||
|
it.it = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,29 +22,91 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// binaryAccountIterator is a simplistic iterator to step over the accounts in
|
// binaryIterator is a simplistic iterator to step over the accounts or storage
|
||||||
// a snapshot, which may or may npt be composed of multiple layers. Performance
|
// in a snapshot, which may or may not be composed of multiple layers. Performance
|
||||||
// wise this iterator is slow, it's meant for cross validating the fast one,
|
// wise this iterator is slow, it's meant for cross validating the fast one,
|
||||||
type binaryAccountIterator struct {
|
type binaryIterator struct {
|
||||||
a *diffAccountIterator
|
a Iterator
|
||||||
b AccountIterator
|
b Iterator
|
||||||
aDone bool
|
aDone bool
|
||||||
bDone bool
|
bDone bool
|
||||||
k common.Hash
|
accountIterator bool
|
||||||
fail error
|
k common.Hash
|
||||||
|
account common.Hash
|
||||||
|
fail error
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBinaryAccountIterator creates a simplistic account iterator to step over
|
// initBinaryAccountIterator creates a simplistic iterator to step over all the
|
||||||
// all the accounts in a slow, but eaily verifiable way.
|
// accounts in a slow, but eaily verifiable way. Note this function is used for
|
||||||
func (dl *diffLayer) newBinaryAccountIterator() AccountIterator {
|
// initialization, use `newBinaryAccountIterator` as the API.
|
||||||
|
func (dl *diffLayer) initBinaryAccountIterator() Iterator {
|
||||||
parent, ok := dl.parent.(*diffLayer)
|
parent, ok := dl.parent.(*diffLayer)
|
||||||
if !ok {
|
if !ok {
|
||||||
// parent is the disk layer
|
l := &binaryIterator{
|
||||||
return dl.AccountIterator(common.Hash{})
|
a: dl.AccountIterator(common.Hash{}),
|
||||||
|
b: dl.Parent().AccountIterator(common.Hash{}),
|
||||||
|
accountIterator: true,
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = !l.b.Next()
|
||||||
|
return l
|
||||||
}
|
}
|
||||||
l := &binaryAccountIterator{
|
l := &binaryIterator{
|
||||||
a: dl.AccountIterator(common.Hash{}).(*diffAccountIterator),
|
a: dl.AccountIterator(common.Hash{}),
|
||||||
b: parent.newBinaryAccountIterator(),
|
b: parent.initBinaryAccountIterator(),
|
||||||
|
accountIterator: true,
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = !l.b.Next()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// initBinaryStorageIterator creates a simplistic iterator to step over all the
|
||||||
|
// storage slots in a slow, but eaily verifiable way. Note this function is used
|
||||||
|
// for initialization, use `newBinaryStorageIterator` as the API.
|
||||||
|
func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator {
|
||||||
|
parent, ok := dl.parent.(*diffLayer)
|
||||||
|
if !ok {
|
||||||
|
// If the storage in this layer is already destructed, discard all
|
||||||
|
// deeper layers but still return an valid single-branch iterator.
|
||||||
|
a, destructed := dl.StorageIterator(account, common.Hash{})
|
||||||
|
if destructed {
|
||||||
|
l := &binaryIterator{
|
||||||
|
a: a,
|
||||||
|
account: account,
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = true
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
// The parent is disk layer, don't need to take care "destructed"
|
||||||
|
// anymore.
|
||||||
|
b, _ := dl.Parent().StorageIterator(account, common.Hash{})
|
||||||
|
l := &binaryIterator{
|
||||||
|
a: a,
|
||||||
|
b: b,
|
||||||
|
account: account,
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = !l.b.Next()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
// If the storage in this layer is already destructed, discard all
|
||||||
|
// deeper layers but still return an valid single-branch iterator.
|
||||||
|
a, destructed := dl.StorageIterator(account, common.Hash{})
|
||||||
|
if destructed {
|
||||||
|
l := &binaryIterator{
|
||||||
|
a: a,
|
||||||
|
account: account,
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = true
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
l := &binaryIterator{
|
||||||
|
a: a,
|
||||||
|
b: parent.initBinaryStorageIterator(account),
|
||||||
|
account: account,
|
||||||
}
|
}
|
||||||
l.aDone = !l.a.Next()
|
l.aDone = !l.a.Next()
|
||||||
l.bDone = !l.b.Next()
|
l.bDone = !l.b.Next()
|
||||||
|
|
@ -54,23 +116,22 @@ func (dl *diffLayer) newBinaryAccountIterator() AccountIterator {
|
||||||
// Next steps the iterator forward one element, returning false if exhausted,
|
// Next steps the iterator forward one element, returning false if exhausted,
|
||||||
// or an error if iteration failed for some reason (e.g. root being iterated
|
// or an error if iteration failed for some reason (e.g. root being iterated
|
||||||
// becomes stale and garbage collected).
|
// becomes stale and garbage collected).
|
||||||
func (it *binaryAccountIterator) Next() bool {
|
func (it *binaryIterator) Next() bool {
|
||||||
if it.aDone && it.bDone {
|
if it.aDone && it.bDone {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
nextB := it.b.Hash()
|
|
||||||
first:
|
first:
|
||||||
nextA := it.a.Hash()
|
|
||||||
if it.aDone {
|
if it.aDone {
|
||||||
|
it.k = it.b.Hash()
|
||||||
it.bDone = !it.b.Next()
|
it.bDone = !it.b.Next()
|
||||||
it.k = nextB
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if it.bDone {
|
if it.bDone {
|
||||||
|
it.k = it.a.Hash()
|
||||||
it.aDone = !it.a.Next()
|
it.aDone = !it.a.Next()
|
||||||
it.k = nextA
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
nextA, nextB := it.a.Hash(), it.b.Hash()
|
||||||
if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 {
|
if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 {
|
||||||
it.aDone = !it.a.Next()
|
it.aDone = !it.a.Next()
|
||||||
it.k = nextA
|
it.k = nextA
|
||||||
|
|
@ -87,20 +148,43 @@ first:
|
||||||
|
|
||||||
// Error returns any failure that occurred during iteration, which might have
|
// Error returns any failure that occurred during iteration, which might have
|
||||||
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
||||||
func (it *binaryAccountIterator) Error() error {
|
func (it *binaryIterator) Error() error {
|
||||||
return it.fail
|
return it.fail
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash of the account the iterator is currently at.
|
// Hash returns the hash of the account the iterator is currently at.
|
||||||
func (it *binaryAccountIterator) Hash() common.Hash {
|
func (it *binaryIterator) Hash() common.Hash {
|
||||||
return it.k
|
return it.k
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account returns the RLP encoded slim account the iterator is currently at, or
|
// Account returns the RLP encoded slim account the iterator is currently at, or
|
||||||
// nil if the iterated snapshot stack became stale (you can check Error after
|
// nil if the iterated snapshot stack became stale (you can check Error after
|
||||||
// to see if it failed or not).
|
// to see if it failed or not).
|
||||||
func (it *binaryAccountIterator) Account() []byte {
|
//
|
||||||
blob, err := it.a.layer.AccountRLP(it.k)
|
// Note the returned account is not a copy, please don't modify it.
|
||||||
|
func (it *binaryIterator) Account() []byte {
|
||||||
|
if !it.accountIterator {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// The topmost iterator must be `diffAccountIterator`
|
||||||
|
blob, err := it.a.(*diffAccountIterator).layer.AccountRLP(it.k)
|
||||||
|
if err != nil {
|
||||||
|
it.fail = err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return blob
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot returns the raw storage slot data the iterator is currently at, or
|
||||||
|
// nil if the iterated snapshot stack became stale (you can check Error after
|
||||||
|
// to see if it failed or not).
|
||||||
|
//
|
||||||
|
// Note the returned slot is not a copy, please don't modify it.
|
||||||
|
func (it *binaryIterator) Slot() []byte {
|
||||||
|
if it.accountIterator {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
blob, err := it.a.(*diffStorageIterator).layer.Storage(it.account, it.k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
it.fail = err
|
it.fail = err
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -109,7 +193,21 @@ func (it *binaryAccountIterator) Account() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release recursively releases all the iterators in the stack.
|
// Release recursively releases all the iterators in the stack.
|
||||||
func (it *binaryAccountIterator) Release() {
|
func (it *binaryIterator) Release() {
|
||||||
it.a.Release()
|
it.a.Release()
|
||||||
it.b.Release()
|
it.b.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newBinaryAccountIterator creates a simplistic account iterator to step over
|
||||||
|
// all the accounts in a slow, but eaily verifiable way.
|
||||||
|
func (dl *diffLayer) newBinaryAccountIterator() AccountIterator {
|
||||||
|
iter := dl.initBinaryAccountIterator()
|
||||||
|
return iter.(AccountIterator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBinaryStorageIterator creates a simplistic account iterator to step over
|
||||||
|
// all the storage slots in a slow, but eaily verifiable way.
|
||||||
|
func (dl *diffLayer) newBinaryStorageIterator(account common.Hash) StorageIterator {
|
||||||
|
iter := dl.initBinaryStorageIterator(account)
|
||||||
|
return iter.(StorageIterator)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,23 +24,23 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// weightedAccountIterator is an account iterator with an assigned weight. It is
|
// weightedIterator is a iterator with an assigned weight. It is used to prioritise
|
||||||
// used to prioritise which account is the correct one if multiple iterators find
|
// which account or storage slot is the correct one if multiple iterators find the
|
||||||
// the same one (modified in multiple consecutive blocks).
|
// same one (modified in multiple consecutive blocks).
|
||||||
type weightedAccountIterator struct {
|
type weightedIterator struct {
|
||||||
it AccountIterator
|
it Iterator
|
||||||
priority int
|
priority int
|
||||||
}
|
}
|
||||||
|
|
||||||
// weightedAccountIterators is a set of iterators implementing the sort.Interface.
|
// weightedIterators is a set of iterators implementing the sort.Interface.
|
||||||
type weightedAccountIterators []*weightedAccountIterator
|
type weightedIterators []*weightedIterator
|
||||||
|
|
||||||
// Len implements sort.Interface, returning the number of active iterators.
|
// Len implements sort.Interface, returning the number of active iterators.
|
||||||
func (its weightedAccountIterators) Len() int { return len(its) }
|
func (its weightedIterators) Len() int { return len(its) }
|
||||||
|
|
||||||
// Less implements sort.Interface, returning which of two iterators in the stack
|
// Less implements sort.Interface, returning which of two iterators in the stack
|
||||||
// is before the other.
|
// is before the other.
|
||||||
func (its weightedAccountIterators) Less(i, j int) bool {
|
func (its weightedIterators) Less(i, j int) bool {
|
||||||
// Order the iterators primarily by the account hashes
|
// Order the iterators primarily by the account hashes
|
||||||
hashI := its[i].it.Hash()
|
hashI := its[i].it.Hash()
|
||||||
hashJ := its[j].it.Hash()
|
hashJ := its[j].it.Hash()
|
||||||
|
|
@ -51,45 +51,64 @@ func (its weightedAccountIterators) Less(i, j int) bool {
|
||||||
case 1:
|
case 1:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Same account in multiple layers, split by priority
|
// Same account/storage-slot in multiple layers, split by priority
|
||||||
return its[i].priority < its[j].priority
|
return its[i].priority < its[j].priority
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap implements sort.Interface, swapping two entries in the iterator stack.
|
// Swap implements sort.Interface, swapping two entries in the iterator stack.
|
||||||
func (its weightedAccountIterators) Swap(i, j int) {
|
func (its weightedIterators) Swap(i, j int) {
|
||||||
its[i], its[j] = its[j], its[i]
|
its[i], its[j] = its[j], its[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// fastAccountIterator is a more optimized multi-layer iterator which maintains a
|
// fastIterator is a more optimized multi-layer iterator which maintains a
|
||||||
// direct mapping of all iterators leading down to the bottom layer.
|
// direct mapping of all iterators leading down to the bottom layer.
|
||||||
type fastAccountIterator struct {
|
type fastIterator struct {
|
||||||
tree *Tree // Snapshot tree to reinitialize stale sub-iterators with
|
tree *Tree // Snapshot tree to reinitialize stale sub-iterators with
|
||||||
root common.Hash // Root hash to reinitialize stale sub-iterators through
|
root common.Hash // Root hash to reinitialize stale sub-iterators through
|
||||||
curAccount []byte
|
|
||||||
|
|
||||||
iterators weightedAccountIterators
|
curAccount []byte
|
||||||
|
curSlot []byte
|
||||||
|
|
||||||
|
iterators weightedIterators
|
||||||
initiated bool
|
initiated bool
|
||||||
|
account bool
|
||||||
fail error
|
fail error
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFastAccountIterator creates a new hierarhical account iterator with one
|
// newFastIterator creates a new hierarhical account or storage iterator with one
|
||||||
// element per diff layer. The returned combo iterator can be used to walk over
|
// element per diff layer. The returned combo iterator can be used to walk over
|
||||||
// the entire snapshot diff stack simultaneously.
|
// the entire snapshot diff stack simultaneously.
|
||||||
func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) {
|
func newFastIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash, accountIterator bool) (*fastIterator, error) {
|
||||||
snap := tree.Snapshot(root)
|
snap := tree.Snapshot(root)
|
||||||
if snap == nil {
|
if snap == nil {
|
||||||
return nil, fmt.Errorf("unknown snapshot: %x", root)
|
return nil, fmt.Errorf("unknown snapshot: %x", root)
|
||||||
}
|
}
|
||||||
fi := &fastAccountIterator{
|
fi := &fastIterator{
|
||||||
tree: tree,
|
tree: tree,
|
||||||
root: root,
|
root: root,
|
||||||
|
account: accountIterator,
|
||||||
}
|
}
|
||||||
current := snap.(snapshot)
|
current := snap.(snapshot)
|
||||||
for depth := 0; current != nil; depth++ {
|
for depth := 0; current != nil; depth++ {
|
||||||
fi.iterators = append(fi.iterators, &weightedAccountIterator{
|
if accountIterator {
|
||||||
it: current.AccountIterator(seek),
|
fi.iterators = append(fi.iterators, &weightedIterator{
|
||||||
priority: depth,
|
it: current.AccountIterator(seek),
|
||||||
})
|
priority: depth,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// If the whole storage is destructed in this layer, don't
|
||||||
|
// bother deeper layer anymore. But we should still keep
|
||||||
|
// the iterator for this layer, since the iterator can contain
|
||||||
|
// some valid slots which belongs to the re-created account.
|
||||||
|
it, destructed := current.StorageIterator(account, seek)
|
||||||
|
fi.iterators = append(fi.iterators, &weightedIterator{
|
||||||
|
it: it,
|
||||||
|
priority: depth,
|
||||||
|
})
|
||||||
|
if destructed {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
current = current.Parent()
|
current = current.Parent()
|
||||||
}
|
}
|
||||||
fi.init()
|
fi.init()
|
||||||
|
|
@ -98,7 +117,7 @@ func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (Acc
|
||||||
|
|
||||||
// init walks over all the iterators and resolves any clashes between them, after
|
// init walks over all the iterators and resolves any clashes between them, after
|
||||||
// which it prepares the stack for step-by-step iteration.
|
// which it prepares the stack for step-by-step iteration.
|
||||||
func (fi *fastAccountIterator) init() {
|
func (fi *fastIterator) init() {
|
||||||
// Track which account hashes are iterators positioned on
|
// Track which account hashes are iterators positioned on
|
||||||
var positioned = make(map[common.Hash]int)
|
var positioned = make(map[common.Hash]int)
|
||||||
|
|
||||||
|
|
@ -153,7 +172,7 @@ func (fi *fastAccountIterator) init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next steps the iterator forward one element, returning false if exhausted.
|
// Next steps the iterator forward one element, returning false if exhausted.
|
||||||
func (fi *fastAccountIterator) Next() bool {
|
func (fi *fastIterator) Next() bool {
|
||||||
if len(fi.iterators) == 0 {
|
if len(fi.iterators) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -161,21 +180,25 @@ func (fi *fastAccountIterator) Next() bool {
|
||||||
// Don't forward first time -- we had to 'Next' once in order to
|
// Don't forward first time -- we had to 'Next' once in order to
|
||||||
// do the sorting already
|
// do the sorting already
|
||||||
fi.initiated = true
|
fi.initiated = true
|
||||||
fi.curAccount = fi.iterators[0].it.Account()
|
if fi.account {
|
||||||
|
fi.curAccount = fi.iterators[0].it.(AccountIterator).Account()
|
||||||
|
} else {
|
||||||
|
fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot()
|
||||||
|
}
|
||||||
if innerErr := fi.iterators[0].it.Error(); innerErr != nil {
|
if innerErr := fi.iterators[0].it.Error(); innerErr != nil {
|
||||||
fi.fail = innerErr
|
fi.fail = innerErr
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if fi.curAccount != nil {
|
if fi.curAccount != nil || fi.curSlot != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Implicit else: we've hit a nil-account, and need to fall through to the
|
// Implicit else: we've hit a nil-account or nil-slot, and need to
|
||||||
// loop below to land on something non-nil
|
// fall through to the loop below to land on something non-nil
|
||||||
}
|
}
|
||||||
// If an account is deleted in one of the layers, the key will still be there,
|
// If an account or a slot is deleted in one of the layers, the key will
|
||||||
// but the actual value will be nil. However, the iterator should not
|
// still be there, but the actual value will be nil. However, the iterator
|
||||||
// export nil-values (but instead simply omit the key), so we need to loop
|
// should not export nil-values (but instead simply omit the key), so we
|
||||||
// here until we either
|
// need to loop here until we either
|
||||||
// - get a non-nil value,
|
// - get a non-nil value,
|
||||||
// - hit an error,
|
// - hit an error,
|
||||||
// - or exhaust the iterator
|
// - or exhaust the iterator
|
||||||
|
|
@ -183,12 +206,16 @@ func (fi *fastAccountIterator) Next() bool {
|
||||||
if !fi.next(0) {
|
if !fi.next(0) {
|
||||||
return false // exhausted
|
return false // exhausted
|
||||||
}
|
}
|
||||||
fi.curAccount = fi.iterators[0].it.Account()
|
if fi.account {
|
||||||
|
fi.curAccount = fi.iterators[0].it.(AccountIterator).Account()
|
||||||
|
} else {
|
||||||
|
fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot()
|
||||||
|
}
|
||||||
if innerErr := fi.iterators[0].it.Error(); innerErr != nil {
|
if innerErr := fi.iterators[0].it.Error(); innerErr != nil {
|
||||||
fi.fail = innerErr
|
fi.fail = innerErr
|
||||||
return false // error
|
return false // error
|
||||||
}
|
}
|
||||||
if fi.curAccount != nil {
|
if fi.curAccount != nil || fi.curSlot != nil {
|
||||||
break // non-nil value found
|
break // non-nil value found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +228,7 @@ func (fi *fastAccountIterator) Next() bool {
|
||||||
// For example, if the iterated hashes become [2,3,5,5,8,9,10], then we should
|
// For example, if the iterated hashes become [2,3,5,5,8,9,10], then we should
|
||||||
// invoke next(3), which will call Next on elem 3 (the second '5') and will
|
// invoke next(3), which will call Next on elem 3 (the second '5') and will
|
||||||
// cascade along the list, applying the same operation if needed.
|
// cascade along the list, applying the same operation if needed.
|
||||||
func (fi *fastAccountIterator) next(idx int) bool {
|
func (fi *fastIterator) next(idx int) bool {
|
||||||
// If this particular iterator got exhausted, remove it and return true (the
|
// If this particular iterator got exhausted, remove it and return true (the
|
||||||
// next one is surely not exhausted yet, otherwise it would have been removed
|
// next one is surely not exhausted yet, otherwise it would have been removed
|
||||||
// already).
|
// already).
|
||||||
|
|
@ -262,7 +289,7 @@ func (fi *fastAccountIterator) next(idx int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// move advances an iterator to another position in the list.
|
// move advances an iterator to another position in the list.
|
||||||
func (fi *fastAccountIterator) move(index, newpos int) {
|
func (fi *fastIterator) move(index, newpos int) {
|
||||||
elem := fi.iterators[index]
|
elem := fi.iterators[index]
|
||||||
copy(fi.iterators[index:], fi.iterators[index+1:newpos+1])
|
copy(fi.iterators[index:], fi.iterators[index+1:newpos+1])
|
||||||
fi.iterators[newpos] = elem
|
fi.iterators[newpos] = elem
|
||||||
|
|
@ -270,23 +297,30 @@ func (fi *fastAccountIterator) move(index, newpos int) {
|
||||||
|
|
||||||
// Error returns any failure that occurred during iteration, which might have
|
// Error returns any failure that occurred during iteration, which might have
|
||||||
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
// caused a premature iteration exit (e.g. snapshot stack becoming stale).
|
||||||
func (fi *fastAccountIterator) Error() error {
|
func (fi *fastIterator) Error() error {
|
||||||
return fi.fail
|
return fi.fail
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the current key
|
// Hash returns the current key
|
||||||
func (fi *fastAccountIterator) Hash() common.Hash {
|
func (fi *fastIterator) Hash() common.Hash {
|
||||||
return fi.iterators[0].it.Hash()
|
return fi.iterators[0].it.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account returns the current key
|
// Account returns the current account blob.
|
||||||
func (fi *fastAccountIterator) Account() []byte {
|
// Note the returned account is not a copy, please don't modify it.
|
||||||
|
func (fi *fastIterator) Account() []byte {
|
||||||
return fi.curAccount
|
return fi.curAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slot returns the current storage slot.
|
||||||
|
// Note the returned slot is not a copy, please don't modify it.
|
||||||
|
func (fi *fastIterator) Slot() []byte {
|
||||||
|
return fi.curSlot
|
||||||
|
}
|
||||||
|
|
||||||
// Release iterates over all the remaining live layer iterators and releases each
|
// Release iterates over all the remaining live layer iterators and releases each
|
||||||
// of thme individually.
|
// of thme individually.
|
||||||
func (fi *fastAccountIterator) Release() {
|
func (fi *fastIterator) Release() {
|
||||||
for _, it := range fi.iterators {
|
for _, it := range fi.iterators {
|
||||||
it.it.Release()
|
it.it.Release()
|
||||||
}
|
}
|
||||||
|
|
@ -294,9 +328,23 @@ func (fi *fastAccountIterator) Release() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug is a convencience helper during testing
|
// Debug is a convencience helper during testing
|
||||||
func (fi *fastAccountIterator) Debug() {
|
func (fi *fastIterator) Debug() {
|
||||||
for _, it := range fi.iterators {
|
for _, it := range fi.iterators {
|
||||||
fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0])
|
fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0])
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newFastAccountIterator creates a new hierarhical account iterator with one
|
||||||
|
// element per diff layer. The returned combo iterator can be used to walk over
|
||||||
|
// the entire snapshot diff stack simultaneously.
|
||||||
|
func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) {
|
||||||
|
return newFastIterator(tree, root, common.Hash{}, seek, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFastStorageIterator creates a new hierarhical storage iterator with one
|
||||||
|
// element per diff layer. The returned combo iterator can be used to walk over
|
||||||
|
// the entire snapshot diff stack simultaneously.
|
||||||
|
func newFastStorageIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) {
|
||||||
|
return newFastIterator(tree, root, account, seek, false)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestAccountIteratorBasics tests some simple single-layer iteration
|
// TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration
|
||||||
func TestAccountIteratorBasics(t *testing.T) {
|
func TestAccountIteratorBasics(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
destructs = make(map[common.Hash]struct{})
|
||||||
|
|
@ -53,9 +53,55 @@ func TestAccountIteratorBasics(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some (identical) layers on top
|
// Add some (identical) layers on top
|
||||||
parent := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
||||||
it := parent.AccountIterator(common.Hash{})
|
it := diffLayer.AccountIterator(common.Hash{})
|
||||||
verifyIterator(t, 100, it)
|
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
|
|
||||||
|
diskLayer := diffToDisk(diffLayer)
|
||||||
|
it = diskLayer.AccountIterator(common.Hash{})
|
||||||
|
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestStorageIteratorBasics tests some simple single-layer(diff and disk) iteration for storage
|
||||||
|
func TestStorageIteratorBasics(t *testing.T) {
|
||||||
|
var (
|
||||||
|
nilStorage = make(map[common.Hash]int)
|
||||||
|
accounts = make(map[common.Hash][]byte)
|
||||||
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
|
)
|
||||||
|
// Fill some random data
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
h := randomHash()
|
||||||
|
accounts[h] = randomAccount()
|
||||||
|
|
||||||
|
accStorage := make(map[common.Hash][]byte)
|
||||||
|
value := make([]byte, 32)
|
||||||
|
|
||||||
|
var nilstorage int
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
rand.Read(value)
|
||||||
|
if rand.Intn(2) == 0 {
|
||||||
|
accStorage[randomHash()] = common.CopyBytes(value)
|
||||||
|
} else {
|
||||||
|
accStorage[randomHash()] = nil // delete slot
|
||||||
|
nilstorage += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storage[h] = accStorage
|
||||||
|
nilStorage[h] = nilstorage
|
||||||
|
}
|
||||||
|
// Add some (identical) layers on top
|
||||||
|
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, nil, copyAccounts(accounts), copyStorage(storage))
|
||||||
|
for account := range accounts {
|
||||||
|
it, _ := diffLayer.StorageIterator(account, common.Hash{})
|
||||||
|
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
diskLayer := diffToDisk(diffLayer)
|
||||||
|
for account := range accounts {
|
||||||
|
it, _ := diskLayer.StorageIterator(account, common.Hash{})
|
||||||
|
verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type testIterator struct {
|
type testIterator struct {
|
||||||
|
|
@ -87,6 +133,10 @@ func (ti *testIterator) Account() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ti *testIterator) Slot() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ti *testIterator) Release() {}
|
func (ti *testIterator) Release() {}
|
||||||
|
|
||||||
func TestFastIteratorBasics(t *testing.T) {
|
func TestFastIteratorBasics(t *testing.T) {
|
||||||
|
|
@ -102,13 +152,12 @@ func TestFastIteratorBasics(t *testing.T) {
|
||||||
{9, 10}, {10, 13, 15, 16}},
|
{9, 10}, {10, 13, 15, 16}},
|
||||||
expKeys: []byte{0, 1, 2, 7, 8, 9, 10, 13, 14, 15, 16}},
|
expKeys: []byte{0, 1, 2, 7, 8, 9, 10, 13, 14, 15, 16}},
|
||||||
} {
|
} {
|
||||||
var iterators []*weightedAccountIterator
|
var iterators []*weightedIterator
|
||||||
for i, data := range tc.lists {
|
for i, data := range tc.lists {
|
||||||
it := newTestIterator(data...)
|
it := newTestIterator(data...)
|
||||||
iterators = append(iterators, &weightedAccountIterator{it, i})
|
iterators = append(iterators, &weightedIterator{it, i})
|
||||||
|
|
||||||
}
|
}
|
||||||
fi := &fastAccountIterator{
|
fi := &fastIterator{
|
||||||
iterators: iterators,
|
iterators: iterators,
|
||||||
initiated: false,
|
initiated: false,
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +171,15 @@ func TestFastIteratorBasics(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyIterator(t *testing.T, expCount int, it AccountIterator) {
|
type verifyContent int
|
||||||
|
|
||||||
|
const (
|
||||||
|
verifyNothing verifyContent = iota
|
||||||
|
verifyAccount
|
||||||
|
verifyStorage
|
||||||
|
)
|
||||||
|
|
||||||
|
func verifyIterator(t *testing.T, expCount int, it Iterator, verify verifyContent) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -134,10 +191,13 @@ func verifyIterator(t *testing.T, expCount int, it AccountIterator) {
|
||||||
if bytes.Compare(last[:], hash[:]) >= 0 {
|
if bytes.Compare(last[:], hash[:]) >= 0 {
|
||||||
t.Errorf("wrong order: %x >= %x", last, hash)
|
t.Errorf("wrong order: %x >= %x", last, hash)
|
||||||
}
|
}
|
||||||
if it.Account() == nil {
|
count++
|
||||||
|
if verify == verifyAccount && len(it.(AccountIterator).Account()) == 0 {
|
||||||
|
t.Errorf("iterator returned nil-value for hash %x", hash)
|
||||||
|
} else if verify == verifyStorage && len(it.(StorageIterator).Slot()) == 0 {
|
||||||
t.Errorf("iterator returned nil-value for hash %x", hash)
|
t.Errorf("iterator returned nil-value for hash %x", hash)
|
||||||
}
|
}
|
||||||
count++
|
last = hash
|
||||||
}
|
}
|
||||||
if count != expCount {
|
if count != expCount {
|
||||||
t.Errorf("iterator count mismatch: have %d, want %d", count, expCount)
|
t.Errorf("iterator count mismatch: have %d, want %d", count, expCount)
|
||||||
|
|
@ -173,13 +233,74 @@ func TestAccountIteratorTraversal(t *testing.T) {
|
||||||
// Verify the single and multi-layer iterators
|
// Verify the single and multi-layer iterators
|
||||||
head := snaps.Snapshot(common.HexToHash("0x04"))
|
head := snaps.Snapshot(common.HexToHash("0x04"))
|
||||||
|
|
||||||
verifyIterator(t, 3, head.(snapshot).AccountIterator(common.Hash{}))
|
verifyIterator(t, 3, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing)
|
||||||
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator())
|
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount)
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
||||||
defer it.Release()
|
verifyIterator(t, 7, it, verifyAccount)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
verifyIterator(t, 7, it)
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x04"), 2)
|
||||||
|
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount)
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
||||||
|
verifyIterator(t, 7, it, verifyAccount)
|
||||||
|
it.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorageIteratorTraversal(t *testing.T) {
|
||||||
|
// Create an empty base layer and a snapshot tree out of it
|
||||||
|
base := &diskLayer{
|
||||||
|
diskdb: rawdb.NewMemoryDatabase(),
|
||||||
|
root: common.HexToHash("0x01"),
|
||||||
|
cache: fastcache.New(1024 * 500),
|
||||||
|
}
|
||||||
|
snaps := &Tree{
|
||||||
|
layers: map[common.Hash]snapshot{
|
||||||
|
base.root: base,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Stack three diff layers on top with various overlaps
|
||||||
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
||||||
|
|
||||||
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04", "0x05", "0x06"}}, nil))
|
||||||
|
|
||||||
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
||||||
|
|
||||||
|
// Verify the single and multi-layer iterators
|
||||||
|
head := snaps.Snapshot(common.HexToHash("0x04"))
|
||||||
|
|
||||||
|
diffIter, _ := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 3, diffIter, verifyNothing)
|
||||||
|
verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage)
|
||||||
|
|
||||||
|
it, _ := snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 6, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x04"), 2)
|
||||||
|
verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage)
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 6, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAccountIteratorTraversalValues tests some multi-layer iteration, where we
|
// TestAccountIteratorTraversalValues tests some multi-layer iteration, where we
|
||||||
|
|
@ -242,8 +363,6 @@ func TestAccountIteratorTraversalValues(t *testing.T) {
|
||||||
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil)
|
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil)
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
||||||
defer it.Release()
|
|
||||||
|
|
||||||
head := snaps.Snapshot(common.HexToHash("0x09"))
|
head := snaps.Snapshot(common.HexToHash("0x09"))
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
hash := it.Hash()
|
hash := it.Hash()
|
||||||
|
|
@ -255,6 +374,128 @@ func TestAccountIteratorTraversalValues(t *testing.T) {
|
||||||
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x09"), 2)
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
||||||
|
for it.Next() {
|
||||||
|
hash := it.Hash()
|
||||||
|
want, err := head.AccountRLP(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve expected account: %v", err)
|
||||||
|
}
|
||||||
|
if have := it.Account(); !bytes.Equal(want, have) {
|
||||||
|
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorageIteratorTraversalValues(t *testing.T) {
|
||||||
|
// Create an empty base layer and a snapshot tree out of it
|
||||||
|
base := &diskLayer{
|
||||||
|
diskdb: rawdb.NewMemoryDatabase(),
|
||||||
|
root: common.HexToHash("0x01"),
|
||||||
|
cache: fastcache.New(1024 * 500),
|
||||||
|
}
|
||||||
|
snaps := &Tree{
|
||||||
|
layers: map[common.Hash]snapshot{
|
||||||
|
base.root: base,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapStorage := func(storage map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte {
|
||||||
|
return map[common.Hash]map[common.Hash][]byte{
|
||||||
|
common.HexToHash("0xaa"): storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a batch of storage sets to seed subsequent layers with
|
||||||
|
var (
|
||||||
|
a = make(map[common.Hash][]byte)
|
||||||
|
b = make(map[common.Hash][]byte)
|
||||||
|
c = make(map[common.Hash][]byte)
|
||||||
|
d = make(map[common.Hash][]byte)
|
||||||
|
e = make(map[common.Hash][]byte)
|
||||||
|
f = make(map[common.Hash][]byte)
|
||||||
|
g = make(map[common.Hash][]byte)
|
||||||
|
h = make(map[common.Hash][]byte)
|
||||||
|
)
|
||||||
|
for i := byte(2); i < 0xff; i++ {
|
||||||
|
a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i))
|
||||||
|
if i > 20 && i%2 == 0 {
|
||||||
|
b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i))
|
||||||
|
}
|
||||||
|
if i%4 == 0 {
|
||||||
|
c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i))
|
||||||
|
}
|
||||||
|
if i%7 == 0 {
|
||||||
|
d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i))
|
||||||
|
}
|
||||||
|
if i%8 == 0 {
|
||||||
|
e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i))
|
||||||
|
}
|
||||||
|
if i > 50 || i < 85 {
|
||||||
|
f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i))
|
||||||
|
}
|
||||||
|
if i%64 == 0 {
|
||||||
|
g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i))
|
||||||
|
}
|
||||||
|
if i%128 == 0 {
|
||||||
|
h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Assemble a stack of snapshots from the account layers
|
||||||
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, randomAccountSet("0xaa"), wrapStorage(a))
|
||||||
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, randomAccountSet("0xaa"), wrapStorage(b))
|
||||||
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, randomAccountSet("0xaa"), wrapStorage(c))
|
||||||
|
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, randomAccountSet("0xaa"), wrapStorage(d))
|
||||||
|
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), nil, randomAccountSet("0xaa"), wrapStorage(e))
|
||||||
|
snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), nil, randomAccountSet("0xaa"), wrapStorage(e))
|
||||||
|
snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), nil, randomAccountSet("0xaa"), wrapStorage(g))
|
||||||
|
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, randomAccountSet("0xaa"), wrapStorage(h))
|
||||||
|
|
||||||
|
it, _ := snaps.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
head := snaps.Snapshot(common.HexToHash("0x09"))
|
||||||
|
for it.Next() {
|
||||||
|
hash := it.Hash()
|
||||||
|
want, err := head.Storage(common.HexToHash("0xaa"), hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve expected storage slot: %v", err)
|
||||||
|
}
|
||||||
|
if have := it.Slot(); !bytes.Equal(want, have) {
|
||||||
|
t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x09"), 2)
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
for it.Next() {
|
||||||
|
hash := it.Hash()
|
||||||
|
want, err := head.Storage(common.HexToHash("0xaa"), hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve expected slot: %v", err)
|
||||||
|
}
|
||||||
|
if have := it.Slot(); !bytes.Equal(want, have) {
|
||||||
|
t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This testcase is notorious, all layers contain the exact same 200 accounts.
|
// This testcase is notorious, all layers contain the exact same 200 accounts.
|
||||||
|
|
@ -285,13 +526,27 @@ func TestAccountIteratorLargeTraversal(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Iterate the entire stack and ensure everything is hit only once
|
// Iterate the entire stack and ensure everything is hit only once
|
||||||
head := snaps.Snapshot(common.HexToHash("0x80"))
|
head := snaps.Snapshot(common.HexToHash("0x80"))
|
||||||
verifyIterator(t, 200, head.(snapshot).AccountIterator(common.Hash{}))
|
verifyIterator(t, 200, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing)
|
||||||
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator())
|
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount)
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
||||||
defer it.Release()
|
verifyIterator(t, 200, it, verifyAccount)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
verifyIterator(t, 200, it)
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x80"), 2)
|
||||||
|
|
||||||
|
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount)
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
||||||
|
verifyIterator(t, 200, it, verifyAccount)
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAccountIteratorFlattening tests what happens when we
|
// TestAccountIteratorFlattening tests what happens when we
|
||||||
|
|
@ -351,40 +606,112 @@ func TestAccountIteratorSeek(t *testing.T) {
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
||||||
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
||||||
|
|
||||||
// Construct various iterators and ensure their tranversal is correct
|
// Account set is now
|
||||||
|
// 02: aa, ee, f0, ff
|
||||||
|
// 03: aa, bb, dd, ee, f0 (, f0), ff
|
||||||
|
// 04: aa, bb, cc, dd, ee, f0 (, f0), ff (, ff)
|
||||||
|
// Construct various iterators and ensure their traversal is correct
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd"))
|
it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 3, it) // expected: ee, f0, ff
|
verifyIterator(t, 3, it, verifyAccount) // expected: ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 3, it) // expected: ee, f0, ff
|
verifyIterator(t, 4, it, verifyAccount) // expected: aa, ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 0, it) // expected: nothing
|
verifyIterator(t, 1, it, verifyAccount) // expected: ff
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff1"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 0, it, verifyAccount) // expected: nothing
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 5, it) // expected: cc, dd, ee, f0, ff
|
verifyIterator(t, 6, it, verifyAccount) // expected: bb, cc, dd, ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 2, it) // expected: f0, ff
|
verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 1, it) // expected: ff
|
verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 0, it) // expected: nothing
|
verifyIterator(t, 1, it, verifyAccount) // expected: ff
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff1"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 0, it, verifyAccount) // expected: nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestIteratorDeletions tests that the iterator behaves correct when there are
|
func TestStorageIteratorSeek(t *testing.T) {
|
||||||
|
// Create a snapshot stack with some initial data
|
||||||
|
base := &diskLayer{
|
||||||
|
diskdb: rawdb.NewMemoryDatabase(),
|
||||||
|
root: common.HexToHash("0x01"),
|
||||||
|
cache: fastcache.New(1024 * 500),
|
||||||
|
}
|
||||||
|
snaps := &Tree{
|
||||||
|
layers: map[common.Hash]snapshot{
|
||||||
|
base.root: base,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Stack three diff layers on top with various overlaps
|
||||||
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
||||||
|
|
||||||
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x05", "0x06"}}, nil))
|
||||||
|
|
||||||
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x05", "0x08"}}, nil))
|
||||||
|
|
||||||
|
// Account set is now
|
||||||
|
// 02: 01, 03, 05
|
||||||
|
// 03: 01, 02, 03, 05 (, 05), 06
|
||||||
|
// 04: 01(, 01), 02, 03, 05(, 05, 05), 06, 08
|
||||||
|
// Construct various iterators and ensure their traversal is correct
|
||||||
|
it, _ := snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 3, it, verifyStorage) // expected: 01, 03, 05
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 2, it, verifyStorage) // expected: 03, 05
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 1, it, verifyStorage) // expected: 05
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 0, it, verifyStorage) // expected: nothing
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 6, it, verifyStorage) // expected: 01, 02, 03, 05, 06, 08
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 3, it, verifyStorage) // expected: 05, 06, 08
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 1, it, verifyStorage) // expected: 08
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09"))
|
||||||
|
defer it.Release()
|
||||||
|
verifyIterator(t, 0, it, verifyStorage) // expected: nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAccountIteratorDeletions tests that the iterator behaves correct when there are
|
||||||
// deleted accounts (where the Account() value is nil). The iterator
|
// deleted accounts (where the Account() value is nil). The iterator
|
||||||
// should not output any accounts or nil-values for those cases.
|
// should not output any accounts or nil-values for those cases.
|
||||||
func TestIteratorDeletions(t *testing.T) {
|
func TestAccountIteratorDeletions(t *testing.T) {
|
||||||
// Create an empty base layer and a snapshot tree out of it
|
// Create an empty base layer and a snapshot tree out of it
|
||||||
base := &diskLayer{
|
base := &diskLayer{
|
||||||
diskdb: rawdb.NewMemoryDatabase(),
|
diskdb: rawdb.NewMemoryDatabase(),
|
||||||
|
|
@ -402,7 +729,7 @@ func TestIteratorDeletions(t *testing.T) {
|
||||||
|
|
||||||
deleted := common.HexToHash("0x22")
|
deleted := common.HexToHash("0x22")
|
||||||
destructed := map[common.Hash]struct{}{
|
destructed := map[common.Hash]struct{}{
|
||||||
deleted: struct{}{},
|
deleted: {},
|
||||||
}
|
}
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
destructed, randomAccountSet("0x11", "0x33"), nil)
|
destructed, randomAccountSet("0x11", "0x33"), nil)
|
||||||
|
|
@ -413,7 +740,7 @@ func TestIteratorDeletions(t *testing.T) {
|
||||||
// The output should be 11,33,44,55
|
// The output should be 11,33,44,55
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
||||||
// Do a quick check
|
// Do a quick check
|
||||||
verifyIterator(t, 4, it)
|
verifyIterator(t, 4, it, verifyAccount)
|
||||||
it.Release()
|
it.Release()
|
||||||
|
|
||||||
// And a more detailed verification that we indeed do not see '0x22'
|
// And a more detailed verification that we indeed do not see '0x22'
|
||||||
|
|
@ -430,6 +757,63 @@ func TestIteratorDeletions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStorageIteratorDeletions(t *testing.T) {
|
||||||
|
// Create an empty base layer and a snapshot tree out of it
|
||||||
|
base := &diskLayer{
|
||||||
|
diskdb: rawdb.NewMemoryDatabase(),
|
||||||
|
root: common.HexToHash("0x01"),
|
||||||
|
cache: fastcache.New(1024 * 500),
|
||||||
|
}
|
||||||
|
snaps := &Tree{
|
||||||
|
layers: map[common.Hash]snapshot{
|
||||||
|
base.root: base,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Stack three diff layers on top with various overlaps
|
||||||
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
||||||
|
|
||||||
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}}))
|
||||||
|
|
||||||
|
// The output should be 02,04,05,06
|
||||||
|
it, _ := snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 4, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// The output should be 04,05,06
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03"))
|
||||||
|
verifyIterator(t, 3, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Destruct the whole storage
|
||||||
|
destructed := map[common.Hash]struct{}{
|
||||||
|
common.HexToHash("0xaa"): {},
|
||||||
|
}
|
||||||
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), destructed, nil, nil)
|
||||||
|
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 0, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Re-insert the slots of the same account
|
||||||
|
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil,
|
||||||
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil))
|
||||||
|
|
||||||
|
// The output should be 07,08,09
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 3, it, verifyStorage)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Destruct the whole storage but re-create the account in the same layer
|
||||||
|
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), destructed, randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, nil))
|
||||||
|
it, _ = snaps.StorageIterator(common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
|
verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage)
|
||||||
|
}
|
||||||
|
|
||||||
// BenchmarkAccountIteratorTraversal is a bit a bit notorious -- all layers contain the
|
// BenchmarkAccountIteratorTraversal is a bit a bit notorious -- all layers contain the
|
||||||
// exact same 200 accounts. That means that we need to process 2000 items, but
|
// exact same 200 accounts. That means that we need to process 2000 items, but
|
||||||
// only spit out 200 values eventually.
|
// only spit out 200 values eventually.
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,11 @@ func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
|
||||||
}
|
}
|
||||||
accountData := make(map[common.Hash][]byte)
|
accountData := make(map[common.Hash][]byte)
|
||||||
for _, entry := range accounts {
|
for _, entry := range accounts {
|
||||||
accountData[entry.Hash] = entry.Blob
|
if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
|
||||||
|
accountData[entry.Hash] = entry.Blob
|
||||||
|
} else {
|
||||||
|
accountData[entry.Hash] = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var storage []journalStorage
|
var storage []journalStorage
|
||||||
if err := r.Decode(&storage); err != nil {
|
if err := r.Decode(&storage); err != nil {
|
||||||
|
|
@ -168,7 +172,11 @@ func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
|
||||||
for _, entry := range storage {
|
for _, entry := range storage {
|
||||||
slots := make(map[common.Hash][]byte)
|
slots := make(map[common.Hash][]byte)
|
||||||
for i, key := range entry.Keys {
|
for i, key := range entry.Keys {
|
||||||
slots[key] = entry.Vals[i]
|
if len(entry.Vals[i]) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
|
||||||
|
slots[key] = entry.Vals[i]
|
||||||
|
} else {
|
||||||
|
slots[key] = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
storageData[entry.Hash] = slots
|
storageData[entry.Hash] = slots
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,9 @@ type snapshot interface {
|
||||||
|
|
||||||
// AccountIterator creates an account iterator over an arbitrary layer.
|
// AccountIterator creates an account iterator over an arbitrary layer.
|
||||||
AccountIterator(seek common.Hash) AccountIterator
|
AccountIterator(seek common.Hash) AccountIterator
|
||||||
|
|
||||||
|
// StorageIterator creates a storage iterator over an arbitrary layer.
|
||||||
|
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotTree is an Ethereum state snapshot tree. It consists of one persistent
|
// SnapshotTree is an Ethereum state snapshot tree. It consists of one persistent
|
||||||
|
|
@ -601,3 +604,9 @@ func (t *Tree) Rebuild(root common.Hash) {
|
||||||
func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) {
|
func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) {
|
||||||
return newFastAccountIterator(t, root, seek)
|
return newFastAccountIterator(t, root, seek)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorageIterator creates a new storage iterator for the specified root hash and
|
||||||
|
// account. The iterator will be move to the specific start position.
|
||||||
|
func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) {
|
||||||
|
return newFastStorageIterator(t, root, account, seek)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,29 @@ func randomAccountSet(hashes ...string) map[common.Hash][]byte {
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// randomStorageSet generates a set of random slots with the given strings as
|
||||||
|
// the slot addresses.
|
||||||
|
func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte {
|
||||||
|
storages := make(map[common.Hash]map[common.Hash][]byte)
|
||||||
|
for index, account := range accounts {
|
||||||
|
storages[common.HexToHash(account)] = make(map[common.Hash][]byte)
|
||||||
|
|
||||||
|
if index < len(hashes) {
|
||||||
|
hashes := hashes[index]
|
||||||
|
for _, hash := range hashes {
|
||||||
|
storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index < len(nilStorage) {
|
||||||
|
nils := nilStorage[index]
|
||||||
|
for _, hash := range nils {
|
||||||
|
storages[common.HexToHash(account)][common.HexToHash(hash)] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storages
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that if a disk layer becomes stale, no active external references will
|
// Tests that if a disk layer becomes stale, no active external references will
|
||||||
// be returned with junk data. This version of the test flattens every diff layer
|
// be returned with junk data. This version of the test flattens every diff layer
|
||||||
// to check internal corner case around the bottom-most memory accumulator.
|
// to check internal corner case around the bottom-most memory accumulator.
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ func wipeKeyRange(db ethdb.KeyValueStore, kind string, prefix []byte, keylen int
|
||||||
// Iterate over the key-range and delete all of them
|
// Iterate over the key-range and delete all of them
|
||||||
start, logged := time.Now(), time.Now()
|
start, logged := time.Now(), time.Now()
|
||||||
|
|
||||||
it := db.NewIteratorWithStart(prefix)
|
it := db.NewIterator(prefix, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
// Skip any keys with the correct prefix but wrong lenth (trie nodes)
|
// Skip any keys with the correct prefix but wrong lenth (trie nodes)
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
|
|
@ -113,7 +113,8 @@ func wipeKeyRange(db ethdb.KeyValueStore, kind string, prefix []byte, keylen int
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
batch.Reset()
|
batch.Reset()
|
||||||
it = db.NewIteratorWithStart(key)
|
seekPos := key[len(prefix):]
|
||||||
|
it = db.NewIterator(prefix, seekPos)
|
||||||
|
|
||||||
if time.Since(logged) > 8*time.Second {
|
if time.Since(logged) > 8*time.Second {
|
||||||
log.Info("Deleting state snapshot leftovers", "kind", kind, "wiped", items, "elapsed", common.PrettyDuration(time.Since(start)))
|
log.Info("Deleting state snapshot leftovers", "kind", kind, "wiped", items, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func TestWipe(t *testing.T) {
|
||||||
// Sanity check that all the keys are present
|
// Sanity check that all the keys are present
|
||||||
var items int
|
var items int
|
||||||
|
|
||||||
it := db.NewIteratorWithPrefix(rawdb.SnapshotAccountPrefix)
|
it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
@ -69,7 +69,7 @@ func TestWipe(t *testing.T) {
|
||||||
items++
|
items++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it = db.NewIteratorWithPrefix(rawdb.SnapshotStoragePrefix)
|
it = db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
@ -88,7 +88,7 @@ func TestWipe(t *testing.T) {
|
||||||
<-wipeSnapshot(db, true)
|
<-wipeSnapshot(db, true)
|
||||||
|
|
||||||
// Iterate over the database end ensure no snapshot information remains
|
// Iterate over the database end ensure no snapshot information remains
|
||||||
it = db.NewIteratorWithPrefix(rawdb.SnapshotAccountPrefix)
|
it = db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
@ -97,7 +97,7 @@ func TestWipe(t *testing.T) {
|
||||||
t.Errorf("snapshot entry remained after wipe: %x", key)
|
t.Errorf("snapshot entry remained after wipe: %x", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it = db.NewIteratorWithPrefix(rawdb.SnapshotStoragePrefix)
|
it = db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
@ -112,7 +112,7 @@ func TestWipe(t *testing.T) {
|
||||||
// Iterate over the database and ensure miscellaneous items are present
|
// Iterate over the database and ensure miscellaneous items are present
|
||||||
items = 0
|
items = 0
|
||||||
|
|
||||||
it = db.NewIterator()
|
it = db.NewIterator(nil, nil)
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -294,9 +295,12 @@ func (s *StateDB) GetCodeSize(addr common.Address) int {
|
||||||
if stateObject.code != nil {
|
if stateObject.code != nil {
|
||||||
return len(stateObject.code)
|
return len(stateObject.code)
|
||||||
}
|
}
|
||||||
|
if bytes.Equal(stateObject.CodeHash(), emptyCode[:]) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
size, err := s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
|
size, err := s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.setError(err)
|
s.setError(fmt.Errorf("GetCodeSize (%x) error: %v", addr[:], err))
|
||||||
}
|
}
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
@ -465,14 +469,16 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
|
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
|
||||||
}
|
}
|
||||||
s.setError(s.trie.TryUpdate(addr[:], data))
|
if err = s.trie.TryUpdate(addr[:], data); err != nil {
|
||||||
|
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
||||||
|
}
|
||||||
|
|
||||||
// If state snapshotting is active, cache the data til commit. Note, this
|
// If state snapshotting is active, cache the data til commit. Note, this
|
||||||
// update mechanism is not symmetric to the deletion, because whereas it is
|
// update mechanism is not symmetric to the deletion, because whereas it is
|
||||||
// enough to track account updates at commit time, deletions need tracking
|
// enough to track account updates at commit time, deletions need tracking
|
||||||
// at transaction boundary level to ensure we capture state clearing.
|
// at transaction boundary level to ensure we capture state clearing.
|
||||||
if s.snap != nil {
|
if s.snap != nil {
|
||||||
s.snapAccounts[obj.addrHash] = snapshot.AccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash)
|
s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,7 +490,9 @@ func (s *StateDB) deleteStateObject(obj *stateObject) {
|
||||||
}
|
}
|
||||||
// Delete the account from the trie
|
// Delete the account from the trie
|
||||||
addr := obj.Address()
|
addr := obj.Address()
|
||||||
s.setError(s.trie.TryDelete(addr[:]))
|
if err := s.trie.TryDelete(addr[:]); err != nil {
|
||||||
|
s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStateObject retrieves a state object given by the address, returning nil if
|
// getStateObject retrieves a state object given by the address, returning nil if
|
||||||
|
|
@ -536,8 +544,11 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
||||||
defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
|
defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
|
||||||
}
|
}
|
||||||
enc, err := s.trie.TryGet(addr[:])
|
enc, err := s.trie.TryGet(addr[:])
|
||||||
|
if err != nil {
|
||||||
|
s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr[:], err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if len(enc) == 0 {
|
if len(enc) == 0 {
|
||||||
s.setError(err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
||||||
|
|
@ -804,6 +815,9 @@ func (s *StateDB) clearJournalAndRefund() {
|
||||||
|
|
||||||
// Commit writes the state to the underlying in-memory trie database.
|
// Commit writes the state to the underlying in-memory trie database.
|
||||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||||
|
if s.dbErr != nil {
|
||||||
|
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||||
|
}
|
||||||
// Finalize any pending changes and merge everything into the tries
|
// Finalize any pending changes and merge everything into the tries
|
||||||
s.IntermediateRoot(deleteEmptyObjects)
|
s.IntermediateRoot(deleteEmptyObjects)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that no data was leaked into the database
|
// Ensure that no data was leaked into the database
|
||||||
it := db.NewIterator()
|
it := db.NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
|
t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
|
t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
it := finalDb.NewIterator()
|
it := finalDb.NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key, fvalue := it.Key(), it.Value()
|
key, fvalue := it.Key(), it.Value()
|
||||||
tvalue, err := transDb.Get(key)
|
tvalue, err := transDb.Get(key)
|
||||||
|
|
@ -131,7 +131,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
}
|
}
|
||||||
it.Release()
|
it.Release()
|
||||||
|
|
||||||
it = transDb.NewIterator()
|
it = transDb.NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key, tvalue := it.Key(), it.Value()
|
key, tvalue := it.Key(), it.Value()
|
||||||
fvalue, err := finalDb.Get(key)
|
fvalue, err := finalDb.Get(key)
|
||||||
|
|
@ -680,3 +680,50 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
t.Fatalf("self-destructed contract came alive")
|
t.Fatalf("self-destructed contract came alive")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMissingTrieNodes tests that if the statedb fails to load parts of the trie,
|
||||||
|
// the Commit operation fails with an error
|
||||||
|
// If we are missing trie nodes, we should not continue writing to the trie
|
||||||
|
func TestMissingTrieNodes(t *testing.T) {
|
||||||
|
|
||||||
|
// Create an initial state with a few accounts
|
||||||
|
memDb := rawdb.NewMemoryDatabase()
|
||||||
|
db := NewDatabase(memDb)
|
||||||
|
var root common.Hash
|
||||||
|
state, _ := New(common.Hash{}, db, nil)
|
||||||
|
addr := toAddr([]byte("so"))
|
||||||
|
{
|
||||||
|
state.SetBalance(addr, big.NewInt(1))
|
||||||
|
state.SetCode(addr, []byte{1, 2, 3})
|
||||||
|
a2 := toAddr([]byte("another"))
|
||||||
|
state.SetBalance(a2, big.NewInt(100))
|
||||||
|
state.SetCode(a2, []byte{1, 2, 4})
|
||||||
|
root, _ = state.Commit(false)
|
||||||
|
t.Logf("root: %x", root)
|
||||||
|
// force-flush
|
||||||
|
state.Database().TrieDB().Cap(0)
|
||||||
|
}
|
||||||
|
// Create a new state on the old root
|
||||||
|
state, _ = New(root, db, nil)
|
||||||
|
// Now we clear out the memdb
|
||||||
|
it := memDb.NewIterator(nil, nil)
|
||||||
|
for it.Next() {
|
||||||
|
k := it.Key()
|
||||||
|
// Leave the root intact
|
||||||
|
if !bytes.Equal(k, root[:]) {
|
||||||
|
t.Logf("key: %x", k)
|
||||||
|
memDb.Delete(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
balance := state.GetBalance(addr)
|
||||||
|
// The removed elem should lead to it returning zero balance
|
||||||
|
if exp, got := uint64(0), balance.Uint64(); got != exp {
|
||||||
|
t.Errorf("expected %d, got %d", exp, got)
|
||||||
|
}
|
||||||
|
// Modify the state
|
||||||
|
state.SetBalance(addr, big.NewInt(2))
|
||||||
|
root, err := state.Commit(false)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error, got root :%x", root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,6 @@ func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *co
|
||||||
context := NewEVMContext(msg, header, bc, author)
|
context := NewEVMContext(msg, header, bc, author)
|
||||||
vm := vm.NewEVM(context, statedb, config, cfg)
|
vm := vm.NewEVM(context, statedb, config, cfg)
|
||||||
|
|
||||||
_, _, _, err = ApplyMessage(vm, msg, gaspool)
|
_, err = ApplyMessage(vm, msg, gaspool)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
|
result, err := ApplyMessage(vmenv, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -107,13 +107,13 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||||
}
|
}
|
||||||
*usedGas += gas
|
*usedGas += result.UsedGas
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
||||||
receipt := types.NewReceipt(root, failed, *usedGas)
|
receipt := types.NewReceipt(root, result.Failed(), *usedGas)
|
||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = gas
|
receipt.GasUsed = result.UsedGas
|
||||||
// if the transaction created a contract, store the creation address in the receipt.
|
// if the transaction created a contract, store the creation address in the receipt.
|
||||||
if msg.To() == nil {
|
if msg.To() == nil {
|
||||||
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,14 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The State Transitioning Model
|
The State Transitioning Model
|
||||||
|
|
||||||
|
|
@ -63,7 +57,6 @@ type StateTransition struct {
|
||||||
// Message represents a message sent to a contract.
|
// Message represents a message sent to a contract.
|
||||||
type Message interface {
|
type Message interface {
|
||||||
From() common.Address
|
From() common.Address
|
||||||
//FromFrontier() (common.Address, error)
|
|
||||||
To() *common.Address
|
To() *common.Address
|
||||||
|
|
||||||
GasPrice() *big.Int
|
GasPrice() *big.Int
|
||||||
|
|
@ -75,6 +68,41 @@ type Message interface {
|
||||||
Data() []byte
|
Data() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionResult includes all output after executing given evm
|
||||||
|
// message no matter the execution itself is successful or not.
|
||||||
|
type ExecutionResult struct {
|
||||||
|
UsedGas uint64 // Total used gas but include the refunded gas
|
||||||
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the internal evm error which allows us for further
|
||||||
|
// analysis outside.
|
||||||
|
func (result *ExecutionResult) Unwrap() error {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed returns the indicator whether the execution is successful or not
|
||||||
|
func (result *ExecutionResult) Failed() bool { return result.Err != nil }
|
||||||
|
|
||||||
|
// Return is a helper function to help caller distinguish between revert reason
|
||||||
|
// and function return. Return returns the data after execution if no error occurs.
|
||||||
|
func (result *ExecutionResult) Return() []byte {
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert returns the concrete revert reason if the execution is aborted by `REVERT`
|
||||||
|
// opcode. Note the reason can be nil if no data supplied with revert opcode.
|
||||||
|
func (result *ExecutionResult) Revert() []byte {
|
||||||
|
if result.Err != vm.ErrExecutionReverted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
|
|
@ -99,13 +127,13 @@ func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 boo
|
||||||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||||
}
|
}
|
||||||
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
||||||
return 0, vm.ErrOutOfGas
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += nz * nonZeroGas
|
gas += nz * nonZeroGas
|
||||||
|
|
||||||
z := uint64(len(data)) - nz
|
z := uint64(len(data)) - nz
|
||||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||||
return 0, vm.ErrOutOfGas
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += z * params.TxDataZeroGas
|
gas += z * params.TxDataZeroGas
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +160,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||||
// indicates a core error meaning that the message would always fail for that particular
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// state and would never be accepted within a block.
|
||||||
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
|
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) {
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb()
|
return NewStateTransition(evm, msg, gp).TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,19 +172,10 @@ func (st *StateTransition) to() common.Address {
|
||||||
return *st.msg.To()
|
return *st.msg.To()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) useGas(amount uint64) error {
|
|
||||||
if st.gas < amount {
|
|
||||||
return vm.ErrOutOfGas
|
|
||||||
}
|
|
||||||
st.gas -= amount
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *StateTransition) buyGas() error {
|
func (st *StateTransition) buyGas() error {
|
||||||
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
||||||
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
|
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
|
||||||
return errInsufficientBalanceForGas
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -182,11 +201,32 @@ func (st *StateTransition) preCheck() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransitionDb will transition the state by applying the current message and
|
// TransitionDb will transition the state by applying the current message and
|
||||||
// returning the result including the used gas. It returns an error if failed.
|
// returning the evm execution result with following fields.
|
||||||
// An error indicates a consensus issue.
|
//
|
||||||
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
|
// - used gas:
|
||||||
if err = st.preCheck(); err != nil {
|
// total gas used (including gas being refunded)
|
||||||
return
|
// - returndata:
|
||||||
|
// the returned data from evm
|
||||||
|
// - concrete execution error:
|
||||||
|
// various **EVM** error which aborts the execution,
|
||||||
|
// e.g. ErrOutOfGas, ErrExecutionReverted
|
||||||
|
//
|
||||||
|
// However if any consensus issue encountered, return the error directly with
|
||||||
|
// nil evm execution result.
|
||||||
|
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
|
// First check this message satisfies all consensus rules before
|
||||||
|
// applying the message. The rules include these clauses
|
||||||
|
//
|
||||||
|
// 1. the nonce of the message caller is correct
|
||||||
|
// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
|
||||||
|
// 3. the amount of gas required is available in the block
|
||||||
|
// 4. the purchased gas is enough to cover intrinsic usage
|
||||||
|
// 5. there is no overflow when calculating intrinsic gas
|
||||||
|
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
||||||
|
|
||||||
|
// Check clauses 1-3, buy gas if everything is correct
|
||||||
|
if err := st.preCheck(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
msg := st.msg
|
msg := st.msg
|
||||||
sender := vm.AccountRef(msg.From())
|
sender := vm.AccountRef(msg.From())
|
||||||
|
|
@ -194,42 +234,39 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
|
||||||
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber)
|
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber)
|
||||||
contractCreation := msg.To() == nil
|
contractCreation := msg.To() == nil
|
||||||
|
|
||||||
// Pay intrinsic gas
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
|
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = st.useGas(gas); err != nil {
|
if st.gas < gas {
|
||||||
return nil, 0, false, err
|
return nil, ErrIntrinsicGas
|
||||||
}
|
}
|
||||||
|
st.gas -= gas
|
||||||
|
|
||||||
|
// Check clause 6
|
||||||
|
if msg.Value().Sign() > 0 && !st.evm.CanTransfer(st.state, msg.From(), msg.Value()) {
|
||||||
|
return nil, ErrInsufficientFundsForTransfer
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
evm = st.evm
|
ret []byte
|
||||||
// vm errors do not effect consensus and are therefor
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
// not assigned to err, except for insufficient balance
|
|
||||||
// error.
|
|
||||||
vmerr error
|
|
||||||
)
|
)
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
|
ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
||||||
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
||||||
}
|
|
||||||
if vmerr != nil {
|
|
||||||
log.Debug("VM returned with error", "err", vmerr)
|
|
||||||
// The only possible consensus-error would be if there wasn't
|
|
||||||
// sufficient balance to make the transfer happen. The first
|
|
||||||
// balance transfer may never fail.
|
|
||||||
if vmerr == vm.ErrInsufficientBalance {
|
|
||||||
return nil, 0, false, vmerr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
st.refundGas()
|
st.refundGas()
|
||||||
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||||
|
|
||||||
return ret, st.gasUsed(), vmerr != nil, err
|
return &ExecutionResult{
|
||||||
|
UsedGas: st.gasUsed(),
|
||||||
|
Err: vmerr,
|
||||||
|
ReturnData: ret,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas() {
|
func (st *StateTransition) refundGas() {
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,6 @@ var (
|
||||||
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||||
ErrInvalidSender = errors.New("invalid sender")
|
ErrInvalidSender = errors.New("invalid sender")
|
||||||
|
|
||||||
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
|
||||||
// one present in the local chain.
|
|
||||||
ErrNonceTooLow = errors.New("nonce too low")
|
|
||||||
|
|
||||||
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||||
// configured for the transaction pool.
|
// configured for the transaction pool.
|
||||||
ErrUnderpriced = errors.New("transaction underpriced")
|
ErrUnderpriced = errors.New("transaction underpriced")
|
||||||
|
|
@ -71,14 +67,6 @@ var (
|
||||||
// with a different one without the required price bump.
|
// with a different one without the required price bump.
|
||||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
||||||
|
|
||||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
|
||||||
// is higher than the balance of the user's account.
|
|
||||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
|
||||||
|
|
||||||
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
|
||||||
// than required to start the invocation.
|
|
||||||
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
|
||||||
|
|
||||||
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||||
// maximum allowance of the current block.
|
// maximum allowance of the current block.
|
||||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||||
last = nonce
|
last = nonce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nonce := pool.Nonce(addr); nonce != last+1 {
|
if nonce := pool.pendingNonces.get(addr); nonce != last+1 {
|
||||||
return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
|
return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -857,8 +857,12 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||||
//
|
//
|
||||||
// This logic should not hold for local transactions, unless the local tracking
|
// This logic should not hold for local transactions, unless the local tracking
|
||||||
// mechanism is disabled.
|
// mechanism is disabled.
|
||||||
func TestTransactionQueueTimeLimiting(t *testing.T) { testTransactionQueueTimeLimiting(t, false) }
|
func TestTransactionQueueTimeLimiting(t *testing.T) {
|
||||||
func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) { testTransactionQueueTimeLimiting(t, true) }
|
testTransactionQueueTimeLimiting(t, false)
|
||||||
|
}
|
||||||
|
func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) {
|
||||||
|
testTransactionQueueTimeLimiting(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||||
// Reduce the eviction interval to a testable amount
|
// Reduce the eviction interval to a testable amount
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue