mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge pull request #1490 from maticnetwork/v2.0.1-beta-candidate
Release v2.0.1
This commit is contained in:
commit
49239f0d4f
129 changed files with 1652 additions and 8978 deletions
|
|
@ -64,10 +64,6 @@ issues:
|
||||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
||||||
- path: core/vm/contracts.go
|
- path: core/vm/contracts.go
|
||||||
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.'
|
||||||
- path: accounts/usbwallet/trezor.go
|
|
||||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
|
||||||
- path: accounts/usbwallet/trezor/
|
|
||||||
text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.'
|
|
||||||
exclude:
|
exclude:
|
||||||
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
- 'SA1019: event.TypeMux is deprecated: use Feed'
|
||||||
- 'SA1019: strings.Title is deprecated'
|
- 'SA1019: strings.Title is deprecated'
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -35,9 +35,8 @@ protoc:
|
||||||
|
|
||||||
generate-mocks:
|
generate-mocks:
|
||||||
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
||||||
go generate mockgen -destination=./eth/filters/IDatabase.go -package=filters ./ethdb Database
|
|
||||||
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
||||||
go generate mockgen -destination=../eth/filters/IDatabase.go -package=filters ./ethdb Database
|
go generate mockgen -destination=./eth/filters/IDatabase.go -package=filters ./ethdb Database
|
||||||
|
|
||||||
#? geth: Build geth.
|
#? geth: Build geth.
|
||||||
geth:
|
geth:
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -11,14 +11,14 @@ https://pkg.go.dev/badge/github.com/maticnetwork/bor
|
||||||
|
|
||||||
### Installing bor using packaging
|
### Installing bor using packaging
|
||||||
|
|
||||||
The easiest way to get started with bor is to install the packages using the command below. Refer to the [releases](https://github.com/maticnetwork/bor/releases) section to find the latest stable version of bor.
|
The easiest way to get started with bor is to install the packages using the command below. Please take a look at the [releases](https://github.com/maticnetwork/bor/releases) section to find the latest stable version of bor.
|
||||||
|
|
||||||
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/bor.sh | bash -s -- v0.4.0 <network> <node_type>
|
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/bor.sh | bash -s -- v0.4.0 <network> <node_type>
|
||||||
|
|
||||||
The network accepts `mainnet`,`amoy` or `mumbai` and the node type accepts `validator` or `sentry` or `archive`. The installation script does the following things:
|
The network accepts `mainnet`,`amoy` or `mumbai` and the node type accepts `validator` or `sentry` or `archive`. The installation script does the following things:
|
||||||
- Create a new user named `bor`.
|
- Create a new user named `bor`.
|
||||||
- Install the bor binary at `/usr/bin/bor`.
|
- Install the bor binary at `/usr/bin/bor`.
|
||||||
- Dump the suitable config file (based on the network and node type provided) at `/var/lib/bor` and uses it as the home dir.
|
- Dump the suitable config file (based on the network and node type provided) at `/var/lib/bor` and use it as the home dir.
|
||||||
- Create a systemd service named `bor` at `/lib/systemd/system/bor.service` which starts bor using the config file as `bor` user.
|
- Create a systemd service named `bor` at `/lib/systemd/system/bor.service` which starts bor using the config file as `bor` user.
|
||||||
|
|
||||||
The releases supports both the networks i.e. Polygon Mainnet, Amoy and Mumbai (Testnet) unless explicitly specified. Before the stable release for mainnet, pre-releases will be available marked with `beta` tag for deploying on Mumbai/Amoy (testnet). On sufficient testing, stable release for mainnet will be announced with a forum post.
|
The releases supports both the networks i.e. Polygon Mainnet, Amoy and Mumbai (Testnet) unless explicitly specified. Before the stable release for mainnet, pre-releases will be available marked with `beta` tag for deploying on Mumbai/Amoy (testnet). On sufficient testing, stable release for mainnet will be announced with a forum post.
|
||||||
|
|
@ -30,7 +30,7 @@ The releases supports both the networks i.e. Polygon Mainnet, Amoy and Mumbai (T
|
||||||
```shell
|
```shell
|
||||||
make bor
|
make bor
|
||||||
```
|
```
|
||||||
- Start bor using the ideal config files for validator and sentry provided in the `packaging` folder.
|
- Start bor using the ideal config files for the validator and sentry provided in the `packaging` folder.
|
||||||
```shell
|
```shell
|
||||||
./build/bin/bor server --config ./packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
|
./build/bin/bor server --config ./packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
|
||||||
```
|
```
|
||||||
|
|
@ -45,19 +45,19 @@ The releases supports both the networks i.e. Polygon Mainnet, Amoy and Mumbai (T
|
||||||
|
|
||||||
#### Using the new cli
|
#### Using the new cli
|
||||||
|
|
||||||
Post `v0.3.0` release, bor uses a new command line interface (cli). The new-cli (located at `internal/cli`) has been built with keeping the flag usage similar to old-cli (located at `cmd/geth`) with a few notable changes. Please refer to [docs](./docs) section for flag usage guide and example.
|
Post `v0.3.0` release, bor uses a new command line interface (cli). The new-cli (located at `internal/cli`) has been built while keeping the flag usage similar to old-cli (located at `cmd/geth`) with a few notable changes. Please refer to [docs](./docs) section for the flag usage guide and example.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- The official documentation for the Polygon PoS chain can be found [here](https://wiki.polygon.technology/docs/pos/getting-started/). It contains all the conceptual and architectural details of the chain along with operational guide for users running the nodes.
|
- The official documentation for the Polygon PoS chain can be found [here](https://wiki.polygon.technology/docs/pos/getting-started/). It contains all the conceptual and architectural details of the chain along with an operational guide for users running the nodes.
|
||||||
- New release announcements and discussions can be found on our [forum page](https://forum.polygon.technology/).
|
- New release announcements and discussions can be found on our [forum page](https://forum.polygon.technology/).
|
||||||
- Polygon improvement proposals can be found [here](https://github.com/maticnetwork/Polygon-Improvement-Proposals/)
|
- Polygon improvement proposals can be found [here](https://github.com/maticnetwork/Polygon-Improvement-Proposals/)
|
||||||
|
|
||||||
### Contribution guidelines
|
### Contribution guidelines
|
||||||
|
|
||||||
Thank you for considering helping out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! If you'd like to contribute to bor, please fork, fix, commit and send a pull request for the maintainers to review and merge into the main code base.
|
Thank you for considering helping out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! If you'd like to contribute to bor, please fork, fix, commit, and send a pull request for the maintainers to review and merge into the main code base.
|
||||||
|
|
||||||
From the outset we defined some guidelines to ensure new contributions only ever enhance the project:
|
From the outset, we defined some guidelines to ensure new contributions only ever enhance the project:
|
||||||
|
|
||||||
* Quality: Code in the Polygon project should meet the style guidelines, with sufficient test-cases, descriptive commit messages, evidence that the contribution does not break any compatibility commitments or cause adverse feature interactions, and evidence of high-quality peer-review. Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
* Quality: Code in the Polygon project should meet the style guidelines, with sufficient test-cases, descriptive commit messages, evidence that the contribution does not break any compatibility commitments or cause adverse feature interactions, and evidence of high-quality peer-review. Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||||
* Testing: Please ensure that the updated code passes all the tests locally before submitting a pull request. In order to run unit tests, run `make test` and to run integration tests, run `make test-integration`.
|
* Testing: Please ensure that the updated code passes all the tests locally before submitting a pull request. In order to run unit tests, run `make test` and to run integration tests, run `make test-integration`.
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ type TransactOpts struct {
|
||||||
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||||
|
|
||||||
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
||||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||||
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||||
|
AccessList types.AccessList // Access list to set for the transaction execution (nil = no access list)
|
||||||
|
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
|
|
||||||
|
|
@ -320,21 +321,22 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
}
|
}
|
||||||
|
|
||||||
baseTx := &types.DynamicFeeTx{
|
baseTx := &types.DynamicFeeTx{
|
||||||
To: contract,
|
To: contract,
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
GasFeeCap: gasFeeCap,
|
GasFeeCap: gasFeeCap,
|
||||||
GasTipCap: gasTipCap,
|
GasTipCap: gasTipCap,
|
||||||
Gas: gasLimit,
|
Gas: gasLimit,
|
||||||
Value: value,
|
Value: value,
|
||||||
Data: input,
|
Data: input,
|
||||||
|
AccessList: opts.AccessList,
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewTx(baseTx), nil
|
return types.NewTx(baseTx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
if opts.GasFeeCap != nil || opts.GasTipCap != nil || opts.AccessList != nil {
|
||||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas or accessList specified but london is not active yet")
|
||||||
}
|
}
|
||||||
// Normalize value
|
// Normalize value
|
||||||
value := opts.Value
|
value := opts.Value
|
||||||
|
|
|
||||||
487
accounts/abi/bind/source.go.tpl
Normal file
487
accounts/abi/bind/source.go.tpl
Normal file
|
|
@ -0,0 +1,487 @@
|
||||||
|
// Code generated - DO NOT EDIT.
|
||||||
|
// This file is a generated binding and any manual changes will be lost.
|
||||||
|
|
||||||
|
package {{.Package}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
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/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var (
|
||||||
|
_ = errors.New
|
||||||
|
_ = big.NewInt
|
||||||
|
_ = strings.NewReader
|
||||||
|
_ = ethereum.NotFound
|
||||||
|
_ = bind.Bind
|
||||||
|
_ = common.Big1
|
||||||
|
_ = types.BloomLookup
|
||||||
|
_ = event.NewSubscription
|
||||||
|
_ = abi.ConvertType
|
||||||
|
)
|
||||||
|
|
||||||
|
{{$structs := .Structs}}
|
||||||
|
{{range $structs}}
|
||||||
|
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
||||||
|
type {{.Name}} struct {
|
||||||
|
{{range $field := .Fields}}
|
||||||
|
{{$field.Name}} {{$field.Type}}{{end}}
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $contract := .Contracts}}
|
||||||
|
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
|
||||||
|
var {{.Type}}MetaData = &bind.MetaData{
|
||||||
|
ABI: "{{.InputABI}}",
|
||||||
|
{{if $contract.FuncSigs -}}
|
||||||
|
Sigs: map[string]string{
|
||||||
|
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
||||||
|
{{end}}
|
||||||
|
},
|
||||||
|
{{end -}}
|
||||||
|
{{if .InputBin -}}
|
||||||
|
Bin: "0x{{.InputBin}}",
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||||
|
// Deprecated: Use {{.Type}}MetaData.ABI instead.
|
||||||
|
var {{.Type}}ABI = {{.Type}}MetaData.ABI
|
||||||
|
|
||||||
|
{{if $contract.FuncSigs}}
|
||||||
|
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
|
||||||
|
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
||||||
|
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .InputBin}}
|
||||||
|
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
||||||
|
// Deprecated: Use {{.Type}}MetaData.Bin instead.
|
||||||
|
var {{.Type}}Bin = {{.Type}}MetaData.Bin
|
||||||
|
|
||||||
|
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||||
|
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
||||||
|
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
if parsed == nil {
|
||||||
|
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
|
||||||
|
}
|
||||||
|
{{range $pattern, $name := .Libraries}}
|
||||||
|
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||||
|
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
|
||||||
|
{{end}}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
// {{.Type}} is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}} struct {
|
||||||
|
{{.Type}}Caller // Read-only binding to the contract
|
||||||
|
{{.Type}}Transactor // Write-only binding to the contract
|
||||||
|
{{.Type}}Filterer // Log filterer for contract events
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}}Caller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}}Transactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||||
|
type {{.Type}}Filterer struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type {{.Type}}Session struct {
|
||||||
|
Contract *{{.Type}} // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type {{.Type}}CallerSession struct {
|
||||||
|
Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type {{.Type}}TransactorSession struct {
|
||||||
|
Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}}Raw struct {
|
||||||
|
Contract *{{.Type}} // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}}CallerRaw struct {
|
||||||
|
Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type {{.Type}}TransactorRaw struct {
|
||||||
|
Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
||||||
|
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
|
||||||
|
contract, err := bind{{.Type}}(address, backend, backend, backend)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||||
|
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
|
||||||
|
contract, err := bind{{.Type}}(address, caller, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &{{.Type}}Caller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
|
||||||
|
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
|
||||||
|
contract, err := bind{{.Type}}(address, nil, transactor, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &{{.Type}}Transactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
|
||||||
|
func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
|
||||||
|
contract, err := bind{{.Type}}(address, nil, nil, filterer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &{{.Type}}Filterer{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
||||||
|
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||||
|
return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||||
|
return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{range .Calls}}
|
||||||
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
{{if .Structured}}
|
||||||
|
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
|
||||||
|
if err != nil {
|
||||||
|
return *outstruct, err
|
||||||
|
}
|
||||||
|
{{range $i, $t := .Normalized.Outputs}}
|
||||||
|
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
||||||
|
|
||||||
|
return *outstruct, err
|
||||||
|
{{else}}
|
||||||
|
if err != nil {
|
||||||
|
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
|
||||||
|
}
|
||||||
|
{{range $i, $t := .Normalized.Outputs}}
|
||||||
|
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
||||||
|
|
||||||
|
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range .Transacts}}
|
||||||
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Fallback}}
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Receive}}
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range .Events}}
|
||||||
|
// {{$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 {
|
||||||
|
Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
|
||||||
|
|
||||||
|
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||||
|
event string // Event name to use for unpacking event data
|
||||||
|
|
||||||
|
logs chan types.Log // Log channel receiving the found contract events
|
||||||
|
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||||
|
done bool // Whether the subscription completed delivering logs
|
||||||
|
fail error // Occurred error to stop iteration
|
||||||
|
}
|
||||||
|
// Next advances the iterator to the subsequent event, returning whether there
|
||||||
|
// are any more events found. In case of a retrieval or parsing error, false is
|
||||||
|
// returned and Error() can be queried for the exact failure.
|
||||||
|
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
|
||||||
|
// If the iterator failed, stop iterating
|
||||||
|
if (it.fail != nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If the iterator completed, deliver directly whatever's available
|
||||||
|
if (it.done) {
|
||||||
|
select {
|
||||||
|
case log := <-it.logs:
|
||||||
|
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||||
|
it.fail = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.Event.Raw = log
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Iterator still in progress, wait for either a data or an error event
|
||||||
|
select {
|
||||||
|
case log := <-it.logs:
|
||||||
|
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||||
|
it.fail = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.Event.Raw = log
|
||||||
|
return true
|
||||||
|
|
||||||
|
case err := <-it.sub.Err():
|
||||||
|
it.done = true
|
||||||
|
it.fail = err
|
||||||
|
return it.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Error returns any retrieval or parsing error occurred during filtering.
|
||||||
|
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
|
||||||
|
return it.fail
|
||||||
|
}
|
||||||
|
// Close terminates the iteration process, releasing any pending underlying
|
||||||
|
// resources.
|
||||||
|
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
|
||||||
|
it.sub.Unsubscribe()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
|
||||||
|
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
|
||||||
|
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
|
||||||
|
Raw types.Log // Blockchain specific contextual infos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
||||||
|
{{range .Normalized.Inputs}}
|
||||||
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
|
for _, {{.Name}}Item := range {{.Name}} {
|
||||||
|
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
||||||
|
}{{end}}{{end}}
|
||||||
|
|
||||||
|
logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
|
||||||
|
{{range .Normalized.Inputs}}
|
||||||
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
|
for _, {{.Name}}Item := range {{.Name}} {
|
||||||
|
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
||||||
|
}{{end}}{{end}}
|
||||||
|
|
||||||
|
logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case log := <-logs:
|
||||||
|
// New log arrived, parse the event and forward to the user
|
||||||
|
event := new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
event.Raw = log
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sink <- event:
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||||
|
event := new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
event.Raw = log
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package bind
|
package bind
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/accounts/abi"
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
)
|
||||||
|
|
||||||
// tmplData is the data structure required to fill the binding template.
|
// tmplData is the data structure required to fill the binding template.
|
||||||
type tmplData struct {
|
type tmplData struct {
|
||||||
|
|
@ -80,492 +84,6 @@ var tmplSource = map[Lang]string{
|
||||||
|
|
||||||
// tmplSourceGo is the Go source template that the generated Go contract binding
|
// tmplSourceGo is the Go source template that the generated Go contract binding
|
||||||
// is based on.
|
// is based on.
|
||||||
const tmplSourceGo = `
|
//
|
||||||
// Code generated - DO NOT EDIT.
|
//go:embed source.go.tpl
|
||||||
// This file is a generated binding and any manual changes will be lost.
|
var tmplSourceGo string
|
||||||
|
|
||||||
package {{.Package}}
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
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/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var (
|
|
||||||
_ = errors.New
|
|
||||||
_ = big.NewInt
|
|
||||||
_ = strings.NewReader
|
|
||||||
_ = ethereum.NotFound
|
|
||||||
_ = bind.Bind
|
|
||||||
_ = common.Big1
|
|
||||||
_ = types.BloomLookup
|
|
||||||
_ = event.NewSubscription
|
|
||||||
_ = abi.ConvertType
|
|
||||||
)
|
|
||||||
|
|
||||||
{{$structs := .Structs}}
|
|
||||||
{{range $structs}}
|
|
||||||
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type {{.Name}} struct {
|
|
||||||
{{range $field := .Fields}}
|
|
||||||
{{$field.Name}} {{$field.Type}}{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
|
||||||
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
|
|
||||||
var {{.Type}}MetaData = &bind.MetaData{
|
|
||||||
ABI: "{{.InputABI}}",
|
|
||||||
{{if $contract.FuncSigs -}}
|
|
||||||
Sigs: map[string]string{
|
|
||||||
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
|
||||||
{{end}}
|
|
||||||
},
|
|
||||||
{{end -}}
|
|
||||||
{{if .InputBin -}}
|
|
||||||
Bin: "0x{{.InputBin}}",
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
|
||||||
// Deprecated: Use {{.Type}}MetaData.ABI instead.
|
|
||||||
var {{.Type}}ABI = {{.Type}}MetaData.ABI
|
|
||||||
|
|
||||||
{{if $contract.FuncSigs}}
|
|
||||||
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
|
|
||||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
|
||||||
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .InputBin}}
|
|
||||||
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
|
||||||
// Deprecated: Use {{.Type}}MetaData.Bin instead.
|
|
||||||
var {{.Type}}Bin = {{.Type}}MetaData.Bin
|
|
||||||
|
|
||||||
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
|
||||||
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
|
||||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, nil, nil, err
|
|
||||||
}
|
|
||||||
if parsed == nil {
|
|
||||||
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
|
|
||||||
}
|
|
||||||
{{range $pattern, $name := .Libraries}}
|
|
||||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
|
||||||
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
|
|
||||||
{{end}}
|
|
||||||
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, nil, nil, err
|
|
||||||
}
|
|
||||||
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
// {{.Type}} is an auto generated Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}} struct {
|
|
||||||
{{.Type}}Caller // Read-only binding to the contract
|
|
||||||
{{.Type}}Transactor // Write-only binding to the contract
|
|
||||||
{{.Type}}Filterer // Log filterer for contract events
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}}Caller struct {
|
|
||||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}}Transactor struct {
|
|
||||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
|
||||||
type {{.Type}}Filterer struct {
|
|
||||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
|
|
||||||
// with pre-set call and transact options.
|
|
||||||
type {{.Type}}Session struct {
|
|
||||||
Contract *{{.Type}} // Generic contract binding to set the session for
|
|
||||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
|
||||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
|
||||||
// with pre-set call options.
|
|
||||||
type {{.Type}}CallerSession struct {
|
|
||||||
Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
|
|
||||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
|
||||||
// with pre-set transact options.
|
|
||||||
type {{.Type}}TransactorSession struct {
|
|
||||||
Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
|
|
||||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}}Raw struct {
|
|
||||||
Contract *{{.Type}} // Generic contract binding to access the raw methods on
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}}CallerRaw struct {
|
|
||||||
Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
|
||||||
type {{.Type}}TransactorRaw struct {
|
|
||||||
Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
|
|
||||||
}
|
|
||||||
|
|
||||||
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
|
||||||
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
|
|
||||||
contract, err := bind{{.Type}}(address, backend, backend, backend)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
|
|
||||||
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
|
|
||||||
contract, err := bind{{.Type}}(address, caller, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &{{.Type}}Caller{contract: contract}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
|
|
||||||
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
|
|
||||||
contract, err := bind{{.Type}}(address, nil, transactor, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &{{.Type}}Transactor{contract: contract}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
|
|
||||||
func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
|
|
||||||
contract, err := bind{{.Type}}(address, nil, nil, filterer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &{{.Type}}Filterer{contract: contract}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
|
||||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
|
||||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call invokes the (constant) contract method with params as input values and
|
|
||||||
// sets the output to result. The result type might be a single field for simple
|
|
||||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
|
||||||
// returns.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
|
||||||
return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
|
||||||
// its default method if one is available.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transact invokes the (paid) contract method with params as input values.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call invokes the (constant) contract method with params as input values and
|
|
||||||
// sets the output to result. The result type might be a single field for simple
|
|
||||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
|
||||||
// returns.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
|
||||||
return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
|
||||||
// its default method if one is available.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.contract.Transfer(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transact invokes the (paid) contract method with params as input values.
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
{{range .Calls}}
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
|
|
||||||
var out []interface{}
|
|
||||||
err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
{{if .Structured}}
|
|
||||||
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
|
|
||||||
if err != nil {
|
|
||||||
return *outstruct, err
|
|
||||||
}
|
|
||||||
{{range $i, $t := .Normalized.Outputs}}
|
|
||||||
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
|
||||||
|
|
||||||
return *outstruct, err
|
|
||||||
{{else}}
|
|
||||||
if err != nil {
|
|
||||||
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
|
|
||||||
}
|
|
||||||
{{range $i, $t := .Normalized.Outputs}}
|
|
||||||
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
|
||||||
|
|
||||||
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Transacts}}
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Fallback}}
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Receive}}
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
|
|
||||||
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Events}}
|
|
||||||
// {{$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 {
|
|
||||||
Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
|
|
||||||
|
|
||||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
|
||||||
event string // Event name to use for unpacking event data
|
|
||||||
|
|
||||||
logs chan types.Log // Log channel receiving the found contract events
|
|
||||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
|
||||||
done bool // Whether the subscription completed delivering logs
|
|
||||||
fail error // Occurred error to stop iteration
|
|
||||||
}
|
|
||||||
// Next advances the iterator to the subsequent event, returning whether there
|
|
||||||
// are any more events found. In case of a retrieval or parsing error, false is
|
|
||||||
// returned and Error() can be queried for the exact failure.
|
|
||||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
|
|
||||||
// If the iterator failed, stop iterating
|
|
||||||
if (it.fail != nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// If the iterator completed, deliver directly whatever's available
|
|
||||||
if (it.done) {
|
|
||||||
select {
|
|
||||||
case log := <-it.logs:
|
|
||||||
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
|
||||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
|
||||||
it.fail = err
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
it.Event.Raw = log
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Iterator still in progress, wait for either a data or an error event
|
|
||||||
select {
|
|
||||||
case log := <-it.logs:
|
|
||||||
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
|
|
||||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
|
||||||
it.fail = err
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
it.Event.Raw = log
|
|
||||||
return true
|
|
||||||
|
|
||||||
case err := <-it.sub.Err():
|
|
||||||
it.done = true
|
|
||||||
it.fail = err
|
|
||||||
return it.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Error returns any retrieval or parsing error occurred during filtering.
|
|
||||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
|
|
||||||
return it.fail
|
|
||||||
}
|
|
||||||
// Close terminates the iteration process, releasing any pending underlying
|
|
||||||
// resources.
|
|
||||||
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
|
|
||||||
it.sub.Unsubscribe()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
|
|
||||||
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
|
|
||||||
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
|
|
||||||
Raw types.Log // Blockchain specific contextual infos
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
|
||||||
{{range .Normalized.Inputs}}
|
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
|
||||||
for _, {{.Name}}Item := range {{.Name}} {
|
|
||||||
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
|
||||||
}{{end}}{{end}}
|
|
||||||
|
|
||||||
logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
|
|
||||||
{{range .Normalized.Inputs}}
|
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
|
||||||
for _, {{.Name}}Item := range {{.Name}} {
|
|
||||||
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
|
|
||||||
}{{end}}{{end}}
|
|
||||||
|
|
||||||
logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case log := <-logs:
|
|
||||||
// New log arrived, parse the event and forward to the user
|
|
||||||
event := new({{$contract.Type}}{{.Normalized.Name}})
|
|
||||||
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
event.Raw = log
|
|
||||||
|
|
||||||
select {
|
|
||||||
case sink <- event:
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
|
||||||
event := new({{$contract.Type}}{{.Normalized.Name}})
|
|
||||||
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
event.Raw = log
|
|
||||||
return event, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
`
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ func TestWatchNewFile(t *testing.T) {
|
||||||
func TestWatchNoDir(t *testing.T) {
|
func TestWatchNoDir(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Create ks but not the directory that it watches.
|
// Create ks but not the directory that it watches.
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
dir := filepath.Join(t.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
list := ks.Accounts()
|
list := ks.Accounts()
|
||||||
|
|
@ -131,8 +131,6 @@ func TestWatchNoDir(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Create the directory and copy a key file into it.
|
// Create the directory and copy a key file into it.
|
||||||
os.MkdirAll(dir, 0700)
|
os.MkdirAll(dir, 0700)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
file := filepath.Join(dir, "aaa")
|
file := filepath.Join(dir, "aaa")
|
||||||
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/golang/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
// - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases
|
// - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases
|
||||||
// - Build with the usual `./configure && make` and ensure it's on your $PATH
|
// - Build with the usual `./configure && make` and ensure it's on your $PATH
|
||||||
// - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor
|
// - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor
|
||||||
// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go`
|
// - Grab the latest Go plugin `go get -u google.golang.org/protobuf/cmd/protoc-gen-go`
|
||||||
// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...`
|
// - Vendor in the latest Go plugin `govendor fetch google.golang.org/protobuf/...`
|
||||||
|
|
||||||
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
|
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ package trezor
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type returns the protocol buffer type number of a specific message. If the
|
// Type returns the protocol buffer type number of a specific message. If the
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -198,23 +199,21 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
//
|
//
|
||||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
// Withdrawals value will propagate through the returned block. Empty
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
// Withdrawals value must be passed via non-nil, length 0 value in data.
|
||||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||||
txs, err := decodeTransactions(params.Transactions)
|
txs, err := decodeTransactions(data.Transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(data.ExtraData) > int(params.MaximumExtraDataSize) {
|
||||||
if len(params.ExtraData) > 32 {
|
return nil, fmt.Errorf("invalid extradata length: %v", len(data.ExtraData))
|
||||||
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
|
||||||
}
|
}
|
||||||
|
if len(data.LogsBloom) != 256 {
|
||||||
if len(params.LogsBloom) != 256 {
|
return nil, fmt.Errorf("invalid logsBloom length: %v", len(data.LogsBloom))
|
||||||
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
|
|
||||||
}
|
}
|
||||||
// Check that baseFeePerGas is not negative or too big
|
// Check that baseFeePerGas is not negative or too big
|
||||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) {
|
||||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas)
|
||||||
}
|
}
|
||||||
var blobHashes = make([]common.Hash, 0, len(txs))
|
var blobHashes = make([]common.Hash, 0, len(txs))
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
|
|
@ -232,36 +231,35 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
||||||
// ExecutableData before withdrawals are enabled by marshaling
|
// ExecutableData before withdrawals are enabled by marshaling
|
||||||
// Withdrawals as the json null value.
|
// Withdrawals as the json null value.
|
||||||
var withdrawalsRoot *common.Hash
|
var withdrawalsRoot *common.Hash
|
||||||
|
if data.Withdrawals != nil {
|
||||||
if params.Withdrawals != nil {
|
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
||||||
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
|
||||||
withdrawalsRoot = &h
|
withdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
|
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: params.ParentHash,
|
ParentHash: data.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
Coinbase: params.FeeRecipient,
|
Coinbase: data.FeeRecipient,
|
||||||
Root: params.StateRoot,
|
Root: data.StateRoot,
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
ReceiptHash: params.ReceiptsRoot,
|
ReceiptHash: data.ReceiptsRoot,
|
||||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
Bloom: types.BytesToBloom(data.LogsBloom),
|
||||||
Difficulty: common.Big0,
|
Difficulty: common.Big0,
|
||||||
Number: new(big.Int).SetUint64(params.Number),
|
Number: new(big.Int).SetUint64(data.Number),
|
||||||
GasLimit: params.GasLimit,
|
GasLimit: data.GasLimit,
|
||||||
GasUsed: params.GasUsed,
|
GasUsed: data.GasUsed,
|
||||||
Time: params.Timestamp,
|
Time: data.Timestamp,
|
||||||
BaseFee: params.BaseFeePerGas,
|
BaseFee: data.BaseFeePerGas,
|
||||||
Extra: params.ExtraData,
|
Extra: data.ExtraData,
|
||||||
MixDigest: params.Random,
|
MixDigest: data.Random,
|
||||||
WithdrawalsHash: withdrawalsRoot,
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
ExcessBlobGas: params.ExcessBlobGas,
|
ExcessBlobGas: data.ExcessBlobGas,
|
||||||
BlobGasUsed: params.BlobGasUsed,
|
BlobGasUsed: data.BlobGasUsed,
|
||||||
ParentBeaconRoot: beaconRoot,
|
ParentBeaconRoot: beaconRoot,
|
||||||
}
|
}
|
||||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals})
|
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals})
|
||||||
if block.Hash() != params.BlockHash {
|
if block.Hash() != data.BlockHash {
|
||||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
return block, nil
|
return block, nil
|
||||||
|
|
|
||||||
|
|
@ -5,55 +5,55 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.22.4
|
# version:golang 1.22.6
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
fed720678e728a7ca30ba8d1ded1caafe27d16028fab0232b8ba8e22008fb784 go1.22.4.src.tar.gz
|
9e48d99d519882579917d8189c17e98c373ce25abaebb98772e2927088992a51 go1.22.6.src.tar.gz
|
||||||
b9647fa9fc83a0cc5d4f092a19eaeaecf45f063a5aa7d4962fde65aeb7ae6ce1 go1.22.4.aix-ppc64.tar.gz
|
eeb0cc42120cbae6d3695dae2e5420fa0e93a5db957db139b55efdb879dd9856 go1.22.6.aix-ppc64.tar.gz
|
||||||
7788f40f3a46f201df1dc46ca640403eb535d5513fc33449164a90dbd229b761 go1.22.4.darwin-amd64.pkg
|
b47ac340f0b072943fed1f558a26eb260cc23bd21b8af175582e9103141d465b go1.22.6.darwin-amd64.pkg
|
||||||
c95967f50aa4ace34af0c236cbdb49a9a3e80ee2ad09d85775cb4462a5c19ed3 go1.22.4.darwin-amd64.tar.gz
|
9c3c0124b01b5365f73a1489649f78f971ecf84844ad9ca58fde133096ddb61b go1.22.6.darwin-amd64.tar.gz
|
||||||
4036c88faf57a6b096916f1827edcdbf5290a47cc5f59956e88cdd9b1b71088c go1.22.4.darwin-arm64.pkg
|
14d0355ec1c0eeb213a16efa8635fac1f16067ef78a8173abf9a8c7b805e551e go1.22.6.darwin-arm64.pkg
|
||||||
242b78dc4c8f3d5435d28a0d2cec9b4c1aa999b601fb8aa59fb4e5a1364bf827 go1.22.4.darwin-arm64.tar.gz
|
ebac39fd44fc22feed1bb519af431c84c55776e39b30f4fd62930da9c0cfd1e3 go1.22.6.darwin-arm64.tar.gz
|
||||||
f2fbb51af4719d3616efb482d6ed2b96579b474156f85a7ddc6f126764feec4b go1.22.4.dragonfly-amd64.tar.gz
|
3695b10c722a4920c8a736284f8820c142e1e752f3a87f797a45c64366f7a173 go1.22.6.dragonfly-amd64.tar.gz
|
||||||
7c54884bb9f274884651d41e61d1bc12738863ad1497e97ea19ad0e9aa6bf7b5 go1.22.4.freebsd-386.tar.gz
|
a9b9570c80294a664d50b566d6bd1aa42465997d2d76a57936b32f55f5c69c63 go1.22.6.freebsd-386.tar.gz
|
||||||
88d44500e1701dd35797619774d6dd51bf60f45a8338b0a82ddc018e4e63fb78 go1.22.4.freebsd-amd64.tar.gz
|
424a5618406800365fe3ad96a795fb55ce394bea3ff48eaf56d292bf7a916d1e go1.22.6.freebsd-amd64.tar.gz
|
||||||
3d9efe47db142a22679aba46b1772e3900b0d87ae13bd2b3bc80dbf2ac0b2cd6 go1.22.4.freebsd-arm.tar.gz
|
e0dce3a6dbe8e7e054d329dd4cb403935c63c0f7e22e693077aa60e12018b883 go1.22.6.freebsd-arm.tar.gz
|
||||||
726dc093cf020277be45debf03c3b02b43c2efb3e2a5d4fba8f52579d65327dc go1.22.4.freebsd-arm64.tar.gz
|
34930b01f58889c71f7a78c51c6c3bd2ce289ac7862c76dab691303cfa935fd1 go1.22.6.freebsd-arm64.tar.gz
|
||||||
5f6b67e5e32f1d6ccb2d4dcb44934a5e2e870a877ba7443d86ec43cfc28afa71 go1.22.4.freebsd-riscv64.tar.gz
|
4c9d630e55d4d600a5b4297e59620c3bdfe63a441981682b3638e2fdda228a44 go1.22.6.freebsd-riscv64.tar.gz
|
||||||
d56ecc2f85b6418a21ef83879594d0c42ab4f65391a676bb12254870e6690d63 go1.22.4.illumos-amd64.tar.gz
|
9ed63feaf2ef56c56f1cf0d9d3fab4006efd22a38e2f1f5252e95c6ac09332f3 go1.22.6.illumos-amd64.tar.gz
|
||||||
47a2a8d249a91eb8605c33bceec63aedda0441a43eac47b4721e3975ff916cec go1.22.4.linux-386.tar.gz
|
9e680027b058beab10ce5938607660964b6d2c564bf50bdb01aa090dc5beda98 go1.22.6.linux-386.tar.gz
|
||||||
ba79d4526102575196273416239cca418a651e049c2b099f3159db85e7bade7d go1.22.4.linux-amd64.tar.gz
|
999805bed7d9039ec3da1a53bfbcafc13e367da52aa823cb60b68ba22d44c616 go1.22.6.linux-amd64.tar.gz
|
||||||
a8e177c354d2e4a1b61020aca3562e27ea3e8f8247eca3170e3fa1e0c2f9e771 go1.22.4.linux-arm64.tar.gz
|
c15fa895341b8eaf7f219fada25c36a610eb042985dc1a912410c1c90098eaf2 go1.22.6.linux-arm64.tar.gz
|
||||||
e2b143fbacbc9cbd448e9ef41ac3981f0488ce849af1cf37e2341d09670661de go1.22.4.linux-armv6l.tar.gz
|
b566484fe89a54c525dd1a4cbfec903c1f6e8f0b7b3dbaf94c79bc9145391083 go1.22.6.linux-armv6l.tar.gz
|
||||||
e2ff9436e4b34bf6926b06d97916e26d67a909a2effec17967245900f0816f1d go1.22.4.linux-loong64.tar.gz
|
1ee6e1896aea856142d2af7045cea118995b39404aa61afd12677d023d47ee69 go1.22.6.linux-loong64.tar.gz
|
||||||
73f0dcc60458c4770593b05a7bc01cc0d31fc98f948c0c2334812c7a1f2fc3f1 go1.22.4.linux-mips.tar.gz
|
fdd0e1a3e178f9bc79adf6ff1e3de4554ce581b4c468fd6e113c43fbbbe1eec6 go1.22.6.linux-mips.tar.gz
|
||||||
417af97fc2630a647052375768be4c38adcc5af946352ea5b28613ea81ca5d45 go1.22.4.linux-mips64.tar.gz
|
d3e5a621fc5a07759e503a971af0b28ded6a7d6f5604ab511f51f930a18dd3e4 go1.22.6.linux-mips64.tar.gz
|
||||||
7486e2d7dd8c98eb44df815ace35a7fe7f30b7c02326e3741bd934077508139b go1.22.4.linux-mips64le.tar.gz
|
01547606c5b5c1b0e5587b3afd65172860d2f4755e523785832905759ecce2d7 go1.22.6.linux-mips64le.tar.gz
|
||||||
69479c8aad301e459a8365b40cad1074a0dbba5defb9291669f94809c4c4be6e go1.22.4.linux-mipsle.tar.gz
|
2cd771416ae03c11240cfdb551d66ab9a941077664f3727b966f94386c23b0fa go1.22.6.linux-mipsle.tar.gz
|
||||||
dd238847e65bc3e2745caca475a5db6522a2fcf85cf6c38fc36a06642b19efd7 go1.22.4.linux-ppc64.tar.gz
|
6ef61d517777925e6bdb0321ea42d5f60acc20c1314dd902b9d0bfa3a5fd4fca go1.22.6.linux-ppc64.tar.gz
|
||||||
a3e5834657ef92523f570f798fed42f1f87bc18222a16815ec76b84169649ec4 go1.22.4.linux-ppc64le.tar.gz
|
9d99fce3f6f72a76630fe91ec0884dfe3db828def4713368424900fa98bb2bd6 go1.22.6.linux-ppc64le.tar.gz
|
||||||
56a827ff7dc6245bcd7a1e9288dffaa1d8b0fd7468562264c1523daf3b4f1b4a go1.22.4.linux-riscv64.tar.gz
|
30be9c9b9cc4f044d4da9a33ee601ab7b3aff4246107d323a79e08888710754e go1.22.6.linux-riscv64.tar.gz
|
||||||
7590c3e278e2dc6040aae0a39da3ca1eb2e3921673a7304cc34d588c45889eec go1.22.4.linux-s390x.tar.gz
|
82f3bae3ddb4ede45b848db48c5486fadb58551e74507bda45484257e7194a95 go1.22.6.linux-s390x.tar.gz
|
||||||
ddd2eebe34471a2502de6c5dad04ab27c9fc80cbde7a9ad5b3c66ecec4504e1d go1.22.4.netbsd-386.tar.gz
|
85b2eb9d40a930bd3e75d0096a6eb5847aac86c5085e6d13a5845e9ef03f8d4b go1.22.6.netbsd-386.tar.gz
|
||||||
33af79f6f935f6fbacc5d23876450b3567b79348fc065beef8e64081127dd234 go1.22.4.netbsd-amd64.tar.gz
|
6e9acbdc34fb2a942d547c47c9c1989bb6e32b4a37d57fb312499e2bb33b46b7 go1.22.6.netbsd-amd64.tar.gz
|
||||||
fa3550ebd5375a70b3bcd342b5a71f4bd271dcbbfaf4eabefa2144ab5d8924b6 go1.22.4.netbsd-arm.tar.gz
|
e6eff3cf0038f2a9b0c9e01e228577a783bddcd8051222a3d949e24ee392e769 go1.22.6.netbsd-arm.tar.gz
|
||||||
c9a2971dec9f6d320c6f2b049b2353c6d0a2d35e87b8a4b2d78a2f0d62545f8e go1.22.4.netbsd-arm64.tar.gz
|
43a7e2ba22da700b844f7561e3dd5434540ed6c9781be2e9c42e8a8cbf558f8e go1.22.6.netbsd-arm64.tar.gz
|
||||||
d21af022331bfdc2b5b161d616c3a1a4573d33cf7a30416ee509a8f3641deb47 go1.22.4.openbsd-386.tar.gz
|
a90b758ccb45d8a17af8e140fafa1e97607de5a7ecd53a4c55f69258bfb043d0 go1.22.6.openbsd-386.tar.gz
|
||||||
72c0094c43f7e5722ec49c2a3e9dfa7a1123ac43a5f3a63eecf3e3795d3ff0ae go1.22.4.openbsd-amd64.tar.gz
|
cc13436c4a644e55bedcea65981eb80ca8317b39b129f5563ab3b6da1391bd47 go1.22.6.openbsd-amd64.tar.gz
|
||||||
1096831ea3c5ea3ca57d14251d9eda3786889531eb40d7d6775dcaa324d4b065 go1.22.4.openbsd-arm.tar.gz
|
aee34f61ba2b0a8f2618f5c7065e20da7714ce7651680509eda30728fe01ee88 go1.22.6.openbsd-arm.tar.gz
|
||||||
a7ab8d4e0b02bf06ed144ba42c61c0e93ee00f2b433415dfd4ad4b6e79f31650 go1.22.4.openbsd-arm64.tar.gz
|
c67d57daf8baada93c69c8fb02401270cd33159730b1f2d70d9e724ba1a918cf go1.22.6.openbsd-arm64.tar.gz
|
||||||
9716327c8a628358798898dc5148c49dbbeb5196bf2cbf088e550721a6e4f60b go1.22.4.openbsd-ppc64.tar.gz
|
03e1f96002e94a6b381bcf66a0a62b9d5f63148682a780d727840ad540185c7c go1.22.6.openbsd-ppc64.tar.gz
|
||||||
a8dd4503c95c32a502a616ab78870a19889c9325fe9bd31eb16dd69346e4bfa8 go1.22.4.plan9-386.tar.gz
|
0ac2b5bbe2c8a293d284512630e629bf0578aaa7b7b1f39ac4ee182c7924aaad go1.22.6.plan9-386.tar.gz
|
||||||
5423a25808d76fe5aca8607a2e5ac5673abf45446b168cb5e9d8519ee9fe39a1 go1.22.4.plan9-amd64.tar.gz
|
f9afdab8a72a8d874f023f5605482cc94160843ac768dbd840e6f772d16578c7 go1.22.6.plan9-amd64.tar.gz
|
||||||
6af939ad583f5c85c09c53728ab7d38c3cc2b39167562d6c18a07c5c6608b370 go1.22.4.plan9-arm.tar.gz
|
4b9f01a47e6a29d57cbb3097b6770583336cef9c8f0d51d3d1451e42a851002e go1.22.6.plan9-arm.tar.gz
|
||||||
e8cabe69c03085725afdb32a6f9998191a3e55a747b270d835fd05000d56abba go1.22.4.solaris-amd64.tar.gz
|
46c2552ac7b8d6314a52e14e0a0761aaeebdd6aba5f531de386f4cf2b66ec723 go1.22.6.solaris-amd64.tar.gz
|
||||||
5c6446e2ea80bc6a971d2b34446f16e6517e638b0ff8d3ea229228d1931790b0 go1.22.4.windows-386.msi
|
a57821dab76af1ef7a6b62db1628f0caa74343e0c7cb829df9ce8ea0713a3e8e go1.22.6.windows-386.msi
|
||||||
aca4e2c37278a10f1c70dd0df142f7d66b50334fcee48978d409202d308d6d25 go1.22.4.windows-386.zip
|
eb734bacc9aabca1273b61dd392bb84a9bb33783f5e2fff2cd6ab9885bbefbe6 go1.22.6.windows-386.zip
|
||||||
3c21105d7b584759b6e266383b777caf6e87142d304a10b539dbc66ab482bb5f go1.22.4.windows-amd64.msi
|
1238a3e6892eb8a0eb3fe0640e18ab82ca21cc1a933f16897b2ad081f057b5da go1.22.6.windows-amd64.msi
|
||||||
26321c4d945a0035d8a5bc4a1965b0df401ff8ceac66ce2daadabf9030419a98 go1.22.4.windows-amd64.zip
|
6023083a6e4d3199b44c37e9ba7b25d9674da20fd846a35ee5f9589d81c21a6a go1.22.6.windows-amd64.zip
|
||||||
c4303f02b864304eb83dd1db0b4ebf9d2ec9d216e7ef44a7657b166a52889c7f go1.22.4.windows-arm.msi
|
6791218c568a3d000cb36317506541d7fd67e7cfe613baaf361ca36cad5e2cd5 go1.22.6.windows-arm.msi
|
||||||
5fcd0671a49cecf39b41021621ee1b6e7aa1370f37122b72e80d4fd4185833b6 go1.22.4.windows-arm.zip
|
ee41ca83bb07c4fd46a1d6b2d083519bb8ca156fcd9db37ee711234d43126e2f go1.22.6.windows-arm.zip
|
||||||
553cc6c460f4e3eb4fad5b897c0bb22cd8bbeb20929f0e3eeb939420320292ce go1.22.4.windows-arm64.msi
|
91c6b3376612095315a0aeae4b03e3da34fabe9dfd4532d023e2a70f913cf22a go1.22.6.windows-arm64.msi
|
||||||
8a2daa9ea28cbdafddc6171aefed384f4e5b6e714fb52116fe9ed25a132f37ed go1.22.4.windows-arm64.zip
|
7cf55f357ba8116cd3bff992980e20a704ba451b3dab341cf1787b133d900512 go1.22.6.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 1.59.0
|
# version:golangci 1.59.0
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,6 @@ package tools
|
||||||
import (
|
import (
|
||||||
// Tool imports for go:generate.
|
// Tool imports for go:generate.
|
||||||
_ "github.com/fjl/gencodec"
|
_ "github.com/fjl/gencodec"
|
||||||
_ "github.com/golang/protobuf/protoc-gen-go"
|
|
||||||
_ "golang.org/x/tools/cmd/stringer"
|
_ "golang.org/x/tools/cmd/stringer"
|
||||||
|
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,8 @@ import (
|
||||||
// TestImportRaw tests clef --importraw
|
// TestImportRaw tests clef --importraw
|
||||||
func TestImportRaw(t *testing.T) {
|
func TestImportRaw(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||||
_ = os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||||
|
|
||||||
t.Cleanup(func() { os.Remove(keyPath) })
|
|
||||||
|
|
||||||
t.Run("happy-path", func(t *testing.T) {
|
t.Run("happy-path", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
@ -72,10 +70,8 @@ func TestImportRaw(t *testing.T) {
|
||||||
// TestListAccounts tests clef --list-accounts
|
// TestListAccounts tests clef --list-accounts
|
||||||
func TestListAccounts(t *testing.T) {
|
func TestListAccounts(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||||
_ = os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||||
|
|
||||||
t.Cleanup(func() { os.Remove(keyPath) })
|
|
||||||
|
|
||||||
t.Run("no-accounts", func(t *testing.T) {
|
t.Run("no-accounts", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
@ -102,10 +98,8 @@ func TestListAccounts(t *testing.T) {
|
||||||
// TestListWallets tests clef --list-wallets
|
// TestListWallets tests clef --list-wallets
|
||||||
func TestListWallets(t *testing.T) {
|
func TestListWallets(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name()))
|
||||||
_ = os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777)
|
||||||
|
|
||||||
t.Cleanup(func() { os.Remove(keyPath) })
|
|
||||||
|
|
||||||
t.Run("no-accounts", func(t *testing.T) {
|
t.Run("no-accounts", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeJWTSecret() (string, [32]byte, error) {
|
func makeJWTSecret(t *testing.T) (string, [32]byte, error) {
|
||||||
var secret [32]byte
|
var secret [32]byte
|
||||||
if _, err := crand.Read(secret[:]); err != nil {
|
if _, err := crand.Read(secret[:]); err != nil {
|
||||||
return "", secret, fmt.Errorf("failed to create jwt secret: %v", err)
|
return "", secret, fmt.Errorf("failed to create jwt secret: %v", err)
|
||||||
}
|
}
|
||||||
jwtPath := filepath.Join(os.TempDir(), "jwt_secret")
|
jwtPath := filepath.Join(t.TempDir(), "jwt_secret")
|
||||||
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
||||||
return "", secret, fmt.Errorf("failed to prepare jwt secret file: %v", err)
|
return "", secret, fmt.Errorf("failed to prepare jwt secret file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ func makeJWTSecret() (string, [32]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEthSuite(t *testing.T) {
|
func TestEthSuite(t *testing.T) {
|
||||||
jwtPath, secret, err := makeJWTSecret()
|
jwtPath, secret, err := makeJWTSecret(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not make jwt secret: %v", err)
|
t.Fatalf("could not make jwt secret: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ func TestEthSuite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSnapSuite(t *testing.T) {
|
func TestSnapSuite(t *testing.T) {
|
||||||
jwtPath, secret, err := makeJWTSecret()
|
jwtPath, secret, err := makeJWTSecret(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not make jwt secret: %v", err)
|
t.Fatalf("could not make jwt secret: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ func rlpxPing(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
tcpEndpoint, ok := n.TCPEndpoint()
|
tcpEndpoint, ok := n.TCPEndpoint()
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("node has no TCP endpoint")
|
return errors.New("node has no TCP endpoint")
|
||||||
}
|
}
|
||||||
fd, err := net.Dial("tcp", tcpEndpoint.String())
|
fd, err := net.Dial("tcp", tcpEndpoint.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ which can
|
||||||
1. Take a prestate, including
|
1. Take a prestate, including
|
||||||
- Accounts,
|
- Accounts,
|
||||||
- Block context information,
|
- Block context information,
|
||||||
- Previous blockshashes (*optional)
|
- Previous blocks hashes (*optional)
|
||||||
2. Apply a set of transactions,
|
2. Apply a set of transactions,
|
||||||
3. Apply a mining-reward (*optional),
|
3. Apply a mining-reward (*optional),
|
||||||
4. And generate a post-state, including
|
4. And generate a post-state, including
|
||||||
|
|
@ -26,7 +26,7 @@ which can
|
||||||
|
|
||||||
### Specification
|
### Specification
|
||||||
|
|
||||||
The idea is to specify the behaviour of this binary very _strict_, so that other
|
The idea is to specify the behavior of this binary very _strict_, so that other
|
||||||
node implementors can build replicas based on their own state-machines, and the
|
node implementors can build replicas based on their own state-machines, and the
|
||||||
state generators can swap between a \`geth\`-based implementation and a \`parityvm\`-based
|
state generators can swap between a \`geth\`-based implementation and a \`parityvm\`-based
|
||||||
implementation.
|
implementation.
|
||||||
|
|
@ -59,7 +59,7 @@ following object definitions are required.
|
||||||
|
|
||||||
##### `alloc`
|
##### `alloc`
|
||||||
|
|
||||||
The `alloc` object defines the prestate that transition will begin with.
|
The `alloc` object defines the prestate that the transition will begin with.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Map of address to account definition.
|
// Map of address to account definition.
|
||||||
|
|
@ -195,12 +195,12 @@ There are a few (not many) errors that can occur, those are defined below.
|
||||||
- Other EVM error. Exit code `2`
|
- Other EVM error. Exit code `2`
|
||||||
- Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`.
|
- Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`.
|
||||||
- Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH`
|
- Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH`
|
||||||
is invoked targeting a block which history has not been provided for, the program will
|
is invoked targeting a block whose history has not been provided for, the program will
|
||||||
exit with code `4`.
|
exit with code `4`.
|
||||||
|
|
||||||
##### IO errors (`10`-`20`)
|
##### IO errors (`10`-`20`)
|
||||||
|
|
||||||
- Invalid input json: the supplied data could not be marshalled.
|
- Invalid input json: the supplied data could not be marshaled.
|
||||||
The program will exit with code `10`
|
The program will exit with code `10`
|
||||||
- IO problems: failure to load or save files, the program will exit with code `11`
|
- IO problems: failure to load or save files, the program will exit with code `11`
|
||||||
|
|
||||||
|
|
@ -620,7 +620,7 @@ of encoding should always be accepted.
|
||||||
|
|
||||||
There are many test cases in the [`cmd/evm/testdata`](./testdata) directory.
|
There are many test cases in the [`cmd/evm/testdata`](./testdata) directory.
|
||||||
These fixtures are used to power the `t8n` tests in
|
These fixtures are used to power the `t8n` tests in
|
||||||
[`t8n_test.go`](./t8n_test.go). The best way to verify correctness of new `evm`
|
[`t8n_test.go`](./t8n_test.go). The best way to verify the correctness of new `evm`
|
||||||
implementations is to execute these and verify the output and error codes match
|
implementations is to execute these and verify the output and error codes match
|
||||||
the expected values.
|
the expected values.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
Time: genesisConfig.Timestamp,
|
Time: genesisConfig.Timestamp,
|
||||||
Coinbase: genesisConfig.Coinbase,
|
Coinbase: genesisConfig.Coinbase,
|
||||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||||
|
BaseFee: genesisConfig.BaseFee,
|
||||||
BlobHashes: blobHashes,
|
BlobHashes: blobHashes,
|
||||||
BlobBaseFee: blobBaseFee,
|
BlobBaseFee: blobBaseFee,
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ JavaScript API. See https://geth.ethereum.org/docs/interacting-with-geth/javascr
|
||||||
func localConsole(ctx *cli.Context) error {
|
func localConsole(ctx *cli.Context) error {
|
||||||
// Create and start the node based on the CLI flags
|
// Create and start the node based on the CLI flags
|
||||||
prepare(ctx)
|
prepare(ctx)
|
||||||
stack, backend := makeFullNode(ctx)
|
stack, _ := makeFullNode(ctx)
|
||||||
startNode(ctx, stack, backend, true)
|
startNode(ctx, stack, true)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
// Attach to the newly started node and create the JavaScript console.
|
// Attach to the newly started node and create the JavaScript console.
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,8 @@ func removeDB(ctx *cli.Context) error {
|
||||||
// Delete state data
|
// Delete state data
|
||||||
statePaths := []string{
|
statePaths := []string{
|
||||||
rootDir,
|
rootDir,
|
||||||
filepath.Join(ancientDir, rawdb.StateFreezerName),
|
filepath.Join(ancientDir, rawdb.MerkleStateFreezerName),
|
||||||
|
filepath.Join(ancientDir, rawdb.VerkleStateFreezerName),
|
||||||
}
|
}
|
||||||
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ import (
|
||||||
// TestExport does a basic test of "geth export", exporting the test-genesis.
|
// TestExport does a basic test of "geth export", exporting the test-genesis.
|
||||||
func TestExport(t *testing.T) {
|
func TestExport(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
outfile := fmt.Sprintf("%v/testExport.out", os.TempDir())
|
outfile := fmt.Sprintf("%v/testExport.out", t.TempDir())
|
||||||
defer os.Remove(outfile)
|
|
||||||
geth := runGeth(t, "--datadir", initGeth(t), "export", outfile)
|
geth := runGeth(t, "--datadir", initGeth(t), "export", outfile)
|
||||||
geth.WaitExit()
|
geth.WaitExit()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,9 +201,8 @@ func TestFileOut(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
have, want []byte
|
have, want []byte
|
||||||
err error
|
err error
|
||||||
path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63())
|
path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63())
|
||||||
)
|
)
|
||||||
t.Cleanup(func() { os.Remove(path) })
|
|
||||||
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "logtest"); err != nil {
|
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "logtest"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -222,9 +221,8 @@ func TestRotatingFileOut(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
have, want []byte
|
have, want []byte
|
||||||
err error
|
err error
|
||||||
path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63())
|
path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63())
|
||||||
)
|
)
|
||||||
t.Cleanup(func() { os.Remove(path) })
|
|
||||||
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "--log.rotate", "logtest"); err != nil {
|
if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "--log.rotate", "logtest"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/console/prompt"
|
"github.com/ethereum/go-ethereum/console/prompt"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
@ -376,10 +374,10 @@ func geth(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare(ctx)
|
prepare(ctx)
|
||||||
stack, backend := makeFullNode(ctx)
|
stack, _ := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
startNode(ctx, stack, backend, false)
|
startNode(ctx, stack, false)
|
||||||
stack.Wait()
|
stack.Wait()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -388,7 +386,11 @@ func geth(ctx *cli.Context) error {
|
||||||
// startNode boots up the system node and all registered protocols, after which
|
// startNode boots up the system node and all registered protocols, after which
|
||||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||||
// miner.
|
// miner.
|
||||||
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
|
|
||||||
|
// 1.14.8 - We don't use cmd/geth for cli purposes,
|
||||||
|
// and to maintain compatibility with the future upstream merges,
|
||||||
|
// we are removing the backend ethapi.Backend param from the function signature.
|
||||||
|
func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||||
// Start up the node itself
|
// Start up the node itself
|
||||||
utils.StartNode(ctx, stack, isConsole)
|
utils.StartNode(ctx, stack, isConsole)
|
||||||
|
|
||||||
|
|
@ -463,25 +465,6 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start auxiliary services if enabled
|
|
||||||
if ctx.Bool(utils.MiningEnabledFlag.Name) {
|
|
||||||
// Mining only makes sense if a full Ethereum node is running
|
|
||||||
if ctx.String(utils.SyncModeFlag.Name) == "light" {
|
|
||||||
utils.Fatalf("Light clients do not support mining")
|
|
||||||
}
|
|
||||||
|
|
||||||
ethBackend, ok := backend.(*eth.EthAPIBackend)
|
|
||||||
if !ok {
|
|
||||||
utils.Fatalf("Ethereum service not running")
|
|
||||||
}
|
|
||||||
// Set the gas price to the limits from the CLI and start mining
|
|
||||||
gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
|
||||||
ethBackend.TxPool().SetGasTip(gasprice)
|
|
||||||
if err := ethBackend.StartMining(); err != nil {
|
|
||||||
utils.Fatalf("Failed to start mining: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlockAccounts unlocks any account specifically requested.
|
// unlockAccounts unlocks any account specifically requested.
|
||||||
|
|
|
||||||
|
|
@ -1,500 +0,0 @@
|
||||||
// Copyright 2017 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/>.
|
|
||||||
|
|
||||||
// p2psim provides a command-line client for a simulation HTTP API.
|
|
||||||
//
|
|
||||||
// Here is an example of creating a 2 node network with the first node
|
|
||||||
// connected to the second:
|
|
||||||
//
|
|
||||||
// $ p2psim node create
|
|
||||||
// Created node01
|
|
||||||
//
|
|
||||||
// $ p2psim node start node01
|
|
||||||
// Started node01
|
|
||||||
//
|
|
||||||
// $ p2psim node create
|
|
||||||
// Created node02
|
|
||||||
//
|
|
||||||
// $ p2psim node start node02
|
|
||||||
// Started node02
|
|
||||||
//
|
|
||||||
// $ p2psim node connect node01 node02
|
|
||||||
// Connected node01 to node02
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var client *simulations.Client
|
|
||||||
|
|
||||||
var (
|
|
||||||
// global command flags
|
|
||||||
apiFlag = &cli.StringFlag{
|
|
||||||
Name: "api",
|
|
||||||
Value: "http://localhost:8888",
|
|
||||||
Usage: "simulation API URL",
|
|
||||||
EnvVars: []string{"P2PSIM_API_URL"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// events subcommand flags
|
|
||||||
currentFlag = &cli.BoolFlag{
|
|
||||||
Name: "current",
|
|
||||||
Usage: "get existing nodes and conns first",
|
|
||||||
}
|
|
||||||
filterFlag = &cli.StringFlag{
|
|
||||||
Name: "filter",
|
|
||||||
Value: "",
|
|
||||||
Usage: "message filter",
|
|
||||||
}
|
|
||||||
|
|
||||||
// node create subcommand flags
|
|
||||||
nameFlag = &cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "node name",
|
|
||||||
}
|
|
||||||
servicesFlag = &cli.StringFlag{
|
|
||||||
Name: "services",
|
|
||||||
Value: "",
|
|
||||||
Usage: "node services (comma separated)",
|
|
||||||
}
|
|
||||||
keyFlag = &cli.StringFlag{
|
|
||||||
Name: "key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "node private key (hex encoded)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// node rpc subcommand flags
|
|
||||||
subscribeFlag = &cli.BoolFlag{
|
|
||||||
Name: "subscribe",
|
|
||||||
Usage: "method is a subscription",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := flags.NewApp("devp2p simulation command-line client")
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
apiFlag,
|
|
||||||
}
|
|
||||||
app.Before = func(ctx *cli.Context) error {
|
|
||||||
client = simulations.NewClient(ctx.String(apiFlag.Name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
app.Commands = []*cli.Command{
|
|
||||||
{
|
|
||||||
Name: "show",
|
|
||||||
Usage: "show network information",
|
|
||||||
Action: showNetwork,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "events",
|
|
||||||
Usage: "stream network events",
|
|
||||||
Action: streamNetwork,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
currentFlag,
|
|
||||||
filterFlag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "snapshot",
|
|
||||||
Usage: "create a network snapshot to stdout",
|
|
||||||
Action: createSnapshot,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "load",
|
|
||||||
Usage: "load a network snapshot from stdin",
|
|
||||||
Action: loadSnapshot,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "node",
|
|
||||||
Usage: "manage simulation nodes",
|
|
||||||
Action: listNodes,
|
|
||||||
Subcommands: []*cli.Command{
|
|
||||||
{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "list nodes",
|
|
||||||
Action: listNodes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "create a node",
|
|
||||||
Action: createNode,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
nameFlag,
|
|
||||||
servicesFlag,
|
|
||||||
keyFlag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "show",
|
|
||||||
ArgsUsage: "<node>",
|
|
||||||
Usage: "show node information",
|
|
||||||
Action: showNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "start",
|
|
||||||
ArgsUsage: "<node>",
|
|
||||||
Usage: "start a node",
|
|
||||||
Action: startNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "stop",
|
|
||||||
ArgsUsage: "<node>",
|
|
||||||
Usage: "stop a node",
|
|
||||||
Action: stopNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "connect",
|
|
||||||
ArgsUsage: "<node> <peer>",
|
|
||||||
Usage: "connect a node to a peer node",
|
|
||||||
Action: connectNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "disconnect",
|
|
||||||
ArgsUsage: "<node> <peer>",
|
|
||||||
Usage: "disconnect a node from a peer node",
|
|
||||||
Action: disconnectNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rpc",
|
|
||||||
ArgsUsage: "<node> <method> [<args>]",
|
|
||||||
Usage: "call a node RPC method",
|
|
||||||
Action: rpcNode,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
subscribeFlag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func showNetwork(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
network, err := client.GetNetwork()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
|
||||||
defer w.Flush()
|
|
||||||
fmt.Fprintf(w, "NODES\t%d\n", len(network.Nodes))
|
|
||||||
fmt.Fprintf(w, "CONNS\t%d\n", len(network.Conns))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func streamNetwork(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
events := make(chan *simulations.Event)
|
|
||||||
|
|
||||||
sub, err := client.SubscribeNetwork(events, simulations.SubscribeOpts{
|
|
||||||
Current: ctx.Bool(currentFlag.Name),
|
|
||||||
Filter: ctx.String(filterFlag.Name),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
|
|
||||||
enc := json.NewEncoder(ctx.App.Writer)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-events:
|
|
||||||
if err := enc.Encode(event); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSnapshot(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
snap, err := client.CreateSnapshot()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.NewEncoder(os.Stdout).Encode(snap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSnapshot(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
snap := &simulations.Snapshot{}
|
|
||||||
if err := json.NewDecoder(os.Stdin).Decode(snap); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.LoadSnapshot(snap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listNodes(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, err := client.GetNodes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
|
||||||
defer w.Flush()
|
|
||||||
fmt.Fprintf(w, "NAME\tPROTOCOLS\tID\n")
|
|
||||||
|
|
||||||
for _, node := range nodes {
|
|
||||||
fmt.Fprintf(w, "%s\t%s\t%s\n", node.Name, strings.Join(protocolList(node), ","), node.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func protocolList(node *p2p.NodeInfo) []string {
|
|
||||||
protos := make([]string, 0, len(node.Protocols))
|
|
||||||
for name := range node.Protocols {
|
|
||||||
protos = append(protos, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return protos
|
|
||||||
}
|
|
||||||
|
|
||||||
func createNode(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 0 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
config := adapters.RandomNodeConfig()
|
|
||||||
config.Name = ctx.String(nameFlag.Name)
|
|
||||||
|
|
||||||
if key := ctx.String(keyFlag.Name); key != "" {
|
|
||||||
privKey, err := crypto.HexToECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
|
|
||||||
config.PrivateKey = privKey
|
|
||||||
}
|
|
||||||
|
|
||||||
if services := ctx.String(servicesFlag.Name); services != "" {
|
|
||||||
config.Lifecycles = strings.Split(services, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
node, err := client.CreateNode(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Created", node.Name)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func showNode(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 1 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeName := ctx.Args().First()
|
|
||||||
|
|
||||||
node, err := client.GetNode(nodeName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
|
|
||||||
defer w.Flush()
|
|
||||||
fmt.Fprintf(w, "NAME\t%s\n", node.Name)
|
|
||||||
fmt.Fprintf(w, "PROTOCOLS\t%s\n", strings.Join(protocolList(node), ","))
|
|
||||||
fmt.Fprintf(w, "ID\t%s\n", node.ID)
|
|
||||||
fmt.Fprintf(w, "ENODE\t%s\n", node.Enode)
|
|
||||||
|
|
||||||
for name, proto := range node.Protocols {
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
fmt.Fprintf(w, "--- PROTOCOL INFO: %s\n", name)
|
|
||||||
fmt.Fprintf(w, "%v\n", proto)
|
|
||||||
fmt.Fprintf(w, "---\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func startNode(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 1 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeName := ctx.Args().First()
|
|
||||||
if err := client.StartNode(nodeName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Started", nodeName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopNode(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 1 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeName := ctx.Args().First()
|
|
||||||
if err := client.StopNode(nodeName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Stopped", nodeName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectNode(ctx *cli.Context) error {
|
|
||||||
if ctx.NArg() != 2 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := ctx.Args()
|
|
||||||
nodeName := args.Get(0)
|
|
||||||
peerName := args.Get(1)
|
|
||||||
|
|
||||||
if err := client.ConnectNode(nodeName, peerName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Connected", nodeName, "to", peerName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnectNode(ctx *cli.Context) error {
|
|
||||||
args := ctx.Args()
|
|
||||||
if args.Len() != 2 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeName := args.Get(0)
|
|
||||||
peerName := args.Get(1)
|
|
||||||
|
|
||||||
if err := client.DisconnectNode(nodeName, peerName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Disconnected", nodeName, "from", peerName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcNode(ctx *cli.Context) error {
|
|
||||||
args := ctx.Args()
|
|
||||||
if args.Len() < 2 {
|
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeName := args.Get(0)
|
|
||||||
method := args.Get(1)
|
|
||||||
|
|
||||||
rpcClient, err := client.RPCClient(context.Background(), nodeName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Bool(subscribeFlag.Name) {
|
|
||||||
return rpcSubscribe(rpcClient, ctx.App.Writer, method, args.Slice()[3:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result interface{}
|
|
||||||
|
|
||||||
params := make([]interface{}, len(args.Slice()[3:]))
|
|
||||||
for i, v := range args.Slice()[3:] {
|
|
||||||
params[i] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rpcClient.Call(&result, method, params...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.NewEncoder(ctx.App.Writer).Encode(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcSubscribe(client *rpc.Client, out io.Writer, method string, args ...string) error {
|
|
||||||
namespace, method, _ := strings.Cut(method, "_")
|
|
||||||
ch := make(chan interface{})
|
|
||||||
subArgs := make([]interface{}, len(args)+1)
|
|
||||||
subArgs[0] = method
|
|
||||||
|
|
||||||
for i, v := range args {
|
|
||||||
subArgs[i+1] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
sub, err := client.Subscribe(context.Background(), namespace, ch, subArgs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
|
|
||||||
enc := json.NewEncoder(out)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case v := <-ch:
|
|
||||||
if err := enc.Encode(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -29,18 +29,12 @@ import (
|
||||||
|
|
||||||
// TestExport does basic sanity checks on the export/import functionality
|
// TestExport does basic sanity checks on the export/import functionality
|
||||||
func TestExport(t *testing.T) {
|
func TestExport(t *testing.T) {
|
||||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump", t.TempDir())
|
||||||
defer func() {
|
|
||||||
os.Remove(f)
|
|
||||||
}()
|
|
||||||
testExport(t, f)
|
testExport(t, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportGzip(t *testing.T) {
|
func TestExportGzip(t *testing.T) {
|
||||||
f := fmt.Sprintf("%v/tempdump.gz", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump.gz", t.TempDir())
|
||||||
defer func() {
|
|
||||||
os.Remove(f)
|
|
||||||
}()
|
|
||||||
testExport(t, f)
|
testExport(t, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,20 +100,14 @@ func testExport(t *testing.T, f string) {
|
||||||
|
|
||||||
// TestDeletionExport tests if the deletion markers can be exported/imported correctly
|
// TestDeletionExport tests if the deletion markers can be exported/imported correctly
|
||||||
func TestDeletionExport(t *testing.T) {
|
func TestDeletionExport(t *testing.T) {
|
||||||
f := fmt.Sprintf("%v/tempdump", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump", t.TempDir())
|
||||||
defer func() {
|
|
||||||
os.Remove(f)
|
|
||||||
}()
|
|
||||||
testDeletion(t, f)
|
testDeletion(t, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDeletionExportGzip tests if the deletion markers can be exported/imported
|
// TestDeletionExportGzip tests if the deletion markers can be exported/imported
|
||||||
// correctly with gz compression.
|
// correctly with gz compression.
|
||||||
func TestDeletionExportGzip(t *testing.T) {
|
func TestDeletionExportGzip(t *testing.T) {
|
||||||
f := fmt.Sprintf("%v/tempdump.gz", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump.gz", t.TempDir())
|
||||||
defer func() {
|
|
||||||
os.Remove(f)
|
|
||||||
}()
|
|
||||||
testDeletion(t, f)
|
testDeletion(t, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,11 +173,7 @@ func testDeletion(t *testing.T, f string) {
|
||||||
// TestImportFutureFormat tests that we reject unsupported future versions.
|
// TestImportFutureFormat tests that we reject unsupported future versions.
|
||||||
func TestImportFutureFormat(t *testing.T) {
|
func TestImportFutureFormat(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
f := fmt.Sprintf("%v/tempdump-future", os.TempDir())
|
f := fmt.Sprintf("%v/tempdump-future", t.TempDir())
|
||||||
defer func() {
|
|
||||||
os.Remove(f)
|
|
||||||
}()
|
|
||||||
|
|
||||||
fh, err := os.OpenFile(f, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
fh, err := os.OpenFile(f, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -305,7 +306,7 @@ var (
|
||||||
}
|
}
|
||||||
BeaconApiHeaderFlag = &cli.StringSliceFlag{
|
BeaconApiHeaderFlag = &cli.StringSliceFlag{
|
||||||
Name: "beacon.api.header",
|
Name: "beacon.api.header",
|
||||||
Usage: "Pass custom HTTP header fields to the emote beacon node API in \"key:value\" format. This flag can be given multiple times.",
|
Usage: "Pass custom HTTP header fields to the remote beacon node API in \"key:value\" format. This flag can be given multiple times.",
|
||||||
Category: flags.BeaconCategory,
|
Category: flags.BeaconCategory,
|
||||||
}
|
}
|
||||||
BeaconThresholdFlag = &cli.IntFlag{
|
BeaconThresholdFlag = &cli.IntFlag{
|
||||||
|
|
@ -1633,6 +1634,18 @@ func setTxPool(ctx *cli.Context, cfg *legacypool.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setBlobPool(ctx *cli.Context, cfg *blobpool.Config) {
|
||||||
|
if ctx.IsSet(BlobPoolDataDirFlag.Name) {
|
||||||
|
cfg.Datadir = ctx.String(BlobPoolDataDirFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.IsSet(BlobPoolDataCapFlag.Name) {
|
||||||
|
cfg.Datacap = ctx.Uint64(BlobPoolDataCapFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.IsSet(BlobPoolPriceBumpFlag.Name) {
|
||||||
|
cfg.PriceBump = ctx.Uint64(BlobPoolPriceBumpFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||||
if ctx.Bool(MiningEnabledFlag.Name) {
|
if ctx.Bool(MiningEnabledFlag.Name) {
|
||||||
log.Warn("The flag --mine is deprecated and will be removed")
|
log.Warn("The flag --mine is deprecated and will be removed")
|
||||||
|
|
@ -1746,6 +1759,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
setEtherbase(ctx, cfg)
|
setEtherbase(ctx, cfg)
|
||||||
setGPO(ctx, &cfg.GPO)
|
setGPO(ctx, &cfg.GPO)
|
||||||
setTxPool(ctx, &cfg.TxPool)
|
setTxPool(ctx, &cfg.TxPool)
|
||||||
|
setBlobPool(ctx, &cfg.BlobPool)
|
||||||
setMiner(ctx, &cfg.Miner)
|
setMiner(ctx, &cfg.Miner)
|
||||||
setRequiredBlocks(ctx, cfg)
|
setRequiredBlocks(ctx, cfg)
|
||||||
setLes(ctx, cfg)
|
setLes(ctx, cfg)
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
||||||
// (c) the extradata is limited to 32 bytes
|
// (c) the extradata is limited to 32 bytes
|
||||||
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
||||||
// Ensure that the header's extra-data section is of a reasonable size
|
// Ensure that the header's extra-data section is of a reasonable size
|
||||||
if len(header.Extra) > 32 {
|
if len(header.Extra) > int(params.MaximumExtraDataSize) {
|
||||||
return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra))
|
return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra))
|
||||||
}
|
}
|
||||||
// Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
|
// Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
triedb := triedb.NewDatabase(db, triedb.VerkleDefaults)
|
||||||
block := genesis.MustCommit(db, triedb)
|
block := genesis.MustCommit(db, triedb)
|
||||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
|
||||||
|
|
@ -330,8 +330,8 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
if !triedb.IsVerkle() {
|
if !triedb.IsVerkle() {
|
||||||
t.Fatalf("expected trie to be verkle")
|
t.Fatalf("expected trie to be verkle")
|
||||||
}
|
}
|
||||||
|
vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
|
||||||
if !rawdb.HasAccountTrieNode(db, nil) {
|
if !rawdb.HasAccountTrieNode(vdb, nil) {
|
||||||
t.Fatal("could not find node")
|
t.Fatal("could not find node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
|
||||||
|
|
||||||
// ReadStateScheme reads the state scheme of persistent state, or none
|
// ReadStateScheme reads the state scheme of persistent state, or none
|
||||||
// if the state is not present in database.
|
// if the state is not present in database.
|
||||||
func ReadStateScheme(db ethdb.Reader) string {
|
func ReadStateScheme(db ethdb.Database) string {
|
||||||
// Check if state in path-based scheme is present.
|
// Check if state in path-based scheme is present.
|
||||||
if HasAccountTrieNode(db, nil) {
|
if HasAccountTrieNode(db, nil) {
|
||||||
return PathScheme
|
return PathScheme
|
||||||
|
|
@ -256,6 +256,16 @@ func ReadStateScheme(db ethdb.Reader) string {
|
||||||
if id := ReadPersistentStateID(db); id != 0 {
|
if id := ReadPersistentStateID(db); id != 0 {
|
||||||
return PathScheme
|
return PathScheme
|
||||||
}
|
}
|
||||||
|
// Check if verkle state in path-based scheme is present.
|
||||||
|
vdb := NewTable(db, string(VerklePrefix))
|
||||||
|
if HasAccountTrieNode(vdb, nil) {
|
||||||
|
return PathScheme
|
||||||
|
}
|
||||||
|
// The root node of verkle might be deleted during the initial snap sync,
|
||||||
|
// check the persistent state id then.
|
||||||
|
if id := ReadPersistentStateID(vdb); id != 0 {
|
||||||
|
return PathScheme
|
||||||
|
}
|
||||||
// In a hash-based scheme, the genesis state is consistently stored
|
// In a hash-based scheme, the genesis state is consistently stored
|
||||||
// on the disk. To assess the scheme of the persistent state, it
|
// on the disk. To assess the scheme of the persistent state, it
|
||||||
// suffices to inspect the scheme of the genesis state.
|
// suffices to inspect the scheme of the genesis state.
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,13 @@ var stateFreezerNoSnappy = map[string]bool{
|
||||||
|
|
||||||
// The list of identifiers of ancient stores.
|
// The list of identifiers of ancient stores.
|
||||||
var (
|
var (
|
||||||
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
|
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
|
||||||
StateFreezerName = "state" // the folder name of reverse diff ancient store.
|
MerkleStateFreezerName = "state" // the folder name of state history ancient store.
|
||||||
|
VerkleStateFreezerName = "state_verkle" // the folder name of state history ancient store.
|
||||||
)
|
)
|
||||||
|
|
||||||
// freezers the collections of all builtin freezers.
|
// freezers the collections of all builtin freezers.
|
||||||
var freezers = []string{ChainFreezerName, StateFreezerName}
|
var freezers = []string{ChainFreezerName, MerkleStateFreezerName, VerkleStateFreezerName}
|
||||||
|
|
||||||
// NewStateFreezer initializes the ancient store for state history.
|
// NewStateFreezer initializes the ancient store for state history.
|
||||||
//
|
//
|
||||||
|
|
@ -86,9 +87,15 @@ var freezers = []string{ChainFreezerName, StateFreezerName}
|
||||||
// state freezer (e.g. dev mode).
|
// state freezer (e.g. dev mode).
|
||||||
// - if non-empty directory is given, initializes the regular file-based
|
// - if non-empty directory is given, initializes the regular file-based
|
||||||
// state freezer.
|
// state freezer.
|
||||||
func NewStateFreezer(ancientDir string, readOnly bool) (ethdb.ResettableAncientStore, error) {
|
func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.ResettableAncientStore, error) {
|
||||||
if ancientDir == "" {
|
if ancientDir == "" {
|
||||||
return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil
|
return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil
|
||||||
}
|
}
|
||||||
return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
var name string
|
||||||
|
if verkle {
|
||||||
|
name = filepath.Join(ancientDir, VerkleStateFreezerName)
|
||||||
|
} else {
|
||||||
|
name = filepath.Join(ancientDir, MerkleStateFreezerName)
|
||||||
|
}
|
||||||
|
return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,12 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||||
}
|
}
|
||||||
infos = append(infos, info)
|
infos = append(infos, info)
|
||||||
|
|
||||||
case StateFreezerName:
|
case MerkleStateFreezerName, VerkleStateFreezerName:
|
||||||
datadir, err := db.AncientDatadir()
|
datadir, err := db.AncientDatadir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f, err := NewStateFreezer(datadir, true)
|
f, err := NewStateFreezer(datadir, freezer == VerkleStateFreezerName, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue // might be possible the state freezer is not existent
|
continue // might be possible the state freezer is not existent
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +128,7 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
|
||||||
switch freezerName {
|
switch freezerName {
|
||||||
case ChainFreezerName:
|
case ChainFreezerName:
|
||||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||||
case StateFreezerName:
|
case MerkleStateFreezerName, VerkleStateFreezerName:
|
||||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
||||||
|
|
|
||||||
|
|
@ -654,6 +654,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
beaconHeaders stat
|
beaconHeaders stat
|
||||||
cliqueSnaps stat
|
cliqueSnaps stat
|
||||||
|
|
||||||
|
// Verkle statistics
|
||||||
|
verkleTries stat
|
||||||
|
verkleStateLookups stat
|
||||||
|
|
||||||
// Les statistic
|
// Les statistic
|
||||||
chtTrieNodes stat
|
chtTrieNodes stat
|
||||||
bloomTrieNodes stat
|
bloomTrieNodes stat
|
||||||
|
|
@ -725,6 +729,24 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
|
bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
|
||||||
bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
|
bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
|
||||||
bloomTrieNodes.Add(size)
|
bloomTrieNodes.Add(size)
|
||||||
|
|
||||||
|
// Verkle trie data is detected, determine the sub-category
|
||||||
|
case bytes.HasPrefix(key, VerklePrefix):
|
||||||
|
remain := key[len(VerklePrefix):]
|
||||||
|
switch {
|
||||||
|
case IsAccountTrieNode(remain):
|
||||||
|
verkleTries.Add(size)
|
||||||
|
case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength:
|
||||||
|
verkleStateLookups.Add(size)
|
||||||
|
case bytes.Equal(remain, persistentStateIDKey):
|
||||||
|
metadata.Add(size)
|
||||||
|
case bytes.Equal(remain, trieJournalKey):
|
||||||
|
metadata.Add(size)
|
||||||
|
case bytes.Equal(remain, snapSyncStatusFlagKey):
|
||||||
|
metadata.Add(size)
|
||||||
|
default:
|
||||||
|
unaccounted.Add(size)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
var accounted bool
|
var accounted bool
|
||||||
|
|
||||||
|
|
@ -770,6 +792,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
||||||
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
|
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
|
||||||
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
|
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
|
||||||
|
{"Key-Value store", "Verkle trie nodes", verkleTries.Size(), verkleTries.Count()},
|
||||||
|
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.Size(), verkleStateLookups.Count()},
|
||||||
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
|
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
|
||||||
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
|
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
|
||||||
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadWriteFreezerTableMeta(t *testing.T) {
|
func TestReadWriteFreezerTableMeta(t *testing.T) {
|
||||||
f, err := os.CreateTemp(os.TempDir(), "*")
|
f, err := os.CreateTemp(t.TempDir(), "*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create file %v", err)
|
t.Fatalf("Failed to create file %v", err)
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
err = writeMetadata(f, newMetadata(100))
|
err = writeMetadata(f, newMetadata(100))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to write metadata %v", err)
|
t.Fatalf("Failed to write metadata %v", err)
|
||||||
|
|
@ -47,11 +47,11 @@ func TestReadWriteFreezerTableMeta(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitializeFreezerTableMeta(t *testing.T) {
|
func TestInitializeFreezerTableMeta(t *testing.T) {
|
||||||
f, err := os.CreateTemp(os.TempDir(), "*")
|
f, err := os.CreateTemp(t.TempDir(), "*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create file %v", err)
|
t.Fatalf("Failed to create file %v", err)
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
meta, err := loadMetadata(f, uint64(100))
|
meta, err := loadMetadata(f, uint64(100))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to read metadata %v", err)
|
t.Fatalf("Failed to read metadata %v", err)
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,13 @@ var (
|
||||||
TrieNodeStoragePrefix = []byte("O") // TrieNodeStoragePrefix + accountHash + hexPath -> trie node
|
TrieNodeStoragePrefix = []byte("O") // TrieNodeStoragePrefix + accountHash + hexPath -> trie node
|
||||||
stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
|
stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
|
||||||
|
|
||||||
|
// VerklePrefix is the database prefix for Verkle trie data, which includes:
|
||||||
|
// (a) Trie nodes
|
||||||
|
// (b) In-memory trie node journal
|
||||||
|
// (c) Persistent state ID
|
||||||
|
// (d) State ID lookups, etc.
|
||||||
|
VerklePrefix = []byte("v")
|
||||||
|
|
||||||
PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
|
PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
|
||||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||||
genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
|
genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
|
||||||
|
|
|
||||||
|
|
@ -211,13 +211,6 @@ func (t *table) NewBatchWithSize(size int) ethdb.Batch {
|
||||||
return &tableBatch{t.db.NewBatchWithSize(size), t.prefix}
|
return &tableBatch{t.db.NewBatchWithSize(size), t.prefix}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot creates a database snapshot based on the current state.
|
|
||||||
// The created snapshot will not be affected by all following mutations
|
|
||||||
// happened on the database.
|
|
||||||
func (t *table) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
return t.db.NewSnapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
// tableBatch is a wrapper around a database batch that prefixes each key access
|
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||||
// with a pre-configured string.
|
// with a pre-configured string.
|
||||||
type tableBatch struct {
|
type tableBatch struct {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,4 @@ var (
|
||||||
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
|
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
|
||||||
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
|
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
|
||||||
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
|
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
|
||||||
|
|
||||||
slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
|
|
||||||
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
|
|
||||||
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
|
|
||||||
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
|
|
||||||
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1318,12 +1318,16 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
}
|
}
|
||||||
obj := s.stateObjects[addr] // closure for the task runner below
|
obj := s.stateObjects[addr] // closure for the task runner below
|
||||||
workers.Go(func() error {
|
workers.Go(func() error {
|
||||||
obj.updateRoot()
|
if s.db.TrieDB().IsVerkle() {
|
||||||
|
obj.updateTrie()
|
||||||
|
} else {
|
||||||
|
obj.updateRoot()
|
||||||
|
|
||||||
// If witness building is enabled and the state object has a trie,
|
// If witness building is enabled and the state object has a trie,
|
||||||
// gather the witnesses for its specific storage trie
|
// gather the witnesses for its specific storage trie
|
||||||
if s.witness != nil && obj.trie != nil {
|
if s.witness != nil && obj.trie != nil {
|
||||||
s.witness.AddState(obj.trie.Witness())
|
s.witness.AddState(obj.trie.Witness())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -1365,9 +1369,12 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
// Now we're about to start to write changes to the trie. The trie is so far
|
// Now we're about to start to write changes to the trie. The trie is so far
|
||||||
// _untouched_. We can check with the prefetcher, if it can give us a trie
|
// _untouched_. We can check with the prefetcher, if it can give us a trie
|
||||||
// which has the same root, but also has some content loaded into it.
|
// which has the same root, but also has some content loaded into it.
|
||||||
|
//
|
||||||
|
// Don't check prefetcher if verkle trie has been used. In the context of verkle,
|
||||||
|
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
||||||
|
// here could result in losing uncommitted changes from storage.
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
|
if s.prefetcher != nil && (s.trie == nil || !s.trie.IsVerkle()) {
|
||||||
if s.prefetcher != nil {
|
|
||||||
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
||||||
log.Error("Failed to retrieve account pre-fetcher trie")
|
log.Error("Failed to retrieve account pre-fetcher trie")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1445,76 +1452,70 @@ func (s *StateDB) clearJournalAndRefund() {
|
||||||
// of a specific account. It leverages the associated state snapshot for fast
|
// of a specific account. It leverages the associated state snapshot for fast
|
||||||
// storage iteration and constructs trie node deletion markers by creating
|
// storage iteration and constructs trie node deletion markers by creating
|
||||||
// stack trie with iterated slots.
|
// stack trie with iterated slots.
|
||||||
func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
defer iter.Release()
|
defer iter.Release()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
size common.StorageSize
|
|
||||||
nodes = trienode.NewNodeSet(addrHash)
|
nodes = trienode.NewNodeSet(addrHash)
|
||||||
slots = make(map[common.Hash][]byte)
|
slots = make(map[common.Hash][]byte)
|
||||||
)
|
)
|
||||||
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
nodes.AddNode(path, trienode.NewDeleted())
|
nodes.AddNode(path, trienode.NewDeleted())
|
||||||
size += common.StorageSize(len(path))
|
|
||||||
})
|
})
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
slot := common.CopyBytes(iter.Slot())
|
slot := common.CopyBytes(iter.Slot())
|
||||||
if err := iter.Error(); err != nil { // error might occur after Slot function
|
if err := iter.Error(); err != nil { // error might occur after Slot function
|
||||||
return 0, nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
size += common.StorageSize(common.HashLength + len(slot))
|
|
||||||
slots[iter.Hash()] = slot
|
slots[iter.Hash()] = slot
|
||||||
|
|
||||||
if err := stack.Update(iter.Hash().Bytes(), slot); err != nil {
|
if err := stack.Update(iter.Hash().Bytes(), slot); err != nil {
|
||||||
return 0, nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := iter.Error(); err != nil { // error might occur during iteration
|
if err := iter.Error(); err != nil { // error might occur during iteration
|
||||||
return 0, nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if stack.Hash() != root {
|
if stack.Hash() != root {
|
||||||
return 0, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
|
return nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
|
||||||
}
|
}
|
||||||
return size, slots, nodes, nil
|
return slots, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
||||||
// employed when the associated state snapshot is not available. It iterates the
|
// employed when the associated state snapshot is not available. It iterates the
|
||||||
// storage slots along with all internal trie nodes via trie directly.
|
// storage slots along with all internal trie nodes via trie directly.
|
||||||
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
|
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
return nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||||
}
|
}
|
||||||
it, err := tr.NodeIterator(nil)
|
it, err := tr.NodeIterator(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
return nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
size common.StorageSize
|
|
||||||
nodes = trienode.NewNodeSet(addrHash)
|
nodes = trienode.NewNodeSet(addrHash)
|
||||||
slots = make(map[common.Hash][]byte)
|
slots = make(map[common.Hash][]byte)
|
||||||
)
|
)
|
||||||
for it.Next(true) {
|
for it.Next(true) {
|
||||||
if it.Leaf() {
|
if it.Leaf() {
|
||||||
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob())
|
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob())
|
||||||
size += common.StorageSize(common.HashLength + len(it.LeafBlob()))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if it.Hash() == (common.Hash{}) {
|
if it.Hash() == (common.Hash{}) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
size += common.StorageSize(len(it.Path()))
|
|
||||||
nodes.AddNode(it.Path(), trienode.NewDeleted())
|
nodes.AddNode(it.Path(), trienode.NewDeleted())
|
||||||
}
|
}
|
||||||
if err := it.Error(); err != nil {
|
if err := it.Error(); err != nil {
|
||||||
return 0, nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return size, slots, nodes, nil
|
return slots, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteStorage is designed to delete the storage trie of a designated account.
|
// deleteStorage is designed to delete the storage trie of a designated account.
|
||||||
|
|
@ -1523,9 +1524,7 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r
|
||||||
// efficient approach.
|
// efficient approach.
|
||||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
var (
|
var (
|
||||||
start = time.Now()
|
|
||||||
err error
|
err error
|
||||||
size common.StorageSize
|
|
||||||
slots map[common.Hash][]byte
|
slots map[common.Hash][]byte
|
||||||
nodes *trienode.NodeSet
|
nodes *trienode.NodeSet
|
||||||
)
|
)
|
||||||
|
|
@ -1533,24 +1532,14 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
|
||||||
// generated, or it's internally corrupted. Fallback to the slow
|
// generated, or it's internally corrupted. Fallback to the slow
|
||||||
// one just in case.
|
// one just in case.
|
||||||
if s.snap != nil {
|
if s.snap != nil {
|
||||||
size, slots, nodes, err = s.fastDeleteStorage(addrHash, root)
|
slots, nodes, err = s.fastDeleteStorage(addrHash, root)
|
||||||
}
|
}
|
||||||
if s.snap == nil || err != nil {
|
if s.snap == nil || err != nil {
|
||||||
size, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Report the metrics
|
|
||||||
n := int64(len(slots))
|
|
||||||
|
|
||||||
slotDeletionMaxCount.UpdateIfGt(int64(len(slots)))
|
|
||||||
slotDeletionMaxSize.UpdateIfGt(int64(size))
|
|
||||||
|
|
||||||
slotDeletionTimer.UpdateSince(start)
|
|
||||||
slotDeletionCount.Mark(n)
|
|
||||||
slotDeletionSize.Mark(int64(size))
|
|
||||||
|
|
||||||
return slots, nodes, nil
|
return slots, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1629,6 +1618,10 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
|
||||||
// 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)
|
||||||
|
|
||||||
|
// Short circuit if any error occurs within the IntermediateRoot.
|
||||||
|
if s.dbErr != nil {
|
||||||
|
return nil, fmt.Errorf("commit aborted due to database error: %v", s.dbErr)
|
||||||
|
}
|
||||||
// Commit objects to the trie, measuring the elapsed time
|
// Commit objects to the trie, measuring the elapsed time
|
||||||
var (
|
var (
|
||||||
accountTrieNodesUpdated int
|
accountTrieNodesUpdated int
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
All notable changes to the tracing interface will be documented in this file.
|
All notable changes to the tracing interface will be documented in this file.
|
||||||
|
|
||||||
## [Unreleased]
|
## [v1.14.3]
|
||||||
|
|
||||||
There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork.
|
There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork.
|
||||||
|
|
||||||
|
|
@ -76,4 +76,5 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale
|
||||||
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
||||||
|
|
||||||
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.0...master
|
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.0...master
|
||||||
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
||||||
|
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
||||||
|
|
|
||||||
|
|
@ -1116,7 +1116,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
||||||
ExistingCost: func(addr common.Address, nonce uint64) *big.Int {
|
ExistingCost: func(addr common.Address, nonce uint64) *big.Int {
|
||||||
next := p.state.GetNonce(addr)
|
next := p.state.GetNonce(addr)
|
||||||
if uint64(len(p.index[addr])) > nonce-next {
|
if uint64(len(p.index[addr])) > nonce-next {
|
||||||
return p.index[addr][int(tx.Nonce()-next)].costCap.ToBig()
|
return p.index[addr][int(nonce-next)].costCap.ToBig()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
@ -1598,8 +1598,9 @@ func (p *BlobPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool
|
||||||
// Nonce returns the next nonce of an account, with all transactions executable
|
// Nonce returns the next nonce of an account, with all transactions executable
|
||||||
// by the pool already applied on top.
|
// by the pool already applied on top.
|
||||||
func (p *BlobPool) Nonce(addr common.Address) uint64 {
|
func (p *BlobPool) Nonce(addr common.Address) uint64 {
|
||||||
p.lock.RLock()
|
// We need a write lock here, since state.GetNonce might write the cache.
|
||||||
defer p.lock.RUnlock()
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
if txs, ok := p.index[addr]; ok {
|
if txs, ok := p.index[addr]; ok {
|
||||||
return txs[len(txs)-1].nonce + 1
|
return txs[len(txs)-1].nonce + 1
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -1748,7 +1749,7 @@ func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
type accountSet struct {
|
type accountSet struct {
|
||||||
accounts map[common.Address]struct{}
|
accounts map[common.Address]struct{}
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
cache *[]common.Address
|
cache []common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAccountSet creates a new address set with an associated signer for sender
|
// newAccountSet creates a new address set with an associated signer for sender
|
||||||
|
|
@ -1796,20 +1797,14 @@ func (as *accountSet) addTx(tx *types.Transaction) {
|
||||||
// reuse. The returned slice should not be changed!
|
// reuse. The returned slice should not be changed!
|
||||||
func (as *accountSet) flatten() []common.Address {
|
func (as *accountSet) flatten() []common.Address {
|
||||||
if as.cache == nil {
|
if as.cache == nil {
|
||||||
accounts := make([]common.Address, 0, len(as.accounts))
|
as.cache = maps.Keys(as.accounts)
|
||||||
for account := range as.accounts {
|
|
||||||
accounts = append(accounts, account)
|
|
||||||
}
|
|
||||||
as.cache = &accounts
|
|
||||||
}
|
}
|
||||||
return *as.cache
|
return as.cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge adds all addresses from the 'other' set into 'as'.
|
// merge adds all addresses from the 'other' set into 'as'.
|
||||||
func (as *accountSet) merge(other *accountSet) {
|
func (as *accountSet) merge(other *accountSet) {
|
||||||
for addr := range other.accounts {
|
maps.Copy(as.accounts, other.accounts)
|
||||||
as.accounts[addr] = struct{}{}
|
|
||||||
}
|
|
||||||
as.cache = nil
|
as.cache = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ type ValidationOptionsWithState struct {
|
||||||
// rules without duplicating code and running the risk of missed updates.
|
// rules without duplicating code and running the risk of missed updates.
|
||||||
func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error {
|
func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error {
|
||||||
// Ensure the transaction adheres to nonce ordering
|
// Ensure the transaction adheres to nonce ordering
|
||||||
from, err := signer.Sender(tx) // already validated (and cached), but cleaner to check
|
from, err := types.Sender(signer, tx) // already validated (and cached), but cleaner to check
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Transaction sender recovery failed", "err", err)
|
log.Error("Transaction sender recovery failed", "err", err)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -572,8 +572,8 @@ func deriveChainId(v *big.Int) *big.Int {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetUint64((v - 35) / 2)
|
return new(big.Int).SetUint64((v - 35) / 2)
|
||||||
}
|
}
|
||||||
v.Sub(v, big.NewInt(35))
|
vCopy := new(big.Int).Sub(v, big.NewInt(35))
|
||||||
return v.Rsh(v, 1)
|
return vCopy.Rsh(vCopy, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeSigner implements the Signer interface and accepts unprotected transactions
|
// FakeSigner implements the Signer interface and accepts unprotected transactions
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,41 @@ func TestTransactionCoding(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLegacyTransaction_ConsistentV_LargeChainIds(t *testing.T) {
|
||||||
|
chainId := new(big.Int).SetUint64(13317435930671861669)
|
||||||
|
|
||||||
|
txdata := &LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Gas: 1,
|
||||||
|
GasPrice: big.NewInt(2),
|
||||||
|
Data: []byte("abcdef"),
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not generate key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := SignNewTx(key, NewEIP2930Signer(chainId), txdata)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not sign transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy of the initial V value
|
||||||
|
preV, _, _ := tx.RawSignatureValues()
|
||||||
|
preV = new(big.Int).Set(preV)
|
||||||
|
|
||||||
|
if tx.ChainId().Cmp(chainId) != 0 {
|
||||||
|
t.Fatalf("wrong chain id: %v", tx.ChainId())
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _, _ := tx.RawSignatureValues()
|
||||||
|
|
||||||
|
if v.Cmp(preV) != 0 {
|
||||||
|
t.Fatalf("wrong v value: %v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
|
func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
|
||||||
data, err := json.Marshal(tx)
|
data, err := json.Marshal(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -48,6 +49,12 @@ type Withdrawals []*Withdrawal
|
||||||
// Len returns the length of s.
|
// Len returns the length of s.
|
||||||
func (s Withdrawals) Len() int { return len(s) }
|
func (s Withdrawals) Len() int { return len(s) }
|
||||||
|
|
||||||
|
var withdrawalSize = int(reflect.TypeOf(Withdrawal{}).Size())
|
||||||
|
|
||||||
|
func (s Withdrawals) Size() int {
|
||||||
|
return withdrawalSize * len(s)
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
|
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
|
||||||
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
|
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
|
||||||
// constructed by decoding or via public API in this package.
|
// constructed by decoding or via public API in this package.
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
|
|
||||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||||
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
if interpreter.hasher == nil {
|
if interpreter.hasher == nil {
|
||||||
interpreter.hasher = crypto.NewKeccakState()
|
interpreter.hasher = crypto.NewKeccakState()
|
||||||
|
|
@ -554,7 +554,7 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
|
|
||||||
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
v := scope.Stack.peek()
|
v := scope.Stack.peek()
|
||||||
offset := int64(v.Uint64())
|
offset := v.Uint64()
|
||||||
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -647,6 +647,86 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opSwap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap1()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap2()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap3()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap4()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap5()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap6()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap7()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap8()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap9()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap10()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap11()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap12()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap13()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap14()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap15()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
scope.Stack.swap16()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
if interpreter.readOnly {
|
if interpreter.readOnly {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
|
|
@ -655,7 +735,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||||
var (
|
var (
|
||||||
value = scope.Stack.pop()
|
value = scope.Stack.pop()
|
||||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -704,7 +784,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
endowment = scope.Stack.pop()
|
endowment = scope.Stack.pop()
|
||||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||||
salt = scope.Stack.pop()
|
salt = scope.Stack.pop()
|
||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -746,7 +826,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get the arguments from the memory.
|
// Get the arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
if interpreter.readOnly && !value.IsZero() {
|
if interpreter.readOnly && !value.IsZero() {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
|
|
@ -787,7 +867,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
gas += params.CallStipend
|
||||||
|
|
@ -823,7 +903,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -855,7 +935,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
toAddr := common.Address(addr.Bytes20())
|
toAddr := common.Address(addr.Bytes20())
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -879,14 +959,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||||
|
|
||||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
return ret, errStopToken
|
return ret, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
|
|
||||||
|
|
@ -960,7 +1040,7 @@ func makeLog(size int) executionFunc {
|
||||||
topics[i] = addr.Bytes32()
|
topics[i] = addr.Bytes32()
|
||||||
}
|
}
|
||||||
|
|
||||||
d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64())
|
||||||
interpreter.evm.StateDB.AddLog(&types.Log{
|
interpreter.evm.StateDB.AddLog(&types.Log{
|
||||||
Address: scope.Contract.Address(),
|
Address: scope.Contract.Address(),
|
||||||
Topics: topics,
|
Topics: topics,
|
||||||
|
|
@ -1019,14 +1099,3 @@ func makeDup(size int64) executionFunc {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make swap instruction function
|
|
||||||
func makeSwap(size int64) executionFunc {
|
|
||||||
// switch n + 1 otherwise n would be swapped with n
|
|
||||||
size++
|
|
||||||
|
|
||||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
scope.Stack.swap(int(size))
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -905,97 +905,97 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
maxStack: maxDupStack(16),
|
maxStack: maxDupStack(16),
|
||||||
},
|
},
|
||||||
SWAP1: {
|
SWAP1: {
|
||||||
execute: makeSwap(1),
|
execute: opSwap1,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(2),
|
minStack: minSwapStack(2),
|
||||||
maxStack: maxSwapStack(2),
|
maxStack: maxSwapStack(2),
|
||||||
},
|
},
|
||||||
SWAP2: {
|
SWAP2: {
|
||||||
execute: makeSwap(2),
|
execute: opSwap2,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(3),
|
minStack: minSwapStack(3),
|
||||||
maxStack: maxSwapStack(3),
|
maxStack: maxSwapStack(3),
|
||||||
},
|
},
|
||||||
SWAP3: {
|
SWAP3: {
|
||||||
execute: makeSwap(3),
|
execute: opSwap3,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(4),
|
minStack: minSwapStack(4),
|
||||||
maxStack: maxSwapStack(4),
|
maxStack: maxSwapStack(4),
|
||||||
},
|
},
|
||||||
SWAP4: {
|
SWAP4: {
|
||||||
execute: makeSwap(4),
|
execute: opSwap4,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(5),
|
minStack: minSwapStack(5),
|
||||||
maxStack: maxSwapStack(5),
|
maxStack: maxSwapStack(5),
|
||||||
},
|
},
|
||||||
SWAP5: {
|
SWAP5: {
|
||||||
execute: makeSwap(5),
|
execute: opSwap5,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(6),
|
minStack: minSwapStack(6),
|
||||||
maxStack: maxSwapStack(6),
|
maxStack: maxSwapStack(6),
|
||||||
},
|
},
|
||||||
SWAP6: {
|
SWAP6: {
|
||||||
execute: makeSwap(6),
|
execute: opSwap6,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(7),
|
minStack: minSwapStack(7),
|
||||||
maxStack: maxSwapStack(7),
|
maxStack: maxSwapStack(7),
|
||||||
},
|
},
|
||||||
SWAP7: {
|
SWAP7: {
|
||||||
execute: makeSwap(7),
|
execute: opSwap7,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(8),
|
minStack: minSwapStack(8),
|
||||||
maxStack: maxSwapStack(8),
|
maxStack: maxSwapStack(8),
|
||||||
},
|
},
|
||||||
SWAP8: {
|
SWAP8: {
|
||||||
execute: makeSwap(8),
|
execute: opSwap8,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(9),
|
minStack: minSwapStack(9),
|
||||||
maxStack: maxSwapStack(9),
|
maxStack: maxSwapStack(9),
|
||||||
},
|
},
|
||||||
SWAP9: {
|
SWAP9: {
|
||||||
execute: makeSwap(9),
|
execute: opSwap9,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(10),
|
minStack: minSwapStack(10),
|
||||||
maxStack: maxSwapStack(10),
|
maxStack: maxSwapStack(10),
|
||||||
},
|
},
|
||||||
SWAP10: {
|
SWAP10: {
|
||||||
execute: makeSwap(10),
|
execute: opSwap10,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(11),
|
minStack: minSwapStack(11),
|
||||||
maxStack: maxSwapStack(11),
|
maxStack: maxSwapStack(11),
|
||||||
},
|
},
|
||||||
SWAP11: {
|
SWAP11: {
|
||||||
execute: makeSwap(11),
|
execute: opSwap11,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(12),
|
minStack: minSwapStack(12),
|
||||||
maxStack: maxSwapStack(12),
|
maxStack: maxSwapStack(12),
|
||||||
},
|
},
|
||||||
SWAP12: {
|
SWAP12: {
|
||||||
execute: makeSwap(12),
|
execute: opSwap12,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(13),
|
minStack: minSwapStack(13),
|
||||||
maxStack: maxSwapStack(13),
|
maxStack: maxSwapStack(13),
|
||||||
},
|
},
|
||||||
SWAP13: {
|
SWAP13: {
|
||||||
execute: makeSwap(13),
|
execute: opSwap13,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(14),
|
minStack: minSwapStack(14),
|
||||||
maxStack: maxSwapStack(14),
|
maxStack: maxSwapStack(14),
|
||||||
},
|
},
|
||||||
SWAP14: {
|
SWAP14: {
|
||||||
execute: makeSwap(14),
|
execute: opSwap14,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(15),
|
minStack: minSwapStack(15),
|
||||||
maxStack: maxSwapStack(15),
|
maxStack: maxSwapStack(15),
|
||||||
},
|
},
|
||||||
SWAP15: {
|
SWAP15: {
|
||||||
execute: makeSwap(15),
|
execute: opSwap15,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(16),
|
minStack: minSwapStack(16),
|
||||||
maxStack: maxSwapStack(16),
|
maxStack: maxSwapStack(16),
|
||||||
},
|
},
|
||||||
SWAP16: {
|
SWAP16: {
|
||||||
execute: makeSwap(16),
|
execute: opSwap16,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minSwapStack(17),
|
minStack: minSwapStack(17),
|
||||||
maxStack: maxSwapStack(17),
|
maxStack: maxSwapStack(17),
|
||||||
|
|
|
||||||
|
|
@ -67,32 +67,25 @@ func (m *Memory) Resize(size uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCopy returns offset + size as a new slice
|
// GetCopy returns offset + size as a new slice
|
||||||
func (m *Memory) GetCopy(offset, size int64) (cpy []byte) {
|
func (m *Memory) GetCopy(offset, size uint64) (cpy []byte) {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.store) > int(offset) {
|
// memory is always resized before being accessed, no need to check bounds
|
||||||
cpy = make([]byte, size)
|
cpy = make([]byte, size)
|
||||||
copy(cpy, m.store[offset:offset+size])
|
copy(cpy, m.store[offset:offset+size])
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPtr returns the offset + size
|
// GetPtr returns the offset + size
|
||||||
func (m *Memory) GetPtr(offset, size int64) []byte {
|
func (m *Memory) GetPtr(offset, size uint64) []byte {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.store) > int(offset) {
|
// memory is always resized before being accessed, no need to check bounds
|
||||||
return m.store[offset : offset+size]
|
return m.store[offset : offset+size]
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the length of the backing slice
|
// Len returns the length of the backing slice
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import (
|
||||||
|
|
||||||
// force-load js tracers to trigger registration
|
// force-load js tracers to trigger registration
|
||||||
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
|
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefaults(t *testing.T) {
|
func TestDefaults(t *testing.T) {
|
||||||
|
|
@ -221,6 +220,35 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
|
||||||
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
|
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEVM_SWAP1(b *testing.B) {
|
||||||
|
// returns a contract that does n swaps (SWAP1)
|
||||||
|
swapContract := func(n uint64) []byte {
|
||||||
|
contract := []byte{
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
}
|
||||||
|
for i := uint64(0); i < n; i++ {
|
||||||
|
contract = append(contract, byte(vm.SWAP1))
|
||||||
|
}
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
contractAddr := common.BytesToAddress([]byte("contract"))
|
||||||
|
|
||||||
|
b.Run("10k", func(b *testing.B) {
|
||||||
|
contractCode := swapContract(10_000)
|
||||||
|
state.SetCode(contractAddr, contractCode)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
|
func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
|
||||||
header := types.Header{
|
header := types.Header{
|
||||||
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
|
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
|
||||||
|
|
@ -356,13 +384,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
Tracer: tracer.Hooks,
|
Tracer: tracer.Hooks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
destination := common.BytesToAddress([]byte("contract"))
|
||||||
var (
|
|
||||||
destination = common.BytesToAddress([]byte("contract"))
|
|
||||||
vmenv = NewEnv(cfg)
|
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
|
||||||
)
|
|
||||||
|
|
||||||
cfg.State.CreateAccount(destination)
|
cfg.State.CreateAccount(destination)
|
||||||
|
|
||||||
eoa := common.HexToAddress("E0")
|
eoa := common.HexToAddress("E0")
|
||||||
|
|
@ -384,16 +406,13 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
//cfg.State.CreateAccount(cfg.Origin)
|
//cfg.State.CreateAccount(cfg.Origin)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
cfg.State.SetCode(destination, code)
|
cfg.State.SetCode(destination, code)
|
||||||
|
Call(destination, nil, cfg)
|
||||||
// nolint: errcheck
|
|
||||||
vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value), nil)
|
|
||||||
|
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
// nolint: errcheck
|
Call(destination, nil, cfg)
|
||||||
vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value), nil)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ var stackPool = sync.Pool{
|
||||||
|
|
||||||
// Stack is an object for basic stack operations. Items popped to the stack are
|
// Stack is an object for basic stack operations. Items popped to the stack are
|
||||||
// expected to be changed and modified. stack does not take care of adding newly
|
// expected to be changed and modified. stack does not take care of adding newly
|
||||||
// initialised objects.
|
// initialized objects.
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
data []uint256.Int
|
data []uint256.Int
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +65,53 @@ func (st *Stack) len() int {
|
||||||
return len(st.data)
|
return len(st.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) swap(n int) {
|
func (st *Stack) swap1() {
|
||||||
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
|
st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap2() {
|
||||||
|
st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap3() {
|
||||||
|
st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap4() {
|
||||||
|
st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap5() {
|
||||||
|
st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap6() {
|
||||||
|
st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap7() {
|
||||||
|
st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap8() {
|
||||||
|
st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap9() {
|
||||||
|
st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap10() {
|
||||||
|
st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap11() {
|
||||||
|
st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap12() {
|
||||||
|
st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap13() {
|
||||||
|
st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap14() {
|
||||||
|
st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap15() {
|
||||||
|
st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16]
|
||||||
|
}
|
||||||
|
func (st *Stack) swap16() {
|
||||||
|
st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) dup(n int) {
|
func (st *Stack) dup(n int) {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const RecoveryIDOffset = 64
|
||||||
const DigestLength = 32
|
const DigestLength = 32
|
||||||
|
|
||||||
var (
|
var (
|
||||||
secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
|
secp256k1N = S256().Params().N
|
||||||
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
|
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,10 +88,7 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
return nil, errors.New("invalid private key")
|
return nil, errors.New("invalid private key")
|
||||||
}
|
}
|
||||||
defer priv.Zero()
|
defer priv.Zero()
|
||||||
sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
|
sig := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Convert to Ethereum signature format with 'recovery id' v at the end.
|
// Convert to Ethereum signature format with 'recovery id' v at the end.
|
||||||
v := sig[0] - 27
|
v := sig[0] - 27
|
||||||
copy(sig, sig[1:])
|
copy(sig, sig[1:])
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
- The `bor dumpconfig` sub-command prints the default configurations, in the TOML format, on the terminal. One can `pipe (>)` this to a file (say `config.toml`) and use it to start bor.
|
- The `bor dumpconfig` sub-command prints the default configurations, in the TOML format, on the terminal. One can `pipe (>)` this to a file (say `config.toml`) and use it to start bor.
|
||||||
|
|
||||||
- A toml file now can be used instead of flags and can contain all configuration for the node to run. To simply run bor with a configuration file, the following command can be used.
|
- A toml file now can be used instead of flags and can contain all configurations for the node to run. To simply run bor with a configuration file, the following command can be used.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bor server --config <path_to_config.toml>
|
$ bor server --config <path_to_config.toml>
|
||||||
|
|
|
||||||
|
|
@ -179,9 +179,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gpoParams := config.GPO
|
gpoParams := config.GPO
|
||||||
if gpoParams.Default == nil {
|
|
||||||
gpoParams.Default = config.Miner.GasPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the chain config with provided settings.
|
// Override the chain config with provided settings.
|
||||||
var overrides core.ChainOverrides
|
var overrides core.ChainOverrides
|
||||||
|
|
@ -263,7 +260,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
|
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
|
// 1.14.8: NewOracle function definition was changed to accept (startPrice *big.Int) param.
|
||||||
|
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams, config.Miner.GasPrice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -317,6 +315,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
eth.miner = miner.New(eth, &config.Miner, eth.blockchain.Config(), eth.EventMux(), eth.engine, eth.isLocalBlock)
|
eth.miner = miner.New(eth, &config.Miner, eth.blockchain.Config(), eth.EventMux(), eth.engine, eth.isLocalBlock)
|
||||||
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
||||||
|
|
||||||
|
eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
|
||||||
|
if eth.APIBackend.allowUnprotectedTxs {
|
||||||
|
log.Info("Unprotected transactions allowed")
|
||||||
|
}
|
||||||
|
// 1.14.8: NewOracle function definition was changed to accept (startPrice *big.Int) param.
|
||||||
|
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
|
||||||
|
|
||||||
// Setup DNS discovery iterators.
|
// Setup DNS discovery iterators.
|
||||||
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
|
dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
|
||||||
eth.ethDialCandidates, err = dnsclient.NewIterator(eth.config.EthDiscoveryURLs...)
|
eth.ethDialCandidates, err = dnsclient.NewIterator(eth.config.EthDiscoveryURLs...)
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
|
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
|
||||||
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not available in database"))
|
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not available in database"))
|
||||||
} else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash {
|
} else if rawdb.ReadCanonicalHash(api.eth.ChainDb(), finalBlock.NumberU64()) != update.FinalizedBlockHash {
|
||||||
log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", update.HeadBlockHash)
|
log.Warn("Final block not in canonical chain", "number", finalBlock.NumberU64(), "hash", update.FinalizedBlockHash)
|
||||||
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not in canonical chain"))
|
return engine.STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not in canonical chain"))
|
||||||
}
|
}
|
||||||
// Set the finalized block
|
// Set the finalized block
|
||||||
|
|
@ -544,7 +544,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
bgu = strconv.Itoa(int(*params.BlobGasUsed))
|
bgu = strconv.Itoa(int(*params.BlobGasUsed))
|
||||||
}
|
}
|
||||||
ebg := "nil"
|
ebg := "nil"
|
||||||
if params.BlobGasUsed != nil {
|
if params.ExcessBlobGas != nil {
|
||||||
ebg = strconv.Itoa(int(*params.ExcessBlobGas))
|
ebg = strconv.Itoa(int(*params.ExcessBlobGas))
|
||||||
}
|
}
|
||||||
log.Warn("Invalid NewPayload params",
|
log.Warn("Invalid NewPayload params",
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
|
||||||
return errors.New("parent not found")
|
return errors.New("parent not found")
|
||||||
}
|
}
|
||||||
withdrawals := c.withdrawals.gatherPending(10)
|
withdrawals := c.withdrawals.gatherPending(10)
|
||||||
return c.sealBlock(withdrawals, parent.Time+uint64(adjustment))
|
return c.sealBlock(withdrawals, parent.Time+uint64(adjustment/time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,8 @@ func (b *beaconBackfiller) resume() {
|
||||||
func (b *beaconBackfiller) setMode(mode SyncMode) {
|
func (b *beaconBackfiller) setMode(mode SyncMode) {
|
||||||
// Update the old sync mode and track if it was changed
|
// Update the old sync mode and track if it was changed
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
updated := b.syncMode != mode
|
oldMode := b.syncMode
|
||||||
|
updated := oldMode != mode
|
||||||
filling := b.filling
|
filling := b.filling
|
||||||
b.syncMode = mode
|
b.syncMode = mode
|
||||||
b.lock.Unlock()
|
b.lock.Unlock()
|
||||||
|
|
@ -133,8 +134,7 @@ func (b *beaconBackfiller) setMode(mode SyncMode) {
|
||||||
if !updated || !filling {
|
if !updated || !filling {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Error("Downloader sync mode changed mid-run", "old", oldMode.String(), "new", mode.String())
|
||||||
log.Error("Downloader sync mode changed mid-run", "old", mode.String(), "new", mode.String())
|
|
||||||
b.suspend()
|
b.suspend()
|
||||||
b.resume()
|
b.resume()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ func (q *queue) Results(block bool) []*fetchResult {
|
||||||
for _, tx := range result.Transactions {
|
for _, tx := range result.Transactions {
|
||||||
size += common.StorageSize(tx.Size())
|
size += common.StorageSize(tx.Size())
|
||||||
}
|
}
|
||||||
|
size += common.StorageSize(result.Withdrawals.Size())
|
||||||
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
|
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
|
||||||
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
|
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -296,21 +296,6 @@ func (mr *MockDatabaseMockRecorder) NewIterator(arg0, arg1 interface{}) *gomock.
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewIterator", reflect.TypeOf((*MockDatabase)(nil).NewIterator), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewIterator", reflect.TypeOf((*MockDatabase)(nil).NewIterator), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot mocks base method.
|
|
||||||
func (m *MockDatabase) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "NewSnapshot")
|
|
||||||
ret0, _ := ret[0].(ethdb.Snapshot)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSnapshot indicates an expected call of NewSnapshot.
|
|
||||||
func (mr *MockDatabaseMockRecorder) NewSnapshot() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSnapshot", reflect.TypeOf((*MockDatabase)(nil).NewSnapshot))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put mocks base method.
|
// Put mocks base method.
|
||||||
func (m *MockDatabase) Put(arg0, arg1 []byte) error {
|
func (m *MockDatabase) Put(arg0, arg1 []byte) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ func TestFeeHistory(t *testing.T) {
|
||||||
MaxHeaderHistory: c.maxHeader,
|
MaxHeaderHistory: c.maxHeader,
|
||||||
MaxBlockHistory: c.maxBlock,
|
MaxBlockHistory: c.maxBlock,
|
||||||
}
|
}
|
||||||
backend := newTestBackend(t, big.NewInt(16), nil, c.pending)
|
backend := newTestBackend(t, big.NewInt(16), big.NewInt(28), c.pending)
|
||||||
oracle := NewOracle(backend, config)
|
oracle := NewOracle(backend, config, nil)
|
||||||
|
|
||||||
first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
|
first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
|
||||||
backend.teardown()
|
backend.teardown()
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ type Config struct {
|
||||||
Percentile int
|
Percentile int
|
||||||
MaxHeaderHistory uint64
|
MaxHeaderHistory uint64
|
||||||
MaxBlockHistory uint64
|
MaxBlockHistory uint64
|
||||||
Default *big.Int `toml:",omitempty"`
|
|
||||||
MaxPrice *big.Int `toml:",omitempty"`
|
MaxPrice *big.Int `toml:",omitempty"`
|
||||||
IgnorePrice *big.Int `toml:",omitempty"`
|
IgnorePrice *big.Int `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +78,7 @@ type Oracle struct {
|
||||||
|
|
||||||
// NewOracle returns a new gasprice oracle which can recommend suitable
|
// NewOracle returns a new gasprice oracle which can recommend suitable
|
||||||
// gasprice for newly created transaction.
|
// gasprice for newly created transaction.
|
||||||
func NewOracle(backend OracleBackend, params Config) *Oracle {
|
func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracle {
|
||||||
blocks := params.Blocks
|
blocks := params.Blocks
|
||||||
if blocks < 1 {
|
if blocks < 1 {
|
||||||
blocks = 1
|
blocks = 1
|
||||||
|
|
@ -121,6 +120,9 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
||||||
maxBlockHistory = 1
|
maxBlockHistory = 1
|
||||||
log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
|
log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
|
||||||
}
|
}
|
||||||
|
if startPrice == nil {
|
||||||
|
startPrice = new(big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
cache := lru.NewCache[cacheKey, processedFees](2048)
|
cache := lru.NewCache[cacheKey, processedFees](2048)
|
||||||
headEvent := make(chan core.ChainHeadEvent, 1)
|
headEvent := make(chan core.ChainHeadEvent, 1)
|
||||||
|
|
@ -139,7 +141,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
||||||
|
|
||||||
return &Oracle{
|
return &Oracle{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
lastPrice: params.Default,
|
lastPrice: startPrice,
|
||||||
maxPrice: maxPrice,
|
maxPrice: maxPrice,
|
||||||
ignorePrice: ignorePrice,
|
ignorePrice: ignorePrice,
|
||||||
checkBlocks: blocks,
|
checkBlocks: blocks,
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,6 @@ func TestSuggestTipCap(t *testing.T) {
|
||||||
config := Config{
|
config := Config{
|
||||||
Blocks: 3,
|
Blocks: 3,
|
||||||
Percentile: 60,
|
Percentile: 60,
|
||||||
Default: big.NewInt(params.GWei),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
|
|
@ -232,7 +231,7 @@ func TestSuggestTipCap(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
backend := newTestBackend(t, c.fork, nil, false)
|
backend := newTestBackend(t, c.fork, nil, false)
|
||||||
oracle := NewOracle(backend, config)
|
oracle := NewOracle(backend, config, big.NewInt(params.GWei))
|
||||||
|
|
||||||
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
|
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
|
||||||
got, err := oracle.SuggestTipCap(context.Background())
|
got, err := oracle.SuggestTipCap(context.Background())
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ type genTrie interface {
|
||||||
// update inserts the state item into generator trie.
|
// update inserts the state item into generator trie.
|
||||||
update(key, value []byte) error
|
update(key, value []byte) error
|
||||||
|
|
||||||
|
// delete removes the state item from the generator trie.
|
||||||
|
delete(key []byte) error
|
||||||
|
|
||||||
// commit flushes the right boundary nodes if complete flag is true. This
|
// commit flushes the right boundary nodes if complete flag is true. This
|
||||||
// function must be called before flushing the associated database batch.
|
// function must be called before flushing the associated database batch.
|
||||||
commit(complete bool) common.Hash
|
commit(complete bool) common.Hash
|
||||||
|
|
@ -113,7 +116,7 @@ func (t *pathTrie) onTrieNode(path []byte, hash common.Hash, blob []byte) {
|
||||||
// removed because it's a sibling of the nodes we want to commit, not
|
// removed because it's a sibling of the nodes we want to commit, not
|
||||||
// the parent or ancestor.
|
// the parent or ancestor.
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
t.delete(path[:i], false)
|
t.deleteNode(path[:i], false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -136,7 +139,7 @@ func (t *pathTrie) onTrieNode(path []byte, hash common.Hash, blob []byte) {
|
||||||
// byte key. In either case, no gaps will be left in the path.
|
// byte key. In either case, no gaps will be left in the path.
|
||||||
if t.last != nil && bytes.HasPrefix(t.last, path) && len(t.last)-len(path) > 1 {
|
if t.last != nil && bytes.HasPrefix(t.last, path) && len(t.last)-len(path) > 1 {
|
||||||
for i := len(path) + 1; i < len(t.last); i++ {
|
for i := len(path) + 1; i < len(t.last); i++ {
|
||||||
t.delete(t.last[:i], true)
|
t.deleteNode(t.last[:i], true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.write(path, blob)
|
t.write(path, blob)
|
||||||
|
|
@ -192,8 +195,8 @@ func (t *pathTrie) deleteStorageNode(path []byte, inner bool) {
|
||||||
rawdb.DeleteStorageTrieNode(t.batch, t.owner, path)
|
rawdb.DeleteStorageTrieNode(t.batch, t.owner, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete commits the node deletion to provided database batch in path mode.
|
// deleteNode commits the node deletion to provided database batch in path mode.
|
||||||
func (t *pathTrie) delete(path []byte, inner bool) {
|
func (t *pathTrie) deleteNode(path []byte, inner bool) {
|
||||||
if t.owner == (common.Hash{}) {
|
if t.owner == (common.Hash{}) {
|
||||||
t.deleteAccountNode(path, inner)
|
t.deleteAccountNode(path, inner)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -207,6 +210,34 @@ func (t *pathTrie) update(key, value []byte) error {
|
||||||
return t.tr.Update(key, value)
|
return t.tr.Update(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete implements genTrie interface, deleting the item from the stack trie.
|
||||||
|
func (t *pathTrie) delete(key []byte) error {
|
||||||
|
// Commit the trie since the right boundary is incomplete because
|
||||||
|
// of the deleted item. This will implicitly discard the last inserted
|
||||||
|
// item and clean some ancestor trie nodes of the last committed
|
||||||
|
// item in the database.
|
||||||
|
t.commit(false)
|
||||||
|
|
||||||
|
// Reset the trie and all the internal trackers
|
||||||
|
t.first = nil
|
||||||
|
t.last = nil
|
||||||
|
t.tr.Reset()
|
||||||
|
|
||||||
|
// Explicitly mark the left boundary as incomplete, as the left-side
|
||||||
|
// item of the next one has been deleted. Be aware that the next item
|
||||||
|
// to be inserted will be ignored from committing as well as it's on
|
||||||
|
// the left boundary.
|
||||||
|
t.skipLeftBoundary = true
|
||||||
|
|
||||||
|
// Explicitly delete the potential leftover nodes on the specific
|
||||||
|
// path from the database.
|
||||||
|
tkey := t.tr.TrieKey(key)
|
||||||
|
for i := 0; i <= len(tkey); i++ {
|
||||||
|
t.deleteNode(tkey[:i], false)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// commit implements genTrie interface, flushing the right boundary if it's
|
// commit implements genTrie interface, flushing the right boundary if it's
|
||||||
// considered as complete. Otherwise, the nodes on the right boundary are
|
// considered as complete. Otherwise, the nodes on the right boundary are
|
||||||
// discarded and cleaned up.
|
// discarded and cleaned up.
|
||||||
|
|
@ -255,7 +286,7 @@ func (t *pathTrie) commit(complete bool) common.Hash {
|
||||||
// with no issues as they are actually complete. Also, from a database
|
// with no issues as they are actually complete. Also, from a database
|
||||||
// perspective, first deleting and then rewriting is a valid data update.
|
// perspective, first deleting and then rewriting is a valid data update.
|
||||||
for i := 0; i < len(t.last); i++ {
|
for i := 0; i < len(t.last); i++ {
|
||||||
t.delete(t.last[:i], false)
|
t.deleteNode(t.last[:i], false)
|
||||||
}
|
}
|
||||||
return common.Hash{} // the hash is meaningless for incomplete commit
|
return common.Hash{} // the hash is meaningless for incomplete commit
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +309,9 @@ func (t *hashTrie) update(key, value []byte) error {
|
||||||
return t.tr.Update(key, value)
|
return t.tr.Update(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete implements genTrie interface, ignoring the state item for deleting.
|
||||||
|
func (t *hashTrie) delete(key []byte) error { return nil }
|
||||||
|
|
||||||
// commit implements genTrie interface, committing the nodes on right boundary.
|
// commit implements genTrie interface, committing the nodes on right boundary.
|
||||||
func (t *hashTrie) commit(complete bool) common.Hash {
|
func (t *hashTrie) commit(complete bool) common.Hash {
|
||||||
if !complete {
|
if !complete {
|
||||||
|
|
|
||||||
|
|
@ -551,3 +551,145 @@ func TestTinyPartialTree(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrieDelete(t *testing.T) {
|
||||||
|
var entries []*kv
|
||||||
|
for i := 0; i < 1024; i++ {
|
||||||
|
entries = append(entries, &kv{
|
||||||
|
k: testrand.Bytes(32),
|
||||||
|
v: testrand.Bytes(32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
slices.SortFunc(entries, (*kv).cmp)
|
||||||
|
|
||||||
|
nodes := make(map[string]common.Hash)
|
||||||
|
tr := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
nodes[string(path)] = hash
|
||||||
|
})
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
tr.Update(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
tr.Hash()
|
||||||
|
|
||||||
|
check := func(index []int) {
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
batch = db.NewBatch()
|
||||||
|
marks = map[int]struct{}{}
|
||||||
|
neighbors = map[int]struct{}{}
|
||||||
|
)
|
||||||
|
for _, n := range index {
|
||||||
|
marks[n] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, n := range index {
|
||||||
|
if n != 0 {
|
||||||
|
if _, ok := marks[n-1]; !ok {
|
||||||
|
neighbors[n-1] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != len(entries)-1 {
|
||||||
|
if _, ok := neighbors[n+1]; !ok {
|
||||||
|
neighbors[n+1] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write the junk nodes as the dangling
|
||||||
|
var injects []string
|
||||||
|
for _, n := range index {
|
||||||
|
nibbles := byteToHex(entries[n].k)
|
||||||
|
for i := 0; i <= len(nibbles); i++ {
|
||||||
|
injects = append(injects, string(nibbles[:i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, path := range injects {
|
||||||
|
rawdb.WriteAccountTrieNode(db, []byte(path), testrand.Bytes(32))
|
||||||
|
}
|
||||||
|
tr := newPathTrie(common.Hash{}, false, db, batch)
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
if _, ok := marks[i]; ok {
|
||||||
|
tr.delete(entries[i].k)
|
||||||
|
} else {
|
||||||
|
tr.update(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.commit(true)
|
||||||
|
|
||||||
|
r := newBatchReplay()
|
||||||
|
batch.Replay(r)
|
||||||
|
batch.Write()
|
||||||
|
|
||||||
|
for _, path := range injects {
|
||||||
|
if rawdb.HasAccountTrieNode(db, []byte(path)) {
|
||||||
|
t.Fatalf("Unexpected leftover node %v", []byte(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all the written nodes match with the complete tree
|
||||||
|
set := make(map[string]common.Hash)
|
||||||
|
for path, hash := range r.modifies() {
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n, ok := nodes[path]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Unexpected trie node: %v", []byte(path))
|
||||||
|
}
|
||||||
|
if n != hash {
|
||||||
|
t.Fatalf("Unexpected trie node content: %v, want: %x, got: %x", []byte(path), n, hash)
|
||||||
|
}
|
||||||
|
set[path] = hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all the missing nodes either on the deleted path, or
|
||||||
|
// on the neighbor paths.
|
||||||
|
isMissing := func(path []byte) bool {
|
||||||
|
for n := range marks {
|
||||||
|
key := byteToHex(entries[n].k)
|
||||||
|
if bytes.HasPrefix(key, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for n := range neighbors {
|
||||||
|
key := byteToHex(entries[n].k)
|
||||||
|
if bytes.HasPrefix(key, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for path := range nodes {
|
||||||
|
if _, ok := set[path]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !isMissing([]byte(path)) {
|
||||||
|
t.Fatalf("Missing node %v", []byte(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cases = []struct {
|
||||||
|
index []int
|
||||||
|
}{
|
||||||
|
// delete the first
|
||||||
|
{[]int{0}},
|
||||||
|
|
||||||
|
// delete the last
|
||||||
|
{[]int{len(entries) - 1}},
|
||||||
|
|
||||||
|
// delete the first two
|
||||||
|
{[]int{0, 1}},
|
||||||
|
|
||||||
|
// delete the last two
|
||||||
|
{[]int{len(entries) - 2, len(entries) - 1}},
|
||||||
|
|
||||||
|
{[]int{
|
||||||
|
0, 2, 4, 6,
|
||||||
|
len(entries) - 1,
|
||||||
|
len(entries) - 3,
|
||||||
|
len(entries) - 5,
|
||||||
|
len(entries) - 7,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
check(c.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2575,14 +2575,21 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
|
||||||
slim := types.SlimAccountRLP(*res.accounts[i])
|
slim := types.SlimAccountRLP(*res.accounts[i])
|
||||||
rawdb.WriteAccountSnapshot(batch, hash, slim)
|
rawdb.WriteAccountSnapshot(batch, hash, slim)
|
||||||
|
|
||||||
// If the task is complete, drop it into the stack trie to generate
|
|
||||||
// account trie nodes for it
|
|
||||||
if !task.needHeal[i] {
|
if !task.needHeal[i] {
|
||||||
|
// If the storage task is complete, drop it into the stack trie
|
||||||
|
// to generate account trie nodes for it
|
||||||
full, err := types.FullAccountRLP(slim) // TODO(karalabe): Slim parsing can be omitted
|
full, err := types.FullAccountRLP(slim) // TODO(karalabe): Slim parsing can be omitted
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // Really shouldn't ever happen
|
panic(err) // Really shouldn't ever happen
|
||||||
}
|
}
|
||||||
task.genTrie.update(hash[:], full)
|
task.genTrie.update(hash[:], full)
|
||||||
|
} else {
|
||||||
|
// If the storage task is incomplete, explicitly delete the corresponding
|
||||||
|
// account item from the account trie to ensure that all nodes along the
|
||||||
|
// path to the incomplete storage trie are cleaned up.
|
||||||
|
if err := task.genTrie.delete(hash[:]); err != nil {
|
||||||
|
panic(err) // Really shouldn't ever happen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Flush anything written just now and update the stats
|
// Flush anything written just now and update the stats
|
||||||
|
|
|
||||||
|
|
@ -985,7 +985,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
byte(vm.PUSH1), 00,
|
byte(vm.PUSH1), 00,
|
||||||
byte(vm.RETURN),
|
byte(vm.RETURN),
|
||||||
}),
|
}),
|
||||||
StateDiff: &map[common.Hash]common.Hash{
|
StateDiff: map[common.Hash]common.Hash{
|
||||||
common.HexToHash("0x03"): common.HexToHash("0x11"),
|
common.HexToHash("0x03"): common.HexToHash("0x11"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1046,9 +1046,9 @@ func newAccounts(n int) (accounts []Account) {
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRPCBalance(balance *big.Int) **hexutil.Big {
|
func newRPCBalance(balance *big.Int) *hexutil.Big {
|
||||||
rpcBalance := (*hexutil.Big)(balance)
|
rpcBalance := (*hexutil.Big)(balance)
|
||||||
return &rpcBalance
|
return rpcBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRPCBytes(bytes []byte) *hexutil.Bytes {
|
func newRPCBytes(bytes []byte) *hexutil.Bytes {
|
||||||
|
|
@ -1056,7 +1056,7 @@ func newRPCBytes(bytes []byte) *hexutil.Bytes {
|
||||||
return &rpcBytes
|
return &rpcBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.Hash {
|
func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash {
|
||||||
if len(keys) != len(vals) {
|
if len(keys) != len(vals) {
|
||||||
panic("invalid input")
|
panic("invalid input")
|
||||||
}
|
}
|
||||||
|
|
@ -1065,8 +1065,7 @@ func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.H
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
m[keys[i]] = vals[i]
|
m[keys[i]] = vals[i]
|
||||||
}
|
}
|
||||||
|
return m
|
||||||
return &m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:typecheck
|
// nolint:typecheck
|
||||||
|
|
|
||||||
|
|
@ -393,8 +393,7 @@ func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) {
|
||||||
if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil {
|
if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if _, ok := version.SetString(ver, 0); !ok {
|
||||||
if _, ok := version.SetString(ver, 10); !ok {
|
|
||||||
return nil, fmt.Errorf("invalid net_version result %q", ver)
|
return nil, fmt.Errorf("invalid net_version result %q", ver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ func TestAdjustTime(t *testing.T) {
|
||||||
block2, _ := client.BlockByNumber(context.Background(), nil)
|
block2, _ := client.BlockByNumber(context.Background(), nil)
|
||||||
prevTime := block1.Time()
|
prevTime := block1.Time()
|
||||||
newTime := block2.Time()
|
newTime := block2.Time()
|
||||||
if newTime-prevTime != uint64(time.Minute) {
|
if newTime-prevTime != 60 {
|
||||||
t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime)
|
t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ type KeyValueStore interface {
|
||||||
Batcher
|
Batcher
|
||||||
Iteratee
|
Iteratee
|
||||||
Compacter
|
Compacter
|
||||||
Snapshotter
|
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +206,5 @@ type Database interface {
|
||||||
Iteratee
|
Iteratee
|
||||||
Stater
|
Stater
|
||||||
Compacter
|
Compacter
|
||||||
Snapshotter
|
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -330,79 +330,6 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Snapshot", func(t *testing.T) {
|
|
||||||
db := New()
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
initial := map[string]string{
|
|
||||||
"k1": "v1", "k2": "v2", "k3": "", "k4": "",
|
|
||||||
}
|
|
||||||
for k, v := range initial {
|
|
||||||
db.Put([]byte(k), []byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot, err := db.NewSnapshot()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range initial {
|
|
||||||
got, err := snapshot.Get([]byte(k))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(got, []byte(v)) {
|
|
||||||
t.Fatalf("Unexpected value want: %v, got %v", v, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush more modifications into the database, ensure the snapshot
|
|
||||||
// isn't affected.
|
|
||||||
var (
|
|
||||||
update = map[string]string{"k1": "v1-b", "k3": "v3-b"}
|
|
||||||
insert = map[string]string{"k5": "v5-b"}
|
|
||||||
delete = map[string]string{"k2": ""}
|
|
||||||
)
|
|
||||||
|
|
||||||
for k, v := range update {
|
|
||||||
db.Put([]byte(k), []byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range insert {
|
|
||||||
db.Put([]byte(k), []byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range delete {
|
|
||||||
db.Delete([]byte(k))
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range initial {
|
|
||||||
got, err := snapshot.Get([]byte(k))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(got, []byte(v)) {
|
|
||||||
t.Fatalf("Unexpected value want: %v, got %v", v, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range insert {
|
|
||||||
got, err := snapshot.Get([]byte(k))
|
|
||||||
if err == nil || len(got) != 0 {
|
|
||||||
t.Fatal("Unexpected value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range delete {
|
|
||||||
got, err := snapshot.Get([]byte(k))
|
|
||||||
if err != nil || len(got) == 0 {
|
|
||||||
t.Fatal("Unexpected deletion")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("OperationsAfterClose", func(t *testing.T) {
|
t.Run("OperationsAfterClose", func(t *testing.T) {
|
||||||
db := New()
|
db := New()
|
||||||
db.Put([]byte("key"), []byte("value"))
|
db.Put([]byte("key"), []byte("value"))
|
||||||
|
|
|
||||||
|
|
@ -252,20 +252,6 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
|
||||||
return db.db.NewIterator(bytesPrefixRange(prefix, start), nil)
|
return db.db.NewIterator(bytesPrefixRange(prefix, start), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot creates a database snapshot based on the current state.
|
|
||||||
// The created snapshot will not be affected by all following mutations
|
|
||||||
// happened on the database.
|
|
||||||
// Note don't forget to release the snapshot once it's used up, otherwise
|
|
||||||
// the stale data will never be cleaned up by the underlying compactor.
|
|
||||||
func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
snap, err := db.db.GetSnapshot()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &snapshot{db: snap}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat returns the statistic data of the database.
|
// Stat returns the statistic data of the database.
|
||||||
func (db *Database) Stat() (string, error) {
|
func (db *Database) Stat() (string, error) {
|
||||||
var stats leveldb.DBStats
|
var stats leveldb.DBStats
|
||||||
|
|
@ -536,26 +522,3 @@ func bytesPrefixRange(prefix, start []byte) *util.Range {
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot wraps a leveldb snapshot for implementing the Snapshot interface.
|
|
||||||
type snapshot struct {
|
|
||||||
db *leveldb.Snapshot
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
|
||||||
// data store.
|
|
||||||
func (snap *snapshot) Has(key []byte) (bool, error) {
|
|
||||||
return snap.db.Has(key, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the given key if it's present in the snapshot backing by
|
|
||||||
// key-value data store.
|
|
||||||
func (snap *snapshot) Get(key []byte) ([]byte, error) {
|
|
||||||
return snap.db.Get(key, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and can
|
|
||||||
// be called multiple times without causing error.
|
|
||||||
func (snap *snapshot) Release() {
|
|
||||||
snap.db.Release()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,6 @@ var (
|
||||||
// errMemorydbNotFound is returned if a key is requested that is not found in
|
// errMemorydbNotFound is returned if a key is requested that is not found in
|
||||||
// the provided memory database.
|
// the provided memory database.
|
||||||
errMemorydbNotFound = errors.New("not found")
|
errMemorydbNotFound = errors.New("not found")
|
||||||
|
|
||||||
// errSnapshotReleased is returned if callers want to retrieve data from a
|
|
||||||
// released snapshot.
|
|
||||||
errSnapshotReleased = errors.New("snapshot released")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database is an ephemeral key-value store. Apart from basic data storage
|
// Database is an ephemeral key-value store. Apart from basic data storage
|
||||||
|
|
@ -187,13 +183,6 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot creates a database snapshot based on the current state.
|
|
||||||
// The created snapshot will not be affected by all following mutations
|
|
||||||
// happened on the database.
|
|
||||||
func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
return newSnapshot(db), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat returns the statistic data of the database.
|
// Stat returns the statistic data of the database.
|
||||||
func (db *Database) Stat() (string, error) {
|
func (db *Database) Stat() (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|
@ -353,64 +342,3 @@ func (it *iterator) Value() []byte {
|
||||||
func (it *iterator) Release() {
|
func (it *iterator) Release() {
|
||||||
it.index, it.keys, it.values = -1, nil, nil
|
it.index, it.keys, it.values = -1, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot wraps a batch of key-value entries deep copied from the in-memory
|
|
||||||
// database for implementing the Snapshot interface.
|
|
||||||
type snapshot struct {
|
|
||||||
db map[string][]byte
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSnapshot initializes the snapshot with the given database instance.
|
|
||||||
func newSnapshot(db *Database) *snapshot {
|
|
||||||
db.lock.RLock()
|
|
||||||
defer db.lock.RUnlock()
|
|
||||||
|
|
||||||
copied := make(map[string][]byte, len(db.db))
|
|
||||||
for key, val := range db.db {
|
|
||||||
copied[key] = common.CopyBytes(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &snapshot{db: copied}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
|
||||||
// data store.
|
|
||||||
func (snap *snapshot) Has(key []byte) (bool, error) {
|
|
||||||
snap.lock.RLock()
|
|
||||||
defer snap.lock.RUnlock()
|
|
||||||
|
|
||||||
if snap.db == nil {
|
|
||||||
return false, errSnapshotReleased
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := snap.db[string(key)]
|
|
||||||
|
|
||||||
return ok, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the given key if it's present in the snapshot backing by
|
|
||||||
// key-value data store.
|
|
||||||
func (snap *snapshot) Get(key []byte) ([]byte, error) {
|
|
||||||
snap.lock.RLock()
|
|
||||||
defer snap.lock.RUnlock()
|
|
||||||
|
|
||||||
if snap.db == nil {
|
|
||||||
return nil, errSnapshotReleased
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry, ok := snap.db[string(key)]; ok {
|
|
||||||
return common.CopyBytes(entry), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errMemorydbNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and can
|
|
||||||
// be called multiple times without causing error.
|
|
||||||
func (snap *snapshot) Release() {
|
|
||||||
snap.lock.Lock()
|
|
||||||
defer snap.lock.Unlock()
|
|
||||||
|
|
||||||
snap.db = nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -367,59 +367,6 @@ func (d *Database) NewBatchWithSize(size int) ethdb.Batch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot wraps a pebble snapshot for implementing the Snapshot interface.
|
|
||||||
type snapshot struct {
|
|
||||||
db *pebble.Snapshot
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSnapshot creates a database snapshot based on the current state.
|
|
||||||
// The created snapshot will not be affected by all following mutations
|
|
||||||
// happened on the database.
|
|
||||||
// Note don't forget to release the snapshot once it's used up, otherwise
|
|
||||||
// the stale data will never be cleaned up by the underlying compactor.
|
|
||||||
func (d *Database) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
snap := d.db.NewSnapshot()
|
|
||||||
return &snapshot{db: snap}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
|
||||||
// data store.
|
|
||||||
func (snap *snapshot) Has(key []byte) (bool, error) {
|
|
||||||
_, closer, err := snap.db.Get(key)
|
|
||||||
if err != nil {
|
|
||||||
if err != pebble.ErrNotFound {
|
|
||||||
return false, err
|
|
||||||
} else {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closer.Close()
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the given key if it's present in the snapshot backing by
|
|
||||||
// key-value data store.
|
|
||||||
func (snap *snapshot) Get(key []byte) ([]byte, error) {
|
|
||||||
dat, closer, err := snap.db.Get(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]byte, len(dat))
|
|
||||||
copy(ret, dat)
|
|
||||||
closer.Close()
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and can
|
|
||||||
// be called multiple times without causing error.
|
|
||||||
func (snap *snapshot) Release() {
|
|
||||||
snap.db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// upperBound returns the upper bound for the given prefix
|
// upperBound returns the upper bound for the given prefix
|
||||||
func upperBound(prefix []byte) (limit []byte) {
|
func upperBound(prefix []byte) (limit []byte) {
|
||||||
for i := len(prefix) - 1; i >= 0; i-- {
|
for i := len(prefix) - 1; i >= 0; i-- {
|
||||||
|
|
|
||||||
|
|
@ -155,10 +155,6 @@ func (db *Database) Compact(start []byte, limit []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) Close() error {
|
func (db *Database) Close() error {
|
||||||
db.remote.Close()
|
db.remote.Close()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
// Copyright 2022 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ethdb
|
|
||||||
|
|
||||||
type Snapshot interface {
|
|
||||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
|
||||||
// data store.
|
|
||||||
Has(key []byte) (bool, error)
|
|
||||||
|
|
||||||
// Get retrieves the given key if it's present in the snapshot backing by
|
|
||||||
// key-value data store.
|
|
||||||
Get(key []byte) ([]byte, error)
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and can
|
|
||||||
// be called multiple times without causing error.
|
|
||||||
Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snapshotter wraps the Snapshot method of a backing data store.
|
|
||||||
type Snapshotter interface {
|
|
||||||
// NewSnapshot creates a database snapshot based on the current state.
|
|
||||||
// The created snapshot will not be affected by all following mutations
|
|
||||||
// happened on the database.
|
|
||||||
// Note don't forget to release the snapshot once it's used up, otherwise
|
|
||||||
// the stale data will never be cleaned up by the underlying compactor.
|
|
||||||
NewSnapshot() (Snapshot, error)
|
|
||||||
}
|
|
||||||
7
go.mod
7
go.mod
|
|
@ -13,7 +13,7 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.18
|
github.com/aws/aws-sdk-go-v2/config v1.27.18
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.18
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.18
|
||||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.3
|
github.com/btcsuite/btcd/btcec/v2 v2.3.4
|
||||||
github.com/cespare/cp v1.1.1
|
github.com/cespare/cp v1.1.1
|
||||||
github.com/cloudflare/cloudflare-go v0.97.0
|
github.com/cloudflare/cloudflare-go v0.97.0
|
||||||
github.com/cockroachdb/pebble v1.1.1
|
github.com/cockroachdb/pebble v1.1.1
|
||||||
|
|
@ -36,7 +36,6 @@ require (
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/golang/protobuf v1.5.4
|
|
||||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
||||||
github.com/google/gofuzz v1.2.0
|
github.com/google/gofuzz v1.2.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
|
@ -49,7 +48,7 @@ require (
|
||||||
github.com/heimdalr/dag v1.4.0
|
github.com/heimdalr/dag v1.4.0
|
||||||
github.com/holiman/billy v0.0.0-20240322075458-72a4e81ec6da
|
github.com/holiman/billy v0.0.0-20240322075458-72a4e81ec6da
|
||||||
github.com/holiman/bloomfilter/v2 v2.0.3
|
github.com/holiman/bloomfilter/v2 v2.0.3
|
||||||
github.com/holiman/uint256 v1.2.4
|
github.com/holiman/uint256 v1.3.1
|
||||||
github.com/huin/goupnp v1.3.0
|
github.com/huin/goupnp v1.3.0
|
||||||
github.com/imdario/mergo v0.3.11
|
github.com/imdario/mergo v0.3.11
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0
|
github.com/influxdata/influxdb-client-go/v2 v2.13.0
|
||||||
|
|
@ -57,7 +56,6 @@ require (
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2
|
github.com/jackpal/go-nat-pmp v1.0.2
|
||||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
|
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
|
||||||
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52
|
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52
|
||||||
github.com/kilic/bls12-381 v0.1.0
|
github.com/kilic/bls12-381 v0.1.0
|
||||||
github.com/kylelemons/godebug v1.1.0
|
github.com/kylelemons/godebug v1.1.0
|
||||||
|
|
@ -213,6 +211,7 @@ require (
|
||||||
github.com/go-redsync/redsync/v4 v4.0.4 // indirect
|
github.com/go-redsync/redsync/v4 v4.0.4 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
|
|
|
||||||
7
go.sum
7
go.sum
|
|
@ -1519,8 +1519,9 @@ github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H
|
||||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0=
|
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||||
|
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||||
|
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||||
|
|
@ -2128,8 +2129,9 @@ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZ
|
||||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||||
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||||
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
||||||
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
|
|
||||||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||||
|
github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
|
||||||
|
github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||||
|
|
@ -2187,7 +2189,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
|
|
|
||||||
|
|
@ -1138,11 +1138,11 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
|
||||||
// if stateDiff is set, all diff will be applied first and then execute the call
|
// if stateDiff is set, all diff will be applied first and then execute the call
|
||||||
// message.
|
// message.
|
||||||
type OverrideAccount struct {
|
type OverrideAccount struct {
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||||
Code *hexutil.Bytes `json:"code"`
|
Code *hexutil.Bytes `json:"code"`
|
||||||
Balance **hexutil.Big `json:"balance"`
|
Balance *hexutil.Big `json:"balance"`
|
||||||
State *map[common.Hash]common.Hash `json:"state"`
|
State map[common.Hash]common.Hash `json:"state"`
|
||||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateOverride is the collection of overridden accounts.
|
// StateOverride is the collection of overridden accounts.
|
||||||
|
|
@ -1165,7 +1165,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error {
|
||||||
}
|
}
|
||||||
// Override account balance.
|
// Override account balance.
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
u256Balance, _ := uint256.FromBig((*big.Int)(*account.Balance))
|
u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance))
|
||||||
statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified)
|
statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1174,11 +1174,11 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error {
|
||||||
}
|
}
|
||||||
// Replace entire state if caller requires.
|
// Replace entire state if caller requires.
|
||||||
if account.State != nil {
|
if account.State != nil {
|
||||||
statedb.SetStorage(addr, *account.State)
|
statedb.SetStorage(addr, account.State)
|
||||||
}
|
}
|
||||||
// Apply state diff into specified accounts.
|
// Apply state diff into specified accounts.
|
||||||
if account.StateDiff != nil {
|
if account.StateDiff != nil {
|
||||||
for key, value := range *account.StateDiff {
|
for key, value := range account.StateDiff {
|
||||||
statedb.SetState(addr, key, value)
|
statedb.SetState(addr, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -989,7 +989,7 @@ func TestCall(t *testing.T) {
|
||||||
overrides: StateOverride{
|
overrides: StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: OverrideAccount{
|
||||||
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
|
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
|
||||||
StateDiff: &map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
|
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
|
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
|
||||||
|
|
@ -1056,7 +1056,33 @@ func TestCall(t *testing.T) {
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: StateOverride{
|
||||||
dad: OverrideAccount{
|
dad: OverrideAccount{
|
||||||
State: &map[common.Hash]common.Hash{},
|
State: map[common.Hash]common.Hash{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// Clear the entire storage set
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[1].addr,
|
||||||
|
// Yul:
|
||||||
|
// object "Test" {
|
||||||
|
// code {
|
||||||
|
// let dad := 0x0000000000000000000000000000000000000dad
|
||||||
|
// if eq(balance(dad), 0) {
|
||||||
|
// revert(0, 0)
|
||||||
|
// }
|
||||||
|
// let slot := sload(0)
|
||||||
|
// mstore(0, slot)
|
||||||
|
// return(0, 32)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
|
||||||
|
},
|
||||||
|
overrides: StateOverride{
|
||||||
|
dad: OverrideAccount{
|
||||||
|
State: map[common.Hash]common.Hash{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
|
@ -1422,9 +1448,9 @@ func newAccounts(n int) (accounts []account) {
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRPCBalance(balance *big.Int) **hexutil.Big {
|
func newRPCBalance(balance *big.Int) *hexutil.Big {
|
||||||
rpcBalance := (*hexutil.Big)(balance)
|
rpcBalance := (*hexutil.Big)(balance)
|
||||||
return &rpcBalance
|
return rpcBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
func hex2Bytes(str string) *hexutil.Bytes {
|
func hex2Bytes(str string) *hexutil.Bytes {
|
||||||
|
|
|
||||||
|
|
@ -707,9 +707,10 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint : paralleltest
|
// nolint: paralleltest
|
||||||
// TestCommitInterruptExperimentBor tests the commit interrupt experiment for bor consensus by inducing an artificial delay at transaction level.
|
// TestCommitInterruptExperimentBor_NormalFlow tests the commit interrupt experiment for bor consensus by inducing
|
||||||
func TestCommitInterruptExperimentBor(t *testing.T) {
|
// an artificial delay at transaction level. It runs the normal mining flow triggered via new head.
|
||||||
|
func TestCommitInterruptExperimentBor_NormalFlow(t *testing.T) {
|
||||||
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
|
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
|
||||||
testCommitInterruptExperimentBor(t, 200, 5, 0)
|
testCommitInterruptExperimentBor(t, 200, 5, 0)
|
||||||
|
|
||||||
|
|
@ -834,6 +835,96 @@ func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int, opc
|
||||||
assert.Check(t, 0 < w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
|
assert.Check(t, 0 < w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCommitInterruptExperimentBor_NewTxFlow tests the commit interrupt experiment for bor consensus by inducing
|
||||||
|
// an artificial delay at transaction level. It runs the mining flow triggered via new transactions channel. The tests
|
||||||
|
// are a bit unconventional compared to normal flow as the situations are only possible in non-validator mode.
|
||||||
|
func TestCommitInterruptExperimentBor_NewTxFlow(t *testing.T) {
|
||||||
|
var (
|
||||||
|
engine consensus.Engine
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
)
|
||||||
|
|
||||||
|
chainConfig = params.BorUnittestChainConfig
|
||||||
|
|
||||||
|
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
|
|
||||||
|
engine, ctrl = getFakeBorFromConfig(t, chainConfig)
|
||||||
|
|
||||||
|
w, b, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), true, uint(0), uint(0))
|
||||||
|
defer func() {
|
||||||
|
w.close()
|
||||||
|
engine.Close()
|
||||||
|
db.Close()
|
||||||
|
ctrl.Finish()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create random transactions (contract interaction)
|
||||||
|
tx1, addr := b.newStorageCreateContractTx()
|
||||||
|
tx2 := b.newStorageContractCallTx(addr, 1)
|
||||||
|
tx3 := b.newStorageContractCallTx(addr, 2)
|
||||||
|
|
||||||
|
// Create a chain head subscription for tests
|
||||||
|
chainHeadCh := make(chan core.ChainHeadEvent, 10)
|
||||||
|
w.chain.SubscribeChainHeadEvent(chainHeadCh)
|
||||||
|
|
||||||
|
// Start mining!
|
||||||
|
w.start()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
head := <-chainHeadCh
|
||||||
|
// We skip the initial 2 blocks as the mining timings are a bit skewed up
|
||||||
|
if head.Block.NumberU64() == 2 {
|
||||||
|
// Stop the miner so that worker assumes it's a sentry and not a validator
|
||||||
|
w.stop()
|
||||||
|
|
||||||
|
// Add the first transaction to be mined normally via `txsCh`
|
||||||
|
b.TxPool().Add([]*types.Transaction{tx1}, false, false)
|
||||||
|
|
||||||
|
// Set it to syncing mode so that it doesn't mine via the `commitWork` flow
|
||||||
|
w.syncing.Store(true)
|
||||||
|
|
||||||
|
// Wait until the mining window (2s) is almost about to reach leaving
|
||||||
|
// a very small time (~10ms) to try to commit transaction before timing out.
|
||||||
|
delay := time.Until(time.Unix(int64(w.current.header.Time), 0))
|
||||||
|
delay -= 10 * time.Millisecond
|
||||||
|
|
||||||
|
// Case 1: This transaction should not be included due to commit interrupt
|
||||||
|
// at opcode level. It will start the EVM execution but will end in between.
|
||||||
|
<-time.After(delay)
|
||||||
|
|
||||||
|
// Set an artificial delay at opcode level
|
||||||
|
w.setInterruptCtx(vm.InterruptCtxOpcodeDelayKey, uint(500))
|
||||||
|
|
||||||
|
// Send the second transaction
|
||||||
|
b.TxPool().Add([]*types.Transaction{tx2}, false, false)
|
||||||
|
|
||||||
|
// Reset the delay again. By this time, we're sure that it has timed out.
|
||||||
|
delay = time.Until(time.Unix(int64(w.current.header.Time), 0))
|
||||||
|
|
||||||
|
// Case 2: This transaction should not be included because the miner loop
|
||||||
|
// won't accept any transactions post the deadline (i.e. header.Timestamp).
|
||||||
|
<-time.After(delay)
|
||||||
|
|
||||||
|
// Reset the artificial opcode delay just to be sure of the exclusion of tx
|
||||||
|
w.setInterruptCtx(vm.InterruptCtxOpcodeDelayKey, uint(0))
|
||||||
|
|
||||||
|
// Send the third transaction
|
||||||
|
b.TxPool().Add([]*types.Transaction{tx3}, false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for enough time to mine 3 blocks
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
|
||||||
|
// Ensure that the last block was 3 and only 1 transactions out of 3 were included
|
||||||
|
assert.Equal(t, w.current.header.Number.Uint64(), uint64(3))
|
||||||
|
assert.Equal(t, w.current.tcount, 1)
|
||||||
|
assert.Equal(t, len(w.current.txs), 1)
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkBorMining(b *testing.B) {
|
func BenchmarkBorMining(b *testing.B) {
|
||||||
chainConfig := params.BorUnittestChainConfig
|
chainConfig := params.BorUnittestChainConfig
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,10 @@
|
||||||
package discover
|
package discover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,38 +42,6 @@ type tableNode struct {
|
||||||
isValidatedLive bool // true if existence of node is considered validated right now
|
isValidatedLive bool // true if existence of node is considered validated right now
|
||||||
}
|
}
|
||||||
|
|
||||||
type encPubkey [64]byte
|
|
||||||
|
|
||||||
func encodePubkey(key *ecdsa.PublicKey) encPubkey {
|
|
||||||
var e encPubkey
|
|
||||||
|
|
||||||
math.ReadBits(key.X, e[:len(e)/2])
|
|
||||||
math.ReadBits(key.Y, e[len(e)/2:])
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodePubkey(curve elliptic.Curve, e []byte) (*ecdsa.PublicKey, error) {
|
|
||||||
if len(e) != len(encPubkey{}) {
|
|
||||||
return nil, errors.New("wrong size public key data")
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)}
|
|
||||||
half := len(e) / 2
|
|
||||||
p.X.SetBytes(e[:half])
|
|
||||||
p.Y.SetBytes(e[half:])
|
|
||||||
|
|
||||||
if !p.Curve.IsOnCurve(p.X, p.Y) {
|
|
||||||
return nil, errors.New("invalid curve point")
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e encPubkey) id() enode.ID {
|
|
||||||
return enode.ID(crypto.Keccak256Hash(e[:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNodes(ns []*tableNode) []*enode.Node {
|
func unwrapNodes(ns []*tableNode) []*enode.Node {
|
||||||
result := make([]*enode.Node, len(ns))
|
result := make([]*enode.Node, len(ns))
|
||||||
for i, n := range ns {
|
for i, n := range ns {
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,18 @@ func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *tableNode) {
|
||||||
// It returns the next time it should be invoked, which is used in the Table main loop
|
// It returns the next time it should be invoked, which is used in the Table main loop
|
||||||
// to schedule a timer. However, run can be called at any time.
|
// to schedule a timer. However, run can be called at any time.
|
||||||
func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) {
|
func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) {
|
||||||
if n := tr.fast.get(now, &tab.rand, tr.activeReq); n != nil {
|
reval := func(list *revalidationList) {
|
||||||
tr.startRequest(tab, n)
|
if list.nextTime <= now {
|
||||||
tr.fast.schedule(now, &tab.rand)
|
if n := list.get(now, &tab.rand, tr.activeReq); n != nil {
|
||||||
}
|
tr.startRequest(tab, n)
|
||||||
if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil {
|
}
|
||||||
tr.startRequest(tab, n)
|
// Update nextTime regardless if any requests were started because
|
||||||
tr.slow.schedule(now, &tab.rand)
|
// current value has passed.
|
||||||
|
list.schedule(now, &tab.rand)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
reval(&tr.fast)
|
||||||
|
reval(&tr.slow)
|
||||||
|
|
||||||
return min(tr.fast.nextTime, tr.slow.nextTime)
|
return min(tr.fast.nextTime, tr.slow.nextTime)
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +204,7 @@ type revalidationList struct {
|
||||||
|
|
||||||
// get returns a random node from the queue. Nodes in the 'exclude' map are not returned.
|
// get returns a random node from the queue. Nodes in the 'exclude' map are not returned.
|
||||||
func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode {
|
func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode {
|
||||||
if now < list.nextTime || len(list.nodes) == 0 {
|
if len(list.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for i := 0; i < len(list.nodes)*3; i++ {
|
for i := 0; i < len(list.nodes)*3; i++ {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover/v4wire"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||||
)
|
)
|
||||||
|
|
@ -306,7 +307,7 @@ func hexEncPrivkey(h string) *ecdsa.PrivateKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// hexEncPubkey decodes h as a public key.
|
// hexEncPubkey decodes h as a public key.
|
||||||
func hexEncPubkey(h string) (ret encPubkey) {
|
func hexEncPubkey(h string) (ret v4wire.Pubkey) {
|
||||||
b, err := hex.DecodeString(h)
|
b, err := hex.DecodeString(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func TestUDPv4_Lookup(t *testing.T) {
|
||||||
test := newUDPTest(t)
|
test := newUDPTest(t)
|
||||||
|
|
||||||
// Lookup on empty table returns no nodes.
|
// Lookup on empty table returns no nodes.
|
||||||
targetKey, _ := decodePubkey(crypto.S256(), lookupTestnet.target[:])
|
targetKey, _ := v4wire.DecodePubkey(crypto.S256(), lookupTestnet.target)
|
||||||
if results := test.udp.LookupPubkey(targetKey); len(results) > 0 {
|
if results := test.udp.LookupPubkey(targetKey); len(results) > 0 {
|
||||||
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ func TestUDPv4_Lookup(t *testing.T) {
|
||||||
t.Logf("results:")
|
t.Logf("results:")
|
||||||
|
|
||||||
for _, e := range results {
|
for _, e := range results {
|
||||||
t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.id(), e.ID()), e.ID().Bytes())
|
t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.ID(), e.ID()), e.ID().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) != bucketSize {
|
if len(results) != bucketSize {
|
||||||
|
|
@ -154,7 +154,7 @@ func serveTestnet(test *udpTest, testnet *preminedTestnet) {
|
||||||
case *v4wire.Ping:
|
case *v4wire.Ping:
|
||||||
test.packetInFrom(nil, key, to, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash})
|
test.packetInFrom(nil, key, to, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash})
|
||||||
case *v4wire.Findnode:
|
case *v4wire.Findnode:
|
||||||
dist := enode.LogDist(n.ID(), testnet.target.id())
|
dist := enode.LogDist(n.ID(), testnet.target.ID())
|
||||||
nodes := testnet.nodesAtDistance(dist - 1)
|
nodes := testnet.nodesAtDistance(dist - 1)
|
||||||
test.packetInFrom(nil, key, to, &v4wire.Neighbors{Expiration: futureExp, Nodes: nodes})
|
test.packetInFrom(nil, key, to, &v4wire.Neighbors{Expiration: futureExp, Nodes: nodes})
|
||||||
}
|
}
|
||||||
|
|
@ -169,12 +169,12 @@ func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node
|
||||||
t.Logf("results:")
|
t.Logf("results:")
|
||||||
|
|
||||||
for _, e := range results {
|
for _, e := range results {
|
||||||
t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes())
|
t.Logf(" ld=%d, %x", enode.LogDist(tn.target.ID(), e.ID()), e.ID().Bytes())
|
||||||
}
|
}
|
||||||
if hasDuplicates(results) {
|
if hasDuplicates(results) {
|
||||||
t.Errorf("result set contains duplicate entries")
|
t.Errorf("result set contains duplicate entries")
|
||||||
}
|
}
|
||||||
if !sortedByDistanceTo(tn.target.id(), results) {
|
if !sortedByDistanceTo(tn.target.ID(), results) {
|
||||||
t.Errorf("result set not sorted by distance to target")
|
t.Errorf("result set not sorted by distance to target")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,7 +245,7 @@ var lookupTestnet = &preminedTestnet{
|
||||||
}
|
}
|
||||||
|
|
||||||
type preminedTestnet struct {
|
type preminedTestnet struct {
|
||||||
target encPubkey
|
target v4wire.Pubkey
|
||||||
dists [hashBits + 1][]*ecdsa.PrivateKey
|
dists [hashBits + 1][]*ecdsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,7 +327,7 @@ func (tn *preminedTestnet) closest(n int) (nodes []*enode.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.SortFunc(nodes, func(a, b *enode.Node) int {
|
slices.SortFunc(nodes, func(a, b *enode.Node) int {
|
||||||
return enode.DistCmp(tn.target.id(), a.ID(), b.ID())
|
return enode.DistCmp(tn.target.ID(), a.ID(), b.ID())
|
||||||
})
|
})
|
||||||
|
|
||||||
return nodes[:n]
|
return nodes[:n]
|
||||||
|
|
@ -343,13 +343,11 @@ func (tn *preminedTestnet) mine() {
|
||||||
tn.dists[i] = nil
|
tn.dists[i] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
targetSha := tn.target.id()
|
targetSha := tn.target.ID()
|
||||||
|
|
||||||
found, need := 0, 40
|
found, need := 0, 40
|
||||||
for found < need {
|
for found < need {
|
||||||
k := newkey()
|
k := newkey()
|
||||||
ld := enode.LogDist(targetSha, encodePubkey(&k.PublicKey).id())
|
ld := enode.LogDist(targetSha, v4wire.EncodePubkey(&k.PublicKey).ID())
|
||||||
|
|
||||||
if len(tn.dists[ld]) < 8 {
|
if len(tn.dists[ld]) < 8 {
|
||||||
tn.dists[ld] = append(tn.dists[ld], k)
|
tn.dists[ld] = append(tn.dists[ld], k)
|
||||||
found++
|
found++
|
||||||
|
|
|
||||||
|
|
@ -281,8 +281,7 @@ func (t *UDPv4) LookupPubkey(key *ecdsa.PublicKey) []*enode.Node {
|
||||||
// case and run the bootstrapping logic.
|
// case and run the bootstrapping logic.
|
||||||
<-t.tab.refresh()
|
<-t.tab.refresh()
|
||||||
}
|
}
|
||||||
|
return t.newLookup(t.closeCtx, v4wire.EncodePubkey(key)).run()
|
||||||
return t.newLookup(t.closeCtx, encodePubkey(key)).run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomNodes is an iterator yielding nodes from a random walk of the DHT.
|
// RandomNodes is an iterator yielding nodes from a random walk of the DHT.
|
||||||
|
|
@ -297,26 +296,25 @@ func (t *UDPv4) lookupRandom() []*enode.Node {
|
||||||
|
|
||||||
// lookupSelf implements transport.
|
// lookupSelf implements transport.
|
||||||
func (t *UDPv4) lookupSelf() []*enode.Node {
|
func (t *UDPv4) lookupSelf() []*enode.Node {
|
||||||
return t.newLookup(t.closeCtx, encodePubkey(&t.priv.PublicKey)).run()
|
pubkey := v4wire.EncodePubkey(&t.priv.PublicKey)
|
||||||
|
return t.newLookup(t.closeCtx, pubkey).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
|
func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
|
||||||
var target encPubkey
|
var target v4wire.Pubkey
|
||||||
|
|
||||||
crand.Read(target[:])
|
crand.Read(target[:])
|
||||||
|
|
||||||
return t.newLookup(ctx, target)
|
return t.newLookup(ctx, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
|
func (t *UDPv4) newLookup(ctx context.Context, targetKey v4wire.Pubkey) *lookup {
|
||||||
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
||||||
ekey := v4wire.Pubkey(targetKey)
|
|
||||||
it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||||
addr, ok := n.UDPEndpoint()
|
addr, ok := n.UDPEndpoint()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errNoUDPEndpoint
|
return nil, errNoUDPEndpoint
|
||||||
}
|
}
|
||||||
return t.findnode(n.ID(), addr, ekey)
|
return t.findnode(n.ID(), addr, targetKey)
|
||||||
})
|
})
|
||||||
|
|
||||||
return it
|
return it
|
||||||
|
|
|
||||||
|
|
@ -339,8 +339,7 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
|
||||||
// queue a pending findnode request
|
// queue a pending findnode request
|
||||||
resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1)
|
resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
rid := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID()
|
||||||
|
|
||||||
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
||||||
if err != nil && len(ns) == 0 {
|
if err != nil && len(ns) == 0 {
|
||||||
errc <- err
|
errc <- err
|
||||||
|
|
@ -462,7 +461,7 @@ func TestUDPv4_successfulPing(t *testing.T) {
|
||||||
// pong packet.
|
// pong packet.
|
||||||
select {
|
select {
|
||||||
case n := <-added:
|
case n := <-added:
|
||||||
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
rid := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID()
|
||||||
if n.ID() != rid {
|
if n.ID() != rid {
|
||||||
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
|
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/testlog"
|
"github.com/ethereum/go-ethereum/internal/testlog"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover/v4wire"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover/v5wire"
|
"github.com/ethereum/go-ethereum/p2p/discover/v5wire"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||||
|
|
@ -637,7 +638,7 @@ func TestUDPv5_lookup(t *testing.T) {
|
||||||
test := newUDPV5Test(t)
|
test := newUDPV5Test(t)
|
||||||
|
|
||||||
// Lookup on empty table returns no nodes.
|
// Lookup on empty table returns no nodes.
|
||||||
if results := test.udp.Lookup(lookupTestnet.target.id()); len(results) > 0 {
|
if results := test.udp.Lookup(lookupTestnet.target.ID()); len(results) > 0 {
|
||||||
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -657,8 +658,7 @@ func TestUDPv5_lookup(t *testing.T) {
|
||||||
// Start the lookup.
|
// Start the lookup.
|
||||||
resultC := make(chan []*enode.Node, 1)
|
resultC := make(chan []*enode.Node, 1)
|
||||||
go func() {
|
go func() {
|
||||||
resultC <- test.udp.Lookup(lookupTestnet.target.id())
|
resultC <- test.udp.Lookup(lookupTestnet.target.ID())
|
||||||
|
|
||||||
test.close()
|
test.close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -872,8 +872,7 @@ func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr netip.AddrPort,
|
||||||
|
|
||||||
// getNode ensures the test knows about a node at the given endpoint.
|
// getNode ensures the test knows about a node at the given endpoint.
|
||||||
func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode {
|
func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode {
|
||||||
id := encodePubkey(&key.PublicKey).id()
|
id := v4wire.EncodePubkey(&key.PublicKey).ID()
|
||||||
|
|
||||||
ln := test.nodesByID[id]
|
ln := test.nodesByID[id]
|
||||||
if ln == nil {
|
if ln == nil {
|
||||||
db, _ := enode.OpenDB("")
|
db, _ := enode.OpenDB("")
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ func (v IPv4Addr) ENRKey() string { return "ip" }
|
||||||
func (v IPv4Addr) EncodeRLP(w io.Writer) error {
|
func (v IPv4Addr) EncodeRLP(w io.Writer) error {
|
||||||
addr := netip.Addr(v)
|
addr := netip.Addr(v)
|
||||||
if !addr.Is4() {
|
if !addr.Is4() {
|
||||||
return fmt.Errorf("address is not IPv4")
|
return errors.New("address is not IPv4")
|
||||||
}
|
}
|
||||||
enc := rlp.NewEncoderBuffer(w)
|
enc := rlp.NewEncoderBuffer(w)
|
||||||
bytes := addr.As4()
|
bytes := addr.As4()
|
||||||
|
|
@ -215,7 +215,7 @@ func (v IPv6Addr) ENRKey() string { return "ip6" }
|
||||||
func (v IPv6Addr) EncodeRLP(w io.Writer) error {
|
func (v IPv6Addr) EncodeRLP(w io.Writer) error {
|
||||||
addr := netip.Addr(v)
|
addr := netip.Addr(v)
|
||||||
if !addr.Is6() {
|
if !addr.Is6() {
|
||||||
return fmt.Errorf("address is not IPv6")
|
return errors.New("address is not IPv6")
|
||||||
}
|
}
|
||||||
enc := rlp.NewEncoderBuffer(w)
|
enc := rlp.NewEncoderBuffer(w)
|
||||||
bytes := addr.As16()
|
bytes := addr.As16()
|
||||||
|
|
|
||||||
|
|
@ -141,8 +141,10 @@ func (n ExtIP) String() string { return fmt.Sprintf("ExtIP(%v)", ne
|
||||||
|
|
||||||
// These do nothing.
|
// These do nothing.
|
||||||
|
|
||||||
func (ExtIP) AddMapping(string, int, int, string, time.Duration) (uint16, error) { return 0, nil }
|
func (ExtIP) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) {
|
||||||
func (ExtIP) DeleteMapping(string, int, int) error { return nil }
|
return uint16(extport), nil
|
||||||
|
}
|
||||||
|
func (ExtIP) DeleteMapping(string, int, int) error { return nil }
|
||||||
|
|
||||||
// Any returns a port mapper that tries to discover any supported
|
// Any returns a port mapper that tries to discover any supported
|
||||||
// mechanism on the local network.
|
// mechanism on the local network.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2024 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -16,17 +16,9 @@
|
||||||
|
|
||||||
package pipes
|
package pipes
|
||||||
|
|
||||||
import (
|
import "net"
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NetPipe wraps net.Pipe in a signature returning an error
|
// TCPPipe creates an in process full duplex pipe based on a localhost TCP socket.
|
||||||
func NetPipe() (net.Conn, net.Conn, error) {
|
|
||||||
p1, p2 := net.Pipe()
|
|
||||||
return p1, p2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCPPipe creates an in process full duplex pipe based on a localhost TCP socket
|
|
||||||
func TCPPipe() (net.Conn, net.Conn, error) {
|
func TCPPipe() (net.Conn, net.Conn, error) {
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
|
"github.com/ethereum/go-ethereum/p2p/pipes"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ func (srv *Server) portMappingLoop() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Couldn't get external IP", "err", err, "interface", srv.NAT)
|
log.Debug("Couldn't get external IP", "err", err, "interface", srv.NAT)
|
||||||
} else if !ip.Equal(lastExtIP) {
|
} else if !ip.Equal(lastExtIP) {
|
||||||
log.Debug("External IP changed", "ip", extip, "interface", srv.NAT)
|
log.Debug("External IP changed", "ip", ip, "interface", srv.NAT)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ func TestServerPortMapping(t *testing.T) {
|
||||||
PrivateKey: newkey(),
|
PrivateKey: newkey(),
|
||||||
NoDial: true,
|
NoDial: true,
|
||||||
ListenAddr: ":0",
|
ListenAddr: ":0",
|
||||||
|
DiscAddr: ":0",
|
||||||
NAT: mockNAT,
|
NAT: mockNAT,
|
||||||
Logger: testlog.Logger(t, log.LvlTrace),
|
Logger: testlog.Logger(t, log.LvlTrace),
|
||||||
clock: clock,
|
clock: clock,
|
||||||
|
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
# devp2p Simulations
|
|
||||||
|
|
||||||
The `p2p/simulations` package implements a simulation framework that supports
|
|
||||||
creating a collection of devp2p nodes, connecting them to form a
|
|
||||||
simulation network, performing simulation actions in that network and then
|
|
||||||
extracting useful information.
|
|
||||||
|
|
||||||
## Nodes
|
|
||||||
|
|
||||||
Each node in a simulation network runs multiple services by wrapping a collection
|
|
||||||
of objects which implement the `node.Service` interface meaning they:
|
|
||||||
|
|
||||||
* can be started and stopped
|
|
||||||
* run p2p protocols
|
|
||||||
* expose RPC APIs
|
|
||||||
|
|
||||||
This means that any object which implements the `node.Service` interface can be
|
|
||||||
used to run a node in the simulation.
|
|
||||||
|
|
||||||
## Services
|
|
||||||
|
|
||||||
Before running a simulation, a set of service initializers must be registered
|
|
||||||
which can then be used to run nodes in the network.
|
|
||||||
|
|
||||||
A service initializer is a function with the following signature:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func(ctx *adapters.ServiceContext) (node.Service, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
These initializers should be registered by calling the `adapters.RegisterServices`
|
|
||||||
function in an `init()` hook:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func init() {
|
|
||||||
adapters.RegisterServices(adapters.Services{
|
|
||||||
"service1": initService1,
|
|
||||||
"service2": initService2,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Node Adapters
|
|
||||||
|
|
||||||
The simulation framework includes multiple "node adapters" which are
|
|
||||||
responsible for creating an environment in which a node runs.
|
|
||||||
|
|
||||||
### SimAdapter
|
|
||||||
|
|
||||||
The `SimAdapter` runs nodes in-memory, connecting them using an in-memory,
|
|
||||||
synchronous `net.Pipe` and connecting to their RPC server using an in-memory
|
|
||||||
`rpc.Client`.
|
|
||||||
|
|
||||||
### ExecAdapter
|
|
||||||
|
|
||||||
The `ExecAdapter` runs nodes as child processes of the running simulation.
|
|
||||||
|
|
||||||
It does this by executing the binary which is running the simulation but
|
|
||||||
setting `argv[0]` (i.e. the program name) to `p2p-node` which is then
|
|
||||||
detected by an init hook in the child process which runs the `node.Service`
|
|
||||||
using the devp2p node stack rather than executing `main()`.
|
|
||||||
|
|
||||||
The nodes listen for devp2p connections and WebSocket RPC clients on random
|
|
||||||
localhost ports.
|
|
||||||
|
|
||||||
## Network
|
|
||||||
|
|
||||||
A simulation network is created with an ID and default service. The default
|
|
||||||
service is used if a node is created without an explicit service. The
|
|
||||||
network has exposed methods for creating, starting, stopping, connecting
|
|
||||||
and disconnecting nodes. It also emits events when certain actions occur.
|
|
||||||
|
|
||||||
### Events
|
|
||||||
|
|
||||||
A simulation network emits the following events:
|
|
||||||
|
|
||||||
* node event - when nodes are created / started / stopped
|
|
||||||
* connection event - when nodes are connected / disconnected
|
|
||||||
* message event - when a protocol message is sent between two nodes
|
|
||||||
|
|
||||||
The events have a "control" flag which when set indicates that the event is the
|
|
||||||
outcome of a controlled simulation action (e.g. creating a node or explicitly
|
|
||||||
connecting two nodes).
|
|
||||||
|
|
||||||
This is in contrast to a non-control event, otherwise called a "live" event,
|
|
||||||
which is the outcome of something happening in the network as a result of a
|
|
||||||
control event (e.g. a node actually started up or a connection was actually
|
|
||||||
established between two nodes).
|
|
||||||
|
|
||||||
Live events are detected by the simulation network by subscribing to node peer
|
|
||||||
events via RPC when the nodes start up.
|
|
||||||
|
|
||||||
## Testing Framework
|
|
||||||
|
|
||||||
The `Simulation` type can be used in tests to perform actions in a simulation
|
|
||||||
network and then wait for expectations to be met.
|
|
||||||
|
|
||||||
With a running simulation network, the `Simulation.Run` method can be called
|
|
||||||
with a `Step` which has the following fields:
|
|
||||||
|
|
||||||
* `Action` - a function that performs some action in the network
|
|
||||||
|
|
||||||
* `Expect` - an expectation function which returns whether or not a
|
|
||||||
given node meets the expectation
|
|
||||||
|
|
||||||
* `Trigger` - a channel that receives node IDs which then trigger a check
|
|
||||||
of the expectation function to be performed against that node
|
|
||||||
|
|
||||||
As a concrete example, consider a simulated network of Ethereum nodes. An
|
|
||||||
`Action` could be the sending of a transaction, `Expect` it being included in
|
|
||||||
a block, and `Trigger` a check for every block that is mined.
|
|
||||||
|
|
||||||
On return, the `Simulation.Run` method returns a `StepResult` which can be used
|
|
||||||
to determine if all nodes met the expectation, how long it took them to meet
|
|
||||||
the expectation and what network events were emitted during the step run.
|
|
||||||
|
|
||||||
## HTTP API
|
|
||||||
|
|
||||||
The simulation framework includes an HTTP API that can be used to control the
|
|
||||||
simulation.
|
|
||||||
|
|
||||||
The API is initialised with a particular node adapter and has the following
|
|
||||||
endpoints:
|
|
||||||
|
|
||||||
```
|
|
||||||
OPTIONS / Response 200 with "Access-Control-Allow-Headers"" header set to "Content-Type""
|
|
||||||
GET / Get network information
|
|
||||||
POST /start Start all nodes in the network
|
|
||||||
POST /stop Stop all nodes in the network
|
|
||||||
POST /mocker/start Start the mocker node simulation
|
|
||||||
POST /mocker/stop Stop the mocker node simulation
|
|
||||||
GET /mocker Get a list of available mockers
|
|
||||||
POST /reset Reset all properties of a network to initial (empty) state
|
|
||||||
GET /events Stream network events
|
|
||||||
GET /snapshot Take a network snapshot
|
|
||||||
POST /snapshot Load a network snapshot
|
|
||||||
POST /nodes Create a node
|
|
||||||
GET /nodes Get all nodes in the network
|
|
||||||
GET /nodes/:nodeid Get node information
|
|
||||||
POST /nodes/:nodeid/start Start a node
|
|
||||||
POST /nodes/:nodeid/stop Stop a node
|
|
||||||
POST /nodes/:nodeid/conn/:peerid Connect two nodes
|
|
||||||
DELETE /nodes/:nodeid/conn/:peerid Disconnect two nodes
|
|
||||||
GET /nodes/:nodeid/rpc Make RPC requests to a node via WebSocket
|
|
||||||
```
|
|
||||||
|
|
||||||
For convenience, `nodeid` in the URL can be the name of a node rather than its
|
|
||||||
ID.
|
|
||||||
|
|
||||||
## Command line client
|
|
||||||
|
|
||||||
`p2psim` is a command line client for the HTTP API, located in
|
|
||||||
`cmd/p2psim`.
|
|
||||||
|
|
||||||
It provides the following commands:
|
|
||||||
|
|
||||||
```
|
|
||||||
p2psim show
|
|
||||||
p2psim events [--current] [--filter=FILTER]
|
|
||||||
p2psim snapshot
|
|
||||||
p2psim load
|
|
||||||
p2psim node create [--name=NAME] [--services=SERVICES] [--key=KEY]
|
|
||||||
p2psim node list
|
|
||||||
p2psim node show <node>
|
|
||||||
p2psim node start <node>
|
|
||||||
p2psim node stop <node>
|
|
||||||
p2psim node connect <node> <peer>
|
|
||||||
p2psim node disconnect <node> <peer>
|
|
||||||
p2psim node rpc <node> <method> [<args>] [--subscribe]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
See [p2p/simulations/examples/README.md](examples/README.md).
|
|
||||||
|
|
@ -1,624 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package adapters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log/slog"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/reexec"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Register a reexec function to start a simulation node when the current binary is
|
|
||||||
// executed as "p2p-node" (rather than whatever the main() function would normally do).
|
|
||||||
reexec.Register("p2p-node", execP2PNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecAdapter is a NodeAdapter which runs simulation nodes by executing the current binary
|
|
||||||
// as a child process.
|
|
||||||
type ExecAdapter struct {
|
|
||||||
// BaseDir is the directory under which the data directories for each
|
|
||||||
// simulation node are created.
|
|
||||||
BaseDir string
|
|
||||||
|
|
||||||
nodes map[enode.ID]*ExecNode
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExecAdapter returns an ExecAdapter which stores node data in
|
|
||||||
// subdirectories of the given base directory
|
|
||||||
func NewExecAdapter(baseDir string) *ExecAdapter {
|
|
||||||
return &ExecAdapter{
|
|
||||||
BaseDir: baseDir,
|
|
||||||
nodes: make(map[enode.ID]*ExecNode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the adapter for logging purposes
|
|
||||||
func (e *ExecAdapter) Name() string {
|
|
||||||
return "exec-adapter"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNode returns a new ExecNode using the given config
|
|
||||||
func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
|
|
||||||
if len(config.Lifecycles) == 0 {
|
|
||||||
return nil, errors.New("node must have at least one service lifecycle")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, service := range config.Lifecycles {
|
|
||||||
if _, exists := lifecycleConstructorFuncs[service]; !exists {
|
|
||||||
return nil, fmt.Errorf("unknown node service %q", service)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the node directory using the first 12 characters of the ID
|
|
||||||
// as Unix socket paths cannot be longer than 256 characters
|
|
||||||
dir := filepath.Join(e.BaseDir, config.ID.String()[:12])
|
|
||||||
if err := os.Mkdir(dir, 0755); err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating node directory: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := config.initDummyEnode()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate the config
|
|
||||||
conf := &execNodeConfig{
|
|
||||||
Stack: node.DefaultConfig,
|
|
||||||
Node: config,
|
|
||||||
}
|
|
||||||
if config.DataDir != "" {
|
|
||||||
conf.Stack.DataDir = config.DataDir
|
|
||||||
} else {
|
|
||||||
conf.Stack.DataDir = filepath.Join(dir, "data")
|
|
||||||
}
|
|
||||||
|
|
||||||
// these parameters are crucial for execadapter node to run correctly
|
|
||||||
conf.Stack.WSHost = "127.0.0.1"
|
|
||||||
conf.Stack.WSPort = 0
|
|
||||||
conf.Stack.WSOrigins = []string{"*"}
|
|
||||||
conf.Stack.WSExposeAll = true
|
|
||||||
conf.Stack.P2P.EnableMsgEvents = config.EnableMsgEvents
|
|
||||||
conf.Stack.P2P.NoDiscovery = true
|
|
||||||
conf.Stack.P2P.NAT = nil
|
|
||||||
|
|
||||||
// Listen on a localhost port, which we set when we
|
|
||||||
// initialise NodeConfig (usually a random port)
|
|
||||||
conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port)
|
|
||||||
|
|
||||||
node := &ExecNode{
|
|
||||||
ID: config.ID,
|
|
||||||
Dir: dir,
|
|
||||||
Config: conf,
|
|
||||||
adapter: e,
|
|
||||||
}
|
|
||||||
node.newCmd = node.execCommand
|
|
||||||
e.nodes[node.ID] = node
|
|
||||||
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecNode starts a simulation node by exec'ing the current binary and
|
|
||||||
// running the configured services
|
|
||||||
type ExecNode struct {
|
|
||||||
ID enode.ID
|
|
||||||
Dir string
|
|
||||||
Config *execNodeConfig
|
|
||||||
Cmd *exec.Cmd
|
|
||||||
Info *p2p.NodeInfo
|
|
||||||
|
|
||||||
adapter *ExecAdapter
|
|
||||||
client *rpc.Client
|
|
||||||
wsAddr string
|
|
||||||
newCmd func() *exec.Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addr returns the node's enode URL
|
|
||||||
func (n *ExecNode) Addr() []byte {
|
|
||||||
if n.Info == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(n.Info.Enode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client returns an rpc.Client which can be used to communicate with the
|
|
||||||
// underlying services (it is set once the node has started)
|
|
||||||
func (n *ExecNode) Client() (*rpc.Client, error) {
|
|
||||||
return n.client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start exec's the node passing the ID and service as command line arguments
|
|
||||||
// and the node config encoded as JSON in an environment variable.
|
|
||||||
func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
|
|
||||||
if n.Cmd != nil {
|
|
||||||
return errors.New("already started")
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
n.Stop()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// encode a copy of the config containing the snapshot
|
|
||||||
confCopy := *n.Config
|
|
||||||
confCopy.Snapshots = snapshots
|
|
||||||
confCopy.PeerAddrs = make(map[string]string)
|
|
||||||
|
|
||||||
for id, node := range n.adapter.nodes {
|
|
||||||
confCopy.PeerAddrs[id.String()] = node.wsAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
confData, err := json.Marshal(confCopy)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error generating node config: %s", err)
|
|
||||||
}
|
|
||||||
// expose the admin namespace via websocket if it's not enabled
|
|
||||||
exposed := confCopy.Stack.WSExposeAll
|
|
||||||
if !exposed {
|
|
||||||
for _, api := range confCopy.Stack.WSModules {
|
|
||||||
if api == "admin" {
|
|
||||||
exposed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exposed {
|
|
||||||
confCopy.Stack.WSModules = append(confCopy.Stack.WSModules, "admin")
|
|
||||||
}
|
|
||||||
// start the one-shot server that waits for startup information
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
statusURL, statusC := n.waitForStartupJSON(ctx)
|
|
||||||
|
|
||||||
// start the node
|
|
||||||
cmd := n.newCmd()
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Env = append(os.Environ(),
|
|
||||||
envStatusURL+"="+statusURL,
|
|
||||||
envNodeConfig+"="+string(confData),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return fmt.Errorf("error starting node: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Cmd = cmd
|
|
||||||
|
|
||||||
// Wait for the node to start.
|
|
||||||
status := <-statusC
|
|
||||||
if status.Err != "" {
|
|
||||||
return errors.New(status.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := rpc.DialWebsocket(ctx, status.WSEndpoint, "")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't connect to RPC server: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node ready :)
|
|
||||||
n.client = client
|
|
||||||
n.wsAddr = status.WSEndpoint
|
|
||||||
n.Info = status.NodeInfo
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitForStartupJSON runs a one-shot HTTP server to receive a startup report.
|
|
||||||
func (n *ExecNode) waitForStartupJSON(ctx context.Context) (string, chan nodeStartupJSON) {
|
|
||||||
var (
|
|
||||||
ch = make(chan nodeStartupJSON, 1)
|
|
||||||
quitOnce sync.Once
|
|
||||||
srv http.Server
|
|
||||||
)
|
|
||||||
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
ch <- nodeStartupJSON{Err: err.Error()}
|
|
||||||
return "", ch
|
|
||||||
}
|
|
||||||
|
|
||||||
quit := func(status nodeStartupJSON) {
|
|
||||||
quitOnce.Do(func() {
|
|
||||||
l.Close()
|
|
||||||
ch <- status
|
|
||||||
})
|
|
||||||
}
|
|
||||||
srv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var status nodeStartupJSON
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&status); err != nil {
|
|
||||||
status.Err = fmt.Sprintf("can't decode startup report: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
quit(status)
|
|
||||||
})
|
|
||||||
// Run the HTTP server, but don't wait forever and shut it down
|
|
||||||
// if the context is canceled.
|
|
||||||
go srv.Serve(l)
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
quit(nodeStartupJSON{Err: "didn't get startup report"})
|
|
||||||
}()
|
|
||||||
|
|
||||||
url := "http://" + l.Addr().String()
|
|
||||||
|
|
||||||
return url, ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// execCommand returns a command which runs the node locally by exec'ing
|
|
||||||
// the current binary but setting argv[0] to "p2p-node" so that the child
|
|
||||||
// runs execP2PNode
|
|
||||||
func (n *ExecNode) execCommand() *exec.Cmd {
|
|
||||||
return &exec.Cmd{
|
|
||||||
Path: reexec.Self(),
|
|
||||||
Args: []string{"p2p-node", strings.Join(n.Config.Node.Lifecycles, ","), n.ID.String()},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop stops the node by first sending SIGTERM and then SIGKILL if the node
|
|
||||||
// doesn't stop within 5s
|
|
||||||
func (n *ExecNode) Stop() error {
|
|
||||||
if n.Cmd == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
n.Cmd = nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
if n.client != nil {
|
|
||||||
n.client.Close()
|
|
||||||
n.client = nil
|
|
||||||
n.wsAddr = ""
|
|
||||||
n.Info = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
|
||||||
return n.Cmd.Process.Kill()
|
|
||||||
}
|
|
||||||
|
|
||||||
waitErr := make(chan error, 1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
waitErr <- n.Cmd.Wait()
|
|
||||||
}()
|
|
||||||
timer := time.NewTimer(5 * time.Second)
|
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-waitErr:
|
|
||||||
return err
|
|
||||||
case <-timer.C:
|
|
||||||
return n.Cmd.Process.Kill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeInfo returns information about the node
|
|
||||||
func (n *ExecNode) NodeInfo() *p2p.NodeInfo {
|
|
||||||
info := &p2p.NodeInfo{
|
|
||||||
ID: n.ID.String(),
|
|
||||||
}
|
|
||||||
if n.client != nil {
|
|
||||||
n.client.Call(&info, "admin_nodeInfo")
|
|
||||||
}
|
|
||||||
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeRPC serves RPC requests over the given connection by dialling the
|
|
||||||
// node's WebSocket address and joining the two connections
|
|
||||||
func (n *ExecNode) ServeRPC(clientConn *websocket.Conn) error {
|
|
||||||
conn, _, err := websocket.DefaultDialer.Dial(n.wsAddr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
wg.Add(2)
|
|
||||||
|
|
||||||
go wsCopy(&wg, conn, clientConn)
|
|
||||||
go wsCopy(&wg, clientConn, conn)
|
|
||||||
wg.Wait()
|
|
||||||
conn.Close()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsCopy(wg *sync.WaitGroup, src, dst *websocket.Conn) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for {
|
|
||||||
msgType, r, err := src.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := dst.NextWriter(msgType)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(w, r); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snapshots creates snapshots of the services by calling the
|
|
||||||
// simulation_snapshot RPC method
|
|
||||||
func (n *ExecNode) Snapshots() (map[string][]byte, error) {
|
|
||||||
if n.client == nil {
|
|
||||||
return nil, errors.New("RPC not started")
|
|
||||||
}
|
|
||||||
|
|
||||||
var snapshots map[string][]byte
|
|
||||||
|
|
||||||
return snapshots, n.client.Call(&snapshots, "simulation_snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// execNodeConfig is used to serialize the node configuration so it can be
|
|
||||||
// passed to the child process as a JSON encoded environment variable
|
|
||||||
type execNodeConfig struct {
|
|
||||||
Stack node.Config `json:"stack"`
|
|
||||||
Node *NodeConfig `json:"node"`
|
|
||||||
Snapshots map[string][]byte `json:"snapshots,omitempty"`
|
|
||||||
PeerAddrs map[string]string `json:"peer_addrs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func initLogging() {
|
|
||||||
// Initialize the logging by default first.
|
|
||||||
var innerHandler slog.Handler
|
|
||||||
innerHandler = slog.NewTextHandler(os.Stderr, nil)
|
|
||||||
glogger := log.NewGlogHandler(innerHandler)
|
|
||||||
glogger.Verbosity(log.LevelInfo)
|
|
||||||
log.SetDefault(log.NewLogger(glogger))
|
|
||||||
|
|
||||||
confEnv := os.Getenv(envNodeConfig)
|
|
||||||
if confEnv == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var conf execNodeConfig
|
|
||||||
if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var writer = os.Stderr
|
|
||||||
|
|
||||||
if conf.Node.LogFile != "" {
|
|
||||||
logWriter, err := os.Create(conf.Node.LogFile)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writer = logWriter
|
|
||||||
}
|
|
||||||
var verbosity = log.LevelInfo
|
|
||||||
if conf.Node.LogVerbosity <= log.LevelTrace && conf.Node.LogVerbosity >= log.LevelCrit {
|
|
||||||
verbosity = log.FromLegacyLevel(int(conf.Node.LogVerbosity))
|
|
||||||
}
|
|
||||||
// Reinitialize the logger
|
|
||||||
innerHandler = log.NewTerminalHandler(writer, true)
|
|
||||||
glogger = log.NewGlogHandler(innerHandler)
|
|
||||||
glogger.Verbosity(verbosity)
|
|
||||||
log.SetDefault(log.NewLogger(glogger))
|
|
||||||
}
|
|
||||||
|
|
||||||
// execP2PNode starts a simulation node when the current binary is executed with
|
|
||||||
// argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2]
|
|
||||||
// and the node config from an environment variable.
|
|
||||||
func execP2PNode() {
|
|
||||||
initLogging()
|
|
||||||
|
|
||||||
statusURL := os.Getenv(envStatusURL)
|
|
||||||
if statusURL == "" {
|
|
||||||
log.Crit("missing " + envStatusURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the node and gather startup report.
|
|
||||||
var status nodeStartupJSON
|
|
||||||
|
|
||||||
stack, stackErr := startExecNodeStack()
|
|
||||||
if stackErr != nil {
|
|
||||||
status.Err = stackErr.Error()
|
|
||||||
} else {
|
|
||||||
status.WSEndpoint = stack.WSEndpoint()
|
|
||||||
status.NodeInfo = stack.Server().NodeInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send status to the host.
|
|
||||||
cli := &http.Client{
|
|
||||||
Timeout: 10 * time.Second,
|
|
||||||
}
|
|
||||||
statusJSON, _ := json.Marshal(status)
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, statusURL, bytes.NewReader(statusJSON))
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Can't build request", "url", statusURL, "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Can't post startup info", "url", statusURL, "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
if stackErr != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the stack if we get a SIGTERM signal.
|
|
||||||
go func() {
|
|
||||||
sigc := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigc, syscall.SIGTERM)
|
|
||||||
|
|
||||||
defer signal.Stop(sigc)
|
|
||||||
<-sigc
|
|
||||||
log.Info("Received SIGTERM, shutting down...")
|
|
||||||
stack.Close()
|
|
||||||
}()
|
|
||||||
stack.Wait() // Wait for the stack to exit.
|
|
||||||
}
|
|
||||||
|
|
||||||
func startExecNodeStack() (*node.Node, error) {
|
|
||||||
// read the services from argv
|
|
||||||
serviceNames := strings.Split(os.Args[1], ",")
|
|
||||||
|
|
||||||
// decode the config
|
|
||||||
confEnv := os.Getenv(envNodeConfig)
|
|
||||||
if confEnv == "" {
|
|
||||||
return nil, errors.New("missing " + envNodeConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
var conf execNodeConfig
|
|
||||||
if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding %s: %v", envNodeConfig, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create enode record
|
|
||||||
nodeTcpConn, _ := net.ResolveTCPAddr("tcp", conf.Stack.P2P.ListenAddr)
|
|
||||||
if nodeTcpConn.IP == nil {
|
|
||||||
nodeTcpConn.IP = net.IPv4(127, 0, 0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.Node.initEnode(nodeTcpConn.IP, nodeTcpConn.Port, nodeTcpConn.Port)
|
|
||||||
conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
|
|
||||||
conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
|
|
||||||
|
|
||||||
// initialize the devp2p stack
|
|
||||||
stack, err := node.New(&conf.Stack)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating node stack: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the services, collecting them into a map so they can
|
|
||||||
// be accessed by the snapshot API.
|
|
||||||
services := make(map[string]node.Lifecycle, len(serviceNames))
|
|
||||||
|
|
||||||
for _, name := range serviceNames {
|
|
||||||
lifecycleFunc, exists := lifecycleConstructorFuncs[name]
|
|
||||||
if !exists {
|
|
||||||
return nil, fmt.Errorf("unknown node service %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := &ServiceContext{
|
|
||||||
RPCDialer: &wsRPCDialer{addrs: conf.PeerAddrs},
|
|
||||||
Config: conf.Node,
|
|
||||||
}
|
|
||||||
if conf.Snapshots != nil {
|
|
||||||
ctx.Snapshot = conf.Snapshots[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
service, err := lifecycleFunc(ctx, stack)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
services[name] = service
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the snapshot API.
|
|
||||||
stack.RegisterAPIs([]rpc.API{{
|
|
||||||
Namespace: "simulation",
|
|
||||||
Service: SnapshotAPI{services},
|
|
||||||
}})
|
|
||||||
|
|
||||||
if err = stack.Start(); err != nil {
|
|
||||||
err = fmt.Errorf("error starting stack: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
envStatusURL = "_P2P_STATUS_URL"
|
|
||||||
envNodeConfig = "_P2P_NODE_CONFIG"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nodeStartupJSON is sent to the simulation host after startup.
|
|
||||||
type nodeStartupJSON struct {
|
|
||||||
Err string
|
|
||||||
WSEndpoint string
|
|
||||||
NodeInfo *p2p.NodeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// SnapshotAPI provides an RPC method to create snapshots of services
|
|
||||||
type SnapshotAPI struct {
|
|
||||||
services map[string]node.Lifecycle
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api SnapshotAPI) Snapshot() (map[string][]byte, error) {
|
|
||||||
snapshots := make(map[string][]byte)
|
|
||||||
|
|
||||||
for name, service := range api.services {
|
|
||||||
if s, ok := service.(interface {
|
|
||||||
Snapshot() ([]byte, error)
|
|
||||||
}); ok {
|
|
||||||
snap, err := s.Snapshot()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots[name] = snap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return snapshots, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type wsRPCDialer struct {
|
|
||||||
addrs map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialRPC implements the RPCDialer interface by creating a WebSocket RPC
|
|
||||||
// client of the given node
|
|
||||||
func (w *wsRPCDialer) DialRPC(id enode.ID) (*rpc.Client, error) {
|
|
||||||
addr, ok := w.addrs[id.String()]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown node: %s", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rpc.DialWebsocket(context.Background(), addr, "http://localhost")
|
|
||||||
}
|
|
||||||
|
|
@ -1,369 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package adapters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"maps"
|
|
||||||
"math"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SimAdapter is a NodeAdapter which creates in-memory simulation nodes and
|
|
||||||
// connects them using net.Pipe
|
|
||||||
type SimAdapter struct {
|
|
||||||
pipe func() (net.Conn, net.Conn, error)
|
|
||||||
mtx sync.RWMutex
|
|
||||||
nodes map[enode.ID]*SimNode
|
|
||||||
lifecycles LifecycleConstructors
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSimAdapter creates a SimAdapter which is capable of running in-memory
|
|
||||||
// simulation nodes running any of the given services (the services to run on a
|
|
||||||
// particular node are passed to the NewNode function in the NodeConfig)
|
|
||||||
// the adapter uses a net.Pipe for in-memory simulated network connections
|
|
||||||
func NewSimAdapter(services LifecycleConstructors) *SimAdapter {
|
|
||||||
return &SimAdapter{
|
|
||||||
pipe: pipes.NetPipe,
|
|
||||||
nodes: make(map[enode.ID]*SimNode),
|
|
||||||
lifecycles: services,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the adapter for logging purposes
|
|
||||||
func (s *SimAdapter) Name() string {
|
|
||||||
return "sim-adapter"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNode returns a new SimNode using the given config
|
|
||||||
func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
|
||||||
s.mtx.Lock()
|
|
||||||
defer s.mtx.Unlock()
|
|
||||||
|
|
||||||
id := config.ID
|
|
||||||
// verify that the node has a private key in the config
|
|
||||||
if config.PrivateKey == nil {
|
|
||||||
return nil, fmt.Errorf("node is missing private key: %s", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check a node with the ID doesn't already exist
|
|
||||||
if _, exists := s.nodes[id]; exists {
|
|
||||||
return nil, fmt.Errorf("node already exists: %s", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the services are valid
|
|
||||||
if len(config.Lifecycles) == 0 {
|
|
||||||
return nil, errors.New("node must have at least one service")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, service := range config.Lifecycles {
|
|
||||||
if _, exists := s.lifecycles[service]; !exists {
|
|
||||||
return nil, fmt.Errorf("unknown node service %q", service)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := config.initDummyEnode()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := node.New(&node.Config{
|
|
||||||
P2P: p2p.Config{
|
|
||||||
PrivateKey: config.PrivateKey,
|
|
||||||
MaxPeers: math.MaxInt32,
|
|
||||||
NoDiscovery: true,
|
|
||||||
Dialer: s,
|
|
||||||
EnableMsgEvents: config.EnableMsgEvents,
|
|
||||||
},
|
|
||||||
ExternalSigner: config.ExternalSigner,
|
|
||||||
Logger: log.New("node.id", id.String()),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
simNode := &SimNode{
|
|
||||||
ID: id,
|
|
||||||
config: config,
|
|
||||||
node: n,
|
|
||||||
adapter: s,
|
|
||||||
running: make(map[string]node.Lifecycle),
|
|
||||||
}
|
|
||||||
s.nodes[id] = simNode
|
|
||||||
|
|
||||||
return simNode, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial implements the p2p.NodeDialer interface by connecting to the node using
|
|
||||||
// an in-memory net.Pipe
|
|
||||||
func (s *SimAdapter) Dial(ctx context.Context, dest *enode.Node) (conn net.Conn, err error) {
|
|
||||||
node, ok := s.GetNode(dest.ID())
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown node: %s", dest.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := node.Server()
|
|
||||||
if srv == nil {
|
|
||||||
return nil, fmt.Errorf("node not running: %s", dest.ID())
|
|
||||||
}
|
|
||||||
// SimAdapter.pipe is net.Pipe (NewSimAdapter)
|
|
||||||
pipe1, pipe2, err := s.pipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// this is simulated 'listening'
|
|
||||||
// asynchronously call the dialed destination node's p2p server
|
|
||||||
// to set up connection on the 'listening' side
|
|
||||||
go srv.SetupConn(pipe1, 0, nil)
|
|
||||||
|
|
||||||
return pipe2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialRPC implements the RPCDialer interface by creating an in-memory RPC
|
|
||||||
// client of the given node
|
|
||||||
func (s *SimAdapter) DialRPC(id enode.ID) (*rpc.Client, error) {
|
|
||||||
node, ok := s.GetNode(id)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown node: %s", id)
|
|
||||||
}
|
|
||||||
return node.node.Attach(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNode returns the node with the given ID if it exists
|
|
||||||
func (s *SimAdapter) GetNode(id enode.ID) (*SimNode, bool) {
|
|
||||||
s.mtx.RLock()
|
|
||||||
defer s.mtx.RUnlock()
|
|
||||||
node, ok := s.nodes[id]
|
|
||||||
|
|
||||||
return node, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimNode is an in-memory simulation node which connects to other nodes using
|
|
||||||
// net.Pipe (see SimAdapter.Dial), running devp2p protocols directly over that
|
|
||||||
// pipe
|
|
||||||
type SimNode struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
ID enode.ID
|
|
||||||
config *NodeConfig
|
|
||||||
adapter *SimAdapter
|
|
||||||
node *node.Node
|
|
||||||
running map[string]node.Lifecycle
|
|
||||||
client *rpc.Client
|
|
||||||
registerOnce sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the underlying node.Node to release
|
|
||||||
// acquired resources.
|
|
||||||
func (sn *SimNode) Close() error {
|
|
||||||
return sn.node.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addr returns the node's discovery address
|
|
||||||
func (sn *SimNode) Addr() []byte {
|
|
||||||
return []byte(sn.Node().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node returns a node descriptor representing the SimNode
|
|
||||||
func (sn *SimNode) Node() *enode.Node {
|
|
||||||
return sn.config.Node()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client returns an rpc.Client which can be used to communicate with the
|
|
||||||
// underlying services (it is set once the node has started)
|
|
||||||
func (sn *SimNode) Client() (*rpc.Client, error) {
|
|
||||||
sn.lock.RLock()
|
|
||||||
defer sn.lock.RUnlock()
|
|
||||||
|
|
||||||
if sn.client == nil {
|
|
||||||
return nil, errors.New("node not started")
|
|
||||||
}
|
|
||||||
|
|
||||||
return sn.client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeRPC serves RPC requests over the given connection by creating an
|
|
||||||
// in-memory client to the node's RPC server.
|
|
||||||
func (sn *SimNode) ServeRPC(conn *websocket.Conn) error {
|
|
||||||
handler, err := sn.node.RPCHandler()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
codec := rpc.NewFuncCodec(conn, func(v any, _ bool) error { return conn.WriteJSON(v) }, conn.ReadJSON)
|
|
||||||
handler.ServeCodec(codec, 0)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snapshots creates snapshots of the services by calling the
|
|
||||||
// simulation_snapshot RPC method
|
|
||||||
func (sn *SimNode) Snapshots() (map[string][]byte, error) {
|
|
||||||
sn.lock.RLock()
|
|
||||||
services := maps.Clone(sn.running)
|
|
||||||
sn.lock.RUnlock()
|
|
||||||
|
|
||||||
if len(services) == 0 {
|
|
||||||
return nil, errors.New("no running services")
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots := make(map[string][]byte)
|
|
||||||
|
|
||||||
for name, service := range services {
|
|
||||||
if s, ok := service.(interface {
|
|
||||||
Snapshot() ([]byte, error)
|
|
||||||
}); ok {
|
|
||||||
snap, err := s.Snapshot()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots[name] = snap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return snapshots, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start registers the services and starts the underlying devp2p node
|
|
||||||
func (sn *SimNode) Start(snapshots map[string][]byte) error {
|
|
||||||
// ensure we only register the services once in the case of the node
|
|
||||||
// being stopped and then started again
|
|
||||||
var regErr error
|
|
||||||
|
|
||||||
sn.registerOnce.Do(func() {
|
|
||||||
for _, name := range sn.config.Lifecycles {
|
|
||||||
ctx := &ServiceContext{
|
|
||||||
RPCDialer: sn.adapter,
|
|
||||||
Config: sn.config,
|
|
||||||
}
|
|
||||||
if snapshots != nil {
|
|
||||||
ctx.Snapshot = snapshots[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceFunc := sn.adapter.lifecycles[name]
|
|
||||||
|
|
||||||
service, err := serviceFunc(ctx, sn.node)
|
|
||||||
if err != nil {
|
|
||||||
regErr = err
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// if the service has already been registered, don't register it again.
|
|
||||||
if _, ok := sn.running[name]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sn.running[name] = service
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if regErr != nil {
|
|
||||||
return regErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := sn.node.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an in-process RPC client
|
|
||||||
client := sn.node.Attach()
|
|
||||||
sn.lock.Lock()
|
|
||||||
sn.client = client
|
|
||||||
sn.lock.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop closes the RPC client and stops the underlying devp2p node
|
|
||||||
func (sn *SimNode) Stop() error {
|
|
||||||
sn.lock.Lock()
|
|
||||||
if sn.client != nil {
|
|
||||||
sn.client.Close()
|
|
||||||
sn.client = nil
|
|
||||||
}
|
|
||||||
sn.lock.Unlock()
|
|
||||||
|
|
||||||
return sn.node.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service returns a running service by name
|
|
||||||
func (sn *SimNode) Service(name string) node.Lifecycle {
|
|
||||||
sn.lock.RLock()
|
|
||||||
defer sn.lock.RUnlock()
|
|
||||||
|
|
||||||
return sn.running[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Services returns a copy of the underlying services
|
|
||||||
func (sn *SimNode) Services() []node.Lifecycle {
|
|
||||||
sn.lock.RLock()
|
|
||||||
defer sn.lock.RUnlock()
|
|
||||||
|
|
||||||
services := make([]node.Lifecycle, 0, len(sn.running))
|
|
||||||
for _, service := range sn.running {
|
|
||||||
services = append(services, service)
|
|
||||||
}
|
|
||||||
|
|
||||||
return services
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServiceMap returns a map by names of the underlying services
|
|
||||||
func (sn *SimNode) ServiceMap() map[string]node.Lifecycle {
|
|
||||||
sn.lock.RLock()
|
|
||||||
defer sn.lock.RUnlock()
|
|
||||||
return maps.Clone(sn.running)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server returns the underlying p2p.Server
|
|
||||||
func (sn *SimNode) Server() *p2p.Server {
|
|
||||||
return sn.node.Server()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeEvents subscribes the given channel to peer events from the
|
|
||||||
// underlying p2p.Server
|
|
||||||
func (sn *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
|
|
||||||
srv := sn.Server()
|
|
||||||
if srv == nil {
|
|
||||||
panic("node not running")
|
|
||||||
}
|
|
||||||
|
|
||||||
return srv.SubscribeEvents(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeInfo returns information about the node
|
|
||||||
func (sn *SimNode) NodeInfo() *p2p.NodeInfo {
|
|
||||||
server := sn.Server()
|
|
||||||
if server == nil {
|
|
||||||
return &p2p.NodeInfo{
|
|
||||||
ID: sn.ID.String(),
|
|
||||||
Enode: sn.Node().String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.NodeInfo()
|
|
||||||
}
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package adapters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTCPPipe(t *testing.T) {
|
|
||||||
c1, c2, err := pipes.TCPPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs := 50
|
|
||||||
size := 1024
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := make([]byte, size)
|
|
||||||
binary.PutUvarint(msg, uint64(i))
|
|
||||||
|
|
||||||
if _, err := c1.Write(msg); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := make([]byte, size)
|
|
||||||
binary.PutUvarint(msg, uint64(i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
if _, err := c2.Read(out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(msg, out) {
|
|
||||||
t.Fatalf("expected %#v, got %#v", msg, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTCPPipeBidirections(t *testing.T) {
|
|
||||||
c1, c2, err := pipes.TCPPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs := 50
|
|
||||||
size := 7
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := []byte(fmt.Sprintf("ping %02d", i))
|
|
||||||
if _, err := c1.Write(msg); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
expected := []byte(fmt.Sprintf("ping %02d", i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
if _, err := c2.Read(out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, out) {
|
|
||||||
t.Fatalf("expected %#v, got %#v", expected, out)
|
|
||||||
} else {
|
|
||||||
msg := []byte(fmt.Sprintf("pong %02d", i))
|
|
||||||
if _, err := c2.Write(msg); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
expected := []byte(fmt.Sprintf("pong %02d", i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
if _, err := c1.Read(out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, out) {
|
|
||||||
t.Fatalf("expected %#v, got %#v", expected, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNetPipe(t *testing.T) {
|
|
||||||
c1, c2, err := pipes.NetPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs := 50
|
|
||||||
size := 1024
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
defer wg.Wait()
|
|
||||||
|
|
||||||
// netPipe is blocking, so writes are emitted asynchronously
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := make([]byte, size)
|
|
||||||
binary.PutUvarint(msg, uint64(i))
|
|
||||||
|
|
||||||
if _, err := c1.Write(msg); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := make([]byte, size)
|
|
||||||
binary.PutUvarint(msg, uint64(i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
if _, err := c2.Read(out); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(msg, out) {
|
|
||||||
t.Errorf("expected %#v, got %#v", msg, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNetPipeBidirections(t *testing.T) {
|
|
||||||
c1, c2, err := pipes.NetPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs := 1000
|
|
||||||
size := 8
|
|
||||||
pingTemplate := "ping %03d"
|
|
||||||
pongTemplate := "pong %03d"
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
defer wg.Wait()
|
|
||||||
|
|
||||||
// netPipe is blocking, so writes are emitted asynchronously
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
msg := []byte(fmt.Sprintf(pingTemplate, i))
|
|
||||||
if _, err := c1.Write(msg); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// netPipe is blocking, so reads for pong are emitted asynchronously
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
expected := []byte(fmt.Sprintf(pongTemplate, i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
if _, err := c1.Read(out); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, out) {
|
|
||||||
t.Errorf("expected %#v, got %#v", expected, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// expect to read pings, and respond with pongs to the alternate connection
|
|
||||||
for i := 0; i < msgs; i++ {
|
|
||||||
expected := []byte(fmt.Sprintf(pingTemplate, i))
|
|
||||||
|
|
||||||
out := make([]byte, size)
|
|
||||||
|
|
||||||
_, err := c2.Read(out)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, out) {
|
|
||||||
t.Errorf("expected %#v, got %#v", expected, out)
|
|
||||||
} else {
|
|
||||||
msg := []byte(fmt.Sprintf(pongTemplate, i))
|
|
||||||
if _, err := c2.Write(msg); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,339 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package adapters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/reexec"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Node represents a node in a simulation network which is created by a
|
|
||||||
// NodeAdapter, for example:
|
|
||||||
//
|
|
||||||
// - SimNode, an in-memory node in the same process
|
|
||||||
// - ExecNode, a child process node
|
|
||||||
type Node interface {
|
|
||||||
// Addr returns the node's address (e.g. an Enode URL)
|
|
||||||
Addr() []byte
|
|
||||||
|
|
||||||
// Client returns the RPC client which is created once the node is
|
|
||||||
// up and running
|
|
||||||
Client() (*rpc.Client, error)
|
|
||||||
|
|
||||||
// ServeRPC serves RPC requests over the given connection
|
|
||||||
ServeRPC(*websocket.Conn) error
|
|
||||||
|
|
||||||
// Start starts the node with the given snapshots
|
|
||||||
Start(snapshots map[string][]byte) error
|
|
||||||
|
|
||||||
// Stop stops the node
|
|
||||||
Stop() error
|
|
||||||
|
|
||||||
// NodeInfo returns information about the node
|
|
||||||
NodeInfo() *p2p.NodeInfo
|
|
||||||
|
|
||||||
// Snapshots creates snapshots of the running services
|
|
||||||
Snapshots() (map[string][]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeAdapter is used to create Nodes in a simulation network
|
|
||||||
type NodeAdapter interface {
|
|
||||||
// Name returns the name of the adapter for logging purposes
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
// NewNode creates a new node with the given configuration
|
|
||||||
NewNode(config *NodeConfig) (Node, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeConfig is the configuration used to start a node in a simulation
|
|
||||||
// network
|
|
||||||
type NodeConfig struct {
|
|
||||||
// ID is the node's ID which is used to identify the node in the
|
|
||||||
// simulation network
|
|
||||||
ID enode.ID
|
|
||||||
|
|
||||||
// PrivateKey is the node's private key which is used by the devp2p
|
|
||||||
// stack to encrypt communications
|
|
||||||
PrivateKey *ecdsa.PrivateKey
|
|
||||||
|
|
||||||
// Enable peer events for Msgs
|
|
||||||
EnableMsgEvents bool
|
|
||||||
|
|
||||||
// Name is a human friendly name for the node like "node01"
|
|
||||||
Name string
|
|
||||||
|
|
||||||
// Use an existing database instead of a temporary one if non-empty
|
|
||||||
DataDir string
|
|
||||||
|
|
||||||
// Lifecycles are the names of the service lifecycles which should be run when
|
|
||||||
// starting the node (for SimNodes it should be the names of service lifecycles
|
|
||||||
// contained in SimAdapter.lifecycles, for other nodes it should be
|
|
||||||
// service lifecycles registered by calling the RegisterLifecycle function)
|
|
||||||
Lifecycles []string
|
|
||||||
|
|
||||||
// Properties are the names of the properties this node should hold
|
|
||||||
// within running services (e.g. "bootnode", "lightnode" or any custom values)
|
|
||||||
// These values need to be checked and acted upon by node Services
|
|
||||||
Properties []string
|
|
||||||
|
|
||||||
// ExternalSigner specifies an external URI for a clef-type signer
|
|
||||||
ExternalSigner string
|
|
||||||
|
|
||||||
// Enode
|
|
||||||
node *enode.Node
|
|
||||||
|
|
||||||
// ENR Record with entries to overwrite
|
|
||||||
Record enr.Record
|
|
||||||
|
|
||||||
// function to sanction or prevent suggesting a peer
|
|
||||||
Reachable func(id enode.ID) bool
|
|
||||||
|
|
||||||
Port uint16
|
|
||||||
|
|
||||||
// LogFile is the log file name of the p2p node at runtime.
|
|
||||||
//
|
|
||||||
// The default value is empty so that the default log writer
|
|
||||||
// is the system standard output.
|
|
||||||
LogFile string
|
|
||||||
|
|
||||||
// LogVerbosity is the log verbosity of the p2p node at runtime.
|
|
||||||
//
|
|
||||||
// The default verbosity is INFO.
|
|
||||||
LogVerbosity slog.Level
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
|
|
||||||
// all fields as strings
|
|
||||||
type nodeConfigJSON struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
PrivateKey string `json:"private_key"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Lifecycles []string `json:"lifecycles"`
|
|
||||||
Properties []string `json:"properties"`
|
|
||||||
EnableMsgEvents bool `json:"enable_msg_events"`
|
|
||||||
Port uint16 `json:"port"`
|
|
||||||
LogFile string `json:"logfile"`
|
|
||||||
LogVerbosity int `json:"log_verbosity"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface by encoding the config
|
|
||||||
// fields as strings
|
|
||||||
func (n *NodeConfig) MarshalJSON() ([]byte, error) {
|
|
||||||
confJSON := nodeConfigJSON{
|
|
||||||
ID: n.ID.String(),
|
|
||||||
Name: n.Name,
|
|
||||||
Lifecycles: n.Lifecycles,
|
|
||||||
Properties: n.Properties,
|
|
||||||
Port: n.Port,
|
|
||||||
EnableMsgEvents: n.EnableMsgEvents,
|
|
||||||
LogFile: n.LogFile,
|
|
||||||
LogVerbosity: int(n.LogVerbosity),
|
|
||||||
}
|
|
||||||
if n.PrivateKey != nil {
|
|
||||||
confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(confJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements the json.Unmarshaler interface by decoding the json
|
|
||||||
// string values into the config fields
|
|
||||||
func (n *NodeConfig) UnmarshalJSON(data []byte) error {
|
|
||||||
var confJSON nodeConfigJSON
|
|
||||||
if err := json.Unmarshal(data, &confJSON); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if confJSON.ID != "" {
|
|
||||||
if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if confJSON.PrivateKey != "" {
|
|
||||||
key, err := hex.DecodeString(confJSON.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
privKey, err := crypto.ToECDSA(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
n.PrivateKey = privKey
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Name = confJSON.Name
|
|
||||||
n.Lifecycles = confJSON.Lifecycles
|
|
||||||
n.Properties = confJSON.Properties
|
|
||||||
n.Port = confJSON.Port
|
|
||||||
n.EnableMsgEvents = confJSON.EnableMsgEvents
|
|
||||||
n.LogFile = confJSON.LogFile
|
|
||||||
n.LogVerbosity = slog.Level(confJSON.LogVerbosity)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node returns the node descriptor represented by the config.
|
|
||||||
func (n *NodeConfig) Node() *enode.Node {
|
|
||||||
return n.node
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomNodeConfig returns node configuration with a randomly generated ID and
|
|
||||||
// PrivateKey
|
|
||||||
func RandomNodeConfig() *NodeConfig {
|
|
||||||
prvkey, err := crypto.GenerateKey()
|
|
||||||
if err != nil {
|
|
||||||
panic("unable to generate key")
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := assignTCPPort()
|
|
||||||
if err != nil {
|
|
||||||
panic("unable to assign tcp port")
|
|
||||||
}
|
|
||||||
|
|
||||||
enodId := enode.PubkeyToIDV4(&prvkey.PublicKey)
|
|
||||||
|
|
||||||
return &NodeConfig{
|
|
||||||
PrivateKey: prvkey,
|
|
||||||
ID: enodId,
|
|
||||||
Name: fmt.Sprintf("node_%s", enodId.String()),
|
|
||||||
Port: port,
|
|
||||||
EnableMsgEvents: true,
|
|
||||||
LogVerbosity: log.LvlInfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assignTCPPort() (uint16, error) {
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Close()
|
|
||||||
|
|
||||||
_, port, err := net.SplitHostPort(l.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := strconv.ParseUint(port, 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint16(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServiceContext is a collection of options and methods which can be utilised
|
|
||||||
// when starting services
|
|
||||||
type ServiceContext struct {
|
|
||||||
RPCDialer
|
|
||||||
|
|
||||||
Config *NodeConfig
|
|
||||||
Snapshot []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCDialer is used when initialising services which need to connect to
|
|
||||||
// other nodes in the network (for example a simulated Swarm node which needs
|
|
||||||
// to connect to a Geth node to resolve ENS names)
|
|
||||||
type RPCDialer interface {
|
|
||||||
DialRPC(id enode.ID) (*rpc.Client, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LifecycleConstructor allows a Lifecycle to be constructed during node start-up.
|
|
||||||
// While the service-specific package usually takes care of Lifecycle creation and registration,
|
|
||||||
// for testing purposes, it is useful to be able to construct a Lifecycle on spot.
|
|
||||||
type LifecycleConstructor func(ctx *ServiceContext, stack *node.Node) (node.Lifecycle, error)
|
|
||||||
|
|
||||||
// LifecycleConstructors stores LifecycleConstructor functions to call during node start-up.
|
|
||||||
type LifecycleConstructors map[string]LifecycleConstructor
|
|
||||||
|
|
||||||
// lifecycleConstructorFuncs is a map of registered services which are used to boot devp2p
|
|
||||||
// nodes
|
|
||||||
var lifecycleConstructorFuncs = make(LifecycleConstructors)
|
|
||||||
|
|
||||||
// RegisterLifecycles registers the given Services which can then be used to
|
|
||||||
// start devp2p nodes using either the Exec or Docker adapters.
|
|
||||||
//
|
|
||||||
// It should be called in an init function so that it has the opportunity to
|
|
||||||
// execute the services before main() is called.
|
|
||||||
func RegisterLifecycles(lifecycles LifecycleConstructors) {
|
|
||||||
for name, f := range lifecycles {
|
|
||||||
if _, exists := lifecycleConstructorFuncs[name]; exists {
|
|
||||||
panic(fmt.Sprintf("node service already exists: %q", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleConstructorFuncs[name] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we have registered the services, run reexec.Init() which will
|
|
||||||
// potentially start one of the services if the current binary has
|
|
||||||
// been exec'd with argv[0] set to "p2p-node"
|
|
||||||
if reexec.Init() {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds the host part to the configuration's ENR, signs it
|
|
||||||
// creates and adds the corresponding enode object to the configuration
|
|
||||||
func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error {
|
|
||||||
enrIp := enr.IP(ip)
|
|
||||||
n.Record.Set(&enrIp)
|
|
||||||
|
|
||||||
enrTcpPort := enr.TCP(tcpport)
|
|
||||||
n.Record.Set(&enrTcpPort)
|
|
||||||
|
|
||||||
enrUdpPort := enr.UDP(udpport)
|
|
||||||
n.Record.Set(&enrUdpPort)
|
|
||||||
|
|
||||||
err := enode.SignV4(&n.Record, n.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to generate ENR: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nod, err := enode.New(enode.V4ID{}, &n.Record)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create enode: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("simnode new", "record", n.Record)
|
|
||||||
n.node = nod
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NodeConfig) initDummyEnode() error {
|
|
||||||
return n.initEnode(net.IPv4(127, 0, 0, 1), int(n.Port), 0)
|
|
||||||
}
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package simulations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNodeNotFound = errors.New("node not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConnectToLastNode connects the node with provided NodeID
|
|
||||||
// to the last node that is up, and avoiding connection to self.
|
|
||||||
// It is useful when constructing a chain network topology
|
|
||||||
// when Network adds and removes nodes dynamically.
|
|
||||||
func (net *Network) ConnectToLastNode(id enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
ids := net.getUpNodeIDs()
|
|
||||||
|
|
||||||
l := len(ids)
|
|
||||||
if l < 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
last := ids[l-1]
|
|
||||||
if last == id {
|
|
||||||
last = ids[l-2]
|
|
||||||
}
|
|
||||||
|
|
||||||
return net.connectNotConnected(last, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectToRandomNode connects the node with provided NodeID
|
|
||||||
// to a random node that is up.
|
|
||||||
func (net *Network) ConnectToRandomNode(id enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
selected := net.getRandomUpNode(id)
|
|
||||||
if selected == nil {
|
|
||||||
return ErrNodeNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return net.connectNotConnected(selected.ID(), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectNodesFull connects all nodes one to another.
|
|
||||||
// It provides a complete connectivity in the network
|
|
||||||
// which should be rarely needed.
|
|
||||||
func (net *Network) ConnectNodesFull(ids []enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
if ids == nil {
|
|
||||||
ids = net.getUpNodeIDs()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, lid := range ids {
|
|
||||||
for _, rid := range ids[i+1:] {
|
|
||||||
if err = net.connectNotConnected(lid, rid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectNodesChain connects all nodes in a chain topology.
|
|
||||||
// If ids argument is nil, all nodes that are up will be connected.
|
|
||||||
func (net *Network) ConnectNodesChain(ids []enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
return net.connectNodesChain(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (net *Network) connectNodesChain(ids []enode.ID) (err error) {
|
|
||||||
if ids == nil {
|
|
||||||
ids = net.getUpNodeIDs()
|
|
||||||
}
|
|
||||||
|
|
||||||
l := len(ids)
|
|
||||||
for i := 0; i < l-1; i++ {
|
|
||||||
if err := net.connectNotConnected(ids[i], ids[i+1]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectNodesRing connects all nodes in a ring topology.
|
|
||||||
// If ids argument is nil, all nodes that are up will be connected.
|
|
||||||
func (net *Network) ConnectNodesRing(ids []enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
if ids == nil {
|
|
||||||
ids = net.getUpNodeIDs()
|
|
||||||
}
|
|
||||||
|
|
||||||
l := len(ids)
|
|
||||||
if l < 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := net.connectNodesChain(ids); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return net.connectNotConnected(ids[l-1], ids[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectNodesStar connects all nodes into a star topology
|
|
||||||
// If ids argument is nil, all nodes that are up will be connected.
|
|
||||||
func (net *Network) ConnectNodesStar(ids []enode.ID, center enode.ID) (err error) {
|
|
||||||
net.lock.Lock()
|
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
if ids == nil {
|
|
||||||
ids = net.getUpNodeIDs()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, id := range ids {
|
|
||||||
if center == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := net.connectNotConnected(center, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (net *Network) connectNotConnected(oneID, otherID enode.ID) error {
|
|
||||||
return ignoreAlreadyConnectedErr(net.connect(oneID, otherID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ignoreAlreadyConnectedErr(err error) error {
|
|
||||||
if err == nil || strings.Contains(err.Error(), "already connected") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package simulations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTestNetwork(t *testing.T, nodeCount int) (*Network, []enode.ID) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
adapter := adapters.NewSimAdapter(adapters.LifecycleConstructors{
|
|
||||||
"noopwoop": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
|
|
||||||
return NewNoopService(nil), nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// create network
|
|
||||||
network := NewNetwork(adapter, &NetworkConfig{
|
|
||||||
DefaultService: "noopwoop",
|
|
||||||
})
|
|
||||||
|
|
||||||
// create and start nodes
|
|
||||||
ids := make([]enode.ID, nodeCount)
|
|
||||||
for i := range ids {
|
|
||||||
conf := adapters.RandomNodeConfig()
|
|
||||||
|
|
||||||
node, err := network.NewNodeWithConfig(conf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error creating node: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := network.Start(node.ID()); err != nil {
|
|
||||||
t.Fatalf("error starting node: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids[i] = node.ID()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(network.Conns) > 0 {
|
|
||||||
t.Fatal("no connections should exist after just adding nodes")
|
|
||||||
}
|
|
||||||
|
|
||||||
return network, ids
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectToLastNode(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, 10)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
first := ids[0]
|
|
||||||
if err := net.ConnectToLastNode(first); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
last := ids[len(ids)-1]
|
|
||||||
for i, id := range ids {
|
|
||||||
if id == first || id == last {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if net.GetConn(first, id) != nil {
|
|
||||||
t.Errorf("connection must not exist with node(ind: %v, id: %v)", i, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if net.GetConn(first, last) == nil {
|
|
||||||
t.Error("first and last node must be connected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectToRandomNode(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, 10)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
err := net.ConnectToRandomNode(ids[0])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var cc int
|
|
||||||
|
|
||||||
for i, a := range ids {
|
|
||||||
for _, b := range ids[i:] {
|
|
||||||
if net.GetConn(a, b) != nil {
|
|
||||||
cc++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cc != 1 {
|
|
||||||
t.Errorf("expected one connection, got %v", cc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectNodesFull(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
nodeCount int
|
|
||||||
}{
|
|
||||||
{name: "no node", nodeCount: 0},
|
|
||||||
{name: "single node", nodeCount: 1},
|
|
||||||
{name: "2 nodes", nodeCount: 2},
|
|
||||||
{name: "3 nodes", nodeCount: 3},
|
|
||||||
{name: "even number of nodes", nodeCount: 12},
|
|
||||||
{name: "odd number of nodes", nodeCount: 13},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, test.nodeCount)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
err := net.ConnectNodesFull(ids)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
VerifyFull(t, net, ids)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectNodesChain(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, 10)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
err := net.ConnectNodesChain(ids)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
VerifyChain(t, net, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectNodesRing(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, 10)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
err := net.ConnectNodesRing(ids)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
VerifyRing(t, net, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectNodesStar(t *testing.T) {
|
|
||||||
net, ids := newTestNetwork(t, 10)
|
|
||||||
defer net.Shutdown()
|
|
||||||
|
|
||||||
pivotIndex := 2
|
|
||||||
|
|
||||||
err := net.ConnectNodesStar(ids, ids[pivotIndex])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
VerifyStar(t, net, ids, pivotIndex)
|
|
||||||
}
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package simulations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventType is the type of event emitted by a simulation network
|
|
||||||
type EventType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// EventTypeNode is the type of event emitted when a node is either
|
|
||||||
// created, started or stopped
|
|
||||||
EventTypeNode EventType = "node"
|
|
||||||
|
|
||||||
// EventTypeConn is the type of event emitted when a connection is
|
|
||||||
// either established or dropped between two nodes
|
|
||||||
EventTypeConn EventType = "conn"
|
|
||||||
|
|
||||||
// EventTypeMsg is the type of event emitted when a p2p message it
|
|
||||||
// sent between two nodes
|
|
||||||
EventTypeMsg EventType = "msg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event is an event emitted by a simulation network
|
|
||||||
type Event struct {
|
|
||||||
// Type is the type of the event
|
|
||||||
Type EventType `json:"type"`
|
|
||||||
|
|
||||||
// Time is the time the event happened
|
|
||||||
Time time.Time `json:"time"`
|
|
||||||
|
|
||||||
// Control indicates whether the event is the result of a controlled
|
|
||||||
// action in the network
|
|
||||||
Control bool `json:"control"`
|
|
||||||
|
|
||||||
// Node is set if the type is EventTypeNode
|
|
||||||
Node *Node `json:"node,omitempty"`
|
|
||||||
|
|
||||||
// Conn is set if the type is EventTypeConn
|
|
||||||
Conn *Conn `json:"conn,omitempty"`
|
|
||||||
|
|
||||||
// Msg is set if the type is EventTypeMsg
|
|
||||||
Msg *Msg `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
//Optionally provide data (currently for simulation frontends only)
|
|
||||||
Data interface{} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEvent creates a new event for the given object which should be either a
|
|
||||||
// Node, Conn or Msg.
|
|
||||||
//
|
|
||||||
// The object is copied so that the event represents the state of the object
|
|
||||||
// when NewEvent is called.
|
|
||||||
func NewEvent(v interface{}) *Event {
|
|
||||||
event := &Event{Time: time.Now()}
|
|
||||||
switch v := v.(type) {
|
|
||||||
case *Node:
|
|
||||||
event.Type = EventTypeNode
|
|
||||||
event.Node = v.copy()
|
|
||||||
case *Conn:
|
|
||||||
event.Type = EventTypeConn
|
|
||||||
conn := *v
|
|
||||||
event.Conn = &conn
|
|
||||||
case *Msg:
|
|
||||||
event.Type = EventTypeMsg
|
|
||||||
msg := *v
|
|
||||||
event.Msg = &msg
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid event type: %T", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return event
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlEvent creates a new control event
|
|
||||||
func ControlEvent(v interface{}) *Event {
|
|
||||||
event := NewEvent(v)
|
|
||||||
event.Control = true
|
|
||||||
|
|
||||||
return event
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation of the event
|
|
||||||
func (e *Event) String() string {
|
|
||||||
switch e.Type {
|
|
||||||
case EventTypeNode:
|
|
||||||
return fmt.Sprintf("<node-event> id: %s up: %t", e.Node.ID().TerminalString(), e.Node.Up())
|
|
||||||
case EventTypeConn:
|
|
||||||
return fmt.Sprintf("<conn-event> nodes: %s->%s up: %t", e.Conn.One.TerminalString(), e.Conn.Other.TerminalString(), e.Conn.Up)
|
|
||||||
case EventTypeMsg:
|
|
||||||
return fmt.Sprintf("<msg-event> nodes: %s->%s proto: %s, code: %d, received: %t", e.Msg.One.TerminalString(), e.Msg.Other.TerminalString(), e.Msg.Protocol, e.Msg.Code, e.Msg.Received)
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# devp2p simulation examples
|
|
||||||
|
|
||||||
## ping-pong
|
|
||||||
|
|
||||||
`ping-pong.go` implements a simulation network which contains nodes running a
|
|
||||||
simple "ping-pong" protocol where nodes send a ping message to all their
|
|
||||||
connected peers every 10s and receive pong messages in return.
|
|
||||||
|
|
||||||
To run the simulation, run `go run ping-pong.go` in one terminal to start the
|
|
||||||
simulation API and `./ping-pong.sh` in another to start and connect the nodes:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go run ping-pong.go
|
|
||||||
INFO [08-15|13:53:49] using sim adapter
|
|
||||||
INFO [08-15|13:53:49] starting simulation server on 0.0.0.0:8888...
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./ping-pong.sh
|
|
||||||
---> 13:58:12 creating 10 nodes
|
|
||||||
Created node01
|
|
||||||
Started node01
|
|
||||||
...
|
|
||||||
Created node10
|
|
||||||
Started node10
|
|
||||||
---> 13:58:13 connecting node01 to all other nodes
|
|
||||||
Connected node01 to node02
|
|
||||||
...
|
|
||||||
Connected node01 to node10
|
|
||||||
---> 13:58:14 done
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the `--adapter` flag to choose the adapter type:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go run ping-pong.go --adapter exec
|
|
||||||
INFO [08-15|14:01:14] using exec adapter tmpdir=/var/folders/k6/wpsgfg4n23ddbc6f5cnw5qg00000gn/T/p2p-example992833779
|
|
||||||
INFO [08-15|14:01:14] starting simulation server on 0.0.0.0:8888...
|
|
||||||
```
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue