mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
make bor && make all successful
This commit is contained in:
commit
f09f89e1cb
125 changed files with 1631 additions and 8960 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'
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ type TransactOpts struct {
|
||||||
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)
|
||||||
|
|
||||||
|
|
@ -327,14 +328,15 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
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.
|
|
||||||
// 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}}
|
//go:embed source.go.tpl
|
||||||
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 tmplSourceGo string
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) {
|
||||||
case "capella":
|
case "capella":
|
||||||
obj = new(capella.BeaconBlock)
|
obj = new(capella.BeaconBlock)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported fork: " + forkName)
|
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, obj); err != nil {
|
if err := json.Unmarshal(data, obj); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, er
|
||||||
case "deneb":
|
case "deneb":
|
||||||
obj = new(deneb.ExecutionPayloadHeader)
|
obj = new(deneb.ExecutionPayloadHeader)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported fork: " + forkName)
|
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, obj); err != nil {
|
if err := json.Unmarshal(data, obj); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -171,8 +171,6 @@ func runCmd(ctx *cli.Context) error {
|
||||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.CreateAccount(sender)
|
|
||||||
|
|
||||||
if ctx.String(ReceiverFlag.Name) != "" {
|
if ctx.String(ReceiverFlag.Name) != "" {
|
||||||
receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
|
receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
@ -237,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,7 @@ 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) {
|
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)
|
||||||
|
|
||||||
|
|
@ -464,24 +462,28 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start auxiliary services if enabled
|
// POS-2798 - We don't really use cmd/geth for cli purposes
|
||||||
if ctx.Bool(utils.MiningEnabledFlag.Name) {
|
// And to maintain compatibility with the future upstream merges,
|
||||||
// Mining only makes sense if a full Ethereum node is running
|
// we are removing the backend ethapi.Backend param from the function signature
|
||||||
if ctx.String(utils.SyncModeFlag.Name) == "light" {
|
|
||||||
utils.Fatalf("Light clients do not support mining")
|
|
||||||
}
|
|
||||||
|
|
||||||
ethBackend, ok := backend.(*eth.EthAPIBackend)
|
// Start auxiliary services if enabled
|
||||||
if !ok {
|
// if ctx.Bool(utils.MiningEnabledFlag.Name) {
|
||||||
utils.Fatalf("Ethereum service not running")
|
// // Mining only makes sense if a full Ethereum node is running
|
||||||
}
|
// if ctx.String(utils.SyncModeFlag.Name) == "light" {
|
||||||
// Set the gas price to the limits from the CLI and start mining
|
// utils.Fatalf("Light clients do not support mining")
|
||||||
gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
// }
|
||||||
ethBackend.TxPool().SetGasTip(gasprice)
|
|
||||||
if err := ethBackend.StartMining(); err != nil {
|
// ethBackend, ok := backend.(*eth.EthAPIBackend)
|
||||||
utils.Fatalf("Failed to start mining: %v", err)
|
// 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)
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,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.
|
||||||
|
|
|
||||||
66
consensus/bor/gomock_reflect_128788329/prog.go
Normal file
66
consensus/bor/gomock_reflect_128788329/prog.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/mock/mockgen/model"
|
||||||
|
|
||||||
|
pkg_ "github.com/ethereum/go-ethereum/consensus/bor"
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = flag.String("output", "", "The output file name, or empty to use stdout.")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
its := []struct{
|
||||||
|
sym string
|
||||||
|
typ reflect.Type
|
||||||
|
}{
|
||||||
|
|
||||||
|
{ "Spanner", reflect.TypeOf((*pkg_.Spanner)(nil)).Elem()},
|
||||||
|
|
||||||
|
}
|
||||||
|
pkg := &model.Package{
|
||||||
|
// NOTE: This behaves contrary to documented behaviour if the
|
||||||
|
// package name is not the final component of the import path.
|
||||||
|
// The reflect package doesn't expose the package name, though.
|
||||||
|
Name: path.Base("github.com/ethereum/go-ethereum/consensus/bor"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, it := range its {
|
||||||
|
intf, err := model.InterfaceFromInterfaceType(it.typ)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
intf.Name = it.sym
|
||||||
|
pkg.Interfaces = append(pkg.Interfaces, intf)
|
||||||
|
}
|
||||||
|
|
||||||
|
outfile := os.Stdout
|
||||||
|
if len(*output) != 0 {
|
||||||
|
var err error
|
||||||
|
outfile, err = os.Create(*output)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := outfile.Close(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,12 @@ 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,6 +1318,9 @@ 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 {
|
||||||
|
if s.db.TrieDB().IsVerkle() {
|
||||||
|
obj.updateTrie()
|
||||||
|
} else {
|
||||||
obj.updateRoot()
|
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,
|
||||||
|
|
@ -1325,6 +1328,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
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.
|
||||||
|
|
||||||
|
|
@ -77,3 +77,4 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale
|
||||||
|
|
||||||
[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 (
|
||||||
|
|
@ -1752,7 +1753,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
|
||||||
|
|
@ -1800,20 +1801,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,34 +67,27 @@ 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
|
||||||
func (m *Memory) Len() int {
|
func (m *Memory) Len() int {
|
||||||
return len(m.store)
|
return len(m.store)
|
||||||
|
|
|
||||||
|
|
@ -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:])
|
||||||
|
|
|
||||||
|
|
@ -179,9 +179,11 @@ 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
|
// POS-2798: Default was removed from gasprice.Config
|
||||||
}
|
// 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 +265,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)
|
// POS-2798: NewOracle function definition was changed to accept (startPrice *big.Int)
|
||||||
|
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 +320,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")
|
||||||
|
}
|
||||||
|
// POS-2798: NewOracle function definition was changed to accept (startPrice *big.Int)
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -387,8 +387,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
|
|
@ -15,7 +15,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
|
||||||
|
|
@ -38,7 +38,6 @@ require (
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||||
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
|
||||||
|
|
@ -51,7 +50,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
|
||||||
|
|
@ -59,7 +58,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
|
||||||
|
|
@ -215,6 +213,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
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -945,8 +945,8 @@ 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.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
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=
|
||||||
|
|
@ -1545,8 +1545,9 @@ github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iU
|
||||||
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.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
github.com/holiman/uint256 v1.2.3/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=
|
||||||
|
|
@ -1605,7 +1606,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=
|
||||||
|
|
|
||||||
|
|
@ -1140,9 +1140,9 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
|
||||||
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 {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
if list.nextTime <= now {
|
||||||
|
if n := list.get(now, &tab.rand, tr.activeReq); n != nil {
|
||||||
tr.startRequest(tab, n)
|
tr.startRequest(tab, n)
|
||||||
tr.fast.schedule(now, &tab.rand)
|
|
||||||
}
|
}
|
||||||
if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil {
|
// Update nextTime regardless if any requests were started because
|
||||||
tr.startRequest(tab, n)
|
// current value has passed.
|
||||||
tr.slow.schedule(now, &tab.rand)
|
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,7 +141,9 @@ 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) {
|
||||||
|
return uint16(extport), nil
|
||||||
|
}
|
||||||
func (ExtIP) DeleteMapping(string, int, int) error { return 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
|
||||||
|
|
|
||||||
|
|
@ -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...
|
|
||||||
```
|
|
||||||
|
|
@ -1,181 +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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
||||||
)
|
|
||||||
|
|
||||||
var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim" or "exec")`)
|
|
||||||
|
|
||||||
// main() starts a simulation network which contains nodes running a simple
|
|
||||||
// ping-pong protocol
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// set the log level to Trace
|
|
||||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
|
|
||||||
|
|
||||||
// register a single ping-pong service
|
|
||||||
services := map[string]adapters.LifecycleConstructor{
|
|
||||||
"ping-pong": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
|
|
||||||
pps := newPingPongService(ctx.Config.ID)
|
|
||||||
stack.RegisterProtocols(pps.Protocols())
|
|
||||||
return pps, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
adapters.RegisterLifecycles(services)
|
|
||||||
|
|
||||||
// create the NodeAdapter
|
|
||||||
var adapter adapters.NodeAdapter
|
|
||||||
|
|
||||||
switch *adapterType {
|
|
||||||
case "sim":
|
|
||||||
log.Info("using sim adapter")
|
|
||||||
|
|
||||||
adapter = adapters.NewSimAdapter(services)
|
|
||||||
|
|
||||||
case "exec":
|
|
||||||
tmpdir, err := os.MkdirTemp("", "p2p-example")
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("error creating temp dir", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
log.Info("using exec adapter", "tmpdir", tmpdir)
|
|
||||||
adapter = adapters.NewExecAdapter(tmpdir)
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType))
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the HTTP API
|
|
||||||
log.Info("starting simulation server on 0.0.0.0:8888...")
|
|
||||||
|
|
||||||
network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
|
||||||
DefaultService: "ping-pong",
|
|
||||||
})
|
|
||||||
if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil {
|
|
||||||
log.Crit("error starting simulation server", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pingPongService runs a ping-pong protocol between nodes where each node
|
|
||||||
// sends a ping to all its connected peers every 10s and receives a pong in
|
|
||||||
// return
|
|
||||||
type pingPongService struct {
|
|
||||||
id enode.ID
|
|
||||||
log log.Logger
|
|
||||||
received atomic.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPingPongService(id enode.ID) *pingPongService {
|
|
||||||
return &pingPongService{
|
|
||||||
id: id,
|
|
||||||
log: log.New("node.id", id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pingPongService) Protocols() []p2p.Protocol {
|
|
||||||
return []p2p.Protocol{{
|
|
||||||
Name: "ping-pong",
|
|
||||||
Version: 1,
|
|
||||||
Length: 2,
|
|
||||||
Run: p.Run,
|
|
||||||
NodeInfo: p.Info,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pingPongService) Start() error {
|
|
||||||
p.log.Info("ping-pong service starting")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pingPongService) Stop() error {
|
|
||||||
p.log.Info("ping-pong service stopping")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pingPongService) Info() interface{} {
|
|
||||||
return struct {
|
|
||||||
Received int64 `json:"received"`
|
|
||||||
}{
|
|
||||||
p.received.Load(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
pingMsgCode = iota
|
|
||||||
pongMsgCode
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run implements the ping-pong protocol which sends ping messages to the peer
|
|
||||||
// at 10s intervals, and responds to pings with pong messages.
|
|
||||||
func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
|
|
||||||
log := p.log.New("peer.id", peer.ID())
|
|
||||||
|
|
||||||
errC := make(chan error, 1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for range time.Tick(10 * time.Second) {
|
|
||||||
log.Info("sending ping")
|
|
||||||
|
|
||||||
if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
msg, err := rw.ReadMsg()
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := io.ReadAll(msg.Payload)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload))
|
|
||||||
p.received.Add(1)
|
|
||||||
if msg.Code == pingMsgCode {
|
|
||||||
log.Info("sending pong")
|
|
||||||
|
|
||||||
go p2p.Send(rw, pongMsgCode, "PONG")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return <-errC
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Boot a ping-pong network simulation using the HTTP API started by ping-pong.go
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
main() {
|
|
||||||
if ! which p2psim &>/dev/null; then
|
|
||||||
fail "missing p2psim binary (you need to build cmd/p2psim and put it in \$PATH)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "creating 10 nodes"
|
|
||||||
for i in $(seq 1 10); do
|
|
||||||
p2psim node create --name "$(node_name $i)"
|
|
||||||
p2psim node start "$(node_name $i)"
|
|
||||||
done
|
|
||||||
|
|
||||||
info "connecting node01 to all other nodes"
|
|
||||||
for i in $(seq 2 10); do
|
|
||||||
p2psim node connect "node01" "$(node_name $i)"
|
|
||||||
done
|
|
||||||
|
|
||||||
info "done"
|
|
||||||
}
|
|
||||||
|
|
||||||
node_name() {
|
|
||||||
local num=$1
|
|
||||||
echo "node$(printf '%02d' $num)"
|
|
||||||
}
|
|
||||||
|
|
||||||
info() {
|
|
||||||
echo -e "\033[1;32m---> $(date +%H:%M:%S) ${@}\033[0m"
|
|
||||||
}
|
|
||||||
|
|
||||||
fail() {
|
|
||||||
echo -e "\033[1;31mERROR: ${@}\033[0m" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue