Compare commits

...

56 Commits

Author SHA1 Message Date
winniehere
c5c36a8e2c accounts/abi: embed Go template instead of string literal (#30098)
refactor(accounts/abi): use embed pkg to split default template to file
2024-07-02 18:31:05 +02:00
Guillaume Ballet
afddce396d params: begin v1.14.7 release cycle 2024-07-02 18:31:05 +02:00
Guillaume Ballet
2b5d289b66 params: release Geth v1.14.6 2024-07-02 18:31:05 +02:00
Hteev Oli
09056601d8 core/state: fix inconsistent verkle test error messages (#29753) 2024-07-01 21:57:04 +02:00
jwasinger
41abab9e39 build: add check for stale generated files (#30037)
Co-authored-by: Felix Lange <fjl@twurst.com>
2024-07-01 17:16:15 +02:00
jwasinger
a4e338f05e accounts/usbwallet/trezor: upgrade to generate with protoc 27.1 (#30058) 2024-07-01 16:18:38 +02:00
Ceyhun Onur
7cfff30ba3 rpc: truncate call error data logs (#30028)
Co-authored-by: Felix Lange <fjl@twurst.com>
2024-06-28 20:37:58 +02:00
gitglorythegreat
06f1d077d3 all: replace division with right shift if possible (#29911) 2024-06-28 18:08:31 +02:00
maskpp
4939c25341 cmd/evm/internal/t8ntool: log writeTraceResult error message (#30038) 2024-06-28 18:05:57 +02:00
maskpp
36d67be41b core/txpool/blobpool: improve newPriceHeap function (#30050)
Co-authored-by: Felix Lange <fjl@twurst.com>
2024-06-28 15:51:27 +02:00
lilasxie
19c3c1e205 triedb/pathdb: fix flaky test in pathdb (#29901) 2024-06-28 21:15:54 +08:00
rjl493456442
045b9718d5 trie: relocate state execution logic into pathdb package (#29861) 2024-06-27 20:30:39 +08:00
Halimao
269e80b07e eth/tracers,trie: remove unnecessary check (#30071) 2024-06-27 11:29:50 +02:00
maskpp
9298d2db88 trie/trienode: remove unnecessary check in Summary (#30047) 2024-06-25 15:45:33 +02:00
maskpp
98b5930d2d core/txpool/blobpool: avoid use *map as parameter. (#30048) 2024-06-25 14:19:04 +02:00
jwasinger
ed8fd0ac09 all: stateless witness builder and (self-)cross validator (#29719)
* all: add stateless verifications

* all: simplify witness and integrate it into live geth

---------

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2024-06-25 14:48:08 +03:00
AMIR
73f7e7c087 internal/debug: remove unnecessary log level assignment (#30044)
Log level is specified in L259 so it's unnecessary to specify it for handlers (L234, L236).
2024-06-25 11:30:58 +02:00
Halimao
fe0c0b04fe accounts/keystore: use t.TempDir in test (#30052) 2024-06-25 11:24:33 +02:00
lightclient
0a651f8972 .github: add lightclient as codeowner to relevant packages (#30062) 2024-06-25 11:16:27 +02:00
lightclient
d8ea7ac2b0 cmd/blsync: use debug.Setup for logging configuration (#30065) 2024-06-25 11:14:12 +02:00
Halimao
a71f6f91fd p2p/discover: improve flaky revalidation tests (#30023) 2024-06-21 15:29:07 +02:00
rjl493456442
c10ac4f48f Revert "core/state/snapshot: tiny fixes" (#30039)
Revert "core/state/snapshot: tiny fixes (#29995)"

This reverts commit e0e45dbc32.
2024-06-21 10:42:43 +03:00
rjl493456442
e0e45dbc32 core/state/snapshot: tiny fixes (#29995) 2024-06-21 09:51:03 +08:00
David Theodore
27654d3022 p2p/rlpx: 2KB maximum size for handshake messages (#30029)
Co-authored-by: Felix Lange <fjl@twurst.com>
2024-06-20 14:08:54 +02:00
maskpp
00675c5876 trie/trienode: avoid unnecessary copy (#30019)
* avoid unnecessary copy

* delete the never used function ProofList

* eth/protocols/snap, trie/trienode: polish the code

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
2024-06-20 11:47:29 +08:00
psogv0308
27008408a5 core/txpool/blobpool: change rw-lock to r-lock (#29989) 2024-06-19 14:46:57 +02:00
Halimao
c11aac249d common: using ParseUint instead of ParseInt (#30020)
Since Decimal is defined as unsiged `uint64`, we should use `strconv.ParseUint` instead of `strconv.ParseInt` during unmarshalling.

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
2024-06-19 11:06:52 +02:00
jwasinger
0e3a0a693c trie: don't reset tracer at the end of Commit (#30024)
* trie: don't reset tracer at the end of Commit

* Update trie.go

---------

Co-authored-by: rjl493456442 <garyrong0905@gmail.com>
2024-06-19 10:58:22 +03:00
Ha DANG
67a862db9d cmd/geth, ethdb/pebble: improve database statistic (#29948)
* cmd/geth, ethdb/pebble: polish method naming and code comment

* implement db stat for pebble

* cmd, core, ethdb, internal, trie: remove db property selector

* cmd, core, ethdb: fix function description

---------

Co-authored-by: prpeh <prpeh@proton.me>
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
2024-06-19 14:47:17 +08:00
Marius van der Wijden
7cf6a63687 core/state/snapshot: acquire the lock on Release (#30011)
* core/state/snapshot: acquire the lock on release

* core/state/snapshot: only acquire read-lock when iterating
2024-06-18 10:52:49 +08:00
Dean Eigenmann
d8664490da common/math: fix out of bounds access in json unmarshalling (#30014)
Co-authored-by: Martin Holst Swende <martin@swende.se>
2024-06-17 21:53:00 +02:00
maskpp
c736b04d9b triedb/pathdb: use maps.Clone and maps.Keys (#29985) 2024-06-17 17:09:29 +02:00
maskpp
115d154392 trie, triedb/pathdb: prealloc capacity for map and slice (#29986) 2024-06-17 11:42:41 +02:00
Zoro
b78d2352ef log: fix some functions comments (#29907)
updates some docstrings
---------

Co-authored-by: rjl493456442 <garyrong0905@gmail.com>
2024-06-17 11:03:27 +02:00
Péter Szilágyi
a58e4f0674 go.mod: update Pebble to sort out a deleted upstream dependency (#30010) 2024-06-17 11:15:27 +03:00
maskpp
34b46a2f75 core/state/snapshot: add a missing lock (#30001)
* upgrade lock usage

* revert unnecessary change
2024-06-17 10:42:39 +03:00
Darioush Jalali
fd5078c779 trie/triedb: add Reader to backend interface (#29988) 2024-06-14 14:52:46 +08:00
Felföldi Zsolt
86150af2e5 beacon/light: fix shutdown issues (#29946)
* beacon/light/request: add server test for event after unsubscribe

* beacon/light/api: fixed double stream.Close()

* beacon/light/request: add checks for nil event callback function

* beacon/light/request: unlock server mutex while unsubscribing from parent
2024-06-12 16:38:19 +02:00
jwasinger
69351e8b0f core/state, eth/protocols, trie, triedb/pathdb: remove unused error from trie Commit (#29869)
* core/state, eth/protocols, trie, triedb/pathdb:  remove unused error return from trie Commit

* move set back to account-trie-update block scoping for easier readability

* address review

* undo tests submodule change

* trie:  panic if BatchSerialize returns an error in Verkle trie Commit

* trie: verkle comment nitpicks

---------

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2024-06-12 12:23:16 +03:00
jackyin
3687c34cfc accounts: avoid duplicate regex compilation (#29943)
* fix: Optimize regular initialization

* modify var name

* variable change to private types
2024-06-12 10:46:36 +03:00
Felix Lange
1e97148249 all: fix inconsistent receiver name and add lint rule for it (#29974)
* .golangci.yml: enable check for consistent receiver name

* beacon/light/sync: fix receiver name

* core/txpool/blobpool: fix receiver name

* core/types: fix receiver name

* internal/ethapi: use consistent receiver name 'api' for handler object

* signer/core/apitypes: fix receiver name

* signer/core: use consistent receiver name 'api' for handler object

* log: fix receiver name
2024-06-12 10:45:42 +03:00
bugmaker9371
b6f2bbd417 p2p/simulations: update doc of HTTP endpoints (#29894) 2024-06-11 19:41:17 +02:00
Guillaume Ballet
c732039a34 .github: disable cache in actions run (#29926) 2024-06-11 15:57:41 +02:00
bugmaker9371
caa066dcb0 cmd/devp2p: fix log output (#29972) 2024-06-11 16:27:35 +03:00
ucwong
ffb29be7d4 ethconfig: regenerate config (#29970) 2024-06-11 20:34:56 +08:00
maskpp
3aa874bed2 core/state: rename all the AccessList receivers to 'al' (#29921)
rename all the receivers to 'al'
2024-06-11 11:24:44 +03:00
jwasinger
85587d5ef2 cmd, core: prefetch reads too from tries if requested (#29807)
* cmd/utils, consensus/beacon, core/state: when configured via stub  flag: prefetch all reads from account/storage tries, terminate prefetcher synchronously.

* cmd, core/state: fix nil panic, fix error handling, prefetch nosnap too

* core/state: expand prefetcher metrics for reads and writes separately

* cmd/utils, eth: fix noop collect witness flag

---------

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2024-06-11 11:10:07 +03:00
TinyFoxy
2eb185c92b core, rlp: remove duplicated words (#29964) 2024-06-10 20:55:47 +08:00
jwasinger
db273c8733 core: initialize developer genesis beacon root contract with 0 balance (#29963) 2024-06-10 13:58:50 +08:00
Gealber Morales
8bda642963 p2p: use package slices to sort in PeersInfo (#29957) 2024-06-09 22:50:22 +02:00
Gealber Morales
349fcdd22d p2p/discover: add missing lock when calling tab.handleAddNode (#29960) 2024-06-09 22:47:51 +02:00
Ha DANG
1098d148a5 cmd/geth: remove unused param (#29952) 2024-06-08 13:04:16 +02:00
kukuru909
deaf10982c cmd/clef, cmd/evm: fix markdown issues in README (#29954) 2024-06-08 13:00:53 +02:00
ucwong
6a49d13c13 go.mod : tidy 2024-06-07 15:57:46 +02:00
Gealber Morales
4405f18519 cmd/evm/internal/t8ntool: remove unused parameter (#29930) 2024-06-07 20:04:18 +08:00
Gary Rong
4461c1fc17 params: begin v1.14.6 release cycle 2024-06-06 21:23:44 +08:00
132 changed files with 7027 additions and 4329 deletions

6
.github/CODEOWNERS vendored
View File

@@ -4,14 +4,18 @@
accounts/usbwallet @karalabe accounts/usbwallet @karalabe
accounts/scwallet @gballet accounts/scwallet @gballet
accounts/abi @gballet @MariusVanDerWijden accounts/abi @gballet @MariusVanDerWijden
beacon/engine @lightclient
cmd/clef @holiman cmd/clef @holiman
cmd/evm @holiman @MariusVanDerWijden @lightclient
consensus @karalabe consensus @karalabe
core/ @karalabe @holiman @rjl493456442 core/ @karalabe @holiman @rjl493456442
eth/ @karalabe @holiman @rjl493456442 eth/ @karalabe @holiman @rjl493456442
eth/catalyst/ @gballet eth/catalyst/ @gballet @lightclient
eth/tracers/ @s1na eth/tracers/ @s1na
core/tracing/ @s1na core/tracing/ @s1na
graphql/ @s1na graphql/ @s1na
internal/ethapi @lightclient
internal/era @lightclient
les/ @zsfelfoldi @rjl493456442 les/ @zsfelfoldi @rjl493456442
light/ @zsfelfoldi @rjl493456442 light/ @zsfelfoldi @rjl493456442
node/ @fjl node/ @fjl

View File

@@ -16,6 +16,7 @@ jobs:
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.21.4 go-version: 1.21.4
cache: false
- name: Run tests - name: Run tests
run: go test -short ./... run: go test -short ./...
env: env:

View File

@@ -23,6 +23,7 @@ linters:
- durationcheck - durationcheck
- exportloopref - exportloopref
- whitespace - whitespace
- revive # only certain checks enabled
### linters we tried and will not be using: ### linters we tried and will not be using:
### ###
@@ -38,6 +39,15 @@ linters:
linters-settings: linters-settings:
gofmt: gofmt:
simplify: true simplify: true
revive:
enable-all-rules: false
# here we enable specific useful rules
# see https://golangci-lint.run/usage/linters/#revive for supported rules
rules:
- name: receiver-naming
severity: warning
disabled: false
exclude: [""]
issues: issues:
exclude-files: exclude-files:
@@ -47,6 +57,9 @@ issues:
linters: linters:
- deadcode - deadcode
- staticcheck - staticcheck
- path: crypto/bn256/
linters:
- revive
- path: internal/build/pgp.go - path: internal/build/pgp.go
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

View 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}}

View File

@@ -16,7 +16,11 @@
package bind package bind
import "github.com/ethereum/go-ethereum/accounts/abi" import (
_ "embed"
"github.com/ethereum/go-ethereum/accounts/abi"
)
// tmplData is the data structure required to fill the binding template. // tmplData is the data structure required to fill the binding template.
type tmplData struct { type tmplData struct {
@@ -80,492 +84,6 @@ var tmplSource = map[Lang]string{
// tmplSourceGo is the Go source template that the generated Go contract binding // tmplSourceGo is the Go source template that the generated Go contract binding
// is based on. // is based on.
const tmplSourceGo = ` //
// Code generated - DO NOT EDIT. //go:embed source.go.tpl
// This file is a generated binding and any manual changes will be lost. var tmplSourceGo string
package {{.Package}}
import (
"math/big"
"strings"
"errors"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
_ = abi.ConvertType
)
{{$structs := .Structs}}
{{range $structs}}
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
type {{.Name}} struct {
{{range $field := .Fields}}
{{$field.Name}} {{$field.Type}}{{end}}
}
{{end}}
{{range $contract := .Contracts}}
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
var {{.Type}}MetaData = &bind.MetaData{
ABI: "{{.InputABI}}",
{{if $contract.FuncSigs -}}
Sigs: map[string]string{
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
{{end}}
},
{{end -}}
{{if .InputBin -}}
Bin: "0x{{.InputBin}}",
{{end}}
}
// {{.Type}}ABI is the input ABI used to generate the binding from.
// Deprecated: Use {{.Type}}MetaData.ABI instead.
var {{.Type}}ABI = {{.Type}}MetaData.ABI
{{if $contract.FuncSigs}}
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
{{end}}
{{if .InputBin}}
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use {{.Type}}MetaData.Bin instead.
var {{.Type}}Bin = {{.Type}}MetaData.Bin
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
parsed, err := {{.Type}}MetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
{{range $pattern, $name := .Libraries}}
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
{{end}}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
}
{{end}}
// {{.Type}} is an auto generated Go binding around an Ethereum contract.
type {{.Type}} struct {
{{.Type}}Caller // Read-only binding to the contract
{{.Type}}Transactor // Write-only binding to the contract
{{.Type}}Filterer // Log filterer for contract events
}
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
type {{.Type}}Caller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
type {{.Type}}Transactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
type {{.Type}}Filterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type {{.Type}}Session struct {
Contract *{{.Type}} // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type {{.Type}}CallerSession struct {
Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type {{.Type}}TransactorSession struct {
Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
type {{.Type}}Raw struct {
Contract *{{.Type}} // Generic contract binding to access the raw methods on
}
// {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type {{.Type}}CallerRaw struct {
Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
}
// {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type {{.Type}}TransactorRaw struct {
Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
}
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
contract, err := bind{{.Type}}(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
}
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
contract, err := bind{{.Type}}(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &{{.Type}}Caller{contract: contract}, nil
}
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
contract, err := bind{{.Type}}(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &{{.Type}}Transactor{contract: contract}, nil
}
// New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
contract, err := bind{{.Type}}(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &{{.Type}}Filterer{contract: contract}, nil
}
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := {{.Type}}MetaData.GetAbi()
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
}
{{range .Calls}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
var out []interface{}
err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
{{if .Structured}}
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
if err != nil {
return *outstruct, err
}
{{range $i, $t := .Normalized.Outputs}}
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return *outstruct, err
{{else}}
if err != nil {
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
}
{{range $i, $t := .Normalized.Outputs}}
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
{{end}}
}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
}
{{end}}
{{range .Transacts}}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
}
{{end}}
{{if .Fallback}}
// Fallback is a paid mutator transaction binding the contract fallback function.
//
// Solidity: {{.Fallback.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
}
// Fallback is a paid mutator transaction binding the contract fallback function.
//
// Solidity: {{.Fallback.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
}
// Fallback is a paid mutator transaction binding the contract fallback function.
//
// Solidity: {{.Fallback.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
}
{{end}}
{{if .Receive}}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: {{.Receive.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: {{.Receive.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: {{.Receive.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
}
{{end}}
{{range .Events}}
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
// If the iterator failed, stop iterating
if (it.fail != nil) {
return false
}
// If the iterator completed, deliver directly whatever's available
if (it.done) {
select {
case log := <-it.logs:
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
Raw types.Log // Blockchain specific contextual infos
}
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
{{range .Normalized.Inputs}}
{{if .Indexed}}var {{.Name}}Rule []interface{}
for _, {{.Name}}Item := range {{.Name}} {
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
}{{end}}{{end}}
logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
if err != nil {
return nil, err
}
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
}
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
{{range .Normalized.Inputs}}
{{if .Indexed}}var {{.Name}}Rule []interface{}
for _, {{.Name}}Item := range {{.Name}} {
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
}{{end}}{{end}}
logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new({{$contract.Type}}{{.Normalized.Name}})
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
event := new({{$contract.Type}}{{.Normalized.Name}})
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
{{end}}
{{end}}
`

View File

@@ -64,6 +64,9 @@ type Type struct {
var ( var (
// typeRegex parses the abi sub types // typeRegex parses the abi sub types
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
// sliceSizeRegex grab the slice size
sliceSizeRegex = regexp.MustCompile("[0-9]+")
) )
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
@@ -91,8 +94,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
// grab the last cell and create a type from there // grab the last cell and create a type from there
sliced := t[i:] sliced := t[i:]
// grab the slice size with regexp // grab the slice size with regexp
re := regexp.MustCompile("[0-9]+") intz := sliceSizeRegex.FindAllString(sliced, -1)
intz := re.FindAllString(sliced, -1)
if len(intz) == 0 { if len(intz) == 0 {
// is a slice // is a slice

View File

@@ -325,11 +325,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Parallel() t.Parallel()
// Create a temporary keystore to test with // Create a temporary keystore to test with
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int())) dir := t.TempDir()
// Create the directory
os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir)
ks := NewKeyStore(dir, LightScryptN, LightScryptP) ks := NewKeyStore(dir, LightScryptN, LightScryptP)

View File

@@ -73,6 +73,14 @@ var (
DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes()) DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes())
) )
var (
// PinRegexp is the regular expression used to validate PIN codes.
pinRegexp = regexp.MustCompile(`^[0-9]{6,}$`)
// PukRegexp is the regular expression used to validate PUK codes.
pukRegexp = regexp.MustCompile(`^[0-9]{12,}$`)
)
// List of APDU command-related constants // List of APDU command-related constants
const ( const (
claISO7816 = 0 claISO7816 = 0
@@ -380,7 +388,7 @@ func (w *Wallet) Open(passphrase string) error {
case passphrase == "": case passphrase == "":
return ErrPINUnblockNeeded return ErrPINUnblockNeeded
case status.PinRetryCount > 0: case status.PinRetryCount > 0:
if !regexp.MustCompile(`^[0-9]{6,}$`).MatchString(passphrase) { if !pinRegexp.MatchString(passphrase) {
w.log.Error("PIN needs to be at least 6 digits") w.log.Error("PIN needs to be at least 6 digits")
return ErrPINNeeded return ErrPINNeeded
} }
@@ -388,7 +396,7 @@ func (w *Wallet) Open(passphrase string) error {
return err return err
} }
default: default:
if !regexp.MustCompile(`^[0-9]{12,}$`).MatchString(passphrase) { if !pukRegexp.MatchString(passphrase) {
w.log.Error("PUK needs to be at least 12 digits") w.log.Error("PUK needs to be at least 12 digits")
return ErrPINUnblockNeeded return ErrPINUnblockNeeded
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@
syntax = "proto2"; syntax = "proto2";
package hw.trezor.messages.common; package hw.trezor.messages.common;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
/** /**
* Response: Success of the previous request * Response: Success of the previous request
* @end * @end

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@
syntax = "proto2"; syntax = "proto2";
package hw.trezor.messages.ethereum; package hw.trezor.messages.ethereum;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java // Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageEthereum"; option java_outer_classname = "TrezorMessageEthereum";

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@
syntax = "proto2"; syntax = "proto2";
package hw.trezor.messages.management; package hw.trezor.messages.management;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java // Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageManagement"; option java_outer_classname = "TrezorMessageManagement";

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,13 @@ package hw.trezor.messages;
* Messages for TREZOR communication * Messages for TREZOR communication
*/ */
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java // Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessage"; option java_outer_classname = "TrezorMessage";
import "google/protobuf/descriptor.proto"; import "google/protobuf/descriptor.proto";
/** /**

View File

@@ -42,7 +42,7 @@
// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go` // - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go`
// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...` // - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...`
//go:generate protoc -I/usr/local/include:. --go_out=import_path=trezor:. 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
// Package trezor contains the wire protocol. // Package trezor contains the wire protocol.
package trezor package trezor

View File

@@ -24,6 +24,7 @@ for:
- image: Ubuntu - image: Ubuntu
build_script: build_script:
- go run build/ci.go lint - go run build/ci.go lint
- go run build/ci.go generate -verify
- go run build/ci.go install -dlgo - go run build/ci.go install -dlgo
test_script: test_script:
- go run build/ci.go test -dlgo -short - go run build/ci.go test -dlgo -short

View File

@@ -494,9 +494,6 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
for { for {
select { select {
case <-ctx.Done():
stream.Close()
case event, ok := <-stream.Events: case event, ok := <-stream.Events:
if !ok { if !ok {
log.Trace("Event stream closed") log.Trace("Event stream closed")

View File

@@ -186,10 +186,14 @@ func (s *serverWithTimeout) eventCallback(event Event) {
// call will just do nothing // call will just do nothing
timer.Stop() timer.Stop()
delete(s.timeouts, id) delete(s.timeouts, id)
s.childEventCb(event) if s.childEventCb != nil {
s.childEventCb(event)
}
} }
default: default:
s.childEventCb(event) if s.childEventCb != nil {
s.childEventCb(event)
}
} }
} }
@@ -211,25 +215,27 @@ func (s *serverWithTimeout) startTimeout(reqData RequestResponse) {
delete(s.timeouts, id) delete(s.timeouts, id)
childEventCb := s.childEventCb childEventCb := s.childEventCb
s.lock.Unlock() s.lock.Unlock()
childEventCb(Event{Type: EvFail, Data: reqData}) if childEventCb != nil {
childEventCb(Event{Type: EvFail, Data: reqData})
}
}) })
childEventCb := s.childEventCb childEventCb := s.childEventCb
s.lock.Unlock() s.lock.Unlock()
childEventCb(Event{Type: EvTimeout, Data: reqData}) if childEventCb != nil {
childEventCb(Event{Type: EvTimeout, Data: reqData})
}
}) })
} }
// unsubscribe stops all goroutines associated with the server. // unsubscribe stops all goroutines associated with the server.
func (s *serverWithTimeout) unsubscribe() { func (s *serverWithTimeout) unsubscribe() {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock()
for _, timer := range s.timeouts { for _, timer := range s.timeouts {
if timer != nil { if timer != nil {
timer.Stop() timer.Stop()
} }
} }
s.childEventCb = nil s.lock.Unlock()
s.parent.Unsubscribe() s.parent.Unsubscribe()
} }
@@ -328,10 +334,10 @@ func (s *serverWithLimits) eventCallback(event Event) {
} }
childEventCb := s.childEventCb childEventCb := s.childEventCb
s.lock.Unlock() s.lock.Unlock()
if passEvent { if passEvent && childEventCb != nil {
childEventCb(event) childEventCb(event)
} }
if sendCanRequestAgain { if sendCanRequestAgain && childEventCb != nil {
childEventCb(Event{Type: EvCanRequestAgain}) childEventCb(Event{Type: EvCanRequestAgain})
} }
} }
@@ -347,13 +353,12 @@ func (s *serverWithLimits) sendRequest(request Request) (reqId ID) {
// unsubscribe stops all goroutines associated with the server. // unsubscribe stops all goroutines associated with the server.
func (s *serverWithLimits) unsubscribe() { func (s *serverWithLimits) unsubscribe() {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock()
if s.delayTimer != nil { if s.delayTimer != nil {
s.delayTimer.Stop() s.delayTimer.Stop()
s.delayTimer = nil s.delayTimer = nil
} }
s.childEventCb = nil s.childEventCb = nil
s.lock.Unlock()
s.serverWithTimeout.unsubscribe() s.serverWithTimeout.unsubscribe()
} }
@@ -383,7 +388,7 @@ func (s *serverWithLimits) canRequestNow() bool {
} }
childEventCb := s.childEventCb childEventCb := s.childEventCb
s.lock.Unlock() s.lock.Unlock()
if sendCanRequestAgain { if sendCanRequestAgain && childEventCb != nil {
childEventCb(Event{Type: EvCanRequestAgain}) childEventCb(Event{Type: EvCanRequestAgain})
} }
return canRequest return canRequest
@@ -415,7 +420,7 @@ func (s *serverWithLimits) delay(delay time.Duration) {
} }
childEventCb := s.childEventCb childEventCb := s.childEventCb
s.lock.Unlock() s.lock.Unlock()
if sendCanRequestAgain { if sendCanRequestAgain && childEventCb != nil {
childEventCb(Event{Type: EvCanRequestAgain}) childEventCb(Event{Type: EvCanRequestAgain})
} }
}) })

View File

@@ -51,6 +51,7 @@ func TestServerEvents(t *testing.T) {
expEvent(EvFail) expEvent(EvFail)
rs.eventCb(Event{Type: EvResponse, Data: RequestResponse{ID: 1, Request: testRequest, Response: testResponse}}) rs.eventCb(Event{Type: EvResponse, Data: RequestResponse{ID: 1, Request: testRequest, Response: testResponse}})
expEvent(nil) expEvent(nil)
srv.unsubscribe()
} }
func TestServerParallel(t *testing.T) { func TestServerParallel(t *testing.T) {
@@ -129,9 +130,7 @@ func TestServerEventRateLimit(t *testing.T) {
srv := NewServer(rs, clock) srv := NewServer(rs, clock)
var eventCount int var eventCount int
srv.subscribe(func(event Event) { srv.subscribe(func(event Event) {
if !event.IsRequestEvent() { eventCount++
eventCount++
}
}) })
expEvents := func(send, expAllowed int) { expEvents := func(send, expAllowed int) {
eventCount = 0 eventCount = 0
@@ -147,6 +146,30 @@ func TestServerEventRateLimit(t *testing.T) {
expEvents(5, 1) expEvents(5, 1)
clock.Run(maxServerEventRate * maxServerEventBuffer * 2) clock.Run(maxServerEventRate * maxServerEventBuffer * 2)
expEvents(maxServerEventBuffer+5, maxServerEventBuffer) expEvents(maxServerEventBuffer+5, maxServerEventBuffer)
srv.unsubscribe()
}
func TestServerUnsubscribe(t *testing.T) {
rs := &testRequestServer{}
clock := &mclock.Simulated{}
srv := NewServer(rs, clock)
var eventCount int
srv.subscribe(func(event Event) {
eventCount++
})
eventCb := rs.eventCb
eventCb(Event{Type: testEventType})
if eventCount != 1 {
t.Errorf("Server event callback not called before unsubscribe")
}
srv.unsubscribe()
if rs.eventCb != nil {
t.Errorf("Server event callback not removed after unsubscribe")
}
eventCb(Event{Type: testEventType})
if eventCount != 1 {
t.Errorf("Server event callback called after unsubscribe")
}
} }
type testRequestServer struct { type testRequestServer struct {
@@ -156,4 +179,4 @@ type testRequestServer struct {
func (rs *testRequestServer) Name() string { return "" } func (rs *testRequestServer) Name() string { return "" }
func (rs *testRequestServer) Subscribe(eventCb func(Event)) { rs.eventCb = eventCb } func (rs *testRequestServer) Subscribe(eventCb func(Event)) { rs.eventCb = eventCb }
func (rs *testRequestServer) SendRequest(ID, Request) {} func (rs *testRequestServer) SendRequest(ID, Request) {}
func (rs *testRequestServer) Unsubscribe() {} func (rs *testRequestServer) Unsubscribe() { rs.eventCb = nil }

View File

@@ -173,24 +173,24 @@ type TestCommitteeChain struct {
init bool init bool
} }
func (t *TestCommitteeChain) CheckpointInit(bootstrap types.BootstrapData) error { func (tc *TestCommitteeChain) CheckpointInit(bootstrap types.BootstrapData) error {
t.fsp, t.nsp, t.init = bootstrap.Header.SyncPeriod(), bootstrap.Header.SyncPeriod()+2, true tc.fsp, tc.nsp, tc.init = bootstrap.Header.SyncPeriod(), bootstrap.Header.SyncPeriod()+2, true
return nil return nil
} }
func (t *TestCommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error { func (tc *TestCommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error {
period := update.AttestedHeader.Header.SyncPeriod() period := update.AttestedHeader.Header.SyncPeriod()
if period < t.fsp || period > t.nsp || !t.init { if period < tc.fsp || period > tc.nsp || !tc.init {
return light.ErrInvalidPeriod return light.ErrInvalidPeriod
} }
if period == t.nsp { if period == tc.nsp {
t.nsp++ tc.nsp++
} }
return nil return nil
} }
func (t *TestCommitteeChain) NextSyncPeriod() (uint64, bool) { func (tc *TestCommitteeChain) NextSyncPeriod() (uint64, bool) {
return t.nsp, t.init return tc.nsp, tc.init
} }
func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) { func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) {
@@ -199,8 +199,8 @@ func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) {
} }
} }
func (t *TestCommitteeChain) SetNextSyncPeriod(nsp uint64) { func (tc *TestCommitteeChain) SetNextSyncPeriod(nsp uint64) {
t.init, t.nsp = true, nsp tc.init, tc.nsp = true, nsp
} }
func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) { func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) {

View File

@@ -99,3 +99,28 @@ d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.t
# version:ppa-builder-2 1.21.9 # version:ppa-builder-2 1.21.9
# https://go.dev/dl/ # https://go.dev/dl/
58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz 58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz
# version:protoc 27.1
# https://github.com/protocolbuffers/protobuf/releases/
# https://github.com/protocolbuffers/protobuf/releases/download/v27.1/
8809c2ec85368c6b6e9af161b6771a153aa92670a24adbe46dd34fa02a04df2f protoc-27.1-linux-aarch_64.zip
5d21979a6d27475e810b76b88863d1e784fa01ffb15e511a3ec5bd1924d89426 protoc-27.1-linux-ppcle_64.zip
84d8852750ed186dc4a057a1a86bcac409be5362d6af04770f42367fee6b7bc1 protoc-27.1-linux-s390_64.zip
2f028796ff5741691650e0eea290e61ff2f1c0d87f8d31fe45ef47fd967cef0c protoc-27.1-linux-x86_32.zip
8970e3d8bbd67d53768fe8c2e3971bdd71e51cfe2001ca06dacad17258a7dae3 protoc-27.1-linux-x86_64.zip
03b7af1bf469e7285dc51976ee5fa99412704dbd1c017105114852a37b165c12 protoc-27.1-osx-aarch_64.zip
f14d3973cf13283d07c520ed6f4c12405ad41b9efd18089a1c74897037d742b5 protoc-27.1-osx-universal_binary.zip
8520d944f3a3890fa296a3b3b0d4bb18337337e2526bbbf1b507eeea3c2a1ec4 protoc-27.1-osx-x86_64.zip
6263718ff96547b8392a079f6fdf02a4156f2e8d13cd51649a0d03fb7afa2de8 protoc-27.1-win32.zip
da531c51ccd1290d8d34821f0ce4e219c7fbaa6f9825f5a3fb092a9d03fe6206 protoc-27.1-win64.zip
# version:protoc-gen-go 1.34.2
# https://github.com/protocolbuffers/protobuf-go/releases/
# https://github.com/protocolbuffers/protobuf-go/releases/download/v1.34.2/
9b48d8f90add02e8e94e14962fed74e7ce2b2d6bda4dd42f1f4fbccf0f766f1a protoc-gen-go.v1.34.2.darwin.amd64.tar.gz
17aca7f948dbb624049030cf841e35895cf34183ba006e721247fdeb95ff2780 protoc-gen-go.v1.34.2.darwin.arm64.tar.gz
a191849433fd489f1d44f37788d762658f3f5fb225f3a85d4ce6ba32666703ed protoc-gen-go.v1.34.2.linux.386.tar.gz
b87bc134dee55576a842141bf0ed27761c635d746780fce5dee038c6dd16554f protoc-gen-go.v1.34.2.linux.amd64.tar.gz
63d400167e75ab9f6690688f6fdc6a9455aa20bc1faa71e32149dbd322f7f198 protoc-gen-go.v1.34.2.linux.arm64.tar.gz
56e7675816db6e62be4f833a51544d5716b8420c462515579e05ca8444ab06ed protoc-gen-go.v1.34.2.windows.386.zip
abafd39612177dd4e9a65207cadd5374a9352d8611e8e040f8462fcfa3010daf protoc-gen-go.v1.34.2.windows.amd64.zip

View File

@@ -39,9 +39,11 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"flag" "flag"
"fmt" "fmt"
"io"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@@ -169,6 +171,8 @@ func main() {
doPurge(os.Args[2:]) doPurge(os.Args[2:])
case "sanitycheck": case "sanitycheck":
doSanityCheck() doSanityCheck()
case "generate":
doGenerate()
default: default:
log.Fatal("unknown command ", os.Args[1]) log.Fatal("unknown command ", os.Args[1])
} }
@@ -345,6 +349,86 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
return filepath.Join(cachedir, base) return filepath.Join(cachedir, base)
} }
// hashSourceFiles iterates all files under the top-level project directory
// computing the hash of each file (excluding files within the tests
// subrepo)
func hashSourceFiles() (map[string]common.Hash, error) {
res := make(map[string]common.Hash)
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
return filepath.SkipDir
}
if !d.Type().IsRegular() {
return nil
}
// open the file and hash it
f, err := os.OpenFile(path, os.O_RDONLY, 0666)
if err != nil {
return err
}
hasher := sha256.New()
if _, err := io.Copy(hasher, f); err != nil {
return err
}
res[path] = common.Hash(hasher.Sum(nil))
return nil
})
if err != nil {
return nil, err
}
return res, nil
}
// doGenerate ensures that re-generating generated files does not cause
// any mutations in the source file tree: i.e. all generated files were
// updated and committed. Any stale generated files are updated.
func doGenerate() {
var (
tc = new(build.GoToolchain)
cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.")
verify = flag.Bool("verify", false, "check whether any files are changed by go generate")
)
protocPath := downloadProtoc(*cachedir)
protocGenGoPath := downloadProtocGenGo(*cachedir)
var preHashes map[string]common.Hash
if *verify {
var err error
preHashes, err = hashSourceFiles()
if err != nil {
log.Fatal("failed to compute map of source hashes", "err", err)
}
}
c := tc.Go("generate", "./...")
pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")}
c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator)))
build.MustRun(c)
if !*verify {
return
}
// Check if files were changed.
postHashes, err := hashSourceFiles()
if err != nil {
log.Fatal("error computing source tree file hashes", "err", err)
}
updates := []string{}
for path, postHash := range postHashes {
preHash, ok := preHashes[path]
if !ok || preHash != postHash {
updates = append(updates, path)
}
}
for _, updatedFile := range updates {
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
}
if len(updates) != 0 {
log.Fatal("One or more generated files were updated by running 'go generate ./...'")
}
}
// doLint runs golangci-lint on requested packages. // doLint runs golangci-lint on requested packages.
func doLint(cmdline []string) { func doLint(cmdline []string) {
var ( var (
@@ -390,6 +474,96 @@ func downloadLinter(cachedir string) string {
return filepath.Join(cachedir, base, "golangci-lint") return filepath.Join(cachedir, base, "golangci-lint")
} }
// protocArchiveBaseName returns the name of the protoc archive file for
// the current system, stripped of version and file suffix.
func protocArchiveBaseName() (string, error) {
switch runtime.GOOS + "-" + runtime.GOARCH {
case "windows-amd64":
return "win64", nil
case "windows-386":
return "win32", nil
case "linux-arm64":
return "linux-aarch_64", nil
case "linux-386":
return "linux-x86_32", nil
case "linux-amd64":
return "linux-x86_64", nil
case "darwin-arm64":
return "osx-aarch_64", nil
case "darwin-amd64":
return "osx-x86_64", nil
default:
return "", fmt.Errorf("no prebuilt release of protoc available for this system (os: %s, arch: %s)", runtime.GOOS, runtime.GOARCH)
}
}
// downloadProtocGenGo downloads protoc-gen-go, which is used by protoc
// in the generate command. It returns the full path of the directory
// containing the 'protoc-gen-go' executable.
func downloadProtocGenGo(cachedir string) string {
csdb := build.MustLoadChecksums("build/checksums.txt")
version, err := build.Version(csdb, "protoc-gen-go")
if err != nil {
log.Fatal(err)
}
baseName := fmt.Sprintf("protoc-gen-go.v%s.%s.%s", version, runtime.GOOS, runtime.GOARCH)
archiveName := baseName
if runtime.GOOS == "windows" {
archiveName += ".zip"
} else {
archiveName += ".tar.gz"
}
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
archivePath := path.Join(cachedir, archiveName)
if err := csdb.DownloadFile(url, archivePath); err != nil {
log.Fatal(err)
}
extractDest := filepath.Join(cachedir, baseName)
if err := build.ExtractArchive(archivePath, extractDest); err != nil {
log.Fatal(err)
}
extractDest, err = filepath.Abs(extractDest)
if err != nil {
log.Fatal("error resolving absolute path for protoc", "err", err)
}
return extractDest
}
// downloadProtoc downloads the prebuilt protoc binary used to lint generated
// files as a CI step. It returns the full path to the directory containing
// the protoc executable.
func downloadProtoc(cachedir string) string {
csdb := build.MustLoadChecksums("build/checksums.txt")
version, err := build.Version(csdb, "protoc")
if err != nil {
log.Fatal(err)
}
baseName, err := protocArchiveBaseName()
if err != nil {
log.Fatal(err)
}
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
archiveFileName := fileName + ".zip"
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
archivePath := filepath.Join(cachedir, archiveFileName)
if err := csdb.DownloadFile(url, archivePath); err != nil {
log.Fatal(err)
}
extractDest := filepath.Join(cachedir, fileName)
if err := build.ExtractArchive(archivePath, extractDest); err != nil {
log.Fatal(err)
}
extractDest, err = filepath.Abs(extractDest)
if err != nil {
log.Fatal("error resolving absolute path for protoc", "err", err)
}
return extractDest
}
// Release Packaging // Release Packaging
func doArchive(cmdline []string) { func doArchive(cmdline []string) {
var ( var (

View File

@@ -19,39 +19,21 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
"github.com/ethereum/go-ethereum/beacon/blsync" "github.com/ethereum/go-ethereum/beacon/blsync"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
"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/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var (
verbosityFlag = &cli.IntFlag{
Name: "verbosity",
Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
Value: 3,
Category: flags.LoggingCategory,
}
vmoduleFlag = &cli.StringFlag{
Name: "vmodule",
Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)",
Value: "",
Hidden: true,
Category: flags.LoggingCategory,
}
)
func main() { func main() {
app := flags.NewApp("beacon light syncer tool") app := flags.NewApp("beacon light syncer tool")
app.Flags = []cli.Flag{ app.Flags = flags.Merge([]cli.Flag{
utils.BeaconApiFlag, utils.BeaconApiFlag,
utils.BeaconApiHeaderFlag, utils.BeaconApiHeaderFlag,
utils.BeaconThresholdFlag, utils.BeaconThresholdFlag,
@@ -66,8 +48,16 @@ func main() {
utils.GoerliFlag, utils.GoerliFlag,
utils.BlsyncApiFlag, utils.BlsyncApiFlag,
utils.BlsyncJWTSecretFlag, utils.BlsyncJWTSecretFlag,
verbosityFlag, },
vmoduleFlag, debug.Flags,
)
app.Before = func(ctx *cli.Context) error {
flags.MigrateGlobalFlags(ctx)
return debug.Setup(ctx)
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
return nil
} }
app.Action = sync app.Action = sync
@@ -78,14 +68,6 @@ func main() {
} }
func sync(ctx *cli.Context) error { func sync(ctx *cli.Context) error {
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorable(os.Stderr)
}
verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor)))
// set up blsync // set up blsync
client := blsync.NewClient(ctx) client := blsync.NewClient(ctx)
client.SetEngineRPC(makeRPCClient(ctx)) client.SetEngineRPC(makeRPCClient(ctx))

View File

@@ -225,8 +225,8 @@ Response
- `value` [number:optional]: amount of Wei to send with the transaction - `value` [number:optional]: amount of Wei to send with the transaction
- `data` [data:optional]: input data - `data` [data:optional]: input data
- `nonce` [number]: account nonce - `nonce` [number]: account nonce
1. method signature [string:optional] 2. method signature [string:optional]
- The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
#### Result #### Result

View File

@@ -109,7 +109,7 @@ func rlpxPing(ctx *cli.Context) error {
} }
return fmt.Errorf("received disconnect message: %v", msg[0]) return fmt.Errorf("received disconnect message: %v", msg[0])
default: default:
return fmt.Errorf("invalid message code %d, expected handshake (code zero)", code) return fmt.Errorf("invalid message code %d, expected handshake (code zero) or disconnect (code one)", code)
} }
return nil return nil
} }

View File

@@ -14,15 +14,15 @@ The `evm t8n` tool is a stateless state transition utility. It is a utility
which can which can
1. Take a prestate, including 1. Take a prestate, including
- Accounts, - Accounts,
- Block context information, - Block context information,
- Previous blockshashes (*optional) - Previous blockshashes (*optional)
2. Apply a set of transactions, 2. Apply a set of transactions,
3. Apply a mining-reward (*optional), 3. Apply a mining-reward (*optional),
4. And generate a post-state, including 4. And generate a post-state, including
- State root, transaction root, receipt root, - State root, transaction root, receipt root,
- Information about rejected transactions, - Information about rejected transactions,
- Optionally: a full or partial post-state dump - Optionally: a full or partial post-state dump
### Specification ### Specification

View File

@@ -86,7 +86,7 @@ func blockTestCmd(ctx *cli.Context) error {
continue continue
} }
test := tests[name] test := tests[name]
if err := test.Run(false, rawdb.HashScheme, tracer, func(res error, chain *core.BlockChain) { if err := test.Run(false, rawdb.HashScheme, false, tracer, func(res error, chain *core.BlockChain) {
if ctx.Bool(DumpFlag.Name) { if ctx.Bool(DumpFlag.Name) {
if state, _ := chain.State(); state != nil { if state, _ := chain.State(); state != nil {
fmt.Println(string(state.Dump(nil))) fmt.Println(string(state.Dump(nil)))

View File

@@ -306,7 +306,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
if tracer.Hooks.OnTxEnd != nil { if tracer.Hooks.OnTxEnd != nil {
tracer.Hooks.OnTxEnd(receipt, nil) tracer.Hooks.OnTxEnd(receipt, nil)
} }
writeTraceResult(tracer, traceOutput) if err = writeTraceResult(tracer, traceOutput); err != nil {
log.Warn("Error writing tracer output", "err", err)
}
} }
} }
@@ -323,7 +325,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
var ( var (
blockReward = big.NewInt(miningReward) blockReward = big.NewInt(miningReward)
minerReward = new(big.Int).Set(blockReward) minerReward = new(big.Int).Set(blockReward)
perOmmer = new(big.Int).Div(blockReward, big.NewInt(32)) perOmmer = new(big.Int).Rsh(blockReward, 5)
) )
for _, ommer := range pre.Env.Ommers { for _, ommer := range pre.Env.Ommers {
// Add 1/32th for each ommer included // Add 1/32th for each ommer included
@@ -332,7 +334,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
reward := big.NewInt(8) reward := big.NewInt(8)
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
reward.Mul(reward, blockReward) reward.Mul(reward, blockReward)
reward.Div(reward, big.NewInt(8)) reward.Rsh(reward, 3)
statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward), tracing.BalanceIncreaseRewardMineUncle) statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward), tracing.BalanceIncreaseRewardMineUncle)
} }
statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward), tracing.BalanceIncreaseRewardMineBlock) statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward), tracing.BalanceIncreaseRewardMineBlock)

View File

@@ -181,7 +181,7 @@ func Transition(ctx *cli.Context) error {
// Set the chain id // Set the chain id
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
if txIt, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil { if txIt, err = loadTransactions(txStr, inputData, chainConfig); err != nil {
return err return err
} }
if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil { if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {

View File

@@ -112,7 +112,7 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran
return signedTxs, nil return signedTxs, nil
} }
func loadTransactions(txStr string, inputData *input, env stEnv, chainConfig *params.ChainConfig) (txIterator, error) { func loadTransactions(txStr string, inputData *input, chainConfig *params.ChainConfig) (txIterator, error) {
var txsWithKeys []*txWithKey var txsWithKeys []*txWithKey
if txStr != stdinSelector { if txStr != stdinSelector {
data, err := os.ReadFile(txStr) data, err := os.ReadFile(txStr)

View File

@@ -39,7 +39,6 @@ import (
"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"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@@ -337,7 +336,7 @@ func importChain(ctx *cli.Context) error {
fmt.Printf("Import done in %v.\n\n", time.Since(start)) fmt.Printf("Import done in %v.\n\n", time.Since(start))
// Output pre-compaction stats mostly to see the import trashing // Output pre-compaction stats mostly to see the import trashing
showLeveldbStats(db) showDBStats(db)
// Print the memory statistics used by the importing // Print the memory statistics used by the importing
mem := new(runtime.MemStats) mem := new(runtime.MemStats)
@@ -360,7 +359,7 @@ func importChain(ctx *cli.Context) error {
} }
fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
showLeveldbStats(db) showDBStats(db)
return importErr return importErr
} }
@@ -516,7 +515,7 @@ func importPreimages(ctx *cli.Context) error {
return nil return nil
} }
func parseDumpConfig(ctx *cli.Context, stack *node.Node, db ethdb.Database) (*state.DumpConfig, common.Hash, error) { func parseDumpConfig(ctx *cli.Context, db ethdb.Database) (*state.DumpConfig, common.Hash, error) {
var header *types.Header var header *types.Header
if ctx.NArg() > 1 { if ctx.NArg() > 1 {
return nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg()) return nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg())
@@ -580,7 +579,7 @@ func dump(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true) db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close() defer db.Close()
conf, root, err := parseDumpConfig(ctx, stack, db) conf, root, err := parseDumpConfig(ctx, db)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -407,17 +407,13 @@ func checkStateContent(ctx *cli.Context) error {
return nil return nil
} }
func showLeveldbStats(db ethdb.KeyValueStater) { func showDBStats(db ethdb.KeyValueStater) {
if stats, err := db.Stat("leveldb.stats"); err != nil { stats, err := db.Stat()
if err != nil {
log.Warn("Failed to read database stats", "error", err) log.Warn("Failed to read database stats", "error", err)
} else { return
fmt.Println(stats)
}
if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
log.Warn("Failed to read database iostats", "error", err)
} else {
fmt.Println(ioStats)
} }
fmt.Println(stats)
} }
func dbStats(ctx *cli.Context) error { func dbStats(ctx *cli.Context) error {
@@ -427,7 +423,7 @@ func dbStats(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true) db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close() defer db.Close()
showLeveldbStats(db) showDBStats(db)
return nil return nil
} }
@@ -439,7 +435,7 @@ func dbCompact(ctx *cli.Context) error {
defer db.Close() defer db.Close()
log.Info("Stats before compaction") log.Info("Stats before compaction")
showLeveldbStats(db) showDBStats(db)
log.Info("Triggering compaction") log.Info("Triggering compaction")
if err := db.Compact(nil, nil); err != nil { if err := db.Compact(nil, nil); err != nil {
@@ -447,7 +443,7 @@ func dbCompact(ctx *cli.Context) error {
return err return err
} }
log.Info("Stats after compaction") log.Info("Stats after compaction")
showLeveldbStats(db) showDBStats(db)
return nil return nil
} }

View File

@@ -156,6 +156,7 @@ var (
utils.BeaconGenesisRootFlag, utils.BeaconGenesisRootFlag,
utils.BeaconGenesisTimeFlag, utils.BeaconGenesisTimeFlag,
utils.BeaconCheckpointFlag, utils.BeaconCheckpointFlag,
utils.CollectWitnessFlag,
}, utils.NetworkFlags, utils.DatabaseFlags) }, utils.NetworkFlags, utils.DatabaseFlags)
rpcFlags = []cli.Flag{ rpcFlags = []cli.Flag{

View File

@@ -544,7 +544,7 @@ func dumpState(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true) db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close() defer db.Close()
conf, root, err := parseDumpConfig(ctx, stack, db) conf, root, err := parseDumpConfig(ctx, db)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -604,6 +604,11 @@ var (
Usage: "Disables db compaction after import", Usage: "Disables db compaction after import",
Category: flags.LoggingCategory, Category: flags.LoggingCategory,
} }
CollectWitnessFlag = &cli.BoolFlag{
Name: "collectwitness",
Usage: "Enable state witness generation during block execution. Work in progress flag, don't use.",
Category: flags.MiscCategory,
}
// MISC settings // MISC settings
SyncTargetFlag = &cli.StringFlag{ SyncTargetFlag = &cli.StringFlag{
@@ -1760,6 +1765,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// TODO(fjl): force-enable this in --dev mode // TODO(fjl): force-enable this in --dev mode
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
} }
if ctx.IsSet(CollectWitnessFlag.Name) {
cfg.EnableWitnessCollection = ctx.Bool(CollectWitnessFlag.Name)
}
if ctx.IsSet(RPCGlobalGasCapFlag.Name) { if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
@@ -2190,7 +2198,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) {
cache.TrieDirtyLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 cache.TrieDirtyLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
} }
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} vmcfg := vm.Config{
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
EnableWitnessCollection: ctx.Bool(CollectWitnessFlag.Name),
}
if ctx.IsSet(VMTraceFlag.Name) { if ctx.IsSet(VMTraceFlag.Name) {
if name := ctx.String(VMTraceFlag.Name); name != "" { if name := ctx.String(VMTraceFlag.Name); name != "" {
var config json.RawMessage var config json.RawMessage

View File

@@ -54,7 +54,7 @@ func NewHexOrDecimal256(x int64) *HexOrDecimal256 {
// It is similar to UnmarshalText, but allows parsing real decimals too, not just // It is similar to UnmarshalText, but allows parsing real decimals too, not just
// quoted decimal strings. // quoted decimal strings.
func (i *HexOrDecimal256) UnmarshalJSON(input []byte) error { func (i *HexOrDecimal256) UnmarshalJSON(input []byte) error {
if len(input) > 0 && input[0] == '"' { if len(input) > 1 && input[0] == '"' {
input = input[1 : len(input)-1] input = input[1 : len(input)-1]
} }
return i.UnmarshalText(input) return i.UnmarshalText(input)

View File

@@ -46,7 +46,7 @@ type HexOrDecimal64 uint64
// It is similar to UnmarshalText, but allows parsing real decimals too, not just // It is similar to UnmarshalText, but allows parsing real decimals too, not just
// quoted decimal strings. // quoted decimal strings.
func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error {
if len(input) > 0 && input[0] == '"' { if len(input) > 1 && input[0] == '"' {
input = input[1 : len(input)-1] input = input[1 : len(input)-1]
} }
return i.UnmarshalText(input) return i.UnmarshalText(input)

View File

@@ -468,7 +468,7 @@ func (d *Decimal) UnmarshalJSON(input []byte) error {
if !isString(input) { if !isString(input) {
return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(uint64(0))} return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(uint64(0))}
} }
if i, err := strconv.ParseInt(string(input[1:len(input)-1]), 10, 64); err == nil { if i, err := strconv.ParseUint(string(input[1:len(input)-1]), 10, 64); err == nil {
*d = Decimal(i) *d = Decimal(i)
return nil return nil
} else { } else {

View File

@@ -21,6 +21,7 @@ import (
"database/sql/driver" "database/sql/driver"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"math/big" "math/big"
"reflect" "reflect"
"strings" "strings"
@@ -595,3 +596,29 @@ func BenchmarkPrettyDuration(b *testing.B) {
} }
b.Logf("Post %s", a) b.Logf("Post %s", a)
} }
func TestDecimalUnmarshalJSON(t *testing.T) {
// These should error
for _, tc := range []string{``, `"`, `""`, `"-1"`} {
if err := new(Decimal).UnmarshalJSON([]byte(tc)); err == nil {
t.Errorf("input %s should cause error", tc)
}
}
// These should succeed
for _, tc := range []struct {
input string
want uint64
}{
{`"0"`, 0},
{`"9223372036854775807"`, math.MaxInt64},
{`"18446744073709551615"`, math.MaxUint64},
} {
have := new(Decimal)
if err := have.UnmarshalJSON([]byte(tc.input)); err != nil {
t.Errorf("input %q triggered error: %v", tc.input, err)
}
if uint64(*have) != tc.want {
t.Errorf("input %q, have %d want %d", tc.input, *have, tc.want)
}
}
}

View File

@@ -562,12 +562,6 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
return hash return hash
} }
// Some weird constants to avoid constant memory allocs for them.
var (
u256_8 = uint256.NewInt(8)
u256_32 = uint256.NewInt(32)
)
// accumulateRewards credits the coinbase of the given block with the mining // accumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for // reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded. // included uncles. The coinbase of each uncle block is also rewarded.
@@ -589,10 +583,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, heade
r.AddUint64(uNum, 8) r.AddUint64(uNum, 8)
r.Sub(r, hNum) r.Sub(r, hNum)
r.Mul(r, blockReward) r.Mul(r, blockReward)
r.Div(r, u256_8) r.Rsh(r, 3)
stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle) stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle)
r.Div(blockReward, u256_32) r.Rsh(blockReward, 5)
reward.Add(reward, r) reward.Add(reward, r)
} }
stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock) stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock)

View File

@@ -20,8 +20,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless"
"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/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
@@ -34,14 +36,12 @@ import (
type BlockValidator struct { type BlockValidator struct {
config *params.ChainConfig // Chain configuration options config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain bc *BlockChain // Canonical block chain
engine consensus.Engine // Consensus engine used for validating
} }
// NewBlockValidator returns a new block validator which is safe for re-use // NewBlockValidator returns a new block validator which is safe for re-use
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator { func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *BlockValidator {
validator := &BlockValidator{ validator := &BlockValidator{
config: config, config: config,
engine: engine,
bc: blockchain, bc: blockchain,
} }
return validator return validator
@@ -59,7 +59,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Header validity is known at this point. Here we verify that uncles, transactions // Header validity is known at this point. Here we verify that uncles, transactions
// and withdrawals given in the block body match the header. // and withdrawals given in the block body match the header.
header := block.Header() header := block.Header()
if err := v.engine.VerifyUncles(v.bc, block); err != nil { if err := v.bc.engine.VerifyUncles(v.bc, block); err != nil {
return err return err
} }
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
@@ -121,7 +121,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// ValidateState validates the various changes that happen after a state transition, // ValidateState validates the various changes that happen after a state transition,
// such as amount of used gas, the receipt roots and the state root itself. // such as amount of used gas, the receipt roots and the state root itself.
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error {
header := block.Header() header := block.Header()
if block.GasUsed() != usedGas { if block.GasUsed() != usedGas {
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
@@ -132,6 +132,11 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
if rbloom != header.Bloom { if rbloom != header.Bloom {
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
} }
// In stateless mode, return early because the receipt and state root are not
// provided through the witness, rather the cross validator needs to return it.
if stateless {
return nil
}
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]])) // The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil)) receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
if receiptSha != header.ReceiptHash { if receiptSha != header.ReceiptHash {
@@ -145,6 +150,28 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
return nil return nil
} }
// ValidateWitness cross validates a block execution with stateless remote clients.
//
// Normally we'd distribute the block witness to remote cross validators, wait
// for them to respond and then merge the results. For now, however, it's only
// Geth, so do an internal stateless run.
func (v *BlockValidator) ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error {
// Run the cross client stateless execution
// TODO(karalabe): Self-stateless for now, swap with other clients
crossReceiptRoot, crossStateRoot, err := ExecuteStateless(v.config, witness)
if err != nil {
return fmt.Errorf("stateless execution failed: %v", err)
}
// Stateless cross execution suceeeded, validate the withheld computed fields
if crossReceiptRoot != receiptRoot {
return fmt.Errorf("cross validator receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, receiptRoot)
}
if crossStateRoot != stateRoot {
return fmt.Errorf("cross validator state root mismatch (cross: %x local: %x)", crossStateRoot, stateRoot)
}
return nil
}
// CalcGasLimit computes the gas limit of the next block after parent. It aims // CalcGasLimit computes the gas limit of the next block after parent. It aims
// to keep the baseline gas close to the provided target, and increase it towards // to keep the baseline gas close to the provided target, and increase it towards
// the target if the baseline gas is lower. // the target if the baseline gas is lower.

View File

@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@@ -302,18 +303,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
vmConfig: vmConfig, vmConfig: vmConfig,
logger: vmConfig.Tracer, logger: vmConfig.Tracer,
} }
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
bc.forker = NewForkChoice(bc, shouldPreserve)
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
bc.validator = NewBlockValidator(chainConfig, bc, engine)
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
bc.processor = NewStateProcessor(chainConfig, bc, engine)
var err error var err error
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped) bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
bc.forker = NewForkChoice(bc, shouldPreserve)
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
bc.validator = NewBlockValidator(chainConfig, bc)
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
bc.processor = NewStateProcessor(chainConfig, bc.hc)
bc.genesisBlock = bc.GetBlockByNumber(0) bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil { if bc.genesisBlock == nil {
return nil, ErrNoGenesis return nil, ErrNoGenesis
@@ -1809,7 +1810,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// while processing transactions. Before Byzantium the prefetcher is mostly // while processing transactions. Before Byzantium the prefetcher is mostly
// useless due to the intermediate root hashing after each transaction. // useless due to the intermediate root hashing after each transaction.
if bc.chainConfig.IsByzantium(block.Number()) { if bc.chainConfig.IsByzantium(block.Number()) {
statedb.StartPrefetcher("chain") var witness *stateless.Witness
if bc.vmConfig.EnableWitnessCollection {
witness, err = stateless.NewWitness(bc, block)
if err != nil {
return it.index, err
}
}
statedb.StartPrefetcher("chain", witness)
} }
activeState = statedb activeState = statedb
@@ -1924,11 +1932,18 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
ptime := time.Since(pstart) ptime := time.Since(pstart)
vstart := time.Now() vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return nil, err return nil, err
} }
vtime := time.Since(vstart) vtime := time.Since(vstart)
if witness := statedb.Witness(); witness != nil {
if err = bc.validator.ValidateWitness(witness, block.ReceiptHash(), block.Root()); err != nil {
bc.reportBlock(block, receipts, err)
return nil, fmt.Errorf("cross verification failed: %v", err)
}
}
proctime := time.Since(start) // processing + validation proctime := time.Since(start) // processing + validation
// Update the metrics touched during block processing and validation // Update the metrics touched during block processing and validation

View File

@@ -168,8 +168,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.reportBlock(block, receipts, err) blockchain.reportBlock(block, receipts, err)
return err return err
} }
err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) if err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
if err != nil {
blockchain.reportBlock(block, receipts, err) blockchain.reportBlock(block, receipts, err)
return err return err
} }

View File

@@ -594,7 +594,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
// Pre-deploy EIP-4788 system contract // Pre-deploy EIP-4788 system contract
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode}, params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
}, },
} }
if faucet != nil { if faucet != nil {

View File

@@ -147,9 +147,9 @@ func (t *table) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
} }
} }
// Stat returns a particular internal stat of the database. // Stat returns the statistic data of the database.
func (t *table) Stat(property string) (string, error) { func (t *table) Stat() (string, error) {
return t.db.Stat(property) return t.db.Stat()
} }
// Compact flattens the underlying data store for the given key range. In essence, // Compact flattens the underlying data store for the given key range. In essence,

View File

@@ -41,8 +41,8 @@ func TestAccountHeaderGas(t *testing.T) {
// Check cold read cost // Check cold read cost
gas := ae.VersionGas(testAddr, false) gas := ae.VersionGas(testAddr, false)
if gas != params.WitnessBranchReadCost+params.WitnessChunkReadCost { if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessChunkReadCost) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check warm read cost // Check warm read cost
@@ -71,8 +71,8 @@ func TestAccountHeaderGas(t *testing.T) {
// Check cold write cost // Check cold write cost
gas = ae.VersionGas(testAddr, true) gas = ae.VersionGas(testAddr, true)
if gas != params.WitnessBranchWriteCost+params.WitnessChunkWriteCost { if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessBranchWriteCost) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check warm write cost // Check warm write cost
@@ -83,8 +83,8 @@ func TestAccountHeaderGas(t *testing.T) {
// Check a write without a read charges both read and write costs // Check a write without a read charges both read and write costs
gas = ae.BalanceGas(testAddr2, true) gas = ae.BalanceGas(testAddr2, true)
if gas != params.WitnessBranchReadCost+params.WitnessBranchWriteCost+params.WitnessChunkWriteCost+params.WitnessChunkReadCost { if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessBranchWriteCost+params.WitnessChunkWriteCost+params.WitnessChunkReadCost) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check that a write followed by a read charges nothing // Check that a write followed by a read charges nothing
@@ -113,8 +113,8 @@ func TestContractCreateInitGas(t *testing.T) {
// Check cold read cost, without a value // Check cold read cost, without a value
gas := ae.ContractCreateInitGas(testAddr, false) gas := ae.ContractCreateInitGas(testAddr, false)
if gas != params.WitnessBranchWriteCost+params.WitnessBranchReadCost+params.WitnessChunkWriteCost*2+params.WitnessChunkReadCost*2 { if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*2; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchWriteCost+params.WitnessBranchReadCost+params.WitnessChunkWriteCost*3) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check warm read cost // Check warm read cost
@@ -131,8 +131,8 @@ func TestMessageCallGas(t *testing.T) {
// Check cold read cost, without a value // Check cold read cost, without a value
gas := ae.MessageCallGas(testAddr) gas := ae.MessageCallGas(testAddr)
if gas != params.WitnessBranchReadCost+params.WitnessChunkReadCost*2 { if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost*2; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessChunkReadCost*2) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check that reading the version and code size of the same account does not incur the branch read cost // Check that reading the version and code size of the same account does not incur the branch read cost

View File

@@ -60,11 +60,11 @@ func newAccessList() *accessList {
} }
// Copy creates an independent copy of an accessList. // Copy creates an independent copy of an accessList.
func (a *accessList) Copy() *accessList { func (al *accessList) Copy() *accessList {
cp := newAccessList() cp := newAccessList()
cp.addresses = maps.Clone(a.addresses) cp.addresses = maps.Clone(al.addresses)
cp.slots = make([]map[common.Hash]struct{}, len(a.slots)) cp.slots = make([]map[common.Hash]struct{}, len(al.slots))
for i, slotMap := range a.slots { for i, slotMap := range al.slots {
cp.slots[i] = maps.Clone(slotMap) cp.slots[i] = maps.Clone(slotMap)
} }
return cp return cp

View File

@@ -123,7 +123,11 @@ type Trie interface {
// The returned nodeset can be nil if the trie is clean(nothing to commit). // The returned nodeset can be nil if the trie is clean(nothing to commit).
// Once the trie is committed, it's not usable anymore. A new trie must // Once the trie is committed, it's not usable anymore. A new trie must
// be created with new root and updated trie database for following usage // be created with new root and updated trie database for following usage
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet)
// Witness returns a set containing all trie nodes that have been accessed.
// The returned map could be nil if the witness is empty.
Witness() map[string]struct{}
// NodeIterator returns an iterator that returns nodes of the trie. Iteration // NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key. And error will be returned // starts at the key after the given start key. And error will be returned

View File

@@ -360,10 +360,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
for i, key := range result.keys { for i, key := range result.keys {
snapTrie.Update(key, result.vals[i]) snapTrie.Update(key, result.vals[i])
} }
root, nodes, err := snapTrie.Commit(false) root, nodes := snapTrie.Commit(false)
if err != nil {
return false, nil, err
}
if nodes != nil { if nodes != nil {
tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
tdb.Commit(root, false) tdb.Commit(root, false)

View File

@@ -210,7 +210,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st
if !commit { if !commit {
return stTrie.Hash() return stTrie.Hash()
} }
root, nodes, _ := stTrie.Commit(false) root, nodes := stTrie.Commit(false)
if nodes != nil { if nodes != nil {
t.nodes.Merge(nodes) t.nodes.Merge(nodes)
} }
@@ -218,7 +218,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st
} }
func (t *testHelper) Commit() common.Hash { func (t *testHelper) Commit() common.Hash {
root, nodes, _ := t.accTrie.Commit(true) root, nodes := t.accTrie.Commit(true)
if nodes != nil { if nodes != nil {
t.nodes.Merge(nodes) t.nodes.Merge(nodes)
} }

View File

@@ -815,7 +815,7 @@ func TestStorageIteratorDeletions(t *testing.T) {
verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage)
} }
// BenchmarkAccountIteratorTraversal is a bit a bit notorious -- all layers contain the // BenchmarkAccountIteratorTraversal is a bit notorious -- all layers contain the
// exact same 200 accounts. That means that we need to process 2000 items, but // exact same 200 accounts. That means that we need to process 2000 items, but
// only spit out 200 values eventually. // only spit out 200 values eventually.
// //

View File

@@ -666,6 +666,9 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
// Release releases resources // Release releases resources
func (t *Tree) Release() { func (t *Tree) Release() {
t.lock.RLock()
defer t.lock.RUnlock()
if dl := t.disklayer(); dl != nil { if dl := t.disklayer(); dl != nil {
dl.Release() dl.Release()
} }
@@ -829,6 +832,8 @@ func (t *Tree) disklayer() *diskLayer {
case *diskLayer: case *diskLayer:
return layer return layer
case *diffLayer: case *diffLayer:
layer.lock.RLock()
defer layer.lock.RUnlock()
return layer.origin return layer.origin
default: default:
panic(fmt.Sprintf("%T: undefined layer", snap)) panic(fmt.Sprintf("%T: undefined layer", snap))
@@ -848,8 +853,8 @@ func (t *Tree) diskRoot() common.Hash {
// generating is an internal helper function which reports whether the snapshot // generating is an internal helper function which reports whether the snapshot
// is still under the construction. // is still under the construction.
func (t *Tree) generating() (bool, error) { func (t *Tree) generating() (bool, error) {
t.lock.Lock() t.lock.RLock()
defer t.lock.Unlock() defer t.lock.RUnlock()
layer := t.disklayer() layer := t.disklayer()
if layer == nil { if layer == nil {
@@ -860,10 +865,10 @@ func (t *Tree) generating() (bool, error) {
return layer.genMarker != nil, nil return layer.genMarker != nil, nil
} }
// DiskRoot is a external helper function to return the disk layer root. // DiskRoot is an external helper function to return the disk layer root.
func (t *Tree) DiskRoot() common.Hash { func (t *Tree) DiskRoot() common.Hash {
t.lock.Lock() t.lock.RLock()
defer t.lock.Unlock() defer t.lock.RUnlock()
return t.diskRoot() return t.diskRoot()
} }

View File

@@ -230,6 +230,14 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
} }
value.SetBytes(val) value.SetBytes(val)
} }
// Independent of where we loaded the data from, add it to the prefetcher.
// Whilst this would be a bit weird if snapshots are disabled, but we still
// want the trie nodes to end up in the prefetcher too, so just push through.
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, [][]byte{key[:]}, true); err != nil {
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
}
}
s.originStorage[key] = value s.originStorage[key] = value
return value return value
} }
@@ -293,7 +301,7 @@ func (s *stateObject) finalise() {
s.pendingStorage[key] = value s.pendingStorage[key] = value
} }
if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch); err != nil { if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch, false); err != nil {
log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err) log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err)
} }
} }
@@ -315,11 +323,17 @@ func (s *stateObject) finalise() {
// //
// It assumes all the dirty storage slots have been finalized before. // It assumes all the dirty storage slots have been finalized before.
func (s *stateObject) updateTrie() (Trie, error) { func (s *stateObject) updateTrie() (Trie, error) {
// Short circuit if nothing changed, don't bother with hashing anything // Short circuit if nothing was accessed, don't trigger a prefetcher warning
if len(s.uncommittedStorage) == 0 { if len(s.uncommittedStorage) == 0 {
return s.trie, nil // Nothing was written, so we could stop early. Unless we have both reads
// and witness collection enabled, in which case we need to fetch the trie.
if s.db.witness == nil || len(s.originStorage) == 0 {
return s.trie, nil
}
} }
// Retrieve a pretecher populated trie, or fall back to the database // Retrieve a pretecher populated trie, or fall back to the database. This will
// block until all prefetch tasks are done, which are needed for witnesses even
// for unmodified state objects.
tr := s.getPrefetchedTrie() tr := s.getPrefetchedTrie()
if tr != nil { if tr != nil {
// Prefetcher returned a live trie, swap it out for the current one // Prefetcher returned a live trie, swap it out for the current one
@@ -333,6 +347,10 @@ func (s *stateObject) updateTrie() (Trie, error) {
return nil, err return nil, err
} }
} }
// Short circuit if nothing changed, don't bother with hashing anything
if len(s.uncommittedStorage) == 0 {
return s.trie, nil
}
// Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes // Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes
// in circumstances similar to the following: // in circumstances similar to the following:
// //
@@ -462,10 +480,7 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
s.origin = s.data.Copy() s.origin = s.data.Copy()
return op, nil, nil return op, nil, nil
} }
root, nodes, err := s.trie.Commit(false) root, nodes := s.trie.Commit(false)
if err != nil {
return nil, nil, err
}
s.data.Root = root s.data.Root = root
s.origin = s.data.Copy() s.origin = s.data.Copy()
return op, nodes, nil return op, nodes, nil

View File

@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@@ -105,7 +106,7 @@ type StateDB struct {
// resurrection. The account value is tracked as the original value // resurrection. The account value is tracked as the original value
// before the transition. This map is populated at the transaction // before the transition. This map is populated at the transaction
// boundaries. // boundaries.
stateObjectsDestruct map[common.Address]*types.StateAccount stateObjectsDestruct map[common.Address]*stateObject
// This map tracks the account mutations that occurred during the // This map tracks the account mutations that occurred during the
// transition. Uncommitted mutations belonging to the same account // transition. Uncommitted mutations belonging to the same account
@@ -146,6 +147,9 @@ type StateDB struct {
validRevisions []revision validRevisions []revision
nextRevisionId int nextRevisionId int
// State witness if cross validation is needed
witness *stateless.Witness
// Measurements gathered during execution for debugging purposes // Measurements gathered during execution for debugging purposes
AccountReads time.Duration AccountReads time.Duration
AccountHashes time.Duration AccountHashes time.Duration
@@ -177,7 +181,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
originalRoot: root, originalRoot: root,
snaps: snaps, snaps: snaps,
stateObjects: make(map[common.Address]*stateObject), stateObjects: make(map[common.Address]*stateObject),
stateObjectsDestruct: make(map[common.Address]*types.StateAccount), stateObjectsDestruct: make(map[common.Address]*stateObject),
mutations: make(map[common.Address]*mutation), mutations: make(map[common.Address]*mutation),
logs: make(map[common.Hash][]*types.Log), logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte), preimages: make(map[common.Hash][]byte),
@@ -200,14 +204,19 @@ func (s *StateDB) SetLogger(l *tracing.Hooks) {
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
// state trie concurrently while the state is mutated so that when we reach the // state trie concurrently while the state is mutated so that when we reach the
// commit phase, most of the needed data is already hot. // commit phase, most of the needed data is already hot.
func (s *StateDB) StartPrefetcher(namespace string) { func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
// Terminate any previously running prefetcher
if s.prefetcher != nil { if s.prefetcher != nil {
s.prefetcher.terminate(false) s.prefetcher.terminate(false)
s.prefetcher.report() s.prefetcher.report()
s.prefetcher = nil s.prefetcher = nil
} }
// Enable witness collection if requested
s.witness = witness
// If snapshots are enabled, start prefethers explicitly
if s.snap != nil { if s.snap != nil {
s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace, witness == nil)
// With the switch to the Proof-of-Stake consensus algorithm, block production // With the switch to the Proof-of-Stake consensus algorithm, block production
// rewards are now handled at the consensus layer. Consequently, a block may // rewards are now handled at the consensus layer. Consequently, a block may
@@ -218,7 +227,7 @@ func (s *StateDB) StartPrefetcher(namespace string) {
// To prevent this, the account trie is always scheduled for prefetching once // To prevent this, the account trie is always scheduled for prefetching once
// the prefetcher is constructed. For more details, see: // the prefetcher is constructed. For more details, see:
// https://github.com/ethereum/go-ethereum/issues/29880 // https://github.com/ethereum/go-ethereum/issues/29880
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil); err != nil { if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, false); err != nil {
log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err) log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err)
} }
} }
@@ -582,7 +591,6 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
start := time.Now() start := time.Now()
acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())) acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes()))
s.SnapshotAccountReads += time.Since(start) s.SnapshotAccountReads += time.Since(start)
if err == nil { if err == nil {
if acc == nil { if acc == nil {
return nil return nil
@@ -616,6 +624,14 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
return nil return nil
} }
} }
// Independent of where we loaded the data from, add it to the prefetcher.
// Whilst this would be a bit weird if snapshots are disabled, but we still
// want the trie nodes to end up in the prefetcher too, so just push through.
if s.prefetcher != nil {
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{addr[:]}, true); err != nil {
log.Error("Failed to prefetch account", "addr", addr, "err", err)
}
}
// Insert into the live set // Insert into the live set
obj := newObject(s, addr, data) obj := newObject(s, addr, data)
s.setStateObject(obj) s.setStateObject(obj)
@@ -675,7 +691,7 @@ func (s *StateDB) Copy() *StateDB {
hasher: crypto.NewKeccakState(), hasher: crypto.NewKeccakState(),
originalRoot: s.originalRoot, originalRoot: s.originalRoot,
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)), stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
stateObjectsDestruct: maps.Clone(s.stateObjectsDestruct), stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
mutations: make(map[common.Address]*mutation, len(s.mutations)), mutations: make(map[common.Address]*mutation, len(s.mutations)),
dbErr: s.dbErr, dbErr: s.dbErr,
refund: s.refund, refund: s.refund,
@@ -695,10 +711,17 @@ func (s *StateDB) Copy() *StateDB {
snaps: s.snaps, snaps: s.snaps,
snap: s.snap, snap: s.snap,
} }
if s.witness != nil {
state.witness = s.witness.Copy()
}
// Deep copy cached state objects. // Deep copy cached state objects.
for addr, obj := range s.stateObjects { for addr, obj := range s.stateObjects {
state.stateObjects[addr] = obj.deepCopy(state) state.stateObjects[addr] = obj.deepCopy(state)
} }
// Deep copy destructed state objects.
for addr, obj := range s.stateObjectsDestruct {
state.stateObjectsDestruct[addr] = obj.deepCopy(state)
}
// Deep copy the object state markers. // Deep copy the object state markers.
for addr, op := range s.mutations { for addr, op := range s.mutations {
state.mutations[addr] = op.copy() state.mutations[addr] = op.copy()
@@ -780,7 +803,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
// set indefinitely). Note only the first occurred self-destruct // set indefinitely). Note only the first occurred self-destruct
// event is tracked. // event is tracked.
if _, ok := s.stateObjectsDestruct[obj.address]; !ok { if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
s.stateObjectsDestruct[obj.address] = obj.origin s.stateObjectsDestruct[obj.address] = obj
} }
} else { } else {
obj.finalise() obj.finalise()
@@ -792,7 +815,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure
} }
if s.prefetcher != nil && len(addressesToPrefetch) > 0 { if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch); err != nil { if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, false); err != nil {
log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err) log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err)
} }
} }
@@ -838,9 +861,46 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
obj := s.stateObjects[addr] // closure for the task runner below obj := s.stateObjects[addr] // closure for the task runner below
workers.Go(func() error { workers.Go(func() error {
obj.updateRoot() obj.updateRoot()
// If witness building is enabled and the state object has a trie,
// gather the witnesses for its specific storage trie
if s.witness != nil && obj.trie != nil {
s.witness.AddState(obj.trie.Witness())
}
return nil return nil
}) })
} }
// If witness building is enabled, gather all the read-only accesses
if s.witness != nil {
// Pull in anything that has been accessed before destruction
for _, obj := range s.stateObjectsDestruct {
// Skip any objects that haven't touched their storage
if len(obj.originStorage) == 0 {
continue
}
if trie := obj.getPrefetchedTrie(); trie != nil {
s.witness.AddState(trie.Witness())
} else if obj.trie != nil {
s.witness.AddState(obj.trie.Witness())
}
}
// Pull in only-read and non-destructed trie witnesses
for _, obj := range s.stateObjects {
// Skip any objects that have been updated
if _, ok := s.mutations[obj.address]; ok {
continue
}
// Skip any objects that haven't touched their storage
if len(obj.originStorage) == 0 {
continue
}
if trie := obj.getPrefetchedTrie(); trie != nil {
s.witness.AddState(trie.Witness())
} else if obj.trie != nil {
s.witness.AddState(obj.trie.Witness())
}
}
}
workers.Wait() workers.Wait()
s.StorageUpdates += time.Since(start) s.StorageUpdates += time.Since(start)
@@ -896,7 +956,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Track the amount of time wasted on hashing the account trie // Track the amount of time wasted on hashing the account trie
defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
return s.trie.Hash() hash := s.trie.Hash()
// If witness building is enabled, gather the account trie witness
if s.witness != nil {
s.witness.AddState(s.trie.Witness())
}
return hash
} }
// SetTxContext sets the current transaction hash and index which are // SetTxContext sets the current transaction hash and index which are
@@ -1052,7 +1118,9 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno
buf = crypto.NewKeccakState() buf = crypto.NewKeccakState()
deletes = make(map[common.Hash]*accountDelete) deletes = make(map[common.Hash]*accountDelete)
) )
for addr, prev := range s.stateObjectsDestruct { for addr, prevObj := range s.stateObjectsDestruct {
prev := prevObj.origin
// The account was non-existent, and it's marked as destructed in the scope // The account was non-existent, and it's marked as destructed in the scope
// of block. It can be either case (a) or (b) and will be interpreted as // of block. It can be either case (a) or (b) and will be interpreted as
// null->null state transition. // null->null state transition.
@@ -1171,10 +1239,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
// code didn't anticipate for. // code didn't anticipate for.
workers.Go(func() error { workers.Go(func() error {
// Write the account trie changes, measuring the amount of wasted time // Write the account trie changes, measuring the amount of wasted time
newroot, set, err := s.trie.Commit(true) newroot, set := s.trie.Commit(true)
if err != nil {
return err
}
root = newroot root = newroot
if err := merge(set); err != nil { if err := merge(set); err != nil {
@@ -1234,7 +1299,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
// Clear all internal flags and update state root at the end. // Clear all internal flags and update state root at the end.
s.mutations = make(map[common.Address]*mutation) s.mutations = make(map[common.Address]*mutation)
s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) s.stateObjectsDestruct = make(map[common.Address]*stateObject)
origin := s.originalRoot origin := s.originalRoot
s.originalRoot = root s.originalRoot = root
@@ -1407,3 +1472,8 @@ func (s *StateDB) markUpdate(addr common.Address) {
func (s *StateDB) PointCache() *utils.PointCache { func (s *StateDB) PointCache() *utils.PointCache {
return s.db.PointCache() return s.db.PointCache()
} }
// Witness retrieves the current state witness being collected.
func (s *StateDB) Witness() *stateless.Witness {
return s.witness
}

View File

@@ -44,31 +44,49 @@ type triePrefetcher struct {
root common.Hash // Root hash of the account trie for metrics root common.Hash // Root hash of the account trie for metrics
fetchers map[string]*subfetcher // Subfetchers for each trie fetchers map[string]*subfetcher // Subfetchers for each trie
term chan struct{} // Channel to signal interruption term chan struct{} // Channel to signal interruption
noreads bool // Whether to ignore state-read-only prefetch requests
deliveryMissMeter metrics.Meter deliveryMissMeter metrics.Meter
accountLoadMeter metrics.Meter
accountDupMeter metrics.Meter accountLoadReadMeter metrics.Meter
accountWasteMeter metrics.Meter accountLoadWriteMeter metrics.Meter
storageLoadMeter metrics.Meter accountDupReadMeter metrics.Meter
storageDupMeter metrics.Meter accountDupWriteMeter metrics.Meter
storageWasteMeter metrics.Meter accountDupCrossMeter metrics.Meter
accountWasteMeter metrics.Meter
storageLoadReadMeter metrics.Meter
storageLoadWriteMeter metrics.Meter
storageDupReadMeter metrics.Meter
storageDupWriteMeter metrics.Meter
storageDupCrossMeter metrics.Meter
storageWasteMeter metrics.Meter
} }
func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher { func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
prefix := triePrefetchMetricsPrefix + namespace prefix := triePrefetchMetricsPrefix + namespace
return &triePrefetcher{ return &triePrefetcher{
db: db, db: db,
root: root, root: root,
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
term: make(chan struct{}), term: make(chan struct{}),
noreads: noreads,
deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil), deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil),
accountLoadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load", nil),
accountDupMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup", nil), accountLoadReadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load/read", nil),
accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil), accountLoadWriteMeter: metrics.GetOrRegisterMeter(prefix+"/account/load/write", nil),
storageLoadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load", nil), accountDupReadMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/read", nil),
storageDupMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup", nil), accountDupWriteMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/write", nil),
storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), accountDupCrossMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/cross", nil),
accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil),
storageLoadReadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load/read", nil),
storageLoadWriteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load/write", nil),
storageDupReadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/read", nil),
storageDupWriteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/write", nil),
storageDupCrossMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/cross", nil),
storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil),
} }
} }
@@ -98,19 +116,31 @@ func (p *triePrefetcher) report() {
fetcher.wait() // ensure the fetcher's idle before poking in its internals fetcher.wait() // ensure the fetcher's idle before poking in its internals
if fetcher.root == p.root { if fetcher.root == p.root {
p.accountLoadMeter.Mark(int64(len(fetcher.seen))) p.accountLoadReadMeter.Mark(int64(len(fetcher.seenRead)))
p.accountDupMeter.Mark(int64(fetcher.dups)) p.accountLoadWriteMeter.Mark(int64(len(fetcher.seenWrite)))
p.accountDupReadMeter.Mark(int64(fetcher.dupsRead))
p.accountDupWriteMeter.Mark(int64(fetcher.dupsWrite))
p.accountDupCrossMeter.Mark(int64(fetcher.dupsCross))
for _, key := range fetcher.used { for _, key := range fetcher.used {
delete(fetcher.seen, string(key)) delete(fetcher.seenRead, string(key))
delete(fetcher.seenWrite, string(key))
} }
p.accountWasteMeter.Mark(int64(len(fetcher.seen))) p.accountWasteMeter.Mark(int64(len(fetcher.seenRead) + len(fetcher.seenWrite)))
} else { } else {
p.storageLoadMeter.Mark(int64(len(fetcher.seen))) p.storageLoadReadMeter.Mark(int64(len(fetcher.seenRead)))
p.storageDupMeter.Mark(int64(fetcher.dups)) p.storageLoadWriteMeter.Mark(int64(len(fetcher.seenWrite)))
p.storageDupReadMeter.Mark(int64(fetcher.dupsRead))
p.storageDupWriteMeter.Mark(int64(fetcher.dupsWrite))
p.storageDupCrossMeter.Mark(int64(fetcher.dupsCross))
for _, key := range fetcher.used { for _, key := range fetcher.used {
delete(fetcher.seen, string(key)) delete(fetcher.seenRead, string(key))
delete(fetcher.seenWrite, string(key))
} }
p.storageWasteMeter.Mark(int64(len(fetcher.seen))) p.storageWasteMeter.Mark(int64(len(fetcher.seenRead) + len(fetcher.seenWrite)))
} }
} }
} }
@@ -126,7 +156,11 @@ func (p *triePrefetcher) report() {
// upon the same contract, the parameters invoking this method may be // upon the same contract, the parameters invoking this method may be
// repeated. // repeated.
// 2. Finalize of the main account trie. This happens only once per block. // 2. Finalize of the main account trie. This happens only once per block.
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) error { func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte, read bool) error {
// If the state item is only being read, but reads are disabled, return
if read && p.noreads {
return nil
}
// Ensure the subfetcher is still alive // Ensure the subfetcher is still alive
select { select {
case <-p.term: case <-p.term:
@@ -139,7 +173,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr comm
fetcher = newSubfetcher(p.db, p.root, owner, root, addr) fetcher = newSubfetcher(p.db, p.root, owner, root, addr)
p.fetchers[id] = fetcher p.fetchers[id] = fetcher
} }
return fetcher.schedule(keys) return fetcher.schedule(keys, read)
} }
// trie returns the trie matching the root hash, blocking until the fetcher of // trie returns the trie matching the root hash, blocking until the fetcher of
@@ -186,38 +220,51 @@ type subfetcher struct {
addr common.Address // Address of the account that the trie belongs to addr common.Address // Address of the account that the trie belongs to
trie Trie // Trie being populated with nodes trie Trie // Trie being populated with nodes
tasks [][]byte // Items queued up for retrieval tasks []*subfetcherTask // Items queued up for retrieval
lock sync.Mutex // Lock protecting the task queue lock sync.Mutex // Lock protecting the task queue
wake chan struct{} // Wake channel if a new task is scheduled wake chan struct{} // Wake channel if a new task is scheduled
stop chan struct{} // Channel to interrupt processing stop chan struct{} // Channel to interrupt processing
term chan struct{} // Channel to signal interruption term chan struct{} // Channel to signal interruption
seen map[string]struct{} // Tracks the entries already loaded seenRead map[string]struct{} // Tracks the entries already loaded via read operations
dups int // Number of duplicate preload tasks seenWrite map[string]struct{} // Tracks the entries already loaded via write operations
used [][]byte // Tracks the entries used in the end
dupsRead int // Number of duplicate preload tasks via reads only
dupsWrite int // Number of duplicate preload tasks via writes only
dupsCross int // Number of duplicate preload tasks via read-write-crosses
used [][]byte // Tracks the entries used in the end
}
// subfetcherTask is a trie path to prefetch, tagged with whether it originates
// from a read or a write request.
type subfetcherTask struct {
read bool
key []byte
} }
// newSubfetcher creates a goroutine to prefetch state items belonging to a // newSubfetcher creates a goroutine to prefetch state items belonging to a
// particular root hash. // particular root hash.
func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher { func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher {
sf := &subfetcher{ sf := &subfetcher{
db: db, db: db,
state: state, state: state,
owner: owner, owner: owner,
root: root, root: root,
addr: addr, addr: addr,
wake: make(chan struct{}, 1), wake: make(chan struct{}, 1),
stop: make(chan struct{}), stop: make(chan struct{}),
term: make(chan struct{}), term: make(chan struct{}),
seen: make(map[string]struct{}), seenRead: make(map[string]struct{}),
seenWrite: make(map[string]struct{}),
} }
go sf.loop() go sf.loop()
return sf return sf
} }
// schedule adds a batch of trie keys to the queue to prefetch. // schedule adds a batch of trie keys to the queue to prefetch.
func (sf *subfetcher) schedule(keys [][]byte) error { func (sf *subfetcher) schedule(keys [][]byte, read bool) error {
// Ensure the subfetcher is still alive // Ensure the subfetcher is still alive
select { select {
case <-sf.term: case <-sf.term:
@@ -226,7 +273,10 @@ func (sf *subfetcher) schedule(keys [][]byte) error {
} }
// Append the tasks to the current queue // Append the tasks to the current queue
sf.lock.Lock() sf.lock.Lock()
sf.tasks = append(sf.tasks, keys...) for _, key := range keys {
key := key // closure for the append below
sf.tasks = append(sf.tasks, &subfetcherTask{read: read, key: key})
}
sf.lock.Unlock() sf.lock.Unlock()
// Notify the background thread to execute scheduled tasks // Notify the background thread to execute scheduled tasks
@@ -303,16 +353,36 @@ func (sf *subfetcher) loop() {
sf.lock.Unlock() sf.lock.Unlock()
for _, task := range tasks { for _, task := range tasks {
if _, ok := sf.seen[string(task)]; ok { key := string(task.key)
sf.dups++ if task.read {
continue if _, ok := sf.seenRead[key]; ok {
} sf.dupsRead++
if len(task) == common.AddressLength { continue
sf.trie.GetAccount(common.BytesToAddress(task)) }
if _, ok := sf.seenWrite[key]; ok {
sf.dupsCross++
continue
}
} else { } else {
sf.trie.GetStorage(sf.addr, task) if _, ok := sf.seenRead[key]; ok {
sf.dupsCross++
continue
}
if _, ok := sf.seenWrite[key]; ok {
sf.dupsWrite++
continue
}
}
if len(task.key) == common.AddressLength {
sf.trie.GetAccount(common.BytesToAddress(task.key))
} else {
sf.trie.GetStorage(sf.addr, task.key)
}
if task.read {
sf.seenRead[key] = struct{}{}
} else {
sf.seenWrite[key] = struct{}{}
} }
sf.seen[string(task)] = struct{}{}
} }
case <-sf.stop: case <-sf.stop:

View File

@@ -47,15 +47,15 @@ func filledStateDB() *StateDB {
func TestUseAfterTerminate(t *testing.T) { func TestUseAfterTerminate(t *testing.T) {
db := filledStateDB() db := filledStateDB()
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "") prefetcher := newTriePrefetcher(db.db, db.originalRoot, "", true)
skey := common.HexToHash("aaa") skey := common.HexToHash("aaa")
if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}); err != nil { if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}, false); err != nil {
t.Errorf("Prefetch failed before terminate: %v", err) t.Errorf("Prefetch failed before terminate: %v", err)
} }
prefetcher.terminate(false) prefetcher.terminate(false)
if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}); err == nil { if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}, false); err == nil {
t.Errorf("Prefetch succeeded after terminate: %v", err) t.Errorf("Prefetch succeeded after terminate: %v", err)
} }
if tr := prefetcher.trie(common.Hash{}, db.originalRoot); tr == nil { if tr := prefetcher.trie(common.Hash{}, db.originalRoot); tr == nil {

View File

@@ -19,7 +19,6 @@ package core
import ( import (
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@@ -31,16 +30,14 @@ import (
// data from disk before the main block processor start executing. // data from disk before the main block processor start executing.
type statePrefetcher struct { type statePrefetcher struct {
config *params.ChainConfig // Chain configuration options config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain chain *HeaderChain // Canonical block chain
engine consensus.Engine // Consensus engine used for block rewards
} }
// newStatePrefetcher initialises a new statePrefetcher. // newStatePrefetcher initialises a new statePrefetcher.
func newStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *statePrefetcher { func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePrefetcher {
return &statePrefetcher{ return &statePrefetcher{
config: config, config: config,
bc: bc, chain: chain,
engine: engine,
} }
} }
@@ -51,7 +48,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
var ( var (
header = block.Header() header = block.Header()
gaspool = new(GasPool).AddGas(block.GasLimit()) gaspool = new(GasPool).AddGas(block.GasLimit())
blockContext = NewEVMBlockContext(header, p.bc, nil) blockContext = NewEVMBlockContext(header, p.chain, nil)
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number, header.Time) signer = types.MakeSigner(p.config, header.Number, header.Time)
) )

View File

@@ -22,7 +22,6 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@@ -37,16 +36,14 @@ import (
// StateProcessor implements Processor. // StateProcessor implements Processor.
type StateProcessor struct { type StateProcessor struct {
config *params.ChainConfig // Chain configuration options config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain chain *HeaderChain // Canonical header chain
engine consensus.Engine // Consensus engine used for block rewards
} }
// NewStateProcessor initialises a new StateProcessor. // NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StateProcessor {
return &StateProcessor{ return &StateProcessor{
config: config, config: config,
bc: bc, chain: chain,
engine: engine,
} }
} }
@@ -73,10 +70,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
var ( var (
context = NewEVMBlockContext(header, p.bc, nil) context vm.BlockContext
vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number, header.Time) signer = types.MakeSigner(p.config, header.Number, header.Time)
) )
context = NewEVMBlockContext(header, p.chain, nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
if beaconRoot := block.BeaconRoot(); beaconRoot != nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
} }
@@ -101,7 +99,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, nil, 0, errors.New("withdrawals before shanghai") return nil, nil, 0, errors.New("withdrawals before shanghai")
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Body()) p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }

73
core/stateless.go Normal file
View File

@@ -0,0 +1,73 @@
// Copyright 2024 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 core
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
)
// ExecuteStateless runs a stateless execution based on a witness, verifies
// everything it can locally and returns the two computed fields that need the
// other side to explicitly check.
//
// This method is a bit of a sore thumb here, but:
// - It cannot be placed in core/stateless, because state.New prodces a circular dep
// - It cannot be placed outside of core, because it needs to construct a dud headerchain
//
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
func ExecuteStateless(config *params.ChainConfig, witness *stateless.Witness) (common.Hash, common.Hash, error) {
// Create and populate the state database to serve as the stateless backend
memdb := witness.MakeHashDB()
db, err := state.New(witness.Root(), state.NewDatabaseWithConfig(memdb, triedb.HashDefaults), nil)
if err != nil {
return common.Hash{}, common.Hash{}, err
}
// Create a blockchain that is idle, but can be used to access headers through
chain := &HeaderChain{
config: config,
chainDb: memdb,
headerCache: lru.NewCache[common.Hash, *types.Header](256),
engine: beacon.New(ethash.NewFaker()),
}
processor := NewStateProcessor(config, chain)
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
// Run the stateless blocks processing and self-validate certain fields
receipts, _, usedGas, err := processor.Process(witness.Block, db, vm.Config{})
if err != nil {
return common.Hash{}, common.Hash{}, err
}
if err = validator.ValidateState(witness.Block, db, receipts, usedGas, true); err != nil {
return common.Hash{}, common.Hash{}, err
}
// Almost everything validated, but receipt and state root needs to be returned
receiptRoot := types.DeriveSha(receipts, trie.NewStackTrie(nil))
stateRoot := db.IntermediateRoot(config.IsEIP158(witness.Block.Number()))
return receiptRoot, stateRoot, nil
}

View File

@@ -0,0 +1,60 @@
// Copyright 2024 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 stateless
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)
// MakeHashDB imports tries, codes and block hashes from a witness into a new
// hash-based memory db. We could eventually rewrite this into a pathdb, but
// simple is better for now.
func (w *Witness) MakeHashDB() ethdb.Database {
var (
memdb = rawdb.NewMemoryDatabase()
hasher = crypto.NewKeccakState()
hash = make([]byte, 32)
)
// Inject all the "block hashes" (i.e. headers) into the ephemeral database
for _, header := range w.Headers {
rawdb.WriteHeader(memdb, header)
}
// Inject all the bytecodes into the ephemeral database
for code := range w.Codes {
blob := []byte(code)
hasher.Reset()
hasher.Write(blob)
hasher.Read(hash)
rawdb.WriteCode(memdb, common.BytesToHash(hash), blob)
}
// Inject all the MPT trie nodes into the ephemeral database
for node := range w.State {
blob := []byte(node)
hasher.Reset()
hasher.Write(blob)
hasher.Read(hash)
rawdb.WriteLegacyTrieNode(memdb, common.BytesToHash(hash), blob)
}
return memdb
}

129
core/stateless/encoding.go Normal file
View File

@@ -0,0 +1,129 @@
// Copyright 2024 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 stateless
import (
"bytes"
"errors"
"fmt"
"io"
"slices"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
//go:generate go run github.com/fjl/gencodec -type extWitness -field-override extWitnessMarshalling -out gen_encoding_json.go
// toExtWitness converts our internal witness representation to the consensus one.
func (w *Witness) toExtWitness() *extWitness {
ext := &extWitness{
Block: w.Block,
Headers: w.Headers,
}
ext.Codes = make([][]byte, 0, len(w.Codes))
for code := range w.Codes {
ext.Codes = append(ext.Codes, []byte(code))
}
slices.SortFunc(ext.Codes, bytes.Compare)
ext.State = make([][]byte, 0, len(w.State))
for node := range w.State {
ext.State = append(ext.State, []byte(node))
}
slices.SortFunc(ext.State, bytes.Compare)
return ext
}
// fromExtWitness converts the consensus witness format into our internal one.
func (w *Witness) fromExtWitness(ext *extWitness) error {
w.Block, w.Headers = ext.Block, ext.Headers
w.Codes = make(map[string]struct{}, len(ext.Codes))
for _, code := range ext.Codes {
w.Codes[string(code)] = struct{}{}
}
w.State = make(map[string]struct{}, len(ext.State))
for _, node := range ext.State {
w.State[string(node)] = struct{}{}
}
return w.sanitize()
}
// MarshalJSON marshals a witness as JSON.
func (w *Witness) MarshalJSON() ([]byte, error) {
return w.toExtWitness().MarshalJSON()
}
// EncodeRLP serializes a witness as RLP.
func (w *Witness) EncodeRLP(wr io.Writer) error {
return rlp.Encode(wr, w.toExtWitness())
}
// UnmarshalJSON unmarshals from JSON.
func (w *Witness) UnmarshalJSON(input []byte) error {
var ext extWitness
if err := ext.UnmarshalJSON(input); err != nil {
return err
}
return w.fromExtWitness(&ext)
}
// DecodeRLP decodes a witness from RLP.
func (w *Witness) DecodeRLP(s *rlp.Stream) error {
var ext extWitness
if err := s.Decode(&ext); err != nil {
return err
}
return w.fromExtWitness(&ext)
}
// sanitize checks for some mandatory fields in the witness after decoding so
// the rest of the code can assume invariants and doesn't have to deal with
// corrupted data.
func (w *Witness) sanitize() error {
// Verify that the "parent" header (i.e. index 0) is available, and is the
// true parent of the block-to-be executed, since we use that to link the
// current block to the pre-state.
if len(w.Headers) == 0 {
return errors.New("parent header (for pre-root hash) missing")
}
for i, header := range w.Headers {
if header == nil {
return fmt.Errorf("witness header nil at position %d", i)
}
}
if w.Headers[0].Hash() != w.Block.ParentHash() {
return fmt.Errorf("parent hash different: witness %v, block parent %v", w.Headers[0].Hash(), w.Block.ParentHash())
}
return nil
}
// extWitness is a witness RLP encoding for transferring across clients.
type extWitness struct {
Block *types.Block `json:"block" gencodec:"required"`
Headers []*types.Header `json:"headers" gencodec:"required"`
Codes [][]byte `json:"codes"`
State [][]byte `json:"state"`
}
// extWitnessMarshalling defines the hex marshalling types for a witness.
type extWitnessMarshalling struct {
Codes []hexutil.Bytes
State []hexutil.Bytes
}

View File

@@ -0,0 +1,74 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package stateless
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = (*extWitnessMarshalling)(nil)
// MarshalJSON marshals as JSON.
func (e extWitness) MarshalJSON() ([]byte, error) {
type extWitness struct {
Block *types.Block `json:"block" gencodec:"required"`
Headers []*types.Header `json:"headers" gencodec:"required"`
Codes []hexutil.Bytes `json:"codes"`
State []hexutil.Bytes `json:"state"`
}
var enc extWitness
enc.Block = e.Block
enc.Headers = e.Headers
if e.Codes != nil {
enc.Codes = make([]hexutil.Bytes, len(e.Codes))
for k, v := range e.Codes {
enc.Codes[k] = v
}
}
if e.State != nil {
enc.State = make([]hexutil.Bytes, len(e.State))
for k, v := range e.State {
enc.State[k] = v
}
}
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (e *extWitness) UnmarshalJSON(input []byte) error {
type extWitness struct {
Block *types.Block `json:"block" gencodec:"required"`
Headers []*types.Header `json:"headers" gencodec:"required"`
Codes []hexutil.Bytes `json:"codes"`
State []hexutil.Bytes `json:"state"`
}
var dec extWitness
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Block == nil {
return errors.New("missing required field 'block' for extWitness")
}
e.Block = dec.Block
if dec.Headers == nil {
return errors.New("missing required field 'headers' for extWitness")
}
e.Headers = dec.Headers
if dec.Codes != nil {
e.Codes = make([][]byte, len(dec.Codes))
for k, v := range dec.Codes {
e.Codes[k] = v
}
}
if dec.State != nil {
e.State = make([][]byte, len(dec.State))
for k, v := range dec.State {
e.State[k] = v
}
}
return nil
}

159
core/stateless/witness.go Normal file
View File

@@ -0,0 +1,159 @@
// Copyright 2024 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 stateless
import (
"bytes"
"errors"
"fmt"
"maps"
"slices"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
// HeaderReader is an interface to pull in headers in place of block hashes for
// the witness.
type HeaderReader interface {
// GetHeader retrieves a block header from the database by hash and number,
GetHeader(hash common.Hash, number uint64) *types.Header
}
// Witness encompasses a block, state and any other chain data required to apply
// a set of transactions and derive a post state/receipt root.
type Witness struct {
Block *types.Block // Current block with rootHash and receiptHash zeroed out
Headers []*types.Header // Past headers in reverse order (0=parent, 1=parent's-parent, etc). First *must* be set.
Codes map[string]struct{} // Set of bytecodes ran or accessed
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
chain HeaderReader // Chain reader to convert block hash ops to header proofs
lock sync.Mutex // Lock to allow concurrent state insertions
}
// NewWitness creates an empty witness ready for population.
func NewWitness(chain HeaderReader, block *types.Block) (*Witness, error) {
// Zero out the result fields to avoid accidentally sending them to the verifier
header := block.Header()
header.Root = common.Hash{}
header.ReceiptHash = common.Hash{}
// Retrieve the parent header, which will *always* be included to act as a
// trustless pre-root hash container
parent := chain.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, errors.New("failed to retrieve parent header")
}
// Create the wtness with a reconstructed gutted out block
return &Witness{
Block: types.NewBlockWithHeader(header).WithBody(*block.Body()),
Codes: make(map[string]struct{}),
State: make(map[string]struct{}),
Headers: []*types.Header{parent},
chain: chain,
}, nil
}
// AddBlockHash adds a "blockhash" to the witness with the designated offset from
// chain head. Under the hood, this method actually pulls in enough headers from
// the chain to cover the block being added.
func (w *Witness) AddBlockHash(number uint64) {
// Keep pulling in headers until this hash is populated
for int(w.Block.NumberU64()-number) > len(w.Headers) {
tail := w.Block.Header()
if len(w.Headers) > 0 {
tail = w.Headers[len(w.Headers)-1]
}
w.Headers = append(w.Headers, w.chain.GetHeader(tail.ParentHash, tail.Number.Uint64()-1))
}
}
// AddCode adds a bytecode blob to the witness.
func (w *Witness) AddCode(code []byte) {
if len(code) == 0 {
return
}
w.Codes[string(code)] = struct{}{}
}
// AddState inserts a batch of MPT trie nodes into the witness.
func (w *Witness) AddState(nodes map[string]struct{}) {
if len(nodes) == 0 {
return
}
w.lock.Lock()
defer w.lock.Unlock()
for node := range nodes {
w.State[node] = struct{}{}
}
}
// Copy deep-copies the witness object. Witness.Block isn't deep-copied as it
// is never mutated by Witness
func (w *Witness) Copy() *Witness {
return &Witness{
Block: w.Block,
Headers: slices.Clone(w.Headers),
Codes: maps.Clone(w.Codes),
State: maps.Clone(w.State),
}
}
// String prints a human-readable summary containing the total size of the
// witness and the sizes of the underlying components
func (w *Witness) String() string {
blob, _ := rlp.EncodeToBytes(w)
bytesTotal := len(blob)
blob, _ = rlp.EncodeToBytes(w.Block)
bytesBlock := len(blob)
bytesHeaders := 0
for _, header := range w.Headers {
blob, _ = rlp.EncodeToBytes(header)
bytesHeaders += len(blob)
}
bytesCodes := 0
for code := range w.Codes {
bytesCodes += len(code)
}
bytesState := 0
for node := range w.State {
bytesState += len(node)
}
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "Witness #%d: %v\n", w.Block.Number(), common.StorageSize(bytesTotal))
fmt.Fprintf(buf, " block (%4d txs): %10v\n", len(w.Block.Transactions()), common.StorageSize(bytesBlock))
fmt.Fprintf(buf, "%4d headers: %10v\n", len(w.Headers), common.StorageSize(bytesHeaders))
fmt.Fprintf(buf, "%4d trie nodes: %10v\n", len(w.State), common.StorageSize(bytesState))
fmt.Fprintf(buf, "%4d codes: %10v\n", len(w.Codes), common.StorageSize(bytesCodes))
return buf.String()
}
// Root returns the pre-state root from the first header.
//
// Note, this method will panic in case of a bad witness (but RLP decoding will
// sanitize it and fail before that).
func (w *Witness) Root() common.Hash {
return w.Headers[0].Root
}

View File

@@ -199,7 +199,7 @@ type Hooks struct {
// for tracing and reporting. // for tracing and reporting.
type BalanceChangeReason byte type BalanceChangeReason byte
//go:generate stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go //go:generate go run golang.org/x/tools/cmd/stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go
const ( const (
BalanceChangeUnspecified BalanceChangeReason = 0 BalanceChangeUnspecified BalanceChangeReason = 0

View File

@@ -407,7 +407,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres
if p.head.ExcessBlobGas != nil { if p.head.ExcessBlobGas != nil {
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas)) blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas))
} }
p.evict = newPriceHeap(basefee, blobfee, &p.index) p.evict = newPriceHeap(basefee, blobfee, p.index)
// Pool initialized, attach the blob limbo to it to track blobs included // Pool initialized, attach the blob limbo to it to track blobs included
// recently but not yet finalized // recently but not yet finalized
@@ -1598,8 +1598,8 @@ 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.Lock() p.lock.RLock()
defer p.lock.Unlock() defer p.lock.RUnlock()
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
@@ -1610,8 +1610,8 @@ func (p *BlobPool) Nonce(addr common.Address) uint64 {
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions. // number of queued (non-executable) transactions.
func (p *BlobPool) Stats() (int, int) { func (p *BlobPool) Stats() (int, int) {
p.lock.Lock() p.lock.RLock()
defer p.lock.Unlock() defer p.lock.RUnlock()
var pending int var pending int
for _, txs := range p.index { for _, txs := range p.index {

View File

@@ -143,7 +143,7 @@ func (bc *testBlockChain) CurrentFinalBlock() *types.Header {
} }
} }
func (bt *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
return nil return nil
} }

View File

@@ -17,13 +17,13 @@
package blobpool package blobpool
import ( import (
"bytes"
"container/heap" "container/heap"
"math" "math"
"sort" "slices"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/exp/maps"
) )
// evictHeap is a helper data structure to keep track of the cheapest bottleneck // evictHeap is a helper data structure to keep track of the cheapest bottleneck
@@ -35,7 +35,7 @@ import (
// The goal of the heap is to decide which account has the worst bottleneck to // The goal of the heap is to decide which account has the worst bottleneck to
// evict transactions from. // evict transactions from.
type evictHeap struct { type evictHeap struct {
metas *map[common.Address][]*blobTxMeta // Pointer to the blob pool's index for price retrievals metas map[common.Address][]*blobTxMeta // Pointer to the blob pool's index for price retrievals
basefeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the base fee basefeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the base fee
blobfeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the blob fee blobfeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the blob fee
@@ -46,23 +46,18 @@ type evictHeap struct {
// newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict // newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict
// from in case of over saturation. // from in case of over saturation.
func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap { func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index map[common.Address][]*blobTxMeta) *evictHeap {
heap := &evictHeap{ heap := &evictHeap{
metas: index, metas: index,
index: make(map[common.Address]int), index: make(map[common.Address]int, len(index)),
} }
// Populate the heap in account sort order. Not really needed in practice, // Populate the heap in account sort order. Not really needed in practice,
// but it makes the heap initialization deterministic and less annoying to // but it makes the heap initialization deterministic and less annoying to
// test in unit tests. // test in unit tests.
addrs := make([]common.Address, 0, len(*index)) heap.addrs = maps.Keys(index)
for addr := range *index { slices.SortFunc(heap.addrs, common.Address.Cmp)
addrs = append(addrs, addr) for i, addr := range heap.addrs {
} heap.index[addr] = i
sort.Slice(addrs, func(i, j int) bool { return bytes.Compare(addrs[i][:], addrs[j][:]) < 0 })
for _, addr := range addrs {
heap.index[addr] = len(heap.addrs)
heap.addrs = append(heap.addrs, addr)
} }
heap.reinit(basefee, blobfee, true) heap.reinit(basefee, blobfee, true)
return heap return heap
@@ -94,8 +89,8 @@ func (h *evictHeap) Len() int {
// Less implements sort.Interface as part of heap.Interface, returning which of // Less implements sort.Interface as part of heap.Interface, returning which of
// the two requested accounts has a cheaper bottleneck. // the two requested accounts has a cheaper bottleneck.
func (h *evictHeap) Less(i, j int) bool { func (h *evictHeap) Less(i, j int) bool {
txsI := (*(h.metas))[h.addrs[i]] txsI := h.metas[h.addrs[i]]
txsJ := (*(h.metas))[h.addrs[j]] txsJ := h.metas[h.addrs[j]]
lastI := txsI[len(txsI)-1] lastI := txsI[len(txsI)-1]
lastJ := txsJ[len(txsJ)-1] lastJ := txsJ[len(txsJ)-1]

View File

@@ -37,17 +37,17 @@ func verifyHeapInternals(t *testing.T, evict *evictHeap) {
seen := make(map[common.Address]struct{}) seen := make(map[common.Address]struct{})
for i, addr := range evict.addrs { for i, addr := range evict.addrs {
seen[addr] = struct{}{} seen[addr] = struct{}{}
if _, ok := (*evict.metas)[addr]; !ok { if _, ok := evict.metas[addr]; !ok {
t.Errorf("heap contains unexpected address at slot %d: %v", i, addr) t.Errorf("heap contains unexpected address at slot %d: %v", i, addr)
} }
} }
for addr := range *evict.metas { for addr := range evict.metas {
if _, ok := seen[addr]; !ok { if _, ok := seen[addr]; !ok {
t.Errorf("heap is missing required address %v", addr) t.Errorf("heap is missing required address %v", addr)
} }
} }
if len(evict.addrs) != len(*evict.metas) { if len(evict.addrs) != len(evict.metas) {
t.Errorf("heap size %d mismatches metadata size %d", len(evict.addrs), len(*evict.metas)) t.Errorf("heap size %d mismatches metadata size %d", len(evict.addrs), len(evict.metas))
} }
// Ensure that all accounts are present in the heap order index and no extras // Ensure that all accounts are present in the heap order index and no extras
have := make([]common.Address, len(evict.index)) have := make([]common.Address, len(evict.index))
@@ -159,7 +159,7 @@ func TestPriceHeapSorting(t *testing.T) {
}} }}
} }
// Create a price heap and check the pop order // Create a price heap and check the pop order
priceheap := newPriceHeap(uint256.NewInt(tt.basefee), uint256.NewInt(tt.blobfee), &index) priceheap := newPriceHeap(uint256.NewInt(tt.basefee), uint256.NewInt(tt.blobfee), index)
verifyHeapInternals(t, priceheap) verifyHeapInternals(t, priceheap)
for j := 0; j < len(tt.order); j++ { for j := 0; j < len(tt.order); j++ {
@@ -218,7 +218,7 @@ func benchmarkPriceHeapReinit(b *testing.B, datacap uint64) {
}} }}
} }
// Create a price heap and reinit it over and over // Create a price heap and reinit it over and over
heap := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), &index) heap := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), index)
basefees := make([]*uint256.Int, b.N) basefees := make([]*uint256.Int, b.N)
blobfees := make([]*uint256.Int, b.N) blobfees := make([]*uint256.Int, b.N)
@@ -278,7 +278,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) {
}} }}
} }
// Create a price heap and overflow it over and over // Create a price heap and overflow it over and over
evict := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), &index) evict := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), index)
var ( var (
addrs = make([]common.Address, b.N) addrs = make([]common.Address, b.N)
metas = make([]*blobTxMeta, b.N) metas = make([]*blobTxMeta, b.N)

View File

@@ -19,7 +19,9 @@ package core
import ( import (
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
) )
@@ -33,7 +35,10 @@ type Validator interface {
// ValidateState validates the given statedb and optionally the receipts and // ValidateState validates the given statedb and optionally the receipts and
// gas used. // gas used.
ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error
// ValidateWitness cross validates a block execution with stateless remote clients.
ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error
} }
// Prefetcher is an interface for pre-caching transaction signatures and state. // Prefetcher is an interface for pre-caching transaction signatures and state.

View File

@@ -459,11 +459,11 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
// homestead rules. // homestead rules.
type HomesteadSigner struct{ FrontierSigner } type HomesteadSigner struct{ FrontierSigner }
func (s HomesteadSigner) ChainID() *big.Int { func (hs HomesteadSigner) ChainID() *big.Int {
return nil return nil
} }
func (s HomesteadSigner) Equal(s2 Signer) bool { func (hs HomesteadSigner) Equal(s2 Signer) bool {
_, ok := s2.(HomesteadSigner) _, ok := s2.(HomesteadSigner)
return ok return ok
} }
@@ -486,11 +486,11 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
// frontier rules. // frontier rules.
type FrontierSigner struct{} type FrontierSigner struct{}
func (s FrontierSigner) ChainID() *big.Int { func (fs FrontierSigner) ChainID() *big.Int {
return nil return nil
} }
func (s FrontierSigner) Equal(s2 Signer) bool { func (fs FrontierSigner) Equal(s2 Signer) bool {
_, ok := s2.(FrontierSigner) _, ok := s2.(FrontierSigner)
return ok return ok
} }
@@ -572,6 +572,6 @@ func deriveChainId(v *big.Int) *big.Int {
} }
return new(big.Int).SetUint64((v - 35) / 2) return new(big.Int).SetUint64((v - 35) / 2)
} }
v = new(big.Int).Sub(v, big.NewInt(35)) v.Sub(v, big.NewInt(35))
return v.Div(v, big.NewInt(2)) return v.Rsh(v, 1)
} }

View File

@@ -296,10 +296,7 @@ type bigModExp struct {
var ( var (
big1 = big.NewInt(1) big1 = big.NewInt(1)
big3 = big.NewInt(3) big3 = big.NewInt(3)
big4 = big.NewInt(4)
big7 = big.NewInt(7) big7 = big.NewInt(7)
big8 = big.NewInt(8)
big16 = big.NewInt(16)
big20 = big.NewInt(20) big20 = big.NewInt(20)
big32 = big.NewInt(32) big32 = big.NewInt(32)
big64 = big.NewInt(64) big64 = big.NewInt(64)
@@ -325,13 +322,13 @@ func modexpMultComplexity(x *big.Int) *big.Int {
case x.Cmp(big1024) <= 0: case x.Cmp(big1024) <= 0:
// (x ** 2 // 4 ) + ( 96 * x - 3072) // (x ** 2 // 4 ) + ( 96 * x - 3072)
x = new(big.Int).Add( x = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(x, x), big4), new(big.Int).Rsh(new(big.Int).Mul(x, x), 2),
new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072), new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
) )
default: default:
// (x ** 2 // 16) + (480 * x - 199680) // (x ** 2 // 16) + (480 * x - 199680)
x = new(big.Int).Add( x = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(x, x), big16), new(big.Int).Rsh(new(big.Int).Mul(x, x), 4),
new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680), new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
) )
} }
@@ -369,7 +366,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
adjExpLen := new(big.Int) adjExpLen := new(big.Int)
if expLen.Cmp(big32) > 0 { if expLen.Cmp(big32) > 0 {
adjExpLen.Sub(expLen, big32) adjExpLen.Sub(expLen, big32)
adjExpLen.Mul(big8, adjExpLen) adjExpLen.Lsh(adjExpLen, 3)
} }
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
// Calculate the gas cost of the operation // Calculate the gas cost of the operation
@@ -383,8 +380,8 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
// ceiling(x/8)^2 // ceiling(x/8)^2
// //
//where is x is max(length_of_MODULUS, length_of_BASE) //where is x is max(length_of_MODULUS, length_of_BASE)
gas = gas.Add(gas, big7) gas.Add(gas, big7)
gas = gas.Div(gas, big8) gas.Rsh(gas, 3)
gas.Mul(gas, gas) gas.Mul(gas, gas)
gas.Mul(gas, math.BigMax(adjExpLen, big1)) gas.Mul(gas, math.BigMax(adjExpLen, big1))

View File

@@ -231,6 +231,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
code := evm.StateDB.GetCode(addr) code := evm.StateDB.GetCode(addr)
if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(code)
}
if len(code) == 0 { if len(code) == 0 {
ret, err = nil, nil // gas is unchanged ret, err = nil, nil // gas is unchanged
} else { } else {
@@ -298,6 +301,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas) contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy))
}
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
@@ -345,6 +351,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
addrCopy := addr addrCopy := addr
// Initialise a new contract and make initialise the delegate values // Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy))
}
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
@@ -400,6 +409,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas) contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy))
}
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally

View File

@@ -340,6 +340,10 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek() slot := scope.Stack.peek()
address := slot.Bytes20()
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
witness.AddCode(interpreter.evm.StateDB.GetCode(address))
}
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
return nil, nil return nil, nil
} }
@@ -378,7 +382,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
uint64CodeOffset = math.MaxUint64 uint64CodeOffset = math.MaxUint64
} }
addr := common.Address(a.Bytes20()) addr := common.Address(a.Bytes20())
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) code := interpreter.evm.StateDB.GetCode(addr)
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
witness.AddCode(code)
}
codeCopy := getData(code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
return nil, nil return nil, nil
@@ -443,7 +451,11 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
lower = upper - 256 lower = upper - 256
} }
if num64 >= lower && num64 < upper { if num64 >= lower && num64 < upper {
num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) res := interpreter.evm.Context.GetHash(num64)
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
witness.AddBlockHash(num64)
}
num.SetBytes(res[:])
} else { } else {
num.Clear() num.Clear()
} }

View File

@@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"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/params"
@@ -87,6 +88,8 @@ type StateDB interface {
AddLog(*types.Log) AddLog(*types.Log)
AddPreimage(common.Hash, []byte) AddPreimage(common.Hash, []byte)
Witness() *stateless.Witness
} }
// CallContext provides a basic interface for the EVM calling conventions. The EVM // CallContext provides a basic interface for the EVM calling conventions. The EVM

View File

@@ -33,6 +33,7 @@ type Config struct {
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
ExtraEips []int // Additional EIPS that are to be enabled ExtraEips []int // Additional EIPS that are to be enabled
EnableWitnessCollection bool // true if witness collection is enabled
} }
// ScopeContext contains the things that are per-call, such as stack and memory, // ScopeContext contains the things that are per-call, such as stack and memory,

View File

@@ -184,6 +184,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
var ( var (
vmConfig = vm.Config{ vmConfig = vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording, EnablePreimageRecording: config.EnablePreimageRecording,
EnableWitnessCollection: config.EnableWitnessCollection,
} }
cacheConfig = &core.CacheConfig{ cacheConfig = &core.CacheConfig{
TrieCleanLimit: config.TrieCleanCache, TrieCleanLimit: config.TrieCleanCache,

View File

@@ -141,6 +141,9 @@ type Config struct {
// Enables tracking of SHA3 preimages in the VM // Enables tracking of SHA3 preimages in the VM
EnablePreimageRecording bool EnablePreimageRecording bool
// Enables prefetching trie nodes for read operations too
EnableWitnessCollection bool `toml:"-"`
// Enables VM tracing // Enables VM tracing
VMTrace string VMTrace string
VMTraceJsonConfig string VMTraceJsonConfig string

View File

@@ -50,6 +50,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
BlobPool blobpool.Config BlobPool blobpool.Config
GPO gasprice.Config GPO gasprice.Config
EnablePreimageRecording bool EnablePreimageRecording bool
EnableWitnessCollection bool `toml:"-"`
VMTrace string VMTrace string
VMTraceJsonConfig string VMTraceJsonConfig string
DocRoot string `toml:"-"` DocRoot string `toml:"-"`
@@ -93,6 +94,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.BlobPool = c.BlobPool enc.BlobPool = c.BlobPool
enc.GPO = c.GPO enc.GPO = c.GPO
enc.EnablePreimageRecording = c.EnablePreimageRecording enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.EnableWitnessCollection = c.EnableWitnessCollection
enc.VMTrace = c.VMTrace enc.VMTrace = c.VMTrace
enc.VMTraceJsonConfig = c.VMTraceJsonConfig enc.VMTraceJsonConfig = c.VMTraceJsonConfig
enc.DocRoot = c.DocRoot enc.DocRoot = c.DocRoot
@@ -140,6 +142,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
BlobPool *blobpool.Config BlobPool *blobpool.Config
GPO *gasprice.Config GPO *gasprice.Config
EnablePreimageRecording *bool EnablePreimageRecording *bool
EnableWitnessCollection *bool `toml:"-"`
VMTrace *string VMTrace *string
VMTraceJsonConfig *string VMTraceJsonConfig *string
DocRoot *string `toml:"-"` DocRoot *string `toml:"-"`
@@ -252,6 +255,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.EnablePreimageRecording != nil { if dec.EnablePreimageRecording != nil {
c.EnablePreimageRecording = *dec.EnablePreimageRecording c.EnablePreimageRecording = *dec.EnablePreimageRecording
} }
if dec.EnableWitnessCollection != nil {
c.EnableWitnessCollection = *dec.EnableWitnessCollection
}
if dec.VMTrace != nil { if dec.VMTrace != nil {
c.VMTrace = *dec.VMTrace c.VMTrace = *dec.VMTrace
} }

View File

@@ -332,11 +332,7 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac
return nil, nil return nil, nil
} }
} }
var proofs [][]byte return accounts, proof.List()
for _, blob := range proof.List() {
proofs = append(proofs, blob)
}
return accounts, proofs
} }
func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesPacket) ([][]*StorageData, [][]byte) { func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesPacket) ([][]*StorageData, [][]byte) {
@@ -438,9 +434,7 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
return nil, nil return nil, nil
} }
} }
for _, blob := range proof.List() { proofs = append(proofs, proof.List()...)
proofs = append(proofs, blob)
}
// Proof terminates the reply as proofs are only added if a node // Proof terminates the reply as proofs are only added if a node
// refuses to serve more data (exception when a contract fetch is // refuses to serve more data (exception when a contract fetch is
// finishing, but that's that). // finishing, but that's that).

View File

@@ -286,10 +286,7 @@ func createAccountRequestResponse(t *testPeer, root common.Hash, origin common.H
t.logger.Error("Could not prove last item", "error", err) t.logger.Error("Could not prove last item", "error", err)
} }
} }
for _, blob := range proof.List() { return keys, vals, proof.List()
proofs = append(proofs, blob)
}
return keys, vals, proofs
} }
// defaultStorageRequestHandler is a well-behaving storage request handler // defaultStorageRequestHandler is a well-behaving storage request handler
@@ -371,9 +368,7 @@ func createStorageRequestResponse(t *testPeer, root common.Hash, accounts []comm
t.logger.Error("Could not prove last item", "error", err) t.logger.Error("Could not prove last item", "error", err)
} }
} }
for _, blob := range proof.List() { proofs = append(proofs, proof.List()...)
proofs = append(proofs, blob)
}
break break
} }
} }
@@ -430,9 +425,7 @@ func createStorageRequestResponseAlwaysProve(t *testPeer, root common.Hash, acco
t.logger.Error("Could not prove last item", "error", err) t.logger.Error("Could not prove last item", "error", err)
} }
} }
for _, blob := range proof.List() { proofs = append(proofs, proof.List()...)
proofs = append(proofs, blob)
}
break break
} }
} }
@@ -586,9 +579,8 @@ func testSyncBloatedProof(t *testing.T, scheme string) {
source.accountRequestHandler = func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { source.accountRequestHandler = func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error {
var ( var (
proofs [][]byte keys []common.Hash
keys []common.Hash vals [][]byte
vals [][]byte
) )
// The values // The values
for _, entry := range t.accountValues { for _, entry := range t.accountValues {
@@ -618,10 +610,7 @@ func testSyncBloatedProof(t *testing.T, scheme string) {
keys = append(keys[:1], keys[2:]...) keys = append(keys[:1], keys[2:]...)
vals = append(vals[:1], vals[2:]...) vals = append(vals[:1], vals[2:]...)
} }
for _, blob := range proof.List() { if err := t.remote.OnAccounts(t, requestId, keys, vals, proof.List()); err != nil {
proofs = append(proofs, blob)
}
if err := t.remote.OnAccounts(t, requestId, keys, vals, proofs); err != nil {
t.logger.Info("remote error on delivery (as expected)", "error", err) t.logger.Info("remote error on delivery (as expected)", "error", err)
t.term() t.term()
// This is actually correct, signal to exit the test successfully // This is actually correct, signal to exit the test successfully
@@ -1525,7 +1514,7 @@ func makeAccountTrieNoStorage(n int, scheme string) (string, *trie.Trie, []*kv)
// Commit the state changes into db and re-create the trie // Commit the state changes into db and re-create the trie
// for accessing later. // for accessing later.
root, nodes, _ := accTrie.Commit(false) root, nodes := accTrie.Commit(false)
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
accTrie, _ = trie.New(trie.StateTrieID(root), db) accTrie, _ = trie.New(trie.StateTrieID(root), db)
@@ -1587,7 +1576,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) {
// Commit the state changes into db and re-create the trie // Commit the state changes into db and re-create the trie
// for accessing later. // for accessing later.
root, nodes, _ := accTrie.Commit(false) root, nodes := accTrie.Commit(false)
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
accTrie, _ = trie.New(trie.StateTrieID(root), db) accTrie, _ = trie.New(trie.StateTrieID(root), db)
@@ -1633,7 +1622,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(scheme string, accounts, slots
slices.SortFunc(entries, (*kv).cmp) slices.SortFunc(entries, (*kv).cmp)
// Commit account trie // Commit account trie
root, set, _ := accTrie.Commit(true) root, set := accTrie.Commit(true)
nodes.Merge(set) nodes.Merge(set)
// Commit gathered dirty nodes into database // Commit gathered dirty nodes into database
@@ -1700,7 +1689,7 @@ func makeAccountTrieWithStorage(scheme string, accounts, slots int, code, bounda
slices.SortFunc(entries, (*kv).cmp) slices.SortFunc(entries, (*kv).cmp)
// Commit account trie // Commit account trie
root, set, _ := accTrie.Commit(true) root, set := accTrie.Commit(true)
nodes.Merge(set) nodes.Merge(set)
// Commit gathered dirty nodes into database // Commit gathered dirty nodes into database
@@ -1742,7 +1731,7 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *triedb.Datab
entries = append(entries, elem) entries = append(entries, elem)
} }
slices.SortFunc(entries, (*kv).cmp) slices.SortFunc(entries, (*kv).cmp)
root, nodes, _ := trie.Commit(false) root, nodes := trie.Commit(false)
return root, nodes, entries return root, nodes, entries
} }
@@ -1793,7 +1782,7 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *triedb.Database) (com
entries = append(entries, elem) entries = append(entries, elem)
} }
slices.SortFunc(entries, (*kv).cmp) slices.SortFunc(entries, (*kv).cmp)
root, nodes, _ := trie.Commit(false) root, nodes := trie.Commit(false)
return root, nodes, entries return root, nodes, entries
} }
@@ -1825,7 +1814,7 @@ func makeUnevenStorageTrie(owner common.Hash, slots int, db *triedb.Database) (c
} }
} }
slices.SortFunc(entries, (*kv).cmp) slices.SortFunc(entries, (*kv).cmp)
root, nodes, _ := tr.Commit(false) root, nodes := tr.Commit(false)
return root, nodes, entries return root, nodes, entries
} }

View File

@@ -204,12 +204,10 @@ func (s *supply) internalTxsHandler(call *supplyTxCallstack) {
s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn) s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn)
} }
if len(call.calls) > 0 { // Recursively handle internal calls
// Recursively handle internal calls for _, call := range call.calls {
for _, call := range call.calls { callCopy := call
callCopy := call s.internalTxsHandler(&callCopy)
s.internalTxsHandler(&callCopy)
}
} }
} }

View File

@@ -274,16 +274,14 @@ func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx
} }
output = append(output, *frame) output = append(output, *frame)
if len(input.Calls) > 0 { for i, childCall := range input.Calls {
for i, childCall := range input.Calls { childAddr := childTraceAddress(traceAddress, i)
childAddr := childTraceAddress(traceAddress, i) childCallCopy := childCall
childCallCopy := childCall flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx)
flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) if err != nil {
if err != nil { return nil, err
return nil, err
}
output = append(output, flat...)
} }
output = append(output, flat...)
} }
return output, nil return output, nil

View File

@@ -39,8 +39,8 @@ type KeyValueWriter interface {
// KeyValueStater wraps the Stat method of a backing data store. // KeyValueStater wraps the Stat method of a backing data store.
type KeyValueStater interface { type KeyValueStater interface {
// Stat returns a particular internal stat of the database. // Stat returns the statistic data of the database.
Stat(property string) (string, error) Stat() (string, error)
} }
// Compacter wraps the Compact method of a backing data store. // Compacter wraps the Compact method of a backing data store.

View File

@@ -22,7 +22,6 @@ package leveldb
import ( import (
"fmt" "fmt"
"strings"
"sync" "sync"
"time" "time"
@@ -244,14 +243,53 @@ func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
return &snapshot{db: snap}, nil return &snapshot{db: snap}, nil
} }
// Stat returns a particular internal stat of the database. // Stat returns the statistic data of the database.
func (db *Database) Stat(property string) (string, error) { func (db *Database) Stat() (string, error) {
if property == "" { var stats leveldb.DBStats
property = "leveldb.stats" if err := db.db.Stats(&stats); err != nil {
} else if !strings.HasPrefix(property, "leveldb.") { return "", err
property = "leveldb." + property
} }
return db.db.GetProperty(property) var (
message string
totalRead int64
totalWrite int64
totalSize int64
totalTables int
totalDuration time.Duration
)
if len(stats.LevelSizes) > 0 {
message += " Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" +
"-------+------------+---------------+---------------+---------------+---------------\n"
for level, size := range stats.LevelSizes {
read := stats.LevelRead[level]
write := stats.LevelWrite[level]
duration := stats.LevelDurations[level]
tables := stats.LevelTablesCounts[level]
if tables == 0 && duration == 0 {
continue
}
totalTables += tables
totalSize += size
totalRead += read
totalWrite += write
totalDuration += duration
message += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n",
level, tables, float64(size)/1048576.0, duration.Seconds(),
float64(read)/1048576.0, float64(write)/1048576.0)
}
message += "-------+------------+---------------+---------------+---------------+---------------\n"
message += fmt.Sprintf(" Total | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n",
totalTables, float64(totalSize)/1048576.0, totalDuration.Seconds(),
float64(totalRead)/1048576.0, float64(totalWrite)/1048576.0)
message += "-------+------------+---------------+---------------+---------------+---------------\n\n"
}
message += fmt.Sprintf("Read(MB):%.5f Write(MB):%.5f\n", float64(stats.IORead)/1048576.0, float64(stats.IOWrite)/1048576.0)
message += fmt.Sprintf("BlockCache(MB):%.5f FileCache:%d\n", float64(stats.BlockCacheSize)/1048576.0, stats.OpenedTablesCount)
message += fmt.Sprintf("MemoryCompaction:%d Level0Compaction:%d NonLevel0Compaction:%d SeekCompaction:%d\n", stats.MemComp, stats.Level0Comp, stats.NonLevel0Comp, stats.SeekComp)
message += fmt.Sprintf("WriteDelayCount:%d WriteDelayDuration:%s Paused:%t\n", stats.WriteDelayCount, common.PrettyDuration(stats.WriteDelayDuration), stats.WritePaused)
message += fmt.Sprintf("Snapshots:%d Iterators:%d\n", stats.AliveSnapshots, stats.AliveIterators)
return message, nil
} }
// Compact flattens the underlying data store for the given key range. In essence, // Compact flattens the underlying data store for the given key range. In essence,

View File

@@ -182,9 +182,9 @@ func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
return newSnapshot(db), nil return newSnapshot(db), nil
} }
// Stat returns a particular internal stat of the database. // Stat returns the statistic data of the database.
func (db *Database) Stat(property string) (string, error) { func (db *Database) Stat() (string, error) {
return "", errors.New("unknown property") return "", nil
} }
// Compact is not supported on a memory database, but there's no need either as // Compact is not supported on a memory database, but there's no need either as

View File

@@ -416,10 +416,8 @@ func upperBound(prefix []byte) (limit []byte) {
} }
// Stat returns the internal metrics of Pebble in a text format. It's a developer // Stat returns the internal metrics of Pebble in a text format. It's a developer
// method to read everything there is to read independent of Pebble version. // method to read everything there is to read, independent of Pebble version.
// func (d *Database) Stat() (string, error) {
// The property is unused in Pebble as there's only one thing to retrieve.
func (d *Database) Stat(property string) (string, error) {
return d.db.Metrics().String(), nil return d.db.Metrics().String(), nil
} }

View File

@@ -126,8 +126,8 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
panic("not supported") panic("not supported")
} }
func (db *Database) Stat(property string) (string, error) { func (db *Database) Stat() (string, error) {
panic("not supported") return "", nil
} }
func (db *Database) AncientDatadir() (string, error) { func (db *Database) AncientDatadir() (string, error) {

13
go.mod
View File

@@ -13,7 +13,7 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.2.0 github.com/btcsuite/btcd/btcec/v2 v2.2.0
github.com/cespare/cp v0.1.0 github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.79.0 github.com/cloudflare/cloudflare-go v0.79.0
github.com/cockroachdb/pebble v1.1.0 github.com/cockroachdb/pebble v1.1.1
github.com/consensys/gnark-crypto v0.12.1 github.com/consensys/gnark-crypto v0.12.1
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c
github.com/crate-crypto/go-kzg-4844 v1.0.0 github.com/crate-crypto/go-kzg-4844 v1.0.0
@@ -61,13 +61,14 @@ require (
github.com/rs/cors v1.7.0 github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.2.0 github.com/status-im/keycard-go v0.2.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
github.com/supranational/blst v0.3.11 github.com/supranational/blst v0.3.11
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tyler-smith/go-bip39 v1.1.0 github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
go.uber.org/automaxprocs v1.5.2 go.uber.org/automaxprocs v1.5.2
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/sync v0.7.0 golang.org/x/sync v0.7.0
golang.org/x/sys v0.20.0 golang.org/x/sys v0.20.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
@@ -94,7 +95,8 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
@@ -104,7 +106,7 @@ require (
github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
github.com/getsentry/sentry-go v0.18.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
@@ -115,7 +117,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
@@ -139,7 +141,6 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/net v0.24.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect

22
go.sum
View File

@@ -116,12 +116,14 @@ github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw=
github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
@@ -187,8 +189,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@@ -347,8 +349,8 @@ github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -494,8 +496,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=

View File

@@ -272,17 +272,22 @@ func extractFile(arpath string, armode os.FileMode, data io.Reader, dest string)
return fmt.Errorf("path %q escapes archive destination", target) return fmt.Errorf("path %q escapes archive destination", target)
} }
// Ensure the destination directory exists. // Remove the preivously-extracted file if it exists
if err := os.RemoveAll(target); err != nil {
return err
}
// Recreate the destination directory
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err return err
} }
// Copy file data. // Copy file data.
file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, armode) file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, armode)
if err != nil { if err != nil {
return err return err
} }
if _, err := io.Copy(file, data); err != nil { if _, err = io.Copy(file, data); err != nil {
file.Close() file.Close()
os.Remove(target) os.Remove(target)
return err return err

View File

@@ -231,9 +231,9 @@ func Setup(ctx *cli.Context) error {
case ctx.Bool(logjsonFlag.Name): case ctx.Bool(logjsonFlag.Name):
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set // Retain backwards compatibility with `--log.json` flag if `--log.format` not set
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead") defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
handler = log.JSONHandlerWithLevel(output, log.LevelInfo) handler = log.JSONHandler(output)
case logFmtFlag == "json": case logFmtFlag == "json":
handler = log.JSONHandlerWithLevel(output, log.LevelInfo) handler = log.JSONHandler(output)
case logFmtFlag == "logfmt": case logFmtFlag == "logfmt":
handler = log.LogfmtHandler(output) handler = log.LogfmtHandler(output)
case logFmtFlag == "", logFmtFlag == "terminal": case logFmtFlag == "", logFmtFlag == "terminal":

File diff suppressed because it is too large Load Diff

View File

@@ -263,7 +263,6 @@ web3._extend({
new web3._extend.Method({ new web3._extend.Method({
name: 'chaindbProperty', name: 'chaindbProperty',
call: 'debug_chaindbProperty', call: 'debug_chaindbProperty',
params: 1,
outputFormatter: console.log outputFormatter: console.log
}), }),
new web3._extend.Method({ new web3._extend.Method({

View File

@@ -101,10 +101,10 @@ func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
} }
// ResetFieldPadding zeroes the field-padding for all attribute pairs. // ResetFieldPadding zeroes the field-padding for all attribute pairs.
func (t *TerminalHandler) ResetFieldPadding() { func (h *TerminalHandler) ResetFieldPadding() {
t.mu.Lock() h.mu.Lock()
t.fieldPadding = make(map[string]int) h.fieldPadding = make(map[string]int)
t.mu.Unlock() h.mu.Unlock()
} }
type leveler struct{ minLevel slog.Level } type leveler struct{ minLevel slog.Level }

View File

@@ -139,11 +139,15 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
return nil return nil
} }
// Enabled implements slog.Handler, reporting whether the handler handles records
// at the given level.
func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
// fast-track skipping logging if override not enabled and the provided verbosity is above configured // fast-track skipping logging if override not enabled and the provided verbosity is above configured
return h.override.Load() || slog.Level(h.level.Load()) <= lvl return h.override.Load() || slog.Level(h.level.Load()) <= lvl
} }
// WithAttrs implements slog.Handler, returning a new Handler whose attributes
// consist of both the receiver's attributes and the arguments.
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
h.lock.RLock() h.lock.RLock()
siteCache := maps.Clone(h.siteCache) siteCache := maps.Clone(h.siteCache)
@@ -164,12 +168,16 @@ func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &res return &res
} }
// WithGroup implements slog.Handler, returning a new Handler with the given
// group appended to the receiver's existing groups.
//
// Note, this function is not implemented.
func (h *GlogHandler) WithGroup(name string) slog.Handler { func (h *GlogHandler) WithGroup(name string) slog.Handler {
panic("not implemented") panic("not implemented")
} }
// Log implements Handler.Log, filtering a log record through the global, local // Handle implements slog.Handler, filtering a log record through the global,
// and backtrace filters, finally emitting it if either allow it through. // local and backtrace filters, finally emitting it if either allow it through.
func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
// If the global log level allows, fast track logging // If the global log level allows, fast track logging
if slog.Level(h.level.Load()) <= r.Level { if slog.Level(h.level.Load()) <= r.Level {

View File

@@ -35,7 +35,7 @@ const (
LvlDebug = LevelDebug LvlDebug = LevelDebug
) )
// convert from old Geth verbosity level constants // FromLegacyLevel converts from old Geth verbosity level constants
// to levels defined by slog // to levels defined by slog
func FromLegacyLevel(lvl int) slog.Level { func FromLegacyLevel(lvl int) slog.Level {
switch lvl { switch lvl {
@@ -107,7 +107,7 @@ type Logger interface {
// With returns a new Logger that has this logger's attributes plus the given attributes // With returns a new Logger that has this logger's attributes plus the given attributes
With(ctx ...interface{}) Logger With(ctx ...interface{}) Logger
// With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. // New returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'.
New(ctx ...interface{}) Logger New(ctx ...interface{}) Logger
// Log logs a message at the specified level with context key/value pairs // Log logs a message at the specified level with context key/value pairs
@@ -156,7 +156,7 @@ func (l *logger) Handler() slog.Handler {
return l.inner.Handler() return l.inner.Handler()
} }
// Write logs a message at the specified level: // Write logs a message at the specified level.
func (l *logger) Write(level slog.Level, msg string, attrs ...any) { func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
if !l.inner.Enabled(context.Background(), level) { if !l.inner.Enabled(context.Background(), level) {
return return

View File

@@ -452,7 +452,9 @@ func (tab *Table) loadSeedNodes() {
addr, _ := seed.UDPEndpoint() addr, _ := seed.UDPEndpoint()
tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age) tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age)
} }
tab.mutex.Lock()
tab.handleAddNode(addNodeOp{node: seed, isInbound: false}) tab.handleAddNode(addNodeOp{node: seed, isInbound: false})
tab.mutex.Unlock()
} }
} }

View File

@@ -135,7 +135,7 @@ func waitForRevalidationPing(t *testing.T, transport *pingRecorder, tab *Table,
simclock.Run(tab.cfg.PingInterval * slowRevalidationFactor) simclock.Run(tab.cfg.PingInterval * slowRevalidationFactor)
p := transport.waitPing(2 * time.Second) p := transport.waitPing(2 * time.Second)
if p == nil { if p == nil {
t.Fatal("Table did not send revalidation ping") continue
} }
if id == (enode.ID{}) || p.ID() == id { if id == (enode.ID{}) || p.ID() == id {
return p return p

Some files were not shown because too many files have changed in this diff Show More