",
Description: `The run command runs arbitrary EVM code.`,
Flags: flags.Merge(vmFlags, traceFlags),
From 525db7b2c5fde2e54393d0c3f9b68eed154c6754 Mon Sep 17 00:00:00 2001
From: levisyin <150114626+levisyin@users.noreply.github.com>
Date: Tue, 21 Nov 2023 16:16:57 +0800
Subject: [PATCH 021/103] accounts/abi: context info on unpack-errors (#28529)
adds contextual information to errors returned by unpack
---
accounts/abi/error.go | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
index 218a22f1e452..8e50112ec5df 100644
--- a/accounts/abi/error.go
+++ b/accounts/abi/error.go
@@ -18,7 +18,6 @@ package abi
import (
"bytes"
- "errors"
"fmt"
"strings"
@@ -84,10 +83,10 @@ func (e Error) String() string {
func (e *Error) Unpack(data []byte) (interface{}, error) {
if len(data) < 4 {
- return "", errors.New("invalid data for unpacking")
+ return "", fmt.Errorf("insufficient data for unpacking: have %d, want at least 4", len(data))
}
if !bytes.Equal(data[:4], e.ID[:4]) {
- return "", errors.New("invalid data for unpacking")
+ return "", fmt.Errorf("invalid identifier, have %#x want %#x", data[:4], e.ID[:4])
}
return e.Inputs.Unpack(data[4:])
}
From 146e8d999c705612f4d043a40f75fcb5195554c1 Mon Sep 17 00:00:00 2001
From: Marius van der Wijden
Date: Tue, 21 Nov 2023 14:19:28 +0300
Subject: [PATCH 022/103] core, trie, rpc: speed up tests (#28461)
* rpc: make subscription test faster
reduces time for TestClientSubscriptionChannelClose
from 25 sec to < 1 sec.
* trie: cache trie nodes for faster sanity check
This reduces the time spent on TestIncompleteSyncHash
from ~25s to ~16s.
* core/forkid: speed up validation test
This takes the validation test from > 5s to sub 1 sec
* core/state: improve snapshot test run
brings the time for TestSnapshotRandom from 13s down to 6s
* accounts/keystore: improve keyfile test
This removes some unnecessary waits and reduces the
runtime of TestUpdatedKeyfileContents from 5 to 3 seconds
* trie: remove resolver
* trie: only check ~5% of all trie nodes
---
accounts/keystore/account_cache_test.go | 8 ++++----
core/forkid/forkid_test.go | 3 ++-
core/state/statedb_test.go | 8 +++-----
rpc/client_test.go | 2 +-
trie/sync_test.go | 5 +++++
5 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
index 3847e9daf6ae..371d274441e7 100644
--- a/accounts/keystore/account_cache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -68,7 +68,7 @@ func waitWatcherStart(ks *KeyStore) bool {
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
var list []accounts.Account
- for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(200 * time.Millisecond) {
+ for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(100 * time.Millisecond) {
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
// ks should have also received change notifications
@@ -350,7 +350,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
return
}
// needed so that modTime of `file` is different to its current value after forceCopyFile
- time.Sleep(time.Second)
+ os.Chtimes(file, time.Now().Add(-time.Second), time.Now().Add(-time.Second))
// Now replace file contents
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
@@ -366,7 +366,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
}
// needed so that modTime of `file` is different to its current value after forceCopyFile
- time.Sleep(time.Second)
+ os.Chtimes(file, time.Now().Add(-time.Second), time.Now().Add(-time.Second))
// Now replace file contents again
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
@@ -382,7 +382,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
}
// needed so that modTime of `file` is different to its current value after os.WriteFile
- time.Sleep(time.Second)
+ os.Chtimes(file, time.Now().Add(-time.Second), time.Now().Add(-time.Second))
// Now replace file contents with crap
if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go
index db634bc14b90..e311c0b43f89 100644
--- a/core/forkid/forkid_test.go
+++ b/core/forkid/forkid_test.go
@@ -366,8 +366,9 @@ func TestValidation(t *testing.T) {
// TODO(karalabe): Enable this when Cancun is specced
//{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale},
}
+ genesis := core.DefaultGenesisBlock().ToBlock()
for i, tt := range tests {
- filter := newFilter(tt.config, core.DefaultGenesisBlock().ToBlock(), func() (uint64, uint64) { return tt.head, tt.time })
+ filter := newFilter(tt.config, genesis, func() (uint64, uint64) { return tt.head, tt.time })
if err := filter(tt.id); err != tt.err {
t.Errorf("test %d: validation error mismatch: have %v, want %v", i, err, tt.err)
}
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index ad829a0c8f09..df1cd5547d3d 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -426,10 +426,12 @@ func (test *snapshotTest) run() bool {
state, _ = New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
snapshotRevs = make([]int, len(test.snapshots))
sindex = 0
+ checkstates = make([]*StateDB, len(test.snapshots))
)
for i, action := range test.actions {
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
snapshotRevs[sindex] = state.Snapshot()
+ checkstates[sindex] = state.Copy()
sindex++
}
action.fn(action, state)
@@ -437,12 +439,8 @@ func (test *snapshotTest) run() bool {
// Revert all snapshots in reverse order. Each revert must yield a state
// that is equivalent to fresh state with all actions up the snapshot applied.
for sindex--; sindex >= 0; sindex-- {
- checkstate, _ := New(types.EmptyRootHash, state.Database(), nil)
- for _, action := range test.actions[:test.snapshots[sindex]] {
- action.fn(action, checkstate)
- }
state.RevertToSnapshot(snapshotRevs[sindex])
- if err := test.checkEqual(state, checkstate); err != nil {
+ if err := test.checkEqual(state, checkstates[sindex]); err != nil {
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
return false
}
diff --git a/rpc/client_test.go b/rpc/client_test.go
index 7c96b2d6667b..ac02ad33cf6a 100644
--- a/rpc/client_test.go
+++ b/rpc/client_test.go
@@ -595,7 +595,7 @@ func TestClientSubscriptionChannelClose(t *testing.T) {
for i := 0; i < 100; i++ {
ch := make(chan int, 100)
- sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", maxClientSubscriptionBuffer-1, 1)
+ sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 100, 1)
if err != nil {
t.Fatal(err)
}
diff --git a/trie/sync_test.go b/trie/sync_test.go
index 7032c6d2f76c..5edfb32a3733 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -19,6 +19,7 @@ package trie
import (
"bytes"
"fmt"
+ "math/rand"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -587,6 +588,10 @@ func testIncompleteSync(t *testing.T, scheme string) {
}
// Sanity check that removing any node from the database is detected
for i, path := range addedKeys {
+ if rand.Int31n(100) > 5 {
+ // Only check 5 percent of added keys as a sanity check
+ continue
+ }
owner, inner := ResolvePath([]byte(path))
nodeHash := addedHashes[i]
value := rawdb.ReadTrieNode(diskdb, owner, inner, nodeHash, scheme)
From 6489a0dd1f98e9ce1c64c2eae93c8a88df7ae674 Mon Sep 17 00:00:00 2001
From: Maciej Kulawik <10907694+magicxyyz@users.noreply.github.com>
Date: Tue, 21 Nov 2023 14:22:22 +0000
Subject: [PATCH 023/103] ethdb/pebble: don't double-close iterator inside
pebbleIterator (#28566)
Adds 'released' flag to pebbleIterator to avoid double closing cockroachdb/pebble.Iterator as it is an invalid operation.
Fixes #28565
---
ethdb/pebble/pebble.go | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go
index 6d0ea9496255..d58329c6d60e 100644
--- a/ethdb/pebble/pebble.go
+++ b/ethdb/pebble/pebble.go
@@ -609,9 +609,12 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error {
// pebbleIterator is a wrapper of underlying iterator in storage engine.
// The purpose of this structure is to implement the missing APIs.
+//
+// The pebble iterator is not thread-safe.
type pebbleIterator struct {
- iter *pebble.Iterator
- moved bool
+ iter *pebble.Iterator
+ moved bool
+ released bool
}
// NewIterator creates a binary-alphabetical iterator over a subset
@@ -623,7 +626,7 @@ func (d *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
UpperBound: upperBound(prefix),
})
iter.First()
- return &pebbleIterator{iter: iter, moved: true}
+ return &pebbleIterator{iter: iter, moved: true, released: false}
}
// Next moves the iterator to the next key/value pair. It returns whether the
@@ -658,4 +661,9 @@ func (iter *pebbleIterator) Value() []byte {
// Release releases associated resources. Release should always succeed and can
// be called multiple times without causing error.
-func (iter *pebbleIterator) Release() { iter.iter.Close() }
+func (iter *pebbleIterator) Release() {
+ if !iter.released {
+ iter.iter.Close()
+ iter.released = true
+ }
+}
From e9f59b5d5ea44df607f826c72f42916b552ab33d Mon Sep 17 00:00:00 2001
From: ucwong
Date: Tue, 21 Nov 2023 14:28:44 +0000
Subject: [PATCH 024/103] eth/filters: reuse error msg for invalid block range
(#28479)
---
eth/filters/filter.go | 2 +-
eth/filters/filter_test.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index a5750c193482..83e3284a2b5b 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -114,7 +114,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
// special case for pending logs
if beginPending && !endPending {
- return nil, errors.New("invalid block range")
+ return nil, errInvalidBlockRange
}
// Short-cut if all we care about is pending logs
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 4e09a9038b3e..1db917c960e5 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -353,7 +353,7 @@ func TestFilters(t *testing.T) {
},
{
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
- err: "invalid block range",
+ err: errInvalidBlockRange.Error(),
},
} {
logs, err := tc.f.Logs(context.Background())
From 347fecd8817bf9ac928f00f85796fc381fd650d5 Mon Sep 17 00:00:00 2001
From: Mario Vega
Date: Wed, 22 Nov 2023 04:00:44 -0600
Subject: [PATCH 025/103] core/types: make 'v' optional for DynamicFeeTx and
BlobTx (#28564)
This fixes an issue where transactions would not be accepted when they have only
'yParity' and not 'v'.
---
core/types/transaction.go | 3 +
core/types/transaction_marshalling.go | 12 +---
core/types/transaction_test.go | 94 +++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 9 deletions(-)
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 6f83c21d8f8a..9ec0199a0383 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -37,6 +37,9 @@ var (
ErrTxTypeNotSupported = errors.New("transaction type not supported")
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
errShortTypedTx = errors.New("typed transaction too short")
+ errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
+ errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
+ errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
)
// Transaction types.
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index e5d71a85d6b1..08ce80b07c6a 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -57,18 +57,18 @@ func (tx *txJSON) yParityValue() (*big.Int, error) {
if tx.YParity != nil {
val := uint64(*tx.YParity)
if val != 0 && val != 1 {
- return nil, errors.New("'yParity' field must be 0 or 1")
+ return nil, errInvalidYParity
}
bigval := new(big.Int).SetUint64(val)
if tx.V != nil && tx.V.ToInt().Cmp(bigval) != 0 {
- return nil, errors.New("'v' and 'yParity' fields do not match")
+ return nil, errVYParityMismatch
}
return bigval, nil
}
if tx.V != nil {
return tx.V.ToInt(), nil
}
- return nil, errors.New("missing 'yParity' or 'v' field in transaction")
+ return nil, errVYParityMissing
}
// MarshalJSON marshals as JSON with a hash.
@@ -294,9 +294,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
- if dec.V == nil {
- return errors.New("missing required field 'v' in transaction")
- }
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
@@ -361,9 +358,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
- if dec.V == nil {
- return errors.New("missing required field 'v' in transaction")
- }
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 25ced0841ba1..76a010d2e502 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -451,3 +451,97 @@ func TestTransactionSizes(t *testing.T) {
}
}
}
+
+func TestYParityJSONUnmarshalling(t *testing.T) {
+ baseJson := map[string]interface{}{
+ // type is filled in by the test
+ "chainId": "0x7",
+ "nonce": "0x0",
+ "to": "0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425",
+ "gas": "0x124f8",
+ "gasPrice": "0x693d4ca8",
+ "maxPriorityFeePerGas": "0x3b9aca00",
+ "maxFeePerGas": "0x6fc23ac00",
+ "maxFeePerBlobGas": "0x3b9aca00",
+ "value": "0x0",
+ "input": "0x",
+ "accessList": []interface{}{},
+ "blobVersionedHashes": []string{
+ "0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014",
+ },
+
+ // v and yParity are filled in by the test
+ "r": "0x2a922afc784d07e98012da29f2f37cae1f73eda78aa8805d3df6ee5dbb41ec1",
+ "s": "0x4f1f75ae6bcdf4970b4f305da1a15d8c5ddb21f555444beab77c9af2baab14",
+ }
+
+ tests := []struct {
+ name string
+ v string
+ yParity string
+ wantErr error
+ }{
+ // Valid v and yParity
+ {"valid v and yParity, 0x0", "0x0", "0x0", nil},
+ {"valid v and yParity, 0x1", "0x1", "0x1", nil},
+
+ // Valid v, missing yParity
+ {"valid v, missing yParity, 0x0", "0x0", "", nil},
+ {"valid v, missing yParity, 0x1", "0x1", "", nil},
+
+ // Valid yParity, missing v
+ {"valid yParity, missing v, 0x0", "", "0x0", nil},
+ {"valid yParity, missing v, 0x1", "", "0x1", nil},
+
+ // Invalid yParity
+ {"invalid yParity, 0x2", "", "0x2", errInvalidYParity},
+
+ // Conflicting v and yParity
+ {"conflicting v and yParity", "0x1", "0x0", errVYParityMismatch},
+
+ // Missing v and yParity
+ {"missing v and yParity", "", "", errVYParityMissing},
+ }
+
+ // Run for all types that accept yParity
+ t.Parallel()
+ for _, txType := range []uint64{
+ AccessListTxType,
+ DynamicFeeTxType,
+ BlobTxType,
+ } {
+ txType := txType
+ for _, test := range tests {
+ test := test
+ t.Run(fmt.Sprintf("txType=%d: %s", txType, test.name), func(t *testing.T) {
+ // Copy the base json
+ testJson := make(map[string]interface{})
+ for k, v := range baseJson {
+ testJson[k] = v
+ }
+
+ // Set v, yParity and type
+ if test.v != "" {
+ testJson["v"] = test.v
+ }
+ if test.yParity != "" {
+ testJson["yParity"] = test.yParity
+ }
+ testJson["type"] = fmt.Sprintf("0x%x", txType)
+
+ // Marshal the JSON
+ jsonBytes, err := json.Marshal(testJson)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Unmarshal the tx
+ var tx Transaction
+ err = tx.UnmarshalJSON(jsonBytes)
+ if err != test.wantErr {
+ t.Fatalf("wrong error: got %v, want %v", err, test.wantErr)
+ }
+ })
+ }
+ }
+}
From d6cea4832ae946b770bd71529ef539f92c5ba06a Mon Sep 17 00:00:00 2001
From: Delweng
Date: Wed, 22 Nov 2023 18:24:54 +0800
Subject: [PATCH 026/103] rpc: improve performance of subscription notification
encoding (#28328)
It turns out that encoding json.RawMessage is slow because
package json basically parses the message again to ensure it is valid.
We can avoid the slowdown by encoding the entire RPC notification once,
which yields a 30% speedup.
---
rpc/json.go | 11 ++++++++
rpc/subscription.go | 27 ++++++++----------
rpc/subscription_test.go | 60 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+), 16 deletions(-)
diff --git a/rpc/json.go b/rpc/json.go
index 78f7d7a65054..5557a8076040 100644
--- a/rpc/json.go
+++ b/rpc/json.go
@@ -46,6 +46,17 @@ type subscriptionResult struct {
Result json.RawMessage `json:"result,omitempty"`
}
+type subscriptionResultEnc struct {
+ ID string `json:"subscription"`
+ Result any `json:"result"`
+}
+
+type jsonrpcSubscriptionNotification struct {
+ Version string `json:"jsonrpc"`
+ Method string `json:"method"`
+ Params subscriptionResultEnc `json:"params"`
+}
+
// A value of this type can a JSON-RPC request, notification, successful response or
// error response. Which one it is depends on the fields.
type jsonrpcMessage struct {
diff --git a/rpc/subscription.go b/rpc/subscription.go
index 3231c2ceec9b..9cb07275479e 100644
--- a/rpc/subscription.go
+++ b/rpc/subscription.go
@@ -105,7 +105,7 @@ type Notifier struct {
mu sync.Mutex
sub *Subscription
- buffer []json.RawMessage
+ buffer []any
callReturned bool
activated bool
}
@@ -129,12 +129,7 @@ func (n *Notifier) CreateSubscription() *Subscription {
// Notify sends a notification to the client with the given data as payload.
// If an error occurs the RPC connection is closed and the error is returned.
-func (n *Notifier) Notify(id ID, data interface{}) error {
- enc, err := json.Marshal(data)
- if err != nil {
- return err
- }
-
+func (n *Notifier) Notify(id ID, data any) error {
n.mu.Lock()
defer n.mu.Unlock()
@@ -144,9 +139,9 @@ func (n *Notifier) Notify(id ID, data interface{}) error {
panic("Notify with wrong ID")
}
if n.activated {
- return n.send(n.sub, enc)
+ return n.send(n.sub, data)
}
- n.buffer = append(n.buffer, enc)
+ n.buffer = append(n.buffer, data)
return nil
}
@@ -181,16 +176,16 @@ func (n *Notifier) activate() error {
return nil
}
-func (n *Notifier) send(sub *Subscription, data json.RawMessage) error {
- params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data})
- ctx := context.Background()
-
- msg := &jsonrpcMessage{
+func (n *Notifier) send(sub *Subscription, data any) error {
+ msg := jsonrpcSubscriptionNotification{
Version: vsn,
Method: n.namespace + notificationMethodSuffix,
- Params: params,
+ Params: subscriptionResultEnc{
+ ID: string(sub.ID),
+ Result: data,
+ },
}
- return n.h.conn.writeJSON(ctx, msg, false)
+ return n.h.conn.writeJSON(context.Background(), &msg, false)
}
// A Subscription is created by a notifier and tied to that notifier. The client can use
diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go
index b2704578291e..3a131c8e6bd2 100644
--- a/rpc/subscription_test.go
+++ b/rpc/subscription_test.go
@@ -17,12 +17,19 @@
package rpc
import (
+ "bytes"
+ "context"
"encoding/json"
"fmt"
+ "io"
+ "math/big"
"net"
"strings"
"testing"
"time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
)
func TestNewID(t *testing.T) {
@@ -218,3 +225,56 @@ func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionRe
return nil, nil, fmt.Errorf("unrecognized message: %v", msg)
}
}
+
+type mockConn struct {
+ enc *json.Encoder
+}
+
+// writeJSON writes a message to the connection.
+func (c *mockConn) writeJSON(ctx context.Context, msg interface{}, isError bool) error {
+ return c.enc.Encode(msg)
+}
+
+// Closed returns a channel which is closed when the connection is closed.
+func (c *mockConn) closed() <-chan interface{} { return nil }
+
+// RemoteAddr returns the peer address of the connection.
+func (c *mockConn) remoteAddr() string { return "" }
+
+// BenchmarkNotify benchmarks the performance of notifying a subscription.
+func BenchmarkNotify(b *testing.B) {
+ id := ID("test")
+ notifier := &Notifier{
+ h: &handler{conn: &mockConn{json.NewEncoder(io.Discard)}},
+ sub: &Subscription{ID: id},
+ activated: true,
+ }
+ msg := &types.Header{
+ ParentHash: common.HexToHash("0x01"),
+ Number: big.NewInt(100),
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ notifier.Notify(id, msg)
+ }
+}
+
+func TestNotify(t *testing.T) {
+ out := new(bytes.Buffer)
+ id := ID("test")
+ notifier := &Notifier{
+ h: &handler{conn: &mockConn{json.NewEncoder(out)}},
+ sub: &Subscription{ID: id},
+ activated: true,
+ }
+ msg := &types.Header{
+ ParentHash: common.HexToHash("0x01"),
+ Number: big.NewInt(100),
+ }
+ notifier.Notify(id, msg)
+ have := strings.TrimSpace(out.String())
+ want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000001","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":null,"number":"0x64","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":null,"withdrawalsRoot":null,"blobGasUsed":null,"excessBlobGas":null,"parentBeaconBlockRoot":null,"hash":"0xe5fb877dde471b45b9742bb4bb4b3d74a761e2fb7cb849a3d2b687eed90fb604"}}}`
+ if have != want {
+ t.Errorf("have:\n%v\nwant:\n%v\n", have, want)
+ }
+}
From 104dbf7821ef5366adc2b297938b24895c6924d0 Mon Sep 17 00:00:00 2001
From: jwasinger
Date: Wed, 22 Nov 2023 19:01:38 +0800
Subject: [PATCH 027/103] cmd/utils: validate pre-existing genesis in --dev
mode (#28468)
geth --dev can be used with an existing data directory and genesis block. Since
dev mode only works with PoS, we need to verify that the merge has happened.
Co-authored-by: Felix Lange
---
cmd/utils/flags.go | 15 +++++++++++++++
core/genesis.go | 2 +-
eth/catalyst/simulated_beacon.go | 4 ----
params/config.go | 6 ++----
4 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 8bbacac51d8d..234fd1916249 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1870,6 +1870,21 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
cfg.Genesis = nil // fallback to db content
+
+ //validate genesis has PoS enabled in block 0
+ genesis, err := core.ReadGenesis(chaindb)
+ if err != nil {
+ Fatalf("Could not read genesis from database: %v", err)
+ }
+ if !genesis.Config.TerminalTotalDifficultyPassed {
+ Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true in developer mode")
+ }
+ if genesis.Config.TerminalTotalDifficulty == nil {
+ Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified.")
+ }
+ if genesis.Difficulty.Cmp(genesis.Config.TerminalTotalDifficulty) != 1 {
+ Fatalf("Bad developer-mode genesis configuration: genesis block difficulty must be > terminalTotalDifficulty")
+ }
}
chaindb.Close()
}
diff --git a/core/genesis.go b/core/genesis.go
index 60c2f9a8bc38..aa748884e10e 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -589,7 +589,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
Config: &config,
GasLimit: gasLimit,
BaseFee: big.NewInt(params.InitialBaseFee),
- Difficulty: big.NewInt(0),
+ Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go
index a9a2bb4a9a7b..d8b8641e6a84 100644
--- a/eth/catalyst/simulated_beacon.go
+++ b/eth/catalyst/simulated_beacon.go
@@ -82,10 +82,6 @@ type SimulatedBeacon struct {
}
func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) {
- chainConfig := eth.APIBackend.ChainConfig()
- if !chainConfig.IsDevMode {
- return nil, errors.New("incompatible pre-existing chain configuration")
- }
block := eth.BlockChain().CurrentBlock()
current := engine.ForkchoiceStateV1{
HeadBlockHash: block.Hash(),
diff --git a/params/config.go b/params/config.go
index 88ff772a1d5c..463041bd0161 100644
--- a/params/config.go
+++ b/params/config.go
@@ -180,7 +180,6 @@ var (
ShanghaiTime: newUint64(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
- IsDevMode: true,
}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
@@ -329,9 +328,8 @@ type ChainConfig struct {
TerminalTotalDifficultyPassed bool `json:"terminalTotalDifficultyPassed,omitempty"`
// Various consensus engines
- Ethash *EthashConfig `json:"ethash,omitempty"`
- Clique *CliqueConfig `json:"clique,omitempty"`
- IsDevMode bool `json:"isDev,omitempty"`
+ Ethash *EthashConfig `json:"ethash,omitempty"`
+ Clique *CliqueConfig `json:"clique,omitempty"`
}
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
From 3cfcd252db04aa1ff44ac6a40e33ef4c18e272a9 Mon Sep 17 00:00:00 2001
From: jwasinger
Date: Wed, 22 Nov 2023 19:08:39 +0800
Subject: [PATCH 028/103] cmd/geth: add support for --dev flag in dumpgenesis
(#28463)
Co-authored-by: Felix Lange
Co-authored-by: lightclient
---
cmd/geth/chaincmd.go | 13 ++++++++++---
cmd/utils/flags.go | 2 +-
console/console_test.go | 2 +-
core/genesis.go | 9 ++++++---
eth/catalyst/simulated_beacon_test.go | 2 +-
5 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index b65827f5bc66..9f51540984d3 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -224,14 +224,21 @@ func initGenesis(ctx *cli.Context) error {
}
func dumpGenesis(ctx *cli.Context) error {
- // if there is a testnet preset enabled, dump that
+ // check if there is a testnet preset enabled
+ var genesis *core.Genesis
if utils.IsNetworkPreset(ctx) {
- genesis := utils.MakeGenesis(ctx)
+ genesis = utils.MakeGenesis(ctx)
+ } else if ctx.IsSet(utils.DeveloperFlag.Name) && !ctx.IsSet(utils.DataDirFlag.Name) {
+ genesis = core.DeveloperGenesisBlock(11_500_000, nil)
+ }
+
+ if genesis != nil {
if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil {
utils.Fatalf("could not encode genesis: %s", err)
}
return nil
}
+
// dump whatever already exists in the datadir
stack, _ := makeConfigNode(ctx)
for _, name := range []string{"chaindata", "lightchaindata"} {
@@ -256,7 +263,7 @@ func dumpGenesis(ctx *cli.Context) error {
if ctx.IsSet(utils.DataDirFlag.Name) {
utils.Fatalf("no existing datadir at %s", stack.Config().DataDir)
}
- utils.Fatalf("no network preset provided, no existing genesis in the default datadir")
+ utils.Fatalf("no network preset provided, and no genesis exists in the default datadir")
return nil
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 234fd1916249..72a56e9c28c9 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1865,7 +1865,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
log.Info("Using developer account", "address", developer.Address)
// Create a new developer genesis block or reuse existing one
- cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address)
+ cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), &developer.Address)
if ctx.IsSet(DataDirFlag.Name) {
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
diff --git a/console/console_test.go b/console/console_test.go
index ee5c36be4ac9..a13be6a99ded 100644
--- a/console/console_test.go
+++ b/console/console_test.go
@@ -94,7 +94,7 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
t.Fatalf("failed to create node: %v", err)
}
ethConf := ðconfig.Config{
- Genesis: core.DeveloperGenesisBlock(11_500_000, common.Address{}),
+ Genesis: core.DeveloperGenesisBlock(11_500_000, nil),
Miner: miner.Config{
Etherbase: common.HexToAddress(testAddress),
},
diff --git a/core/genesis.go b/core/genesis.go
index aa748884e10e..634be9a9e0b1 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -580,12 +580,12 @@ func DefaultHoleskyGenesisBlock() *Genesis {
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
-func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
+func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
// Override the default period to the user requested one
config := *params.AllDevChainProtocolChanges
// Assemble and return the genesis with the precompiles and faucet pre-funded
- return &Genesis{
+ genesis := &Genesis{
Config: &config,
GasLimit: gasLimit,
BaseFee: big.NewInt(params.InitialBaseFee),
@@ -600,9 +600,12 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
- faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
}
+ if faucet != nil {
+ genesis.Alloc[*faucet] = GenesisAccount{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
+ }
+ return genesis
}
func decodePrealloc(data string) GenesisAlloc {
diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go
index 0df195fb9da4..6fa97ad87a2a 100644
--- a/eth/catalyst/simulated_beacon_test.go
+++ b/eth/catalyst/simulated_beacon_test.go
@@ -85,7 +85,7 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
// short period (1 second) for testing purposes
var gasLimit uint64 = 10_000_000
- genesis := core.DeveloperGenesisBlock(gasLimit, testAddr)
+ genesis := core.DeveloperGenesisBlock(gasLimit, &testAddr)
node, ethService, mock := startSimulatedBeaconEthService(t, genesis)
_ = mock
defer node.Close()
From 5ff929c22fb0315ebdb6efc5a871469f70522850 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?=
Date: Wed, 22 Nov 2023 14:00:30 +0100
Subject: [PATCH 029/103] les/vflux: run tests in parallel (#28524)
---
les/vflux/client/fillset_test.go | 2 ++
les/vflux/client/queueiterator_test.go | 4 +++
les/vflux/client/requestbasket_test.go | 10 ++++++
les/vflux/client/serverpool_test.go | 46 ++++++++++++++++++++++----
les/vflux/client/timestats_test.go | 8 +++++
les/vflux/client/valuetracker_test.go | 2 ++
les/vflux/client/wrsiterator_test.go | 2 ++
les/vflux/server/balance_test.go | 20 +++++++++++
les/vflux/server/clientdb_test.go | 4 +++
les/vflux/server/clientpool_test.go | 34 +++++++++++++++++++
les/vflux/server/prioritypool_test.go | 4 +++
11 files changed, 129 insertions(+), 7 deletions(-)
diff --git a/les/vflux/client/fillset_test.go b/les/vflux/client/fillset_test.go
index 652dcf9f62be..9a5a2a98a857 100644
--- a/les/vflux/client/fillset_test.go
+++ b/les/vflux/client/fillset_test.go
@@ -65,6 +65,8 @@ func (i *testIter) waiting(timeout time.Duration) bool {
}
func TestFillSet(t *testing.T) {
+ t.Parallel()
+
ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
iter := &testIter{
waitCh: make(chan struct{}),
diff --git a/les/vflux/client/queueiterator_test.go b/les/vflux/client/queueiterator_test.go
index 400d978e19f8..c7cb6490824d 100644
--- a/les/vflux/client/queueiterator_test.go
+++ b/les/vflux/client/queueiterator_test.go
@@ -31,10 +31,14 @@ func testNode(i int) *enode.Node {
}
func TestQueueIteratorFIFO(t *testing.T) {
+ t.Parallel()
+
testQueueIterator(t, true)
}
func TestQueueIteratorLIFO(t *testing.T) {
+ t.Parallel()
+
testQueueIterator(t, false)
}
diff --git a/les/vflux/client/requestbasket_test.go b/les/vflux/client/requestbasket_test.go
index 7c5f87c618f1..320d1b4b3e29 100644
--- a/les/vflux/client/requestbasket_test.go
+++ b/les/vflux/client/requestbasket_test.go
@@ -36,6 +36,8 @@ func checkF64(t *testing.T, name string, value, exp, tol float64) {
}
func TestServerBasket(t *testing.T) {
+ t.Parallel()
+
var s serverBasket
s.init(2)
// add some requests with different request value factors
@@ -70,6 +72,8 @@ func TestServerBasket(t *testing.T) {
}
func TestConvertMapping(t *testing.T) {
+ t.Parallel()
+
b := requestBasket{items: []basketItem{{3, 3}, {1, 1}, {2, 2}}}
oldMap := []string{"req3", "req1", "req2"}
newMap := []string{"req1", "req2", "req3", "req4"}
@@ -82,6 +86,8 @@ func TestConvertMapping(t *testing.T) {
}
func TestReqValueFactor(t *testing.T) {
+ t.Parallel()
+
var ref referenceBasket
ref.basket = requestBasket{items: make([]basketItem, 4)}
for i := range ref.basket.items {
@@ -95,6 +101,8 @@ func TestReqValueFactor(t *testing.T) {
}
func TestNormalize(t *testing.T) {
+ t.Parallel()
+
for cycle := 0; cycle < 100; cycle += 1 {
// Initialize data for testing
valueRange, lower := 1000000, 1000000
@@ -119,6 +127,8 @@ func TestNormalize(t *testing.T) {
}
func TestReqValueAdjustment(t *testing.T) {
+ t.Parallel()
+
var s1, s2 serverBasket
s1.init(3)
s2.init(3)
diff --git a/les/vflux/client/serverpool_test.go b/les/vflux/client/serverpool_test.go
index f1fd987d7edb..19d4fe6630f4 100644
--- a/les/vflux/client/serverpool_test.go
+++ b/les/vflux/client/serverpool_test.go
@@ -326,9 +326,21 @@ func (s *ServerPoolTest) checkNodes(t *testing.T, nodes []int) {
}
}
-func TestServerPool(t *testing.T) { testServerPool(t, false, false) }
-func TestServerPoolWithPreNeg(t *testing.T) { testServerPool(t, true, false) }
-func TestServerPoolWithPreNegFail(t *testing.T) { testServerPool(t, true, true) }
+func TestServerPool(t *testing.T) {
+ t.Parallel()
+
+ testServerPool(t, false, false)
+}
+func TestServerPoolWithPreNeg(t *testing.T) {
+ t.Parallel()
+
+ testServerPool(t, true, false)
+}
+func TestServerPoolWithPreNegFail(t *testing.T) {
+ t.Parallel()
+
+ testServerPool(t, true, true)
+}
func testServerPool(t *testing.T, preNeg, fail bool) {
s := newServerPoolTest(preNeg, fail)
nodes := s.setNodes(100, 200, 200, true, false)
@@ -339,8 +351,16 @@ func testServerPool(t *testing.T, preNeg, fail bool) {
s.checkNodes(t, nodes)
}
-func TestServerPoolChangedNodes(t *testing.T) { testServerPoolChangedNodes(t, false) }
-func TestServerPoolChangedNodesWithPreNeg(t *testing.T) { testServerPoolChangedNodes(t, true) }
+func TestServerPoolChangedNodes(t *testing.T) {
+ t.Parallel()
+
+ testServerPoolChangedNodes(t, false)
+}
+func TestServerPoolChangedNodesWithPreNeg(t *testing.T) {
+ t.Parallel()
+
+ testServerPoolChangedNodes(t, true)
+}
func testServerPoolChangedNodes(t *testing.T, preNeg bool) {
s := newServerPoolTest(preNeg, false)
nodes := s.setNodes(100, 200, 200, true, false)
@@ -358,8 +378,14 @@ func testServerPoolChangedNodes(t *testing.T, preNeg bool) {
s.stop()
}
-func TestServerPoolRestartNoDiscovery(t *testing.T) { testServerPoolRestartNoDiscovery(t, false) }
+func TestServerPoolRestartNoDiscovery(t *testing.T) {
+ t.Parallel()
+
+ testServerPoolRestartNoDiscovery(t, false)
+}
func TestServerPoolRestartNoDiscoveryWithPreNeg(t *testing.T) {
+ t.Parallel()
+
testServerPoolRestartNoDiscovery(t, true)
}
func testServerPoolRestartNoDiscovery(t *testing.T, preNeg bool) {
@@ -377,8 +403,14 @@ func testServerPoolRestartNoDiscovery(t *testing.T, preNeg bool) {
s.checkNodes(t, nodes)
}
-func TestServerPoolTrustedNoDiscovery(t *testing.T) { testServerPoolTrustedNoDiscovery(t, false) }
+func TestServerPoolTrustedNoDiscovery(t *testing.T) {
+ t.Parallel()
+
+ testServerPoolTrustedNoDiscovery(t, false)
+}
func TestServerPoolTrustedNoDiscoveryWithPreNeg(t *testing.T) {
+ t.Parallel()
+
testServerPoolTrustedNoDiscovery(t, true)
}
func testServerPoolTrustedNoDiscovery(t *testing.T, preNeg bool) {
diff --git a/les/vflux/client/timestats_test.go b/les/vflux/client/timestats_test.go
index a28460171e60..80ea2047c620 100644
--- a/les/vflux/client/timestats_test.go
+++ b/les/vflux/client/timestats_test.go
@@ -26,6 +26,8 @@ import (
)
func TestTransition(t *testing.T) {
+ t.Parallel()
+
var epsilon = 0.01
var cases = []time.Duration{
time.Millisecond, minResponseTime,
@@ -47,6 +49,8 @@ func TestTransition(t *testing.T) {
var maxResponseWeights = TimeoutWeights(maxResponseTime)
func TestValue(t *testing.T) {
+ t.Parallel()
+
noexp := utils.ExpirationFactor{Factor: 1}
for i := 0; i < 1000; i++ {
max := minResponseTime + time.Duration(rand.Int63n(int64(maxResponseTime-minResponseTime)))
@@ -70,6 +74,8 @@ func TestValue(t *testing.T) {
}
func TestAddSubExpire(t *testing.T) {
+ t.Parallel()
+
var (
sum1, sum2 ResponseTimeStats
sum1ValueExp, sum2ValueExp float64
@@ -110,6 +116,8 @@ func TestAddSubExpire(t *testing.T) {
}
func TestTimeout(t *testing.T) {
+ t.Parallel()
+
testTimeoutRange(t, 0, time.Second)
testTimeoutRange(t, time.Second, time.Second*2)
testTimeoutRange(t, time.Second, maxResponseTime)
diff --git a/les/vflux/client/valuetracker_test.go b/les/vflux/client/valuetracker_test.go
index 87a337be8da7..332d65ee5122 100644
--- a/les/vflux/client/valuetracker_test.go
+++ b/les/vflux/client/valuetracker_test.go
@@ -38,6 +38,8 @@ const (
)
func TestValueTracker(t *testing.T) {
+ t.Parallel()
+
db := memorydb.New()
clock := &mclock.Simulated{}
requestList := make([]RequestInfo, testReqTypes)
diff --git a/les/vflux/client/wrsiterator_test.go b/les/vflux/client/wrsiterator_test.go
index 77bb5ee0cacd..f6eb2d8813e3 100644
--- a/les/vflux/client/wrsiterator_test.go
+++ b/les/vflux/client/wrsiterator_test.go
@@ -37,6 +37,8 @@ var (
const iterTestNodeCount = 6
func TestWrsIterator(t *testing.T) {
+ t.Parallel()
+
ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
w := NewWrsIterator(ns, sfTest2, sfTest3.Or(sfTest4), sfiTestWeight)
ns.Start()
diff --git a/les/vflux/server/balance_test.go b/les/vflux/server/balance_test.go
index 7c100aab509f..e1ff7bf4e9a3 100644
--- a/les/vflux/server/balance_test.go
+++ b/les/vflux/server/balance_test.go
@@ -107,6 +107,8 @@ func (b *balanceTestSetup) stop() {
}
func TestAddBalance(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
@@ -143,6 +145,8 @@ func TestAddBalance(t *testing.T) {
}
func TestSetBalance(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000)
@@ -167,6 +171,8 @@ func TestSetBalance(t *testing.T) {
}
func TestBalanceTimeCost(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000)
@@ -207,6 +213,8 @@ func TestBalanceTimeCost(t *testing.T) {
}
func TestBalanceReqCost(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000)
@@ -235,6 +243,8 @@ func TestBalanceReqCost(t *testing.T) {
}
func TestBalanceToPriority(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000)
@@ -260,6 +270,8 @@ func TestBalanceToPriority(t *testing.T) {
}
func TestEstimatedPriority(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000000000)
@@ -299,6 +311,8 @@ func TestEstimatedPriority(t *testing.T) {
}
func TestPositiveBalanceCounting(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
@@ -340,6 +354,8 @@ func TestPositiveBalanceCounting(t *testing.T) {
}
func TestCallbackChecking(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000000)
@@ -363,6 +379,8 @@ func TestCallbackChecking(t *testing.T) {
}
func TestCallback(t *testing.T) {
+ t.Parallel()
+
b := newBalanceTestSetup(nil, nil, nil)
defer b.stop()
node := b.newNode(1000)
@@ -392,6 +410,8 @@ func TestCallback(t *testing.T) {
}
func TestBalancePersistence(t *testing.T) {
+ t.Parallel()
+
posExp := &utils.Expirer{}
negExp := &utils.Expirer{}
posExp.SetRate(0, math.Log(2)/float64(time.Hour*2)) // halves every two hours
diff --git a/les/vflux/server/clientdb_test.go b/les/vflux/server/clientdb_test.go
index 353d84aead47..caa4384e19e2 100644
--- a/les/vflux/server/clientdb_test.go
+++ b/les/vflux/server/clientdb_test.go
@@ -32,6 +32,8 @@ func expval(v uint64) utils.ExpiredValue {
}
func TestNodeDB(t *testing.T) {
+ t.Parallel()
+
ndb := newNodeDB(rawdb.NewMemoryDatabase(), mclock.System{})
defer ndb.close()
@@ -85,6 +87,8 @@ func TestNodeDB(t *testing.T) {
}
func TestNodeDBExpiration(t *testing.T) {
+ t.Parallel()
+
var (
iterated int
done = make(chan struct{}, 1)
diff --git a/les/vflux/server/clientpool_test.go b/les/vflux/server/clientpool_test.go
index f75c70afcaba..7319be08242f 100644
--- a/les/vflux/server/clientpool_test.go
+++ b/les/vflux/server/clientpool_test.go
@@ -32,26 +32,38 @@ import (
const defaultConnectedBias = time.Minute * 3
func TestClientPoolL10C100Free(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 10, 100, 0, true)
}
func TestClientPoolL40C200Free(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 40, 200, 0, true)
}
func TestClientPoolL100C300Free(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 100, 300, 0, true)
}
func TestClientPoolL10C100P4(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 10, 100, 4, false)
}
func TestClientPoolL40C200P30(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 40, 200, 30, false)
}
func TestClientPoolL100C300P20(t *testing.T) {
+ t.Parallel()
+
testClientPool(t, 100, 300, 20, false)
}
@@ -244,6 +256,8 @@ func testPriorityConnect(t *testing.T, pool *ClientPool, p *poolTestPeer, cap ui
}
func TestConnectPaidClient(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -260,6 +274,8 @@ func TestConnectPaidClient(t *testing.T) {
}
func TestConnectPaidClientToSmallPool(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -278,6 +294,8 @@ func TestConnectPaidClientToSmallPool(t *testing.T) {
}
func TestConnectPaidClientToFullPool(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -304,6 +322,8 @@ func TestConnectPaidClientToFullPool(t *testing.T) {
}
func TestPaidClientKickedOut(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -337,6 +357,8 @@ func TestPaidClientKickedOut(t *testing.T) {
}
func TestConnectFreeClient(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -353,6 +375,8 @@ func TestConnectFreeClient(t *testing.T) {
}
func TestConnectFreeClientToFullPool(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -381,6 +405,8 @@ func TestConnectFreeClientToFullPool(t *testing.T) {
}
func TestFreeClientKickedOut(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -425,6 +451,8 @@ func TestFreeClientKickedOut(t *testing.T) {
}
func TestPositiveBalanceCalculation(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -448,6 +476,8 @@ func TestPositiveBalanceCalculation(t *testing.T) {
}
func TestDowngradePriorityClient(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -484,6 +514,8 @@ func TestDowngradePriorityClient(t *testing.T) {
}
func TestNegativeBalanceCalculation(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
@@ -523,6 +555,8 @@ func TestNegativeBalanceCalculation(t *testing.T) {
}
func TestInactiveClient(t *testing.T) {
+ t.Parallel()
+
var (
clock mclock.Simulated
db = rawdb.NewMemoryDatabase()
diff --git a/les/vflux/server/prioritypool_test.go b/les/vflux/server/prioritypool_test.go
index 51523121160b..60b7b83bbcb5 100644
--- a/les/vflux/server/prioritypool_test.go
+++ b/les/vflux/server/prioritypool_test.go
@@ -48,6 +48,8 @@ func (c *ppTestClient) estimatePriority(cap uint64, addBalance int64, future, bi
}
func TestPriorityPool(t *testing.T) {
+ t.Parallel()
+
clock := &mclock.Simulated{}
setup := newServerSetup()
setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{}))
@@ -160,6 +162,8 @@ func TestPriorityPool(t *testing.T) {
}
func TestCapacityCurve(t *testing.T) {
+ t.Parallel()
+
clock := &mclock.Simulated{}
setup := newServerSetup()
setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{}))
From d468c333a7ffdf939900b6678aa3a3d6fed879b1 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 22 Nov 2023 14:48:25 +0100
Subject: [PATCH 030/103] cmd/{geth,utils}: add cmd to export preimages in snap
enumeration order (#28256)
Adds a subcommand: `geth snapshot export-preimages`, to export preimages of every hash found during a snapshot enumeration: that is, it exports _only the active state_, and not _all_ preimages that have been used but are no longer part of the state.
This tool is needed for the verkle transition, in order to distribute the preimages needed for the conversion. Since only the 'active' preimages are exported, the output is shrunk from ~70GB to ~4GB.
The order of the output is the order used by the snapshot enumeration, which avoids database thrashing. However, it also means that storage-slot preimages are not deduplicated.
---
cmd/geth/chaincmd.go | 37 ++---------------
cmd/geth/main.go | 1 -
cmd/geth/snapshot.go | 54 +++++++++++++++++++++++++
cmd/utils/cmd.go | 96 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 154 insertions(+), 34 deletions(-)
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 9f51540984d3..4e528d6502fc 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -137,20 +137,7 @@ The import-preimages command imports hash preimages from an RLP encoded stream.
It's deprecated, please use "geth db import" instead.
`,
}
- exportPreimagesCommand = &cli.Command{
- Action: exportPreimages,
- Name: "export-preimages",
- Usage: "Export the preimage database into an RLP stream",
- ArgsUsage: "",
- Flags: flags.Merge([]cli.Flag{
- utils.CacheFlag,
- utils.SyncModeFlag,
- }, utils.DatabaseFlags),
- Description: `
-The export-preimages command exports hash preimages to an RLP encoded stream.
-It's deprecated, please use "geth db export" instead.
-`,
- }
+
dumpCommand = &cli.Command{
Action: dump,
Name: "dump",
@@ -386,6 +373,9 @@ func exportChain(ctx *cli.Context) error {
}
// importPreimages imports preimage data from the specified file.
+// it is deprecated, and the export function has been removed, but
+// the import function is kept around for the time being so that
+// older file formats can still be imported.
func importPreimages(ctx *cli.Context) error {
if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
@@ -405,25 +395,6 @@ func importPreimages(ctx *cli.Context) error {
return nil
}
-// exportPreimages dumps the preimage data to specified json file in streaming way.
-func exportPreimages(ctx *cli.Context) error {
- if ctx.Args().Len() < 1 {
- utils.Fatalf("This command requires an argument.")
- }
- stack, _ := makeConfigNode(ctx)
- defer stack.Close()
-
- db := utils.MakeChainDatabase(ctx, stack, true)
- defer db.Close()
- start := time.Now()
-
- if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil {
- utils.Fatalf("Export error: %v\n", err)
- }
- fmt.Printf("Export done in %v\n", time.Since(start))
- return nil
-}
-
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 2d4fe3dc060c..d1b14b81cd00 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -208,7 +208,6 @@ func init() {
importCommand,
exportCommand,
importPreimagesCommand,
- exportPreimagesCommand,
removedbCommand,
dumpCommand,
dumpGenesisCommand,
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index 82beb4f2e4c5..a635e5eb102a 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -20,6 +20,7 @@ import (
"bytes"
"encoding/json"
"errors"
+ "fmt"
"os"
"time"
@@ -147,6 +148,17 @@ as the backend data source, making this command a lot faster.
The argument is interpreted as block number or hash. If none is provided, the latest
block is used.
+`,
+ },
+ {
+ Action: snapshotExportPreimages,
+ Name: "export-preimages",
+ Usage: "Export the preimage in snapshot enumeration order",
+ ArgsUsage: " []",
+ Flags: utils.DatabaseFlags,
+ Description: `
+The export-preimages command exports hash preimages to a flat file, in exactly
+the expected order for the overlay tree migration.
`,
},
},
@@ -604,6 +616,48 @@ func dumpState(ctx *cli.Context) error {
return nil
}
+// snapshotExportPreimages dumps the preimage data to a flat file.
+func snapshotExportPreimages(ctx *cli.Context) error {
+ if ctx.NArg() < 1 {
+ utils.Fatalf("This command requires an argument.")
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chaindb := utils.MakeChainDatabase(ctx, stack, true)
+ defer chaindb.Close()
+
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ defer triedb.Close()
+
+ var root common.Hash
+ if ctx.NArg() > 1 {
+ rootBytes := common.FromHex(ctx.Args().Get(1))
+ if len(rootBytes) != common.HashLength {
+ return fmt.Errorf("invalid hash: %s", ctx.Args().Get(1))
+ }
+ root = common.BytesToHash(rootBytes)
+ } else {
+ headBlock := rawdb.ReadHeadBlock(chaindb)
+ if headBlock == nil {
+ log.Error("Failed to load head block")
+ return errors.New("no head block")
+ }
+ root = headBlock.Root()
+ }
+ snapConfig := snapshot.Config{
+ CacheSize: 256,
+ Recovery: false,
+ NoBuild: true,
+ AsyncBuild: false,
+ }
+ snaptree, err := snapshot.New(snapConfig, chaindb, triedb, root)
+ if err != nil {
+ return err
+ }
+ return utils.ExportSnapshotPreimages(chaindb, snaptree, ctx.Args().First(), root)
+}
+
// checkAccount iterates the snap data layers, and looks up the given account
// across all layers.
func checkAccount(ctx *cli.Context) error {
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index a7563a081e2f..8b571be1ef85 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig"
@@ -374,6 +375,101 @@ func ExportPreimages(db ethdb.Database, fn string) error {
return nil
}
+// ExportSnapshotPreimages exports the preimages corresponding to the enumeration of
+// the snapshot for a given root.
+func ExportSnapshotPreimages(chaindb ethdb.Database, snaptree *snapshot.Tree, fn string, root common.Hash) error {
+ log.Info("Exporting preimages", "file", fn)
+
+ fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
+ if err != nil {
+ return err
+ }
+ defer fh.Close()
+
+ // Enable gzip compressing if file name has gz suffix.
+ var writer io.Writer = fh
+ if strings.HasSuffix(fn, ".gz") {
+ gz := gzip.NewWriter(writer)
+ defer gz.Close()
+ writer = gz
+ }
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ writer = buf
+
+ type hashAndPreimageSize struct {
+ Hash common.Hash
+ Size int
+ }
+ hashCh := make(chan hashAndPreimageSize)
+
+ var (
+ start = time.Now()
+ logged = time.Now()
+ preimages int
+ )
+ go func() {
+ defer close(hashCh)
+ accIt, err := snaptree.AccountIterator(root, common.Hash{})
+ if err != nil {
+ log.Error("Failed to create account iterator", "error", err)
+ return
+ }
+ defer accIt.Release()
+
+ for accIt.Next() {
+ acc, err := types.FullAccount(accIt.Account())
+ if err != nil {
+ log.Error("Failed to get full account", "error", err)
+ return
+ }
+ preimages += 1
+ hashCh <- hashAndPreimageSize{Hash: accIt.Hash(), Size: common.AddressLength}
+
+ if acc.Root != (common.Hash{}) && acc.Root != types.EmptyRootHash {
+ stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{})
+ if err != nil {
+ log.Error("Failed to create storage iterator", "error", err)
+ return
+ }
+ for stIt.Next() {
+ preimages += 1
+ hashCh <- hashAndPreimageSize{Hash: stIt.Hash(), Size: common.HashLength}
+
+ if time.Since(logged) > time.Second*8 {
+ logged = time.Now()
+ log.Info("Exporting preimages", "count", preimages, "elapsed", common.PrettyDuration(time.Since(start)))
+ }
+ }
+ stIt.Release()
+ }
+ if time.Since(logged) > time.Second*8 {
+ logged = time.Now()
+ log.Info("Exporting preimages", "count", preimages, "elapsed", common.PrettyDuration(time.Since(start)))
+ }
+ }
+ }()
+
+ for item := range hashCh {
+ preimage := rawdb.ReadPreimage(chaindb, item.Hash)
+ if len(preimage) == 0 {
+ return fmt.Errorf("missing preimage for %v", item.Hash)
+ }
+ if len(preimage) != item.Size {
+ return fmt.Errorf("invalid preimage size, have %d", len(preimage))
+ }
+ rlpenc, err := rlp.EncodeToBytes(preimage)
+ if err != nil {
+ return fmt.Errorf("error encoding preimage: %w", err)
+ }
+ if _, err := writer.Write(rlpenc); err != nil {
+ return fmt.Errorf("failed to write preimage: %w", err)
+ }
+ }
+ log.Info("Exported preimages", "count", preimages, "elapsed", common.PrettyDuration(time.Since(start)), "file", fn)
+ return nil
+}
+
// exportHeader is used in the export/import flow. When we do an export,
// the first element we output is the exportHeader.
// Whenever a backwards-incompatible change is made, the Version header
From 63127f5443bbf4dd6c56fcb11236d35b1ecad848 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Wed, 22 Nov 2023 16:32:43 +0100
Subject: [PATCH 031/103] cmd/geth: fix build error (#28585)
---
cmd/geth/snapshot.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index a635e5eb102a..80d946b894d8 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -627,7 +627,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
var root common.Hash
From eec37e3b713b92c2049723f767af43faa3591a15 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Thu, 23 Nov 2023 09:22:09 +0100
Subject: [PATCH 032/103] cmd/devp2p/internal/ethtest: undo debug-hack (#28588)
cmd/devp2p/internal/ethtest: remove a debug-hack flaw which prevented certain tests from running
---
cmd/devp2p/internal/ethtest/snap.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go
index f50159a0deb7..21a5c8232a16 100644
--- a/cmd/devp2p/internal/ethtest/snap.go
+++ b/cmd/devp2p/internal/ethtest/snap.go
@@ -461,7 +461,7 @@ func (s *Suite) TestSnapTrieNodes(t *utesting.T) {
common.HexToHash("0xbe3d75a1729be157e79c3b77f00206db4d54e3ea14375a015451c88ec067c790"),
},
},
- }[7:] {
+ } {
tc := tc
if err := s.snapGetTrieNodes(t, &tc); err != nil {
t.Errorf("test %d \n #hashes %x\n root: %#x\n bytes: %d\nfailed: %v", i, len(tc.expHashes), tc.root, tc.nBytes, err)
From d76efbb9be403689bc8d677a16af453728a7a513 Mon Sep 17 00:00:00 2001
From: Mikel Cortes <45786396+cortze@users.noreply.github.com>
Date: Thu, 23 Nov 2023 10:14:10 +0000
Subject: [PATCH 033/103] params: update discV5 bootnodes (#28562)
update discV5 bootnodes from https://github.com/eth-clients/eth2-networks/blob/master/shared/mainnet/bootstrap_nodes.txt
---
params/bootnodes.go | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/params/bootnodes.go b/params/bootnodes.go
index a843896914a6..5e2c7c218102 100644
--- a/params/bootnodes.go
+++ b/params/bootnodes.go
@@ -66,20 +66,25 @@ var GoerliBootnodes = []string{
var V5Bootnodes = []string{
// Teku team's bootnode
- "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
- "enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA",
+ "enr:-KG4QMOEswP62yzDjSwWS4YEjtTZ5PO6r65CPqYBkgTTkrpaedQ8uEUo1uMALtJIvb2w_WWEVmg5yt1UAuK1ftxUU7QDhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQEnfA2iXNlY3AyNTZrMaEDfol8oLr6XJ7FsdAYE7lpJhKMls4G_v6qQOGKJUWGb_uDdGNwgiMog3VkcIIjKA", // # 4.157.240.54 | azure-us-east-virginia
+ "enr:-KG4QF4B5WrlFcRhUU6dZETwY5ZzAXnA0vGC__L1Kdw602nDZwXSTs5RFXFIFUnbQJmhNGVU6OIX7KVrCSTODsz1tK4DhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQExNYEiXNlY3AyNTZrMaECQmM9vp7KhaXhI-nqL_R0ovULLCFSFTa9CPPSdb1zPX6DdGNwgiMog3VkcIIjKA", // 4.196.214.4 | azure-au-east-sydney
// Prylab team's bootnodes
- "enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg",
- "enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA",
- "enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg",
+ "enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg", // 18.223.219.100 | aws-us-east-2-ohio
+ "enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA", // 18.223.219.100 | aws-us-east-2-ohio
+ "enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg", // 18.223.219.100 | aws-us-east-2-ohio
// Lighthouse team's bootnodes
- "enr:-IS4QLkKqDMy_ExrpOEWa59NiClemOnor-krjp4qoeZwIw2QduPC-q7Kz4u1IOWf3DDbdxqQIgC4fejavBOuUPy-HE4BgmlkgnY0gmlwhCLzAHqJc2VjcDI1NmsxoQLQSJfEAHZApkm5edTCZ_4qps_1k_ub2CxHFxi-gr2JMIN1ZHCCIyg",
- "enr:-IS4QDAyibHCzYZmIYZCjXwU9BqpotWmv2BsFlIq1V31BwDDMJPFEbox1ijT5c2Ou3kvieOKejxuaCqIcjxBjJ_3j_cBgmlkgnY0gmlwhAMaHiCJc2VjcDI1NmsxoQJIdpj_foZ02MXz4It8xKD7yUHTBx7lVFn3oeRP21KRV4N1ZHCCIyg",
+ "enr:-Le4QPUXJS2BTORXxyx2Ia-9ae4YqA_JWX3ssj4E_J-3z1A-HmFGrU8BpvpqhNabayXeOZ2Nq_sbeDgtzMJpLLnXFgAChGV0aDKQtTA_KgEAAAAAIgEAAAAAAIJpZIJ2NIJpcISsaa0Zg2lwNpAkAIkHAAAAAPA8kv_-awoTiXNlY3AyNTZrMaEDHAD2JKYevx89W0CcFJFiskdcEzkH_Wdv9iW42qLK79ODdWRwgiMohHVkcDaCI4I", // 172.105.173.25 | linode-au-sydney
+ "enr:-Le4QLHZDSvkLfqgEo8IWGG96h6mxwe_PsggC20CL3neLBjfXLGAQFOPSltZ7oP6ol54OvaNqO02Rnvb8YmDR274uq8ChGV0aDKQtTA_KgEAAAAAIgEAAAAAAIJpZIJ2NIJpcISLosQxg2lwNpAqAX4AAAAAAPA8kv_-ax65iXNlY3AyNTZrMaEDBJj7_dLFACaxBfaI8KZTh_SSJUjhyAyfshimvSqo22WDdWRwgiMohHVkcDaCI4I", // 139.162.196.49 | linode-uk-london
+ "enr:-Le4QH6LQrusDbAHPjU_HcKOuMeXfdEB5NJyXgHWFadfHgiySqeDyusQMvfphdYWOzuSZO9Uq2AMRJR5O4ip7OvVma8BhGV0aDKQtTA_KgEAAAAAIgEAAAAAAIJpZIJ2NIJpcISLY9ncg2lwNpAkAh8AgQIBAAAAAAAAAAmXiXNlY3AyNTZrMaECDYCZTZEksF-kmgPholqgVt8IXr-8L7Nu7YrZ7HUpgxmDdWRwgiMohHVkcDaCI4I", // 139.99.217.220 | ovh-au-sydney
+ "enr:-Le4QIqLuWybHNONr933Lk0dcMmAB5WgvGKRyDihy1wHDIVlNuuztX62W51voT4I8qD34GcTEOTmag1bcdZ_8aaT4NUBhGV0aDKQtTA_KgEAAAAAIgEAAAAAAIJpZIJ2NIJpcISLY04ng2lwNpAkAh8AgAIBAAAAAAAAAA-fiXNlY3AyNTZrMaEDscnRV6n1m-D9ID5UsURk0jsoKNXt1TIrj8uKOGW6iluDdWRwgiMohHVkcDaCI4I", // 139.99.78.39 | ovh-singapore
// EF bootnodes
- "enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg",
- "enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg",
- "enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg",
- "enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg",
+ "enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg", // 3.17.30.69 | aws-us-east-2-ohio
+ "enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg", // 18.216.248.220 | aws-us-east-2-ohio
+ "enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg", // 54.178.44.198 | aws-ap-northeast-1-tokyo
+ "enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg", // 54.65.172.253 | aws-ap-northeast-1-tokyo
+ // Nimbus team's bootnodes
+ "enr:-LK4QA8FfhaAjlb_BXsXxSfiysR7R52Nhi9JBt4F8SPssu8hdE1BXQQEtVDC3qStCW60LSO7hEsVHv5zm8_6Vnjhcn0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAN4aBKJc2VjcDI1NmsxoQJerDhsJ-KxZ8sHySMOCmTO6sHM3iCFQ6VMvLTe948MyYN0Y3CCI4yDdWRwgiOM", // 3.120.104.18 | aws-eu-central-1-frankfurt
+ "enr:-LK4QKWrXTpV9T78hNG6s8AM6IO4XH9kFT91uZtFg1GcsJ6dKovDOr1jtAAFPnS2lvNltkOGA9k29BUN7lFh_sjuc9QBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhANAdd-Jc2VjcDI1NmsxoQLQa6ai7y9PMN5hpLe5HmiJSlYzMuzP7ZhwRiwHvqNXdoN0Y3CCI4yDdWRwgiOM", // 3.64.117.223 | aws-eu-central-1-frankfurt}
}
const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@"
From bdf5e388ca0d1e6f5e227ba52481fe6b7667ec4d Mon Sep 17 00:00:00 2001
From: Marius van der Wijden
Date: Thu, 23 Nov 2023 17:28:26 +0300
Subject: [PATCH 034/103] cmd, les, tests: remove light client code (#28586)
* cmd, les, tests: remove light client code
This commit removes the light client (LES) code.
Since the merge the light client has been broken and
it is hard to maintain it alongside the normal client.
We decided it would be best to remove it for now and
maybe rework and reintroduce it in the future.
* cmd, eth: remove some more mentions of light mode
* cmd: re-add flags and mark as deprecated
* cmd: warn the user about deprecated flags
* eth: better error message
---
cmd/faucet/README.md | 52 -
cmd/faucet/faucet.go | 891 --------------
cmd/faucet/faucet.html | 233 ----
cmd/faucet/faucet_test.go | 46 -
cmd/geth/config.go | 3 +-
cmd/geth/les_test.go | 206 ----
cmd/geth/main.go | 31 +-
cmd/geth/run_test.go | 9 +
cmd/utils/flags.go | 128 +-
cmd/utils/flags_legacy.go | 41 +
eth/backend.go | 2 +-
eth/ethconfig/config.go | 10 -
ethstats/ethstats.go | 3 +-
les/api.go | 349 ------
les/api_backend.go | 337 ------
les/api_test.go | 512 --------
les/benchmark.go | 351 ------
les/bloombits.go | 75 --
les/client.go | 377 ------
les/client_handler.go | 309 -----
les/commons.go | 99 --
les/costtracker.go | 517 --------
les/distributor.go | 313 -----
les/distributor_test.go | 189 ---
les/enr_entry.go | 72 --
les/flowcontrol/control.go | 433 -------
les/flowcontrol/logger.go | 65 --
les/flowcontrol/manager.go | 476 --------
les/flowcontrol/manager_test.go | 130 ---
les/handler_test.go | 754 ------------
les/metrics.go | 151 ---
les/odr.go | 237 ----
les/odr_requests.go | 537 ---------
les/odr_test.go | 458 --------
les/peer.go | 1362 ----------------------
les/peer_test.go | 166 ---
les/protocol.go | 327 ------
les/request_test.go | 129 --
les/retrieve.go | 421 -------
les/server.go | 281 -----
les/server_handler.go | 436 -------
les/server_requests.go | 566 ---------
les/servingqueue.go | 365 ------
les/state_accessor.go | 80 --
les/test_helper.go | 626 ----------
les/txrelay.go | 179 ---
les/utils/exec_queue.go | 105 --
les/utils/exec_queue_test.go | 60 -
les/utils/expiredvalue.go | 270 -----
les/utils/expiredvalue_test.go | 195 ----
les/utils/limiter.go | 398 -------
les/utils/limiter_test.go | 206 ----
les/utils/timeutils.go | 69 --
les/utils/timeutils_test.go | 47 -
les/utils/weighted_select.go | 183 ---
les/utils/weighted_select_test.go | 68 --
les/vflux/client/api.go | 107 --
les/vflux/client/fillset.go | 107 --
les/vflux/client/fillset_test.go | 119 --
les/vflux/client/queueiterator.go | 123 --
les/vflux/client/queueiterator_test.go | 99 --
les/vflux/client/requestbasket.go | 285 -----
les/vflux/client/requestbasket_test.go | 171 ---
les/vflux/client/serverpool.go | 605 ----------
les/vflux/client/serverpool_test.go | 424 -------
les/vflux/client/timestats.go | 237 ----
les/vflux/client/timestats_test.go | 145 ---
les/vflux/client/valuetracker.go | 506 --------
les/vflux/client/valuetracker_test.go | 137 ---
les/vflux/client/wrsiterator.go | 127 --
les/vflux/client/wrsiterator_test.go | 105 --
les/vflux/requests.go | 180 ---
les/vflux/server/balance.go | 693 -----------
les/vflux/server/balance_test.go | 459 --------
les/vflux/server/balance_tracker.go | 300 -----
les/vflux/server/clientdb.go | 250 ----
les/vflux/server/clientdb_test.go | 148 ---
les/vflux/server/clientpool.go | 328 ------
les/vflux/server/clientpool_test.go | 640 ----------
les/vflux/server/metrics.go | 35 -
les/vflux/server/prioritypool.go | 695 -----------
les/vflux/server/prioritypool_test.go | 237 ----
les/vflux/server/service.go | 120 --
les/vflux/server/status.go | 59 -
tests/fuzzers/les/les-fuzzer.go | 411 -------
tests/fuzzers/les/les_test.go | 25 -
tests/fuzzers/vflux/clientpool-fuzzer.go | 333 ------
tests/fuzzers/vflux/clientpool_test.go | 25 -
88 files changed, 81 insertions(+), 23089 deletions(-)
delete mode 100644 cmd/faucet/README.md
delete mode 100644 cmd/faucet/faucet.go
delete mode 100644 cmd/faucet/faucet.html
delete mode 100644 cmd/faucet/faucet_test.go
delete mode 100644 cmd/geth/les_test.go
delete mode 100644 les/api.go
delete mode 100644 les/api_backend.go
delete mode 100644 les/api_test.go
delete mode 100644 les/benchmark.go
delete mode 100644 les/bloombits.go
delete mode 100644 les/client.go
delete mode 100644 les/client_handler.go
delete mode 100644 les/commons.go
delete mode 100644 les/costtracker.go
delete mode 100644 les/distributor.go
delete mode 100644 les/distributor_test.go
delete mode 100644 les/enr_entry.go
delete mode 100644 les/flowcontrol/control.go
delete mode 100644 les/flowcontrol/logger.go
delete mode 100644 les/flowcontrol/manager.go
delete mode 100644 les/flowcontrol/manager_test.go
delete mode 100644 les/handler_test.go
delete mode 100644 les/metrics.go
delete mode 100644 les/odr.go
delete mode 100644 les/odr_requests.go
delete mode 100644 les/odr_test.go
delete mode 100644 les/peer.go
delete mode 100644 les/peer_test.go
delete mode 100644 les/protocol.go
delete mode 100644 les/request_test.go
delete mode 100644 les/retrieve.go
delete mode 100644 les/server.go
delete mode 100644 les/server_handler.go
delete mode 100644 les/server_requests.go
delete mode 100644 les/servingqueue.go
delete mode 100644 les/state_accessor.go
delete mode 100644 les/test_helper.go
delete mode 100644 les/txrelay.go
delete mode 100644 les/utils/exec_queue.go
delete mode 100644 les/utils/exec_queue_test.go
delete mode 100644 les/utils/expiredvalue.go
delete mode 100644 les/utils/expiredvalue_test.go
delete mode 100644 les/utils/limiter.go
delete mode 100644 les/utils/limiter_test.go
delete mode 100644 les/utils/timeutils.go
delete mode 100644 les/utils/timeutils_test.go
delete mode 100644 les/utils/weighted_select.go
delete mode 100644 les/utils/weighted_select_test.go
delete mode 100644 les/vflux/client/api.go
delete mode 100644 les/vflux/client/fillset.go
delete mode 100644 les/vflux/client/fillset_test.go
delete mode 100644 les/vflux/client/queueiterator.go
delete mode 100644 les/vflux/client/queueiterator_test.go
delete mode 100644 les/vflux/client/requestbasket.go
delete mode 100644 les/vflux/client/requestbasket_test.go
delete mode 100644 les/vflux/client/serverpool.go
delete mode 100644 les/vflux/client/serverpool_test.go
delete mode 100644 les/vflux/client/timestats.go
delete mode 100644 les/vflux/client/timestats_test.go
delete mode 100644 les/vflux/client/valuetracker.go
delete mode 100644 les/vflux/client/valuetracker_test.go
delete mode 100644 les/vflux/client/wrsiterator.go
delete mode 100644 les/vflux/client/wrsiterator_test.go
delete mode 100644 les/vflux/requests.go
delete mode 100644 les/vflux/server/balance.go
delete mode 100644 les/vflux/server/balance_test.go
delete mode 100644 les/vflux/server/balance_tracker.go
delete mode 100644 les/vflux/server/clientdb.go
delete mode 100644 les/vflux/server/clientdb_test.go
delete mode 100644 les/vflux/server/clientpool.go
delete mode 100644 les/vflux/server/clientpool_test.go
delete mode 100644 les/vflux/server/metrics.go
delete mode 100644 les/vflux/server/prioritypool.go
delete mode 100644 les/vflux/server/prioritypool_test.go
delete mode 100644 les/vflux/server/service.go
delete mode 100644 les/vflux/server/status.go
delete mode 100644 tests/fuzzers/les/les-fuzzer.go
delete mode 100644 tests/fuzzers/les/les_test.go
delete mode 100644 tests/fuzzers/vflux/clientpool-fuzzer.go
delete mode 100644 tests/fuzzers/vflux/clientpool_test.go
diff --git a/cmd/faucet/README.md b/cmd/faucet/README.md
deleted file mode 100644
index 7e857fa0ee87..000000000000
--- a/cmd/faucet/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Faucet
-
-The `faucet` is a simplistic web application with the goal of distributing small amounts of Ether in private and test networks.
-
-Users need to post their Ethereum addresses to fund in a Twitter status update or public Facebook post and share the link to the faucet. The faucet will in turn deduplicate user requests and send the Ether. After a funding round, the faucet prevents the same user from requesting again for a pre-configured amount of time, proportional to the amount of Ether requested.
-
-## Operation
-
-The `faucet` is a single binary app (everything included) with all configurations set via command line flags and a few files.
-
-First things first, the `faucet` needs to connect to an Ethereum network, for which it needs the necessary genesis and network infos. Each of the following flags must be set:
-
-- `-genesis` is a path to a file containing the network `genesis.json`. or using:
- - `-goerli` with the faucet with Görli network config
- - `-sepolia` with the faucet with Sepolia network config
-- `-network` is the devp2p network id used during connection
-- `-bootnodes` is a list of `enode://` ids to join the network through
-
-The `faucet` will use the `les` protocol to join the configured Ethereum network and will store its data in `$HOME/.faucet` (currently not configurable).
-
-## Funding
-
-To be able to distribute funds, the `faucet` needs access to an already funded Ethereum account. This can be configured via:
-
-- `-account.json` is a path to the Ethereum account's JSON key file
-- `-account.pass` is a path to a text file with the decryption passphrase
-
-The faucet is able to distribute various amounts of Ether in exchange for various timeouts. These can be configured via:
-
-- `-faucet.amount` is the number of Ethers to send by default
-- `-faucet.minutes` is the time to wait before allowing a rerequest
-- `-faucet.tiers` is the funding tiers to support (x3 time, x2.5 funds)
-
-## Sybil protection
-
-To prevent the same user from exhausting funds in a loop, the `faucet` ties requests to social networks and captcha resolvers.
-
-Captcha protection uses Google's invisible ReCaptcha, thus the `faucet` needs to run on a live domain. The domain needs to be registered in Google's systems to retrieve the captcha API token and secrets. After doing so, captcha protection may be enabled via:
-
-- `-captcha.token` is the API token for ReCaptcha
-- `-captcha.secret` is the API secret for ReCaptcha
-
-Sybil protection via Twitter requires an API key as of 15th December, 2020. To obtain it, a Twitter user must be upgraded to developer status and a new Twitter App deployed with it. The app's `Bearer` token is required by the faucet to retrieve tweet data:
-
-- `-twitter.token` is the Bearer token for `v2` API access
-- `-twitter.token.v1` is the Bearer token for `v1` API access
-
-Sybil protection via Facebook uses the website to directly download post data thus does not currently require an API configuration.
-
-## Miscellaneous
-
-Beside the above - mostly essential - CLI flags, there are a number that can be used to fine-tune the `faucet`'s operation. Please see `faucet --help` for a full list.
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
deleted file mode 100644
index 8f4127216e73..000000000000
--- a/cmd/faucet/faucet.go
+++ /dev/null
@@ -1,891 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// faucet is an Ether faucet backed by a light client.
-package main
-
-import (
- "bytes"
- "context"
- _ "embed"
- "encoding/json"
- "errors"
- "flag"
- "fmt"
- "html/template"
- "io"
- "math"
- "math/big"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/accounts/keystore"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethclient"
- "github.com/ethereum/go-ethereum/ethstats"
- "github.com/ethereum/go-ethereum/internal/version"
- "github.com/ethereum/go-ethereum/les"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/params"
- "github.com/gorilla/websocket"
-)
-
-var (
- genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with")
- apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection")
- ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection")
- bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with")
- netFlag = flag.Uint64("network", 0, "Network ID to use for the Ethereum protocol")
- statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string")
-
- netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet")
- payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request")
- minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds")
- tiersFlag = flag.Int("faucet.tiers", 3, "Number of funding tiers to enable (x3 time, x2.5 funds)")
-
- accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with")
- accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds")
-
- captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
- captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
-
- noauthFlag = flag.Bool("noauth", false, "Enables funding requests without authentication")
- logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
-
- twitterTokenFlag = flag.String("twitter.token", "", "Bearer token to authenticate with the v2 Twitter API")
- twitterTokenV1Flag = flag.String("twitter.token.v1", "", "Bearer token to authenticate with the v1.1 Twitter API")
-
- goerliFlag = flag.Bool("goerli", false, "Initializes the faucet with Görli network config")
- sepoliaFlag = flag.Bool("sepolia", false, "Initializes the faucet with Sepolia network config")
-)
-
-var (
- ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
-)
-
-//go:embed faucet.html
-var websiteTmpl string
-
-func main() {
- // Parse the flags and set up the logger to print everything requested
- flag.Parse()
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
-
- // Construct the payout tiers
- amounts := make([]string, *tiersFlag)
- periods := make([]string, *tiersFlag)
- for i := 0; i < *tiersFlag; i++ {
- // Calculate the amount for the next tier and format it
- amount := float64(*payoutFlag) * math.Pow(2.5, float64(i))
- amounts[i] = fmt.Sprintf("%s Ethers", strconv.FormatFloat(amount, 'f', -1, 64))
- if amount == 1 {
- amounts[i] = strings.TrimSuffix(amounts[i], "s")
- }
- // Calculate the period for the next tier and format it
- period := *minutesFlag * int(math.Pow(3, float64(i)))
- periods[i] = fmt.Sprintf("%d mins", period)
- if period%60 == 0 {
- period /= 60
- periods[i] = fmt.Sprintf("%d hours", period)
-
- if period%24 == 0 {
- period /= 24
- periods[i] = fmt.Sprintf("%d days", period)
- }
- }
- if period == 1 {
- periods[i] = strings.TrimSuffix(periods[i], "s")
- }
- }
- website := new(bytes.Buffer)
- err := template.Must(template.New("").Parse(websiteTmpl)).Execute(website, map[string]interface{}{
- "Network": *netnameFlag,
- "Amounts": amounts,
- "Periods": periods,
- "Recaptcha": *captchaToken,
- "NoAuth": *noauthFlag,
- })
- if err != nil {
- log.Crit("Failed to render the faucet template", "err", err)
- }
- // Load and parse the genesis block requested by the user
- genesis, err := getGenesis(*genesisFlag, *goerliFlag, *sepoliaFlag)
- if err != nil {
- log.Crit("Failed to parse genesis config", "err", err)
- }
- // Convert the bootnodes to internal enode representations
- var enodes []*enode.Node
- for _, boot := range strings.Split(*bootFlag, ",") {
- if url, err := enode.Parse(enode.ValidSchemes, boot); err == nil {
- enodes = append(enodes, url)
- } else {
- log.Error("Failed to parse bootnode URL", "url", boot, "err", err)
- }
- }
- // Load up the account key and decrypt its password
- blob, err := os.ReadFile(*accPassFlag)
- if err != nil {
- log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err)
- }
- pass := strings.TrimSuffix(string(blob), "\n")
-
- ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP)
- if blob, err = os.ReadFile(*accJSONFlag); err != nil {
- log.Crit("Failed to read account key contents", "file", *accJSONFlag, "err", err)
- }
- acc, err := ks.Import(blob, pass, pass)
- if err != nil && err != keystore.ErrAccountAlreadyExists {
- log.Crit("Failed to import faucet signer account", "err", err)
- }
- if err := ks.Unlock(acc, pass); err != nil {
- log.Crit("Failed to unlock faucet signer account", "err", err)
- }
- // Assemble and start the faucet light service
- faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes())
- if err != nil {
- log.Crit("Failed to start faucet", "err", err)
- }
- defer faucet.close()
-
- if err := faucet.listenAndServe(*apiPortFlag); err != nil {
- log.Crit("Failed to launch faucet API", "err", err)
- }
-}
-
-// request represents an accepted funding request.
-type request struct {
- Avatar string `json:"avatar"` // Avatar URL to make the UI nicer
- Account common.Address `json:"account"` // Ethereum address being funded
- Time time.Time `json:"time"` // Timestamp when the request was accepted
- Tx *types.Transaction `json:"tx"` // Transaction funding the account
-}
-
-// faucet represents a crypto faucet backed by an Ethereum light client.
-type faucet struct {
- config *params.ChainConfig // Chain configurations for signing
- stack *node.Node // Ethereum protocol stack
- client *ethclient.Client // Client connection to the Ethereum chain
- index []byte // Index page to serve up on the web
-
- keystore *keystore.KeyStore // Keystore containing the single signer
- account accounts.Account // Account funding user faucet requests
- head *types.Header // Current head header of the faucet
- balance *big.Int // Current balance of the faucet
- nonce uint64 // Current pending nonce of the faucet
- price *big.Int // Current gas price to issue funds with
-
- conns []*wsConn // Currently live websocket connections
- timeouts map[string]time.Time // History of users and their funding timeouts
- reqs []*request // Currently pending funding requests
- update chan struct{} // Channel to signal request updates
-
- lock sync.RWMutex // Lock protecting the faucet's internals
-}
-
-// wsConn wraps a websocket connection with a write mutex as the underlying
-// websocket library does not synchronize access to the stream.
-type wsConn struct {
- conn *websocket.Conn
- wlock sync.Mutex
-}
-
-func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
- // Assemble the raw devp2p protocol stack
- git, _ := version.VCS()
- stack, err := node.New(&node.Config{
- Name: "geth",
- Version: params.VersionWithCommit(git.Commit, git.Date),
- DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"),
- P2P: p2p.Config{
- NAT: nat.Any(),
- NoDiscovery: true,
- DiscoveryV5: true,
- ListenAddr: fmt.Sprintf(":%d", port),
- MaxPeers: 25,
- BootstrapNodesV5: enodes,
- },
- })
- if err != nil {
- return nil, err
- }
-
- // Assemble the Ethereum light client protocol
- cfg := ethconfig.Defaults
- cfg.SyncMode = downloader.LightSync
- cfg.NetworkId = network
- cfg.Genesis = genesis
- utils.SetDNSDiscoveryDefaults(&cfg, genesis.ToBlock().Hash())
-
- lesBackend, err := les.New(stack, &cfg)
- if err != nil {
- return nil, fmt.Errorf("failed to register the Ethereum service: %w", err)
- }
-
- // Assemble the ethstats monitoring and reporting service'
- if stats != "" {
- if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), stats); err != nil {
- return nil, err
- }
- }
- // Boot up the client and ensure it connects to bootnodes
- if err := stack.Start(); err != nil {
- return nil, err
- }
- for _, boot := range enodes {
- old, err := enode.Parse(enode.ValidSchemes, boot.String())
- if err == nil {
- stack.Server().AddPeer(old)
- }
- }
- // Attach to the client and retrieve and interesting metadatas
- api := stack.Attach()
- client := ethclient.NewClient(api)
-
- return &faucet{
- config: genesis.Config,
- stack: stack,
- client: client,
- index: index,
- keystore: ks,
- account: ks.Accounts()[0],
- timeouts: make(map[string]time.Time),
- update: make(chan struct{}, 1),
- }, nil
-}
-
-// close terminates the Ethereum connection and tears down the faucet.
-func (f *faucet) close() error {
- return f.stack.Close()
-}
-
-// listenAndServe registers the HTTP handlers for the faucet and boots it up
-// for service user funding requests.
-func (f *faucet) listenAndServe(port int) error {
- go f.loop()
-
- http.HandleFunc("/", f.webHandler)
- http.HandleFunc("/api", f.apiHandler)
- return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
-}
-
-// webHandler handles all non-api requests, simply flattening and returning the
-// faucet website.
-func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
- w.Write(f.index)
-}
-
-// apiHandler handles requests for Ether grants and transaction statuses.
-func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
- upgrader := websocket.Upgrader{}
- conn, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- return
- }
-
- // Start tracking the connection and drop at the end
- defer conn.Close()
-
- f.lock.Lock()
- wsconn := &wsConn{conn: conn}
- f.conns = append(f.conns, wsconn)
- f.lock.Unlock()
-
- defer func() {
- f.lock.Lock()
- for i, c := range f.conns {
- if c.conn == conn {
- f.conns = append(f.conns[:i], f.conns[i+1:]...)
- break
- }
- }
- f.lock.Unlock()
- }()
- // Gather the initial stats from the network to report
- var (
- head *types.Header
- balance *big.Int
- nonce uint64
- )
- for head == nil || balance == nil {
- // Retrieve the current stats cached by the faucet
- f.lock.RLock()
- if f.head != nil {
- head = types.CopyHeader(f.head)
- }
- if f.balance != nil {
- balance = new(big.Int).Set(f.balance)
- }
- nonce = f.nonce
- f.lock.RUnlock()
-
- if head == nil || balance == nil {
- // Report the faucet offline until initial stats are ready
- //lint:ignore ST1005 This error is to be displayed in the browser
- if err = sendError(wsconn, errors.New("Faucet offline")); err != nil {
- log.Warn("Failed to send faucet error to client", "err", err)
- return
- }
- time.Sleep(3 * time.Second)
- }
- }
- // Send over the initial stats and the latest header
- f.lock.RLock()
- reqs := f.reqs
- f.lock.RUnlock()
- if err = send(wsconn, map[string]interface{}{
- "funds": new(big.Int).Div(balance, ether),
- "funded": nonce,
- "peers": f.stack.Server().PeerCount(),
- "requests": reqs,
- }, 3*time.Second); err != nil {
- log.Warn("Failed to send initial stats to client", "err", err)
- return
- }
- if err = send(wsconn, head, 3*time.Second); err != nil {
- log.Warn("Failed to send initial header to client", "err", err)
- return
- }
- // Keep reading requests from the websocket until the connection breaks
- for {
- // Fetch the next funding request and validate against github
- var msg struct {
- URL string `json:"url"`
- Tier uint `json:"tier"`
- Captcha string `json:"captcha"`
- }
- if err = conn.ReadJSON(&msg); err != nil {
- return
- }
- if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://twitter.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") {
- if err = sendError(wsconn, errors.New("URL doesn't link to supported services")); err != nil {
- log.Warn("Failed to send URL error to client", "err", err)
- return
- }
- continue
- }
- if msg.Tier >= uint(*tiersFlag) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- if err = sendError(wsconn, errors.New("Invalid funding tier requested")); err != nil {
- log.Warn("Failed to send tier error to client", "err", err)
- return
- }
- continue
- }
- log.Info("Faucet funds requested", "url", msg.URL, "tier", msg.Tier)
-
- // If captcha verifications are enabled, make sure we're not dealing with a robot
- if *captchaToken != "" {
- form := url.Values{}
- form.Add("secret", *captchaSecret)
- form.Add("response", msg.Captcha)
-
- res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form)
- if err != nil {
- if err = sendError(wsconn, err); err != nil {
- log.Warn("Failed to send captcha post error to client", "err", err)
- return
- }
- continue
- }
- var result struct {
- Success bool `json:"success"`
- Errors json.RawMessage `json:"error-codes"`
- }
- err = json.NewDecoder(res.Body).Decode(&result)
- res.Body.Close()
- if err != nil {
- if err = sendError(wsconn, err); err != nil {
- log.Warn("Failed to send captcha decode error to client", "err", err)
- return
- }
- continue
- }
- if !result.Success {
- log.Warn("Captcha verification failed", "err", string(result.Errors))
- //lint:ignore ST1005 it's funny and the robot won't mind
- if err = sendError(wsconn, errors.New("Beep-bop, you're a robot!")); err != nil {
- log.Warn("Failed to send captcha failure to client", "err", err)
- return
- }
- continue
- }
- }
- // Retrieve the Ethereum address to fund, the requesting user and a profile picture
- var (
- id string
- username string
- avatar string
- address common.Address
- )
- switch {
- case strings.HasPrefix(msg.URL, "https://twitter.com/"):
- id, username, avatar, address, err = authTwitter(msg.URL, *twitterTokenV1Flag, *twitterTokenFlag)
- case strings.HasPrefix(msg.URL, "https://www.facebook.com/"):
- username, avatar, address, err = authFacebook(msg.URL)
- id = username
- case *noauthFlag:
- username, avatar, address, err = authNoAuth(msg.URL)
- id = username
- default:
- //lint:ignore ST1005 This error is to be displayed in the browser
- err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues")
- }
- if err != nil {
- if err = sendError(wsconn, err); err != nil {
- log.Warn("Failed to send prefix error to client", "err", err)
- return
- }
- continue
- }
- log.Info("Faucet request valid", "url", msg.URL, "tier", msg.Tier, "user", username, "address", address)
-
- // Ensure the user didn't request funds too recently
- f.lock.Lock()
- var (
- fund bool
- timeout time.Time
- )
- if timeout = f.timeouts[id]; time.Now().After(timeout) {
- // User wasn't funded recently, create the funding transaction
- amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether)
- amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil))
- amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))
-
- tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil)
- signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID)
- if err != nil {
- f.lock.Unlock()
- if err = sendError(wsconn, err); err != nil {
- log.Warn("Failed to send transaction creation error to client", "err", err)
- return
- }
- continue
- }
- // Submit the transaction and mark as funded if successful
- if err := f.client.SendTransaction(context.Background(), signed); err != nil {
- f.lock.Unlock()
- if err = sendError(wsconn, err); err != nil {
- log.Warn("Failed to send transaction transmission error to client", "err", err)
- return
- }
- continue
- }
- f.reqs = append(f.reqs, &request{
- Avatar: avatar,
- Account: address,
- Time: time.Now(),
- Tx: signed,
- })
- timeout := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute
- grace := timeout / 288 // 24h timeout => 5m grace
-
- f.timeouts[id] = time.Now().Add(timeout - grace)
- fund = true
- }
- f.lock.Unlock()
-
- // Send an error if too frequent funding, othewise a success
- if !fund {
- if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple
- log.Warn("Failed to send funding error to client", "err", err)
- return
- }
- continue
- }
- if err = sendSuccess(wsconn, fmt.Sprintf("Funding request accepted for %s into %s", username, address.Hex())); err != nil {
- log.Warn("Failed to send funding success to client", "err", err)
- return
- }
- select {
- case f.update <- struct{}{}:
- default:
- }
- }
-}
-
-// refresh attempts to retrieve the latest header from the chain and extract the
-// associated faucet balance and nonce for connectivity caching.
-func (f *faucet) refresh(head *types.Header) error {
- // Ensure a state update does not run for too long
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
-
- // If no header was specified, use the current chain head
- var err error
- if head == nil {
- if head, err = f.client.HeaderByNumber(ctx, nil); err != nil {
- return err
- }
- }
- // Retrieve the balance, nonce and gas price from the current head
- var (
- balance *big.Int
- nonce uint64
- price *big.Int
- )
- if balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number); err != nil {
- return err
- }
- if nonce, err = f.client.NonceAt(ctx, f.account.Address, head.Number); err != nil {
- return err
- }
- if price, err = f.client.SuggestGasPrice(ctx); err != nil {
- return err
- }
- // Everything succeeded, update the cached stats and eject old requests
- f.lock.Lock()
- f.head, f.balance = head, balance
- f.price, f.nonce = price, nonce
- for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
- f.reqs = f.reqs[1:]
- }
- f.lock.Unlock()
-
- return nil
-}
-
-// loop keeps waiting for interesting events and pushes them out to connected
-// websockets.
-func (f *faucet) loop() {
- // Wait for chain events and push them to clients
- heads := make(chan *types.Header, 16)
- sub, err := f.client.SubscribeNewHead(context.Background(), heads)
- if err != nil {
- log.Crit("Failed to subscribe to head events", "err", err)
- }
- defer sub.Unsubscribe()
-
- // Start a goroutine to update the state from head notifications in the background
- update := make(chan *types.Header)
-
- go func() {
- for head := range update {
- // New chain head arrived, query the current stats and stream to clients
- timestamp := time.Unix(int64(head.Time), 0)
- if time.Since(timestamp) > time.Hour {
- log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp))
- continue
- }
- if err := f.refresh(head); err != nil {
- log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err)
- continue
- }
- // Faucet state retrieved, update locally and send to clients
- f.lock.RLock()
- log.Info("Updated faucet state", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp), "balance", f.balance, "nonce", f.nonce, "price", f.price)
-
- balance := new(big.Int).Div(f.balance, ether)
- peers := f.stack.Server().PeerCount()
-
- for _, conn := range f.conns {
- if err := send(conn, map[string]interface{}{
- "funds": balance,
- "funded": f.nonce,
- "peers": peers,
- "requests": f.reqs,
- }, time.Second); err != nil {
- log.Warn("Failed to send stats to client", "err", err)
- conn.conn.Close()
- continue
- }
- if err := send(conn, head, time.Second); err != nil {
- log.Warn("Failed to send header to client", "err", err)
- conn.conn.Close()
- }
- }
- f.lock.RUnlock()
- }
- }()
- // Wait for various events and assing to the appropriate background threads
- for {
- select {
- case head := <-heads:
- // New head arrived, send if for state update if there's none running
- select {
- case update <- head:
- default:
- }
-
- case <-f.update:
- // Pending requests updated, stream to clients
- f.lock.RLock()
- for _, conn := range f.conns {
- if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
- log.Warn("Failed to send requests to client", "err", err)
- conn.conn.Close()
- }
- }
- f.lock.RUnlock()
- }
- }
-}
-
-// sends transmits a data packet to the remote end of the websocket, but also
-// setting a write deadline to prevent waiting forever on the node.
-func send(conn *wsConn, value interface{}, timeout time.Duration) error {
- if timeout == 0 {
- timeout = 60 * time.Second
- }
- conn.wlock.Lock()
- defer conn.wlock.Unlock()
- conn.conn.SetWriteDeadline(time.Now().Add(timeout))
- return conn.conn.WriteJSON(value)
-}
-
-// sendError transmits an error to the remote end of the websocket, also setting
-// the write deadline to 1 second to prevent waiting forever.
-func sendError(conn *wsConn, err error) error {
- return send(conn, map[string]string{"error": err.Error()}, time.Second)
-}
-
-// sendSuccess transmits a success message to the remote end of the websocket, also
-// setting the write deadline to 1 second to prevent waiting forever.
-func sendSuccess(conn *wsConn, msg string) error {
- return send(conn, map[string]string{"success": msg}, time.Second)
-}
-
-// authTwitter tries to authenticate a faucet request using Twitter posts, returning
-// the uniqueness identifier (user id/username), username, avatar URL and Ethereum address to fund on success.
-func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, common.Address, error) {
- // Ensure the user specified a meaningful URL, no fancy nonsense
- parts := strings.Split(url, "/")
- if len(parts) < 4 || parts[len(parts)-2] != "status" {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("Invalid Twitter status URL")
- }
- // Strip any query parameters from the tweet id and ensure it's numeric
- tweetID := strings.Split(parts[len(parts)-1], "?")[0]
- if !regexp.MustCompile("^[0-9]+$").MatchString(tweetID) {
- return "", "", "", common.Address{}, errors.New("Invalid Tweet URL")
- }
- // Twitter's API isn't really friendly with direct links.
- // It is restricted to 300 queries / 15 minute with an app api key.
- // Anything more will require read only authorization from the users and that we want to avoid.
-
- // If Twitter bearer token is provided, use the API, selecting the version
- // the user would prefer (currently there's a limit of 1 v2 app / developer
- // but unlimited v1.1 apps).
- switch {
- case tokenV1 != "":
- return authTwitterWithTokenV1(tweetID, tokenV1)
- case tokenV2 != "":
- return authTwitterWithTokenV2(tweetID, tokenV2)
- }
- // Twitter API token isn't provided so we just load the public posts
- // and scrape it for the Ethereum address and profile URL. We need to load
- // the mobile page though since the main page loads tweet contents via JS.
- url = strings.Replace(url, "https://twitter.com/", "https://mobile.twitter.com/", 1)
-
- res, err := http.Get(url)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- defer res.Body.Close()
-
- // Resolve the username from the final redirect, no intermediate junk
- parts = strings.Split(res.Request.URL.String(), "/")
- if len(parts) < 4 || parts[len(parts)-2] != "status" {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("Invalid Twitter status URL")
- }
- username := parts[len(parts)-3]
-
- body, err := io.ReadAll(res.Body)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
- if address == (common.Address{}) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
- }
- var avatar string
- if parts = regexp.MustCompile(`src="([^"]+twimg\.com/profile_images[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
- avatar = parts[1]
- }
- return username + "@twitter", username, avatar, address, nil
-}
-
-// authTwitterWithTokenV1 tries to authenticate a faucet request using Twitter's v1
-// API, returning the user id, username, avatar URL and Ethereum address to fund on
-// success.
-func authTwitterWithTokenV1(tweetID string, token string) (string, string, string, common.Address, error) {
- // Query the tweet details from Twitter
- url := fmt.Sprintf("https://api.twitter.com/1.1/statuses/show.json?id=%s", tweetID)
- req, err := http.NewRequest(http.MethodGet, url, nil)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
- res, err := http.DefaultClient.Do(req)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- defer res.Body.Close()
-
- var result struct {
- Text string `json:"text"`
- User struct {
- ID string `json:"id_str"`
- Username string `json:"screen_name"`
- Avatar string `json:"profile_image_url"`
- } `json:"user"`
- }
- err = json.NewDecoder(res.Body).Decode(&result)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(result.Text))
- if address == (common.Address{}) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
- }
- return result.User.ID + "@twitter", result.User.Username, result.User.Avatar, address, nil
-}
-
-// authTwitterWithTokenV2 tries to authenticate a faucet request using Twitter's v2
-// API, returning the user id, username, avatar URL and Ethereum address to fund on
-// success.
-func authTwitterWithTokenV2(tweetID string, token string) (string, string, string, common.Address, error) {
- // Query the tweet details from Twitter
- url := fmt.Sprintf("https://api.twitter.com/2/tweets/%s?expansions=author_id&user.fields=profile_image_url", tweetID)
- req, err := http.NewRequest(http.MethodGet, url, nil)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
- res, err := http.DefaultClient.Do(req)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
- defer res.Body.Close()
-
- var result struct {
- Data struct {
- AuthorID string `json:"author_id"`
- Text string `json:"text"`
- } `json:"data"`
- Includes struct {
- Users []struct {
- ID string `json:"id"`
- Username string `json:"username"`
- Avatar string `json:"profile_image_url"`
- } `json:"users"`
- } `json:"includes"`
- }
-
- err = json.NewDecoder(res.Body).Decode(&result)
- if err != nil {
- return "", "", "", common.Address{}, err
- }
-
- address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(result.Data.Text))
- if address == (common.Address{}) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("No Ethereum address found to fund")
- }
- return result.Data.AuthorID + "@twitter", result.Includes.Users[0].Username, result.Includes.Users[0].Avatar, address, nil
-}
-
-// authFacebook tries to authenticate a faucet request using Facebook posts,
-// returning the username, avatar URL and Ethereum address to fund on success.
-func authFacebook(url string) (string, string, common.Address, error) {
- // Ensure the user specified a meaningful URL, no fancy nonsense
- parts := strings.Split(strings.Split(url, "?")[0], "/")
- if parts[len(parts)-1] == "" {
- parts = parts[0 : len(parts)-1]
- }
- if len(parts) < 4 || parts[len(parts)-2] != "posts" {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", common.Address{}, errors.New("Invalid Facebook post URL")
- }
- username := parts[len(parts)-3]
-
- // Facebook's Graph API isn't really friendly with direct links. Still, we don't
- // want to do ask read permissions from users, so just load the public posts and
- // scrape it for the Ethereum address and profile URL.
- //
- // Facebook recently changed their desktop webpage to use AJAX for loading post
- // content, so switch over to the mobile site for now. Will probably end up having
- // to use the API eventually.
- crawl := strings.Replace(url, "www.facebook.com", "m.facebook.com", 1)
-
- res, err := http.Get(crawl)
- if err != nil {
- return "", "", common.Address{}, err
- }
- defer res.Body.Close()
-
- body, err := io.ReadAll(res.Body)
- if err != nil {
- return "", "", common.Address{}, err
- }
- address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
- if address == (common.Address{}) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund. Please check the post URL and verify that it can be viewed publicly.")
- }
- var avatar string
- if parts = regexp.MustCompile(`src="([^"]+fbcdn\.net[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
- avatar = parts[1]
- }
- return username + "@facebook", avatar, address, nil
-}
-
-// authNoAuth tries to interpret a faucet request as a plain Ethereum address,
-// without actually performing any remote authentication. This mode is prone to
-// Byzantine attack, so only ever use for truly private networks.
-func authNoAuth(url string) (string, string, common.Address, error) {
- address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
- if address == (common.Address{}) {
- //lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
- }
- return address.Hex() + "@noauth", "", address, nil
-}
-
-// getGenesis returns a genesis based on input args
-func getGenesis(genesisFlag string, goerliFlag bool, sepoliaFlag bool) (*core.Genesis, error) {
- switch {
- case genesisFlag != "":
- var genesis core.Genesis
- err := common.LoadJSON(genesisFlag, &genesis)
- return &genesis, err
- case goerliFlag:
- return core.DefaultGoerliGenesisBlock(), nil
- case sepoliaFlag:
- return core.DefaultSepoliaGenesisBlock(), nil
- default:
- return nil, errors.New("no genesis flag provided")
- }
-}
diff --git a/cmd/faucet/faucet.html b/cmd/faucet/faucet.html
deleted file mode 100644
index dad5ad84f210..000000000000
--- a/cmd/faucet/faucet.html
+++ /dev/null
@@ -1,233 +0,0 @@
-
-
-
-
-
-
-
- {{.Network}}: Authenticated Faucet
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{.Network}} Authenticated Faucet
-
-
-
-
-
-
-
-
-
-
- {{if .Recaptcha}}
- {{end}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- How does this work?
- This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to common 3rd party social network accounts. Anyone having a Twitter or Facebook account may request funds within the permitted limits.
-
-
- - To request funds via Twitter, make a tweet with your Ethereum address pasted into the contents (surrounding text doesn't matter).
Copy-paste the tweets URL into the above input box and fire away!
-
-
- - To request funds via Facebook, publish a new public post with your Ethereum address embedded into the content (surrounding text doesn't matter).
Copy-paste the posts URL into the above input box and fire away!
-
- {{if .NoAuth}}
-
- - To request funds without authentication, simply copy-paste your Ethereum address into the above input box (surrounding text doesn't matter) and fire away.
This mode is susceptible to Byzantine attacks. Only use for debugging or private networks!
- {{end}}
-
- You can track the current pending requests below the input field to see how much you have to wait until your turn comes.
- {{if .Recaptcha}}The faucet is running invisible reCaptcha protection against bots.{{end}}
-
-
-
-
-
- {{if .Recaptcha}}
- {{end}}
-
-
diff --git a/cmd/faucet/faucet_test.go b/cmd/faucet/faucet_test.go
deleted file mode 100644
index 39b62c49392a..000000000000
--- a/cmd/faucet/faucet_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-package main
-
-import (
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-func TestFacebook(t *testing.T) {
- t.Parallel()
- // TODO: Remove facebook auth or implement facebook api, which seems to require an API key
- t.Skipf("The facebook access is flaky, needs to be reimplemented or removed")
- for _, tt := range []struct {
- url string
- want common.Address
- }{
- {
- "https://www.facebook.com/fooz.gazonk/posts/2837228539847129",
- common.HexToAddress("0xDeadDeaDDeaDbEefbEeFbEEfBeeFBeefBeeFbEEF"),
- },
- } {
- _, _, gotAddress, err := authFacebook(tt.url)
- if err != nil {
- t.Fatal(err)
- }
- if gotAddress != tt.want {
- t.Fatalf("address wrong, have %v want %v", gotAddress, tt.want)
- }
- }
-}
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index 027dac7bd60a..5f52f1df5442 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -35,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/eth/catalyst"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
@@ -222,7 +221,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
}
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
stack.RegisterLifecycle(simBeacon)
- } else if cfg.Eth.SyncMode != downloader.LightSync {
+ } else {
err := catalyst.Register(stack, eth)
if err != nil {
utils.Fatalf("failed to register catalyst service: %v", err)
diff --git a/cmd/geth/les_test.go b/cmd/geth/les_test.go
deleted file mode 100644
index 98c8a12dc60c..000000000000
--- a/cmd/geth/les_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-package main
-
-import (
- "context"
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-type gethrpc struct {
- name string
- rpc *rpc.Client
- geth *testgeth
- nodeInfo *p2p.NodeInfo
-}
-
-func (g *gethrpc) killAndWait() {
- g.geth.Kill()
- g.geth.WaitExit()
-}
-
-func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
- if err := g.rpc.Call(&result, method, args...); err != nil {
- g.geth.Fatalf("callRPC %v: %v", method, err)
- }
-}
-
-func (g *gethrpc) addPeer(peer *gethrpc) {
- g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
- enode := peer.getNodeInfo().Enode
- peerCh := make(chan *p2p.PeerEvent)
- sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
- if err != nil {
- g.geth.Fatalf("subscribe %v: %v", g.name, err)
- }
- defer sub.Unsubscribe()
- g.callRPC(nil, "admin_addPeer", enode)
- dur := 14 * time.Second
- timeout := time.After(dur)
- select {
- case ev := <-peerCh:
- g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
- case err := <-sub.Err():
- g.geth.Fatalf("%v sub error: %v", g.name, err)
- case <-timeout:
- g.geth.Error("timeout adding peer after", dur)
- }
-}
-
-// Use this function instead of `g.nodeInfo` directly
-func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
- if g.nodeInfo != nil {
- return g.nodeInfo
- }
- g.nodeInfo = &p2p.NodeInfo{}
- g.callRPC(&g.nodeInfo, "admin_nodeInfo")
- return g.nodeInfo
-}
-
-// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
-// account the set data folders as well as the designated platform we're currently
-// running on.
-func ipcEndpoint(ipcPath, datadir string) string {
- // On windows we can only use plain top-level pipes
- if runtime.GOOS == "windows" {
- if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
- return ipcPath
- }
- return `\\.\pipe\` + ipcPath
- }
- // Resolve names into the data directory full paths otherwise
- if filepath.Base(ipcPath) == ipcPath {
- if datadir == "" {
- return filepath.Join(os.TempDir(), ipcPath)
- }
- return filepath.Join(datadir, ipcPath)
- }
- return ipcPath
-}
-
-// nextIPC ensures that each ipc pipe gets a unique name.
-// On linux, it works well to use ipc pipes all over the filesystem (in datadirs),
-// but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
-// nodes simultaneously, we need to distinguish between them, which we do by
-// the pipe filename instead of folder.
-var nextIPC atomic.Uint32
-
-func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
- ipcName := fmt.Sprintf("geth-%d.ipc", nextIPC.Add(1))
- args = append([]string{"--networkid=42", "--port=0", "--authrpc.port", "0", "--ipcpath", ipcName}, args...)
- t.Logf("Starting %v with rpc: %v", name, args)
-
- g := &gethrpc{
- name: name,
- geth: runGeth(t, args...),
- }
- ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
- // We can't know exactly how long geth will take to start, so we try 10
- // times over a 5 second period.
- var err error
- for i := 0; i < 10; i++ {
- time.Sleep(500 * time.Millisecond)
- if g.rpc, err = rpc.Dial(ipcpath); err == nil {
- return g
- }
- }
- t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
- return nil
-}
-
-func initGeth(t *testing.T) string {
- args := []string{"--networkid=42", "init", "./testdata/clique.json"}
- t.Logf("Initializing geth: %v ", args)
- g := runGeth(t, args...)
- datadir := g.Datadir
- g.WaitExit()
- return datadir
-}
-
-func startLightServer(t *testing.T) *gethrpc {
- datadir := initGeth(t)
- t.Logf("Importing keys to geth")
- runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
- account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
- server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--miner.etherbase=0x02f0d131f1f97aef08aec6e3291b957d9efe7105", "--mine", "--light.serve=100", "--light.maxpeers=1", "--discv4=false", "--nat=extip:127.0.0.1", "--verbosity=4")
- return server
-}
-
-func startClient(t *testing.T, name string) *gethrpc {
- datadir := initGeth(t)
- return startGethWithIpc(t, name, "--datadir", datadir, "--discv4=false", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
-}
-
-func TestPriorityClient(t *testing.T) {
- t.Parallel()
- lightServer := startLightServer(t)
- defer lightServer.killAndWait()
-
- // Start client and add lightServer as peer
- freeCli := startClient(t, "freeCli")
- defer freeCli.killAndWait()
- freeCli.addPeer(lightServer)
-
- var peers []*p2p.PeerInfo
- freeCli.callRPC(&peers, "admin_peers")
- if len(peers) != 1 {
- t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
- return
- }
-
- // Set up priority client, get its nodeID, increase its balance on the lightServer
- prioCli := startClient(t, "prioCli")
- defer prioCli.killAndWait()
- // 3_000_000_000 once we move to Go 1.13
- tokens := uint64(3000000000)
- lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens)
- prioCli.addPeer(lightServer)
-
- // Check if priority client is actually syncing and the regular client got kicked out
- prioCli.callRPC(&peers, "admin_peers")
- if len(peers) != 1 {
- t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
- }
-
- nodes := map[string]*gethrpc{
- lightServer.getNodeInfo().ID: lightServer,
- freeCli.getNodeInfo().ID: freeCli,
- prioCli.getNodeInfo().ID: prioCli,
- }
- time.Sleep(1 * time.Second)
- lightServer.callRPC(&peers, "admin_peers")
- peersWithNames := make(map[string]string)
- for _, p := range peers {
- peersWithNames[nodes[p.ID].name] = p.ID
- }
- if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
- t.Error("client is still a peer of lightServer", peersWithNames)
- }
- if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
- t.Error("prio client is not among lightServer peers", peersWithNames)
- }
-}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index d1b14b81cd00..e5a17e45cf7b 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -62,7 +62,7 @@ var (
utils.MinFreeDiskSpaceFlag,
utils.KeyStoreDirFlag,
utils.ExternalSignerFlag,
- utils.NoUSBFlag,
+ utils.NoUSBFlag, // deprecated
utils.USBFlag,
utils.SmartCardDaemonPathFlag,
utils.OverrideCancun,
@@ -87,24 +87,24 @@ var (
utils.ExitWhenSyncedFlag,
utils.GCModeFlag,
utils.SnapshotFlag,
- utils.TxLookupLimitFlag,
+ utils.TxLookupLimitFlag, // deprecated
utils.TransactionHistoryFlag,
utils.StateHistoryFlag,
- utils.LightServeFlag,
- utils.LightIngressFlag,
- utils.LightEgressFlag,
- utils.LightMaxPeersFlag,
- utils.LightNoPruneFlag,
+ utils.LightServeFlag, // deprecated
+ utils.LightIngressFlag, // deprecated
+ utils.LightEgressFlag, // deprecated
+ utils.LightMaxPeersFlag, // deprecated
+ utils.LightNoPruneFlag, // deprecated
utils.LightKDFFlag,
- utils.LightNoSyncServeFlag,
+ utils.LightNoSyncServeFlag, // deprecated
utils.EthRequiredBlocksFlag,
- utils.LegacyWhitelistFlag,
+ utils.LegacyWhitelistFlag, // deprecated
utils.BloomFilterSizeFlag,
utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheTrieFlag,
- utils.CacheTrieJournalFlag,
- utils.CacheTrieRejournalFlag,
+ utils.CacheTrieJournalFlag, // deprecated
+ utils.CacheTrieRejournalFlag, // deprecated
utils.CacheGCFlag,
utils.CacheSnapshotFlag,
utils.CacheNoPrefetchFlag,
@@ -127,7 +127,7 @@ var (
utils.NoDiscoverFlag,
utils.DiscoveryV4Flag,
utils.DiscoveryV5Flag,
- utils.LegacyDiscoveryV5Flag,
+ utils.LegacyDiscoveryV5Flag, // deprecated
utils.NetrestrictFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
@@ -306,7 +306,7 @@ func prepare(ctx *cli.Context) {
log.Info("Starting Geth on Ethereum mainnet...")
}
// If we're a full node on mainnet without --cache specified, bump default cache allowance
- if ctx.String(utils.SyncModeFlag.Name) != "light" && !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
+ if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
!ctx.IsSet(utils.SepoliaFlag.Name) &&
@@ -317,11 +317,6 @@ func prepare(ctx *cli.Context) {
ctx.Set(utils.CacheFlag.Name, strconv.Itoa(4096))
}
}
- // If we're running a light client on any network, drop the cache to some meaningfully low amount
- if ctx.String(utils.SyncModeFlag.Name) == "light" && !ctx.IsSet(utils.CacheFlag.Name) {
- log.Info("Dropping default light client cache", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 128)
- ctx.Set(utils.CacheFlag.Name, strconv.Itoa(128))
- }
// Start metrics export if enabled
utils.SetupMetrics(ctx)
diff --git a/cmd/geth/run_test.go b/cmd/geth/run_test.go
index 2e03dc5eaae5..1d32880325d4 100644
--- a/cmd/geth/run_test.go
+++ b/cmd/geth/run_test.go
@@ -55,6 +55,15 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
+func initGeth(t *testing.T) string {
+ args := []string{"--networkid=42", "init", "./testdata/clique.json"}
+ t.Logf("Initializing geth: %v ", args)
+ g := runGeth(t, args...)
+ datadir := g.Datadir
+ g.WaitExit()
+ return datadir
+}
+
// spawns geth with the given command line args. If the args don't set --datadir, the
// child g gets a temporary data directory.
func runGeth(t *testing.T, args ...string) *testgeth {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 72a56e9c28c9..b49c7c36d5a7 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -57,7 +57,6 @@ import (
"github.com/ethereum/go-ethereum/graphql"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
- "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/exp"
@@ -255,7 +254,7 @@ var (
}
SyncModeFlag = &flags.TextMarshalerFlag{
Name: "syncmode",
- Usage: `Blockchain sync mode ("snap", "full" or "light")`,
+ Usage: `Blockchain sync mode ("snap" or "full")`,
Value: &defaultSyncMode,
Category: flags.StateCategory,
}
@@ -282,41 +281,6 @@ var (
Value: ethconfig.Defaults.TransactionHistory,
Category: flags.StateCategory,
}
- // Light server and client settings
- LightServeFlag = &cli.IntFlag{
- Name: "light.serve",
- Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)",
- Value: ethconfig.Defaults.LightServ,
- Category: flags.LightCategory,
- }
- LightIngressFlag = &cli.IntFlag{
- Name: "light.ingress",
- Usage: "Incoming bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
- Value: ethconfig.Defaults.LightIngress,
- Category: flags.LightCategory,
- }
- LightEgressFlag = &cli.IntFlag{
- Name: "light.egress",
- Usage: "Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
- Value: ethconfig.Defaults.LightEgress,
- Category: flags.LightCategory,
- }
- LightMaxPeersFlag = &cli.IntFlag{
- Name: "light.maxpeers",
- Usage: "Maximum number of light clients to serve, or light servers to attach to",
- Value: ethconfig.Defaults.LightPeers,
- Category: flags.LightCategory,
- }
- LightNoPruneFlag = &cli.BoolFlag{
- Name: "light.nopruning",
- Usage: "Disable ancient light chain data pruning",
- Category: flags.LightCategory,
- }
- LightNoSyncServeFlag = &cli.BoolFlag{
- Name: "light.nosyncserve",
- Usage: "Enables serving light clients before syncing",
- Category: flags.LightCategory,
- }
// Transaction pool settings
TxPoolLocalsFlag = &cli.StringFlag{
Name: "txpool.locals",
@@ -1224,25 +1188,25 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
}
}
-// setLes configures the les server and ultra light client settings from the command line flags.
+// setLes shows the deprecation warnings for LES flags.
func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
if ctx.IsSet(LightServeFlag.Name) {
- cfg.LightServ = ctx.Int(LightServeFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightServeFlag.Name)
}
if ctx.IsSet(LightIngressFlag.Name) {
- cfg.LightIngress = ctx.Int(LightIngressFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightIngressFlag.Name)
}
if ctx.IsSet(LightEgressFlag.Name) {
- cfg.LightEgress = ctx.Int(LightEgressFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightEgressFlag.Name)
}
if ctx.IsSet(LightMaxPeersFlag.Name) {
- cfg.LightPeers = ctx.Int(LightMaxPeersFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightMaxPeersFlag.Name)
}
if ctx.IsSet(LightNoPruneFlag.Name) {
- cfg.LightNoPrune = ctx.Bool(LightNoPruneFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoPruneFlag.Name)
}
if ctx.IsSet(LightNoSyncServeFlag.Name) {
- cfg.LightNoSyncServe = ctx.Bool(LightNoSyncServeFlag.Name)
+ log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoSyncServeFlag.Name)
}
}
@@ -1340,58 +1304,24 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setBootstrapNodes(ctx, cfg)
setBootstrapNodesV5(ctx, cfg)
- lightClient := ctx.String(SyncModeFlag.Name) == "light"
- lightServer := (ctx.Int(LightServeFlag.Name) != 0)
-
- lightPeers := ctx.Int(LightMaxPeersFlag.Name)
- if lightClient && !ctx.IsSet(LightMaxPeersFlag.Name) {
- // dynamic default - for clients we use 1/10th of the default for servers
- lightPeers /= 10
- }
-
if ctx.IsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.Int(MaxPeersFlag.Name)
- if lightServer && !ctx.IsSet(LightMaxPeersFlag.Name) {
- cfg.MaxPeers += lightPeers
- }
- } else {
- if lightServer {
- cfg.MaxPeers += lightPeers
- }
- if lightClient && ctx.IsSet(LightMaxPeersFlag.Name) && cfg.MaxPeers < lightPeers {
- cfg.MaxPeers = lightPeers
- }
- }
- if !(lightClient || lightServer) {
- lightPeers = 0
}
- ethPeers := cfg.MaxPeers - lightPeers
- if lightClient {
- ethPeers = 0
- }
- log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)
+ ethPeers := cfg.MaxPeers
+ log.Info("Maximum peer count", "ETH", ethPeers, "total", cfg.MaxPeers)
if ctx.IsSet(MaxPendingPeersFlag.Name) {
cfg.MaxPendingPeers = ctx.Int(MaxPendingPeersFlag.Name)
}
- if ctx.IsSet(NoDiscoverFlag.Name) || lightClient {
+ if ctx.IsSet(NoDiscoverFlag.Name) {
cfg.NoDiscovery = true
}
- // Disallow --nodiscover when used in conjunction with light mode.
- if (lightClient || lightServer) && ctx.Bool(NoDiscoverFlag.Name) {
- Fatalf("Cannot use --" + NoDiscoverFlag.Name + " in light client or light server mode")
- }
CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
cfg.DiscoveryV4 = ctx.Bool(DiscoveryV4Flag.Name)
cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name)
- // If we're running a light client or server, force enable the v5 peer discovery.
- if lightClient || lightServer {
- cfg.DiscoveryV5 = true
- }
-
if netrestrict := ctx.String(NetrestrictFlag.Name); netrestrict != "" {
list, err := netutil.ParseNetlist(netrestrict)
if err != nil {
@@ -1496,12 +1426,7 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
}
}
-func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
- // If we are running the light client, apply another group
- // settings for gas oracle.
- if light {
- *cfg = ethconfig.LightClientGPO
- }
+func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
if ctx.IsSet(GpoBlocksFlag.Name) {
cfg.Blocks = ctx.Int(GpoBlocksFlag.Name)
}
@@ -1650,12 +1575,11 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
- CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
// Set configurations from CLI flags
setEtherbase(ctx, cfg)
- setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light")
+ setGPO(ctx, &cfg.GPO)
setTxPool(ctx, &cfg.TxPool)
setMiner(ctx, &cfg.Miner)
setRequiredBlocks(ctx, cfg)
@@ -1734,9 +1658,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.TransactionHistory = 0
log.Warn("Disabled transaction unindexing for archive node")
}
- if ctx.IsSet(LightServeFlag.Name) && cfg.TransactionHistory != 0 {
- log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited")
- }
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
}
@@ -1913,9 +1834,6 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
return // already set through flags/config
}
protocol := "all"
- if cfg.SyncMode == downloader.LightSync {
- protocol = "les"
- }
if url := params.KnownDNSNetwork(genesis, protocol); url != "" {
cfg.EthDiscoveryURLs = []string{url}
cfg.SnapDiscoveryURLs = cfg.EthDiscoveryURLs
@@ -1923,27 +1841,12 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
}
// RegisterEthService adds an Ethereum client to the stack.
-// The second return value is the full node instance, which may be nil if the
-// node is running as a light client.
+// The second return value is the full node instance.
func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) {
- if cfg.SyncMode == downloader.LightSync {
- backend, err := les.New(stack, cfg)
- if err != nil {
- Fatalf("Failed to register the Ethereum service: %v", err)
- }
- stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
- return backend.ApiBackend, nil
- }
backend, err := eth.New(stack, cfg)
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
- if cfg.LightServ > 0 {
- _, err := les.NewLesServer(stack, backend, cfg)
- if err != nil {
- Fatalf("Failed to create the LES server: %v", err)
- }
- }
stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
return backend.APIBackend, backend
}
@@ -1965,13 +1868,12 @@ func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, filterSyst
// RegisterFilterAPI adds the eth log filtering RPC API to the node.
func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconfig.Config) *filters.FilterSystem {
- isLightClient := ethcfg.SyncMode == downloader.LightSync
filterSystem := filters.NewFilterSystem(backend, filters.Config{
LogCacheSize: ethcfg.FilterLogCacheSize,
})
stack.RegisterAPIs([]rpc.API{{
Namespace: "eth",
- Service: filters.NewFilterAPI(filterSystem, isLightClient),
+ Service: filters.NewFilterAPI(filterSystem, false),
}})
return filterSystem
}
diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go
index 6669ff176fdd..00237fecaf27 100644
--- a/cmd/utils/flags_legacy.go
+++ b/cmd/utils/flags_legacy.go
@@ -39,6 +39,12 @@ var DeprecatedFlags = []cli.Flag{
CacheTrieRejournalFlag,
LegacyDiscoveryV5Flag,
TxLookupLimitFlag,
+ LightServeFlag,
+ LightIngressFlag,
+ LightEgressFlag,
+ LightMaxPeersFlag,
+ LightNoPruneFlag,
+ LightNoSyncServeFlag,
}
var (
@@ -77,6 +83,41 @@ var (
Value: ethconfig.Defaults.TransactionHistory,
Category: flags.DeprecatedCategory,
}
+ // Light server and client settings, Deprecated November 2023
+ LightServeFlag = &cli.IntFlag{
+ Name: "light.serve",
+ Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
+ Value: ethconfig.Defaults.LightServ,
+ Category: flags.LightCategory,
+ }
+ LightIngressFlag = &cli.IntFlag{
+ Name: "light.ingress",
+ Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
+ Value: ethconfig.Defaults.LightIngress,
+ Category: flags.LightCategory,
+ }
+ LightEgressFlag = &cli.IntFlag{
+ Name: "light.egress",
+ Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
+ Value: ethconfig.Defaults.LightEgress,
+ Category: flags.LightCategory,
+ }
+ LightMaxPeersFlag = &cli.IntFlag{
+ Name: "light.maxpeers",
+ Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
+ Value: ethconfig.Defaults.LightPeers,
+ Category: flags.LightCategory,
+ }
+ LightNoPruneFlag = &cli.BoolFlag{
+ Name: "light.nopruning",
+ Usage: "Disable ancient light chain data pruning (deprecated)",
+ Category: flags.LightCategory,
+ }
+ LightNoSyncServeFlag = &cli.BoolFlag{
+ Name: "light.nosyncserve",
+ Usage: "Enables serving light clients before syncing (deprecated)",
+ Category: flags.LightCategory,
+ }
)
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
diff --git a/eth/backend.go b/eth/backend.go
index 09559f0ac116..774ffaf24877 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -108,7 +108,7 @@ type Ethereum struct {
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// Ensure configuration values are compatible and sane
if config.SyncMode == downloader.LightSync {
- return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
+ return nil, errors.New("can't run eth.Ethereum in light sync mode, light mode has been deprecated")
}
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 5e8f58efdb35..ad664afb5bd1 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -46,16 +46,6 @@ var FullNodeGPO = gasprice.Config{
IgnorePrice: gasprice.DefaultIgnorePrice,
}
-// LightClientGPO contains default gasprice oracle settings for light client.
-var LightClientGPO = gasprice.Config{
- Blocks: 2,
- Percentile: 60,
- MaxHeaderHistory: 300,
- MaxBlockHistory: 5,
- MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
-}
-
// Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{
SyncMode: downloader.SnapSync,
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 84a672280620..75d0faac54e9 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -38,7 +38,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
ethproto "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
@@ -486,7 +485,7 @@ func (s *Service) login(conn *connWrapper) error {
if info := infos.Protocols["eth"]; info != nil {
network = fmt.Sprintf("%d", info.(*ethproto.NodeInfo).Network)
} else {
- network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network)
+ return errors.New("no eth protocol available")
}
auth := &authMsg{
ID: s.node,
diff --git a/les/api.go b/les/api.go
deleted file mode 100644
index e8490f7b0fd2..000000000000
--- a/les/api.go
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "errors"
- "fmt"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- vfs "github.com/ethereum/go-ethereum/les/vflux/server"
- "github.com/ethereum/go-ethereum/p2p/enode"
-)
-
-var errUnknownBenchmarkType = errors.New("unknown benchmark type")
-
-// LightServerAPI provides an API to access the LES light server.
-type LightServerAPI struct {
- server *LesServer
- defaultPosFactors, defaultNegFactors vfs.PriceFactors
-}
-
-// NewLightServerAPI creates a new LES light server API.
-func NewLightServerAPI(server *LesServer) *LightServerAPI {
- return &LightServerAPI{
- server: server,
- defaultPosFactors: defaultPosFactors,
- defaultNegFactors: defaultNegFactors,
- }
-}
-
-// parseNode parses either an enode address a raw hex node id
-func parseNode(node string) (enode.ID, error) {
- if id, err := enode.ParseID(node); err == nil {
- return id, nil
- }
- if node, err := enode.Parse(enode.ValidSchemes, node); err == nil {
- return node.ID(), nil
- } else {
- return enode.ID{}, err
- }
-}
-
-// ServerInfo returns global server parameters
-func (api *LightServerAPI) ServerInfo() map[string]interface{} {
- res := make(map[string]interface{})
- res["minimumCapacity"] = api.server.minCapacity
- res["maximumCapacity"] = api.server.maxCapacity
- _, res["totalCapacity"] = api.server.clientPool.Limits()
- _, res["totalConnectedCapacity"] = api.server.clientPool.Active()
- res["priorityConnectedCapacity"] = 0 //TODO connect when token sale module is added
- return res
-}
-
-// ClientInfo returns information about clients listed in the ids list or matching the given tags
-func (api *LightServerAPI) ClientInfo(nodes []string) map[enode.ID]map[string]interface{} {
- var ids []enode.ID
- for _, node := range nodes {
- if id, err := parseNode(node); err == nil {
- ids = append(ids, id)
- }
- }
-
- res := make(map[enode.ID]map[string]interface{})
- if len(ids) == 0 {
- ids = api.server.peers.ids()
- }
- for _, id := range ids {
- if peer := api.server.peers.peer(id); peer != nil {
- res[id] = api.clientInfo(peer, peer.balance)
- } else {
- api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) {
- res[id] = api.clientInfo(nil, balance)
- })
- }
- }
- return res
-}
-
-// PriorityClientInfo returns information about clients with a positive balance
-// in the given ID range (stop excluded). If stop is null then the iterator stops
-// only at the end of the ID space. MaxCount limits the number of results returned.
-// If maxCount limit is applied but there are more potential results then the ID
-// of the next potential result is included in the map with an empty structure
-// assigned to it.
-func (api *LightServerAPI) PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{} {
- res := make(map[enode.ID]map[string]interface{})
- ids := api.server.clientPool.GetPosBalanceIDs(start, stop, maxCount+1)
- if len(ids) > maxCount {
- res[ids[maxCount]] = make(map[string]interface{})
- ids = ids[:maxCount]
- }
- for _, id := range ids {
- if peer := api.server.peers.peer(id); peer != nil {
- res[id] = api.clientInfo(peer, peer.balance)
- } else {
- api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) {
- res[id] = api.clientInfo(nil, balance)
- })
- }
- }
- return res
-}
-
-// clientInfo creates a client info data structure
-func (api *LightServerAPI) clientInfo(peer *clientPeer, balance vfs.ReadOnlyBalance) map[string]interface{} {
- info := make(map[string]interface{})
- pb, nb := balance.GetBalance()
- info["isConnected"] = peer != nil
- info["pricing/balance"] = pb
- info["priority"] = pb != 0
- // cb := api.server.clientPool.ndb.getCurrencyBalance(id)
- // info["pricing/currency"] = cb.amount
- if peer != nil {
- info["connectionTime"] = float64(mclock.Now()-peer.connectedAt) / float64(time.Second)
- info["capacity"] = peer.getCapacity()
- info["pricing/negBalance"] = nb
- }
- return info
-}
-
-// setParams either sets the given parameters for a single connected client (if specified)
-// or the default parameters applicable to clients connected in the future
-func (api *LightServerAPI) setParams(params map[string]interface{}, client *clientPeer, posFactors, negFactors *vfs.PriceFactors) (updateFactors bool, err error) {
- defParams := client == nil
- for name, value := range params {
- errValue := func() error {
- return fmt.Errorf("invalid value for parameter '%s'", name)
- }
- setFactor := func(v *float64) {
- if val, ok := value.(float64); ok && val >= 0 {
- *v = val / float64(time.Second)
- updateFactors = true
- } else {
- err = errValue()
- }
- }
-
- switch {
- case name == "pricing/timeFactor":
- setFactor(&posFactors.TimeFactor)
- case name == "pricing/capacityFactor":
- setFactor(&posFactors.CapacityFactor)
- case name == "pricing/requestCostFactor":
- setFactor(&posFactors.RequestFactor)
- case name == "pricing/negative/timeFactor":
- setFactor(&negFactors.TimeFactor)
- case name == "pricing/negative/capacityFactor":
- setFactor(&negFactors.CapacityFactor)
- case name == "pricing/negative/requestCostFactor":
- setFactor(&negFactors.RequestFactor)
- case !defParams && name == "capacity":
- if capacity, ok := value.(float64); ok && uint64(capacity) >= api.server.minCapacity {
- _, err = api.server.clientPool.SetCapacity(client.Node(), uint64(capacity), 0, false)
- // time factor recalculation is performed automatically by the balance tracker
- } else {
- err = errValue()
- }
- default:
- if defParams {
- err = fmt.Errorf("invalid default parameter '%s'", name)
- } else {
- err = fmt.Errorf("invalid client parameter '%s'", name)
- }
- }
- if err != nil {
- return
- }
- }
- return
-}
-
-// SetClientParams sets client parameters for all clients listed in the ids list
-// or all connected clients if the list is empty
-func (api *LightServerAPI) SetClientParams(nodes []string, params map[string]interface{}) error {
- var err error
- for _, node := range nodes {
- var id enode.ID
- if id, err = parseNode(node); err != nil {
- return err
- }
- if peer := api.server.peers.peer(id); peer != nil {
- posFactors, negFactors := peer.balance.GetPriceFactors()
- update, e := api.setParams(params, peer, &posFactors, &negFactors)
- if update {
- peer.balance.SetPriceFactors(posFactors, negFactors)
- }
- if e != nil {
- err = e
- }
- } else {
- err = fmt.Errorf("client %064x is not connected", id)
- }
- }
- return err
-}
-
-// SetDefaultParams sets the default parameters applicable to clients connected in the future
-func (api *LightServerAPI) SetDefaultParams(params map[string]interface{}) error {
- update, err := api.setParams(params, nil, &api.defaultPosFactors, &api.defaultNegFactors)
- if update {
- api.server.clientPool.SetDefaultFactors(api.defaultPosFactors, api.defaultNegFactors)
- }
- return err
-}
-
-// SetConnectedBias set the connection bias, which is applied to already connected clients
-// So that already connected client won't be kicked out very soon and we can ensure all
-// connected clients can have enough time to request or sync some data.
-// When the input parameter `bias` < 0 (illegal), return error.
-func (api *LightServerAPI) SetConnectedBias(bias time.Duration) error {
- if bias < time.Duration(0) {
- return fmt.Errorf("bias illegal: %v less than 0", bias)
- }
- api.server.clientPool.SetConnectedBias(bias)
- return nil
-}
-
-// AddBalance adds the given amount to the balance of a client if possible and returns
-// the balance before and after the operation
-func (api *LightServerAPI) AddBalance(node string, amount int64) (balance [2]uint64, err error) {
- var id enode.ID
- if id, err = parseNode(node); err != nil {
- return
- }
- api.server.clientPool.BalanceOperation(id, "", func(nb vfs.AtomicBalanceOperator) {
- balance[0], balance[1], err = nb.AddBalance(amount)
- })
- return
-}
-
-// Benchmark runs a request performance benchmark with a given set of measurement setups
-// in multiple passes specified by passCount. The measurement time for each setup in each
-// pass is specified in milliseconds by length.
-//
-// Note: measurement time is adjusted for each pass depending on the previous ones.
-// Therefore a controlled total measurement time is achievable in multiple passes.
-func (api *LightServerAPI) Benchmark(setups []map[string]interface{}, passCount, length int) ([]map[string]interface{}, error) {
- benchmarks := make([]requestBenchmark, len(setups))
- for i, setup := range setups {
- if t, ok := setup["type"].(string); ok {
- getInt := func(field string, def int) int {
- if value, ok := setup[field].(float64); ok {
- return int(value)
- }
- return def
- }
- getBool := func(field string, def bool) bool {
- if value, ok := setup[field].(bool); ok {
- return value
- }
- return def
- }
- switch t {
- case "header":
- benchmarks[i] = &benchmarkBlockHeaders{
- amount: getInt("amount", 1),
- skip: getInt("skip", 1),
- byHash: getBool("byHash", false),
- reverse: getBool("reverse", false),
- }
- case "body":
- benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: false}
- case "receipts":
- benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: true}
- case "proof":
- benchmarks[i] = &benchmarkProofsOrCode{code: false}
- case "code":
- benchmarks[i] = &benchmarkProofsOrCode{code: true}
- case "cht":
- benchmarks[i] = &benchmarkHelperTrie{
- bloom: false,
- reqCount: getInt("amount", 1),
- }
- case "bloom":
- benchmarks[i] = &benchmarkHelperTrie{
- bloom: true,
- reqCount: getInt("amount", 1),
- }
- case "txSend":
- benchmarks[i] = &benchmarkTxSend{}
- case "txStatus":
- benchmarks[i] = &benchmarkTxStatus{}
- default:
- return nil, errUnknownBenchmarkType
- }
- } else {
- return nil, errUnknownBenchmarkType
- }
- }
- rs := api.server.handler.runBenchmark(benchmarks, passCount, time.Millisecond*time.Duration(length))
- result := make([]map[string]interface{}, len(setups))
- for i, r := range rs {
- res := make(map[string]interface{})
- if r.err == nil {
- res["totalCount"] = r.totalCount
- res["avgTime"] = r.avgTime
- res["maxInSize"] = r.maxInSize
- res["maxOutSize"] = r.maxOutSize
- } else {
- res["error"] = r.err.Error()
- }
- result[i] = res
- }
- return result, nil
-}
-
-// DebugAPI provides an API to debug LES light server functionality.
-type DebugAPI struct {
- server *LesServer
-}
-
-// NewDebugAPI creates a new LES light server debug API.
-func NewDebugAPI(server *LesServer) *DebugAPI {
- return &DebugAPI{
- server: server,
- }
-}
-
-// FreezeClient forces a temporary client freeze which normally happens when the server is overloaded
-func (api *DebugAPI) FreezeClient(node string) error {
- var (
- id enode.ID
- err error
- )
- if id, err = parseNode(node); err != nil {
- return err
- }
- if peer := api.server.peers.peer(id); peer != nil {
- peer.freeze()
- return nil
- } else {
- return fmt.Errorf("client %064x is not connected", id[:])
- }
-}
diff --git a/les/api_backend.go b/les/api_backend.go
deleted file mode 100644
index 3e9dbadce86b..000000000000
--- a/les/api_backend.go
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "context"
- "errors"
- "math/big"
- "time"
-
- "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/bloombits"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/gasprice"
- "github.com/ethereum/go-ethereum/eth/tracers"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-type LesApiBackend struct {
- extRPCEnabled bool
- allowUnprotectedTxs bool
- eth *LightEthereum
- gpo *gasprice.Oracle
-}
-
-func (b *LesApiBackend) ChainConfig() *params.ChainConfig {
- return b.eth.chainConfig
-}
-
-func (b *LesApiBackend) CurrentBlock() *types.Header {
- return b.eth.BlockChain().CurrentHeader()
-}
-
-func (b *LesApiBackend) SetHead(number uint64) {
- b.eth.blockchain.SetHead(number)
-}
-
-func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
- // Return the latest current as the pending one since there
- // is no pending notion in the light client. TODO(rjl493456442)
- // unify the behavior of `HeaderByNumber` and `PendingBlockAndReceipts`.
- if number == rpc.PendingBlockNumber {
- return b.eth.blockchain.CurrentHeader(), nil
- }
- if number == rpc.LatestBlockNumber {
- return b.eth.blockchain.CurrentHeader(), nil
- }
- return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number))
-}
-
-func (b *LesApiBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
- if blockNr, ok := blockNrOrHash.Number(); ok {
- return b.HeaderByNumber(ctx, blockNr)
- }
- if hash, ok := blockNrOrHash.Hash(); ok {
- header, err := b.HeaderByHash(ctx, hash)
- if err != nil {
- return nil, err
- }
- if header == nil {
- return nil, errors.New("header for hash not found")
- }
- if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
- return nil, errors.New("hash is not currently canonical")
- }
- return header, nil
- }
- return nil, errors.New("invalid arguments; neither block nor hash specified")
-}
-
-func (b *LesApiBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
- return b.eth.blockchain.GetHeaderByHash(hash), nil
-}
-
-func (b *LesApiBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
- header, err := b.HeaderByNumber(ctx, number)
- if header == nil || err != nil {
- return nil, err
- }
- return b.BlockByHash(ctx, header.Hash())
-}
-
-func (b *LesApiBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- return b.eth.blockchain.GetBlockByHash(ctx, hash)
-}
-
-func (b *LesApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
- if blockNr, ok := blockNrOrHash.Number(); ok {
- return b.BlockByNumber(ctx, blockNr)
- }
- if hash, ok := blockNrOrHash.Hash(); ok {
- block, err := b.BlockByHash(ctx, hash)
- if err != nil {
- return nil, err
- }
- if block == nil {
- return nil, errors.New("header found, but block body is missing")
- }
- if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(block.NumberU64()) != hash {
- return nil, errors.New("hash is not currently canonical")
- }
- return block, nil
- }
- return nil, errors.New("invalid arguments; neither block nor hash specified")
-}
-
-func (b *LesApiBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
- return light.GetBody(ctx, b.eth.odr, hash, uint64(number))
-}
-
-func (b *LesApiBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
- return nil, nil
-}
-
-func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
- header, err := b.HeaderByNumber(ctx, number)
- if err != nil {
- return nil, nil, err
- }
- if header == nil {
- return nil, nil, errors.New("header not found")
- }
- return light.NewState(ctx, header, b.eth.odr), header, nil
-}
-
-func (b *LesApiBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
- if blockNr, ok := blockNrOrHash.Number(); ok {
- return b.StateAndHeaderByNumber(ctx, blockNr)
- }
- if hash, ok := blockNrOrHash.Hash(); ok {
- header := b.eth.blockchain.GetHeaderByHash(hash)
- if header == nil {
- return nil, nil, errors.New("header for hash not found")
- }
- if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
- return nil, nil, errors.New("hash is not currently canonical")
- }
- return light.NewState(ctx, header, b.eth.odr), header, nil
- }
- return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
-}
-
-func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
- if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil {
- return light.GetBlockReceipts(ctx, b.eth.odr, hash, *number)
- }
- return nil, nil
-}
-
-func (b *LesApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
- return light.GetBlockLogs(ctx, b.eth.odr, hash, number)
-}
-
-func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
- if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil {
- return b.eth.blockchain.GetTdOdr(ctx, hash, *number)
- }
- return nil
-}
-
-func (b *LesApiBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error) {
- if vmConfig == nil {
- vmConfig = new(vm.Config)
- }
- txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
- if blockCtx != nil {
- context = *blockCtx
- }
- return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error
-}
-
-func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
- return b.eth.txPool.Add(ctx, signedTx)
-}
-
-func (b *LesApiBackend) RemoveTx(txHash common.Hash) {
- b.eth.txPool.RemoveTx(txHash)
-}
-
-func (b *LesApiBackend) GetPoolTransactions() (types.Transactions, error) {
- return b.eth.txPool.GetTransactions()
-}
-
-func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
- return b.eth.txPool.GetTransaction(txHash)
-}
-
-func (b *LesApiBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
- return light.GetTransaction(ctx, b.eth.odr, txHash)
-}
-
-func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
- return b.eth.txPool.GetNonce(ctx, addr)
-}
-
-func (b *LesApiBackend) Stats() (pending int, queued int) {
- return b.eth.txPool.Stats(), 0
-}
-
-func (b *LesApiBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
- return b.eth.txPool.Content()
-}
-
-func (b *LesApiBackend) TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) {
- return b.eth.txPool.ContentFrom(addr)
-}
-
-func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return b.eth.txPool.SubscribeNewTxsEvent(ch)
-}
-
-func (b *LesApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
- return b.eth.blockchain.SubscribeChainEvent(ch)
-}
-
-func (b *LesApiBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
- return b.eth.blockchain.SubscribeChainHeadEvent(ch)
-}
-
-func (b *LesApiBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
- return b.eth.blockchain.SubscribeChainSideEvent(ch)
-}
-
-func (b *LesApiBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return b.eth.blockchain.SubscribeLogsEvent(ch)
-}
-
-func (b *LesApiBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return event.NewSubscription(func(quit <-chan struct{}) error {
- <-quit
- return nil
- })
-}
-
-func (b *LesApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
- return b.eth.blockchain.SubscribeRemovedLogsEvent(ch)
-}
-
-func (b *LesApiBackend) SyncProgress() ethereum.SyncProgress {
- return ethereum.SyncProgress{}
-}
-
-func (b *LesApiBackend) ProtocolVersion() int {
- return b.eth.LesVersion() + 10000
-}
-
-func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
- return b.gpo.SuggestTipCap(ctx)
-}
-
-func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
- return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
-}
-
-func (b *LesApiBackend) ChainDb() ethdb.Database {
- return b.eth.chainDb
-}
-
-func (b *LesApiBackend) AccountManager() *accounts.Manager {
- return b.eth.accountManager
-}
-
-func (b *LesApiBackend) ExtRPCEnabled() bool {
- return b.extRPCEnabled
-}
-
-func (b *LesApiBackend) UnprotectedAllowed() bool {
- return b.allowUnprotectedTxs
-}
-
-func (b *LesApiBackend) RPCGasCap() uint64 {
- return b.eth.config.RPCGasCap
-}
-
-func (b *LesApiBackend) RPCEVMTimeout() time.Duration {
- return b.eth.config.RPCEVMTimeout
-}
-
-func (b *LesApiBackend) RPCTxFeeCap() float64 {
- return b.eth.config.RPCTxFeeCap
-}
-
-func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
- if b.eth.bloomIndexer == nil {
- return 0, 0
- }
- sections, _, _ := b.eth.bloomIndexer.Sections()
- return params.BloomBitsBlocksClient, sections
-}
-
-func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
- for i := 0; i < bloomFilterThreads; i++ {
- go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
- }
-}
-
-func (b *LesApiBackend) Engine() consensus.Engine {
- return b.eth.engine
-}
-
-func (b *LesApiBackend) CurrentHeader() *types.Header {
- return b.eth.blockchain.CurrentHeader()
-}
-
-func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
- return b.eth.stateAtBlock(ctx, block, reexec)
-}
-
-func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
- return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
-}
diff --git a/les/api_test.go b/les/api_test.go
deleted file mode 100644
index 484c95504c0d..000000000000
--- a/les/api_test.go
+++ /dev/null
@@ -1,512 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "context"
- crand "crypto/rand"
- "errors"
- "flag"
- "math/rand"
- "os"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/simulations"
- "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/mattn/go-colorable"
-)
-
-// Additional command line flags for the test binary.
-var (
- loglevel = flag.Int("loglevel", 0, "verbosity of logs")
- simAdapter = flag.String("adapter", "exec", "type of simulation: sim|socket|exec|docker")
-)
-
-func TestMain(m *testing.M) {
- flag.Parse()
- log.PrintOrigins(true)
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
- // register the Delivery service which will run as a devp2p
- // protocol when using the exec adapter
- adapters.RegisterLifecycles(services)
- os.Exit(m.Run())
-}
-
-// This test is not meant to be a part of the automatic testing process because it
-// runs for a long time and also requires a large database in order to do a meaningful
-// request performance test. When testServerDataDir is empty, the test is skipped.
-
-const (
- testServerDataDir = "" // should always be empty on the master branch
- testServerCapacity = 200
- testMaxClients = 10
- testTolerance = 0.1
- minRelCap = 0.2
-)
-
-func TestCapacityAPI3(t *testing.T) {
- testCapacityAPI(t, 3)
-}
-
-func TestCapacityAPI6(t *testing.T) {
- testCapacityAPI(t, 6)
-}
-
-func TestCapacityAPI10(t *testing.T) {
- testCapacityAPI(t, 10)
-}
-
-// testCapacityAPI runs an end-to-end simulation test connecting one server with
-// a given number of clients. It sets different priority capacities to all clients
-// except a randomly selected one which runs in free client mode. All clients send
-// similar requests at the maximum allowed rate and the test verifies whether the
-// ratio of processed requests is close enough to the ratio of assigned capacities.
-// Running multiple rounds with different settings ensures that changing capacity
-// while connected and going back and forth between free and priority mode with
-// the supplied API calls is also thoroughly tested.
-func testCapacityAPI(t *testing.T, clientCount int) {
- // Skip test if no data dir specified
- if testServerDataDir == "" {
- return
- }
- for !testSim(t, 1, clientCount, []string{testServerDataDir}, nil, func(ctx context.Context, net *simulations.Network, servers []*simulations.Node, clients []*simulations.Node) bool {
- if len(servers) != 1 {
- t.Fatalf("Invalid number of servers: %d", len(servers))
- }
- server := servers[0]
-
- serverRpcClient, err := server.Client()
- if err != nil {
- t.Fatalf("Failed to obtain rpc client: %v", err)
- }
- headNum, headHash := getHead(ctx, t, serverRpcClient)
- minCap, totalCap := getCapacityInfo(ctx, t, serverRpcClient)
- testCap := totalCap * 3 / 4
- t.Logf("Server testCap: %d minCap: %d head number: %d head hash: %064x\n", testCap, minCap, headNum, headHash)
- reqMinCap := uint64(float64(testCap) * minRelCap / (minRelCap + float64(len(clients)-1)))
- if minCap > reqMinCap {
- t.Fatalf("Minimum client capacity (%d) bigger than required minimum for this test (%d)", minCap, reqMinCap)
- }
- freeIdx := rand.Intn(len(clients))
-
- clientRpcClients := make([]*rpc.Client, len(clients))
- for i, client := range clients {
- var err error
- clientRpcClients[i], err = client.Client()
- if err != nil {
- t.Fatalf("Failed to obtain rpc client: %v", err)
- }
- t.Log("connecting client", i)
- if i != freeIdx {
- setCapacity(ctx, t, serverRpcClient, client.ID(), testCap/uint64(len(clients)))
- }
- net.Connect(client.ID(), server.ID())
-
- for {
- select {
- case <-ctx.Done():
- t.Fatalf("Timeout")
- default:
- }
- num, hash := getHead(ctx, t, clientRpcClients[i])
- if num == headNum && hash == headHash {
- t.Log("client", i, "synced")
- break
- }
- time.Sleep(time.Millisecond * 200)
- }
- }
-
- var wg sync.WaitGroup
- stop := make(chan struct{})
-
- reqCount := make([]atomic.Uint64, len(clientRpcClients))
-
- // Send light request like crazy.
- for i, c := range clientRpcClients {
- wg.Add(1)
- i, c := i, c
- go func() {
- defer wg.Done()
-
- queue := make(chan struct{}, 100)
- reqCount[i].Store(0)
- for {
- select {
- case queue <- struct{}{}:
- select {
- case <-stop:
- return
- case <-ctx.Done():
- return
- default:
- wg.Add(1)
- go func() {
- ok := testRequest(ctx, t, c)
- wg.Done()
- <-queue
- if ok {
- if reqCount[i].Add(1)%10000 == 0 {
- freezeClient(ctx, t, serverRpcClient, clients[i].ID())
- }
- }
- }()
- }
- case <-stop:
- return
- case <-ctx.Done():
- return
- }
- }
- }()
- }
-
- processedSince := func(start []uint64) []uint64 {
- res := make([]uint64, len(reqCount))
- for i := range reqCount {
- res[i] = reqCount[i].Load()
- if start != nil {
- res[i] -= start[i]
- }
- }
- return res
- }
-
- weights := make([]float64, len(clients))
- for c := 0; c < 5; c++ {
- setCapacity(ctx, t, serverRpcClient, clients[freeIdx].ID(), minCap)
- freeIdx = rand.Intn(len(clients))
- var sum float64
- for i := range clients {
- if i == freeIdx {
- weights[i] = 0
- } else {
- weights[i] = rand.Float64()*(1-minRelCap) + minRelCap
- }
- sum += weights[i]
- }
- for i, client := range clients {
- weights[i] *= float64(testCap-minCap-100) / sum
- capacity := uint64(weights[i])
- if i != freeIdx && capacity < getCapacity(ctx, t, serverRpcClient, client.ID()) {
- setCapacity(ctx, t, serverRpcClient, client.ID(), capacity)
- }
- }
- setCapacity(ctx, t, serverRpcClient, clients[freeIdx].ID(), 0)
- for i, client := range clients {
- capacity := uint64(weights[i])
- if i != freeIdx && capacity > getCapacity(ctx, t, serverRpcClient, client.ID()) {
- setCapacity(ctx, t, serverRpcClient, client.ID(), capacity)
- }
- }
- weights[freeIdx] = float64(minCap)
- for i := range clients {
- weights[i] /= float64(testCap)
- }
-
- time.Sleep(flowcontrol.DecParamDelay)
- t.Log("Starting measurement")
- t.Logf("Relative weights:")
- for i := range clients {
- t.Logf(" %f", weights[i])
- }
- t.Log()
- start := processedSince(nil)
- for {
- select {
- case <-ctx.Done():
- t.Fatalf("Timeout")
- default:
- }
-
- _, totalCap = getCapacityInfo(ctx, t, serverRpcClient)
- if totalCap < testCap {
- t.Log("Total capacity underrun")
- close(stop)
- wg.Wait()
- return false
- }
-
- processed := processedSince(start)
- var avg uint64
- t.Logf("Processed")
- for i, p := range processed {
- t.Logf(" %d", p)
- processed[i] = uint64(float64(p) / weights[i])
- avg += processed[i]
- }
- avg /= uint64(len(processed))
-
- if avg >= 10000 {
- var maxDev float64
- for _, p := range processed {
- dev := float64(int64(p-avg)) / float64(avg)
- t.Logf(" %7.4f", dev)
- if dev < 0 {
- dev = -dev
- }
- if dev > maxDev {
- maxDev = dev
- }
- }
- t.Logf(" max deviation: %f totalCap: %d\n", maxDev, totalCap)
- if maxDev <= testTolerance {
- t.Log("success")
- break
- }
- } else {
- t.Log()
- }
- time.Sleep(time.Millisecond * 200)
- }
- }
-
- close(stop)
- wg.Wait()
-
- for i := range reqCount {
- t.Log("client", i, "processed", reqCount[i].Load())
- }
- return true
- }) {
- t.Log("restarting test")
- }
-}
-
-func getHead(ctx context.Context, t *testing.T, client *rpc.Client) (uint64, common.Hash) {
- res := make(map[string]interface{})
- if err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false); err != nil {
- t.Fatalf("Failed to obtain head block: %v", err)
- }
- numStr, ok := res["number"].(string)
- if !ok {
- t.Fatalf("RPC block number field invalid")
- }
- num, err := hexutil.DecodeUint64(numStr)
- if err != nil {
- t.Fatalf("Failed to decode RPC block number: %v", err)
- }
- hashStr, ok := res["hash"].(string)
- if !ok {
- t.Fatalf("RPC block number field invalid")
- }
- hash := common.HexToHash(hashStr)
- return num, hash
-}
-
-func testRequest(ctx context.Context, t *testing.T, client *rpc.Client) bool {
- var res string
- var addr common.Address
- crand.Read(addr[:])
- c, cancel := context.WithTimeout(ctx, time.Second*12)
- defer cancel()
- err := client.CallContext(c, &res, "eth_getBalance", addr, "latest")
- if err != nil {
- t.Log("request error:", err)
- }
- return err == nil
-}
-
-func freezeClient(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID) {
- if err := server.CallContext(ctx, nil, "debug_freezeClient", clientID); err != nil {
- t.Fatalf("Failed to freeze client: %v", err)
- }
-}
-
-func setCapacity(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID, cap uint64) {
- params := make(map[string]interface{})
- params["capacity"] = cap
- if err := server.CallContext(ctx, nil, "les_setClientParams", []enode.ID{clientID}, []string{}, params); err != nil {
- t.Fatalf("Failed to set client capacity: %v", err)
- }
-}
-
-func getCapacity(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID) uint64 {
- var res map[enode.ID]map[string]interface{}
- if err := server.CallContext(ctx, &res, "les_clientInfo", []enode.ID{clientID}, []string{}); err != nil {
- t.Fatalf("Failed to get client info: %v", err)
- }
- info, ok := res[clientID]
- if !ok {
- t.Fatalf("Missing client info")
- }
- v, ok := info["capacity"]
- if !ok {
- t.Fatalf("Missing field in client info: capacity")
- }
- vv, ok := v.(float64)
- if !ok {
- t.Fatalf("Failed to decode capacity field")
- }
- return uint64(vv)
-}
-
-func getCapacityInfo(ctx context.Context, t *testing.T, server *rpc.Client) (minCap, totalCap uint64) {
- var res map[string]interface{}
- if err := server.CallContext(ctx, &res, "les_serverInfo"); err != nil {
- t.Fatalf("Failed to query server info: %v", err)
- }
- decode := func(s string) uint64 {
- v, ok := res[s]
- if !ok {
- t.Fatalf("Missing field in server info: %s", s)
- }
- vv, ok := v.(float64)
- if !ok {
- t.Fatalf("Failed to decode server info field: %s", s)
- }
- return uint64(vv)
- }
- minCap = decode("minimumCapacity")
- totalCap = decode("totalCapacity")
- return
-}
-
-var services = adapters.LifecycleConstructors{
- "lesclient": newLesClientService,
- "lesserver": newLesServerService,
-}
-
-func NewNetwork() (*simulations.Network, func(), error) {
- adapter, adapterTeardown, err := NewAdapter(*simAdapter, services)
- if err != nil {
- return nil, adapterTeardown, err
- }
- defaultService := "streamer"
- net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
- ID: "0",
- DefaultService: defaultService,
- })
- teardown := func() {
- adapterTeardown()
- net.Shutdown()
- }
- return net, teardown, nil
-}
-
-func NewAdapter(adapterType string, services adapters.LifecycleConstructors) (adapter adapters.NodeAdapter, teardown func(), err error) {
- teardown = func() {}
- switch adapterType {
- case "sim":
- adapter = adapters.NewSimAdapter(services)
- // case "socket":
- // adapter = adapters.NewSocketAdapter(services)
- case "exec":
- baseDir, err0 := os.MkdirTemp("", "les-test")
- if err0 != nil {
- return nil, teardown, err0
- }
- teardown = func() { os.RemoveAll(baseDir) }
- adapter = adapters.NewExecAdapter(baseDir)
- /*case "docker":
- adapter, err = adapters.NewDockerAdapter()
- if err != nil {
- return nil, teardown, err
- }*/
- default:
- return nil, teardown, errors.New("adapter needs to be one of sim, socket, exec, docker")
- }
- return adapter, teardown, nil
-}
-
-func testSim(t *testing.T, serverCount, clientCount int, serverDir, clientDir []string, test func(ctx context.Context, net *simulations.Network, servers []*simulations.Node, clients []*simulations.Node) bool) bool {
- net, teardown, err := NewNetwork()
- defer teardown()
- if err != nil {
- t.Fatalf("Failed to create network: %v", err)
- }
- timeout := 1800 * time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
-
- servers := make([]*simulations.Node, serverCount)
- clients := make([]*simulations.Node, clientCount)
-
- for i := range clients {
- clientconf := adapters.RandomNodeConfig()
- clientconf.Lifecycles = []string{"lesclient"}
- if len(clientDir) == clientCount {
- clientconf.DataDir = clientDir[i]
- }
- client, err := net.NewNodeWithConfig(clientconf)
- if err != nil {
- t.Fatalf("Failed to create client: %v", err)
- }
- clients[i] = client
- }
-
- for i := range servers {
- serverconf := adapters.RandomNodeConfig()
- serverconf.Lifecycles = []string{"lesserver"}
- if len(serverDir) == serverCount {
- serverconf.DataDir = serverDir[i]
- }
- server, err := net.NewNodeWithConfig(serverconf)
- if err != nil {
- t.Fatalf("Failed to create server: %v", err)
- }
- servers[i] = server
- }
-
- for _, client := range clients {
- if err := net.Start(client.ID()); err != nil {
- t.Fatalf("Failed to start client node: %v", err)
- }
- }
- for _, server := range servers {
- if err := net.Start(server.ID()); err != nil {
- t.Fatalf("Failed to start server node: %v", err)
- }
- }
-
- return test(ctx, net, servers, clients)
-}
-
-func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
- config := ethconfig.Defaults
- config.SyncMode = downloader.LightSync
- return New(stack, &config)
-}
-
-func newLesServerService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
- config := ethconfig.Defaults
- config.SyncMode = downloader.FullSync
- config.LightServ = testServerCapacity
- config.LightPeers = testMaxClients
- ethereum, err := eth.New(stack, &config)
- if err != nil {
- return nil, err
- }
- _, err = NewLesServer(stack, ethereum, &config)
- if err != nil {
- return nil, err
- }
- return ethereum, nil
-}
diff --git a/les/benchmark.go b/les/benchmark.go
deleted file mode 100644
index d1efa2f5d3ec..000000000000
--- a/les/benchmark.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- crand "crypto/rand"
- "encoding/binary"
- "errors"
- "math/big"
- "math/rand"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// requestBenchmark is an interface for different randomized request generators
-type requestBenchmark interface {
- // init initializes the generator for generating the given number of randomized requests
- init(h *serverHandler, count int) error
- // request initiates sending a single request to the given peer
- request(peer *serverPeer, index int) error
-}
-
-// benchmarkBlockHeaders implements requestBenchmark
-type benchmarkBlockHeaders struct {
- amount, skip int
- reverse, byHash bool
- offset, randMax int64
- hashes []common.Hash
-}
-
-func (b *benchmarkBlockHeaders) init(h *serverHandler, count int) error {
- d := int64(b.amount-1) * int64(b.skip+1)
- b.offset = 0
- b.randMax = h.blockchain.CurrentHeader().Number.Int64() + 1 - d
- if b.randMax < 0 {
- return errors.New("chain is too short")
- }
- if b.reverse {
- b.offset = d
- }
- if b.byHash {
- b.hashes = make([]common.Hash, count)
- for i := range b.hashes {
- b.hashes[i] = rawdb.ReadCanonicalHash(h.chainDb, uint64(b.offset+rand.Int63n(b.randMax)))
- }
- }
- return nil
-}
-
-func (b *benchmarkBlockHeaders) request(peer *serverPeer, index int) error {
- if b.byHash {
- return peer.requestHeadersByHash(0, b.hashes[index], b.amount, b.skip, b.reverse)
- }
- return peer.requestHeadersByNumber(0, uint64(b.offset+rand.Int63n(b.randMax)), b.amount, b.skip, b.reverse)
-}
-
-// benchmarkBodiesOrReceipts implements requestBenchmark
-type benchmarkBodiesOrReceipts struct {
- receipts bool
- hashes []common.Hash
-}
-
-func (b *benchmarkBodiesOrReceipts) init(h *serverHandler, count int) error {
- randMax := h.blockchain.CurrentHeader().Number.Int64() + 1
- b.hashes = make([]common.Hash, count)
- for i := range b.hashes {
- b.hashes[i] = rawdb.ReadCanonicalHash(h.chainDb, uint64(rand.Int63n(randMax)))
- }
- return nil
-}
-
-func (b *benchmarkBodiesOrReceipts) request(peer *serverPeer, index int) error {
- if b.receipts {
- return peer.requestReceipts(0, []common.Hash{b.hashes[index]})
- }
- return peer.requestBodies(0, []common.Hash{b.hashes[index]})
-}
-
-// benchmarkProofsOrCode implements requestBenchmark
-type benchmarkProofsOrCode struct {
- code bool
- headHash common.Hash
-}
-
-func (b *benchmarkProofsOrCode) init(h *serverHandler, count int) error {
- b.headHash = h.blockchain.CurrentHeader().Hash()
- return nil
-}
-
-func (b *benchmarkProofsOrCode) request(peer *serverPeer, index int) error {
- key := make([]byte, 32)
- crand.Read(key)
- if b.code {
- return peer.requestCode(0, []CodeReq{{BHash: b.headHash, AccountAddress: key}})
- }
- return peer.requestProofs(0, []ProofReq{{BHash: b.headHash, Key: key}})
-}
-
-// benchmarkHelperTrie implements requestBenchmark
-type benchmarkHelperTrie struct {
- bloom bool
- reqCount int
- sectionCount, headNum uint64
-}
-
-func (b *benchmarkHelperTrie) init(h *serverHandler, count int) error {
- if b.bloom {
- b.sectionCount, b.headNum, _ = h.server.bloomTrieIndexer.Sections()
- } else {
- b.sectionCount, _, _ = h.server.chtIndexer.Sections()
- b.headNum = b.sectionCount*params.CHTFrequency - 1
- }
- if b.sectionCount == 0 {
- return errors.New("no processed sections available")
- }
- return nil
-}
-
-func (b *benchmarkHelperTrie) request(peer *serverPeer, index int) error {
- reqs := make([]HelperTrieReq, b.reqCount)
-
- if b.bloom {
- bitIdx := uint16(rand.Intn(2048))
- for i := range reqs {
- key := make([]byte, 10)
- binary.BigEndian.PutUint16(key[:2], bitIdx)
- binary.BigEndian.PutUint64(key[2:], uint64(rand.Int63n(int64(b.sectionCount))))
- reqs[i] = HelperTrieReq{Type: htBloomBits, TrieIdx: b.sectionCount - 1, Key: key}
- }
- } else {
- for i := range reqs {
- key := make([]byte, 8)
- binary.BigEndian.PutUint64(key[:], uint64(rand.Int63n(int64(b.headNum))))
- reqs[i] = HelperTrieReq{Type: htCanonical, TrieIdx: b.sectionCount - 1, Key: key, AuxReq: htAuxHeader}
- }
- }
-
- return peer.requestHelperTrieProofs(0, reqs)
-}
-
-// benchmarkTxSend implements requestBenchmark
-type benchmarkTxSend struct {
- txs types.Transactions
-}
-
-func (b *benchmarkTxSend) init(h *serverHandler, count int) error {
- key, _ := crypto.GenerateKey()
- addr := crypto.PubkeyToAddress(key.PublicKey)
- signer := types.LatestSigner(h.server.chainConfig)
- b.txs = make(types.Transactions, count)
-
- for i := range b.txs {
- data := make([]byte, txSizeCostLimit)
- crand.Read(data)
- tx, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), data), signer, key)
- if err != nil {
- panic(err)
- }
- b.txs[i] = tx
- }
- return nil
-}
-
-func (b *benchmarkTxSend) request(peer *serverPeer, index int) error {
- enc, _ := rlp.EncodeToBytes(types.Transactions{b.txs[index]})
- return peer.sendTxs(0, 1, enc)
-}
-
-// benchmarkTxStatus implements requestBenchmark
-type benchmarkTxStatus struct{}
-
-func (b *benchmarkTxStatus) init(h *serverHandler, count int) error {
- return nil
-}
-
-func (b *benchmarkTxStatus) request(peer *serverPeer, index int) error {
- var hash common.Hash
- crand.Read(hash[:])
- return peer.requestTxStatus(0, []common.Hash{hash})
-}
-
-// benchmarkSetup stores measurement data for a single benchmark type
-type benchmarkSetup struct {
- req requestBenchmark
- totalCount int
- totalTime, avgTime time.Duration
- maxInSize, maxOutSize uint32
- err error
-}
-
-// runBenchmark runs a benchmark cycle for all benchmark types in the specified
-// number of passes
-func (h *serverHandler) runBenchmark(benchmarks []requestBenchmark, passCount int, targetTime time.Duration) []*benchmarkSetup {
- setup := make([]*benchmarkSetup, len(benchmarks))
- for i, b := range benchmarks {
- setup[i] = &benchmarkSetup{req: b}
- }
- for i := 0; i < passCount; i++ {
- log.Info("Running benchmark", "pass", i+1, "total", passCount)
- todo := make([]*benchmarkSetup, len(benchmarks))
- copy(todo, setup)
- for len(todo) > 0 {
- // select a random element
- index := rand.Intn(len(todo))
- next := todo[index]
- todo[index] = todo[len(todo)-1]
- todo = todo[:len(todo)-1]
-
- if next.err == nil {
- // calculate request count
- count := 50
- if next.totalTime > 0 {
- count = int(uint64(next.totalCount) * uint64(targetTime) / uint64(next.totalTime))
- }
- if err := h.measure(next, count); err != nil {
- next.err = err
- }
- }
- }
- }
- log.Info("Benchmark completed")
-
- for _, s := range setup {
- if s.err == nil {
- s.avgTime = s.totalTime / time.Duration(s.totalCount)
- }
- }
- return setup
-}
-
-// meteredPipe implements p2p.MsgReadWriter and remembers the largest single
-// message size sent through the pipe
-type meteredPipe struct {
- rw p2p.MsgReadWriter
- maxSize uint32
-}
-
-func (m *meteredPipe) ReadMsg() (p2p.Msg, error) {
- return m.rw.ReadMsg()
-}
-
-func (m *meteredPipe) WriteMsg(msg p2p.Msg) error {
- if msg.Size > m.maxSize {
- m.maxSize = msg.Size
- }
- return m.rw.WriteMsg(msg)
-}
-
-// measure runs a benchmark for a single type in a single pass, with the given
-// number of requests
-func (h *serverHandler) measure(setup *benchmarkSetup, count int) error {
- clientPipe, serverPipe := p2p.MsgPipe()
- clientMeteredPipe := &meteredPipe{rw: clientPipe}
- serverMeteredPipe := &meteredPipe{rw: serverPipe}
- var id enode.ID
- crand.Read(id[:])
-
- peer1 := newServerPeer(lpv2, NetworkId, false, p2p.NewPeer(id, "client", nil), clientMeteredPipe)
- peer2 := newClientPeer(lpv2, NetworkId, p2p.NewPeer(id, "server", nil), serverMeteredPipe)
- peer2.announceType = announceTypeNone
- peer2.fcCosts = make(requestCostTable)
- c := &requestCosts{}
- for code := range requests {
- peer2.fcCosts[code] = c
- }
- peer2.fcParams = flowcontrol.ServerParams{BufLimit: 1, MinRecharge: 1}
- peer2.fcClient = flowcontrol.NewClientNode(h.server.fcManager, peer2.fcParams)
- defer peer2.fcClient.Disconnect()
-
- if err := setup.req.init(h, count); err != nil {
- return err
- }
-
- errCh := make(chan error, 10)
- start := mclock.Now()
-
- go func() {
- for i := 0; i < count; i++ {
- if err := setup.req.request(peer1, i); err != nil {
- errCh <- err
- return
- }
- }
- }()
- go func() {
- for i := 0; i < count; i++ {
- if err := h.handleMsg(peer2, &sync.WaitGroup{}); err != nil {
- errCh <- err
- return
- }
- }
- }()
- go func() {
- for i := 0; i < count; i++ {
- msg, err := clientPipe.ReadMsg()
- if err != nil {
- errCh <- err
- return
- }
- var i interface{}
- msg.Decode(&i)
- }
- // at this point we can be sure that the other two
- // goroutines finished successfully too
- close(errCh)
- }()
- select {
- case err := <-errCh:
- if err != nil {
- return err
- }
- case <-h.closeCh:
- clientPipe.Close()
- serverPipe.Close()
- return errors.New("benchmark cancelled")
- }
-
- setup.totalTime += time.Duration(mclock.Now() - start)
- setup.totalCount += count
- setup.maxInSize = clientMeteredPipe.maxSize
- setup.maxOutSize = serverMeteredPipe.maxSize
- clientPipe.Close()
- serverPipe.Close()
- return nil
-}
diff --git a/les/bloombits.go b/les/bloombits.go
deleted file mode 100644
index a98524ce2e69..000000000000
--- a/les/bloombits.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "time"
-
- "github.com/ethereum/go-ethereum/common/bitutil"
- "github.com/ethereum/go-ethereum/light"
-)
-
-const (
- // bloomServiceThreads is the number of goroutines used globally by an Ethereum
- // instance to service bloombits lookups for all running filters.
- bloomServiceThreads = 16
-
- // bloomFilterThreads is the number of goroutines used locally per filter to
- // multiplex requests onto the global servicing goroutines.
- bloomFilterThreads = 3
-
- // bloomRetrievalBatch is the maximum number of bloom bit retrievals to service
- // in a single batch.
- bloomRetrievalBatch = 16
-
- // bloomRetrievalWait is the maximum time to wait for enough bloom bit requests
- // to accumulate request an entire batch (avoiding hysteresis).
- bloomRetrievalWait = time.Microsecond * 100
-)
-
-// startBloomHandlers starts a batch of goroutines to accept bloom bit database
-// retrievals from possibly a range of filters and serving the data to satisfy.
-func (eth *LightEthereum) startBloomHandlers(sectionSize uint64) {
- for i := 0; i < bloomServiceThreads; i++ {
- go func() {
- defer eth.wg.Done()
- for {
- select {
- case <-eth.closeCh:
- return
-
- case request := <-eth.bloomRequests:
- task := <-request
- task.Bitsets = make([][]byte, len(task.Sections))
- compVectors, err := light.GetBloomBits(task.Context, eth.odr, task.Bit, task.Sections)
- if err == nil {
- for i := range task.Sections {
- if blob, err := bitutil.DecompressBytes(compVectors[i], int(sectionSize/8)); err == nil {
- task.Bitsets[i] = blob
- } else {
- task.Error = err
- }
- }
- } else {
- task.Error = err
- }
- request <- task
- }
- }
- }()
- }
-}
diff --git a/les/client.go b/les/client.go
deleted file mode 100644
index be5e9fd5641d..000000000000
--- a/les/client.go
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package les implements the Light Ethereum Subprotocol.
-package les
-
-import (
- "errors"
- "strings"
- "time"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/bloombits"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/eth/gasprice"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/internal/ethapi"
- "github.com/ethereum/go-ethereum/internal/shutdowncheck"
- "github.com/ethereum/go-ethereum/les/vflux"
- vfc "github.com/ethereum/go-ethereum/les/vflux/client"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-type LightEthereum struct {
- lesCommons
-
- peers *serverPeerSet
- reqDist *requestDistributor
- retriever *retrieveManager
- odr *LesOdr
- relay *lesTxRelay
- handler *clientHandler
- txPool *light.TxPool
- blockchain *light.LightChain
- serverPool *vfc.ServerPool
- serverPoolIterator enode.Iterator
- merger *consensus.Merger
-
- bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
- bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
-
- ApiBackend *LesApiBackend
- eventMux *event.TypeMux
- engine consensus.Engine
- accountManager *accounts.Manager
- netRPCService *ethapi.NetAPI
-
- p2pServer *p2p.Server
- p2pConfig *p2p.Config
- udpEnabled bool
-
- shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
-}
-
-// New creates an instance of the light client.
-func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
- chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false)
- if err != nil {
- return nil, err
- }
- lesDb, err := stack.OpenDatabase("les.client", 0, 0, "eth/db/lesclient/", false)
- if err != nil {
- return nil, err
- }
- var overrides core.ChainOverrides
- if config.OverrideCancun != nil {
- overrides.OverrideCancun = config.OverrideCancun
- }
- if config.OverrideVerkle != nil {
- overrides.OverrideVerkle = config.OverrideVerkle
- }
- triedb := trie.NewDatabase(chainDb, trie.HashDefaults)
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, triedb, config.Genesis, &overrides)
- if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
- return nil, genesisErr
- }
- engine, err := ethconfig.CreateConsensusEngine(chainConfig, chainDb)
- if err != nil {
- return nil, err
- }
- log.Info("")
- log.Info(strings.Repeat("-", 153))
- for _, line := range strings.Split(chainConfig.Description(), "\n") {
- log.Info(line)
- }
- log.Info(strings.Repeat("-", 153))
- log.Info("")
-
- peers := newServerPeerSet()
- merger := consensus.NewMerger(chainDb)
- leth := &LightEthereum{
- lesCommons: lesCommons{
- genesis: genesisHash,
- config: config,
- chainConfig: chainConfig,
- iConfig: light.DefaultClientIndexerConfig,
- chainDb: chainDb,
- lesDb: lesDb,
- closeCh: make(chan struct{}),
- },
- peers: peers,
- eventMux: stack.EventMux(),
- reqDist: newRequestDistributor(peers, &mclock.System{}),
- accountManager: stack.AccountManager(),
- merger: merger,
- engine: engine,
- bloomRequests: make(chan chan *bloombits.Retrieval),
- bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
- p2pServer: stack.Server(),
- p2pConfig: &stack.Config().P2P,
- udpEnabled: stack.Config().P2P.DiscoveryV5,
- shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
- }
-
- var prenegQuery vfc.QueryFunc
- if leth.udpEnabled {
- prenegQuery = leth.prenegQuery
- }
- leth.serverPool, leth.serverPoolIterator = vfc.NewServerPool(lesDb, []byte("serverpool:"), time.Second, prenegQuery, &mclock.System{}, nil, requestList)
- leth.serverPool.AddMetrics(suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge, sessionValueMeter, serverDialedMeter)
-
- leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool.GetTimeout)
- leth.relay = newLesTxRelay(peers, leth.retriever)
-
- leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.peers, leth.retriever)
- leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations, config.LightNoPrune)
- leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency, config.LightNoPrune)
- leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer)
-
- // Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
- // indexers already set but not started yet
- if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine); err != nil {
- return nil, err
- }
- leth.chainReader = leth.blockchain
- leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
-
- // Note: AddChildIndexer starts the update process for the child
- leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer)
- leth.chtIndexer.Start(leth.blockchain)
- leth.bloomIndexer.Start(leth.blockchain)
-
- // Rewind the chain in case of an incompatible config upgrade.
- if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
- log.Warn("Rewinding chain to upgrade configuration", "err", compat)
- if compat.RewindToTime > 0 {
- leth.blockchain.SetHeadWithTimestamp(compat.RewindToTime)
- } else {
- leth.blockchain.SetHead(compat.RewindToBlock)
- }
- rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
- }
-
- leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, leth, nil}
- gpoParams := config.GPO
- if gpoParams.Default == nil {
- gpoParams.Default = config.Miner.GasPrice
- }
- leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
-
- leth.handler = newClientHandler(leth)
- leth.netRPCService = ethapi.NewNetAPI(leth.p2pServer, leth.config.NetworkId)
-
- // Register the backend on the node
- stack.RegisterAPIs(leth.APIs())
- stack.RegisterProtocols(leth.Protocols())
- stack.RegisterLifecycle(leth)
-
- // Successful startup; push a marker and check previous unclean shutdowns.
- leth.shutdownTracker.MarkStartup()
-
- return leth, nil
-}
-
-// VfluxRequest sends a batch of requests to the given node through discv5 UDP TalkRequest and returns the responses
-func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.Replies {
- if !s.udpEnabled {
- return nil
- }
- reqsEnc, _ := rlp.EncodeToBytes(&reqs)
- repliesEnc, _ := s.p2pServer.DiscV5.TalkRequest(s.serverPool.DialNode(n), "vfx", reqsEnc)
- var replies vflux.Replies
- if len(repliesEnc) == 0 || rlp.DecodeBytes(repliesEnc, &replies) != nil {
- return nil
- }
- return replies
-}
-
-// vfxVersion returns the version number of the "les" service subdomain of the vflux UDP
-// service, as advertised in the ENR record
-func (s *LightEthereum) vfxVersion(n *enode.Node) uint {
- if n.Seq() == 0 {
- var err error
- if !s.udpEnabled {
- return 0
- }
- if n, err = s.p2pServer.DiscV5.RequestENR(n); n != nil && err == nil && n.Seq() != 0 {
- s.serverPool.Persist(n)
- } else {
- return 0
- }
- }
-
- var les []rlp.RawValue
- if err := n.Load(enr.WithEntry("les", &les)); err != nil || len(les) < 1 {
- return 0
- }
- var version uint
- rlp.DecodeBytes(les[0], &version) // Ignore additional fields (for forward compatibility).
- return version
-}
-
-// prenegQuery sends a capacity query to the given server node to determine whether
-// a connection slot is immediately available
-func (s *LightEthereum) prenegQuery(n *enode.Node) int {
- if s.vfxVersion(n) < 1 {
- // UDP query not supported, always try TCP connection
- return 1
- }
-
- var requests vflux.Requests
- requests.Add("les", vflux.CapacityQueryName, vflux.CapacityQueryReq{
- Bias: 180,
- AddTokens: []vflux.IntOrInf{{}},
- })
- replies := s.VfluxRequest(n, requests)
- var cqr vflux.CapacityQueryReply
- if replies.Get(0, &cqr) != nil || len(cqr) != 1 { // Note: Get returns an error if replies is nil
- return -1
- }
- if cqr[0] > 0 {
- return 1
- }
- return 0
-}
-
-type LightDummyAPI struct{}
-
-// Etherbase is the address that mining rewards will be sent to
-func (s *LightDummyAPI) Etherbase() (common.Address, error) {
- return common.Address{}, errors.New("mining is not supported in light mode")
-}
-
-// Coinbase is the address that mining rewards will be sent to (alias for Etherbase)
-func (s *LightDummyAPI) Coinbase() (common.Address, error) {
- return common.Address{}, errors.New("mining is not supported in light mode")
-}
-
-// Hashrate returns the POW hashrate
-func (s *LightDummyAPI) Hashrate() hexutil.Uint {
- return 0
-}
-
-// Mining returns an indication if this node is currently mining.
-func (s *LightDummyAPI) Mining() bool {
- return false
-}
-
-// APIs returns the collection of RPC services the ethereum package offers.
-// NOTE, some of these services probably need to be moved to somewhere else.
-func (s *LightEthereum) APIs() []rpc.API {
- apis := ethapi.GetAPIs(s.ApiBackend)
- apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...)
- return append(apis, []rpc.API{
- {
- Namespace: "eth",
- Service: &LightDummyAPI{},
- }, {
- Namespace: "net",
- Service: s.netRPCService,
- }, {
- Namespace: "vflux",
- Service: s.serverPool.API(),
- },
- }...)
-}
-
-func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
- s.blockchain.ResetWithGenesisBlock(gb)
-}
-
-func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain }
-func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool }
-func (s *LightEthereum) Engine() consensus.Engine { return s.engine }
-func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) }
-func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *LightEthereum) Merger() *consensus.Merger { return s.merger }
-
-// Protocols returns all the currently configured network protocols to start.
-func (s *LightEthereum) Protocols() []p2p.Protocol {
- return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
- if p := s.peers.peer(id.String()); p != nil {
- return p.Info()
- }
- return nil
- }, s.serverPoolIterator)
-}
-
-// Start implements node.Lifecycle, starting all internal goroutines needed by the
-// light ethereum protocol implementation.
-func (s *LightEthereum) Start() error {
- log.Warn("Light client mode is an experimental feature")
-
- // Regularly update shutdown marker
- s.shutdownTracker.Start()
-
- if s.udpEnabled && s.p2pServer.DiscV5 == nil {
- s.udpEnabled = false
- log.Error("Discovery v5 is not initialized")
- }
- discovery, err := s.setupDiscovery()
- if err != nil {
- return err
- }
- s.serverPool.AddSource(discovery)
- s.serverPool.Start()
- // Start bloom request workers.
- s.wg.Add(bloomServiceThreads)
- s.startBloomHandlers(params.BloomBitsBlocksClient)
-
- return nil
-}
-
-// Stop implements node.Lifecycle, terminating all internal goroutines used by the
-// Ethereum protocol.
-func (s *LightEthereum) Stop() error {
- close(s.closeCh)
- s.serverPool.Stop()
- s.peers.close()
- s.reqDist.close()
- s.odr.Stop()
- s.relay.Stop()
- s.bloomIndexer.Close()
- s.chtIndexer.Close()
- s.blockchain.Stop()
- s.handler.stop()
- s.txPool.Stop()
- s.engine.Close()
- s.eventMux.Stop()
- // Clean shutdown marker as the last thing before closing db
- s.shutdownTracker.Stop()
-
- s.chainDb.Close()
- s.lesDb.Close()
- s.wg.Wait()
- log.Info("Light ethereum stopped")
- return nil
-}
diff --git a/les/client_handler.go b/les/client_handler.go
deleted file mode 100644
index 50f6dce879ff..000000000000
--- a/les/client_handler.go
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-// clientHandler is responsible for receiving and processing all incoming server
-// responses.
-type clientHandler struct {
- forkFilter forkid.Filter
- backend *LightEthereum
-
- closeCh chan struct{}
- wg sync.WaitGroup // WaitGroup used to track all connected peers.
-}
-
-func newClientHandler(backend *LightEthereum) *clientHandler {
- handler := &clientHandler{
- forkFilter: forkid.NewFilter(backend.blockchain),
- backend: backend,
- closeCh: make(chan struct{}),
- }
- return handler
-}
-
-func (h *clientHandler) stop() {
- close(h.closeCh)
- h.wg.Wait()
-}
-
-// runPeer is the p2p protocol run function for the given version.
-func (h *clientHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error {
- peer := newServerPeer(int(version), h.backend.config.NetworkId, false, p, newMeteredMsgWriter(rw, int(version)))
- defer peer.close()
- h.wg.Add(1)
- defer h.wg.Done()
- err := h.handle(peer, false)
- return err
-}
-
-func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error {
- if h.backend.peers.len() >= h.backend.config.LightPeers && !p.Peer.Info().Network.Trusted {
- return p2p.DiscTooManyPeers
- }
- p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
-
- // Execute the LES handshake
- forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.BlockChain().Genesis(), h.backend.blockchain.CurrentHeader().Number.Uint64(), h.backend.blockchain.CurrentHeader().Time)
- if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil {
- p.Log().Debug("Light Ethereum handshake failed", "err", err)
- return err
- }
- // Register peer with the server pool
- if h.backend.serverPool != nil {
- if nvt, err := h.backend.serverPool.RegisterNode(p.Node()); err == nil {
- p.setValueTracker(nvt)
- p.updateVtParams()
- defer func() {
- p.setValueTracker(nil)
- h.backend.serverPool.UnregisterNode(p.Node())
- }()
- } else {
- return err
- }
- }
- // Register the peer locally
- if err := h.backend.peers.register(p); err != nil {
- p.Log().Error("Light Ethereum peer registration failed", "err", err)
- return err
- }
-
- serverConnectionGauge.Update(int64(h.backend.peers.len()))
-
- connectedAt := mclock.Now()
- defer func() {
- h.backend.peers.unregister(p.id)
- connectionTimer.Update(time.Duration(mclock.Now() - connectedAt))
- serverConnectionGauge.Update(int64(h.backend.peers.len()))
- }()
-
- // Mark the peer starts to be served.
- p.serving.Store(true)
- defer p.serving.Store(false)
-
- // Spawn a main loop to handle all incoming messages.
- for {
- if err := h.handleMsg(p); err != nil {
- p.Log().Debug("Light Ethereum message handling failed", "err", err)
- p.fcServer.DumpLogs()
- return err
- }
- }
-}
-
-// handleMsg is invoked whenever an inbound message is received from a remote
-// peer. The remote connection is torn down upon returning any error.
-func (h *clientHandler) handleMsg(p *serverPeer) error {
- // Read the next message from the remote peer, and ensure it's fully consumed
- msg, err := p.rw.ReadMsg()
- if err != nil {
- return err
- }
- p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size)
-
- if msg.Size > ProtocolMaxMsgSize {
- return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
- }
- defer msg.Discard()
-
- var deliverMsg *Msg
-
- // Handle the message depending on its contents
- switch {
- case msg.Code == AnnounceMsg:
- p.Log().Trace("Received announce message")
- var req announceData
- if err := msg.Decode(&req); err != nil {
- return errResp(ErrDecode, "%v: %v", msg, err)
- }
- if err := req.sanityCheck(); err != nil {
- return err
- }
- update, size := req.Update.decode()
- if p.rejectUpdate(size) {
- return errResp(ErrRequestRejected, "")
- }
- p.updateFlowControl(update)
- p.updateVtParams()
-
- if req.Hash != (common.Hash{}) {
- if p.announceType == announceTypeNone {
- return errResp(ErrUnexpectedResponse, "")
- }
- if p.announceType == announceTypeSigned {
- if err := req.checkSignature(p.ID(), update); err != nil {
- p.Log().Trace("Invalid announcement signature", "err", err)
- return err
- }
- p.Log().Trace("Valid announcement signature")
- }
- p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth)
-
- // Update peer head information first and then notify the announcement
- p.updateHead(req.Hash, req.Number, req.Td)
- }
- case msg.Code == BlockHeadersMsg:
- p.Log().Trace("Received block header response message")
- var resp struct {
- ReqID, BV uint64
- Headers []*types.Header
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
-
- deliverMsg = &Msg{
- MsgType: MsgBlockHeaders,
- ReqID: resp.ReqID,
- Obj: resp.Headers,
- }
- case msg.Code == BlockBodiesMsg:
- p.Log().Trace("Received block bodies response")
- var resp struct {
- ReqID, BV uint64
- Data []*types.Body
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgBlockBodies,
- ReqID: resp.ReqID,
- Obj: resp.Data,
- }
- case msg.Code == CodeMsg:
- p.Log().Trace("Received code response")
- var resp struct {
- ReqID, BV uint64
- Data [][]byte
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgCode,
- ReqID: resp.ReqID,
- Obj: resp.Data,
- }
- case msg.Code == ReceiptsMsg:
- p.Log().Trace("Received receipts response")
- var resp struct {
- ReqID, BV uint64
- Receipts []types.Receipts
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgReceipts,
- ReqID: resp.ReqID,
- Obj: resp.Receipts,
- }
- case msg.Code == ProofsV2Msg:
- p.Log().Trace("Received les/2 proofs response")
- var resp struct {
- ReqID, BV uint64
- Data trienode.ProofList
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgProofsV2,
- ReqID: resp.ReqID,
- Obj: resp.Data,
- }
- case msg.Code == HelperTrieProofsMsg:
- p.Log().Trace("Received helper trie proof response")
- var resp struct {
- ReqID, BV uint64
- Data HelperTrieResps
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgHelperTrieProofs,
- ReqID: resp.ReqID,
- Obj: resp.Data,
- }
- case msg.Code == TxStatusMsg:
- p.Log().Trace("Received tx status response")
- var resp struct {
- ReqID, BV uint64
- Status []light.TxStatus
- }
- if err := msg.Decode(&resp); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
- p.answeredRequest(resp.ReqID)
- deliverMsg = &Msg{
- MsgType: MsgTxStatus,
- ReqID: resp.ReqID,
- Obj: resp.Status,
- }
- case msg.Code == StopMsg && p.version >= lpv3:
- p.freeze()
- h.backend.retriever.frozen(p)
- p.Log().Debug("Service stopped")
- case msg.Code == ResumeMsg && p.version >= lpv3:
- var bv uint64
- if err := msg.Decode(&bv); err != nil {
- return errResp(ErrDecode, "msg %v: %v", msg, err)
- }
- p.fcServer.ResumeFreeze(bv)
- p.unfreeze()
- p.Log().Debug("Service resumed")
- default:
- p.Log().Trace("Received invalid message", "code", msg.Code)
- return errResp(ErrInvalidMsgCode, "%v", msg.Code)
- }
- // Deliver the received response to retriever.
- if deliverMsg != nil {
- if err := h.backend.retriever.deliver(p, deliverMsg); err != nil {
- if val := p.errCount.Add(1, mclock.Now()); val > maxResponseErrors {
- return err
- }
- }
- }
- return nil
-}
diff --git a/les/commons.go b/les/commons.go
deleted file mode 100644
index cb3fc430b767..000000000000
--- a/les/commons.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "fmt"
- "math/big"
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/params"
-)
-
-func errResp(code errCode, format string, v ...interface{}) error {
- return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...))
-}
-
-type chainReader interface {
- CurrentHeader() *types.Header
-}
-
-// lesCommons contains fields needed by both server and client.
-type lesCommons struct {
- genesis common.Hash
- config *ethconfig.Config
- chainConfig *params.ChainConfig
- iConfig *light.IndexerConfig
- chainDb, lesDb ethdb.Database
- chainReader chainReader
- chtIndexer, bloomTrieIndexer *core.ChainIndexer
-
- closeCh chan struct{}
- wg sync.WaitGroup
-}
-
-// NodeInfo represents a short summary of the Ethereum sub-protocol metadata
-// known about the host peer.
-type NodeInfo struct {
- Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Goerli=5)
- Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
- Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
- Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
- Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
-}
-
-// makeProtocols creates protocol descriptors for the given LES versions.
-func (c *lesCommons) makeProtocols(versions []uint, runPeer func(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error, peerInfo func(id enode.ID) interface{}, dialCandidates enode.Iterator) []p2p.Protocol {
- protos := make([]p2p.Protocol, len(versions))
- for i, version := range versions {
- version := version
- protos[i] = p2p.Protocol{
- Name: "les",
- Version: version,
- Length: ProtocolLengths[version],
- NodeInfo: c.nodeInfo,
- Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
- return runPeer(version, peer, rw)
- },
- PeerInfo: peerInfo,
- DialCandidates: dialCandidates,
- }
- }
- return protos
-}
-
-// nodeInfo retrieves some protocol metadata about the running host node.
-func (c *lesCommons) nodeInfo() interface{} {
- head := c.chainReader.CurrentHeader()
- hash := head.Hash()
- return &NodeInfo{
- Network: c.config.NetworkId,
- Difficulty: rawdb.ReadTd(c.chainDb, hash, head.Number.Uint64()),
- Genesis: c.genesis,
- Config: c.chainConfig,
- Head: hash,
- }
-}
diff --git a/les/costtracker.go b/les/costtracker.go
deleted file mode 100644
index 695d54e14147..000000000000
--- a/les/costtracker.go
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "encoding/binary"
- "math"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
-)
-
-const makeCostStats = false // make request cost statistics during operation
-
-var (
- // average request cost estimates based on serving time
- reqAvgTimeCost = requestCostTable{
- GetBlockHeadersMsg: {150000, 30000},
- GetBlockBodiesMsg: {0, 700000},
- GetReceiptsMsg: {0, 1000000},
- GetCodeMsg: {0, 450000},
- GetProofsV2Msg: {0, 600000},
- GetHelperTrieProofsMsg: {0, 1000000},
- SendTxV2Msg: {0, 450000},
- GetTxStatusMsg: {0, 250000},
- }
- // maximum incoming message size estimates
- reqMaxInSize = requestCostTable{
- GetBlockHeadersMsg: {40, 0},
- GetBlockBodiesMsg: {0, 40},
- GetReceiptsMsg: {0, 40},
- GetCodeMsg: {0, 80},
- GetProofsV2Msg: {0, 80},
- GetHelperTrieProofsMsg: {0, 20},
- SendTxV2Msg: {0, 16500},
- GetTxStatusMsg: {0, 50},
- }
- // maximum outgoing message size estimates
- reqMaxOutSize = requestCostTable{
- GetBlockHeadersMsg: {0, 556},
- GetBlockBodiesMsg: {0, 100000},
- GetReceiptsMsg: {0, 200000},
- GetCodeMsg: {0, 50000},
- GetProofsV2Msg: {0, 4000},
- GetHelperTrieProofsMsg: {0, 4000},
- SendTxV2Msg: {0, 100},
- GetTxStatusMsg: {0, 100},
- }
- // request amounts that have to fit into the minimum buffer size minBufferMultiplier times
- minBufferReqAmount = map[uint64]uint64{
- GetBlockHeadersMsg: 192,
- GetBlockBodiesMsg: 1,
- GetReceiptsMsg: 1,
- GetCodeMsg: 1,
- GetProofsV2Msg: 1,
- GetHelperTrieProofsMsg: 16,
- SendTxV2Msg: 8,
- GetTxStatusMsg: 64,
- }
- minBufferMultiplier = 3
-)
-
-const (
- maxCostFactor = 2 // ratio of maximum and average cost estimates
- bufLimitRatio = 6000 // fixed bufLimit/MRR ratio
- gfUsageThreshold = 0.5
- gfUsageTC = time.Second
- gfRaiseTC = time.Second * 200
- gfDropTC = time.Second * 50
- gfDbKey = "_globalCostFactorV6"
-)
-
-// costTracker is responsible for calculating costs and cost estimates on the
-// server side. It continuously updates the global cost factor which is defined
-// as the number of cost units per nanosecond of serving time in a single thread.
-// It is based on statistics collected during serving requests in high-load periods
-// and practically acts as a one-dimension request price scaling factor over the
-// pre-defined cost estimate table.
-//
-// The reason for dynamically maintaining the global factor on the server side is:
-// the estimated time cost of the request is fixed(hardcoded) but the configuration
-// of the machine running the server is really different. Therefore, the request serving
-// time in different machine will vary greatly. And also, the request serving time
-// in same machine may vary greatly with different request pressure.
-//
-// In order to more effectively limit resources, we apply the global factor to serving
-// time to make the result as close as possible to the estimated time cost no matter
-// the server is slow or fast. And also we scale the totalRecharge with global factor
-// so that fast server can serve more requests than estimation and slow server can
-// reduce request pressure.
-//
-// Instead of scaling the cost values, the real value of cost units is changed by
-// applying the factor to the serving times. This is more convenient because the
-// changes in the cost factor can be applied immediately without always notifying
-// the clients about the changed cost tables.
-type costTracker struct {
- db ethdb.Database
- stopCh chan chan struct{}
-
- inSizeFactor float64
- outSizeFactor float64
- factor float64
- utilTarget float64
- minBufLimit uint64
-
- gfLock sync.RWMutex
- reqInfoCh chan reqInfo
- totalRechargeCh chan uint64
-
- stats map[uint64][]atomic.Uint64 // Used for testing purpose.
-
- // TestHooks
- testing bool // Disable real cost evaluation for testing purpose.
- testCostList RequestCostList // Customized cost table for testing purpose.
-}
-
-// newCostTracker creates a cost tracker and loads the cost factor statistics from the database.
-// It also returns the minimum capacity that can be assigned to any peer.
-func newCostTracker(db ethdb.Database, config *ethconfig.Config) (*costTracker, uint64) {
- utilTarget := float64(config.LightServ) * flowcontrol.FixedPointMultiplier / 100
- ct := &costTracker{
- db: db,
- stopCh: make(chan chan struct{}),
- reqInfoCh: make(chan reqInfo, 100),
- utilTarget: utilTarget,
- }
- if config.LightIngress > 0 {
- ct.inSizeFactor = utilTarget / float64(config.LightIngress)
- }
- if config.LightEgress > 0 {
- ct.outSizeFactor = utilTarget / float64(config.LightEgress)
- }
- if makeCostStats {
- ct.stats = make(map[uint64][]atomic.Uint64)
- for code := range reqAvgTimeCost {
- ct.stats[code] = make([]atomic.Uint64, 10)
- }
- }
- ct.gfLoop()
- costList := ct.makeCostList(ct.globalFactor() * 1.25)
- for _, c := range costList {
- amount := minBufferReqAmount[c.MsgCode]
- cost := c.BaseCost + amount*c.ReqCost
- if cost > ct.minBufLimit {
- ct.minBufLimit = cost
- }
- }
- ct.minBufLimit *= uint64(minBufferMultiplier)
- return ct, (ct.minBufLimit-1)/bufLimitRatio + 1
-}
-
-// stop stops the cost tracker and saves the cost factor statistics to the database
-func (ct *costTracker) stop() {
- stopCh := make(chan struct{})
- ct.stopCh <- stopCh
- <-stopCh
- if makeCostStats {
- ct.printStats()
- }
-}
-
-// makeCostList returns upper cost estimates based on the hardcoded cost estimate
-// tables and the optionally specified incoming/outgoing bandwidth limits
-func (ct *costTracker) makeCostList(globalFactor float64) RequestCostList {
- maxCost := func(avgTimeCost, inSize, outSize uint64) uint64 {
- cost := avgTimeCost * maxCostFactor
- inSizeCost := uint64(float64(inSize) * ct.inSizeFactor * globalFactor)
- if inSizeCost > cost {
- cost = inSizeCost
- }
- outSizeCost := uint64(float64(outSize) * ct.outSizeFactor * globalFactor)
- if outSizeCost > cost {
- cost = outSizeCost
- }
- return cost
- }
- var list RequestCostList
- for code, data := range reqAvgTimeCost {
- baseCost := maxCost(data.baseCost, reqMaxInSize[code].baseCost, reqMaxOutSize[code].baseCost)
- reqCost := maxCost(data.reqCost, reqMaxInSize[code].reqCost, reqMaxOutSize[code].reqCost)
- if ct.minBufLimit != 0 {
- // if minBufLimit is set then always enforce maximum request cost <= minBufLimit
- maxCost := baseCost + reqCost*minBufferReqAmount[code]
- if maxCost > ct.minBufLimit {
- mul := 0.999 * float64(ct.minBufLimit) / float64(maxCost)
- baseCost = uint64(float64(baseCost) * mul)
- reqCost = uint64(float64(reqCost) * mul)
- }
- }
-
- list = append(list, requestCostListItem{
- MsgCode: code,
- BaseCost: baseCost,
- ReqCost: reqCost,
- })
- }
- return list
-}
-
-// reqInfo contains the estimated time cost and the actual request serving time
-// which acts as a feed source to update factor maintained by costTracker.
-type reqInfo struct {
- // avgTimeCost is the estimated time cost corresponding to maxCostTable.
- avgTimeCost float64
-
- // servingTime is the CPU time corresponding to the actual processing of
- // the request.
- servingTime float64
-
- // msgCode indicates the type of request.
- msgCode uint64
-}
-
-// gfLoop starts an event loop which updates the global cost factor which is
-// calculated as a weighted average of the average estimate / serving time ratio.
-// The applied weight equals the serving time if gfUsage is over a threshold,
-// zero otherwise. gfUsage is the recent average serving time per time unit in
-// an exponential moving window. This ensures that statistics are collected only
-// under high-load circumstances where the measured serving times are relevant.
-// The total recharge parameter of the flow control system which controls the
-// total allowed serving time per second but nominated in cost units, should
-// also be scaled with the cost factor and is also updated by this loop.
-func (ct *costTracker) gfLoop() {
- var (
- factor, totalRecharge float64
- gfLog, recentTime, recentAvg float64
-
- lastUpdate, expUpdate = mclock.Now(), mclock.Now()
- )
-
- // Load historical cost factor statistics from the database.
- data, _ := ct.db.Get([]byte(gfDbKey))
- if len(data) == 8 {
- gfLog = math.Float64frombits(binary.BigEndian.Uint64(data[:]))
- }
- ct.factor = math.Exp(gfLog)
- factor, totalRecharge = ct.factor, ct.utilTarget*ct.factor
-
- // In order to perform factor data statistics under the high request pressure,
- // we only adjust factor when recent factor usage beyond the threshold.
- threshold := gfUsageThreshold * float64(gfUsageTC) * ct.utilTarget / flowcontrol.FixedPointMultiplier
-
- go func() {
- saveCostFactor := func() {
- var data [8]byte
- binary.BigEndian.PutUint64(data[:], math.Float64bits(gfLog))
- ct.db.Put([]byte(gfDbKey), data[:])
- log.Debug("global cost factor saved", "value", factor)
- }
- saveTicker := time.NewTicker(time.Minute * 10)
- defer saveTicker.Stop()
-
- for {
- select {
- case r := <-ct.reqInfoCh:
- relCost := int64(factor * r.servingTime * 100 / r.avgTimeCost) // Convert the value to a percentage form
-
- // Record more metrics if we are debugging
- if metrics.EnabledExpensive {
- switch r.msgCode {
- case GetBlockHeadersMsg:
- relativeCostHeaderHistogram.Update(relCost)
- case GetBlockBodiesMsg:
- relativeCostBodyHistogram.Update(relCost)
- case GetReceiptsMsg:
- relativeCostReceiptHistogram.Update(relCost)
- case GetCodeMsg:
- relativeCostCodeHistogram.Update(relCost)
- case GetProofsV2Msg:
- relativeCostProofHistogram.Update(relCost)
- case GetHelperTrieProofsMsg:
- relativeCostHelperProofHistogram.Update(relCost)
- case SendTxV2Msg:
- relativeCostSendTxHistogram.Update(relCost)
- case GetTxStatusMsg:
- relativeCostTxStatusHistogram.Update(relCost)
- }
- }
- // SendTxV2 and GetTxStatus requests are two special cases.
- // All other requests will only put pressure on the database, and
- // the corresponding delay is relatively stable. While these two
- // requests involve txpool query, which is usually unstable.
- //
- // TODO(rjl493456442) fixes this.
- if r.msgCode == SendTxV2Msg || r.msgCode == GetTxStatusMsg {
- continue
- }
- requestServedMeter.Mark(int64(r.servingTime))
- requestServedTimer.Update(time.Duration(r.servingTime))
- requestEstimatedMeter.Mark(int64(r.avgTimeCost / factor))
- requestEstimatedTimer.Update(time.Duration(r.avgTimeCost / factor))
- relativeCostHistogram.Update(relCost)
-
- now := mclock.Now()
- dt := float64(now - expUpdate)
- expUpdate = now
- exp := math.Exp(-dt / float64(gfUsageTC))
-
- // calculate factor correction until now, based on previous values
- var gfCorr float64
- max := recentTime
- if recentAvg > max {
- max = recentAvg
- }
- // we apply continuous correction when MAX(recentTime, recentAvg) > threshold
- if max > threshold {
- // calculate correction time between last expUpdate and now
- if max*exp >= threshold {
- gfCorr = dt
- } else {
- gfCorr = math.Log(max/threshold) * float64(gfUsageTC)
- }
- // calculate log(factor) correction with the right direction and time constant
- if recentTime > recentAvg {
- // drop factor if actual serving times are larger than average estimates
- gfCorr /= -float64(gfDropTC)
- } else {
- // raise factor if actual serving times are smaller than average estimates
- gfCorr /= float64(gfRaiseTC)
- }
- }
- // update recent cost values with current request
- recentTime = recentTime*exp + r.servingTime
- recentAvg = recentAvg*exp + r.avgTimeCost/factor
-
- if gfCorr != 0 {
- // Apply the correction to factor
- gfLog += gfCorr
- factor = math.Exp(gfLog)
- // Notify outside modules the new factor and totalRecharge.
- if time.Duration(now-lastUpdate) > time.Second {
- totalRecharge, lastUpdate = ct.utilTarget*factor, now
- ct.gfLock.Lock()
- ct.factor = factor
- ch := ct.totalRechargeCh
- ct.gfLock.Unlock()
- if ch != nil {
- select {
- case ct.totalRechargeCh <- uint64(totalRecharge):
- default:
- }
- }
- globalFactorGauge.Update(int64(1000 * factor))
- log.Debug("global cost factor updated", "factor", factor)
- }
- }
- recentServedGauge.Update(int64(recentTime))
- recentEstimatedGauge.Update(int64(recentAvg))
-
- case <-saveTicker.C:
- saveCostFactor()
-
- case stopCh := <-ct.stopCh:
- saveCostFactor()
- close(stopCh)
- return
- }
- }
- }()
-}
-
-// globalFactor returns the current value of the global cost factor
-func (ct *costTracker) globalFactor() float64 {
- ct.gfLock.RLock()
- defer ct.gfLock.RUnlock()
-
- return ct.factor
-}
-
-// totalRecharge returns the current total recharge parameter which is used by
-// flowcontrol.ClientManager and is scaled by the global cost factor
-func (ct *costTracker) totalRecharge() uint64 {
- ct.gfLock.RLock()
- defer ct.gfLock.RUnlock()
-
- return uint64(ct.factor * ct.utilTarget)
-}
-
-// subscribeTotalRecharge returns all future updates to the total recharge value
-// through a channel and also returns the current value
-func (ct *costTracker) subscribeTotalRecharge(ch chan uint64) uint64 {
- ct.gfLock.Lock()
- defer ct.gfLock.Unlock()
-
- ct.totalRechargeCh = ch
- return uint64(ct.factor * ct.utilTarget)
-}
-
-// updateStats updates the global cost factor and (if enabled) the real cost vs.
-// average estimate statistics
-func (ct *costTracker) updateStats(code, amount, servingTime, realCost uint64) {
- avg := reqAvgTimeCost[code]
- avgTimeCost := avg.baseCost + amount*avg.reqCost
- select {
- case ct.reqInfoCh <- reqInfo{float64(avgTimeCost), float64(servingTime), code}:
- default:
- }
- if makeCostStats {
- realCost <<= 4
- l := 0
- for l < 9 && realCost > avgTimeCost {
- l++
- realCost >>= 1
- }
- ct.stats[code][l].Add(1)
- }
-}
-
-// realCost calculates the final cost of a request based on actual serving time,
-// incoming and outgoing message size
-//
-// Note: message size is only taken into account if bandwidth limitation is applied
-// and the cost based on either message size is greater than the cost based on
-// serving time. A maximum of the three costs is applied instead of their sum
-// because the three limited resources (serving thread time and i/o bandwidth) can
-// also be maxed out simultaneously.
-func (ct *costTracker) realCost(servingTime uint64, inSize, outSize uint32) uint64 {
- cost := float64(servingTime)
- inSizeCost := float64(inSize) * ct.inSizeFactor
- if inSizeCost > cost {
- cost = inSizeCost
- }
- outSizeCost := float64(outSize) * ct.outSizeFactor
- if outSizeCost > cost {
- cost = outSizeCost
- }
- return uint64(cost * ct.globalFactor())
-}
-
-// printStats prints the distribution of real request cost relative to the average estimates
-func (ct *costTracker) printStats() {
- if ct.stats == nil {
- return
- }
- for code, arr := range ct.stats {
- log.Info("Request cost statistics", "code", code, "1/16", arr[0].Load(), "1/8", arr[1].Load(), "1/4", arr[2].Load(), "1/2", arr[3].Load(), "1", arr[4].Load(), "2", arr[5].Load(), "4", arr[6].Load(), "8", arr[7].Load(), "16", arr[8].Load(), ">16", arr[9].Load())
- }
-}
-
-type (
- // requestCostTable assigns a cost estimate function to each request type
- // which is a linear function of the requested amount
- // (cost = baseCost + reqCost * amount)
- requestCostTable map[uint64]*requestCosts
- requestCosts struct {
- baseCost, reqCost uint64
- }
-
- // RequestCostList is a list representation of request costs which is used for
- // database storage and communication through the network
- RequestCostList []requestCostListItem
- requestCostListItem struct {
- MsgCode, BaseCost, ReqCost uint64
- }
-)
-
-// getMaxCost calculates the estimated cost for a given request type and amount
-func (table requestCostTable) getMaxCost(code, amount uint64) uint64 {
- costs := table[code]
- return costs.baseCost + amount*costs.reqCost
-}
-
-// decode converts a cost list to a cost table
-func (list RequestCostList) decode(protocolLength uint64) requestCostTable {
- table := make(requestCostTable)
- for _, e := range list {
- if e.MsgCode < protocolLength {
- table[e.MsgCode] = &requestCosts{
- baseCost: e.BaseCost,
- reqCost: e.ReqCost,
- }
- }
- }
- return table
-}
-
-// testCostList returns a dummy request cost list used by tests
-func testCostList(testCost uint64) RequestCostList {
- cl := make(RequestCostList, len(reqAvgTimeCost))
- var max uint64
- for code := range reqAvgTimeCost {
- if code > max {
- max = code
- }
- }
- i := 0
- for code := uint64(0); code <= max; code++ {
- if _, ok := reqAvgTimeCost[code]; ok {
- cl[i].MsgCode = code
- cl[i].BaseCost = testCost
- cl[i].ReqCost = 0
- i++
- }
- }
- return cl
-}
diff --git a/les/distributor.go b/les/distributor.go
deleted file mode 100644
index a0319c67f737..000000000000
--- a/les/distributor.go
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "container/list"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/les/utils"
-)
-
-// requestDistributor implements a mechanism that distributes requests to
-// suitable peers, obeying flow control rules and prioritizing them in creation
-// order (even when a resend is necessary).
-type requestDistributor struct {
- clock mclock.Clock
- reqQueue *list.List
- lastReqOrder uint64
- peers map[distPeer]struct{}
- peerLock sync.RWMutex
- loopChn chan struct{}
- loopNextSent bool
- lock sync.Mutex
-
- closeCh chan struct{}
- wg sync.WaitGroup
-}
-
-// distPeer is an LES server peer interface for the request distributor.
-// waitBefore returns either the necessary waiting time before sending a request
-// with the given upper estimated cost or the estimated remaining relative buffer
-// value after sending such a request (in which case the request can be sent
-// immediately). At least one of these values is always zero.
-type distPeer interface {
- waitBefore(uint64) (time.Duration, float64)
- canQueue() bool
- queueSend(f func()) bool
-}
-
-// distReq is the request abstraction used by the distributor. It is based on
-// three callback functions:
-// - getCost returns the upper estimate of the cost of sending the request to a given peer
-// - canSend tells if the server peer is suitable to serve the request
-// - request prepares sending the request to the given peer and returns a function that
-// does the actual sending. Request order should be preserved but the callback itself should not
-// block until it is sent because other peers might still be able to receive requests while
-// one of them is blocking. Instead, the returned function is put in the peer's send queue.
-type distReq struct {
- getCost func(distPeer) uint64
- canSend func(distPeer) bool
- request func(distPeer) func()
-
- reqOrder uint64
- sentChn chan distPeer
- element *list.Element
- waitForPeers mclock.AbsTime
- enterQueue mclock.AbsTime
-}
-
-// newRequestDistributor creates a new request distributor
-func newRequestDistributor(peers *serverPeerSet, clock mclock.Clock) *requestDistributor {
- d := &requestDistributor{
- clock: clock,
- reqQueue: list.New(),
- loopChn: make(chan struct{}, 2),
- closeCh: make(chan struct{}),
- peers: make(map[distPeer]struct{}),
- }
- if peers != nil {
- peers.subscribe(d)
- }
- d.wg.Add(1)
- go d.loop()
- return d
-}
-
-// registerPeer implements peerSetNotify
-func (d *requestDistributor) registerPeer(p *serverPeer) {
- d.peerLock.Lock()
- d.peers[p] = struct{}{}
- d.peerLock.Unlock()
-}
-
-// unregisterPeer implements peerSetNotify
-func (d *requestDistributor) unregisterPeer(p *serverPeer) {
- d.peerLock.Lock()
- delete(d.peers, p)
- d.peerLock.Unlock()
-}
-
-// registerTestPeer adds a new test peer
-func (d *requestDistributor) registerTestPeer(p distPeer) {
- d.peerLock.Lock()
- d.peers[p] = struct{}{}
- d.peerLock.Unlock()
-}
-
-var (
- // distMaxWait is the maximum waiting time after which further necessary waiting
- // times are recalculated based on new feedback from the servers
- distMaxWait = time.Millisecond * 50
-
- // waitForPeers is the time window in which a request does not fail even if it
- // has no suitable peers to send to at the moment
- waitForPeers = time.Second * 3
-)
-
-// main event loop
-func (d *requestDistributor) loop() {
- defer d.wg.Done()
- for {
- select {
- case <-d.closeCh:
- d.lock.Lock()
- elem := d.reqQueue.Front()
- for elem != nil {
- req := elem.Value.(*distReq)
- close(req.sentChn)
- req.sentChn = nil
- elem = elem.Next()
- }
- d.lock.Unlock()
- return
- case <-d.loopChn:
- d.lock.Lock()
- d.loopNextSent = false
- loop:
- for {
- peer, req, wait := d.nextRequest()
- if req != nil && wait == 0 {
- chn := req.sentChn // save sentChn because remove sets it to nil
- d.remove(req)
- send := req.request(peer)
- if send != nil {
- peer.queueSend(send)
- requestSendDelay.Update(time.Duration(d.clock.Now() - req.enterQueue))
- }
- chn <- peer
- close(chn)
- } else {
- if wait == 0 {
- // no request to send and nothing to wait for; the next
- // queued request will wake up the loop
- break loop
- }
- d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received
- if wait > distMaxWait {
- // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically
- wait = distMaxWait
- }
- go func() {
- d.clock.Sleep(wait)
- d.loopChn <- struct{}{}
- }()
- break loop
- }
- }
- d.lock.Unlock()
- }
- }
-}
-
-// selectPeerItem represents a peer to be selected for a request by weightedRandomSelect
-type selectPeerItem struct {
- peer distPeer
- req *distReq
- weight uint64
-}
-
-func selectPeerWeight(i interface{}) uint64 {
- return i.(selectPeerItem).weight
-}
-
-// nextRequest returns the next possible request from any peer, along with the
-// associated peer and necessary waiting time
-func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) {
- checkedPeers := make(map[distPeer]struct{})
- elem := d.reqQueue.Front()
- var (
- bestWait time.Duration
- sel *utils.WeightedRandomSelect
- )
-
- d.peerLock.RLock()
- defer d.peerLock.RUnlock()
-
- peerCount := len(d.peers)
- for (len(checkedPeers) < peerCount || elem == d.reqQueue.Front()) && elem != nil {
- req := elem.Value.(*distReq)
- canSend := false
- now := d.clock.Now()
- if req.waitForPeers > now {
- canSend = true
- wait := time.Duration(req.waitForPeers - now)
- if bestWait == 0 || wait < bestWait {
- bestWait = wait
- }
- }
- for peer := range d.peers {
- if _, ok := checkedPeers[peer]; !ok && peer.canQueue() && req.canSend(peer) {
- canSend = true
- cost := req.getCost(peer)
- wait, bufRemain := peer.waitBefore(cost)
- if wait == 0 {
- if sel == nil {
- sel = utils.NewWeightedRandomSelect(selectPeerWeight)
- }
- sel.Update(selectPeerItem{peer: peer, req: req, weight: uint64(bufRemain*1000000) + 1})
- } else {
- if bestWait == 0 || wait < bestWait {
- bestWait = wait
- }
- }
- checkedPeers[peer] = struct{}{}
- }
- }
- next := elem.Next()
- if !canSend && elem == d.reqQueue.Front() {
- close(req.sentChn)
- d.remove(req)
- }
- elem = next
- }
-
- if sel != nil {
- c := sel.Choose().(selectPeerItem)
- return c.peer, c.req, 0
- }
- return nil, nil, bestWait
-}
-
-// queue adds a request to the distribution queue, returns a channel where the
-// receiving peer is sent once the request has been sent (request callback returned).
-// If the request is cancelled or timed out without suitable peers, the channel is
-// closed without sending any peer references to it.
-func (d *requestDistributor) queue(r *distReq) chan distPeer {
- d.lock.Lock()
- defer d.lock.Unlock()
-
- if r.reqOrder == 0 {
- d.lastReqOrder++
- r.reqOrder = d.lastReqOrder
- r.waitForPeers = d.clock.Now().Add(waitForPeers)
- }
- // Assign the timestamp when the request is queued no matter it's
- // a new one or re-queued one.
- r.enterQueue = d.clock.Now()
-
- back := d.reqQueue.Back()
- if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder {
- r.element = d.reqQueue.PushBack(r)
- } else {
- before := d.reqQueue.Front()
- for before.Value.(*distReq).reqOrder < r.reqOrder {
- before = before.Next()
- }
- r.element = d.reqQueue.InsertBefore(r, before)
- }
-
- if !d.loopNextSent {
- d.loopNextSent = true
- d.loopChn <- struct{}{}
- }
-
- r.sentChn = make(chan distPeer, 1)
- return r.sentChn
-}
-
-// cancel removes a request from the queue if it has not been sent yet (returns
-// false if it has been sent already). It is guaranteed that the callback functions
-// will not be called after cancel returns.
-func (d *requestDistributor) cancel(r *distReq) bool {
- d.lock.Lock()
- defer d.lock.Unlock()
-
- if r.sentChn == nil {
- return false
- }
-
- close(r.sentChn)
- d.remove(r)
- return true
-}
-
-// remove removes a request from the queue
-func (d *requestDistributor) remove(r *distReq) {
- r.sentChn = nil
- if r.element != nil {
- d.reqQueue.Remove(r.element)
- r.element = nil
- }
-}
-
-func (d *requestDistributor) close() {
- close(d.closeCh)
- d.wg.Wait()
-}
diff --git a/les/distributor_test.go b/les/distributor_test.go
deleted file mode 100644
index 9a93dba14519..000000000000
--- a/les/distributor_test.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "math/rand"
- "sync"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-type testDistReq struct {
- cost, procTime, order uint64
- canSendTo map[*testDistPeer]struct{}
-}
-
-func (r *testDistReq) getCost(dp distPeer) uint64 {
- return r.cost
-}
-
-func (r *testDistReq) canSend(dp distPeer) bool {
- _, ok := r.canSendTo[dp.(*testDistPeer)]
- return ok
-}
-
-func (r *testDistReq) request(dp distPeer) func() {
- return func() { dp.(*testDistPeer).send(r) }
-}
-
-type testDistPeer struct {
- sent []*testDistReq
- sumCost uint64
- lock sync.RWMutex
-}
-
-func (p *testDistPeer) send(r *testDistReq) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- p.sent = append(p.sent, r)
- p.sumCost += r.cost
-}
-
-func (p *testDistPeer) worker(t *testing.T, checkOrder bool, stop chan struct{}) {
- var last uint64
- for {
- wait := time.Millisecond
- p.lock.Lock()
- if len(p.sent) > 0 {
- rq := p.sent[0]
- wait = time.Duration(rq.procTime)
- p.sumCost -= rq.cost
- if checkOrder {
- if rq.order <= last {
- t.Errorf("Requests processed in wrong order")
- }
- last = rq.order
- }
- p.sent = p.sent[1:]
- }
- p.lock.Unlock()
- select {
- case <-stop:
- return
- case <-time.After(wait):
- }
- }
-}
-
-const (
- testDistBufLimit = 10000000
- testDistMaxCost = 1000000
- testDistPeerCount = 2
- testDistReqCount = 10
- testDistMaxResendCount = 3
-)
-
-func (p *testDistPeer) waitBefore(cost uint64) (time.Duration, float64) {
- p.lock.RLock()
- sumCost := p.sumCost + cost
- p.lock.RUnlock()
- if sumCost < testDistBufLimit {
- return 0, float64(testDistBufLimit-sumCost) / float64(testDistBufLimit)
- }
- return time.Duration(sumCost - testDistBufLimit), 0
-}
-
-func (p *testDistPeer) canQueue() bool {
- return true
-}
-
-func (p *testDistPeer) queueSend(f func()) bool {
- f()
- return true
-}
-
-func TestRequestDistributor(t *testing.T) {
- testRequestDistributor(t, false)
-}
-
-func TestRequestDistributorResend(t *testing.T) {
- testRequestDistributor(t, true)
-}
-
-func testRequestDistributor(t *testing.T, resend bool) {
- stop := make(chan struct{})
- defer close(stop)
-
- dist := newRequestDistributor(nil, &mclock.System{})
- var peers [testDistPeerCount]*testDistPeer
- for i := range peers {
- peers[i] = &testDistPeer{}
- go peers[i].worker(t, !resend, stop)
- dist.registerTestPeer(peers[i])
- }
- // Disable the mechanism that we will wait a few time for request
- // even there is no suitable peer to send right now.
- waitForPeers = 0
-
- var wg sync.WaitGroup
-
- for i := 1; i <= testDistReqCount; i++ {
- cost := uint64(rand.Int63n(testDistMaxCost))
- procTime := uint64(rand.Int63n(int64(cost + 1)))
- rq := &testDistReq{
- cost: cost,
- procTime: procTime,
- order: uint64(i),
- canSendTo: make(map[*testDistPeer]struct{}),
- }
- for _, peer := range peers {
- if rand.Intn(2) != 0 {
- rq.canSendTo[peer] = struct{}{}
- }
- }
-
- wg.Add(1)
- req := &distReq{
- getCost: rq.getCost,
- canSend: rq.canSend,
- request: rq.request,
- }
- chn := dist.queue(req)
- go func() {
- cnt := 1
- if resend && len(rq.canSendTo) != 0 {
- cnt = rand.Intn(testDistMaxResendCount) + 1
- }
- for i := 0; i < cnt; i++ {
- if i != 0 {
- chn = dist.queue(req)
- }
- p := <-chn
- if p == nil {
- if len(rq.canSendTo) != 0 {
- t.Errorf("Request that could have been sent was dropped")
- }
- } else {
- peer := p.(*testDistPeer)
- if _, ok := rq.canSendTo[peer]; !ok {
- t.Errorf("Request sent to wrong peer")
- }
- }
- }
- wg.Done()
- }()
- if rand.Intn(1000) == 0 {
- time.Sleep(time.Duration(rand.Intn(5000000)))
- }
- }
-
- wg.Wait()
-}
diff --git a/les/enr_entry.go b/les/enr_entry.go
deleted file mode 100644
index 307313fb10f8..000000000000
--- a/les/enr_entry.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/p2p/dnsdisc"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// lesEntry is the "les" ENR entry. This is set for LES servers only.
-type lesEntry struct {
- // Ignore additional fields (for forward compatibility).
- VfxVersion uint
- Rest []rlp.RawValue `rlp:"tail"`
-}
-
-func (lesEntry) ENRKey() string { return "les" }
-
-// ethEntry is the "eth" ENR entry. This is redeclared here to avoid depending on package eth.
-type ethEntry struct {
- ForkID forkid.ID
- Tail []rlp.RawValue `rlp:"tail"`
-}
-
-func (ethEntry) ENRKey() string { return "eth" }
-
-// setupDiscovery creates the node discovery source for the eth protocol.
-func (eth *LightEthereum) setupDiscovery() (enode.Iterator, error) {
- it := enode.NewFairMix(0)
-
- // Enable DNS discovery.
- if len(eth.config.EthDiscoveryURLs) != 0 {
- client := dnsdisc.NewClient(dnsdisc.Config{})
- dns, err := client.NewIterator(eth.config.EthDiscoveryURLs...)
- if err != nil {
- return nil, err
- }
- it.AddSource(dns)
- }
-
- // Enable DHT.
- if eth.udpEnabled {
- it.AddSource(eth.p2pServer.DiscV5.RandomNodes())
- }
-
- forkFilter := forkid.NewFilter(eth.blockchain)
- iterator := enode.Filter(it, func(n *enode.Node) bool { return nodeIsServer(forkFilter, n) })
- return iterator, nil
-}
-
-// nodeIsServer checks whether n is an LES server node.
-func nodeIsServer(forkFilter forkid.Filter, n *enode.Node) bool {
- var les lesEntry
- var eth ethEntry
- return n.Load(&les) == nil && n.Load(ð) == nil && forkFilter(eth.ForkID) == nil
-}
diff --git a/les/flowcontrol/control.go b/les/flowcontrol/control.go
deleted file mode 100644
index 76a241fa5a7f..000000000000
--- a/les/flowcontrol/control.go
+++ /dev/null
@@ -1,433 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package flowcontrol implements a client side flow control mechanism
-package flowcontrol
-
-import (
- "fmt"
- "math"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/log"
-)
-
-const (
- // fcTimeConst is the time constant applied for MinRecharge during linear
- // buffer recharge period
- fcTimeConst = time.Millisecond
- // DecParamDelay is applied at server side when decreasing capacity in order to
- // avoid a buffer underrun error due to requests sent by the client before
- // receiving the capacity update announcement
- DecParamDelay = time.Second * 2
- // keepLogs is the duration of keeping logs; logging is not used if zero
- keepLogs = 0
-)
-
-// ServerParams are the flow control parameters specified by a server for a client
-//
-// Note: a server can assign different amounts of capacity to each client by giving
-// different parameters to them.
-type ServerParams struct {
- BufLimit, MinRecharge uint64
-}
-
-// scheduledUpdate represents a delayed flow control parameter update
-type scheduledUpdate struct {
- time mclock.AbsTime
- params ServerParams
-}
-
-// ClientNode is the flow control system's representation of a client
-// (used in server mode only)
-type ClientNode struct {
- params ServerParams
- bufValue int64
- lastTime mclock.AbsTime
- updateSchedule []scheduledUpdate
- sumCost uint64 // sum of req costs received from this client
- accepted map[uint64]uint64 // value = sumCost after accepting the given req
- connected bool
- lock sync.Mutex
- cm *ClientManager
- log *logger
- cmNodeFields
-}
-
-// NewClientNode returns a new ClientNode
-func NewClientNode(cm *ClientManager, params ServerParams) *ClientNode {
- node := &ClientNode{
- cm: cm,
- params: params,
- bufValue: int64(params.BufLimit),
- lastTime: cm.clock.Now(),
- accepted: make(map[uint64]uint64),
- connected: true,
- }
- if keepLogs > 0 {
- node.log = newLogger(keepLogs)
- }
- cm.connect(node)
- return node
-}
-
-// Disconnect should be called when a client is disconnected
-func (node *ClientNode) Disconnect() {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- node.connected = false
- node.cm.disconnect(node)
-}
-
-// BufferStatus returns the current buffer value and limit
-func (node *ClientNode) BufferStatus() (uint64, uint64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- if !node.connected {
- return 0, 0
- }
- now := node.cm.clock.Now()
- node.update(now)
- node.cm.updateBuffer(node, 0, now)
- bv := node.bufValue
- if bv < 0 {
- bv = 0
- }
- return uint64(bv), node.params.BufLimit
-}
-
-// OneTimeCost subtracts the given amount from the node's buffer.
-//
-// Note: this call can take the buffer into the negative region internally.
-// In this case zero buffer value is returned by exported calls and no requests
-// are accepted.
-func (node *ClientNode) OneTimeCost(cost uint64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.cm.clock.Now()
- node.update(now)
- node.bufValue -= int64(cost)
- node.cm.updateBuffer(node, -int64(cost), now)
-}
-
-// Freeze notifies the client manager about a client freeze event in which case
-// the total capacity allowance is slightly reduced.
-func (node *ClientNode) Freeze() {
- node.lock.Lock()
- frozenCap := node.params.MinRecharge
- node.lock.Unlock()
- node.cm.reduceTotalCapacity(frozenCap)
-}
-
-// update recalculates the buffer value at a specified time while also performing
-// scheduled flow control parameter updates if necessary
-func (node *ClientNode) update(now mclock.AbsTime) {
- for len(node.updateSchedule) > 0 && node.updateSchedule[0].time <= now {
- node.recalcBV(node.updateSchedule[0].time)
- node.updateParams(node.updateSchedule[0].params, now)
- node.updateSchedule = node.updateSchedule[1:]
- }
- node.recalcBV(now)
-}
-
-// recalcBV recalculates the buffer value at a specified time
-func (node *ClientNode) recalcBV(now mclock.AbsTime) {
- dt := uint64(now - node.lastTime)
- if now < node.lastTime {
- dt = 0
- }
- node.bufValue += int64(node.params.MinRecharge * dt / uint64(fcTimeConst))
- if node.bufValue > int64(node.params.BufLimit) {
- node.bufValue = int64(node.params.BufLimit)
- }
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("updated bv=%d MRR=%d BufLimit=%d", node.bufValue, node.params.MinRecharge, node.params.BufLimit))
- }
- node.lastTime = now
-}
-
-// UpdateParams updates the flow control parameters of a client node
-func (node *ClientNode) UpdateParams(params ServerParams) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.cm.clock.Now()
- node.update(now)
- if params.MinRecharge >= node.params.MinRecharge {
- node.updateSchedule = nil
- node.updateParams(params, now)
- } else {
- for i, s := range node.updateSchedule {
- if params.MinRecharge >= s.params.MinRecharge {
- s.params = params
- node.updateSchedule = node.updateSchedule[:i+1]
- return
- }
- }
- node.updateSchedule = append(node.updateSchedule, scheduledUpdate{time: now.Add(DecParamDelay), params: params})
- }
-}
-
-// updateParams updates the flow control parameters of the node
-func (node *ClientNode) updateParams(params ServerParams, now mclock.AbsTime) {
- diff := int64(params.BufLimit - node.params.BufLimit)
- if diff > 0 {
- node.bufValue += diff
- } else if node.bufValue > int64(params.BufLimit) {
- node.bufValue = int64(params.BufLimit)
- }
- node.cm.updateParams(node, params, now)
-}
-
-// AcceptRequest returns whether a new request can be accepted and the missing
-// buffer amount if it was rejected due to a buffer underrun. If accepted, maxCost
-// is deducted from the flow control buffer.
-func (node *ClientNode) AcceptRequest(reqID, index, maxCost uint64) (accepted bool, bufShort uint64, priority int64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.cm.clock.Now()
- node.update(now)
- if int64(maxCost) > node.bufValue {
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("rejected reqID=%d bv=%d maxCost=%d", reqID, node.bufValue, maxCost))
- node.log.dump(now)
- }
- return false, maxCost - uint64(node.bufValue), 0
- }
- node.bufValue -= int64(maxCost)
- node.sumCost += maxCost
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("accepted reqID=%d bv=%d maxCost=%d sumCost=%d", reqID, node.bufValue, maxCost, node.sumCost))
- }
- node.accepted[index] = node.sumCost
- return true, 0, node.cm.accepted(node, maxCost, now)
-}
-
-// RequestProcessed should be called when the request has been processed
-func (node *ClientNode) RequestProcessed(reqID, index, maxCost, realCost uint64) uint64 {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.cm.clock.Now()
- node.update(now)
- node.cm.processed(node, maxCost, realCost, now)
- bv := node.bufValue + int64(node.sumCost-node.accepted[index])
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("processed reqID=%d bv=%d maxCost=%d realCost=%d sumCost=%d oldSumCost=%d reportedBV=%d", reqID, node.bufValue, maxCost, realCost, node.sumCost, node.accepted[index], bv))
- }
- delete(node.accepted, index)
- if bv < 0 {
- return 0
- }
- return uint64(bv)
-}
-
-// ServerNode is the flow control system's representation of a server
-// (used in client mode only)
-type ServerNode struct {
- clock mclock.Clock
- bufEstimate uint64
- bufRecharge bool
- lastTime mclock.AbsTime
- params ServerParams
- sumCost uint64 // sum of req costs sent to this server
- pending map[uint64]uint64 // value = sumCost after sending the given req
- log *logger
- lock sync.RWMutex
-}
-
-// NewServerNode returns a new ServerNode
-func NewServerNode(params ServerParams, clock mclock.Clock) *ServerNode {
- node := &ServerNode{
- clock: clock,
- bufEstimate: params.BufLimit,
- bufRecharge: false,
- lastTime: clock.Now(),
- params: params,
- pending: make(map[uint64]uint64),
- }
- if keepLogs > 0 {
- node.log = newLogger(keepLogs)
- }
- return node
-}
-
-// UpdateParams updates the flow control parameters of the node
-func (node *ServerNode) UpdateParams(params ServerParams) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- node.recalcBLE(mclock.Now())
- if params.BufLimit > node.params.BufLimit {
- node.bufEstimate += params.BufLimit - node.params.BufLimit
- } else {
- if node.bufEstimate > params.BufLimit {
- node.bufEstimate = params.BufLimit
- }
- }
- node.params = params
-}
-
-// recalcBLE recalculates the lowest estimate for the client's buffer value at
-// the given server at the specified time
-func (node *ServerNode) recalcBLE(now mclock.AbsTime) {
- if now < node.lastTime {
- return
- }
- if node.bufRecharge {
- dt := uint64(now - node.lastTime)
- node.bufEstimate += node.params.MinRecharge * dt / uint64(fcTimeConst)
- if node.bufEstimate >= node.params.BufLimit {
- node.bufEstimate = node.params.BufLimit
- node.bufRecharge = false
- }
- }
- node.lastTime = now
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("updated bufEst=%d MRR=%d BufLimit=%d", node.bufEstimate, node.params.MinRecharge, node.params.BufLimit))
- }
-}
-
-// safetyMargin is added to the flow control waiting time when estimated buffer value is low
-const safetyMargin = time.Millisecond
-
-// CanSend returns the minimum waiting time required before sending a request
-// with the given maximum estimated cost. Second return value is the relative
-// estimated buffer level after sending the request (divided by BufLimit).
-func (node *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) {
- node.lock.RLock()
- defer node.lock.RUnlock()
-
- if node.params.BufLimit == 0 {
- return time.Duration(math.MaxInt64), 0
- }
- now := node.clock.Now()
- node.recalcBLE(now)
- maxCost += uint64(safetyMargin) * node.params.MinRecharge / uint64(fcTimeConst)
- if maxCost > node.params.BufLimit {
- maxCost = node.params.BufLimit
- }
- if node.bufEstimate >= maxCost {
- relBuf := float64(node.bufEstimate-maxCost) / float64(node.params.BufLimit)
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("canSend bufEst=%d maxCost=%d true relBuf=%f", node.bufEstimate, maxCost, relBuf))
- }
- return 0, relBuf
- }
- timeLeft := time.Duration((maxCost - node.bufEstimate) * uint64(fcTimeConst) / node.params.MinRecharge)
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("canSend bufEst=%d maxCost=%d false timeLeft=%v", node.bufEstimate, maxCost, timeLeft))
- }
- return timeLeft, 0
-}
-
-// QueuedRequest should be called when the request has been assigned to the given
-// server node, before putting it in the send queue. It is mandatory that requests
-// are sent in the same order as the QueuedRequest calls are made.
-func (node *ServerNode) QueuedRequest(reqID, maxCost uint64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.clock.Now()
- node.recalcBLE(now)
- // Note: we do not know when requests actually arrive to the server so bufRecharge
- // is not turned on here if buffer was full; in this case it is going to be turned
- // on by the first reply's bufValue feedback
- if node.bufEstimate >= maxCost {
- node.bufEstimate -= maxCost
- } else {
- log.Error("Queued request with insufficient buffer estimate")
- node.bufEstimate = 0
- }
- node.sumCost += maxCost
- node.pending[reqID] = node.sumCost
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("queued reqID=%d bufEst=%d maxCost=%d sumCost=%d", reqID, node.bufEstimate, maxCost, node.sumCost))
- }
-}
-
-// ReceivedReply adjusts estimated buffer value according to the value included in
-// the latest request reply.
-func (node *ServerNode) ReceivedReply(reqID, bv uint64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- now := node.clock.Now()
- node.recalcBLE(now)
- if bv > node.params.BufLimit {
- bv = node.params.BufLimit
- }
- sc, ok := node.pending[reqID]
- if !ok {
- return
- }
- delete(node.pending, reqID)
- cc := node.sumCost - sc
- newEstimate := uint64(0)
- if bv > cc {
- newEstimate = bv - cc
- }
- if newEstimate > node.bufEstimate {
- // Note: we never reduce the buffer estimate based on the reported value because
- // this can only happen because of the delayed delivery of the latest reply.
- // The lowest estimate based on the previous reply can still be considered valid.
- node.bufEstimate = newEstimate
- }
-
- node.bufRecharge = node.bufEstimate < node.params.BufLimit
- node.lastTime = now
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("received reqID=%d bufEst=%d reportedBv=%d sumCost=%d oldSumCost=%d", reqID, node.bufEstimate, bv, node.sumCost, sc))
- }
-}
-
-// ResumeFreeze cleans all pending requests and sets the buffer estimate to the
-// reported value after resuming from a frozen state
-func (node *ServerNode) ResumeFreeze(bv uint64) {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- for reqID := range node.pending {
- delete(node.pending, reqID)
- }
- now := node.clock.Now()
- node.recalcBLE(now)
- if bv > node.params.BufLimit {
- bv = node.params.BufLimit
- }
- node.bufEstimate = bv
- node.bufRecharge = node.bufEstimate < node.params.BufLimit
- node.lastTime = now
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("unfreeze bv=%d sumCost=%d", bv, node.sumCost))
- }
-}
-
-// DumpLogs dumps the event log if logging is used
-func (node *ServerNode) DumpLogs() {
- node.lock.Lock()
- defer node.lock.Unlock()
-
- if node.log != nil {
- node.log.dump(node.clock.Now())
- }
-}
diff --git a/les/flowcontrol/logger.go b/les/flowcontrol/logger.go
deleted file mode 100644
index 428d7fbf22c9..000000000000
--- a/les/flowcontrol/logger.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package flowcontrol
-
-import (
- "fmt"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-// logger collects events in string format and discards events older than the
-// "keep" parameter
-type logger struct {
- events map[uint64]logEvent
- writePtr, delPtr uint64
- keep time.Duration
-}
-
-// logEvent describes a single event
-type logEvent struct {
- time mclock.AbsTime
- event string
-}
-
-// newLogger creates a new logger
-func newLogger(keep time.Duration) *logger {
- return &logger{
- events: make(map[uint64]logEvent),
- keep: keep,
- }
-}
-
-// add adds a new event and discards old events if possible
-func (l *logger) add(now mclock.AbsTime, event string) {
- keepAfter := now - mclock.AbsTime(l.keep)
- for l.delPtr < l.writePtr && l.events[l.delPtr].time <= keepAfter {
- delete(l.events, l.delPtr)
- l.delPtr++
- }
- l.events[l.writePtr] = logEvent{now, event}
- l.writePtr++
-}
-
-// dump prints all stored events
-func (l *logger) dump(now mclock.AbsTime) {
- for i := l.delPtr; i < l.writePtr; i++ {
- e := l.events[i]
- fmt.Println(time.Duration(e.time-now), e.event)
- }
-}
diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go
deleted file mode 100644
index b7cc9bd9030b..000000000000
--- a/les/flowcontrol/manager.go
+++ /dev/null
@@ -1,476 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package flowcontrol
-
-import (
- "fmt"
- "math"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/common/prque"
-)
-
-// cmNodeFields are ClientNode fields used by the client manager
-// Note: these fields are locked by the client manager's mutex
-type cmNodeFields struct {
- corrBufValue int64 // buffer value adjusted with the extra recharge amount
- rcLastIntValue int64 // past recharge integrator value when corrBufValue was last updated
- rcFullIntValue int64 // future recharge integrator value when corrBufValue will reach maximum
- queueIndex int // position in the recharge queue (-1 if not queued)
-}
-
-// FixedPointMultiplier is applied to the recharge integrator and the recharge curve.
-//
-// Note: fixed point arithmetic is required for the integrator because it is a
-// constantly increasing value that can wrap around int64 limits (which behavior is
-// also supported by the priority queue). A floating point value would gradually lose
-// precision in this application.
-// The recharge curve and all recharge values are encoded as fixed point because
-// sumRecharge is frequently updated by adding or subtracting individual recharge
-// values and perfect precision is required.
-const FixedPointMultiplier = 1000000
-
-var (
- capacityDropFactor = 0.1
- capacityRaiseTC = 1 / (3 * float64(time.Hour)) // time constant for raising the capacity factor
- capacityRaiseThresholdRatio = 1.125 // total/connected capacity ratio threshold for raising the capacity factor
-)
-
-// ClientManager controls the capacity assigned to the clients of a server.
-// Since ServerParams guarantee a safe lower estimate for processable requests
-// even in case of all clients being active, ClientManager calculates a
-// corrugated buffer value and usually allows a higher remaining buffer value
-// to be returned with each reply.
-type ClientManager struct {
- clock mclock.Clock
- lock sync.Mutex
- stop chan chan struct{}
-
- curve PieceWiseLinear
- sumRecharge, totalRecharge, totalConnected uint64
- logTotalCap, totalCapacity float64
- logTotalCapRaiseLimit float64
- minLogTotalCap, maxLogTotalCap float64
- capacityRaiseThreshold uint64
- capLastUpdate mclock.AbsTime
- totalCapacityCh chan uint64
-
- // recharge integrator is increasing in each moment with a rate of
- // (totalRecharge / sumRecharge)*FixedPointMultiplier or 0 if sumRecharge==0
- rcLastUpdate mclock.AbsTime // last time the recharge integrator was updated
- rcLastIntValue int64 // last updated value of the recharge integrator
- priorityOffset int64 // offset for prque priority values ensures that all priorities stay in the int64 range
- // recharge queue is a priority queue with currently recharging client nodes
- // as elements. The priority value is rcFullIntValue which allows to quickly
- // determine which client will first finish recharge.
- rcQueue *prque.Prque[int64, *ClientNode]
-}
-
-// NewClientManager returns a new client manager.
-// Client manager enhances flow control performance by allowing client buffers
-// to recharge quicker than the minimum guaranteed recharge rate if possible.
-// The sum of all minimum recharge rates (sumRecharge) is updated each time
-// a clients starts or finishes buffer recharging. Then an adjusted total
-// recharge rate is calculated using a piecewise linear recharge curve:
-//
-// totalRecharge = curve(sumRecharge)
-// (totalRecharge >= sumRecharge is enforced)
-//
-// Then the "bonus" buffer recharge is distributed between currently recharging
-// clients proportionally to their minimum recharge rates.
-//
-// Note: total recharge is proportional to the average number of parallel running
-// serving threads. A recharge value of 1000000 corresponds to one thread in average.
-// The maximum number of allowed serving threads should always be considerably
-// higher than the targeted average number.
-//
-// Note 2: although it is possible to specify a curve allowing the total target
-// recharge starting from zero sumRecharge, it makes sense to add a linear ramp
-// starting from zero in order to not let a single low-priority client use up
-// the entire server capacity and thus ensure quick availability for others at
-// any moment.
-func NewClientManager(curve PieceWiseLinear, clock mclock.Clock) *ClientManager {
- cm := &ClientManager{
- clock: clock,
- rcQueue: prque.New[int64, *ClientNode](func(a *ClientNode, i int) { a.queueIndex = i }),
- capLastUpdate: clock.Now(),
- stop: make(chan chan struct{}),
- }
- if curve != nil {
- cm.SetRechargeCurve(curve)
- }
- go func() {
- // regularly recalculate and update total capacity
- for {
- select {
- case <-time.After(time.Minute):
- cm.lock.Lock()
- cm.updateTotalCapacity(cm.clock.Now(), true)
- cm.lock.Unlock()
- case stop := <-cm.stop:
- close(stop)
- return
- }
- }
- }()
- return cm
-}
-
-// Stop stops the client manager
-func (cm *ClientManager) Stop() {
- stop := make(chan struct{})
- cm.stop <- stop
- <-stop
-}
-
-// SetRechargeCurve updates the recharge curve
-func (cm *ClientManager) SetRechargeCurve(curve PieceWiseLinear) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- now := cm.clock.Now()
- cm.updateRecharge(now)
- cm.curve = curve
- if len(curve) > 0 {
- cm.totalRecharge = curve[len(curve)-1].Y
- } else {
- cm.totalRecharge = 0
- }
-}
-
-// SetCapacityLimits sets a threshold value used for raising capFactor.
-// Either if the difference between total allowed and connected capacity is less
-// than this threshold or if their ratio is less than capacityRaiseThresholdRatio
-// then capFactor is allowed to slowly raise.
-func (cm *ClientManager) SetCapacityLimits(min, max, raiseThreshold uint64) {
- if min < 1 {
- min = 1
- }
- cm.minLogTotalCap = math.Log(float64(min))
- if max < 1 {
- max = 1
- }
- cm.maxLogTotalCap = math.Log(float64(max))
- cm.logTotalCap = cm.maxLogTotalCap
- cm.capacityRaiseThreshold = raiseThreshold
- cm.refreshCapacity()
-}
-
-// connect should be called when a client is connected, before passing it to any
-// other ClientManager function
-func (cm *ClientManager) connect(node *ClientNode) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- now := cm.clock.Now()
- cm.updateRecharge(now)
- node.corrBufValue = int64(node.params.BufLimit)
- node.rcLastIntValue = cm.rcLastIntValue
- node.queueIndex = -1
- cm.updateTotalCapacity(now, true)
- cm.totalConnected += node.params.MinRecharge
- cm.updateRaiseLimit()
-}
-
-// disconnect should be called when a client is disconnected
-func (cm *ClientManager) disconnect(node *ClientNode) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- now := cm.clock.Now()
- cm.updateRecharge(cm.clock.Now())
- cm.updateTotalCapacity(now, true)
- cm.totalConnected -= node.params.MinRecharge
- cm.updateRaiseLimit()
-}
-
-// accepted is called when a request with given maximum cost is accepted.
-// It returns a priority indicator for the request which is used to determine placement
-// in the serving queue. Older requests have higher priority by default. If the client
-// is almost out of buffer, request priority is reduced.
-func (cm *ClientManager) accepted(node *ClientNode, maxCost uint64, now mclock.AbsTime) (priority int64) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- cm.updateNodeRc(node, -int64(maxCost), &node.params, now)
- rcTime := (node.params.BufLimit - uint64(node.corrBufValue)) * FixedPointMultiplier / node.params.MinRecharge
- return -int64(now) - int64(rcTime)
-}
-
-// processed updates the client buffer according to actual request cost after
-// serving has been finished.
-//
-// Note: processed should always be called for all accepted requests
-func (cm *ClientManager) processed(node *ClientNode, maxCost, realCost uint64, now mclock.AbsTime) {
- if realCost > maxCost {
- realCost = maxCost
- }
- cm.updateBuffer(node, int64(maxCost-realCost), now)
-}
-
-// updateBuffer recalculates the corrected buffer value, adds the given value to it
-// and updates the node's actual buffer value if possible
-func (cm *ClientManager) updateBuffer(node *ClientNode, add int64, now mclock.AbsTime) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- cm.updateNodeRc(node, add, &node.params, now)
- if node.corrBufValue > node.bufValue {
- if node.log != nil {
- node.log.add(now, fmt.Sprintf("corrected bv=%d oldBv=%d", node.corrBufValue, node.bufValue))
- }
- node.bufValue = node.corrBufValue
- }
-}
-
-// updateParams updates the flow control parameters of a client node
-func (cm *ClientManager) updateParams(node *ClientNode, params ServerParams, now mclock.AbsTime) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- cm.updateRecharge(now)
- cm.updateTotalCapacity(now, true)
- cm.totalConnected += params.MinRecharge - node.params.MinRecharge
- cm.updateRaiseLimit()
- cm.updateNodeRc(node, 0, ¶ms, now)
-}
-
-// updateRaiseLimit recalculates the limiting value until which logTotalCap
-// can be raised when no client freeze events occur
-func (cm *ClientManager) updateRaiseLimit() {
- if cm.capacityRaiseThreshold == 0 {
- cm.logTotalCapRaiseLimit = 0
- return
- }
- limit := float64(cm.totalConnected + cm.capacityRaiseThreshold)
- limit2 := float64(cm.totalConnected) * capacityRaiseThresholdRatio
- if limit2 > limit {
- limit = limit2
- }
- if limit < 1 {
- limit = 1
- }
- cm.logTotalCapRaiseLimit = math.Log(limit)
-}
-
-// updateRecharge updates the recharge integrator and checks the recharge queue
-// for nodes with recently filled buffers
-func (cm *ClientManager) updateRecharge(now mclock.AbsTime) {
- lastUpdate := cm.rcLastUpdate
- cm.rcLastUpdate = now
- // updating is done in multiple steps if node buffers are filled and sumRecharge
- // is decreased before the given target time
- for cm.sumRecharge > 0 {
- sumRecharge := cm.sumRecharge
- if sumRecharge > cm.totalRecharge {
- sumRecharge = cm.totalRecharge
- }
- bonusRatio := float64(1)
- v := cm.curve.ValueAt(sumRecharge)
- s := float64(sumRecharge)
- if v > s && s > 0 {
- bonusRatio = v / s
- }
- dt := now - lastUpdate
- // fetch the client that finishes first
- rcqNode := cm.rcQueue.PopItem() // if sumRecharge > 0 then the queue cannot be empty
- // check whether it has already finished
- dtNext := mclock.AbsTime(float64(rcqNode.rcFullIntValue-cm.rcLastIntValue) / bonusRatio)
- if dt < dtNext {
- // not finished yet, put it back, update integrator according
- // to current bonusRatio and return
- cm.addToQueue(rcqNode)
- cm.rcLastIntValue += int64(bonusRatio * float64(dt))
- return
- }
- lastUpdate += dtNext
- // finished recharging, update corrBufValue and sumRecharge if necessary and do next step
- if rcqNode.corrBufValue < int64(rcqNode.params.BufLimit) {
- rcqNode.corrBufValue = int64(rcqNode.params.BufLimit)
- cm.sumRecharge -= rcqNode.params.MinRecharge
- }
- cm.rcLastIntValue = rcqNode.rcFullIntValue
- }
-}
-
-func (cm *ClientManager) addToQueue(node *ClientNode) {
- if cm.priorityOffset-node.rcFullIntValue < -0x4000000000000000 {
- cm.priorityOffset += 0x4000000000000000
- // recreate priority queue with new offset to avoid overflow; should happen very rarely
- newRcQueue := prque.New[int64, *ClientNode](func(a *ClientNode, i int) { a.queueIndex = i })
- for cm.rcQueue.Size() > 0 {
- n := cm.rcQueue.PopItem()
- newRcQueue.Push(n, cm.priorityOffset-n.rcFullIntValue)
- }
- cm.rcQueue = newRcQueue
- }
- cm.rcQueue.Push(node, cm.priorityOffset-node.rcFullIntValue)
-}
-
-// updateNodeRc updates a node's corrBufValue and adds an external correction value.
-// It also adds or removes the rcQueue entry and updates ServerParams and sumRecharge if necessary.
-func (cm *ClientManager) updateNodeRc(node *ClientNode, bvc int64, params *ServerParams, now mclock.AbsTime) {
- cm.updateRecharge(now)
- wasFull := true
- if node.corrBufValue != int64(node.params.BufLimit) {
- wasFull = false
- node.corrBufValue += (cm.rcLastIntValue - node.rcLastIntValue) * int64(node.params.MinRecharge) / FixedPointMultiplier
- if node.corrBufValue > int64(node.params.BufLimit) {
- node.corrBufValue = int64(node.params.BufLimit)
- }
- node.rcLastIntValue = cm.rcLastIntValue
- }
- node.corrBufValue += bvc
- diff := int64(params.BufLimit - node.params.BufLimit)
- if diff > 0 {
- node.corrBufValue += diff
- }
- isFull := false
- if node.corrBufValue >= int64(params.BufLimit) {
- node.corrBufValue = int64(params.BufLimit)
- isFull = true
- }
- if !wasFull {
- cm.sumRecharge -= node.params.MinRecharge
- }
- if params != &node.params {
- node.params = *params
- }
- if !isFull {
- cm.sumRecharge += node.params.MinRecharge
- if node.queueIndex != -1 {
- cm.rcQueue.Remove(node.queueIndex)
- }
- node.rcLastIntValue = cm.rcLastIntValue
- node.rcFullIntValue = cm.rcLastIntValue + (int64(node.params.BufLimit)-node.corrBufValue)*FixedPointMultiplier/int64(node.params.MinRecharge)
- cm.addToQueue(node)
- }
-}
-
-// reduceTotalCapacity reduces the total capacity allowance in case of a client freeze event
-func (cm *ClientManager) reduceTotalCapacity(frozenCap uint64) {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- ratio := float64(1)
- if frozenCap < cm.totalConnected {
- ratio = float64(frozenCap) / float64(cm.totalConnected)
- }
- now := cm.clock.Now()
- cm.updateTotalCapacity(now, false)
- cm.logTotalCap -= capacityDropFactor * ratio
- if cm.logTotalCap < cm.minLogTotalCap {
- cm.logTotalCap = cm.minLogTotalCap
- }
- cm.updateTotalCapacity(now, true)
-}
-
-// updateTotalCapacity updates the total capacity factor. The capacity factor allows
-// the total capacity of the system to go over the allowed total recharge value
-// if clients go to frozen state sufficiently rarely.
-// The capacity factor is dropped instantly by a small amount if a clients is frozen.
-// It is raised slowly (with a large time constant) if the total connected capacity
-// is close to the total allowed amount and no clients are frozen.
-func (cm *ClientManager) updateTotalCapacity(now mclock.AbsTime, refresh bool) {
- dt := now - cm.capLastUpdate
- cm.capLastUpdate = now
-
- if cm.logTotalCap < cm.logTotalCapRaiseLimit {
- cm.logTotalCap += capacityRaiseTC * float64(dt)
- if cm.logTotalCap > cm.logTotalCapRaiseLimit {
- cm.logTotalCap = cm.logTotalCapRaiseLimit
- }
- }
- if cm.logTotalCap > cm.maxLogTotalCap {
- cm.logTotalCap = cm.maxLogTotalCap
- }
- if refresh {
- cm.refreshCapacity()
- }
-}
-
-// refreshCapacity recalculates the total capacity value and sends an update to the subscription
-// channel if the relative change of the value since the last update is more than 0.1 percent
-func (cm *ClientManager) refreshCapacity() {
- totalCapacity := math.Exp(cm.logTotalCap)
- if totalCapacity >= cm.totalCapacity*0.999 && totalCapacity <= cm.totalCapacity*1.001 {
- return
- }
- cm.totalCapacity = totalCapacity
- if cm.totalCapacityCh != nil {
- select {
- case cm.totalCapacityCh <- uint64(cm.totalCapacity):
- default:
- }
- }
-}
-
-// SubscribeTotalCapacity returns all future updates to the total capacity value
-// through a channel and also returns the current value
-func (cm *ClientManager) SubscribeTotalCapacity(ch chan uint64) uint64 {
- cm.lock.Lock()
- defer cm.lock.Unlock()
-
- cm.totalCapacityCh = ch
- return uint64(cm.totalCapacity)
-}
-
-// PieceWiseLinear is used to describe recharge curves
-type PieceWiseLinear []struct{ X, Y uint64 }
-
-// ValueAt returns the curve's value at a given point
-func (pwl PieceWiseLinear) ValueAt(x uint64) float64 {
- l := 0
- h := len(pwl)
- if h == 0 {
- return 0
- }
- for h != l {
- m := (l + h) / 2
- if x > pwl[m].X {
- l = m + 1
- } else {
- h = m
- }
- }
- if l == 0 {
- return float64(pwl[0].Y)
- }
- l--
- if h == len(pwl) {
- return float64(pwl[l].Y)
- }
- dx := pwl[h].X - pwl[l].X
- if dx < 1 {
- return float64(pwl[l].Y)
- }
- return float64(pwl[l].Y) + float64(pwl[h].Y-pwl[l].Y)*float64(x-pwl[l].X)/float64(dx)
-}
-
-// Valid returns true if the X coordinates of the curve points are non-strictly monotonic
-func (pwl PieceWiseLinear) Valid() bool {
- var lastX uint64
- for _, i := range pwl {
- if i.X < lastX {
- return false
- }
- lastX = i.X
- }
- return true
-}
diff --git a/les/flowcontrol/manager_test.go b/les/flowcontrol/manager_test.go
deleted file mode 100644
index 3afc31272f54..000000000000
--- a/les/flowcontrol/manager_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package flowcontrol
-
-import (
- "math"
- "math/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-type testNode struct {
- node *ClientNode
- bufLimit, capacity uint64
- waitUntil mclock.AbsTime
- index, totalCost uint64
-}
-
-const (
- testMaxCost = 1000000
- testLength = 100000
-)
-
-// testConstantTotalCapacity simulates multiple request sender nodes and verifies
-// whether the total amount of served requests matches the expected value based on
-// the total capacity and the duration of the test.
-// Some nodes are sending requests occasionally so that their buffer should regularly
-// reach the maximum while other nodes (the "max capacity nodes") are sending at the
-// maximum permitted rate. The max capacity nodes are changed multiple times during
-// a single test.
-func TestConstantTotalCapacity(t *testing.T) {
- testConstantTotalCapacity(t, 10, 1, 0, false)
- testConstantTotalCapacity(t, 10, 1, 1, false)
- testConstantTotalCapacity(t, 30, 1, 0, false)
- testConstantTotalCapacity(t, 30, 2, 3, false)
- testConstantTotalCapacity(t, 100, 1, 0, false)
- testConstantTotalCapacity(t, 100, 3, 5, false)
- testConstantTotalCapacity(t, 100, 5, 10, false)
- testConstantTotalCapacity(t, 100, 3, 5, true)
-}
-
-func testConstantTotalCapacity(t *testing.T, nodeCount, maxCapacityNodes, randomSend int, priorityOverflow bool) {
- clock := &mclock.Simulated{}
- nodes := make([]*testNode, nodeCount)
- var totalCapacity uint64
- for i := range nodes {
- nodes[i] = &testNode{capacity: uint64(50000 + rand.Intn(100000))}
- totalCapacity += nodes[i].capacity
- }
- m := NewClientManager(PieceWiseLinear{{0, totalCapacity}}, clock)
- if priorityOverflow {
- // provoke a situation where rcLastUpdate overflow needs to be handled
- m.rcLastIntValue = math.MaxInt64 - 10000000000
- }
- for _, n := range nodes {
- n.bufLimit = n.capacity * 6000
- n.node = NewClientNode(m, ServerParams{BufLimit: n.bufLimit, MinRecharge: n.capacity})
- }
- maxNodes := make([]int, maxCapacityNodes)
- for i := range maxNodes {
- // we don't care if some indexes are selected multiple times
- // in that case we have fewer max nodes
- maxNodes[i] = rand.Intn(nodeCount)
- }
-
- var sendCount int
- for i := 0; i < testLength; i++ {
- now := clock.Now()
- for _, idx := range maxNodes {
- for nodes[idx].send(t, now) {
- }
- }
- if rand.Intn(testLength) < maxCapacityNodes*3 {
- maxNodes[rand.Intn(maxCapacityNodes)] = rand.Intn(nodeCount)
- }
-
- sendCount += randomSend
- failCount := randomSend * 10
- for sendCount > 0 && failCount > 0 {
- if nodes[rand.Intn(nodeCount)].send(t, now) {
- sendCount--
- } else {
- failCount--
- }
- }
- clock.Run(time.Millisecond)
- }
-
- var totalCost uint64
- for _, n := range nodes {
- totalCost += n.totalCost
- }
- ratio := float64(totalCost) / float64(totalCapacity) / testLength
- if ratio < 0.98 || ratio > 1.02 {
- t.Errorf("totalCost/totalCapacity/testLength ratio incorrect (expected: 1, got: %f)", ratio)
- }
-}
-
-func (n *testNode) send(t *testing.T, now mclock.AbsTime) bool {
- if now < n.waitUntil {
- return false
- }
- n.index++
- if ok, _, _ := n.node.AcceptRequest(0, n.index, testMaxCost); !ok {
- t.Fatalf("Rejected request after expected waiting time has passed")
- }
- rcost := uint64(rand.Int63n(testMaxCost))
- bv := n.node.RequestProcessed(0, n.index, testMaxCost, rcost)
- if bv < testMaxCost {
- n.waitUntil = now + mclock.AbsTime((testMaxCost-bv)*1001000/n.capacity)
- }
- n.totalCost += rcost
- return true
-}
diff --git a/les/handler_test.go b/les/handler_test.go
deleted file mode 100644
index c803a5ddb3f2..000000000000
--- a/les/handler_test.go
+++ /dev/null
@@ -1,754 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "encoding/binary"
- "math/big"
- "math/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error {
- type resp struct {
- ReqID, BV uint64
- Data interface{}
- }
- return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data})
-}
-
-// Tests that block headers can be retrieved from a remote chain based on user queries.
-func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) }
-func TestGetBlockHeadersLes3(t *testing.T) { testGetBlockHeaders(t, 3) }
-func TestGetBlockHeadersLes4(t *testing.T) { testGetBlockHeaders(t, 4) }
-
-func testGetBlockHeaders(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- blocks: downloader.MaxHeaderFetch + 15,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
- bc := server.handler.blockchain
-
- // Create a "random" unknown hash for testing
- var unknown common.Hash
- for i := range unknown {
- unknown[i] = byte(i)
- }
- // Create a batch of tests for various scenarios
- limit := uint64(MaxHeaderFetch)
- tests := []struct {
- query *GetBlockHeadersData // The query to execute for header retrieval
- expect []common.Hash // The hashes of the block whose headers are expected
- }{
- // A single random block should be retrievable by hash and number too
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
- []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()},
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
- []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()},
- },
- // Multiple headers should be retrievable in both directions
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
- []common.Hash{
- bc.GetBlockByNumber(limit / 2).Hash(),
- bc.GetBlockByNumber(limit/2 + 1).Hash(),
- bc.GetBlockByNumber(limit/2 + 2).Hash(),
- },
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
- []common.Hash{
- bc.GetBlockByNumber(limit / 2).Hash(),
- bc.GetBlockByNumber(limit/2 - 1).Hash(),
- bc.GetBlockByNumber(limit/2 - 2).Hash(),
- },
- },
- // Multiple headers with skip lists should be retrievable
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
- []common.Hash{
- bc.GetBlockByNumber(limit / 2).Hash(),
- bc.GetBlockByNumber(limit/2 + 4).Hash(),
- bc.GetBlockByNumber(limit/2 + 8).Hash(),
- },
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
- []common.Hash{
- bc.GetBlockByNumber(limit / 2).Hash(),
- bc.GetBlockByNumber(limit/2 - 4).Hash(),
- bc.GetBlockByNumber(limit/2 - 8).Hash(),
- },
- },
- // The chain endpoints should be retrievable
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
- []common.Hash{bc.GetBlockByNumber(0).Hash()},
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().Number.Uint64()}, Amount: 1},
- []common.Hash{bc.CurrentBlock().Hash()},
- },
- // Ensure protocol limits are honored
- //{
- // &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().Number.Uint64()() - 1}, Amount: limit + 10, Reverse: true},
- // []common.Hash{},
- //},
- // Check that requesting more than available is handled gracefully
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().Number.Uint64() - 4}, Skip: 3, Amount: 3},
- []common.Hash{
- bc.GetBlockByNumber(bc.CurrentBlock().Number.Uint64() - 4).Hash(),
- bc.GetBlockByNumber(bc.CurrentBlock().Number.Uint64()).Hash(),
- },
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
- []common.Hash{
- bc.GetBlockByNumber(4).Hash(),
- bc.GetBlockByNumber(0).Hash(),
- },
- },
- // Check that requesting more than available is handled gracefully, even if mid skip
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().Number.Uint64() - 4}, Skip: 2, Amount: 3},
- []common.Hash{
- bc.GetBlockByNumber(bc.CurrentBlock().Number.Uint64() - 4).Hash(),
- bc.GetBlockByNumber(bc.CurrentBlock().Number.Uint64() - 1).Hash(),
- },
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
- []common.Hash{
- bc.GetBlockByNumber(4).Hash(),
- bc.GetBlockByNumber(1).Hash(),
- },
- },
- // Check that non existing headers aren't returned
- {
- &GetBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
- []common.Hash{},
- }, {
- &GetBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().Number.Uint64() + 1}, Amount: 1},
- []common.Hash{},
- },
- }
- // Run each of the tests and verify the results against the chain
- var reqID uint64
- for i, tt := range tests {
- // Collect the headers to expect in the response
- var headers []*types.Header
- for _, hash := range tt.expect {
- headers = append(headers, bc.GetHeaderByHash(hash))
- }
- // Send the hash request and verify the response
- reqID++
-
- sendRequest(rawPeer.app, GetBlockHeadersMsg, reqID, tt.query)
- if err := expectResponse(rawPeer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil {
- t.Errorf("test %d: headers mismatch: %v", i, err)
- }
- }
-}
-
-// Tests that block contents can be retrieved from a remote chain based on their hashes.
-func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) }
-func TestGetBlockBodiesLes3(t *testing.T) { testGetBlockBodies(t, 3) }
-func TestGetBlockBodiesLes4(t *testing.T) { testGetBlockBodies(t, 4) }
-
-func testGetBlockBodies(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- blocks: downloader.MaxHeaderFetch + 15,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- // Create a batch of tests for various scenarios
- limit := MaxBodyFetch
- tests := []struct {
- random int // Number of blocks to fetch randomly from the chain
- explicit []common.Hash // Explicitly requested blocks
- available []bool // Availability of explicitly requested blocks
- expected int // Total number of existing blocks to expect
- }{
- {1, nil, nil, 1}, // A single random block should be retrievable
- {10, nil, nil, 10}, // Multiple random blocks should be retrievable
- {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable
- //{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
- {0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable
- {0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
- {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned
-
- // Existing and non-existing blocks interleaved should not cause problems
- {0, []common.Hash{
- {},
- bc.GetBlockByNumber(1).Hash(),
- {},
- bc.GetBlockByNumber(10).Hash(),
- {},
- bc.GetBlockByNumber(100).Hash(),
- {},
- }, []bool{false, true, false, true, false, true, false}, 3},
- }
- // Run each of the tests and verify the results against the chain
- var reqID uint64
- for i, tt := range tests {
- // Collect the hashes to request, and the response to expect
- var hashes []common.Hash
- seen := make(map[int64]bool)
- var bodies []*types.Body
-
- for j := 0; j < tt.random; j++ {
- for {
- num := rand.Int63n(int64(bc.CurrentBlock().Number.Uint64()))
- if !seen[num] {
- seen[num] = true
-
- block := bc.GetBlockByNumber(uint64(num))
- hashes = append(hashes, block.Hash())
- if len(bodies) < tt.expected {
- bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
- }
- break
- }
- }
- }
- for j, hash := range tt.explicit {
- hashes = append(hashes, hash)
- if tt.available[j] && len(bodies) < tt.expected {
- block := bc.GetBlockByHash(hash)
- bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
- }
- }
- reqID++
-
- // Send the hash request and verify the response
- sendRequest(rawPeer.app, GetBlockBodiesMsg, reqID, hashes)
- if err := expectResponse(rawPeer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil {
- t.Errorf("test %d: bodies mismatch: %v", i, err)
- }
- }
-}
-
-// Tests that the contract codes can be retrieved based on account addresses.
-func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) }
-func TestGetCodeLes3(t *testing.T) { testGetCode(t, 3) }
-func TestGetCodeLes4(t *testing.T) { testGetCode(t, 4) }
-
-func testGetCode(t *testing.T, protocol int) {
- // Assemble the test environment
- netconfig := testnetConfig{
- blocks: 4,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- var codereqs []*CodeReq
- var codes [][]byte
- for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ {
- header := bc.GetHeaderByNumber(i)
- req := &CodeReq{
- BHash: header.Hash(),
- AccountAddress: testContractAddr[:],
- }
- codereqs = append(codereqs, req)
- if i >= testContractDeployed {
- codes = append(codes, testContractCodeDeployed)
- }
- }
-
- sendRequest(rawPeer.app, GetCodeMsg, 42, codereqs)
- if err := expectResponse(rawPeer.app, CodeMsg, 42, testBufLimit, codes); err != nil {
- t.Errorf("codes mismatch: %v", err)
- }
-}
-
-// Tests that the stale contract codes can't be retrieved based on account addresses.
-func TestGetStaleCodeLes2(t *testing.T) { testGetStaleCode(t, 2) }
-func TestGetStaleCodeLes3(t *testing.T) { testGetStaleCode(t, 3) }
-func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) }
-
-func testGetStaleCode(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- blocks: core.TriesInMemory + 4,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- check := func(number uint64, expected [][]byte) {
- req := &CodeReq{
- BHash: bc.GetHeaderByNumber(number).Hash(),
- AccountAddress: testContractAddr[:],
- }
- sendRequest(rawPeer.app, GetCodeMsg, 42, []*CodeReq{req})
- if err := expectResponse(rawPeer.app, CodeMsg, 42, testBufLimit, expected); err != nil {
- t.Errorf("codes mismatch: %v", err)
- }
- }
- check(0, [][]byte{}) // Non-exist contract
- check(testContractDeployed, [][]byte{}) // Stale contract
- check(bc.CurrentHeader().Number.Uint64(), [][]byte{testContractCodeDeployed}) // Fresh contract
-}
-
-// Tests that the transaction receipts can be retrieved based on hashes.
-func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) }
-func TestGetReceiptLes3(t *testing.T) { testGetReceipt(t, 3) }
-func TestGetReceiptLes4(t *testing.T) { testGetReceipt(t, 4) }
-
-func testGetReceipt(t *testing.T, protocol int) {
- // Assemble the test environment
- netconfig := testnetConfig{
- blocks: 4,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- // Collect the hashes to request, and the response to expect
- var receipts []types.Receipts
- var hashes []common.Hash
- for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ {
- block := bc.GetBlockByNumber(i)
-
- hashes = append(hashes, block.Hash())
- receipts = append(receipts, rawdb.ReadReceipts(server.db, block.Hash(), block.NumberU64(), block.Time(), bc.Config()))
- }
- // Send the hash request and verify the response
- sendRequest(rawPeer.app, GetReceiptsMsg, 42, hashes)
- if err := expectResponse(rawPeer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil {
- t.Errorf("receipts mismatch: %v", err)
- }
-}
-
-// Tests that trie merkle proofs can be retrieved
-func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) }
-func TestGetProofsLes3(t *testing.T) { testGetProofs(t, 3) }
-func TestGetProofsLes4(t *testing.T) { testGetProofs(t, 4) }
-
-func testGetProofs(t *testing.T, protocol int) {
- // Assemble the test environment
- netconfig := testnetConfig{
- blocks: 4,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- var proofreqs []ProofReq
- proofsV2 := trienode.NewProofSet()
-
- accounts := []common.Address{bankAddr, userAddr1, userAddr2, signerAddr, {}}
- for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ {
- header := bc.GetHeaderByNumber(i)
- trie, _ := trie.New(trie.StateTrieID(header.Root), server.backend.Blockchain().TrieDB())
-
- for _, acc := range accounts {
- req := ProofReq{
- BHash: header.Hash(),
- Key: crypto.Keccak256(acc[:]),
- }
- proofreqs = append(proofreqs, req)
- trie.Prove(crypto.Keccak256(acc[:]), proofsV2)
- }
- }
- // Send the proof request and verify the response
- sendRequest(rawPeer.app, GetProofsV2Msg, 42, proofreqs)
- if err := expectResponse(rawPeer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.List()); err != nil {
- t.Errorf("proofs mismatch: %v", err)
- }
-}
-
-// Tests that the stale contract codes can't be retrieved based on account addresses.
-func TestGetStaleProofLes2(t *testing.T) { testGetStaleProof(t, 2) }
-func TestGetStaleProofLes3(t *testing.T) { testGetStaleProof(t, 3) }
-func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) }
-
-func testGetStaleProof(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- blocks: core.TriesInMemory + 4,
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- check := func(number uint64, wantOK bool) {
- var (
- header = bc.GetHeaderByNumber(number)
- account = crypto.Keccak256(userAddr1.Bytes())
- )
- req := &ProofReq{
- BHash: header.Hash(),
- Key: account,
- }
- sendRequest(rawPeer.app, GetProofsV2Msg, 42, []*ProofReq{req})
-
- var expected []rlp.RawValue
- if wantOK {
- proofsV2 := trienode.NewProofSet()
- t, _ := trie.New(trie.StateTrieID(header.Root), server.backend.Blockchain().TrieDB())
- t.Prove(account, proofsV2)
- expected = proofsV2.List()
- }
- if err := expectResponse(rawPeer.app, ProofsV2Msg, 42, testBufLimit, expected); err != nil {
- t.Errorf("codes mismatch: %v", err)
- }
- }
- check(0, false) // Non-exist proof
- check(2, false) // Stale proof
- check(bc.CurrentHeader().Number.Uint64(), true) // Fresh proof
-}
-
-// Tests that CHT proofs can be correctly retrieved.
-func TestGetCHTProofsLes2(t *testing.T) { testGetCHTProofs(t, 2) }
-func TestGetCHTProofsLes3(t *testing.T) { testGetCHTProofs(t, 3) }
-func TestGetCHTProofsLes4(t *testing.T) { testGetCHTProofs(t, 4) }
-
-func testGetCHTProofs(t *testing.T, protocol int) {
- var (
- config = light.TestServerIndexerConfig
- waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
- for {
- cs, _, _ := cIndexer.Sections()
- if cs >= 1 {
- break
- }
- time.Sleep(10 * time.Millisecond)
- }
- }
- netconfig = testnetConfig{
- blocks: int(config.ChtSize + config.ChtConfirms),
- protocol: protocol,
- indexFn: waitIndexers,
- nopruning: true,
- }
- )
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- // Assemble the proofs from the different protocols
- header := bc.GetHeaderByNumber(config.ChtSize - 1)
- rlp, _ := rlp.EncodeToBytes(header)
-
- key := make([]byte, 8)
- binary.BigEndian.PutUint64(key, config.ChtSize-1)
-
- proofsV2 := HelperTrieResps{
- AuxData: [][]byte{rlp},
- }
- root := light.GetChtRoot(server.db, 0, bc.GetHeaderByNumber(config.ChtSize-1).Hash())
- trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(server.db, string(rawdb.ChtTablePrefix)), trie.HashDefaults))
- trie.Prove(key, &proofsV2.Proofs)
- // Assemble the requests for the different protocols
- requestsV2 := []HelperTrieReq{{
- Type: htCanonical,
- TrieIdx: 0,
- Key: key,
- AuxReq: htAuxHeader,
- }}
- // Send the proof request and verify the response
- sendRequest(rawPeer.app, GetHelperTrieProofsMsg, 42, requestsV2)
- if err := expectResponse(rawPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil {
- t.Errorf("proofs mismatch: %v", err)
- }
-}
-
-func TestGetBloombitsProofsLes2(t *testing.T) { testGetBloombitsProofs(t, 2) }
-func TestGetBloombitsProofsLes3(t *testing.T) { testGetBloombitsProofs(t, 3) }
-func TestGetBloombitsProofsLes4(t *testing.T) { testGetBloombitsProofs(t, 4) }
-
-// Tests that bloombits proofs can be correctly retrieved.
-func testGetBloombitsProofs(t *testing.T, protocol int) {
- var (
- config = light.TestServerIndexerConfig
- waitIndexers = func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
- for {
- bts, _, _ := btIndexer.Sections()
- if bts >= 1 {
- break
- }
- time.Sleep(10 * time.Millisecond)
- }
- }
- netconfig = testnetConfig{
- blocks: int(config.BloomTrieSize + config.BloomTrieConfirms),
- protocol: protocol,
- indexFn: waitIndexers,
- nopruning: true,
- }
- )
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- bc := server.handler.blockchain
-
- // Request and verify each bit of the bloom bits proofs
- for bit := 0; bit < 2048; bit++ {
- // Assemble the request and proofs for the bloombits
- key := make([]byte, 10)
-
- binary.BigEndian.PutUint16(key[:2], uint16(bit))
- // Only the first bloom section has data.
- binary.BigEndian.PutUint64(key[2:], 0)
-
- requests := []HelperTrieReq{{
- Type: htBloomBits,
- TrieIdx: 0,
- Key: key,
- }}
- var proofs HelperTrieResps
-
- root := light.GetBloomTrieRoot(server.db, 0, bc.GetHeaderByNumber(config.BloomTrieSize-1).Hash())
- trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(server.db, string(rawdb.BloomTrieTablePrefix)), trie.HashDefaults))
- trie.Prove(key, &proofs.Proofs)
-
- // Send the proof request and verify the response
- sendRequest(rawPeer.app, GetHelperTrieProofsMsg, 42, requests)
- if err := expectResponse(rawPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil {
- t.Errorf("bit %d: proofs mismatch: %v", bit, err)
- }
- }
-}
-
-func TestTransactionStatusLes2(t *testing.T) { testTransactionStatus(t, lpv2) }
-func TestTransactionStatusLes3(t *testing.T) { testTransactionStatus(t, lpv3) }
-func TestTransactionStatusLes4(t *testing.T) { testTransactionStatus(t, lpv4) }
-
-func testTransactionStatus(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- protocol: protocol,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- server.handler.addTxsSync = true
-
- chain := server.handler.blockchain
-
- var reqID uint64
-
- test := func(tx *types.Transaction, send bool, expStatus light.TxStatus) {
- t.Helper()
-
- reqID++
- if send {
- sendRequest(rawPeer.app, SendTxV2Msg, reqID, types.Transactions{tx})
- } else {
- sendRequest(rawPeer.app, GetTxStatusMsg, reqID, []common.Hash{tx.Hash()})
- }
- if err := expectResponse(rawPeer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil {
- t.Errorf("transaction status mismatch: %v", err)
- }
- }
- signer := types.HomesteadSigner{}
-
- // test error status by sending an underpriced transaction
- tx0, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey)
- test(tx0, true, light.TxStatus{Status: txpool.TxStatusUnknown, Error: "transaction underpriced: tip needed 1, tip permitted 0"})
-
- tx1, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
- test(tx1, false, light.TxStatus{Status: txpool.TxStatusUnknown}) // query before sending, should be unknown
- test(tx1, true, light.TxStatus{Status: txpool.TxStatusPending}) // send valid processable tx, should return pending
- test(tx1, true, light.TxStatus{Status: txpool.TxStatusPending}) // adding it again should not return an error
-
- tx2, _ := types.SignTx(types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
- tx3, _ := types.SignTx(types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
- // send transactions in the wrong order, tx3 should be queued
- test(tx3, true, light.TxStatus{Status: txpool.TxStatusQueued})
- test(tx2, true, light.TxStatus{Status: txpool.TxStatusPending})
- // query again, now tx3 should be pending too
- test(tx3, false, light.TxStatus{Status: txpool.TxStatusPending})
-
- // generate and add a block with tx1 and tx2 included
- gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 1, func(i int, block *core.BlockGen) {
- block.AddTx(tx1)
- block.AddTx(tx2)
- })
- if _, err := chain.InsertChain(gchain); err != nil {
- panic(err)
- }
- // wait until TxPool processes the inserted block
- for i := 0; i < 10; i++ {
- if pending, _ := server.handler.txpool.Stats(); pending == 1 {
- break
- }
- time.Sleep(100 * time.Millisecond)
- }
- if pending, _ := server.handler.txpool.Stats(); pending != 1 {
- t.Fatalf("pending count mismatch: have %d, want 1", pending)
- }
- // Discard new block announcement
- msg, _ := rawPeer.app.ReadMsg()
- msg.Discard()
-
- // check if their status is included now
- block1hash := rawdb.ReadCanonicalHash(server.db, 1)
- test(tx1, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
-
- test(tx2, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
-
- // create a reorg that rolls them back
- gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 2, func(i int, block *core.BlockGen) {})
- if _, err := chain.InsertChain(gchain); err != nil {
- panic(err)
- }
- // wait until TxPool processes the reorg
- for i := 0; i < 10; i++ {
- if pending, _ := server.handler.txpool.Stats(); pending == 3 {
- break
- }
- time.Sleep(100 * time.Millisecond)
- }
- if pending, _ := server.handler.txpool.Stats(); pending != 3 {
- t.Fatalf("pending count mismatch: have %d, want 3", pending)
- }
- // Discard new block announcement
- msg, _ = rawPeer.app.ReadMsg()
- msg.Discard()
-
- // check if their status is pending again
- test(tx1, false, light.TxStatus{Status: txpool.TxStatusPending})
- test(tx2, false, light.TxStatus{Status: txpool.TxStatusPending})
-}
-
-func TestStopResumeLES3(t *testing.T) { testStopResume(t, lpv3) }
-func TestStopResumeLES4(t *testing.T) { testStopResume(t, lpv4) }
-
-func testStopResume(t *testing.T, protocol int) {
- netconfig := testnetConfig{
- protocol: protocol,
- simClock: true,
- nopruning: true,
- }
- server, _, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- server.handler.server.costTracker.testing = true
- server.handler.server.costTracker.testCostList = testCostList(testBufLimit / 10)
-
- rawPeer, closePeer, _ := server.newRawPeer(t, "peer", protocol)
- defer closePeer()
-
- var (
- reqID uint64
- expBuf = testBufLimit
- testCost = testBufLimit / 10
- )
- header := server.handler.blockchain.CurrentHeader()
- req := func() {
- reqID++
- sendRequest(rawPeer.app, GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Hash: header.Hash()}, Amount: 1})
- }
- for i := 1; i <= 5; i++ {
- // send requests while we still have enough buffer and expect a response
- for expBuf >= testCost {
- req()
- expBuf -= testCost
- if err := expectResponse(rawPeer.app, BlockHeadersMsg, reqID, expBuf, []*types.Header{header}); err != nil {
- t.Errorf("expected response and failed: %v", err)
- }
- }
- // send some more requests in excess and expect a single StopMsg
- c := i
- for c > 0 {
- req()
- c--
- }
- if err := p2p.ExpectMsg(rawPeer.app, StopMsg, nil); err != nil {
- t.Errorf("expected StopMsg and failed: %v", err)
- }
- // wait until the buffer is recharged by half of the limit
- wait := testBufLimit / testBufRecharge / 2
- server.clock.(*mclock.Simulated).Run(time.Millisecond * time.Duration(wait))
-
- // expect a ResumeMsg with the partially recharged buffer value
- expBuf += testBufRecharge * wait
- if err := p2p.ExpectMsg(rawPeer.app, ResumeMsg, expBuf); err != nil {
- t.Errorf("expected ResumeMsg and failed: %v", err)
- }
- }
-}
diff --git a/les/metrics.go b/les/metrics.go
deleted file mode 100644
index 07d3133c95a6..000000000000
--- a/les/metrics.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/ethereum/go-ethereum/p2p"
-)
-
-var (
- miscInPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/total", nil)
- miscInTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/total", nil)
- miscInHeaderPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/header", nil)
- miscInHeaderTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/header", nil)
- miscInBodyPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/body", nil)
- miscInBodyTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/body", nil)
- miscInCodePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/code", nil)
- miscInCodeTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/code", nil)
- miscInReceiptPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/receipt", nil)
- miscInReceiptTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/receipt", nil)
- miscInTrieProofPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/proof", nil)
- miscInTrieProofTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/proof", nil)
- miscInHelperTriePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/helperTrie", nil)
- miscInHelperTrieTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/helperTrie", nil)
- miscInTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txs", nil)
- miscInTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txs", nil)
- miscInTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txStatus", nil)
- miscInTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txStatus", nil)
-
- miscOutPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/total", nil)
- miscOutTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/total", nil)
- miscOutHeaderPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/header", nil)
- miscOutHeaderTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/header", nil)
- miscOutBodyPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/body", nil)
- miscOutBodyTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/body", nil)
- miscOutCodePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/code", nil)
- miscOutCodeTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/code", nil)
- miscOutReceiptPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/receipt", nil)
- miscOutReceiptTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/receipt", nil)
- miscOutTrieProofPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/proof", nil)
- miscOutTrieProofTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/proof", nil)
- miscOutHelperTriePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/helperTrie", nil)
- miscOutHelperTrieTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/helperTrie", nil)
- miscOutTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txs", nil)
- miscOutTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txs", nil)
- miscOutTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txStatus", nil)
- miscOutTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txStatus", nil)
-
- miscServingTimeHeaderTimer = metrics.NewRegisteredTimer("les/misc/serve/header", nil)
- miscServingTimeBodyTimer = metrics.NewRegisteredTimer("les/misc/serve/body", nil)
- miscServingTimeCodeTimer = metrics.NewRegisteredTimer("les/misc/serve/code", nil)
- miscServingTimeReceiptTimer = metrics.NewRegisteredTimer("les/misc/serve/receipt", nil)
- miscServingTimeTrieProofTimer = metrics.NewRegisteredTimer("les/misc/serve/proof", nil)
- miscServingTimeHelperTrieTimer = metrics.NewRegisteredTimer("les/misc/serve/helperTrie", nil)
- miscServingTimeTxTimer = metrics.NewRegisteredTimer("les/misc/serve/txs", nil)
- miscServingTimeTxStatusTimer = metrics.NewRegisteredTimer("les/misc/serve/txStatus", nil)
-
- connectionTimer = metrics.NewRegisteredTimer("les/connection/duration", nil)
- serverConnectionGauge = metrics.NewRegisteredGauge("les/connection/server", nil)
-
- totalCapacityGauge = metrics.NewRegisteredGauge("les/server/totalCapacity", nil)
- totalRechargeGauge = metrics.NewRegisteredGauge("les/server/totalRecharge", nil)
- blockProcessingTimer = metrics.NewRegisteredTimer("les/server/blockProcessingTime", nil)
-
- requestServedMeter = metrics.NewRegisteredMeter("les/server/req/avgServedTime", nil)
- requestServedTimer = metrics.NewRegisteredTimer("les/server/req/servedTime", nil)
- requestEstimatedMeter = metrics.NewRegisteredMeter("les/server/req/avgEstimatedTime", nil)
- requestEstimatedTimer = metrics.NewRegisteredTimer("les/server/req/estimatedTime", nil)
- relativeCostHistogram = metrics.NewRegisteredHistogram("les/server/req/relative", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostHeaderHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/header", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostBodyHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/body", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostReceiptHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/receipt", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostCodeHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/code", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostProofHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/proof", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostHelperProofHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/helperTrie", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostSendTxHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/txs", nil, metrics.NewExpDecaySample(1028, 0.015))
- relativeCostTxStatusHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/txStatus", nil, metrics.NewExpDecaySample(1028, 0.015))
-
- globalFactorGauge = metrics.NewRegisteredGauge("les/server/globalFactor", nil)
- recentServedGauge = metrics.NewRegisteredGauge("les/server/recentRequestServed", nil)
- recentEstimatedGauge = metrics.NewRegisteredGauge("les/server/recentRequestEstimated", nil)
- sqServedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/served", nil)
- sqQueuedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/queued", nil)
-
- clientFreezeMeter = metrics.NewRegisteredMeter("les/server/clientEvent/freeze", nil)
- clientErrorMeter = metrics.NewRegisteredMeter("les/server/clientEvent/error", nil)
-
- requestRTT = metrics.NewRegisteredTimer("les/client/req/rtt", nil)
- requestSendDelay = metrics.NewRegisteredTimer("les/client/req/sendDelay", nil)
-
- serverSelectableGauge = metrics.NewRegisteredGauge("les/client/serverPool/selectable", nil)
- serverDialedMeter = metrics.NewRegisteredMeter("les/client/serverPool/dialed", nil)
- serverConnectedGauge = metrics.NewRegisteredGauge("les/client/serverPool/connected", nil)
- sessionValueMeter = metrics.NewRegisteredMeter("les/client/serverPool/sessionValue", nil)
- totalValueGauge = metrics.NewRegisteredGauge("les/client/serverPool/totalValue", nil)
- suggestedTimeoutGauge = metrics.NewRegisteredGauge("les/client/serverPool/timeout", nil)
-)
-
-// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of
-// accumulating the above defined metrics based on the data stream contents.
-type meteredMsgReadWriter struct {
- p2p.MsgReadWriter // Wrapped message stream to meter
- version int // Protocol version to select correct meters
-}
-
-// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the
-// metrics system is disabled, this function returns the original object.
-func newMeteredMsgWriter(rw p2p.MsgReadWriter, version int) p2p.MsgReadWriter {
- if !metrics.Enabled {
- return rw
- }
- return &meteredMsgReadWriter{MsgReadWriter: rw, version: version}
-}
-
-func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) {
- // Read the message and short circuit in case of an error
- msg, err := rw.MsgReadWriter.ReadMsg()
- if err != nil {
- return msg, err
- }
- // Account for the data traffic
- packets, traffic := miscInPacketsMeter, miscInTrafficMeter
- packets.Mark(1)
- traffic.Mark(int64(msg.Size))
-
- return msg, err
-}
-
-func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error {
- // Account for the data traffic
- packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter
- packets.Mark(1)
- traffic.Mark(int64(msg.Size))
-
- // Send the packet to the p2p layer
- return rw.MsgReadWriter.WriteMsg(msg)
-}
diff --git a/les/odr.go b/les/odr.go
deleted file mode 100644
index 943b05fdfc6e..000000000000
--- a/les/odr.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "context"
- "math/rand"
- "sort"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
-)
-
-// LesOdr implements light.OdrBackend
-type LesOdr struct {
- db ethdb.Database
- indexerConfig *light.IndexerConfig
- chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer
- peers *serverPeerSet
- retriever *retrieveManager
- stop chan struct{}
-}
-
-func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, peers *serverPeerSet, retriever *retrieveManager) *LesOdr {
- return &LesOdr{
- db: db,
- indexerConfig: config,
- peers: peers,
- retriever: retriever,
- stop: make(chan struct{}),
- }
-}
-
-// Stop cancels all pending retrievals
-func (odr *LesOdr) Stop() {
- close(odr.stop)
-}
-
-// Database returns the backing database
-func (odr *LesOdr) Database() ethdb.Database {
- return odr.db
-}
-
-// SetIndexers adds the necessary chain indexers to the ODR backend
-func (odr *LesOdr) SetIndexers(chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer) {
- odr.chtIndexer = chtIndexer
- odr.bloomTrieIndexer = bloomTrieIndexer
- odr.bloomIndexer = bloomIndexer
-}
-
-// ChtIndexer returns the CHT chain indexer
-func (odr *LesOdr) ChtIndexer() *core.ChainIndexer {
- return odr.chtIndexer
-}
-
-// BloomTrieIndexer returns the bloom trie chain indexer
-func (odr *LesOdr) BloomTrieIndexer() *core.ChainIndexer {
- return odr.bloomTrieIndexer
-}
-
-// BloomIndexer returns the bloombits chain indexer
-func (odr *LesOdr) BloomIndexer() *core.ChainIndexer {
- return odr.bloomIndexer
-}
-
-// IndexerConfig returns the indexer config.
-func (odr *LesOdr) IndexerConfig() *light.IndexerConfig {
- return odr.indexerConfig
-}
-
-const (
- MsgBlockHeaders = iota
- MsgBlockBodies
- MsgCode
- MsgReceipts
- MsgProofsV2
- MsgHelperTrieProofs
- MsgTxStatus
-)
-
-// Msg encodes a LES message that delivers reply data for a request
-type Msg struct {
- MsgType int
- ReqID uint64
- Obj interface{}
-}
-
-// peerByTxHistory is a heap.Interface implementation which can sort
-// the peerset by transaction history.
-type peerByTxHistory []*serverPeer
-
-func (h peerByTxHistory) Len() int { return len(h) }
-func (h peerByTxHistory) Less(i, j int) bool {
- if h[i].txHistory == txIndexUnlimited {
- return false
- }
- if h[j].txHistory == txIndexUnlimited {
- return true
- }
- return h[i].txHistory < h[j].txHistory
-}
-func (h peerByTxHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
-
-const (
- maxTxStatusRetry = 3 // The maximum retries will be made for tx status request.
- maxTxStatusCandidates = 5 // The maximum les servers the tx status requests will be sent to.
-)
-
-// RetrieveTxStatus retrieves the transaction status from the LES network.
-// There is no guarantee in the LES protocol that the mined transaction will
-// be retrieved back for sure because of different reasons(the transaction
-// is unindexed, the malicious server doesn't reply it deliberately, etc).
-// Therefore, unretrieved transactions(UNKNOWN) will receive a certain number
-// of retries, thus giving a weak guarantee.
-func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequest) error {
- // Sort according to the transaction history supported by the peer and
- // select the peers with longest history.
- var (
- retries int
- peers []*serverPeer
- missing = len(req.Hashes)
- result = make([]light.TxStatus, len(req.Hashes))
- canSend = make(map[string]bool)
- )
- for _, peer := range odr.peers.allPeers() {
- if peer.txHistory == txIndexDisabled {
- continue
- }
- peers = append(peers, peer)
- }
- sort.Sort(sort.Reverse(peerByTxHistory(peers)))
- for i := 0; i < maxTxStatusCandidates && i < len(peers); i++ {
- canSend[peers[i].id] = true
- }
- // Send out the request and assemble the result.
- for {
- if retries >= maxTxStatusRetry || len(canSend) == 0 {
- break
- }
- var (
- // Deep copy the request, so that the partial result won't be mixed.
- req = &TxStatusRequest{Hashes: req.Hashes}
- id = rand.Uint64()
- distreq = &distReq{
- getCost: func(dp distPeer) uint64 { return req.GetCost(dp.(*serverPeer)) },
- canSend: func(dp distPeer) bool { return canSend[dp.(*serverPeer).id] },
- request: func(dp distPeer) func() {
- p := dp.(*serverPeer)
- p.fcServer.QueuedRequest(id, req.GetCost(p))
- delete(canSend, p.id)
- return func() { req.Request(id, p) }
- },
- }
- )
- if err := odr.retriever.retrieve(ctx, id, distreq, func(p distPeer, msg *Msg) error { return req.Validate(odr.db, msg) }, odr.stop); err != nil {
- return err
- }
- // Collect the response and assemble them to the final result.
- // All the response is not verifiable, so always pick the first
- // one we get.
- for index, status := range req.Status {
- if result[index].Status != txpool.TxStatusUnknown {
- continue
- }
- if status.Status == txpool.TxStatusUnknown {
- continue
- }
- result[index], missing = status, missing-1
- }
- // Abort the procedure if all the status are retrieved
- if missing == 0 {
- break
- }
- retries += 1
- }
- req.Status = result
- return nil
-}
-
-// Retrieve tries to fetch an object from the LES network. It's a common API
-// for most of the LES requests except for the TxStatusRequest which needs
-// the additional retry mechanism.
-// If the network retrieval was successful, it stores the object in local db.
-func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) {
- lreq := LesRequest(req)
-
- reqID := rand.Uint64()
- rq := &distReq{
- getCost: func(dp distPeer) uint64 {
- return lreq.GetCost(dp.(*serverPeer))
- },
- canSend: func(dp distPeer) bool {
- p := dp.(*serverPeer)
- if !p.onlyAnnounce {
- return lreq.CanSend(p)
- }
- return false
- },
- request: func(dp distPeer) func() {
- p := dp.(*serverPeer)
- cost := lreq.GetCost(p)
- p.fcServer.QueuedRequest(reqID, cost)
- return func() { lreq.Request(reqID, p) }
- },
- }
-
- defer func(sent mclock.AbsTime) {
- if err != nil {
- return
- }
- requestRTT.Update(time.Duration(mclock.Now() - sent))
- }(mclock.Now())
-
- if err := odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err != nil {
- return err
- }
- req.StoreResult(odr.db)
- return nil
-}
diff --git a/les/odr_requests.go b/les/odr_requests.go
deleted file mode 100644
index c907018590fa..000000000000
--- a/les/odr_requests.go
+++ /dev/null
@@ -1,537 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "encoding/binary"
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-var (
- errInvalidMessageType = errors.New("invalid message type")
- errInvalidEntryCount = errors.New("invalid number of response entries")
- errHeaderUnavailable = errors.New("header unavailable")
- errTxHashMismatch = errors.New("transaction hash mismatch")
- errUncleHashMismatch = errors.New("uncle hash mismatch")
- errReceiptHashMismatch = errors.New("receipt hash mismatch")
- errDataHashMismatch = errors.New("data hash mismatch")
- errCHTHashMismatch = errors.New("cht hash mismatch")
- errCHTNumberMismatch = errors.New("cht number mismatch")
- errUselessNodes = errors.New("useless nodes in merkle proof nodeset")
-)
-
-type LesOdrRequest interface {
- GetCost(*serverPeer) uint64
- CanSend(*serverPeer) bool
- Request(uint64, *serverPeer) error
- Validate(ethdb.Database, *Msg) error
-}
-
-func LesRequest(req light.OdrRequest) LesOdrRequest {
- switch r := req.(type) {
- case *light.BlockRequest:
- return (*BlockRequest)(r)
- case *light.ReceiptsRequest:
- return (*ReceiptsRequest)(r)
- case *light.TrieRequest:
- return (*TrieRequest)(r)
- case *light.CodeRequest:
- return (*CodeRequest)(r)
- case *light.ChtRequest:
- return (*ChtRequest)(r)
- case *light.BloomRequest:
- return (*BloomRequest)(r)
- case *light.TxStatusRequest:
- return (*TxStatusRequest)(r)
- default:
- return nil
- }
-}
-
-// BlockRequest is the ODR request type for block bodies
-type BlockRequest light.BlockRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *BlockRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetBlockBodiesMsg, 1)
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *BlockRequest) CanSend(peer *serverPeer) bool {
- return peer.HasBlock(r.Hash, r.Number, false)
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *BlockRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting block body", "hash", r.Hash)
- return peer.requestBodies(reqID, []common.Hash{r.Hash})
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating block body", "hash", r.Hash)
-
- // Ensure we have a correct message with a single block body
- if msg.MsgType != MsgBlockBodies {
- return errInvalidMessageType
- }
- bodies := msg.Obj.([]*types.Body)
- if len(bodies) != 1 {
- return errInvalidEntryCount
- }
- body := bodies[0]
-
- // Retrieve our stored header and validate block content against it
- if r.Header == nil {
- r.Header = rawdb.ReadHeader(db, r.Hash, r.Number)
- }
- if r.Header == nil {
- return errHeaderUnavailable
- }
- if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), trie.NewStackTrie(nil)) {
- return errTxHashMismatch
- }
- if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) {
- return errUncleHashMismatch
- }
- // Validations passed, encode and store RLP
- data, err := rlp.EncodeToBytes(body)
- if err != nil {
- return err
- }
- r.Rlp = data
- return nil
-}
-
-// ReceiptsRequest is the ODR request type for block receipts by block hash
-type ReceiptsRequest light.ReceiptsRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *ReceiptsRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetReceiptsMsg, 1)
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *ReceiptsRequest) CanSend(peer *serverPeer) bool {
- return peer.HasBlock(r.Hash, r.Number, false)
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *ReceiptsRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting block receipts", "hash", r.Hash)
- return peer.requestReceipts(reqID, []common.Hash{r.Hash})
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating block receipts", "hash", r.Hash)
-
- // Ensure we have a correct message with a single block receipt
- if msg.MsgType != MsgReceipts {
- return errInvalidMessageType
- }
- receipts := msg.Obj.([]types.Receipts)
- if len(receipts) != 1 {
- return errInvalidEntryCount
- }
- receipt := receipts[0]
-
- // Retrieve our stored header and validate receipt content against it
- if r.Header == nil {
- r.Header = rawdb.ReadHeader(db, r.Hash, r.Number)
- }
- if r.Header == nil {
- return errHeaderUnavailable
- }
- if r.Header.ReceiptHash != types.DeriveSha(receipt, trie.NewStackTrie(nil)) {
- return errReceiptHashMismatch
- }
- // Validations passed, store and return
- r.Receipts = receipt
- return nil
-}
-
-type ProofReq struct {
- BHash common.Hash
- AccountAddress, Key []byte
- FromLevel uint
-}
-
-// ODR request type for state/storage trie entries, see LesOdrRequest interface
-type TrieRequest light.TrieRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *TrieRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetProofsV2Msg, 1)
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *TrieRequest) CanSend(peer *serverPeer) bool {
- return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true)
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *TrieRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key)
- req := ProofReq{
- BHash: r.Id.BlockHash,
- AccountAddress: r.Id.AccountAddress,
- Key: r.Key,
- }
- return peer.requestProofs(reqID, []ProofReq{req})
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key)
-
- if msg.MsgType != MsgProofsV2 {
- return errInvalidMessageType
- }
- proofs := msg.Obj.(trienode.ProofList)
- // Verify the proof and store if checks out
- nodeSet := proofs.Set()
- reads := &readTraceDB{db: nodeSet}
- if _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
- return fmt.Errorf("merkle proof verification failed: %v", err)
- }
- // check if all nodes have been read by VerifyProof
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- r.Proof = nodeSet
- return nil
-}
-
-type CodeReq struct {
- BHash common.Hash
- AccountAddress []byte
-}
-
-// CodeRequest is the ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface
-type CodeRequest light.CodeRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *CodeRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetCodeMsg, 1)
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *CodeRequest) CanSend(peer *serverPeer) bool {
- return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true)
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *CodeRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting code data", "hash", r.Hash)
- req := CodeReq{
- BHash: r.Id.BlockHash,
- AccountAddress: r.Id.AccountAddress,
- }
- return peer.requestCode(reqID, []CodeReq{req})
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating code data", "hash", r.Hash)
-
- // Ensure we have a correct message with a single code element
- if msg.MsgType != MsgCode {
- return errInvalidMessageType
- }
- reply := msg.Obj.([][]byte)
- if len(reply) != 1 {
- return errInvalidEntryCount
- }
- data := reply[0]
-
- // Verify the data and store if checks out
- if hash := crypto.Keccak256Hash(data); r.Hash != hash {
- return errDataHashMismatch
- }
- r.Data = data
- return nil
-}
-
-const (
- // helper trie type constants
- htCanonical = iota // Canonical hash trie
- htBloomBits // BloomBits trie
-
- // helper trie auxiliary types
- // htAuxNone = 1 ; deprecated number, used in les2/3 previously.
- htAuxHeader = 2 // applicable for htCanonical, requests for relevant headers
-)
-
-type HelperTrieReq struct {
- Type uint
- TrieIdx uint64
- Key []byte
- FromLevel, AuxReq uint
-}
-
-type HelperTrieResps struct { // describes all responses, not just a single one
- Proofs trienode.ProofList
- AuxData [][]byte
-}
-
-// ChtRequest is the ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
-type ChtRequest light.ChtRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *ChtRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetHelperTrieProofsMsg, 1)
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *ChtRequest) CanSend(peer *serverPeer) bool {
- peer.lock.RLock()
- defer peer.lock.RUnlock()
-
- return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *ChtRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum)
- var encNum [8]byte
- binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
- req := HelperTrieReq{
- Type: htCanonical,
- TrieIdx: r.ChtNum,
- Key: encNum[:],
- AuxReq: htAuxHeader,
- }
- return peer.requestHelperTrieProofs(reqID, []HelperTrieReq{req})
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum)
-
- if msg.MsgType != MsgHelperTrieProofs {
- return errInvalidMessageType
- }
- resp := msg.Obj.(HelperTrieResps)
- if len(resp.AuxData) != 1 {
- return errInvalidEntryCount
- }
- nodeSet := resp.Proofs.Set()
- headerEnc := resp.AuxData[0]
- if len(headerEnc) == 0 {
- return errHeaderUnavailable
- }
- header := new(types.Header)
- if err := rlp.DecodeBytes(headerEnc, header); err != nil {
- return errHeaderUnavailable
- }
- // Verify the CHT
- var (
- node light.ChtNode
- encNumber [8]byte
- )
- binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
-
- reads := &readTraceDB{db: nodeSet}
- value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
- if err != nil {
- return fmt.Errorf("merkle proof verification failed: %v", err)
- }
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- if err := rlp.DecodeBytes(value, &node); err != nil {
- return err
- }
- if node.Hash != header.Hash() {
- return errCHTHashMismatch
- }
- if r.BlockNum != header.Number.Uint64() {
- return errCHTNumberMismatch
- }
- // Verifications passed, store and return
- r.Header = header
- r.Proof = nodeSet
- r.Td = node.Td
- return nil
-}
-
-type BloomReq struct {
- BloomTrieNum, BitIdx, SectionIndex, FromLevel uint64
-}
-
-// BloomRequest is the ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
-type BloomRequest light.BloomRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *BloomRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetHelperTrieProofsMsg, len(r.SectionIndexList))
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *BloomRequest) CanSend(peer *serverPeer) bool {
- peer.lock.RLock()
- defer peer.lock.RUnlock()
-
- if peer.version < lpv2 {
- return false
- }
- return peer.headInfo.Number >= r.Config.BloomTrieConfirms && r.BloomTrieNum <= (peer.headInfo.Number-r.Config.BloomTrieConfirms)/r.Config.BloomTrieSize
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *BloomRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
- reqs := make([]HelperTrieReq, len(r.SectionIndexList))
-
- var encNumber [10]byte
- binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
-
- for i, sectionIdx := range r.SectionIndexList {
- binary.BigEndian.PutUint64(encNumber[2:], sectionIdx)
- reqs[i] = HelperTrieReq{
- Type: htBloomBits,
- TrieIdx: r.BloomTrieNum,
- Key: common.CopyBytes(encNumber[:]),
- }
- }
- return peer.requestHelperTrieProofs(reqID, reqs)
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
-
- // Ensure we have a correct message with a single proof element
- if msg.MsgType != MsgHelperTrieProofs {
- return errInvalidMessageType
- }
- resps := msg.Obj.(HelperTrieResps)
- proofs := resps.Proofs
- nodeSet := proofs.Set()
- reads := &readTraceDB{db: nodeSet}
-
- r.BloomBits = make([][]byte, len(r.SectionIndexList))
-
- // Verify the proofs
- var encNumber [10]byte
- binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
-
- for i, idx := range r.SectionIndexList {
- binary.BigEndian.PutUint64(encNumber[2:], idx)
- value, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
- if err != nil {
- return err
- }
- r.BloomBits[i] = value
- }
-
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- r.Proofs = nodeSet
- return nil
-}
-
-// TxStatusRequest is the ODR request type for transaction status
-type TxStatusRequest light.TxStatusRequest
-
-// GetCost returns the cost of the given ODR request according to the serving
-// peer's cost table (implementation of LesOdrRequest)
-func (r *TxStatusRequest) GetCost(peer *serverPeer) uint64 {
- return peer.getRequestCost(GetTxStatusMsg, len(r.Hashes))
-}
-
-// CanSend tells if a certain peer is suitable for serving the given request
-func (r *TxStatusRequest) CanSend(peer *serverPeer) bool {
- return peer.txHistory != txIndexDisabled
-}
-
-// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
-func (r *TxStatusRequest) Request(reqID uint64, peer *serverPeer) error {
- peer.Log().Debug("Requesting transaction status", "count", len(r.Hashes))
- return peer.requestTxStatus(reqID, r.Hashes)
-}
-
-// Validate processes an ODR request reply message from the LES network
-// returns true and stores results in memory if the message was a valid reply
-// to the request (implementation of LesOdrRequest)
-func (r *TxStatusRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating transaction status", "count", len(r.Hashes))
-
- if msg.MsgType != MsgTxStatus {
- return errInvalidMessageType
- }
- status := msg.Obj.([]light.TxStatus)
- if len(status) != len(r.Hashes) {
- return errInvalidEntryCount
- }
- r.Status = status
- return nil
-}
-
-// readTraceDB stores the keys of database reads. We use this to check that received node
-// sets contain only the trie nodes necessary to make proofs pass.
-type readTraceDB struct {
- db ethdb.KeyValueReader
- reads map[string]struct{}
-}
-
-// Get returns a stored node
-func (db *readTraceDB) Get(k []byte) ([]byte, error) {
- if db.reads == nil {
- db.reads = make(map[string]struct{})
- }
- db.reads[string(k)] = struct{}{}
- return db.db.Get(k)
-}
-
-// Has returns true if the node set contains the given key
-func (db *readTraceDB) Has(key []byte) (bool, error) {
- _, err := db.Get(key)
- return err == nil, nil
-}
diff --git a/les/odr_test.go b/les/odr_test.go
deleted file mode 100644
index 69824a92dd08..000000000000
--- a/les/odr_test.go
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-// Note: these tests are disabled now because they cannot work with the old sync
-// mechanism removed but will be useful again once the PoS ultralight mode is implemented
-
-/*
-import (
- "bytes"
- "context"
- "crypto/rand"
- "fmt"
- "math/big"
- "reflect"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte
-
-func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetBlock) }
-func TestOdrGetBlockLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetBlock) }
-func TestOdrGetBlockLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetBlock) }
-
-func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
- var block *types.Block
- if bc != nil {
- block = bc.GetBlockByHash(bhash)
- } else {
- block, _ = lc.GetBlockByHash(ctx, bhash)
- }
- if block == nil {
- return nil
- }
- rlp, _ := rlp.EncodeToBytes(block)
- return rlp
-}
-
-func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetReceipts) }
-func TestOdrGetReceiptsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetReceipts) }
-func TestOdrGetReceiptsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetReceipts) }
-
-func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
- var receipts types.Receipts
- if bc != nil {
- if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
- if header := rawdb.ReadHeader(db, bhash, *number); header != nil {
- receipts = rawdb.ReadReceipts(db, bhash, *number, header.Time, config)
- }
- }
- } else {
- if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
- receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, *number)
- }
- }
- if receipts == nil {
- return nil
- }
- rlp, _ := rlp.EncodeToBytes(receipts)
- return rlp
-}
-
-func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrAccounts) }
-func TestOdrAccountsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrAccounts) }
-func TestOdrAccountsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrAccounts) }
-
-func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
- dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
- acc := []common.Address{bankAddr, userAddr1, userAddr2, dummyAddr}
-
- var (
- res []byte
- st *state.StateDB
- err error
- )
- for _, addr := range acc {
- if bc != nil {
- header := bc.GetHeaderByHash(bhash)
- st, err = state.New(header.Root, bc.StateCache(), nil)
- } else {
- header := lc.GetHeaderByHash(bhash)
- st = light.NewState(ctx, header, lc.Odr())
- }
- if err == nil {
- bal := st.GetBalance(addr)
- rlp, _ := rlp.EncodeToBytes(bal)
- res = append(res, rlp...)
- }
- }
- return res
-}
-
-func TestOdrContractCallLes2(t *testing.T) { testOdr(t, 2, 2, true, odrContractCall) }
-func TestOdrContractCallLes3(t *testing.T) { testOdr(t, 3, 2, true, odrContractCall) }
-func TestOdrContractCallLes4(t *testing.T) { testOdr(t, 4, 2, true, odrContractCall) }
-
-func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
- data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
-
- var res []byte
- for i := 0; i < 3; i++ {
- data[35] = byte(i)
- if bc != nil {
- header := bc.GetHeaderByHash(bhash)
- statedb, err := state.New(header.Root, bc.StateCache(), nil)
-
- if err == nil {
- from := statedb.GetOrNewStateObject(bankAddr)
- from.SetBalance(math.MaxBig256)
-
- msg := &core.Message{
- From: from.Address(),
- To: &testContractAddr,
- Value: new(big.Int),
- GasLimit: 100000,
- GasPrice: big.NewInt(params.InitialBaseFee),
- GasFeeCap: big.NewInt(params.InitialBaseFee),
- GasTipCap: new(big.Int),
- Data: data,
- SkipAccountChecks: true,
- }
-
- context := core.NewEVMBlockContext(header, bc, nil)
- txContext := core.NewEVMTxContext(msg)
- vmenv := vm.NewEVM(context, txContext, statedb, config, vm.Config{NoBaseFee: true})
-
- //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
- gp := new(core.GasPool).AddGas(math.MaxUint64)
- result, _ := core.ApplyMessage(vmenv, msg, gp)
- res = append(res, result.Return()...)
- }
- } else {
- header := lc.GetHeaderByHash(bhash)
- state := light.NewState(ctx, header, lc.Odr())
- state.SetBalance(bankAddr, math.MaxBig256)
- msg := &core.Message{
- From: bankAddr,
- To: &testContractAddr,
- Value: new(big.Int),
- GasLimit: 100000,
- GasPrice: big.NewInt(params.InitialBaseFee),
- GasFeeCap: big.NewInt(params.InitialBaseFee),
- GasTipCap: new(big.Int),
- Data: data,
- SkipAccountChecks: true,
- }
- context := core.NewEVMBlockContext(header, lc, nil)
- txContext := core.NewEVMTxContext(msg)
- vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true})
- gp := new(core.GasPool).AddGas(math.MaxUint64)
- result, _ := core.ApplyMessage(vmenv, msg, gp)
- if state.Error() == nil {
- res = append(res, result.Return()...)
- }
- }
- }
- return res
-}
-
-func TestOdrTxStatusLes2(t *testing.T) { testOdr(t, 2, 1, false, odrTxStatus) }
-func TestOdrTxStatusLes3(t *testing.T) { testOdr(t, 3, 1, false, odrTxStatus) }
-func TestOdrTxStatusLes4(t *testing.T) { testOdr(t, 4, 1, false, odrTxStatus) }
-
-func odrTxStatus(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
- var txs types.Transactions
- if bc != nil {
- block := bc.GetBlockByHash(bhash)
- txs = block.Transactions()
- } else {
- if block, _ := lc.GetBlockByHash(ctx, bhash); block != nil {
- btxs := block.Transactions()
- txs = make(types.Transactions, len(btxs))
- for i, tx := range btxs {
- var err error
- txs[i], _, _, _, err = light.GetTransaction(ctx, lc.Odr(), tx.Hash())
- if err != nil {
- return nil
- }
- }
- }
- }
- rlp, _ := rlp.EncodeToBytes(txs)
- return rlp
-}
-
-// testOdr tests odr requests whose validation guaranteed by block headers.
-func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn odrTestFn) {
- // Assemble the test environment
- netconfig := testnetConfig{
- blocks: 4,
- protocol: protocol,
- connect: true,
- nopruning: true,
- }
- server, client, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- // Ensure the client has synced all necessary data.
- clientHead := client.handler.backend.blockchain.CurrentHeader()
- if clientHead.Number.Uint64() != 4 {
- t.Fatalf("Failed to sync the chain with server, head: %v", clientHead.Number.Uint64())
- }
- // Disable the mechanism that we will wait a few time for request
- // even there is no suitable peer to send right now.
- waitForPeers = 0
-
- test := func(expFail uint64) {
- // Mark this as a helper to put the failures at the correct lines
- t.Helper()
-
- for i := uint64(0); i <= server.handler.blockchain.CurrentHeader().Number.Uint64(); i++ {
- bhash := rawdb.ReadCanonicalHash(server.db, i)
- b1 := fn(light.NoOdr, server.db, server.handler.server.chainConfig, server.handler.blockchain, nil, bhash)
-
- // Set the timeout as 1 second here, ensure there is enough time
- // for travis to make the action.
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- b2 := fn(ctx, client.db, client.handler.backend.chainConfig, nil, client.handler.backend.blockchain, bhash)
- cancel()
-
- eq := bytes.Equal(b1, b2)
- exp := i < expFail
- if exp && !eq {
- t.Fatalf("odr mismatch: have %x, want %x", b2, b1)
- }
- if !exp && eq {
- t.Fatalf("unexpected odr match")
- }
- }
- }
-
- // expect retrievals to fail (except genesis block) without a les peer
- client.handler.backend.peers.lock.Lock()
- client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return false }
- client.handler.backend.peers.lock.Unlock()
- test(expFail)
-
- // expect all retrievals to pass
- client.handler.backend.peers.lock.Lock()
- client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return true }
- client.handler.backend.peers.lock.Unlock()
- test(5)
-
- // still expect all retrievals to pass, now data should be cached locally
- if checkCached {
- client.handler.backend.peers.unregister(client.peer.speer.id)
- time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed
- test(5)
- }
-}
-
-func TestGetTxStatusFromUnindexedPeersLES4(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv4) }
-
-func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) {
- var (
- blocks = 8
- netconfig = testnetConfig{
- blocks: blocks,
- protocol: protocol,
- nopruning: true,
- }
- )
- server, client, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- // Iterate the chain, create the tx indexes locally
- var (
- testHash common.Hash
- testStatus light.TxStatus
-
- txs = make(map[common.Hash]*types.Transaction) // Transaction objects set
- blockNumbers = make(map[common.Hash]uint64) // Transaction hash to block number mappings
- blockHashes = make(map[common.Hash]common.Hash) // Transaction hash to block hash mappings
- intraIndex = make(map[common.Hash]uint64) // Transaction intra-index in block
- )
- for number := uint64(1); number < server.backend.Blockchain().CurrentBlock().Number.Uint64(); number++ {
- block := server.backend.Blockchain().GetBlockByNumber(number)
- if block == nil {
- t.Fatalf("Failed to retrieve block %d", number)
- }
- for index, tx := range block.Transactions() {
- txs[tx.Hash()] = tx
- blockNumbers[tx.Hash()] = number
- blockHashes[tx.Hash()] = block.Hash()
- intraIndex[tx.Hash()] = uint64(index)
-
- if testHash == (common.Hash{}) {
- testHash = tx.Hash()
- testStatus = light.TxStatus{
- Status: txpool.TxStatusIncluded,
- Lookup: &rawdb.LegacyTxLookupEntry{
- BlockHash: block.Hash(),
- BlockIndex: block.NumberU64(),
- Index: uint64(index),
- },
- }
- }
- }
- }
- // serveMsg processes incoming GetTxStatusMsg and sends the response back.
- serveMsg := func(peer *testPeer, txLookup uint64) error {
- msg, err := peer.app.ReadMsg()
- if err != nil {
- return err
- }
- if msg.Code != GetTxStatusMsg {
- return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, GetTxStatusMsg)
- }
- var r GetTxStatusPacket
- if err := msg.Decode(&r); err != nil {
- return err
- }
- stats := make([]light.TxStatus, len(r.Hashes))
- for i, hash := range r.Hashes {
- number, exist := blockNumbers[hash]
- if !exist {
- continue // Filter out unknown transactions
- }
- min := uint64(blocks) - txLookup
- if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) {
- continue // Filter out unindexed transactions
- }
- stats[i].Status = txpool.TxStatusIncluded
- stats[i].Lookup = &rawdb.LegacyTxLookupEntry{
- BlockHash: blockHashes[hash],
- BlockIndex: number,
- Index: intraIndex[hash],
- }
- }
- data, _ := rlp.EncodeToBytes(stats)
- reply := &reply{peer.app, TxStatusMsg, r.ReqID, data}
- reply.send(testBufLimit)
- return nil
- }
-
- var testspecs = []struct {
- peers int
- txLookups []uint64
- txs []common.Hash
- results []light.TxStatus
- }{
- // Retrieve mined transaction from the empty peerset
- {
- peers: 0,
- txLookups: []uint64{},
- txs: []common.Hash{testHash},
- results: []light.TxStatus{{}},
- },
- // Retrieve unknown transaction from the full peers
- {
- peers: 3,
- txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
- txs: []common.Hash{randomHash()},
- results: []light.TxStatus{{}},
- },
- // Retrieve mined transaction from the full peers
- {
- peers: 3,
- txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
- txs: []common.Hash{testHash},
- results: []light.TxStatus{testStatus},
- },
- // Retrieve mixed transactions from the full peers
- {
- peers: 3,
- txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited},
- txs: []common.Hash{randomHash(), testHash},
- results: []light.TxStatus{{}, testStatus},
- },
- // Retrieve mixed transactions from unindexed peer(but the target is still available)
- {
- peers: 3,
- txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2},
- txs: []common.Hash{randomHash(), testHash},
- results: []light.TxStatus{{}, testStatus},
- },
- // Retrieve mixed transactions from unindexed peer(but the target is not available)
- {
- peers: 3,
- txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2},
- txs: []common.Hash{randomHash(), testHash},
- results: []light.TxStatus{{}, {}},
- },
- }
- for _, testspec := range testspecs {
- // Create a bunch of server peers with different tx history
- var (
- closeFns []func()
- )
- for i := 0; i < testspec.peers; i++ {
- peer, closePeer, _ := client.newRawPeer(t, fmt.Sprintf("server-%d", i), protocol, testspec.txLookups[i])
- closeFns = append(closeFns, closePeer)
-
- // Create a one-time routine for serving message
- go func(i int, peer *testPeer, lookup uint64) {
- serveMsg(peer, lookup)
- }(i, peer, testspec.txLookups[i])
- }
-
- // Send out the GetTxStatus requests, compare the result with
- // expected value.
- r := &light.TxStatusRequest{Hashes: testspec.txs}
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
-
- err := client.handler.backend.odr.RetrieveTxStatus(ctx, r)
- if err != nil {
- t.Errorf("Failed to retrieve tx status %v", err)
- } else {
- if !reflect.DeepEqual(testspec.results, r.Status) {
- t.Errorf("Result mismatch, diff")
- }
- }
-
- // Close all connected peers and start the next round
- for _, closeFn := range closeFns {
- closeFn()
- }
- }
-}
-
-// randomHash generates a random blob of data and returns it as a hash.
-func randomHash() common.Hash {
- var hash common.Hash
- if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
- panic(err)
- }
- return hash
-}
-*/
diff --git a/les/peer.go b/les/peer.go
deleted file mode 100644
index b38a393d4c5c..000000000000
--- a/les/peer.go
+++ /dev/null
@@ -1,1362 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "crypto/ecdsa"
- "errors"
- "fmt"
- "math/big"
- "math/rand"
- "net"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/les/utils"
- vfc "github.com/ethereum/go-ethereum/les/vflux/client"
- vfs "github.com/ethereum/go-ethereum/les/vflux/server"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-var (
- errClosed = errors.New("peer set is closed")
- errAlreadyRegistered = errors.New("peer is already registered")
- errNotRegistered = errors.New("peer is not registered")
-)
-
-const (
- maxRequestErrors = 20 // number of invalid requests tolerated (makes the protocol less brittle but still avoids spam)
- maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam)
-
- allowedUpdateBytes = 100000 // initial/maximum allowed update size
- allowedUpdateRate = time.Millisecond * 10 // time constant for recharging one byte of allowance
-
- freezeTimeBase = time.Millisecond * 700 // fixed component of client freeze time
- freezeTimeRandom = time.Millisecond * 600 // random component of client freeze time
- freezeCheckPeriod = time.Millisecond * 100 // buffer value recheck period after initial freeze time has elapsed
-
- // If the total encoded size of a sent transaction batch is over txSizeCostLimit
- // per transaction then the request cost is calculated as proportional to the
- // encoded size instead of the transaction count
- txSizeCostLimit = 0x4000
-
- // handshakeTimeout is the timeout LES handshake will be treated as failed.
- handshakeTimeout = 5 * time.Second
-)
-
-const (
- announceTypeNone = iota
- announceTypeSimple
- announceTypeSigned
-)
-
-type keyValueEntry struct {
- Key string
- Value rlp.RawValue
-}
-
-type keyValueList []keyValueEntry
-type keyValueMap map[string]rlp.RawValue
-
-func (l keyValueList) add(key string, val interface{}) keyValueList {
- var entry keyValueEntry
- entry.Key = key
- if val == nil {
- val = uint64(0)
- }
- enc, err := rlp.EncodeToBytes(val)
- if err == nil {
- entry.Value = enc
- }
- return append(l, entry)
-}
-
-func (l keyValueList) decode() (keyValueMap, uint64) {
- m := make(keyValueMap)
- var size uint64
- for _, entry := range l {
- m[entry.Key] = entry.Value
- size += uint64(len(entry.Key)) + uint64(len(entry.Value)) + 8
- }
- return m, size
-}
-
-func (m keyValueMap) get(key string, val interface{}) error {
- enc, ok := m[key]
- if !ok {
- return errResp(ErrMissingKey, "%s", key)
- }
- if val == nil {
- return nil
- }
- return rlp.DecodeBytes(enc, val)
-}
-
-// peerCommons contains fields needed by both server peer and client peer.
-type peerCommons struct {
- *p2p.Peer
- rw p2p.MsgReadWriter
-
- id string // Peer identity.
- version int // Protocol version negotiated.
- network uint64 // Network ID being on.
- frozen atomic.Bool // Flag whether the peer is frozen.
- announceType uint64 // New block announcement type.
- serving atomic.Bool // The status indicates the peer is served.
- headInfo blockInfo // Last announced block information.
-
- // Background task queue for caching peer tasks and executing in order.
- sendQueue *utils.ExecQueue
-
- // Flow control agreement.
- fcParams flowcontrol.ServerParams // The config for token bucket.
- fcCosts requestCostTable // The Maximum request cost table.
-
- closeCh chan struct{}
- lock sync.RWMutex // Lock used to protect all thread-sensitive fields.
-}
-
-// isFrozen returns true if the client is frozen or the server has put our
-// client in frozen state
-func (p *peerCommons) isFrozen() bool {
- return p.frozen.Load()
-}
-
-// canQueue returns an indicator whether the peer can queue an operation.
-func (p *peerCommons) canQueue() bool {
- return p.sendQueue.CanQueue() && !p.isFrozen()
-}
-
-// queueSend caches a peer operation in the background task queue.
-// Please ensure to check `canQueue` before call this function
-func (p *peerCommons) queueSend(f func()) bool {
- return p.sendQueue.Queue(f)
-}
-
-// String implements fmt.Stringer.
-func (p *peerCommons) String() string {
- return fmt.Sprintf("Peer %s [%s]", p.id, fmt.Sprintf("les/%d", p.version))
-}
-
-// PeerInfo represents a short summary of the `eth` sub-protocol metadata known
-// about a connected peer.
-type PeerInfo struct {
- Version int `json:"version"` // Ethereum protocol version negotiated
- Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
- Head string `json:"head"` // SHA3 hash of the peer's best owned block
-}
-
-// Info gathers and returns a collection of metadata known about a peer.
-func (p *peerCommons) Info() *PeerInfo {
- return &PeerInfo{
- Version: p.version,
- Difficulty: p.Td(),
- Head: fmt.Sprintf("%x", p.Head()),
- }
-}
-
-// Head retrieves a copy of the current head (most recent) hash of the peer.
-func (p *peerCommons) Head() (hash common.Hash) {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- return p.headInfo.Hash
-}
-
-// Td retrieves the current total difficulty of a peer.
-func (p *peerCommons) Td() *big.Int {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- return new(big.Int).Set(p.headInfo.Td)
-}
-
-// HeadAndTd retrieves the current head hash and total difficulty of a peer.
-func (p *peerCommons) HeadAndTd() (hash common.Hash, td *big.Int) {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- return p.headInfo.Hash, new(big.Int).Set(p.headInfo.Td)
-}
-
-// sendReceiveHandshake exchanges handshake packet with remote peer and returns any error
-// if failed to send or receive packet.
-func (p *peerCommons) sendReceiveHandshake(sendList keyValueList) (keyValueList, error) {
- var (
- errc = make(chan error, 2)
- recvList keyValueList
- )
- // Send out own handshake in a new thread
- go func() {
- errc <- p2p.Send(p.rw, StatusMsg, &sendList)
- }()
- go func() {
- // In the mean time retrieve the remote status message
- msg, err := p.rw.ReadMsg()
- if err != nil {
- errc <- err
- return
- }
- if msg.Code != StatusMsg {
- errc <- errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
- return
- }
- if msg.Size > ProtocolMaxMsgSize {
- errc <- errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
- return
- }
- // Decode the handshake
- if err := msg.Decode(&recvList); err != nil {
- errc <- errResp(ErrDecode, "msg %v: %v", msg, err)
- return
- }
- errc <- nil
- }()
- timeout := time.NewTimer(handshakeTimeout)
- defer timeout.Stop()
- for i := 0; i < 2; i++ {
- select {
- case err := <-errc:
- if err != nil {
- return nil, err
- }
- case <-timeout.C:
- return nil, p2p.DiscReadTimeout
- }
- }
- return recvList, nil
-}
-
-// handshake executes the les protocol handshake, negotiating version number,
-// network IDs, difficulties, head and genesis blocks. Besides the basic handshake
-// fields, server and client can exchange and resolve some specified fields through
-// two callback functions.
-func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, sendCallback func(*keyValueList), recvCallback func(keyValueMap) error) error {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- var send keyValueList
-
- // Add some basic handshake fields
- send = send.add("protocolVersion", uint64(p.version))
- send = send.add("networkId", p.network)
- // Note: the head info announced at handshake is only used in case of server peers
- // but dummy values are still announced by clients for compatibility with older servers
- send = send.add("headTd", td)
- send = send.add("headHash", head)
- send = send.add("headNum", headNum)
- send = send.add("genesisHash", genesis)
-
- // If the protocol version is beyond les4, then pass the forkID
- // as well. Check http://eips.ethereum.org/EIPS/eip-2124 for more
- // spec detail.
- if p.version >= lpv4 {
- send = send.add("forkID", forkID)
- }
- // Add client-specified or server-specified fields
- if sendCallback != nil {
- sendCallback(&send)
- }
- // Exchange the handshake packet and resolve the received one.
- recvList, err := p.sendReceiveHandshake(send)
- if err != nil {
- return err
- }
- recv, size := recvList.decode()
- if size > allowedUpdateBytes {
- return errResp(ErrRequestRejected, "")
- }
- var rGenesis common.Hash
- var rVersion, rNetwork uint64
- if err := recv.get("protocolVersion", &rVersion); err != nil {
- return err
- }
- if err := recv.get("networkId", &rNetwork); err != nil {
- return err
- }
- if err := recv.get("genesisHash", &rGenesis); err != nil {
- return err
- }
- if rGenesis != genesis {
- return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", rGenesis[:8], genesis[:8])
- }
- if rNetwork != p.network {
- return errResp(ErrNetworkIdMismatch, "%d (!= %d)", rNetwork, p.network)
- }
- if int(rVersion) != p.version {
- return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version)
- }
- // Check forkID if the protocol version is beyond the les4
- if p.version >= lpv4 {
- var forkID forkid.ID
- if err := recv.get("forkID", &forkID); err != nil {
- return err
- }
- if err := forkFilter(forkID); err != nil {
- return errResp(ErrForkIDRejected, "%v", err)
- }
- }
- if recvCallback != nil {
- return recvCallback(recv)
- }
- return nil
-}
-
-// close closes the channel and notifies all background routines to exit.
-func (p *peerCommons) close() {
- close(p.closeCh)
- p.sendQueue.Quit()
-}
-
-// serverPeer represents each node to which the client is connected.
-// The node here refers to the les server.
-type serverPeer struct {
- peerCommons
-
- // Status fields
- trusted bool // The flag whether the server is selected as trusted server.
- onlyAnnounce bool // The flag whether the server sends announcement only.
- chainSince, chainRecent uint64 // The range of chain server peer can serve.
- stateSince, stateRecent uint64 // The range of state server peer can serve.
- txHistory uint64 // The length of available tx history, 0 means all, 1 means disabled
-
- fcServer *flowcontrol.ServerNode // Client side mirror token bucket.
- vtLock sync.Mutex
- nodeValueTracker *vfc.NodeValueTracker
- sentReqs map[uint64]sentReqEntry
-
- // Statistics
- errCount utils.LinearExpiredValue // Counter the invalid responses server has replied
- updateCount uint64
- updateTime mclock.AbsTime
-
- // Test callback hooks
- hasBlockHook func(common.Hash, uint64, bool) bool // Used to determine whether the server has the specified block.
-}
-
-func newServerPeer(version int, network uint64, trusted bool, p *p2p.Peer, rw p2p.MsgReadWriter) *serverPeer {
- return &serverPeer{
- peerCommons: peerCommons{
- Peer: p,
- rw: rw,
- id: p.ID().String(),
- version: version,
- network: network,
- sendQueue: utils.NewExecQueue(100),
- closeCh: make(chan struct{}),
- },
- trusted: trusted,
- errCount: utils.LinearExpiredValue{Rate: mclock.AbsTime(time.Hour)},
- }
-}
-
-// rejectUpdate returns true if a parameter update has to be rejected because
-// the size and/or rate of updates exceed the capacity limitation
-func (p *serverPeer) rejectUpdate(size uint64) bool {
- now := mclock.Now()
- if p.updateCount == 0 {
- p.updateTime = now
- } else {
- dt := now - p.updateTime
- p.updateTime = now
-
- r := uint64(dt / mclock.AbsTime(allowedUpdateRate))
- if p.updateCount > r {
- p.updateCount -= r
- } else {
- p.updateCount = 0
- }
- }
- p.updateCount += size
- return p.updateCount > allowedUpdateBytes
-}
-
-// freeze processes Stop messages from the given server and set the status as
-// frozen.
-func (p *serverPeer) freeze() {
- if p.frozen.CompareAndSwap(false, true) {
- p.sendQueue.Clear()
- }
-}
-
-// unfreeze processes Resume messages from the given server and set the status
-// as unfrozen.
-func (p *serverPeer) unfreeze() {
- p.frozen.Store(false)
-}
-
-// sendRequest send a request to the server based on the given message type
-// and content.
-func sendRequest(w p2p.MsgWriter, msgcode, reqID uint64, data interface{}) error {
- type req struct {
- ReqID uint64
- Data interface{}
- }
- return p2p.Send(w, msgcode, &req{reqID, data})
-}
-
-func (p *serverPeer) sendRequest(msgcode, reqID uint64, data interface{}, amount int) error {
- p.sentRequest(reqID, uint32(msgcode), uint32(amount))
- return sendRequest(p.rw, msgcode, reqID, data)
-}
-
-// requestHeadersByHash fetches a batch of blocks' headers corresponding to the
-// specified header query, based on the hash of an origin block.
-func (p *serverPeer) requestHeadersByHash(reqID uint64, origin common.Hash, amount int, skip int, reverse bool) error {
- p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse)
- return p.sendRequest(GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount)
-}
-
-// requestHeadersByNumber fetches a batch of blocks' headers corresponding to the
-// specified header query, based on the number of an origin block.
-func (p *serverPeer) requestHeadersByNumber(reqID, origin uint64, amount int, skip int, reverse bool) error {
- p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse)
- return p.sendRequest(GetBlockHeadersMsg, reqID, &GetBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}, amount)
-}
-
-// requestBodies fetches a batch of blocks' bodies corresponding to the hashes
-// specified.
-func (p *serverPeer) requestBodies(reqID uint64, hashes []common.Hash) error {
- p.Log().Debug("Fetching batch of block bodies", "count", len(hashes))
- return p.sendRequest(GetBlockBodiesMsg, reqID, hashes, len(hashes))
-}
-
-// requestCode fetches a batch of arbitrary data from a node's known state
-// data, corresponding to the specified hashes.
-func (p *serverPeer) requestCode(reqID uint64, reqs []CodeReq) error {
- p.Log().Debug("Fetching batch of codes", "count", len(reqs))
- return p.sendRequest(GetCodeMsg, reqID, reqs, len(reqs))
-}
-
-// requestReceipts fetches a batch of transaction receipts from a remote node.
-func (p *serverPeer) requestReceipts(reqID uint64, hashes []common.Hash) error {
- p.Log().Debug("Fetching batch of receipts", "count", len(hashes))
- return p.sendRequest(GetReceiptsMsg, reqID, hashes, len(hashes))
-}
-
-// requestProofs fetches a batch of merkle proofs from a remote node.
-func (p *serverPeer) requestProofs(reqID uint64, reqs []ProofReq) error {
- p.Log().Debug("Fetching batch of proofs", "count", len(reqs))
- return p.sendRequest(GetProofsV2Msg, reqID, reqs, len(reqs))
-}
-
-// requestHelperTrieProofs fetches a batch of HelperTrie merkle proofs from a remote node.
-func (p *serverPeer) requestHelperTrieProofs(reqID uint64, reqs []HelperTrieReq) error {
- p.Log().Debug("Fetching batch of HelperTrie proofs", "count", len(reqs))
- return p.sendRequest(GetHelperTrieProofsMsg, reqID, reqs, len(reqs))
-}
-
-// requestTxStatus fetches a batch of transaction status records from a remote node.
-func (p *serverPeer) requestTxStatus(reqID uint64, txHashes []common.Hash) error {
- p.Log().Debug("Requesting transaction status", "count", len(txHashes))
- return p.sendRequest(GetTxStatusMsg, reqID, txHashes, len(txHashes))
-}
-
-// sendTxs creates a reply with a batch of transactions to be added to the remote transaction pool.
-func (p *serverPeer) sendTxs(reqID uint64, amount int, txs rlp.RawValue) error {
- p.Log().Debug("Sending batch of transactions", "amount", amount, "size", len(txs))
- sizeFactor := (len(txs) + txSizeCostLimit/2) / txSizeCostLimit
- if sizeFactor > amount {
- amount = sizeFactor
- }
- return p.sendRequest(SendTxV2Msg, reqID, txs, amount)
-}
-
-// waitBefore implements distPeer interface
-func (p *serverPeer) waitBefore(maxCost uint64) (time.Duration, float64) {
- return p.fcServer.CanSend(maxCost)
-}
-
-// getRequestCost returns an estimated request cost according to the flow control
-// rules negotiated between the server and the client.
-func (p *serverPeer) getRequestCost(msgcode uint64, amount int) uint64 {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- costs := p.fcCosts[msgcode]
- if costs == nil {
- return 0
- }
- cost := costs.baseCost + costs.reqCost*uint64(amount)
- if cost > p.fcParams.BufLimit {
- cost = p.fcParams.BufLimit
- }
- return cost
-}
-
-// getTxRelayCost returns an estimated relay cost according to the flow control
-// rules negotiated between the server and the client.
-func (p *serverPeer) getTxRelayCost(amount, size int) uint64 {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- costs := p.fcCosts[SendTxV2Msg]
- if costs == nil {
- return 0
- }
- cost := costs.baseCost + costs.reqCost*uint64(amount)
- sizeCost := costs.baseCost + costs.reqCost*uint64(size)/txSizeCostLimit
- if sizeCost > cost {
- cost = sizeCost
- }
- if cost > p.fcParams.BufLimit {
- cost = p.fcParams.BufLimit
- }
- return cost
-}
-
-// HasBlock checks if the peer has a given block
-func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bool {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- if p.hasBlockHook != nil {
- return p.hasBlockHook(hash, number, hasState)
- }
- head := p.headInfo.Number
- var since, recent uint64
- if hasState {
- since = p.stateSince
- recent = p.stateRecent
- } else {
- since = p.chainSince
- recent = p.chainRecent
- }
- return head >= number && number >= since && (recent == 0 || number+recent+4 > head)
-}
-
-// updateFlowControl updates the flow control parameters belonging to the server
-// node if the announced key/value set contains relevant fields
-func (p *serverPeer) updateFlowControl(update keyValueMap) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- // If any of the flow control params is nil, refuse to update.
- var params flowcontrol.ServerParams
- if update.get("flowControl/BL", ¶ms.BufLimit) == nil && update.get("flowControl/MRR", ¶ms.MinRecharge) == nil {
- // todo can light client set a minimal acceptable flow control params?
- p.fcParams = params
- p.fcServer.UpdateParams(params)
- }
- var MRC RequestCostList
- if update.get("flowControl/MRC", &MRC) == nil {
- costUpdate := MRC.decode(ProtocolLengths[uint(p.version)])
- for code, cost := range costUpdate {
- p.fcCosts[code] = cost
- }
- }
-}
-
-// updateHead updates the head information based on the announcement from
-// the peer.
-func (p *serverPeer) updateHead(hash common.Hash, number uint64, td *big.Int) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- p.headInfo = blockInfo{Hash: hash, Number: number, Td: td}
-}
-
-// Handshake executes the les protocol handshake, negotiating version number,
-// network IDs and genesis blocks.
-func (p *serverPeer) Handshake(genesis common.Hash, forkid forkid.ID, forkFilter forkid.Filter) error {
- // Note: there is no need to share local head with a server but older servers still
- // require these fields so we announce zero values.
- return p.handshake(common.Big0, common.Hash{}, 0, genesis, forkid, forkFilter, func(lists *keyValueList) {
- // Add some client-specific handshake fields
- //
- // Enable signed announcement randomly even the server is not trusted.
- p.announceType = announceTypeSimple
- if p.trusted {
- p.announceType = announceTypeSigned
- }
- *lists = (*lists).add("announceType", p.announceType)
- }, func(recv keyValueMap) error {
- var (
- rHash common.Hash
- rNum uint64
- rTd *big.Int
- )
- if err := recv.get("headTd", &rTd); err != nil {
- return err
- }
- if err := recv.get("headHash", &rHash); err != nil {
- return err
- }
- if err := recv.get("headNum", &rNum); err != nil {
- return err
- }
- p.headInfo = blockInfo{Hash: rHash, Number: rNum, Td: rTd}
- if recv.get("serveChainSince", &p.chainSince) != nil {
- p.onlyAnnounce = true
- }
- if recv.get("serveRecentChain", &p.chainRecent) != nil {
- p.chainRecent = 0
- }
- if recv.get("serveStateSince", &p.stateSince) != nil {
- p.onlyAnnounce = true
- }
- if recv.get("serveRecentState", &p.stateRecent) != nil {
- p.stateRecent = 0
- }
- if recv.get("txRelay", nil) != nil {
- p.onlyAnnounce = true
- }
- if p.version >= lpv4 {
- var recentTx uint
- if err := recv.get("recentTxLookup", &recentTx); err != nil {
- return err
- }
- p.txHistory = uint64(recentTx)
- } else {
- // The weak assumption is held here that legacy les server(les2,3)
- // has unlimited transaction history. The les serving in these legacy
- // versions is disabled if the transaction is unindexed.
- p.txHistory = txIndexUnlimited
- }
- if p.onlyAnnounce && !p.trusted {
- return errResp(ErrUselessPeer, "peer cannot serve requests")
- }
- // Parse flow control handshake packet.
- var sParams flowcontrol.ServerParams
- if err := recv.get("flowControl/BL", &sParams.BufLimit); err != nil {
- return err
- }
- if err := recv.get("flowControl/MRR", &sParams.MinRecharge); err != nil {
- return err
- }
- var MRC RequestCostList
- if err := recv.get("flowControl/MRC", &MRC); err != nil {
- return err
- }
- p.fcParams = sParams
- p.fcServer = flowcontrol.NewServerNode(sParams, &mclock.System{})
- p.fcCosts = MRC.decode(ProtocolLengths[uint(p.version)])
-
- if !p.onlyAnnounce {
- for msgCode := range reqAvgTimeCost {
- if p.fcCosts[msgCode] == nil {
- return errResp(ErrUselessPeer, "peer does not support message %d", msgCode)
- }
- }
- }
- return nil
- })
-}
-
-// setValueTracker sets the value tracker references for connected servers. Note that the
-// references should be removed upon disconnection by setValueTracker(nil, nil).
-func (p *serverPeer) setValueTracker(nvt *vfc.NodeValueTracker) {
- p.vtLock.Lock()
- p.nodeValueTracker = nvt
- if nvt != nil {
- p.sentReqs = make(map[uint64]sentReqEntry)
- } else {
- p.sentReqs = nil
- }
- p.vtLock.Unlock()
-}
-
-// updateVtParams updates the server's price table in the value tracker.
-func (p *serverPeer) updateVtParams() {
- p.vtLock.Lock()
- defer p.vtLock.Unlock()
-
- if p.nodeValueTracker == nil {
- return
- }
- reqCosts := make([]uint64, len(requestList))
- for code, costs := range p.fcCosts {
- if m, ok := requestMapping[uint32(code)]; ok {
- reqCosts[m.first] = costs.baseCost + costs.reqCost
- if m.rest != -1 {
- reqCosts[m.rest] = costs.reqCost
- }
- }
- }
- p.nodeValueTracker.UpdateCosts(reqCosts)
-}
-
-// sentReqEntry remembers sent requests and their sending times
-type sentReqEntry struct {
- reqType, amount uint32
- at mclock.AbsTime
-}
-
-// sentRequest marks a request sent at the current moment to this server.
-func (p *serverPeer) sentRequest(id uint64, reqType, amount uint32) {
- p.vtLock.Lock()
- if p.sentReqs != nil {
- p.sentReqs[id] = sentReqEntry{reqType, amount, mclock.Now()}
- }
- p.vtLock.Unlock()
-}
-
-// answeredRequest marks a request answered at the current moment by this server.
-func (p *serverPeer) answeredRequest(id uint64) {
- p.vtLock.Lock()
- if p.sentReqs == nil {
- p.vtLock.Unlock()
- return
- }
- e, ok := p.sentReqs[id]
- delete(p.sentReqs, id)
- nvt := p.nodeValueTracker
- p.vtLock.Unlock()
- if !ok {
- return
- }
- var (
- vtReqs [2]vfc.ServedRequest
- reqCount int
- )
- m := requestMapping[e.reqType]
- if m.rest == -1 || e.amount <= 1 {
- reqCount = 1
- vtReqs[0] = vfc.ServedRequest{ReqType: uint32(m.first), Amount: e.amount}
- } else {
- reqCount = 2
- vtReqs[0] = vfc.ServedRequest{ReqType: uint32(m.first), Amount: 1}
- vtReqs[1] = vfc.ServedRequest{ReqType: uint32(m.rest), Amount: e.amount - 1}
- }
- dt := time.Duration(mclock.Now() - e.at)
- nvt.Served(vtReqs[:reqCount], dt)
-}
-
-// clientPeer represents each node to which the les server is connected.
-// The node here refers to the light client.
-type clientPeer struct {
- peerCommons
-
- // responseLock ensures that responses are queued in the same order as
- // RequestProcessed is called
- responseLock sync.Mutex
- responseCount uint64 // Counter to generate an unique id for request processing.
-
- balance vfs.ConnectedBalance
-
- // invalidLock is used for protecting invalidCount.
- invalidLock sync.RWMutex
- invalidCount utils.LinearExpiredValue // Counter the invalid request the client peer has made.
-
- capacity uint64
- // lastAnnounce is the last broadcast created by the server; may be newer than the last head
- // sent to the specific client (stored in headInfo) if capacity is zero. In this case the
- // latest head is sent when the client gains non-zero capacity.
- lastAnnounce announceData
-
- connectedAt mclock.AbsTime
- server bool
- errCh chan error
- fcClient *flowcontrol.ClientNode // Server side mirror token bucket.
-}
-
-func newClientPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *clientPeer {
- return &clientPeer{
- peerCommons: peerCommons{
- Peer: p,
- rw: rw,
- id: p.ID().String(),
- version: version,
- network: network,
- sendQueue: utils.NewExecQueue(100),
- closeCh: make(chan struct{}),
- },
- invalidCount: utils.LinearExpiredValue{Rate: mclock.AbsTime(time.Hour)},
- errCh: make(chan error, 1),
- }
-}
-
-// FreeClientId returns a string identifier for the peer. Multiple peers with
-// the same identifier can not be connected in free mode simultaneously.
-func (p *clientPeer) FreeClientId() string {
- if addr, ok := p.RemoteAddr().(*net.TCPAddr); ok {
- if addr.IP.IsLoopback() {
- // using peer id instead of loopback ip address allows multiple free
- // connections from local machine to own server
- return p.id
- } else {
- return addr.IP.String()
- }
- }
- return p.id
-}
-
-// sendStop notifies the client about being in frozen state
-func (p *clientPeer) sendStop() error {
- return p2p.Send(p.rw, StopMsg, struct{}{})
-}
-
-// sendResume notifies the client about getting out of frozen state
-func (p *clientPeer) sendResume(bv uint64) error {
- return p2p.Send(p.rw, ResumeMsg, bv)
-}
-
-// freeze temporarily puts the client in a frozen state which means all unprocessed
-// and subsequent requests are dropped. Unfreezing happens automatically after a short
-// time if the client's buffer value is at least in the slightly positive region.
-// The client is also notified about being frozen/unfrozen with a Stop/Resume message.
-func (p *clientPeer) freeze() {
- if p.version < lpv3 {
- // if Stop/Resume is not supported then just drop the peer after setting
- // its frozen status permanently
- p.frozen.Store(true)
- p.Peer.Disconnect(p2p.DiscUselessPeer)
- return
- }
- if !p.frozen.Swap(true) {
- go func() {
- p.sendStop()
- time.Sleep(freezeTimeBase + time.Duration(rand.Int63n(int64(freezeTimeRandom))))
- for {
- bufValue, bufLimit := p.fcClient.BufferStatus()
- if bufLimit == 0 {
- return
- }
- if bufValue <= bufLimit/8 {
- time.Sleep(freezeCheckPeriod)
- continue
- }
- p.frozen.Store(false)
- p.sendResume(bufValue)
- return
- }
- }()
- }
-}
-
-// reply struct represents a reply with the actual data already RLP encoded and
-// only the bv (buffer value) missing. This allows the serving mechanism to
-// calculate the bv value which depends on the data size before sending the reply.
-type reply struct {
- w p2p.MsgWriter
- msgcode, reqID uint64
- data rlp.RawValue
-}
-
-// send sends the reply with the calculated buffer value
-func (r *reply) send(bv uint64) error {
- type resp struct {
- ReqID, BV uint64
- Data rlp.RawValue
- }
- return p2p.Send(r.w, r.msgcode, &resp{r.reqID, bv, r.data})
-}
-
-// size returns the RLP encoded size of the message data
-func (r *reply) size() uint32 {
- return uint32(len(r.data))
-}
-
-// replyBlockHeaders creates a reply with a batch of block headers
-func (p *clientPeer) replyBlockHeaders(reqID uint64, headers []*types.Header) *reply {
- data, _ := rlp.EncodeToBytes(headers)
- return &reply{p.rw, BlockHeadersMsg, reqID, data}
-}
-
-// replyBlockBodiesRLP creates a reply with a batch of block contents from
-// an already RLP encoded format.
-func (p *clientPeer) replyBlockBodiesRLP(reqID uint64, bodies []rlp.RawValue) *reply {
- data, _ := rlp.EncodeToBytes(bodies)
- return &reply{p.rw, BlockBodiesMsg, reqID, data}
-}
-
-// replyCode creates a reply with a batch of arbitrary internal data, corresponding to the
-// hashes requested.
-func (p *clientPeer) replyCode(reqID uint64, codes [][]byte) *reply {
- data, _ := rlp.EncodeToBytes(codes)
- return &reply{p.rw, CodeMsg, reqID, data}
-}
-
-// replyReceiptsRLP creates a reply with a batch of transaction receipts, corresponding to the
-// ones requested from an already RLP encoded format.
-func (p *clientPeer) replyReceiptsRLP(reqID uint64, receipts []rlp.RawValue) *reply {
- data, _ := rlp.EncodeToBytes(receipts)
- return &reply{p.rw, ReceiptsMsg, reqID, data}
-}
-
-// replyProofsV2 creates a reply with a batch of merkle proofs, corresponding to the ones requested.
-func (p *clientPeer) replyProofsV2(reqID uint64, proofs trienode.ProofList) *reply {
- data, _ := rlp.EncodeToBytes(proofs)
- return &reply{p.rw, ProofsV2Msg, reqID, data}
-}
-
-// replyHelperTrieProofs creates a reply with a batch of HelperTrie proofs, corresponding to the ones requested.
-func (p *clientPeer) replyHelperTrieProofs(reqID uint64, resp HelperTrieResps) *reply {
- data, _ := rlp.EncodeToBytes(resp)
- return &reply{p.rw, HelperTrieProofsMsg, reqID, data}
-}
-
-// replyTxStatus creates a reply with a batch of transaction status records, corresponding to the ones requested.
-func (p *clientPeer) replyTxStatus(reqID uint64, stats []light.TxStatus) *reply {
- data, _ := rlp.EncodeToBytes(stats)
- return &reply{p.rw, TxStatusMsg, reqID, data}
-}
-
-// sendAnnounce announces the availability of a number of blocks through
-// a hash notification.
-func (p *clientPeer) sendAnnounce(request announceData) error {
- return p2p.Send(p.rw, AnnounceMsg, request)
-}
-
-// InactiveAllowance implements vfs.clientPeer
-func (p *clientPeer) InactiveAllowance() time.Duration {
- return 0 // will return more than zero for les/5 clients
-}
-
-// getCapacity returns the current capacity of the peer
-func (p *clientPeer) getCapacity() uint64 {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- return p.capacity
-}
-
-// UpdateCapacity updates the request serving capacity assigned to a given client
-// and also sends an announcement about the updated flow control parameters.
-// Note: UpdateCapacity implements vfs.clientPeer and should not block. The requested
-// parameter is true if the callback was initiated by ClientPool.SetCapacity on the given peer.
-func (p *clientPeer) UpdateCapacity(newCap uint64, requested bool) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- if newCap != p.fcParams.MinRecharge {
- p.fcParams = flowcontrol.ServerParams{MinRecharge: newCap, BufLimit: newCap * bufLimitRatio}
- p.fcClient.UpdateParams(p.fcParams)
- var kvList keyValueList
- kvList = kvList.add("flowControl/MRR", newCap)
- kvList = kvList.add("flowControl/BL", newCap*bufLimitRatio)
- p.queueSend(func() { p.sendAnnounce(announceData{Update: kvList}) })
- }
-
- if p.capacity == 0 && newCap != 0 {
- p.sendLastAnnounce()
- }
- p.capacity = newCap
-}
-
-// announceOrStore sends the given head announcement to the client if the client is
-// active (capacity != 0) and the same announcement hasn't been sent before. If the
-// client is inactive the announcement is stored and sent later if the client is
-// activated again.
-func (p *clientPeer) announceOrStore(announce announceData) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- p.lastAnnounce = announce
- if p.capacity != 0 {
- p.sendLastAnnounce()
- }
-}
-
-// announce sends the given head announcement to the client if it hasn't been sent before
-func (p *clientPeer) sendLastAnnounce() {
- if p.lastAnnounce.Td == nil {
- return
- }
- if p.headInfo.Td == nil || p.lastAnnounce.Td.Cmp(p.headInfo.Td) > 0 {
- if !p.queueSend(func() { p.sendAnnounce(p.lastAnnounce) }) {
- p.Log().Debug("Dropped announcement because queue is full", "number", p.lastAnnounce.Number, "hash", p.lastAnnounce.Hash)
- } else {
- p.Log().Debug("Sent announcement", "number", p.lastAnnounce.Number, "hash", p.lastAnnounce.Hash)
- }
- p.headInfo = blockInfo{Hash: p.lastAnnounce.Hash, Number: p.lastAnnounce.Number, Td: p.lastAnnounce.Td}
- }
-}
-
-// Handshake executes the les protocol handshake, negotiating version number,
-// network IDs, difficulties, head and genesis blocks.
-func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, server *LesServer) error {
- recentTx := server.handler.blockchain.TxLookupLimit()
- if recentTx != txIndexUnlimited {
- if recentTx < blockSafetyMargin {
- recentTx = txIndexDisabled
- } else {
- recentTx -= blockSafetyMargin - txIndexRecentOffset
- }
- }
- if recentTx != txIndexUnlimited && p.version < lpv4 {
- return errors.New("cannot serve old clients without a complete tx index")
- }
- // Note: clientPeer.headInfo should contain the last head announced to the client by us.
- // The values announced in the handshake are dummy values for compatibility reasons and should be ignored.
- p.headInfo = blockInfo{Hash: head, Number: headNum, Td: td}
- return p.handshake(td, head, headNum, genesis, forkID, forkFilter, func(lists *keyValueList) {
- // Add some information which services server can offer.
- *lists = (*lists).add("serveHeaders", nil)
- *lists = (*lists).add("serveChainSince", uint64(0))
- *lists = (*lists).add("serveStateSince", uint64(0))
-
- // If local ethereum node is running in archive mode, advertise ourselves we have
- // all version state data. Otherwise only recent state is available.
- stateRecent := uint64(core.TriesInMemory - blockSafetyMargin)
- if server.archiveMode {
- stateRecent = 0
- }
- *lists = (*lists).add("serveRecentState", stateRecent)
- *lists = (*lists).add("txRelay", nil)
- if p.version >= lpv4 {
- *lists = (*lists).add("recentTxLookup", recentTx)
- }
- *lists = (*lists).add("flowControl/BL", server.defParams.BufLimit)
- *lists = (*lists).add("flowControl/MRR", server.defParams.MinRecharge)
-
- var costList RequestCostList
- if server.costTracker.testCostList != nil {
- costList = server.costTracker.testCostList
- } else {
- costList = server.costTracker.makeCostList(server.costTracker.globalFactor())
- }
- *lists = (*lists).add("flowControl/MRC", costList)
- p.fcCosts = costList.decode(ProtocolLengths[uint(p.version)])
- p.fcParams = server.defParams
- }, func(recv keyValueMap) error {
- p.server = recv.get("flowControl/MRR", nil) == nil
- if p.server {
- p.announceType = announceTypeNone // connected to another server, send no messages
- } else {
- if recv.get("announceType", &p.announceType) != nil {
- // set default announceType on server side
- p.announceType = announceTypeSimple
- }
- }
- return nil
- })
-}
-
-func (p *clientPeer) bumpInvalid() {
- p.invalidLock.Lock()
- p.invalidCount.Add(1, mclock.Now())
- p.invalidLock.Unlock()
-}
-
-func (p *clientPeer) getInvalid() uint64 {
- p.invalidLock.RLock()
- defer p.invalidLock.RUnlock()
- return p.invalidCount.Value(mclock.Now())
-}
-
-// Disconnect implements vfs.clientPeer
-func (p *clientPeer) Disconnect() {
- p.Peer.Disconnect(p2p.DiscRequested)
-}
-
-// serverPeerSubscriber is an interface to notify services about added or
-// removed server peers
-type serverPeerSubscriber interface {
- registerPeer(*serverPeer)
- unregisterPeer(*serverPeer)
-}
-
-// serverPeerSet represents the set of active server peers currently
-// participating in the Light Ethereum sub-protocol.
-type serverPeerSet struct {
- peers map[string]*serverPeer
- // subscribers is a batch of subscribers and peerset will notify
- // these subscribers when the peerset changes(new server peer is
- // added or removed)
- subscribers []serverPeerSubscriber
- closed bool
- lock sync.RWMutex
-}
-
-// newServerPeerSet creates a new peer set to track the active server peers.
-func newServerPeerSet() *serverPeerSet {
- return &serverPeerSet{peers: make(map[string]*serverPeer)}
-}
-
-// subscribe adds a service to be notified about added or removed
-// peers and also register all active peers into the given service.
-func (ps *serverPeerSet) subscribe(sub serverPeerSubscriber) {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- ps.subscribers = append(ps.subscribers, sub)
- for _, p := range ps.peers {
- sub.registerPeer(p)
- }
-}
-
-// register adds a new server peer into the set, or returns an error if the
-// peer is already known.
-func (ps *serverPeerSet) register(peer *serverPeer) error {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- if ps.closed {
- return errClosed
- }
- if _, exist := ps.peers[peer.id]; exist {
- return errAlreadyRegistered
- }
- ps.peers[peer.id] = peer
- for _, sub := range ps.subscribers {
- sub.registerPeer(peer)
- }
- return nil
-}
-
-// unregister removes a remote peer from the active set, disabling any further
-// actions to/from that particular entity. It also initiates disconnection at
-// the networking layer.
-func (ps *serverPeerSet) unregister(id string) error {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- p, ok := ps.peers[id]
- if !ok {
- return errNotRegistered
- }
- delete(ps.peers, id)
- for _, sub := range ps.subscribers {
- sub.unregisterPeer(p)
- }
- p.Peer.Disconnect(p2p.DiscRequested)
- return nil
-}
-
-// ids returns a list of all registered peer IDs
-func (ps *serverPeerSet) ids() []string {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- var ids []string
- for id := range ps.peers {
- ids = append(ids, id)
- }
- return ids
-}
-
-// peer retrieves the registered peer with the given id.
-func (ps *serverPeerSet) peer(id string) *serverPeer {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- return ps.peers[id]
-}
-
-// len returns if the current number of peers in the set.
-func (ps *serverPeerSet) len() int {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- return len(ps.peers)
-}
-
-// allServerPeers returns all server peers in a list.
-func (ps *serverPeerSet) allPeers() []*serverPeer {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- list := make([]*serverPeer, 0, len(ps.peers))
- for _, p := range ps.peers {
- list = append(list, p)
- }
- return list
-}
-
-// close disconnects all peers. No new peers can be registered
-// after close has returned.
-func (ps *serverPeerSet) close() {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- for _, p := range ps.peers {
- p.Disconnect(p2p.DiscQuitting)
- }
- ps.closed = true
-}
-
-// clientPeerSet represents the set of active client peers currently
-// participating in the Light Ethereum sub-protocol.
-type clientPeerSet struct {
- peers map[enode.ID]*clientPeer
- lock sync.RWMutex
- closed bool
-
- privateKey *ecdsa.PrivateKey
- lastAnnounce, signedAnnounce announceData
-}
-
-// newClientPeerSet creates a new peer set to track the client peers.
-func newClientPeerSet() *clientPeerSet {
- return &clientPeerSet{peers: make(map[enode.ID]*clientPeer)}
-}
-
-// register adds a new peer into the peer set, or returns an error if the
-// peer is already known.
-func (ps *clientPeerSet) register(peer *clientPeer) error {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- if ps.closed {
- return errClosed
- }
- if _, exist := ps.peers[peer.ID()]; exist {
- return errAlreadyRegistered
- }
- ps.peers[peer.ID()] = peer
- ps.announceOrStore(peer)
- return nil
-}
-
-// unregister removes a remote peer from the peer set, disabling any further
-// actions to/from that particular entity. It also initiates disconnection
-// at the networking layer.
-func (ps *clientPeerSet) unregister(id enode.ID) error {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- p, ok := ps.peers[id]
- if !ok {
- return errNotRegistered
- }
- delete(ps.peers, id)
- p.Peer.Disconnect(p2p.DiscRequested)
- return nil
-}
-
-// ids returns a list of all registered peer IDs
-func (ps *clientPeerSet) ids() []enode.ID {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- var ids []enode.ID
- for id := range ps.peers {
- ids = append(ids, id)
- }
- return ids
-}
-
-// peer retrieves the registered peer with the given id.
-func (ps *clientPeerSet) peer(id enode.ID) *clientPeer {
- ps.lock.RLock()
- defer ps.lock.RUnlock()
-
- return ps.peers[id]
-}
-
-// setSignerKey sets the signer key for signed announcements. Should be called before
-// starting the protocol handler.
-func (ps *clientPeerSet) setSignerKey(privateKey *ecdsa.PrivateKey) {
- ps.privateKey = privateKey
-}
-
-// broadcast sends the given announcements to all active peers
-func (ps *clientPeerSet) broadcast(announce announceData) {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- ps.lastAnnounce = announce
- for _, peer := range ps.peers {
- ps.announceOrStore(peer)
- }
-}
-
-// announceOrStore sends the requested type of announcement to the given peer or stores
-// it for later if the peer is inactive (capacity == 0).
-func (ps *clientPeerSet) announceOrStore(p *clientPeer) {
- if ps.lastAnnounce.Td == nil {
- return
- }
- switch p.announceType {
- case announceTypeSimple:
- p.announceOrStore(ps.lastAnnounce)
- case announceTypeSigned:
- if ps.signedAnnounce.Hash != ps.lastAnnounce.Hash {
- ps.signedAnnounce = ps.lastAnnounce
- ps.signedAnnounce.sign(ps.privateKey)
- }
- p.announceOrStore(ps.signedAnnounce)
- }
-}
-
-// close disconnects all peers. No new peers can be registered
-// after close has returned.
-func (ps *clientPeerSet) close() {
- ps.lock.Lock()
- defer ps.lock.Unlock()
-
- for _, p := range ps.peers {
- p.Peer.Disconnect(p2p.DiscQuitting)
- }
- ps.closed = true
-}
-
-// serverSet is a special set which contains all connected les servers.
-// Les servers will also be discovered by discovery protocol because they
-// also run the LES protocol. We can't drop them although they are useless
-// for us(server) but for other protocols(e.g. ETH) upon the devp2p they
-// may be useful.
-type serverSet struct {
- lock sync.Mutex
- set map[string]*clientPeer
- closed bool
-}
-
-func newServerSet() *serverSet {
- return &serverSet{set: make(map[string]*clientPeer)}
-}
-
-func (s *serverSet) register(peer *clientPeer) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- if s.closed {
- return errClosed
- }
- if _, exist := s.set[peer.id]; exist {
- return errAlreadyRegistered
- }
- s.set[peer.id] = peer
- return nil
-}
-
-func (s *serverSet) unregister(peer *clientPeer) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- if s.closed {
- return errClosed
- }
- if _, exist := s.set[peer.id]; !exist {
- return errNotRegistered
- }
- delete(s.set, peer.id)
- peer.Peer.Disconnect(p2p.DiscQuitting)
- return nil
-}
-
-func (s *serverSet) close() {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- for _, p := range s.set {
- p.Peer.Disconnect(p2p.DiscQuitting)
- }
- s.closed = true
-}
diff --git a/les/peer_test.go b/les/peer_test.go
deleted file mode 100644
index d6ca0eac7c20..000000000000
--- a/les/peer_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "crypto/rand"
- "errors"
- "math/big"
- "reflect"
- "sort"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/params"
-)
-
-type testServerPeerSub struct {
- regCh chan *serverPeer
- unregCh chan *serverPeer
-}
-
-func newTestServerPeerSub() *testServerPeerSub {
- return &testServerPeerSub{
- regCh: make(chan *serverPeer, 1),
- unregCh: make(chan *serverPeer, 1),
- }
-}
-
-func (t *testServerPeerSub) registerPeer(p *serverPeer) { t.regCh <- p }
-func (t *testServerPeerSub) unregisterPeer(p *serverPeer) { t.unregCh <- p }
-
-func TestPeerSubscription(t *testing.T) {
- peers := newServerPeerSet()
- defer peers.close()
-
- checkIds := func(expect []string) {
- given := peers.ids()
- if len(given) == 0 && len(expect) == 0 {
- return
- }
- sort.Strings(given)
- sort.Strings(expect)
- if !reflect.DeepEqual(given, expect) {
- t.Fatalf("all peer ids mismatch, want %v, given %v", expect, given)
- }
- }
- checkPeers := func(peerCh chan *serverPeer) {
- select {
- case <-peerCh:
- case <-time.NewTimer(100 * time.Millisecond).C:
- t.Fatalf("timeout, no event received")
- }
- select {
- case <-peerCh:
- t.Fatalf("unexpected event received")
- case <-time.NewTimer(10 * time.Millisecond).C:
- }
- }
- checkIds([]string{})
-
- sub := newTestServerPeerSub()
- peers.subscribe(sub)
-
- // Generate a random id and create the peer
- var id enode.ID
- rand.Read(id[:])
- peer := newServerPeer(2, NetworkId, false, p2p.NewPeer(id, "name", nil), nil)
- peers.register(peer)
-
- checkIds([]string{peer.id})
- checkPeers(sub.regCh)
-
- peers.unregister(peer.id)
- checkIds([]string{})
- checkPeers(sub.unregCh)
-}
-
-type fakeChain struct{}
-
-func (f *fakeChain) Config() *params.ChainConfig { return params.MainnetChainConfig }
-func (f *fakeChain) Genesis() *types.Block {
- return core.DefaultGenesisBlock().ToBlock()
-}
-func (f *fakeChain) CurrentHeader() *types.Header { return &types.Header{Number: big.NewInt(10000000)} }
-
-func TestHandshake(t *testing.T) {
- // Create a message pipe to communicate through
- app, net := p2p.MsgPipe()
-
- // Generate a random id and create the peer
- var id enode.ID
- rand.Read(id[:])
-
- peer1 := newClientPeer(2, NetworkId, p2p.NewPeer(id, "name", nil), net)
- peer2 := newServerPeer(2, NetworkId, true, p2p.NewPeer(id, "name", nil), app)
-
- var (
- errCh1 = make(chan error, 1)
- errCh2 = make(chan error, 1)
-
- td = big.NewInt(100)
- head = common.HexToHash("deadbeef")
- headNum = uint64(10)
- genesis = common.HexToHash("cafebabe")
-
- chain1, chain2 = &fakeChain{}, &fakeChain{}
- forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis(), chain1.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Time)
- forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis(), chain2.CurrentHeader().Number.Uint64(), chain2.CurrentHeader().Time)
- filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2)
- )
-
- go func() {
- errCh1 <- peer1.handshake(td, head, headNum, genesis, forkID1, filter1, func(list *keyValueList) {
- var announceType uint64 = announceTypeSigned
- *list = (*list).add("announceType", announceType)
- }, nil)
- }()
- go func() {
- errCh2 <- peer2.handshake(td, head, headNum, genesis, forkID2, filter2, nil, func(recv keyValueMap) error {
- var reqType uint64
- err := recv.get("announceType", &reqType)
- if err != nil {
- return err
- }
- if reqType != announceTypeSigned {
- return errors.New("expected announceTypeSigned")
- }
- return nil
- })
- }()
-
- for i := 0; i < 2; i++ {
- select {
- case err := <-errCh1:
- if err != nil {
- t.Fatalf("handshake failed, %v", err)
- }
- case err := <-errCh2:
- if err != nil {
- t.Fatalf("handshake failed, %v", err)
- }
- case <-time.After(time.Second):
- t.Fatalf("timeout")
- }
- }
-}
diff --git a/les/protocol.go b/les/protocol.go
deleted file mode 100644
index cfebdbfb9a5a..000000000000
--- a/les/protocol.go
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "crypto/ecdsa"
- "errors"
- "fmt"
- "io"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- vfc "github.com/ethereum/go-ethereum/les/vflux/client"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// Constants to match up protocol versions and messages
-const (
- lpv2 = 2
- lpv3 = 3
- lpv4 = 4
-)
-
-// Supported versions of the les protocol (first is primary)
-var (
- ClientProtocolVersions = []uint{lpv2, lpv3, lpv4}
- ServerProtocolVersions = []uint{lpv2, lpv3, lpv4}
-)
-
-// ProtocolLengths is the number of implemented message corresponding to different protocol versions.
-var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24}
-
-const (
- NetworkId = 1
- ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
- blockSafetyMargin = 4 // safety margin applied to block ranges specified relative to head block
-
- txIndexUnlimited = 0 // this value in the "recentTxLookup" handshake field means the entire tx index history is served
- txIndexDisabled = 1 // this value means tx index is not served at all
- txIndexRecentOffset = 1 // txIndexRecentOffset + N in the handshake field means then tx index of the last N blocks is supported
-)
-
-// les protocol message codes
-const (
- // Protocol messages inherited from LPV1
- StatusMsg = 0x00
- AnnounceMsg = 0x01
- GetBlockHeadersMsg = 0x02
- BlockHeadersMsg = 0x03
- GetBlockBodiesMsg = 0x04
- BlockBodiesMsg = 0x05
- GetReceiptsMsg = 0x06
- ReceiptsMsg = 0x07
- GetCodeMsg = 0x0a
- CodeMsg = 0x0b
- // Protocol messages introduced in LPV2
- GetProofsV2Msg = 0x0f
- ProofsV2Msg = 0x10
- GetHelperTrieProofsMsg = 0x11
- HelperTrieProofsMsg = 0x12
- SendTxV2Msg = 0x13
- GetTxStatusMsg = 0x14
- TxStatusMsg = 0x15
- // Protocol messages introduced in LPV3
- StopMsg = 0x16
- ResumeMsg = 0x17
-)
-
-// GetBlockHeadersData represents a block header query (the request ID is not included)
-type GetBlockHeadersData struct {
- Origin hashOrNumber // Block from which to retrieve headers
- Amount uint64 // Maximum number of headers to retrieve
- Skip uint64 // Blocks to skip between consecutive headers
- Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
-}
-
-// GetBlockHeadersPacket represents a block header request
-type GetBlockHeadersPacket struct {
- ReqID uint64
- Query GetBlockHeadersData
-}
-
-// GetBlockBodiesPacket represents a block body request
-type GetBlockBodiesPacket struct {
- ReqID uint64
- Hashes []common.Hash
-}
-
-// GetCodePacket represents a contract code request
-type GetCodePacket struct {
- ReqID uint64
- Reqs []CodeReq
-}
-
-// GetReceiptsPacket represents a block receipts request
-type GetReceiptsPacket struct {
- ReqID uint64
- Hashes []common.Hash
-}
-
-// GetProofsPacket represents a proof request
-type GetProofsPacket struct {
- ReqID uint64
- Reqs []ProofReq
-}
-
-// GetHelperTrieProofsPacket represents a helper trie proof request
-type GetHelperTrieProofsPacket struct {
- ReqID uint64
- Reqs []HelperTrieReq
-}
-
-// SendTxPacket represents a transaction propagation request
-type SendTxPacket struct {
- ReqID uint64
- Txs []*types.Transaction
-}
-
-// GetTxStatusPacket represents a transaction status query
-type GetTxStatusPacket struct {
- ReqID uint64
- Hashes []common.Hash
-}
-
-type requestInfo struct {
- name string
- maxCount uint64
- refBasketFirst, refBasketRest float64
-}
-
-// reqMapping maps an LES request to one or two vflux service vector entries.
-// If rest != -1 and the request type is used with amounts larger than one then the
-// first one of the multi-request is mapped to first while the rest is mapped to rest.
-type reqMapping struct {
- first, rest int
-}
-
-var (
- // requests describes the available LES request types and their initializing amounts
- // in the vfc.ValueTracker reference basket. Initial values are estimates
- // based on the same values as the server's default cost estimates (reqAvgTimeCost).
- requests = map[uint64]requestInfo{
- GetBlockHeadersMsg: {"GetBlockHeaders", MaxHeaderFetch, 10, 1000},
- GetBlockBodiesMsg: {"GetBlockBodies", MaxBodyFetch, 1, 0},
- GetReceiptsMsg: {"GetReceipts", MaxReceiptFetch, 1, 0},
- GetCodeMsg: {"GetCode", MaxCodeFetch, 1, 0},
- GetProofsV2Msg: {"GetProofsV2", MaxProofsFetch, 10, 0},
- GetHelperTrieProofsMsg: {"GetHelperTrieProofs", MaxHelperTrieProofsFetch, 10, 100},
- SendTxV2Msg: {"SendTxV2", MaxTxSend, 1, 0},
- GetTxStatusMsg: {"GetTxStatus", MaxTxStatus, 10, 0},
- }
- requestList []vfc.RequestInfo
- requestMapping map[uint32]reqMapping
-)
-
-// init creates a request list and mapping between protocol message codes and vflux
-// service vector indices.
-func init() {
- requestMapping = make(map[uint32]reqMapping)
- for code, req := range requests {
- cost := reqAvgTimeCost[code]
- rm := reqMapping{len(requestList), -1}
- requestList = append(requestList, vfc.RequestInfo{
- Name: req.name + ".first",
- InitAmount: req.refBasketFirst,
- InitValue: float64(cost.baseCost + cost.reqCost),
- })
- if req.refBasketRest != 0 {
- rm.rest = len(requestList)
- requestList = append(requestList, vfc.RequestInfo{
- Name: req.name + ".rest",
- InitAmount: req.refBasketRest,
- InitValue: float64(cost.reqCost),
- })
- }
- requestMapping[uint32(code)] = rm
- }
-}
-
-type errCode int
-
-const (
- ErrMsgTooLarge = iota
- ErrDecode
- ErrInvalidMsgCode
- ErrProtocolVersionMismatch
- ErrNetworkIdMismatch
- ErrGenesisBlockMismatch
- ErrNoStatusMsg
- ErrExtraStatusMsg
- ErrSuspendedPeer
- ErrUselessPeer
- ErrRequestRejected
- ErrUnexpectedResponse
- ErrInvalidResponse
- ErrTooManyTimeouts
- ErrMissingKey
- ErrForkIDRejected
-)
-
-func (e errCode) String() string {
- return errorToString[int(e)]
-}
-
-// XXX change once legacy code is out
-var errorToString = map[int]string{
- ErrMsgTooLarge: "Message too long",
- ErrDecode: "Invalid message",
- ErrInvalidMsgCode: "Invalid message code",
- ErrProtocolVersionMismatch: "Protocol version mismatch",
- ErrNetworkIdMismatch: "NetworkId mismatch",
- ErrGenesisBlockMismatch: "Genesis block mismatch",
- ErrNoStatusMsg: "No status message",
- ErrExtraStatusMsg: "Extra status message",
- ErrSuspendedPeer: "Suspended peer",
- ErrRequestRejected: "Request rejected",
- ErrUnexpectedResponse: "Unexpected response",
- ErrInvalidResponse: "Invalid response",
- ErrTooManyTimeouts: "Too many request timeouts",
- ErrMissingKey: "Key missing from list",
- ErrForkIDRejected: "ForkID rejected",
-}
-
-// announceData is the network packet for the block announcements.
-type announceData struct {
- Hash common.Hash // Hash of one particular block being announced
- Number uint64 // Number of one particular block being announced
- Td *big.Int // Total difficulty of one particular block being announced
- ReorgDepth uint64
- Update keyValueList
-}
-
-// sanityCheck verifies that the values are reasonable, as a DoS protection
-func (a *announceData) sanityCheck() error {
- if tdlen := a.Td.BitLen(); tdlen > 100 {
- return fmt.Errorf("too large block TD: bitlen %d", tdlen)
- }
- return nil
-}
-
-// sign adds a signature to the block announcement by the given privKey
-func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
- rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
- sig, _ := crypto.Sign(crypto.Keccak256(rlp), privKey)
- a.Update = a.Update.add("sign", sig)
-}
-
-// checkSignature verifies if the block announcement has a valid signature by the given pubKey
-func (a *announceData) checkSignature(id enode.ID, update keyValueMap) error {
- var sig []byte
- if err := update.get("sign", &sig); err != nil {
- return err
- }
- rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
- recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
- if err != nil {
- return err
- }
- if id == enode.PubkeyToIDV4(recPubkey) {
- return nil
- }
- return errors.New("wrong signature")
-}
-
-type blockInfo struct {
- Hash common.Hash // Hash of one particular block being announced
- Number uint64 // Number of one particular block being announced
- Td *big.Int // Total difficulty of one particular block being announced
-}
-
-// hashOrNumber is a combined field for specifying an origin block.
-type hashOrNumber struct {
- Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
- Number uint64 // Block hash from which to retrieve headers (excludes Hash)
-}
-
-// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
-// two contained union fields.
-func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
- if hn.Hash == (common.Hash{}) {
- return rlp.Encode(w, hn.Number)
- }
- if hn.Number != 0 {
- return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
- }
- return rlp.Encode(w, hn.Hash)
-}
-
-// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
-// into either a block hash or a block number.
-func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
- _, size, err := s.Kind()
- switch {
- case err != nil:
- return err
- case size == 32:
- hn.Number = 0
- return s.Decode(&hn.Hash)
- case size <= 8:
- hn.Hash = common.Hash{}
- return s.Decode(&hn.Number)
- default:
- return fmt.Errorf("invalid input size %d for origin", size)
- }
-}
-
-// CodeData is the network response packet for a node data retrieval.
-type CodeData []struct {
- Value []byte
-}
diff --git a/les/request_test.go b/les/request_test.go
deleted file mode 100644
index 5e354b7efd55..000000000000
--- a/les/request_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-// Note: these tests are disabled now because they cannot work with the old sync
-// mechanism removed but will be useful again once the PoS ultralight mode is implemented
-
-/*
-import (
- "context"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
-)
-
-var testBankSecureTrieKey = secAddr(bankAddr)
-
-func secAddr(addr common.Address) []byte {
- return crypto.Keccak256(addr[:])
-}
-
-type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest
-
-func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) }
-func TestBlockAccessLes3(t *testing.T) { testAccess(t, 3, tfBlockAccess) }
-func TestBlockAccessLes4(t *testing.T) { testAccess(t, 4, tfBlockAccess) }
-
-func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest {
- return &light.BlockRequest{Hash: bhash, Number: number}
-}
-
-func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) }
-func TestReceiptsAccessLes3(t *testing.T) { testAccess(t, 3, tfReceiptsAccess) }
-func TestReceiptsAccessLes4(t *testing.T) { testAccess(t, 4, tfReceiptsAccess) }
-
-func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest {
- return &light.ReceiptsRequest{Hash: bhash, Number: number}
-}
-
-func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) }
-func TestTrieEntryAccessLes3(t *testing.T) { testAccess(t, 3, tfTrieEntryAccess) }
-func TestTrieEntryAccessLes4(t *testing.T) { testAccess(t, 4, tfTrieEntryAccess) }
-
-func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest {
- if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
- return &light.TrieRequest{Id: light.StateTrieID(rawdb.ReadHeader(db, bhash, *number)), Key: testBankSecureTrieKey}
- }
- return nil
-}
-
-func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) }
-func TestCodeAccessLes3(t *testing.T) { testAccess(t, 3, tfCodeAccess) }
-func TestCodeAccessLes4(t *testing.T) { testAccess(t, 4, tfCodeAccess) }
-
-func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrRequest {
- number := rawdb.ReadHeaderNumber(db, bhash)
- if number != nil {
- return nil
- }
- header := rawdb.ReadHeader(db, bhash, *number)
- if header.Number.Uint64() < testContractDeployed {
- return nil
- }
- sti := light.StateTrieID(header)
- ci := light.StorageTrieID(sti, testContractAddr, types.EmptyRootHash)
- return &light.CodeRequest{Id: ci, Hash: crypto.Keccak256Hash(testContractCodeDeployed)}
-}
-
-func testAccess(t *testing.T, protocol int, fn accessTestFn) {
- // Assemble the test environment
- netconfig := testnetConfig{
- blocks: 4,
- protocol: protocol,
- indexFn: nil,
- connect: true,
- nopruning: true,
- }
- server, client, tearDown := newClientServerEnv(t, netconfig)
- defer tearDown()
-
- // Ensure the client has synced all necessary data.
- clientHead := client.handler.backend.blockchain.CurrentHeader()
- if clientHead.Number.Uint64() != 4 {
- t.Fatalf("Failed to sync the chain with server, head: %v", clientHead.Number.Uint64())
- }
-
- test := func(expFail uint64) {
- for i := uint64(0); i <= server.handler.blockchain.CurrentHeader().Number.Uint64(); i++ {
- bhash := rawdb.ReadCanonicalHash(server.db, i)
- if req := fn(client.db, bhash, i); req != nil {
- ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
-
- err := client.handler.backend.odr.Retrieve(ctx, req)
- cancel()
-
- got := err == nil
- exp := i < expFail
- if exp && !got {
- t.Errorf("object retrieval failed")
- }
- if !exp && got {
- t.Errorf("unexpected object retrieval success")
- }
- }
- }
- }
- test(5)
-}
-*/
diff --git a/les/retrieve.go b/les/retrieve.go
deleted file mode 100644
index 728f960a547e..000000000000
--- a/les/retrieve.go
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "context"
- "errors"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/light"
-)
-
-var (
- retryQueue = time.Millisecond * 100
- hardRequestTimeout = time.Second * 10
-)
-
-// retrieveManager is a layer on top of requestDistributor which takes care of
-// matching replies by request ID and handles timeouts and resends if necessary.
-type retrieveManager struct {
- dist *requestDistributor
- peers *serverPeerSet
- softRequestTimeout func() time.Duration
-
- lock sync.RWMutex
- sentReqs map[uint64]*sentReq
-}
-
-// validatorFunc is a function that processes a reply message
-type validatorFunc func(distPeer, *Msg) error
-
-// sentReq represents a request sent and tracked by retrieveManager
-type sentReq struct {
- rm *retrieveManager
- req *distReq
- id uint64
- validate validatorFunc
-
- eventsCh chan reqPeerEvent
- stopCh chan struct{}
- stopped bool
- err error
-
- lock sync.RWMutex // protect access to sentTo map
- sentTo map[distPeer]sentReqToPeer
-
- lastReqQueued bool // last request has been queued but not sent
- lastReqSentTo distPeer // if not nil then last request has been sent to given peer but not timed out
- reqSrtoCount int // number of requests that reached soft (but not hard) timeout
-}
-
-// sentReqToPeer notifies the request-from-peer goroutine (tryRequest) about a response
-// delivered by the given peer. Only one delivery is allowed per request per peer,
-// after which delivered is set to true, the validity of the response is sent on the
-// valid channel and no more responses are accepted.
-type sentReqToPeer struct {
- delivered, frozen bool
- event chan int
-}
-
-// reqPeerEvent is sent by the request-from-peer goroutine (tryRequest) to the
-// request state machine (retrieveLoop) through the eventsCh channel.
-type reqPeerEvent struct {
- event int
- peer distPeer
-}
-
-const (
- rpSent = iota // if peer == nil, not sent (no suitable peers)
- rpSoftTimeout
- rpHardTimeout
- rpDeliveredValid
- rpDeliveredInvalid
- rpNotDelivered
-)
-
-// newRetrieveManager creates the retrieve manager
-func newRetrieveManager(peers *serverPeerSet, dist *requestDistributor, srto func() time.Duration) *retrieveManager {
- return &retrieveManager{
- peers: peers,
- dist: dist,
- sentReqs: make(map[uint64]*sentReq),
- softRequestTimeout: srto,
- }
-}
-
-// retrieve sends a request (to multiple peers if necessary) and waits for an answer
-// that is delivered through the deliver function and successfully validated by the
-// validator callback. It returns when a valid answer is delivered or the context is
-// cancelled.
-func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *distReq, val validatorFunc, shutdown chan struct{}) error {
- sentReq := rm.sendReq(reqID, req, val)
- select {
- case <-sentReq.stopCh:
- case <-ctx.Done():
- sentReq.stop(ctx.Err())
- case <-shutdown:
- sentReq.stop(errors.New("client is shutting down"))
- }
- return sentReq.getError()
-}
-
-// sendReq starts a process that keeps trying to retrieve a valid answer for a
-// request from any suitable peers until stopped or succeeded.
-func (rm *retrieveManager) sendReq(reqID uint64, req *distReq, val validatorFunc) *sentReq {
- r := &sentReq{
- rm: rm,
- req: req,
- id: reqID,
- sentTo: make(map[distPeer]sentReqToPeer),
- stopCh: make(chan struct{}),
- eventsCh: make(chan reqPeerEvent, 10),
- validate: val,
- }
-
- canSend := req.canSend
- req.canSend = func(p distPeer) bool {
- // add an extra check to canSend: the request has not been sent to the same peer before
- r.lock.RLock()
- _, sent := r.sentTo[p]
- r.lock.RUnlock()
- return !sent && canSend(p)
- }
-
- request := req.request
- req.request = func(p distPeer) func() {
- // before actually sending the request, put an entry into the sentTo map
- r.lock.Lock()
- r.sentTo[p] = sentReqToPeer{delivered: false, frozen: false, event: make(chan int, 1)}
- r.lock.Unlock()
- return request(p)
- }
- rm.lock.Lock()
- rm.sentReqs[reqID] = r
- rm.lock.Unlock()
-
- go r.retrieveLoop()
- return r
-}
-
-// deliver is called by the LES protocol manager to deliver reply messages to waiting requests
-func (rm *retrieveManager) deliver(peer distPeer, msg *Msg) error {
- rm.lock.RLock()
- req, ok := rm.sentReqs[msg.ReqID]
- rm.lock.RUnlock()
-
- if ok {
- return req.deliver(peer, msg)
- }
- return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID)
-}
-
-// frozen is called by the LES protocol manager when a server has suspended its service and we
-// should not expect an answer for the requests already sent there
-func (rm *retrieveManager) frozen(peer distPeer) {
- rm.lock.RLock()
- defer rm.lock.RUnlock()
-
- for _, req := range rm.sentReqs {
- req.frozen(peer)
- }
-}
-
-// reqStateFn represents a state of the retrieve loop state machine
-type reqStateFn func() reqStateFn
-
-// retrieveLoop is the retrieval state machine event loop
-func (r *sentReq) retrieveLoop() {
- go r.tryRequest()
- r.lastReqQueued = true
- state := r.stateRequesting
-
- for state != nil {
- state = state()
- }
-
- r.rm.lock.Lock()
- delete(r.rm.sentReqs, r.id)
- r.rm.lock.Unlock()
-}
-
-// stateRequesting: a request has been queued or sent recently; when it reaches soft timeout,
-// a new request is sent to a new peer
-func (r *sentReq) stateRequesting() reqStateFn {
- select {
- case ev := <-r.eventsCh:
- r.update(ev)
- switch ev.event {
- case rpSent:
- if ev.peer == nil {
- // request send failed, no more suitable peers
- if r.waiting() {
- // we are already waiting for sent requests which may succeed so keep waiting
- return r.stateNoMorePeers
- }
- // nothing to wait for, no more peers to ask, return with error
- r.stop(light.ErrNoPeers)
- // no need to go to stopped state because waiting() already returned false
- return nil
- }
- case rpSoftTimeout:
- // last request timed out, try asking a new peer
- go r.tryRequest()
- r.lastReqQueued = true
- return r.stateRequesting
- case rpDeliveredInvalid, rpNotDelivered:
- // if it was the last sent request (set to nil by update) then start a new one
- if !r.lastReqQueued && r.lastReqSentTo == nil {
- go r.tryRequest()
- r.lastReqQueued = true
- }
- return r.stateRequesting
- case rpDeliveredValid:
- r.stop(nil)
- return r.stateStopped
- }
- return r.stateRequesting
- case <-r.stopCh:
- return r.stateStopped
- }
-}
-
-// stateNoMorePeers: could not send more requests because no suitable peers are available.
-// Peers may become suitable for a certain request later or new peers may appear so we
-// keep trying.
-func (r *sentReq) stateNoMorePeers() reqStateFn {
- select {
- case <-time.After(retryQueue):
- go r.tryRequest()
- r.lastReqQueued = true
- return r.stateRequesting
- case ev := <-r.eventsCh:
- r.update(ev)
- if ev.event == rpDeliveredValid {
- r.stop(nil)
- return r.stateStopped
- }
- if r.waiting() {
- return r.stateNoMorePeers
- }
- r.stop(light.ErrNoPeers)
- return nil
- case <-r.stopCh:
- return r.stateStopped
- }
-}
-
-// stateStopped: request succeeded or cancelled, just waiting for some peers
-// to either answer or time out hard
-func (r *sentReq) stateStopped() reqStateFn {
- for r.waiting() {
- r.update(<-r.eventsCh)
- }
- return nil
-}
-
-// update updates the queued/sent flags and timed out peers counter according to the event
-func (r *sentReq) update(ev reqPeerEvent) {
- switch ev.event {
- case rpSent:
- r.lastReqQueued = false
- r.lastReqSentTo = ev.peer
- case rpSoftTimeout:
- r.lastReqSentTo = nil
- r.reqSrtoCount++
- case rpHardTimeout:
- r.reqSrtoCount--
- case rpDeliveredValid, rpDeliveredInvalid, rpNotDelivered:
- if ev.peer == r.lastReqSentTo {
- r.lastReqSentTo = nil
- } else {
- r.reqSrtoCount--
- }
- }
-}
-
-// waiting returns true if the retrieval mechanism is waiting for an answer from
-// any peer
-func (r *sentReq) waiting() bool {
- return r.lastReqQueued || r.lastReqSentTo != nil || r.reqSrtoCount > 0
-}
-
-// tryRequest tries to send the request to a new peer and waits for it to either
-// succeed or time out if it has been sent. It also sends the appropriate reqPeerEvent
-// messages to the request's event channel.
-func (r *sentReq) tryRequest() {
- sent := r.rm.dist.queue(r.req)
- var p distPeer
- select {
- case p = <-sent:
- case <-r.stopCh:
- if r.rm.dist.cancel(r.req) {
- p = nil
- } else {
- p = <-sent
- }
- }
-
- r.eventsCh <- reqPeerEvent{rpSent, p}
- if p == nil {
- return
- }
-
- hrto := false
-
- r.lock.RLock()
- s, ok := r.sentTo[p]
- r.lock.RUnlock()
- if !ok {
- panic(nil)
- }
-
- defer func() {
- pp, ok := p.(*serverPeer)
- if hrto && ok {
- pp.Log().Debug("Request timed out hard")
- if r.rm.peers != nil {
- r.rm.peers.unregister(pp.id)
- }
- }
- }()
-
- select {
- case event := <-s.event:
- if event == rpNotDelivered {
- r.lock.Lock()
- delete(r.sentTo, p)
- r.lock.Unlock()
- }
- r.eventsCh <- reqPeerEvent{event, p}
- return
- case <-time.After(r.rm.softRequestTimeout()):
- r.eventsCh <- reqPeerEvent{rpSoftTimeout, p}
- }
-
- select {
- case event := <-s.event:
- if event == rpNotDelivered {
- r.lock.Lock()
- delete(r.sentTo, p)
- r.lock.Unlock()
- }
- r.eventsCh <- reqPeerEvent{event, p}
- case <-time.After(hardRequestTimeout):
- hrto = true
- r.eventsCh <- reqPeerEvent{rpHardTimeout, p}
- }
-}
-
-// deliver a reply belonging to this request
-func (r *sentReq) deliver(peer distPeer, msg *Msg) error {
- r.lock.Lock()
- defer r.lock.Unlock()
-
- s, ok := r.sentTo[peer]
- if !ok || s.delivered {
- return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID)
- }
- if s.frozen {
- return nil
- }
- valid := r.validate(peer, msg) == nil
- r.sentTo[peer] = sentReqToPeer{delivered: true, frozen: false, event: s.event}
- if valid {
- s.event <- rpDeliveredValid
- } else {
- s.event <- rpDeliveredInvalid
- }
- if !valid {
- return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID)
- }
- return nil
-}
-
-// frozen sends a "not delivered" event to the peer event channel belonging to the
-// given peer if the request has been sent there, causing the state machine to not
-// expect an answer and potentially even send the request to the same peer again
-// when canSend allows it.
-func (r *sentReq) frozen(peer distPeer) {
- r.lock.Lock()
- defer r.lock.Unlock()
-
- s, ok := r.sentTo[peer]
- if ok && !s.delivered && !s.frozen {
- r.sentTo[peer] = sentReqToPeer{delivered: false, frozen: true, event: s.event}
- s.event <- rpNotDelivered
- }
-}
-
-// stop stops the retrieval process and sets an error code that will be returned
-// by getError
-func (r *sentReq) stop(err error) {
- r.lock.Lock()
- if !r.stopped {
- r.stopped = true
- r.err = err
- close(r.stopCh)
- }
- r.lock.Unlock()
-}
-
-// getError returns any retrieval error (either internally generated or set by the
-// stop function) after stopCh has been closed
-func (r *sentReq) getError() error {
- return r.err
-}
diff --git a/les/server.go b/les/server.go
deleted file mode 100644
index d84856c7fbf6..000000000000
--- a/les/server.go
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "crypto/ecdsa"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- vfs "github.com/ethereum/go-ethereum/les/vflux/server"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
-)
-
-var (
- defaultPosFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}
- defaultNegFactors = vfs.PriceFactors{TimeFactor: 0, CapacityFactor: 1, RequestFactor: 1}
-)
-
-const defaultConnectedBias = time.Minute * 3
-
-type ethBackend interface {
- ArchiveMode() bool
- BlockChain() *core.BlockChain
- BloomIndexer() *core.ChainIndexer
- ChainDb() ethdb.Database
- Synced() bool
- TxPool() *txpool.TxPool
-}
-
-type LesServer struct {
- lesCommons
-
- archiveMode bool // Flag whether the ethereum node runs in archive mode.
- handler *serverHandler
- peers *clientPeerSet
- serverset *serverSet
- vfluxServer *vfs.Server
- privateKey *ecdsa.PrivateKey
-
- // Flow control and capacity management
- fcManager *flowcontrol.ClientManager
- costTracker *costTracker
- defParams flowcontrol.ServerParams
- servingQueue *servingQueue
- clientPool *vfs.ClientPool
-
- minCapacity, maxCapacity uint64
- threadsIdle int // Request serving threads count when system is idle.
- threadsBusy int // Request serving threads count when system is busy(block insertion).
-
- p2pSrv *p2p.Server
-}
-
-func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*LesServer, error) {
- lesDb, err := node.OpenDatabase("les.server", 0, 0, "eth/db/lesserver/", false)
- if err != nil {
- return nil, err
- }
- // Calculate the number of threads used to service the light client
- // requests based on the user-specified value.
- threads := config.LightServ * 4 / 100
- if threads < 4 {
- threads = 4
- }
- srv := &LesServer{
- lesCommons: lesCommons{
- genesis: e.BlockChain().Genesis().Hash(),
- config: config,
- chainConfig: e.BlockChain().Config(),
- iConfig: light.DefaultServerIndexerConfig,
- chainDb: e.ChainDb(),
- lesDb: lesDb,
- chainReader: e.BlockChain(),
- chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations, true),
- bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency, true),
- closeCh: make(chan struct{}),
- },
- archiveMode: e.ArchiveMode(),
- peers: newClientPeerSet(),
- serverset: newServerSet(),
- vfluxServer: vfs.NewServer(time.Millisecond * 10),
- fcManager: flowcontrol.NewClientManager(nil, &mclock.System{}),
- servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100),
- threadsBusy: config.LightServ/100 + 1,
- threadsIdle: threads,
- p2pSrv: node.Server(),
- }
- issync := e.Synced
- if config.LightNoSyncServe {
- issync = func() bool { return true }
- }
- srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), issync)
- srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config)
-
- // Initialize the bloom trie indexer.
- e.BloomIndexer().AddChildIndexer(srv.bloomTrieIndexer)
-
- // Initialize server capacity management fields.
- srv.defParams = flowcontrol.ServerParams{
- BufLimit: srv.minCapacity * bufLimitRatio,
- MinRecharge: srv.minCapacity,
- }
- // LES flow control tries to more or less guarantee the possibility for the
- // clients to send a certain amount of requests at any time and get a quick
- // response. Most of the clients want this guarantee but don't actually need
- // to send requests most of the time. Our goal is to serve as many clients as
- // possible while the actually used server capacity does not exceed the limits
- totalRecharge := srv.costTracker.totalRecharge()
- srv.maxCapacity = srv.minCapacity * uint64(srv.config.LightPeers)
- if totalRecharge > srv.maxCapacity {
- srv.maxCapacity = totalRecharge
- }
- srv.fcManager.SetCapacityLimits(srv.minCapacity, srv.maxCapacity, srv.minCapacity*2)
- srv.clientPool = vfs.NewClientPool(lesDb, srv.minCapacity, defaultConnectedBias, mclock.System{}, issync)
- srv.clientPool.Start()
- srv.clientPool.SetDefaultFactors(defaultPosFactors, defaultNegFactors)
- srv.vfluxServer.Register(srv.clientPool, "les", "Ethereum light client service")
- srv.chtIndexer.Start(e.BlockChain())
-
- node.RegisterProtocols(srv.Protocols())
- node.RegisterAPIs(srv.APIs())
- node.RegisterLifecycle(srv)
- return srv, nil
-}
-
-func (s *LesServer) APIs() []rpc.API {
- return []rpc.API{
- {
- Namespace: "les",
- Service: NewLightServerAPI(s),
- },
- {
- Namespace: "debug",
- Service: NewDebugAPI(s),
- },
- }
-}
-
-func (s *LesServer) Protocols() []p2p.Protocol {
- ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
- if p := s.peers.peer(id); p != nil {
- return p.Info()
- }
- return nil
- }, nil)
- // Add "les" ENR entries.
- for i := range ps {
- ps[i].Attributes = []enr.Entry{&lesEntry{
- VfxVersion: 1,
- }}
- }
- return ps
-}
-
-// Start starts the LES server
-func (s *LesServer) Start() error {
- s.privateKey = s.p2pSrv.PrivateKey
- s.peers.setSignerKey(s.privateKey)
- s.handler.start()
- s.wg.Add(1)
- go s.capacityManagement()
- if s.p2pSrv.DiscV5 != nil {
- s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
- }
- return nil
-}
-
-// Stop stops the LES service
-func (s *LesServer) Stop() error {
- close(s.closeCh)
-
- s.clientPool.Stop()
- if s.serverset != nil {
- s.serverset.close()
- }
- s.peers.close()
- s.fcManager.Stop()
- s.costTracker.stop()
- s.handler.stop()
- s.servingQueue.stop()
- if s.vfluxServer != nil {
- s.vfluxServer.Stop()
- }
-
- // Note, bloom trie indexer is closed by parent bloombits indexer.
- if s.chtIndexer != nil {
- s.chtIndexer.Close()
- }
- if s.lesDb != nil {
- s.lesDb.Close()
- }
- s.wg.Wait()
- log.Info("Les server stopped")
-
- return nil
-}
-
-// capacityManagement starts an event handler loop that updates the recharge curve of
-// the client manager and adjusts the client pool's size according to the total
-// capacity updates coming from the client manager
-func (s *LesServer) capacityManagement() {
- defer s.wg.Done()
-
- processCh := make(chan bool, 100)
- sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh)
- defer sub.Unsubscribe()
-
- totalRechargeCh := make(chan uint64, 100)
- totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh)
-
- totalCapacityCh := make(chan uint64, 100)
- totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh)
- s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
-
- var (
- busy bool
- freePeers uint64
- blockProcess mclock.AbsTime
- )
- updateRecharge := func() {
- if busy {
- s.servingQueue.setThreads(s.threadsBusy)
- s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}})
- } else {
- s.servingQueue.setThreads(s.threadsIdle)
- s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}})
- }
- }
- updateRecharge()
-
- for {
- select {
- case busy = <-processCh:
- if busy {
- blockProcess = mclock.Now()
- } else {
- blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess))
- }
- updateRecharge()
- case totalRecharge = <-totalRechargeCh:
- totalRechargeGauge.Update(int64(totalRecharge))
- updateRecharge()
- case totalCapacity = <-totalCapacityCh:
- totalCapacityGauge.Update(int64(totalCapacity))
- newFreePeers := totalCapacity / s.minCapacity
- if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) {
- log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers)
- }
- freePeers = newFreePeers
- s.clientPool.SetLimits(uint64(s.config.LightPeers), totalCapacity)
- case <-s.closeCh:
- return
- }
- }
-}
diff --git a/les/server_handler.go b/les/server_handler.go
deleted file mode 100644
index 5b3505064b31..000000000000
--- a/les/server_handler.go
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-const (
- softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
- estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
-
- MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
- MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
- MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
- MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
- MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
- MaxHelperTrieProofsFetch = 64 // Amount of helper tries to be fetched per retrieval request
- MaxTxSend = 64 // Amount of transactions to be send per request
- MaxTxStatus = 256 // Amount of transactions to queried per request
-)
-
-var (
- errTooManyInvalidRequest = errors.New("too many invalid requests made")
-)
-
-// serverHandler is responsible for serving light client and process
-// all incoming light requests.
-type serverHandler struct {
- forkFilter forkid.Filter
- blockchain *core.BlockChain
- chainDb ethdb.Database
- txpool *txpool.TxPool
- server *LesServer
-
- closeCh chan struct{} // Channel used to exit all background routines of handler.
- wg sync.WaitGroup // WaitGroup used to track all background routines of handler.
- synced func() bool // Callback function used to determine whether local node is synced.
-
- // Testing fields
- addTxsSync bool
-}
-
-func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *txpool.TxPool, synced func() bool) *serverHandler {
- handler := &serverHandler{
- forkFilter: forkid.NewFilter(blockchain),
- server: server,
- blockchain: blockchain,
- chainDb: chainDb,
- txpool: txpool,
- closeCh: make(chan struct{}),
- synced: synced,
- }
- return handler
-}
-
-// start starts the server handler.
-func (h *serverHandler) start() {
- h.wg.Add(1)
- go h.broadcastLoop()
-}
-
-// stop stops the server handler.
-func (h *serverHandler) stop() {
- close(h.closeCh)
- h.wg.Wait()
-}
-
-// runPeer is the p2p protocol run function for the given version.
-func (h *serverHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error {
- peer := newClientPeer(int(version), h.server.config.NetworkId, p, newMeteredMsgWriter(rw, int(version)))
- defer peer.close()
- h.wg.Add(1)
- defer h.wg.Done()
- return h.handle(peer)
-}
-
-func (h *serverHandler) handle(p *clientPeer) error {
- p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
-
- // Execute the LES handshake
- var (
- head = h.blockchain.CurrentHeader()
- hash = head.Hash()
- number = head.Number.Uint64()
- td = h.blockchain.GetTd(hash, number)
- forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis(), number, head.Time)
- )
- if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
- p.Log().Debug("Light Ethereum handshake failed", "err", err)
- return err
- }
- // Connected to another server, no messages expected, just wait for disconnection
- if p.server {
- if err := h.server.serverset.register(p); err != nil {
- return err
- }
- _, err := p.rw.ReadMsg()
- h.server.serverset.unregister(p)
- return err
- }
- // Setup flow control mechanism for the peer
- p.fcClient = flowcontrol.NewClientNode(h.server.fcManager, p.fcParams)
- defer p.fcClient.Disconnect()
-
- // Reject light clients if server is not synced. Put this checking here, so
- // that "non-synced" les-server peers are still allowed to keep the connection.
- if !h.synced() {
- p.Log().Debug("Light server not synced, rejecting peer")
- return p2p.DiscRequested
- }
-
- // Register the peer into the peerset and clientpool
- if err := h.server.peers.register(p); err != nil {
- return err
- }
- if p.balance = h.server.clientPool.Register(p); p.balance == nil {
- h.server.peers.unregister(p.ID())
- p.Log().Debug("Client pool already closed")
- return p2p.DiscRequested
- }
- p.connectedAt = mclock.Now()
-
- var wg sync.WaitGroup // Wait group used to track all in-flight task routines.
- defer func() {
- wg.Wait() // Ensure all background task routines have exited.
- h.server.clientPool.Unregister(p)
- h.server.peers.unregister(p.ID())
- p.balance = nil
- connectionTimer.Update(time.Duration(mclock.Now() - p.connectedAt))
- }()
-
- // Mark the peer as being served.
- p.serving.Store(true)
- defer p.serving.Store(false)
-
- // Spawn a main loop to handle all incoming messages.
- for {
- select {
- case err := <-p.errCh:
- p.Log().Debug("Failed to send light ethereum response", "err", err)
- return err
- default:
- }
- if err := h.handleMsg(p, &wg); err != nil {
- p.Log().Debug("Light Ethereum message handling failed", "err", err)
- return err
- }
- }
-}
-
-// beforeHandle will do a series of prechecks before handling message.
-func (h *serverHandler) beforeHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, reqCnt uint64, maxCount uint64) (*servingTask, uint64) {
- // Ensure that the request sent by client peer is valid
- inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0)
- if reqCnt == 0 || reqCnt > maxCount {
- p.fcClient.OneTimeCost(inSizeCost)
- return nil, 0
- }
- // Ensure that the client peer complies with the flow control
- // rules agreed by both sides.
- if p.isFrozen() {
- p.fcClient.OneTimeCost(inSizeCost)
- return nil, 0
- }
- maxCost := p.fcCosts.getMaxCost(msg.Code, reqCnt)
- accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost)
- if !accepted {
- p.freeze()
- p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge)))
- p.fcClient.OneTimeCost(inSizeCost)
- return nil, 0
- }
- // Create a multi-stage task, estimate the time it takes for the task to
- // execute, and cache it in the request service queue.
- factor := h.server.costTracker.globalFactor()
- if factor < 0.001 {
- factor = 1
- p.Log().Error("Invalid global cost factor", "factor", factor)
- }
- maxTime := uint64(float64(maxCost) / factor)
- task := h.server.servingQueue.newTask(p, maxTime, priority)
- if !task.start() {
- p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost)
- return nil, 0
- }
- return task, maxCost
-}
-
-// Afterhandle will perform a series of operations after message handling,
-// such as updating flow control data, sending reply, etc.
-func (h *serverHandler) afterHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, maxCost uint64, reqCnt uint64, task *servingTask, reply *reply) {
- if reply != nil {
- task.done()
- }
- p.responseLock.Lock()
- defer p.responseLock.Unlock()
-
- // Short circuit if the client is already frozen.
- if p.isFrozen() {
- realCost := h.server.costTracker.realCost(task.servingTime, msg.Size, 0)
- p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
- return
- }
- // Positive correction buffer value with real cost.
- var replySize uint32
- if reply != nil {
- replySize = reply.size()
- }
- var realCost uint64
- if h.server.costTracker.testing {
- realCost = maxCost // Assign a fake cost for testing purpose
- } else {
- realCost = h.server.costTracker.realCost(task.servingTime, msg.Size, replySize)
- if realCost > maxCost {
- realCost = maxCost
- }
- }
- bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
- if reply != nil {
- // Feed cost tracker request serving statistic.
- h.server.costTracker.updateStats(msg.Code, reqCnt, task.servingTime, realCost)
- // Reduce priority "balance" for the specific peer.
- p.balance.RequestServed(realCost)
- p.queueSend(func() {
- if err := reply.send(bv); err != nil {
- select {
- case p.errCh <- err:
- default:
- }
- }
- })
- }
-}
-
-// handleMsg is invoked whenever an inbound message is received from a remote
-// peer. The remote connection is torn down upon returning any error.
-func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
- // Read the next message from the remote peer, and ensure it's fully consumed
- msg, err := p.rw.ReadMsg()
- if err != nil {
- return err
- }
- p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size)
-
- // Discard large message which exceeds the limitation.
- if msg.Size > ProtocolMaxMsgSize {
- clientErrorMeter.Mark(1)
- return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
- }
- defer msg.Discard()
-
- // Lookup the request handler table, ensure it's supported
- // message type by the protocol.
- req, ok := Les3[msg.Code]
- if !ok {
- p.Log().Trace("Received invalid message", "code", msg.Code)
- clientErrorMeter.Mark(1)
- return errResp(ErrInvalidMsgCode, "%v", msg.Code)
- }
- p.Log().Trace("Received " + req.Name)
-
- // Decode the p2p message, resolve the concrete handler for it.
- serve, reqID, reqCnt, err := req.Handle(msg)
- if err != nil {
- clientErrorMeter.Mark(1)
- return errResp(ErrDecode, "%v: %v", msg, err)
- }
- if metrics.EnabledExpensive {
- req.InPacketsMeter.Mark(1)
- req.InTrafficMeter.Mark(int64(msg.Size))
- }
- p.responseCount++
- responseCount := p.responseCount
-
- // First check this client message complies all rules before
- // handling it and return a processor if all checks are passed.
- task, maxCost := h.beforeHandle(p, reqID, responseCount, msg, reqCnt, req.MaxCount)
- if task == nil {
- return nil
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
-
- reply := serve(h, p, task.waitOrStop)
- h.afterHandle(p, reqID, responseCount, msg, maxCost, reqCnt, task, reply)
-
- if metrics.EnabledExpensive {
- size := uint32(0)
- if reply != nil {
- size = reply.size()
- }
- req.OutPacketsMeter.Mark(1)
- req.OutTrafficMeter.Mark(int64(size))
- req.ServingTimeMeter.Update(time.Duration(task.servingTime))
- }
- }()
- // If the client has made too much invalid request(e.g. request a non-existent data),
- // reject them to prevent SPAM attack.
- if p.getInvalid() > maxRequestErrors {
- clientErrorMeter.Mark(1)
- return errTooManyInvalidRequest
- }
- return nil
-}
-
-// BlockChain implements serverBackend
-func (h *serverHandler) BlockChain() *core.BlockChain {
- return h.blockchain
-}
-
-// TxPool implements serverBackend
-func (h *serverHandler) TxPool() *txpool.TxPool {
- return h.txpool
-}
-
-// ArchiveMode implements serverBackend
-func (h *serverHandler) ArchiveMode() bool {
- return h.server.archiveMode
-}
-
-// AddTxsSync implements serverBackend
-func (h *serverHandler) AddTxsSync() bool {
- return h.addTxsSync
-}
-
-// getAccount retrieves an account from the state based on root.
-func getAccount(triedb *trie.Database, root common.Hash, addr common.Address) (types.StateAccount, error) {
- trie, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
- if err != nil {
- return types.StateAccount{}, err
- }
- acc, err := trie.GetAccount(addr)
- if err != nil {
- return types.StateAccount{}, err
- }
- if acc == nil {
- return types.StateAccount{}, fmt.Errorf("account %#x is not present", addr)
- }
- return *acc, nil
-}
-
-// GetHelperTrie returns the post-processed trie root for the given trie ID and section index
-func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie {
- var (
- root common.Hash
- prefix string
- )
- switch typ {
- case htCanonical:
- sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1)
- root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), string(rawdb.ChtTablePrefix)
- case htBloomBits:
- sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1)
- root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), string(rawdb.BloomTrieTablePrefix)
- }
- if root == (common.Hash{}) {
- return nil
- }
- triedb := trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix), trie.HashDefaults)
- trie, _ := trie.New(trie.TrieID(root), triedb)
- return trie
-}
-
-// broadcastLoop broadcasts new block information to all connected light
-// clients. According to the agreement between client and server, server should
-// only broadcast new announcement if the total difficulty is higher than the
-// last one. Besides server will add the signature if client requires.
-func (h *serverHandler) broadcastLoop() {
- defer h.wg.Done()
-
- headCh := make(chan core.ChainHeadEvent, 10)
- headSub := h.blockchain.SubscribeChainHeadEvent(headCh)
- defer headSub.Unsubscribe()
-
- var (
- lastHead = h.blockchain.CurrentHeader()
- lastTd = common.Big0
- )
- for {
- select {
- case ev := <-headCh:
- header := ev.Block.Header()
- hash, number := header.Hash(), header.Number.Uint64()
- td := h.blockchain.GetTd(hash, number)
- if td == nil || td.Cmp(lastTd) <= 0 {
- continue
- }
- var reorg uint64
- if lastHead != nil {
- // If a setHead has been performed, the common ancestor can be nil.
- if ancestor := rawdb.FindCommonAncestor(h.chainDb, header, lastHead); ancestor != nil {
- reorg = lastHead.Number.Uint64() - ancestor.Number.Uint64()
- }
- }
- lastHead, lastTd = header, td
- log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
- h.server.peers.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg})
- case <-h.closeCh:
- return
- }
- }
-}
diff --git a/les/server_requests.go b/les/server_requests.go
deleted file mode 100644
index cc5b60171391..000000000000
--- a/les/server_requests.go
+++ /dev/null
@@ -1,566 +0,0 @@
-// Copyright 2021 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 .
-
-package les
-
-import (
- "encoding/binary"
- "encoding/json"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-// serverBackend defines the backend functions needed for serving LES requests
-type serverBackend interface {
- ArchiveMode() bool
- AddTxsSync() bool
- BlockChain() *core.BlockChain
- TxPool() *txpool.TxPool
- GetHelperTrie(typ uint, index uint64) *trie.Trie
-}
-
-// Decoder is implemented by the messages passed to the handler functions
-type Decoder interface {
- Decode(val interface{}) error
-}
-
-// RequestType is a static struct that describes an LES request type and references
-// its handler function.
-type RequestType struct {
- Name string
- MaxCount uint64
- InPacketsMeter, InTrafficMeter, OutPacketsMeter, OutTrafficMeter metrics.Meter
- ServingTimeMeter metrics.Timer
- Handle func(msg Decoder) (serve serveRequestFn, reqID, amount uint64, err error)
-}
-
-// serveRequestFn is returned by the request handler functions after decoding the request.
-// This function does the actual request serving using the supplied backend. waitOrStop is
-// called between serving individual request items and may block if the serving process
-// needs to be throttled. If it returns false then the process is terminated.
-// The reply is not sent by this function yet. The flow control feedback value is supplied
-// by the protocol handler when calling the send function of the returned reply struct.
-type serveRequestFn func(backend serverBackend, peer *clientPeer, waitOrStop func() bool) *reply
-
-// Les3 contains the request types supported by les/2 and les/3
-var Les3 = map[uint64]RequestType{
- GetBlockHeadersMsg: {
- Name: "block header request",
- MaxCount: MaxHeaderFetch,
- InPacketsMeter: miscInHeaderPacketsMeter,
- InTrafficMeter: miscInHeaderTrafficMeter,
- OutPacketsMeter: miscOutHeaderPacketsMeter,
- OutTrafficMeter: miscOutHeaderTrafficMeter,
- ServingTimeMeter: miscServingTimeHeaderTimer,
- Handle: handleGetBlockHeaders,
- },
- GetBlockBodiesMsg: {
- Name: "block bodies request",
- MaxCount: MaxBodyFetch,
- InPacketsMeter: miscInBodyPacketsMeter,
- InTrafficMeter: miscInBodyTrafficMeter,
- OutPacketsMeter: miscOutBodyPacketsMeter,
- OutTrafficMeter: miscOutBodyTrafficMeter,
- ServingTimeMeter: miscServingTimeBodyTimer,
- Handle: handleGetBlockBodies,
- },
- GetCodeMsg: {
- Name: "code request",
- MaxCount: MaxCodeFetch,
- InPacketsMeter: miscInCodePacketsMeter,
- InTrafficMeter: miscInCodeTrafficMeter,
- OutPacketsMeter: miscOutCodePacketsMeter,
- OutTrafficMeter: miscOutCodeTrafficMeter,
- ServingTimeMeter: miscServingTimeCodeTimer,
- Handle: handleGetCode,
- },
- GetReceiptsMsg: {
- Name: "receipts request",
- MaxCount: MaxReceiptFetch,
- InPacketsMeter: miscInReceiptPacketsMeter,
- InTrafficMeter: miscInReceiptTrafficMeter,
- OutPacketsMeter: miscOutReceiptPacketsMeter,
- OutTrafficMeter: miscOutReceiptTrafficMeter,
- ServingTimeMeter: miscServingTimeReceiptTimer,
- Handle: handleGetReceipts,
- },
- GetProofsV2Msg: {
- Name: "les/2 proofs request",
- MaxCount: MaxProofsFetch,
- InPacketsMeter: miscInTrieProofPacketsMeter,
- InTrafficMeter: miscInTrieProofTrafficMeter,
- OutPacketsMeter: miscOutTrieProofPacketsMeter,
- OutTrafficMeter: miscOutTrieProofTrafficMeter,
- ServingTimeMeter: miscServingTimeTrieProofTimer,
- Handle: handleGetProofs,
- },
- GetHelperTrieProofsMsg: {
- Name: "helper trie proof request",
- MaxCount: MaxHelperTrieProofsFetch,
- InPacketsMeter: miscInHelperTriePacketsMeter,
- InTrafficMeter: miscInHelperTrieTrafficMeter,
- OutPacketsMeter: miscOutHelperTriePacketsMeter,
- OutTrafficMeter: miscOutHelperTrieTrafficMeter,
- ServingTimeMeter: miscServingTimeHelperTrieTimer,
- Handle: handleGetHelperTrieProofs,
- },
- SendTxV2Msg: {
- Name: "new transactions",
- MaxCount: MaxTxSend,
- InPacketsMeter: miscInTxsPacketsMeter,
- InTrafficMeter: miscInTxsTrafficMeter,
- OutPacketsMeter: miscOutTxsPacketsMeter,
- OutTrafficMeter: miscOutTxsTrafficMeter,
- ServingTimeMeter: miscServingTimeTxTimer,
- Handle: handleSendTx,
- },
- GetTxStatusMsg: {
- Name: "transaction status query request",
- MaxCount: MaxTxStatus,
- InPacketsMeter: miscInTxStatusPacketsMeter,
- InTrafficMeter: miscInTxStatusTrafficMeter,
- OutPacketsMeter: miscOutTxStatusPacketsMeter,
- OutTrafficMeter: miscOutTxStatusTrafficMeter,
- ServingTimeMeter: miscServingTimeTxStatusTimer,
- Handle: handleGetTxStatus,
- },
-}
-
-// handleGetBlockHeaders handles a block header request
-func handleGetBlockHeaders(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetBlockHeadersPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- // Gather headers until the fetch or network limits is reached
- var (
- bc = backend.BlockChain()
- hashMode = r.Query.Origin.Hash != (common.Hash{})
- first = true
- maxNonCanonical = uint64(100)
- bytes common.StorageSize
- headers []*types.Header
- unknown bool
- )
- for !unknown && len(headers) < int(r.Query.Amount) && bytes < softResponseLimit {
- if !first && !waitOrStop() {
- return nil
- }
- // Retrieve the next header satisfying the r
- var origin *types.Header
- if hashMode {
- if first {
- origin = bc.GetHeaderByHash(r.Query.Origin.Hash)
- if origin != nil {
- r.Query.Origin.Number = origin.Number.Uint64()
- }
- } else {
- origin = bc.GetHeader(r.Query.Origin.Hash, r.Query.Origin.Number)
- }
- } else {
- origin = bc.GetHeaderByNumber(r.Query.Origin.Number)
- }
- if origin == nil {
- break
- }
- headers = append(headers, origin)
- bytes += estHeaderRlpSize
-
- // Advance to the next header of the r
- switch {
- case hashMode && r.Query.Reverse:
- // Hash based traversal towards the genesis block
- ancestor := r.Query.Skip + 1
- if ancestor == 0 {
- unknown = true
- } else {
- r.Query.Origin.Hash, r.Query.Origin.Number = bc.GetAncestor(r.Query.Origin.Hash, r.Query.Origin.Number, ancestor, &maxNonCanonical)
- unknown = r.Query.Origin.Hash == common.Hash{}
- }
- case hashMode && !r.Query.Reverse:
- // Hash based traversal towards the leaf block
- var (
- current = origin.Number.Uint64()
- next = current + r.Query.Skip + 1
- )
- if next <= current {
- infos, _ := json.Marshal(p.Peer.Info())
- p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", r.Query.Skip, "next", next, "attacker", string(infos))
- unknown = true
- } else {
- if header := bc.GetHeaderByNumber(next); header != nil {
- nextHash := header.Hash()
- expOldHash, _ := bc.GetAncestor(nextHash, next, r.Query.Skip+1, &maxNonCanonical)
- if expOldHash == r.Query.Origin.Hash {
- r.Query.Origin.Hash, r.Query.Origin.Number = nextHash, next
- } else {
- unknown = true
- }
- } else {
- unknown = true
- }
- }
- case r.Query.Reverse:
- // Number based traversal towards the genesis block
- if r.Query.Origin.Number >= r.Query.Skip+1 {
- r.Query.Origin.Number -= r.Query.Skip + 1
- } else {
- unknown = true
- }
-
- case !r.Query.Reverse:
- // Number based traversal towards the leaf block
- r.Query.Origin.Number += r.Query.Skip + 1
- }
- first = false
- }
- return p.replyBlockHeaders(r.ReqID, headers)
- }, r.ReqID, r.Query.Amount, nil
-}
-
-// handleGetBlockBodies handles a block body request
-func handleGetBlockBodies(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetBlockBodiesPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- var (
- bytes int
- bodies []rlp.RawValue
- )
- bc := backend.BlockChain()
- for i, hash := range r.Hashes {
- if i != 0 && !waitOrStop() {
- return nil
- }
- if bytes >= softResponseLimit {
- break
- }
- body := bc.GetBodyRLP(hash)
- if body == nil {
- p.bumpInvalid()
- continue
- }
- bodies = append(bodies, body)
- bytes += len(body)
- }
- return p.replyBlockBodiesRLP(r.ReqID, bodies)
- }, r.ReqID, uint64(len(r.Hashes)), nil
-}
-
-// handleGetCode handles a contract code request
-func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetCodePacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- var (
- bytes int
- data [][]byte
- )
- bc := backend.BlockChain()
- for i, request := range r.Reqs {
- if i != 0 && !waitOrStop() {
- return nil
- }
- // Look up the root hash belonging to the request
- header := bc.GetHeaderByHash(request.BHash)
- if header == nil {
- p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash)
- p.bumpInvalid()
- continue
- }
- // Refuse to search stale state data in the database since looking for
- // a non-exist key is kind of expensive.
- local := bc.CurrentHeader().Number.Uint64()
- if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
- p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
- p.bumpInvalid()
- continue
- }
- address := common.BytesToAddress(request.AccountAddress)
- account, err := getAccount(bc.TrieDB(), header.Root, address)
- if err != nil {
- p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", address, "err", err)
- p.bumpInvalid()
- continue
- }
- code, err := bc.StateCache().ContractCode(address, common.BytesToHash(account.CodeHash))
- if err != nil {
- p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", address, "codehash", common.BytesToHash(account.CodeHash), "err", err)
- continue
- }
- // Accumulate the code and abort if enough data was retrieved
- data = append(data, code)
- if bytes += len(code); bytes >= softResponseLimit {
- break
- }
- }
- return p.replyCode(r.ReqID, data)
- }, r.ReqID, uint64(len(r.Reqs)), nil
-}
-
-// handleGetReceipts handles a block receipts request
-func handleGetReceipts(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetReceiptsPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- var (
- bytes int
- receipts []rlp.RawValue
- )
- bc := backend.BlockChain()
- for i, hash := range r.Hashes {
- if i != 0 && !waitOrStop() {
- return nil
- }
- if bytes >= softResponseLimit {
- break
- }
- // Retrieve the requested block's receipts, skipping if unknown to us
- results := bc.GetReceiptsByHash(hash)
- if results == nil {
- if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyReceiptsHash {
- p.bumpInvalid()
- continue
- }
- }
- // If known, encode and queue for response packet
- if encoded, err := rlp.EncodeToBytes(results); err != nil {
- log.Error("Failed to encode receipt", "err", err)
- } else {
- receipts = append(receipts, encoded)
- bytes += len(encoded)
- }
- }
- return p.replyReceiptsRLP(r.ReqID, receipts)
- }, r.ReqID, uint64(len(r.Hashes)), nil
-}
-
-// handleGetProofs handles a proof request
-func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetProofsPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- var (
- lastBHash common.Hash
- root common.Hash
- header *types.Header
- err error
- )
- bc := backend.BlockChain()
- nodes := trienode.NewProofSet()
-
- for i, request := range r.Reqs {
- if i != 0 && !waitOrStop() {
- return nil
- }
- // Look up the root hash belonging to the request
- if request.BHash != lastBHash {
- root, lastBHash = common.Hash{}, request.BHash
-
- if header = bc.GetHeaderByHash(request.BHash); header == nil {
- p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash)
- p.bumpInvalid()
- continue
- }
- // Refuse to search stale state data in the database since looking for
- // a non-exist key is kind of expensive.
- local := bc.CurrentHeader().Number.Uint64()
- if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
- p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
- p.bumpInvalid()
- continue
- }
- root = header.Root
- }
- // If a header lookup failed (non existent), ignore subsequent requests for the same header
- if root == (common.Hash{}) {
- p.bumpInvalid()
- continue
- }
- // Open the account or storage trie for the request
- statedb := bc.StateCache()
-
- var trie state.Trie
- switch len(request.AccountAddress) {
- case 0:
- // No account key specified, open an account trie
- trie, err = statedb.OpenTrie(root)
- if trie == nil || err != nil {
- p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err)
- continue
- }
- default:
- // Account key specified, open a storage trie
- address := common.BytesToAddress(request.AccountAddress)
- account, err := getAccount(bc.TrieDB(), root, address)
- if err != nil {
- p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", address, "err", err)
- p.bumpInvalid()
- continue
- }
- trie, err = statedb.OpenStorageTrie(root, address, account.Root, nil)
- if trie == nil || err != nil {
- p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", address, "root", account.Root, "err", err)
- continue
- }
- }
- // Prove the user's request from the account or storage trie
- if err := trie.Prove(request.Key, nodes); err != nil {
- p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err)
- continue
- }
- if nodes.DataSize() >= softResponseLimit {
- break
- }
- }
- return p.replyProofsV2(r.ReqID, nodes.List())
- }, r.ReqID, uint64(len(r.Reqs)), nil
-}
-
-// handleGetHelperTrieProofs handles a helper trie proof request
-func handleGetHelperTrieProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetHelperTrieProofsPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- var (
- lastIdx uint64
- lastType uint
- auxTrie *trie.Trie
- auxBytes int
- auxData [][]byte
- )
- bc := backend.BlockChain()
- nodes := trienode.NewProofSet()
- for i, request := range r.Reqs {
- if i != 0 && !waitOrStop() {
- return nil
- }
- if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx {
- lastType, lastIdx = request.Type, request.TrieIdx
- auxTrie = backend.GetHelperTrie(request.Type, request.TrieIdx)
- }
- if auxTrie == nil {
- return nil
- }
- // TODO(rjl493456442) short circuit if the proving is failed.
- // The original client side code has a dirty hack to retrieve
- // the headers with no valid proof. Keep the compatibility for
- // legacy les protocol and drop this hack when the les2/3 are
- // not supported.
- err := auxTrie.Prove(request.Key, nodes)
- if p.version >= lpv4 && err != nil {
- return nil
- }
- if request.Type == htCanonical && request.AuxReq == htAuxHeader && len(request.Key) == 8 {
- header := bc.GetHeaderByNumber(binary.BigEndian.Uint64(request.Key))
- data, err := rlp.EncodeToBytes(header)
- if err != nil {
- log.Error("Failed to encode header", "err", err)
- return nil
- }
- auxData = append(auxData, data)
- auxBytes += len(data)
- }
- if nodes.DataSize()+auxBytes >= softResponseLimit {
- break
- }
- }
- return p.replyHelperTrieProofs(r.ReqID, HelperTrieResps{Proofs: nodes.List(), AuxData: auxData})
- }, r.ReqID, uint64(len(r.Reqs)), nil
-}
-
-// handleSendTx handles a transaction propagation request
-func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r SendTxPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- amount := uint64(len(r.Txs))
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- stats := make([]light.TxStatus, len(r.Txs))
- for i, tx := range r.Txs {
- if i != 0 && !waitOrStop() {
- return nil
- }
- hash := tx.Hash()
- stats[i] = txStatus(backend, hash)
- if stats[i].Status == txpool.TxStatusUnknown {
- if errs := backend.TxPool().Add([]*types.Transaction{tx}, false, backend.AddTxsSync()); errs[0] != nil {
- stats[i].Error = errs[0].Error()
- continue
- }
- stats[i] = txStatus(backend, hash)
- }
- }
- return p.replyTxStatus(r.ReqID, stats)
- }, r.ReqID, amount, nil
-}
-
-// handleGetTxStatus handles a transaction status query
-func handleGetTxStatus(msg Decoder) (serveRequestFn, uint64, uint64, error) {
- var r GetTxStatusPacket
- if err := msg.Decode(&r); err != nil {
- return nil, 0, 0, err
- }
- return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
- stats := make([]light.TxStatus, len(r.Hashes))
- for i, hash := range r.Hashes {
- if i != 0 && !waitOrStop() {
- return nil
- }
- stats[i] = txStatus(backend, hash)
- }
- return p.replyTxStatus(r.ReqID, stats)
- }, r.ReqID, uint64(len(r.Hashes)), nil
-}
-
-// txStatus returns the status of a specified transaction.
-func txStatus(b serverBackend, hash common.Hash) light.TxStatus {
- var stat light.TxStatus
- // Looking the transaction in txpool first.
- stat.Status = b.TxPool().Status(hash)
-
- // If the transaction is unknown to the pool, try looking it up locally.
- if stat.Status == txpool.TxStatusUnknown {
- lookup := b.BlockChain().GetTransactionLookup(hash)
- if lookup != nil {
- stat.Status = txpool.TxStatusIncluded
- stat.Lookup = lookup
- }
- }
- return stat
-}
diff --git a/les/servingqueue.go b/les/servingqueue.go
deleted file mode 100644
index b258fc3cafdc..000000000000
--- a/les/servingqueue.go
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "sync"
- "sync/atomic"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/common/prque"
- "golang.org/x/exp/slices"
-)
-
-// servingQueue allows running tasks in a limited number of threads and puts the
-// waiting tasks in a priority queue
-type servingQueue struct {
- recentTime, queuedTime uint64
- servingTimeDiff atomic.Uint64
- burstLimit, burstDropLimit uint64
- burstDecRate float64
- lastUpdate mclock.AbsTime
-
- queueAddCh, queueBestCh chan *servingTask
- stopThreadCh, quit chan struct{}
- setThreadsCh chan int
-
- wg sync.WaitGroup
- threadCount int // number of currently running threads
- queue *prque.Prque[int64, *servingTask] // priority queue for waiting or suspended tasks
- best *servingTask // the highest priority task (not included in the queue)
- suspendBias int64 // priority bias against suspending an already running task
-}
-
-// servingTask represents a request serving task. Tasks can be implemented to
-// run in multiple steps, allowing the serving queue to suspend execution between
-// steps if higher priority tasks are entered. The creator of the task should
-// set the following fields:
-//
-// - priority: greater value means higher priority; values can wrap around the int64 range
-// - run: execute a single step; return true if finished
-// - after: executed after run finishes or returns an error, receives the total serving time
-type servingTask struct {
- sq *servingQueue
- servingTime, timeAdded, maxTime, expTime uint64
- peer *clientPeer
- priority int64
- biasAdded bool
- token runToken
- tokenCh chan runToken
-}
-
-// runToken received by servingTask.start allows the task to run. Closing the
-// channel by servingTask.stop signals the thread controller to allow a new task
-// to start running.
-type runToken chan struct{}
-
-// start blocks until the task can start and returns true if it is allowed to run.
-// Returning false means that the task should be cancelled.
-func (t *servingTask) start() bool {
- if t.peer.isFrozen() {
- return false
- }
- t.tokenCh = make(chan runToken, 1)
- select {
- case t.sq.queueAddCh <- t:
- case <-t.sq.quit:
- return false
- }
- select {
- case t.token = <-t.tokenCh:
- case <-t.sq.quit:
- return false
- }
- if t.token == nil {
- return false
- }
- t.servingTime -= uint64(mclock.Now())
- return true
-}
-
-// done signals the thread controller about the task being finished and returns
-// the total serving time of the task in nanoseconds.
-func (t *servingTask) done() uint64 {
- t.servingTime += uint64(mclock.Now())
- close(t.token)
- diff := t.servingTime - t.timeAdded
- t.timeAdded = t.servingTime
- if t.expTime > diff {
- t.expTime -= diff
- t.sq.servingTimeDiff.Add(t.expTime)
- } else {
- t.expTime = 0
- }
- return t.servingTime
-}
-
-// waitOrStop can be called during the execution of the task. It blocks if there
-// is a higher priority task waiting (a bias is applied in favor of the currently
-// running task). Returning true means that the execution can be resumed. False
-// means the task should be cancelled.
-func (t *servingTask) waitOrStop() bool {
- t.done()
- if !t.biasAdded {
- t.priority += t.sq.suspendBias
- t.biasAdded = true
- }
- return t.start()
-}
-
-// newServingQueue returns a new servingQueue
-func newServingQueue(suspendBias int64, utilTarget float64) *servingQueue {
- sq := &servingQueue{
- queue: prque.New[int64, *servingTask](nil),
- suspendBias: suspendBias,
- queueAddCh: make(chan *servingTask, 100),
- queueBestCh: make(chan *servingTask),
- stopThreadCh: make(chan struct{}),
- quit: make(chan struct{}),
- setThreadsCh: make(chan int, 10),
- burstLimit: uint64(utilTarget * bufLimitRatio * 1200000),
- burstDropLimit: uint64(utilTarget * bufLimitRatio * 1000000),
- burstDecRate: utilTarget,
- lastUpdate: mclock.Now(),
- }
- sq.wg.Add(2)
- go sq.queueLoop()
- go sq.threadCountLoop()
- return sq
-}
-
-// newTask creates a new task with the given priority
-func (sq *servingQueue) newTask(peer *clientPeer, maxTime uint64, priority int64) *servingTask {
- return &servingTask{
- sq: sq,
- peer: peer,
- maxTime: maxTime,
- expTime: maxTime,
- priority: priority,
- }
-}
-
-// threadController is started in multiple goroutines and controls the execution
-// of tasks. The number of active thread controllers equals the allowed number of
-// concurrently running threads. It tries to fetch the highest priority queued
-// task first. If there are no queued tasks waiting then it can directly catch
-// run tokens from the token channel and allow the corresponding tasks to run
-// without entering the priority queue.
-func (sq *servingQueue) threadController() {
- defer sq.wg.Done()
- for {
- token := make(runToken)
- select {
- case best := <-sq.queueBestCh:
- best.tokenCh <- token
- case <-sq.stopThreadCh:
- return
- case <-sq.quit:
- return
- }
- select {
- case <-sq.stopThreadCh:
- return
- case <-sq.quit:
- return
- case <-token:
- }
- }
-}
-
-// peerTasks lists the tasks received from a given peer when selecting peers to freeze
-type peerTasks struct {
- peer *clientPeer
- list []*servingTask
- sumTime uint64
- priority float64
-}
-
-// freezePeers selects the peers with the worst priority queued tasks and freezes
-// them until burstTime goes under burstDropLimit or all peers are frozen
-func (sq *servingQueue) freezePeers() {
- peerMap := make(map[*clientPeer]*peerTasks)
- var peerList []*peerTasks
- if sq.best != nil {
- sq.queue.Push(sq.best, sq.best.priority)
- }
- sq.best = nil
- for sq.queue.Size() > 0 {
- task := sq.queue.PopItem()
- tasks := peerMap[task.peer]
- if tasks == nil {
- bufValue, bufLimit := task.peer.fcClient.BufferStatus()
- if bufLimit < 1 {
- bufLimit = 1
- }
- tasks = &peerTasks{
- peer: task.peer,
- priority: float64(bufValue) / float64(bufLimit), // lower value comes first
- }
- peerMap[task.peer] = tasks
- peerList = append(peerList, tasks)
- }
- tasks.list = append(tasks.list, task)
- tasks.sumTime += task.expTime
- }
- slices.SortFunc(peerList, func(a, b *peerTasks) int {
- if a.priority < b.priority {
- return -1
- }
- if a.priority > b.priority {
- return 1
- }
- return 0
- })
- drop := true
- for _, tasks := range peerList {
- if drop {
- tasks.peer.freeze()
- tasks.peer.fcClient.Freeze()
- sq.queuedTime -= tasks.sumTime
- sqQueuedGauge.Update(int64(sq.queuedTime))
- clientFreezeMeter.Mark(1)
- drop = sq.recentTime+sq.queuedTime > sq.burstDropLimit
- for _, task := range tasks.list {
- task.tokenCh <- nil
- }
- } else {
- for _, task := range tasks.list {
- sq.queue.Push(task, task.priority)
- }
- }
- }
- if sq.queue.Size() > 0 {
- sq.best = sq.queue.PopItem()
- }
-}
-
-// updateRecentTime recalculates the recent serving time value
-func (sq *servingQueue) updateRecentTime() {
- subTime := sq.servingTimeDiff.Swap(0)
- now := mclock.Now()
- dt := now - sq.lastUpdate
- sq.lastUpdate = now
- if dt > 0 {
- subTime += uint64(float64(dt) * sq.burstDecRate)
- }
- if sq.recentTime > subTime {
- sq.recentTime -= subTime
- } else {
- sq.recentTime = 0
- }
-}
-
-// addTask inserts a task into the priority queue
-func (sq *servingQueue) addTask(task *servingTask) {
- if sq.best == nil {
- sq.best = task
- } else if task.priority-sq.best.priority > 0 {
- sq.queue.Push(sq.best, sq.best.priority)
- sq.best = task
- } else {
- sq.queue.Push(task, task.priority)
- }
- sq.updateRecentTime()
- sq.queuedTime += task.expTime
- sqServedGauge.Update(int64(sq.recentTime))
- sqQueuedGauge.Update(int64(sq.queuedTime))
- if sq.recentTime+sq.queuedTime > sq.burstLimit {
- sq.freezePeers()
- }
-}
-
-// queueLoop is an event loop running in a goroutine. It receives tasks from queueAddCh
-// and always tries to send the highest priority task to queueBestCh. Successfully sent
-// tasks are removed from the queue.
-func (sq *servingQueue) queueLoop() {
- defer sq.wg.Done()
- for {
- if sq.best != nil {
- expTime := sq.best.expTime
- select {
- case task := <-sq.queueAddCh:
- sq.addTask(task)
- case sq.queueBestCh <- sq.best:
- sq.updateRecentTime()
- sq.queuedTime -= expTime
- sq.recentTime += expTime
- sqServedGauge.Update(int64(sq.recentTime))
- sqQueuedGauge.Update(int64(sq.queuedTime))
- if sq.queue.Size() == 0 {
- sq.best = nil
- } else {
- sq.best = sq.queue.PopItem()
- }
- case <-sq.quit:
- return
- }
- } else {
- select {
- case task := <-sq.queueAddCh:
- sq.addTask(task)
- case <-sq.quit:
- return
- }
- }
- }
-}
-
-// threadCountLoop is an event loop running in a goroutine. It adjusts the number
-// of active thread controller goroutines.
-func (sq *servingQueue) threadCountLoop() {
- var threadCountTarget int
- defer sq.wg.Done()
- for {
- for threadCountTarget > sq.threadCount {
- sq.wg.Add(1)
- go sq.threadController()
- sq.threadCount++
- }
- if threadCountTarget < sq.threadCount {
- select {
- case threadCountTarget = <-sq.setThreadsCh:
- case sq.stopThreadCh <- struct{}{}:
- sq.threadCount--
- case <-sq.quit:
- return
- }
- } else {
- select {
- case threadCountTarget = <-sq.setThreadsCh:
- case <-sq.quit:
- return
- }
- }
- }
-}
-
-// setThreads sets the allowed processing thread count, suspending tasks as soon as
-// possible if necessary.
-func (sq *servingQueue) setThreads(threadCount int) {
- select {
- case sq.setThreadsCh <- threadCount:
- case <-sq.quit:
- return
- }
-}
-
-// stop stops task processing as soon as possible and shuts down the serving queue.
-func (sq *servingQueue) stop() {
- close(sq.quit)
- sq.wg.Wait()
-}
diff --git a/les/state_accessor.go b/les/state_accessor.go
deleted file mode 100644
index 9a8214ac2f8f..000000000000
--- a/les/state_accessor.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2021 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 .
-
-package les
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
- "github.com/ethereum/go-ethereum/light"
-)
-
-// noopReleaser is returned in case there is no operation expected
-// for releasing state.
-var noopReleaser = tracers.StateReleaseFunc(func() {})
-
-// stateAtBlock retrieves the state database associated with a certain block.
-func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, tracers.StateReleaseFunc, error) {
- return light.NewState(ctx, block.Header(), leth.odr), noopReleaser, nil
-}
-
-// stateAtTransaction returns the execution environment of a certain transaction.
-func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
- // Short circuit if it's genesis block.
- if block.NumberU64() == 0 {
- return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
- }
- // Create the parent state database
- parent, err := leth.blockchain.GetBlock(ctx, block.ParentHash(), block.NumberU64()-1)
- if err != nil {
- return nil, vm.BlockContext{}, nil, nil, err
- }
- statedb, release, err := leth.stateAtBlock(ctx, parent, reexec)
- if err != nil {
- return nil, vm.BlockContext{}, nil, nil, err
- }
- if txIndex == 0 && len(block.Transactions()) == 0 {
- return nil, vm.BlockContext{}, statedb, release, nil
- }
- // Recompute transactions up to the target index.
- signer := types.MakeSigner(leth.blockchain.Config(), block.Number(), block.Time())
- for idx, tx := range block.Transactions() {
- // Assemble the transaction call message and return if the requested offset
- msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
- txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
- statedb.SetTxContext(tx.Hash(), idx)
- if idx == txIndex {
- return msg, context, statedb, release, nil
- }
- // Not yet the searched for transaction, execute on top of the current state
- vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
- if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
- return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
- }
- // Ensure any modifications are committed to the state
- // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
- statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
- }
- return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
-}
diff --git a/les/test_helper.go b/les/test_helper.go
deleted file mode 100644
index 6be13eaecd5f..000000000000
--- a/les/test_helper.go
+++ /dev/null
@@ -1,626 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// This file contains some shares testing functionality, common to multiple
-// different files and modules being tested. Client based network and Server
-// based network can be created easily with available APIs.
-
-package les
-
-import (
- "context"
- "crypto/rand"
- "fmt"
- "math/big"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/forkid"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/txpool/legacypool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- vfs "github.com/ethereum/go-ethereum/les/vflux/server"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-var (
- bankKey, _ = crypto.GenerateKey()
- bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey)
- bankFunds = big.NewInt(1_000_000_000_000_000_000)
-
- userKey1, _ = crypto.GenerateKey()
- userKey2, _ = crypto.GenerateKey()
- userAddr1 = crypto.PubkeyToAddress(userKey1.PublicKey)
- userAddr2 = crypto.PubkeyToAddress(userKey2.PublicKey)
-
- testContractAddr common.Address
- testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
- testContractCodeDeployed = testContractCode[16:]
- testContractDeployed = uint64(2)
-
- testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029")
-
- // Checkpoint oracle relative fields
- signerKey, _ = crypto.GenerateKey()
- signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey)
-)
-
-var (
- // The token bucket buffer limit for testing purpose.
- testBufLimit = uint64(1000000)
-
- // The buffer recharging speed for testing purpose.
- testBufRecharge = uint64(1000)
-)
-
-/*
-contract test {
-
- uint256[100] data;
-
- function Put(uint256 addr, uint256 value) {
- data[addr] = value;
- }
-
- function Get(uint256 addr) constant returns (uint256 value) {
- return data[addr];
- }
-}
-*/
-
-// prepare pre-commits specified number customized blocks into chain.
-func prepare(n int, backend *backends.SimulatedBackend) {
- var (
- ctx = context.Background()
- signer = types.HomesteadSigner{}
- )
- for i := 0; i < n; i++ {
- switch i {
- case 0:
- // Builtin-block
- // number: 1
- // txs: 1
-
- // bankUser transfers some ether to user1
- nonce, _ := backend.PendingNonceAt(ctx, bankAddr)
- tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
- backend.SendTransaction(ctx, tx)
- case 1:
- // Builtin-block
- // number: 2
- // txs: 4
-
- bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
- userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1)
-
- // bankUser transfers more ether to user1
- tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
- backend.SendTransaction(ctx, tx1)
-
- // user1 relays ether to user2
- tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, userKey1)
- backend.SendTransaction(ctx, tx2)
-
- // user1 deploys a test contract
- tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testContractCode), signer, userKey1)
- backend.SendTransaction(ctx, tx3)
- testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1)
-
- // user1 deploys a event contract
- tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testEventEmitterCode), signer, userKey1)
- backend.SendTransaction(ctx, tx4)
- case 2:
- // Builtin-block
- // number: 3
- // txs: 2
-
- // bankUser transfer some ether to signer
- bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
- tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
- backend.SendTransaction(ctx, tx1)
-
- // invoke test contract
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
- tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey)
- backend.SendTransaction(ctx, tx2)
- case 3:
- // Builtin-block
- // number: 4
- // txs: 1
-
- // invoke test contract
- bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
- tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey)
- backend.SendTransaction(ctx, tx)
- }
- backend.Commit()
- }
-}
-
-// testIndexers creates a set of indexers with specified params for testing purpose.
-func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig, disablePruning bool) []*core.ChainIndexer {
- var indexers [3]*core.ChainIndexer
- indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning)
- indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms)
- indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning)
- // make bloomTrieIndexer as a child indexer of bloom indexer.
- indexers[1].AddChildIndexer(indexers[2])
- return indexers[:]
-}
-
-func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *serverPeerSet) (*clientHandler, func()) {
- var (
- evmux = new(event.TypeMux)
- engine = ethash.NewFaker()
- gspec = core.Genesis{
- Config: params.AllEthashProtocolChanges,
- Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
- GasLimit: 100000000,
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- )
- genesis := gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
- chain, _ := light.NewLightChain(odr, gspec.Config, engine)
-
- client := &LightEthereum{
- lesCommons: lesCommons{
- genesis: genesis.Hash(),
- config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId},
- chainConfig: params.AllEthashProtocolChanges,
- iConfig: light.TestClientIndexerConfig,
- chainDb: db,
- chainReader: chain,
- closeCh: make(chan struct{}),
- },
- peers: peers,
- reqDist: odr.retriever.dist,
- retriever: odr.retriever,
- odr: odr,
- engine: engine,
- blockchain: chain,
- eventMux: evmux,
- merger: consensus.NewMerger(rawdb.NewMemoryDatabase()),
- }
- client.handler = newClientHandler(client)
-
- return client.handler, func() {
- client.handler.stop()
- }
-}
-
-func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend, func()) {
- var (
- gspec = core.Genesis{
- Config: params.AllEthashProtocolChanges,
- Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
- GasLimit: 100000000,
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- )
- genesis := gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
-
- // create a simulation backend and pre-commit several customized block to the database.
- simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000)
- prepare(blocks, simulation)
-
- txpoolConfig := legacypool.DefaultConfig
- txpoolConfig.Journal = ""
-
- pool := legacypool.New(txpoolConfig, simulation.Blockchain())
- txpool, _ := txpool.New(new(big.Int).SetUint64(txpoolConfig.PriceLimit), simulation.Blockchain(), []txpool.SubPool{pool})
-
- server := &LesServer{
- lesCommons: lesCommons{
- genesis: genesis.Hash(),
- config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId},
- chainConfig: params.AllEthashProtocolChanges,
- iConfig: light.TestServerIndexerConfig,
- chainDb: db,
- chainReader: simulation.Blockchain(),
- closeCh: make(chan struct{}),
- },
- peers: newClientPeerSet(),
- servingQueue: newServingQueue(int64(time.Millisecond*10), 1),
- defParams: flowcontrol.ServerParams{
- BufLimit: testBufLimit,
- MinRecharge: testBufRecharge,
- },
- fcManager: flowcontrol.NewClientManager(nil, clock),
- }
- server.costTracker, server.minCapacity = newCostTracker(db, server.config)
- server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism.
- server.clientPool = vfs.NewClientPool(db, testBufRecharge, defaultConnectedBias, clock, alwaysTrueFn)
- server.clientPool.Start()
- server.clientPool.SetLimits(10000, 10000) // Assign enough capacity for clientpool
- server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true })
- server.servingQueue.setThreads(4)
- server.handler.start()
- closer := func() { server.Stop() }
- return server.handler, simulation, closer
-}
-
-func alwaysTrueFn() bool {
- return true
-}
-
-// testPeer is a simulated peer to allow testing direct network calls.
-type testPeer struct {
- cpeer *clientPeer
- speer *serverPeer
-
- net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
- app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side
-}
-
-// handshakeWithServer executes the handshake with the remote server peer.
-func (p *testPeer) handshakeWithServer(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID) {
- // It only works for the simulated client peer
- if p.cpeer == nil {
- t.Fatal("handshake for client peer only")
- }
- var sendList keyValueList
- sendList = sendList.add("protocolVersion", uint64(p.cpeer.version))
- sendList = sendList.add("networkId", uint64(NetworkId))
- sendList = sendList.add("headTd", td)
- sendList = sendList.add("headHash", head)
- sendList = sendList.add("headNum", headNum)
- sendList = sendList.add("genesisHash", genesis)
- if p.cpeer.version >= lpv4 {
- sendList = sendList.add("forkID", &forkID)
- }
- if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil {
- t.Fatalf("status recv: %v", err)
- }
- if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil {
- t.Fatalf("status send: %v", err)
- }
-}
-
-// handshakeWithClient executes the handshake with the remote client peer.
-// (used by temporarily disabled tests)
-/*func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, costList RequestCostList, recentTxLookup uint64) {
- // It only works for the simulated client peer
- if p.speer == nil {
- t.Fatal("handshake for server peer only")
- }
- var sendList keyValueList
- sendList = sendList.add("protocolVersion", uint64(p.speer.version))
- sendList = sendList.add("networkId", uint64(NetworkId))
- sendList = sendList.add("headTd", td)
- sendList = sendList.add("headHash", head)
- sendList = sendList.add("headNum", headNum)
- sendList = sendList.add("genesisHash", genesis)
- sendList = sendList.add("serveHeaders", nil)
- sendList = sendList.add("serveChainSince", uint64(0))
- sendList = sendList.add("serveStateSince", uint64(0))
- sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4))
- sendList = sendList.add("txRelay", nil)
- sendList = sendList.add("flowControl/BL", testBufLimit)
- sendList = sendList.add("flowControl/MRR", testBufRecharge)
- sendList = sendList.add("flowControl/MRC", costList)
- if p.speer.version >= lpv4 {
- sendList = sendList.add("forkID", &forkID)
- sendList = sendList.add("recentTxLookup", recentTxLookup)
- }
- if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil {
- t.Fatalf("status recv: %v", err)
- }
- if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil {
- t.Fatalf("status send: %v", err)
- }
-}*/
-
-// close terminates the local side of the peer, notifying the remote protocol
-// manager of termination.
-func (p *testPeer) close() {
- p.app.Close()
-}
-
-func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler, noInitAnnounce bool) (*testPeer, *testPeer, error) {
- // Create a message pipe to communicate through
- app, net := p2p.MsgPipe()
-
- // Generate a random id and create the peer
- var id enode.ID
- rand.Read(id[:])
-
- peer1 := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
- peer2 := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), app)
-
- // Start the peer on a new thread
- errc1 := make(chan error, 1)
- errc2 := make(chan error, 1)
- go func() {
- select {
- case <-server.closeCh:
- errc1 <- p2p.DiscQuitting
- case errc1 <- server.handle(peer1):
- }
- }()
- go func() {
- select {
- case <-client.closeCh:
- errc2 <- p2p.DiscQuitting
- case errc2 <- client.handle(peer2, noInitAnnounce):
- }
- }()
- // Ensure the connection is established or exits when any error occurs
- for {
- select {
- case err := <-errc1:
- return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
- case err := <-errc2:
- return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
- default:
- }
- if peer1.serving.Load() && peer2.serving.Load() {
- break
- }
- time.Sleep(50 * time.Millisecond)
- }
- return &testPeer{cpeer: peer1, net: net, app: app}, &testPeer{speer: peer2, net: app, app: net}, nil
-}
-
-type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer)
-
-// testClient represents a client object for testing with necessary auxiliary fields.
-type testClient struct {
- clock mclock.Clock
- db ethdb.Database
- peer *testPeer
- handler *clientHandler
-
- chtIndexer *core.ChainIndexer
- bloomIndexer *core.ChainIndexer
- bloomTrieIndexer *core.ChainIndexer
-}
-
-// newRawPeer creates a new server peer connects to the server and do the handshake.
-// (used by temporarily disabled tests)
-/*func (client *testClient) newRawPeer(t *testing.T, name string, version int, recentTxLookup uint64) (*testPeer, func(), <-chan error) {
- // Create a message pipe to communicate through
- app, net := p2p.MsgPipe()
-
- // Generate a random id and create the peer
- var id enode.ID
- rand.Read(id[:])
- peer := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net)
-
- // Start the peer on a new thread
- errCh := make(chan error, 1)
- go func() {
- select {
- case <-client.handler.closeCh:
- errCh <- p2p.DiscQuitting
- case errCh <- client.handler.handle(peer, false):
- }
- }()
- tp := &testPeer{
- app: app,
- net: net,
- speer: peer,
- }
- var (
- genesis = client.handler.backend.blockchain.Genesis()
- head = client.handler.backend.blockchain.CurrentHeader()
- td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64())
- )
- forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time)
- tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default
-
- // Ensure the connection is established or exits when any error occurs
- for {
- select {
- case <-errCh:
- return nil, nil, nil
- default:
- }
- if peer.serving.Load() {
- break
- }
- time.Sleep(50 * time.Millisecond)
- }
- closePeer := func() {
- tp.speer.close()
- tp.close()
- }
- return tp, closePeer, errCh
-}*/
-
-// testServer represents a server object for testing with necessary auxiliary fields.
-type testServer struct {
- clock mclock.Clock
- backend *backends.SimulatedBackend
- db ethdb.Database
- peer *testPeer
- handler *serverHandler
-
- chtIndexer *core.ChainIndexer
- bloomIndexer *core.ChainIndexer
- bloomTrieIndexer *core.ChainIndexer
-}
-
-// newRawPeer creates a new client peer connects to the server and do the handshake.
-func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*testPeer, func(), <-chan error) {
- // Create a message pipe to communicate through
- app, net := p2p.MsgPipe()
-
- // Generate a random id and create the peer
- var id enode.ID
- rand.Read(id[:])
- peer := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
-
- // Start the peer on a new thread
- errCh := make(chan error, 1)
- go func() {
- select {
- case <-server.handler.closeCh:
- errCh <- p2p.DiscQuitting
- case errCh <- server.handler.handle(peer):
- }
- }()
- tp := &testPeer{
- app: app,
- net: net,
- cpeer: peer,
- }
- var (
- genesis = server.handler.blockchain.Genesis()
- head = server.handler.blockchain.CurrentHeader()
- td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64())
- )
- forkID := forkid.NewID(server.handler.blockchain.Config(), genesis, head.Number.Uint64(), head.Time)
- tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID)
-
- // Ensure the connection is established or exits when any error occurs
- for {
- select {
- case <-errCh:
- return nil, nil, nil
- default:
- }
- if peer.serving.Load() {
- break
- }
- time.Sleep(50 * time.Millisecond)
- }
- closePeer := func() {
- tp.cpeer.close()
- tp.close()
- }
- return tp, closePeer, errCh
-}
-
-// testnetConfig wraps all the configurations for testing network.
-type testnetConfig struct {
- blocks int
- protocol int
- indexFn indexerCallback
- simClock bool
- connect bool
- nopruning bool
-}
-
-func newClientServerEnv(t *testing.T, config testnetConfig) (*testServer, *testClient, func()) {
- var (
- sdb = rawdb.NewMemoryDatabase()
- cdb = rawdb.NewMemoryDatabase()
- speers = newServerPeerSet()
- )
- var clock mclock.Clock = &mclock.System{}
- if config.simClock {
- clock = &mclock.Simulated{}
- }
- dist := newRequestDistributor(speers, clock)
- rm := newRetrieveManager(speers, dist, func() time.Duration { return time.Millisecond * 500 })
- odr := NewLesOdr(cdb, light.TestClientIndexerConfig, speers, rm)
-
- sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig, true)
- cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig, config.nopruning)
-
- scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2]
- ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2]
- odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer)
-
- server, b, serverClose := newTestServerHandler(config.blocks, sindexers, sdb, clock)
- client, clientClose := newTestClientHandler(b, odr, cIndexers, cdb, speers)
-
- scIndexer.Start(server.blockchain)
- sbIndexer.Start(server.blockchain)
- ccIndexer.Start(client.backend.blockchain)
- cbIndexer.Start(client.backend.blockchain)
-
- if config.indexFn != nil {
- config.indexFn(scIndexer, sbIndexer, sbtIndexer)
- }
- var (
- err error
- speer, cpeer *testPeer
- )
- if config.connect {
- done := make(chan struct{})
- cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client, false)
- if err != nil {
- t.Fatalf("Failed to connect testing peers %v", err)
- }
- select {
- case <-done:
- case <-time.After(10 * time.Second):
- t.Fatal("test peer did not connect and sync within 3s")
- }
- }
- s := &testServer{
- clock: clock,
- backend: b,
- db: sdb,
- peer: cpeer,
- handler: server,
- chtIndexer: scIndexer,
- bloomIndexer: sbIndexer,
- bloomTrieIndexer: sbtIndexer,
- }
- c := &testClient{
- clock: clock,
- db: cdb,
- peer: speer,
- handler: client,
- chtIndexer: ccIndexer,
- bloomIndexer: cbIndexer,
- bloomTrieIndexer: cbtIndexer,
- }
- teardown := func() {
- if config.connect {
- speer.close()
- cpeer.close()
- cpeer.cpeer.close()
- speer.speer.close()
- }
- ccIndexer.Close()
- cbIndexer.Close()
- scIndexer.Close()
- sbIndexer.Close()
- dist.close()
- serverClose()
- b.Close()
- clientClose()
- }
- return s, c, teardown
-}
-
-// NewFuzzerPeer creates a client peer for test purposes, and also returns
-// a function to close the peer: this is needed to avoid goroutine leaks in the
-// exec queue.
-func NewFuzzerPeer(version int) (p *clientPeer, closer func()) {
- p = newClientPeer(version, 0, p2p.NewPeer(enode.ID{}, "", nil), nil)
- return p, func() { p.peerCommons.close() }
-}
diff --git a/les/txrelay.go b/les/txrelay.go
deleted file mode 100644
index 40a51fb76f8e..000000000000
--- a/les/txrelay.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package les
-
-import (
- "context"
- "math/rand"
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-type lesTxRelay struct {
- txSent map[common.Hash]*types.Transaction
- txPending map[common.Hash]struct{}
- peerList []*serverPeer
- peerStartPos int
- lock sync.Mutex
- stop chan struct{}
-
- retriever *retrieveManager
-}
-
-func newLesTxRelay(ps *serverPeerSet, retriever *retrieveManager) *lesTxRelay {
- r := &lesTxRelay{
- txSent: make(map[common.Hash]*types.Transaction),
- txPending: make(map[common.Hash]struct{}),
- retriever: retriever,
- stop: make(chan struct{}),
- }
- ps.subscribe(r)
- return r
-}
-
-func (ltrx *lesTxRelay) Stop() {
- close(ltrx.stop)
-}
-
-func (ltrx *lesTxRelay) registerPeer(p *serverPeer) {
- ltrx.lock.Lock()
- defer ltrx.lock.Unlock()
-
- // Short circuit if the peer is announce only.
- if p.onlyAnnounce {
- return
- }
- ltrx.peerList = append(ltrx.peerList, p)
-}
-
-func (ltrx *lesTxRelay) unregisterPeer(p *serverPeer) {
- ltrx.lock.Lock()
- defer ltrx.lock.Unlock()
-
- for i, peer := range ltrx.peerList {
- if peer == p {
- // Remove from the peer list
- ltrx.peerList = append(ltrx.peerList[:i], ltrx.peerList[i+1:]...)
- return
- }
- }
-}
-
-// send sends a list of transactions to at most a given number of peers.
-func (ltrx *lesTxRelay) send(txs types.Transactions, count int) {
- sendTo := make(map[*serverPeer]types.Transactions)
-
- ltrx.peerStartPos++ // rotate the starting position of the peer list
- if ltrx.peerStartPos >= len(ltrx.peerList) {
- ltrx.peerStartPos = 0
- }
-
- for _, tx := range txs {
- hash := tx.Hash()
- _, ok := ltrx.txSent[hash]
- if !ok {
- ltrx.txSent[hash] = tx
- ltrx.txPending[hash] = struct{}{}
- }
- if len(ltrx.peerList) > 0 {
- cnt := count
- pos := ltrx.peerStartPos
- for {
- peer := ltrx.peerList[pos]
- sendTo[peer] = append(sendTo[peer], tx)
- cnt--
- if cnt == 0 {
- break // sent it to the desired number of peers
- }
- pos++
- if pos == len(ltrx.peerList) {
- pos = 0
- }
- if pos == ltrx.peerStartPos {
- break // tried all available peers
- }
- }
- }
- }
-
- for p, list := range sendTo {
- pp := p
- ll := list
- enc, _ := rlp.EncodeToBytes(ll)
-
- reqID := rand.Uint64()
- rq := &distReq{
- getCost: func(dp distPeer) uint64 {
- peer := dp.(*serverPeer)
- return peer.getTxRelayCost(len(ll), len(enc))
- },
- canSend: func(dp distPeer) bool {
- return !dp.(*serverPeer).onlyAnnounce && dp.(*serverPeer) == pp
- },
- request: func(dp distPeer) func() {
- peer := dp.(*serverPeer)
- cost := peer.getTxRelayCost(len(ll), len(enc))
- peer.fcServer.QueuedRequest(reqID, cost)
- return func() { peer.sendTxs(reqID, len(ll), enc) }
- },
- }
- go ltrx.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, ltrx.stop)
- }
-}
-
-func (ltrx *lesTxRelay) Send(txs types.Transactions) {
- ltrx.lock.Lock()
- defer ltrx.lock.Unlock()
-
- ltrx.send(txs, 3)
-}
-
-func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
- ltrx.lock.Lock()
- defer ltrx.lock.Unlock()
-
- for _, hash := range mined {
- delete(ltrx.txPending, hash)
- }
-
- for _, hash := range rollback {
- ltrx.txPending[hash] = struct{}{}
- }
-
- if len(ltrx.txPending) > 0 {
- txs := make(types.Transactions, len(ltrx.txPending))
- i := 0
- for hash := range ltrx.txPending {
- txs[i] = ltrx.txSent[hash]
- i++
- }
- ltrx.send(txs, 1)
- }
-}
-
-func (ltrx *lesTxRelay) Discard(hashes []common.Hash) {
- ltrx.lock.Lock()
- defer ltrx.lock.Unlock()
-
- for _, hash := range hashes {
- delete(ltrx.txSent, hash)
- delete(ltrx.txPending, hash)
- }
-}
diff --git a/les/utils/exec_queue.go b/les/utils/exec_queue.go
deleted file mode 100644
index 5942b06ec0a1..000000000000
--- a/les/utils/exec_queue.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import "sync"
-
-// ExecQueue implements a queue that executes function calls in a single thread,
-// in the same order as they have been queued.
-type ExecQueue struct {
- mu sync.Mutex
- cond *sync.Cond
- funcs []func()
- closeWait chan struct{}
-}
-
-// NewExecQueue creates a new execution Queue.
-func NewExecQueue(capacity int) *ExecQueue {
- q := &ExecQueue{funcs: make([]func(), 0, capacity)}
- q.cond = sync.NewCond(&q.mu)
- go q.loop()
- return q
-}
-
-func (q *ExecQueue) loop() {
- for f := q.waitNext(false); f != nil; f = q.waitNext(true) {
- f()
- }
- close(q.closeWait)
-}
-
-func (q *ExecQueue) waitNext(drop bool) (f func()) {
- q.mu.Lock()
- if drop && len(q.funcs) > 0 {
- // Remove the function that just executed. We do this here instead of when
- // dequeuing so len(q.funcs) includes the function that is running.
- q.funcs = append(q.funcs[:0], q.funcs[1:]...)
- }
- for !q.isClosed() {
- if len(q.funcs) > 0 {
- f = q.funcs[0]
- break
- }
- q.cond.Wait()
- }
- q.mu.Unlock()
- return f
-}
-
-func (q *ExecQueue) isClosed() bool {
- return q.closeWait != nil
-}
-
-// CanQueue returns true if more function calls can be added to the execution Queue.
-func (q *ExecQueue) CanQueue() bool {
- q.mu.Lock()
- ok := !q.isClosed() && len(q.funcs) < cap(q.funcs)
- q.mu.Unlock()
- return ok
-}
-
-// Queue adds a function call to the execution Queue. Returns true if successful.
-func (q *ExecQueue) Queue(f func()) bool {
- q.mu.Lock()
- ok := !q.isClosed() && len(q.funcs) < cap(q.funcs)
- if ok {
- q.funcs = append(q.funcs, f)
- q.cond.Signal()
- }
- q.mu.Unlock()
- return ok
-}
-
-// Clear drops all queued functions.
-func (q *ExecQueue) Clear() {
- q.mu.Lock()
- q.funcs = q.funcs[:0]
- q.mu.Unlock()
-}
-
-// Quit stops the exec Queue.
-//
-// Quit waits for the current execution to finish before returning.
-func (q *ExecQueue) Quit() {
- q.mu.Lock()
- if !q.isClosed() {
- q.closeWait = make(chan struct{})
- q.cond.Signal()
- }
- q.mu.Unlock()
- <-q.closeWait
-}
diff --git a/les/utils/exec_queue_test.go b/les/utils/exec_queue_test.go
deleted file mode 100644
index 98601c448662..000000000000
--- a/les/utils/exec_queue_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import "testing"
-
-func TestExecQueue(t *testing.T) {
- var (
- N = 10000
- q = NewExecQueue(N)
- counter int
- execd = make(chan int)
- testexit = make(chan struct{})
- )
- defer q.Quit()
- defer close(testexit)
-
- check := func(state string, wantOK bool) {
- c := counter
- counter++
- qf := func() {
- select {
- case execd <- c:
- case <-testexit:
- }
- }
- if q.CanQueue() != wantOK {
- t.Fatalf("CanQueue() == %t for %s", !wantOK, state)
- }
- if q.Queue(qf) != wantOK {
- t.Fatalf("Queue() == %t for %s", !wantOK, state)
- }
- }
-
- for i := 0; i < N; i++ {
- check("queue below cap", true)
- }
- check("full queue", false)
- for i := 0; i < N; i++ {
- if c := <-execd; c != i {
- t.Fatal("execution out of order")
- }
- }
- q.Quit()
- check("closed queue", false)
-}
diff --git a/les/utils/expiredvalue.go b/les/utils/expiredvalue.go
deleted file mode 100644
index 099b61d0536d..000000000000
--- a/les/utils/expiredvalue.go
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "math"
- "sync"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-// ExpiredValue is a scalar value that is continuously expired (decreased
-// exponentially) based on the provided logarithmic expiration offset value.
-//
-// The formula for value calculation is: base*2^(exp-logOffset). In order to
-// simplify the calculation of ExpiredValue, its value is expressed in the form
-// of an exponent with a base of 2.
-//
-// Also here is a trick to reduce a lot of calculations. In theory, when a value X
-// decays over time and then a new value Y is added, the final result should be
-// X*2^(exp-logOffset)+Y. However it's very hard to represent in memory.
-// So the trick is using the idea of inflation instead of exponential decay. At this
-// moment the temporary value becomes: X*2^exp+Y*2^logOffset_1, apply the exponential
-// decay when we actually want to calculate the value.
-//
-// e.g.
-// t0: V = 100
-// t1: add 30, inflationary value is: 100 + 30/0.3, 0.3 is the decay coefficient
-// t2: get value, decay coefficient is 0.2 now, final result is: 200*0.2 = 40
-type ExpiredValue struct {
- Base, Exp uint64 // rlp encoding works by default
-}
-
-// ExpirationFactor is calculated from logOffset. 1 <= Factor < 2 and Factor*2^Exp
-// describes the multiplier applicable for additions and the divider for readouts.
-// If logOffset changes slowly then it saves some expensive operations to not calculate
-// them for each addition and readout but cache this intermediate form for some time.
-// It is also useful for structures where multiple values are expired with the same
-// Expirer.
-type ExpirationFactor struct {
- Exp uint64
- Factor float64
-}
-
-// ExpFactor calculates ExpirationFactor based on logOffset
-func ExpFactor(logOffset Fixed64) ExpirationFactor {
- return ExpirationFactor{Exp: logOffset.ToUint64(), Factor: logOffset.Fraction().Pow2()}
-}
-
-// Value calculates the expired value based on a floating point base and integer
-// power-of-2 exponent. This function should be used by multi-value expired structures.
-func (e ExpirationFactor) Value(base float64, exp uint64) float64 {
- return base / e.Factor * math.Pow(2, float64(int64(exp-e.Exp)))
-}
-
-// Value calculates the value at the given moment.
-func (e ExpiredValue) Value(logOffset Fixed64) uint64 {
- offset := Uint64ToFixed64(e.Exp) - logOffset
- return uint64(float64(e.Base) * offset.Pow2())
-}
-
-// Add adds a signed value at the given moment
-func (e *ExpiredValue) Add(amount int64, logOffset Fixed64) int64 {
- integer, frac := logOffset.ToUint64(), logOffset.Fraction()
- factor := frac.Pow2()
- base := factor * float64(amount)
- if integer < e.Exp {
- base /= math.Pow(2, float64(e.Exp-integer))
- }
- if integer > e.Exp {
- e.Base >>= (integer - e.Exp)
- e.Exp = integer
- }
- if base >= 0 || uint64(-base) <= e.Base {
- // The conversion from negative float64 to
- // uint64 is undefined in golang, and doesn't
- // work with ARMv8. More details at:
- // https://github.com/golang/go/issues/43047
- if base >= 0 {
- e.Base += uint64(base)
- } else {
- e.Base -= uint64(-base)
- }
- return amount
- }
- net := int64(-float64(e.Base) / factor)
- e.Base = 0
- return net
-}
-
-// AddExp adds another ExpiredValue
-func (e *ExpiredValue) AddExp(a ExpiredValue) {
- if e.Exp > a.Exp {
- a.Base >>= (e.Exp - a.Exp)
- }
- if e.Exp < a.Exp {
- e.Base >>= (a.Exp - e.Exp)
- e.Exp = a.Exp
- }
- e.Base += a.Base
-}
-
-// SubExp subtracts another ExpiredValue
-func (e *ExpiredValue) SubExp(a ExpiredValue) {
- if e.Exp > a.Exp {
- a.Base >>= (e.Exp - a.Exp)
- }
- if e.Exp < a.Exp {
- e.Base >>= (a.Exp - e.Exp)
- e.Exp = a.Exp
- }
- if e.Base > a.Base {
- e.Base -= a.Base
- } else {
- e.Base = 0
- }
-}
-
-// IsZero returns true if the value is zero
-func (e *ExpiredValue) IsZero() bool {
- return e.Base == 0
-}
-
-// LinearExpiredValue is very similar with the expiredValue which the value
-// will continuously expired. But the different part is it's expired linearly.
-type LinearExpiredValue struct {
- Offset uint64 // The latest time offset
- Val uint64 // The remaining value, can never be negative
- Rate mclock.AbsTime `rlp:"-"` // Expiration rate(by nanosecond), will ignored by RLP
-}
-
-// Value calculates the value at the given moment. This function always has the
-// assumption that the given timestamp shouldn't less than the recorded one.
-func (e LinearExpiredValue) Value(now mclock.AbsTime) uint64 {
- offset := uint64(now / e.Rate)
- if e.Offset < offset {
- diff := offset - e.Offset
- if e.Val >= diff {
- e.Val -= diff
- } else {
- e.Val = 0
- }
- }
- return e.Val
-}
-
-// Add adds a signed value at the given moment. This function always has the
-// assumption that the given timestamp shouldn't less than the recorded one.
-func (e *LinearExpiredValue) Add(amount int64, now mclock.AbsTime) uint64 {
- offset := uint64(now / e.Rate)
- if e.Offset < offset {
- diff := offset - e.Offset
- if e.Val >= diff {
- e.Val -= diff
- } else {
- e.Val = 0
- }
- e.Offset = offset
- }
- if amount < 0 && uint64(-amount) > e.Val {
- e.Val = 0
- } else {
- e.Val = uint64(int64(e.Val) + amount)
- }
- return e.Val
-}
-
-// ValueExpirer controls value expiration rate
-type ValueExpirer interface {
- SetRate(now mclock.AbsTime, rate float64)
- SetLogOffset(now mclock.AbsTime, logOffset Fixed64)
- LogOffset(now mclock.AbsTime) Fixed64
-}
-
-// Expirer changes logOffset with a linear rate which can be changed during operation.
-// It is not thread safe, if access by multiple goroutines is needed then it should be
-// encapsulated into a locked structure.
-// Note that if neither SetRate nor SetLogOffset are used during operation then LogOffset
-// is thread safe.
-type Expirer struct {
- lock sync.RWMutex
- logOffset Fixed64
- rate float64
- lastUpdate mclock.AbsTime
-}
-
-// SetRate changes the expiration rate which is the inverse of the time constant in
-// nanoseconds.
-func (e *Expirer) SetRate(now mclock.AbsTime, rate float64) {
- e.lock.Lock()
- defer e.lock.Unlock()
-
- dt := now - e.lastUpdate
- if dt > 0 {
- e.logOffset += Fixed64(logToFixedFactor * float64(dt) * e.rate)
- }
- e.lastUpdate = now
- e.rate = rate
-}
-
-// SetLogOffset sets logOffset instantly.
-func (e *Expirer) SetLogOffset(now mclock.AbsTime, logOffset Fixed64) {
- e.lock.Lock()
- defer e.lock.Unlock()
-
- e.lastUpdate = now
- e.logOffset = logOffset
-}
-
-// LogOffset returns the current logarithmic offset.
-func (e *Expirer) LogOffset(now mclock.AbsTime) Fixed64 {
- e.lock.RLock()
- defer e.lock.RUnlock()
-
- dt := now - e.lastUpdate
- if dt <= 0 {
- return e.logOffset
- }
- return e.logOffset + Fixed64(logToFixedFactor*float64(dt)*e.rate)
-}
-
-// fixedFactor is the fixed point multiplier factor used by Fixed64.
-const fixedFactor = 0x1000000
-
-// Fixed64 implements 64-bit fixed point arithmetic functions.
-type Fixed64 int64
-
-// Uint64ToFixed64 converts uint64 integer to Fixed64 format.
-func Uint64ToFixed64(f uint64) Fixed64 {
- return Fixed64(f * fixedFactor)
-}
-
-// Float64ToFixed64 converts float64 to Fixed64 format.
-func Float64ToFixed64(f float64) Fixed64 {
- return Fixed64(f * fixedFactor)
-}
-
-// ToUint64 converts Fixed64 format to uint64.
-func (f64 Fixed64) ToUint64() uint64 {
- return uint64(f64) / fixedFactor
-}
-
-// Fraction returns the fractional part of a Fixed64 value.
-func (f64 Fixed64) Fraction() Fixed64 {
- return f64 % fixedFactor
-}
-
-var (
- logToFixedFactor = float64(fixedFactor) / math.Log(2)
- fixedToLogFactor = math.Log(2) / float64(fixedFactor)
-)
-
-// Pow2 returns the base 2 power of the fixed point value.
-func (f64 Fixed64) Pow2() float64 {
- return math.Exp(float64(f64) * fixedToLogFactor)
-}
diff --git a/les/utils/expiredvalue_test.go b/les/utils/expiredvalue_test.go
deleted file mode 100644
index 1c751d8cc628..000000000000
--- a/les/utils/expiredvalue_test.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "testing"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-func TestValueExpiration(t *testing.T) {
- var cases = []struct {
- input ExpiredValue
- timeOffset Fixed64
- expect uint64
- }{
- {ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(0), 128},
- {ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(1), 64},
- {ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(2), 32},
- {ExpiredValue{Base: 128, Exp: 2}, Uint64ToFixed64(2), 128},
- {ExpiredValue{Base: 128, Exp: 2}, Uint64ToFixed64(3), 64},
- }
- for _, c := range cases {
- if got := c.input.Value(c.timeOffset); got != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
- }
- }
-}
-
-func TestValueAddition(t *testing.T) {
- var cases = []struct {
- input ExpiredValue
- addend int64
- timeOffset Fixed64
- expect uint64
- expectNet int64
- }{
- // Addition
- {ExpiredValue{Base: 128, Exp: 0}, 128, Uint64ToFixed64(0), 256, 128},
- {ExpiredValue{Base: 128, Exp: 2}, 128, Uint64ToFixed64(0), 640, 128},
-
- // Addition with offset
- {ExpiredValue{Base: 128, Exp: 0}, 128, Uint64ToFixed64(1), 192, 128},
- {ExpiredValue{Base: 128, Exp: 2}, 128, Uint64ToFixed64(1), 384, 128},
- {ExpiredValue{Base: 128, Exp: 2}, 128, Uint64ToFixed64(3), 192, 128},
-
- // Subtraction
- {ExpiredValue{Base: 128, Exp: 0}, -64, Uint64ToFixed64(0), 64, -64},
- {ExpiredValue{Base: 128, Exp: 0}, -128, Uint64ToFixed64(0), 0, -128},
- {ExpiredValue{Base: 128, Exp: 0}, -192, Uint64ToFixed64(0), 0, -128},
-
- // Subtraction with offset
- {ExpiredValue{Base: 128, Exp: 0}, -64, Uint64ToFixed64(1), 0, -64},
- {ExpiredValue{Base: 128, Exp: 0}, -128, Uint64ToFixed64(1), 0, -64},
- {ExpiredValue{Base: 128, Exp: 2}, -128, Uint64ToFixed64(1), 128, -128},
- {ExpiredValue{Base: 128, Exp: 2}, -128, Uint64ToFixed64(2), 0, -128},
- }
- for _, c := range cases {
- if net := c.input.Add(c.addend, c.timeOffset); net != c.expectNet {
- t.Fatalf("Net amount mismatch, want=%d, got=%d", c.expectNet, net)
- }
- if got := c.input.Value(c.timeOffset); got != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
- }
- }
-}
-
-func TestExpiredValueAddition(t *testing.T) {
- var cases = []struct {
- input ExpiredValue
- another ExpiredValue
- timeOffset Fixed64
- expect uint64
- }{
- {ExpiredValue{Base: 128, Exp: 0}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(0), 256},
- {ExpiredValue{Base: 128, Exp: 1}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(0), 384},
- {ExpiredValue{Base: 128, Exp: 0}, ExpiredValue{Base: 128, Exp: 1}, Uint64ToFixed64(0), 384},
- {ExpiredValue{Base: 128, Exp: 0}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(1), 128},
- }
- for _, c := range cases {
- c.input.AddExp(c.another)
- if got := c.input.Value(c.timeOffset); got != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
- }
- }
-}
-
-func TestExpiredValueSubtraction(t *testing.T) {
- var cases = []struct {
- input ExpiredValue
- another ExpiredValue
- timeOffset Fixed64
- expect uint64
- }{
- {ExpiredValue{Base: 128, Exp: 0}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(0), 0},
- {ExpiredValue{Base: 128, Exp: 0}, ExpiredValue{Base: 128, Exp: 1}, Uint64ToFixed64(0), 0},
- {ExpiredValue{Base: 128, Exp: 1}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(0), 128},
- {ExpiredValue{Base: 128, Exp: 1}, ExpiredValue{Base: 128, Exp: 0}, Uint64ToFixed64(1), 64},
- }
- for _, c := range cases {
- c.input.SubExp(c.another)
- if got := c.input.Value(c.timeOffset); got != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
- }
- }
-}
-
-func TestLinearExpiredValue(t *testing.T) {
- var cases = []struct {
- value LinearExpiredValue
- now mclock.AbsTime
- expect uint64
- }{
- {LinearExpiredValue{
- Offset: 0,
- Val: 0,
- Rate: mclock.AbsTime(1),
- }, 0, 0},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 1,
- Rate: mclock.AbsTime(1),
- }, 0, 1},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 1,
- Rate: mclock.AbsTime(1),
- }, mclock.AbsTime(2), 0},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 1,
- Rate: mclock.AbsTime(1),
- }, mclock.AbsTime(3), 0},
- }
- for _, c := range cases {
- if value := c.value.Value(c.now); value != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, value)
- }
- }
-}
-
-func TestLinearExpiredAddition(t *testing.T) {
- var cases = []struct {
- value LinearExpiredValue
- amount int64
- now mclock.AbsTime
- expect uint64
- }{
- {LinearExpiredValue{
- Offset: 0,
- Val: 0,
- Rate: mclock.AbsTime(1),
- }, -1, 0, 0},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 1,
- Rate: mclock.AbsTime(1),
- }, -1, 0, 0},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 2,
- Rate: mclock.AbsTime(1),
- }, -1, mclock.AbsTime(2), 0},
-
- {LinearExpiredValue{
- Offset: 1,
- Val: 2,
- Rate: mclock.AbsTime(1),
- }, -2, mclock.AbsTime(2), 0},
- }
- for _, c := range cases {
- if value := c.value.Add(c.amount, c.now); value != c.expect {
- t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, value)
- }
- }
-}
diff --git a/les/utils/limiter.go b/les/utils/limiter.go
deleted file mode 100644
index 70b7ff64f721..000000000000
--- a/les/utils/limiter.go
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright 2021 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 .
-
-package utils
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/p2p/enode"
- "golang.org/x/exp/slices"
-)
-
-const maxSelectionWeight = 1000000000 // maximum selection weight of each individual node/address group
-
-// Limiter protects a network request serving mechanism from denial-of-service attacks.
-// It limits the total amount of resources used for serving requests while ensuring that
-// the most valuable connections always have a reasonable chance of being served.
-type Limiter struct {
- lock sync.Mutex
- cond *sync.Cond
- quit bool
-
- nodes map[enode.ID]*nodeQueue
- addresses map[string]*addressGroup
- addressSelect, valueSelect *WeightedRandomSelect
- maxValue float64
- maxCost, sumCost, sumCostLimit uint
- selectAddressNext bool
-}
-
-// nodeQueue represents queued requests coming from a single node ID
-type nodeQueue struct {
- queue []request // always nil if penaltyCost != 0
- id enode.ID
- address string
- value float64
- flatWeight, valueWeight uint64 // current selection weights in the address/value selectors
- sumCost uint // summed cost of requests queued by the node
- penaltyCost uint // cumulative cost of dropped requests since last processed request
- groupIndex int
-}
-
-// addressGroup is a group of node IDs that have sent their last requests from the same
-// network address
-type addressGroup struct {
- nodes []*nodeQueue
- nodeSelect *WeightedRandomSelect
- sumFlatWeight, groupWeight uint64
-}
-
-// request represents an incoming request scheduled for processing
-type request struct {
- process chan chan struct{}
- cost uint
-}
-
-// flatWeight distributes weights equally between each active network address
-func flatWeight(item interface{}) uint64 { return item.(*nodeQueue).flatWeight }
-
-// add adds the node queue to the address group. It is the caller's responsibility to
-// add the address group to the address map and the address selector if it wasn't
-// there before.
-func (ag *addressGroup) add(nq *nodeQueue) {
- if nq.groupIndex != -1 {
- panic("added node queue is already in an address group")
- }
- l := len(ag.nodes)
- nq.groupIndex = l
- ag.nodes = append(ag.nodes, nq)
- ag.sumFlatWeight += nq.flatWeight
- ag.groupWeight = ag.sumFlatWeight / uint64(l+1)
- ag.nodeSelect.Update(ag.nodes[l])
-}
-
-// update updates the selection weight of the node queue inside the address group.
-// It is the caller's responsibility to update the group's selection weight in the
-// address selector.
-func (ag *addressGroup) update(nq *nodeQueue, weight uint64) {
- if nq.groupIndex == -1 || nq.groupIndex >= len(ag.nodes) || ag.nodes[nq.groupIndex] != nq {
- panic("updated node queue is not in this address group")
- }
- ag.sumFlatWeight += weight - nq.flatWeight
- nq.flatWeight = weight
- ag.groupWeight = ag.sumFlatWeight / uint64(len(ag.nodes))
- ag.nodeSelect.Update(nq)
-}
-
-// remove removes the node queue from the address group. It is the caller's responsibility
-// to remove the address group from the address map if it is empty.
-func (ag *addressGroup) remove(nq *nodeQueue) {
- if nq.groupIndex == -1 || nq.groupIndex >= len(ag.nodes) || ag.nodes[nq.groupIndex] != nq {
- panic("removed node queue is not in this address group")
- }
-
- l := len(ag.nodes) - 1
- if nq.groupIndex != l {
- ag.nodes[nq.groupIndex] = ag.nodes[l]
- ag.nodes[nq.groupIndex].groupIndex = nq.groupIndex
- }
- nq.groupIndex = -1
- ag.nodes = ag.nodes[:l]
- ag.sumFlatWeight -= nq.flatWeight
- if l >= 1 {
- ag.groupWeight = ag.sumFlatWeight / uint64(l)
- } else {
- ag.groupWeight = 0
- }
- ag.nodeSelect.Remove(nq)
-}
-
-// choose selects one of the node queues belonging to the address group
-func (ag *addressGroup) choose() *nodeQueue {
- return ag.nodeSelect.Choose().(*nodeQueue)
-}
-
-// NewLimiter creates a new Limiter
-func NewLimiter(sumCostLimit uint) *Limiter {
- l := &Limiter{
- addressSelect: NewWeightedRandomSelect(func(item interface{}) uint64 { return item.(*addressGroup).groupWeight }),
- valueSelect: NewWeightedRandomSelect(func(item interface{}) uint64 { return item.(*nodeQueue).valueWeight }),
- nodes: make(map[enode.ID]*nodeQueue),
- addresses: make(map[string]*addressGroup),
- sumCostLimit: sumCostLimit,
- }
- l.cond = sync.NewCond(&l.lock)
- go l.processLoop()
- return l
-}
-
-// selectionWeights calculates the selection weights of a node for both the address and
-// the value selector. The selection weight depends on the next request cost or the
-// summed cost of recently dropped requests.
-func (l *Limiter) selectionWeights(reqCost uint, value float64) (flatWeight, valueWeight uint64) {
- if value > l.maxValue {
- l.maxValue = value
- }
- if value > 0 {
- // normalize value to <= 1
- value /= l.maxValue
- }
- if reqCost > l.maxCost {
- l.maxCost = reqCost
- }
- relCost := float64(reqCost) / float64(l.maxCost)
- var f float64
- if relCost <= 0.001 {
- f = 1
- } else {
- f = 0.001 / relCost
- }
- f *= maxSelectionWeight
- flatWeight, valueWeight = uint64(f), uint64(f*value)
- if flatWeight == 0 {
- flatWeight = 1
- }
- return
-}
-
-// Add adds a new request to the node queue belonging to the given id. Value belongs
-// to the requesting node. A higher value gives the request a higher chance of being
-// served quickly in case of heavy load or a DDoS attack. Cost is a rough estimate
-// of the serving cost of the request. A lower cost also gives the request a
-// better chance.
-func (l *Limiter) Add(id enode.ID, address string, value float64, reqCost uint) chan chan struct{} {
- l.lock.Lock()
- defer l.lock.Unlock()
-
- process := make(chan chan struct{}, 1)
- if l.quit {
- close(process)
- return process
- }
- if reqCost == 0 {
- reqCost = 1
- }
- if nq, ok := l.nodes[id]; ok {
- if nq.queue != nil {
- nq.queue = append(nq.queue, request{process, reqCost})
- nq.sumCost += reqCost
- nq.value = value
- if address != nq.address {
- // known id sending request from a new address, move to different address group
- l.removeFromGroup(nq)
- l.addToGroup(nq, address)
- }
- } else {
- // already waiting on a penalty, just add to the penalty cost and drop the request
- nq.penaltyCost += reqCost
- l.update(nq)
- close(process)
- return process
- }
- } else {
- nq := &nodeQueue{
- queue: []request{{process, reqCost}},
- id: id,
- value: value,
- sumCost: reqCost,
- groupIndex: -1,
- }
- nq.flatWeight, nq.valueWeight = l.selectionWeights(reqCost, value)
- if len(l.nodes) == 0 {
- l.cond.Signal()
- }
- l.nodes[id] = nq
- if nq.valueWeight != 0 {
- l.valueSelect.Update(nq)
- }
- l.addToGroup(nq, address)
- }
- l.sumCost += reqCost
- if l.sumCost > l.sumCostLimit {
- l.dropRequests()
- }
- return process
-}
-
-// update updates the selection weights of the node queue
-func (l *Limiter) update(nq *nodeQueue) {
- var cost uint
- if nq.queue != nil {
- cost = nq.queue[0].cost
- } else {
- cost = nq.penaltyCost
- }
- flatWeight, valueWeight := l.selectionWeights(cost, nq.value)
- ag := l.addresses[nq.address]
- ag.update(nq, flatWeight)
- l.addressSelect.Update(ag)
- nq.valueWeight = valueWeight
- l.valueSelect.Update(nq)
-}
-
-// addToGroup adds the node queue to the given address group. The group is created if
-// it does not exist yet.
-func (l *Limiter) addToGroup(nq *nodeQueue, address string) {
- nq.address = address
- ag := l.addresses[address]
- if ag == nil {
- ag = &addressGroup{nodeSelect: NewWeightedRandomSelect(flatWeight)}
- l.addresses[address] = ag
- }
- ag.add(nq)
- l.addressSelect.Update(ag)
-}
-
-// removeFromGroup removes the node queue from its address group
-func (l *Limiter) removeFromGroup(nq *nodeQueue) {
- ag := l.addresses[nq.address]
- ag.remove(nq)
- if len(ag.nodes) == 0 {
- delete(l.addresses, nq.address)
- }
- l.addressSelect.Update(ag)
-}
-
-// remove removes the node queue from its address group, the nodes map and the value
-// selector
-func (l *Limiter) remove(nq *nodeQueue) {
- l.removeFromGroup(nq)
- if nq.valueWeight != 0 {
- l.valueSelect.Remove(nq)
- }
- delete(l.nodes, nq.id)
-}
-
-// choose selects the next node queue to process.
-func (l *Limiter) choose() *nodeQueue {
- if l.valueSelect.IsEmpty() || l.selectAddressNext {
- if ag, ok := l.addressSelect.Choose().(*addressGroup); ok {
- l.selectAddressNext = false
- return ag.choose()
- }
- }
- nq, _ := l.valueSelect.Choose().(*nodeQueue)
- l.selectAddressNext = true
- return nq
-}
-
-// processLoop processes requests sequentially
-func (l *Limiter) processLoop() {
- l.lock.Lock()
- defer l.lock.Unlock()
-
- for {
- if l.quit {
- for _, nq := range l.nodes {
- for _, request := range nq.queue {
- close(request.process)
- }
- }
- return
- }
- nq := l.choose()
- if nq == nil {
- l.cond.Wait()
- continue
- }
- if nq.queue != nil {
- request := nq.queue[0]
- nq.queue = nq.queue[1:]
- nq.sumCost -= request.cost
- l.sumCost -= request.cost
- l.lock.Unlock()
- ch := make(chan struct{})
- request.process <- ch
- <-ch
- l.lock.Lock()
- if len(nq.queue) > 0 {
- l.update(nq)
- } else {
- l.remove(nq)
- }
- } else {
- // penalized queue removed, next request will be added to a clean queue
- l.remove(nq)
- }
- }
-}
-
-// Stop stops the processing loop. All queued and future requests are rejected.
-func (l *Limiter) Stop() {
- l.lock.Lock()
- defer l.lock.Unlock()
-
- l.quit = true
- l.cond.Signal()
-}
-
-type dropListItem struct {
- nq *nodeQueue
- priority float64
-}
-
-// dropRequests selects the nodes with the highest queued request cost to selection
-// weight ratio and drops their queued request. The empty node queues stay in the
-// selectors with a low selection weight in order to penalize these nodes.
-func (l *Limiter) dropRequests() {
- var (
- sumValue float64
- list []dropListItem
- )
- for _, nq := range l.nodes {
- sumValue += nq.value
- }
- for _, nq := range l.nodes {
- if nq.sumCost == 0 {
- continue
- }
- w := 1 / float64(len(l.addresses)*len(l.addresses[nq.address].nodes))
- if sumValue > 0 {
- w += nq.value / sumValue
- }
- list = append(list, dropListItem{
- nq: nq,
- priority: w / float64(nq.sumCost),
- })
- }
- slices.SortFunc(list, func(a, b dropListItem) int {
- if a.priority < b.priority {
- return -1
- }
- if a.priority < b.priority {
- return 1
- }
- return 0
- })
- for _, item := range list {
- for _, request := range item.nq.queue {
- close(request.process)
- }
- // make the queue penalized; no more requests are accepted until the node is
- // selected based on the penalty cost which is the cumulative cost of all dropped
- // requests. This ensures that sending excess requests is always penalized
- // and incentivizes the sender to stop for a while if no replies are received.
- item.nq.queue = nil
- item.nq.penaltyCost = item.nq.sumCost
- l.sumCost -= item.nq.sumCost // penalty costs are not counted in sumCost
- item.nq.sumCost = 0
- l.update(item.nq)
- if l.sumCost <= l.sumCostLimit/2 {
- return
- }
- }
-}
diff --git a/les/utils/limiter_test.go b/les/utils/limiter_test.go
deleted file mode 100644
index c031b21de58b..000000000000
--- a/les/utils/limiter_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2021 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 .
-
-package utils
-
-import (
- "crypto/rand"
- "testing"
-
- "github.com/ethereum/go-ethereum/p2p/enode"
-)
-
-const (
- ltTolerance = 0.03
- ltRounds = 7
-)
-
-type (
- ltNode struct {
- addr, id int
- value, exp float64
- cost uint
- reqRate float64
- reqMax, runCount int
- lastTotalCost uint
-
- served, dropped int
- }
-
- ltResult struct {
- node *ltNode
- ch chan struct{}
- }
-
- limTest struct {
- limiter *Limiter
- results chan ltResult
- runCount int
- expCost, totalCost uint
- }
-)
-
-func (lt *limTest) request(n *ltNode) {
- var (
- address string
- id enode.ID
- )
- if n.addr >= 0 {
- address = string([]byte{byte(n.addr)})
- } else {
- var b [32]byte
- rand.Read(b[:])
- address = string(b[:])
- }
- if n.id >= 0 {
- id = enode.ID{byte(n.id)}
- } else {
- rand.Read(id[:])
- }
- lt.runCount++
- n.runCount++
- cch := lt.limiter.Add(id, address, n.value, n.cost)
- go func() {
- lt.results <- ltResult{n, <-cch}
- }()
-}
-
-func (lt *limTest) moreRequests(n *ltNode) {
- maxStart := int(float64(lt.totalCost-n.lastTotalCost) * n.reqRate)
- if maxStart != 0 {
- n.lastTotalCost = lt.totalCost
- }
- for n.reqMax > n.runCount && maxStart > 0 {
- lt.request(n)
- maxStart--
- }
-}
-
-func (lt *limTest) process() {
- res := <-lt.results
- lt.runCount--
- res.node.runCount--
- if res.ch != nil {
- res.node.served++
- if res.node.exp != 0 {
- lt.expCost += res.node.cost
- }
- lt.totalCost += res.node.cost
- close(res.ch)
- } else {
- res.node.dropped++
- }
-}
-
-func TestLimiter(t *testing.T) {
- limTests := [][]*ltNode{
- { // one id from an individual address and two ids from a shared address
- {addr: 0, id: 0, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.5},
- {addr: 1, id: 1, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25},
- {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25},
- },
- { // varying request costs
- {addr: 0, id: 0, value: 0, cost: 10, reqRate: 0.2, reqMax: 1, exp: 0.5},
- {addr: 1, id: 1, value: 0, cost: 3, reqRate: 0.5, reqMax: 1, exp: 0.25},
- {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25},
- },
- { // different request rate
- {addr: 0, id: 0, value: 0, cost: 1, reqRate: 2, reqMax: 2, exp: 0.5},
- {addr: 1, id: 1, value: 0, cost: 1, reqRate: 10, reqMax: 10, exp: 0.25},
- {addr: 1, id: 2, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25},
- },
- { // adding value
- {addr: 0, id: 0, value: 3, cost: 1, reqRate: 1, reqMax: 1, exp: (0.5 + 0.3) / 2},
- {addr: 1, id: 1, value: 0, cost: 1, reqRate: 1, reqMax: 1, exp: 0.25 / 2},
- {addr: 1, id: 2, value: 7, cost: 1, reqRate: 1, reqMax: 1, exp: (0.25 + 0.7) / 2},
- },
- { // DoS attack from a single address with a single id
- {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 3, id: 3, value: 0, cost: 1, reqRate: 10, reqMax: 1000000000, exp: 0},
- },
- { // DoS attack from a single address with different ids
- {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 3, id: -1, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0},
- },
- { // DDoS attack from different addresses with a single id
- {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: -1, id: 3, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0},
- },
- { // DDoS attack from different addresses with different ids
- {addr: 0, id: 0, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 1, id: 1, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: 2, id: 2, value: 1, cost: 1, reqRate: 1, reqMax: 1, exp: 0.3333},
- {addr: -1, id: -1, value: 0, cost: 1, reqRate: 1, reqMax: 1000000000, exp: 0},
- },
- }
-
- lt := &limTest{
- limiter: NewLimiter(100),
- results: make(chan ltResult),
- }
- for _, test := range limTests {
- lt.expCost, lt.totalCost = 0, 0
- iterCount := 10000
- for j := 0; j < ltRounds; j++ {
- // try to reach expected target range in multiple rounds with increasing iteration counts
- last := j == ltRounds-1
- for _, n := range test {
- lt.request(n)
- }
- for i := 0; i < iterCount; i++ {
- lt.process()
- for _, n := range test {
- lt.moreRequests(n)
- }
- }
- for lt.runCount > 0 {
- lt.process()
- }
- if spamRatio := 1 - float64(lt.expCost)/float64(lt.totalCost); spamRatio > 0.5*(1+ltTolerance) {
- t.Errorf("Spam ratio too high (%f)", spamRatio)
- }
- fail, success := false, true
- for _, n := range test {
- if n.exp != 0 {
- if n.dropped > 0 {
- t.Errorf("Dropped %d requests of non-spam node", n.dropped)
- fail = true
- }
- r := float64(n.served) * float64(n.cost) / float64(lt.expCost)
- if r < n.exp*(1-ltTolerance) || r > n.exp*(1+ltTolerance) {
- if last {
- // print error only if the target is still not reached in the last round
- t.Errorf("Request ratio (%f) does not match expected value (%f)", r, n.exp)
- }
- success = false
- }
- }
- }
- if fail || success {
- break
- }
- // neither failed nor succeeded; try more iterations to reach probability targets
- iterCount *= 2
- }
- }
- lt.limiter.Stop()
-}
diff --git a/les/utils/timeutils.go b/les/utils/timeutils.go
deleted file mode 100644
index 62a4285d1524..000000000000
--- a/les/utils/timeutils.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-type UpdateTimer struct {
- clock mclock.Clock
- lock sync.Mutex
- last mclock.AbsTime
- threshold time.Duration
-}
-
-func NewUpdateTimer(clock mclock.Clock, threshold time.Duration) *UpdateTimer {
- // We don't accept the update threshold less than 0.
- if threshold < 0 {
- return nil
- }
- // Don't panic for lazy users
- if clock == nil {
- clock = mclock.System{}
- }
- return &UpdateTimer{
- clock: clock,
- last: clock.Now(),
- threshold: threshold,
- }
-}
-
-func (t *UpdateTimer) Update(callback func(diff time.Duration) bool) bool {
- return t.UpdateAt(t.clock.Now(), callback)
-}
-
-func (t *UpdateTimer) UpdateAt(at mclock.AbsTime, callback func(diff time.Duration) bool) bool {
- t.lock.Lock()
- defer t.lock.Unlock()
-
- diff := time.Duration(at - t.last)
- if diff < 0 {
- diff = 0
- }
- if diff < t.threshold {
- return false
- }
- if callback(diff) {
- t.last = at
- return true
- }
- return false
-}
diff --git a/les/utils/timeutils_test.go b/les/utils/timeutils_test.go
deleted file mode 100644
index b219d0439dcb..000000000000
--- a/les/utils/timeutils_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
-)
-
-func TestUpdateTimer(t *testing.T) {
- timer := NewUpdateTimer(mclock.System{}, -1)
- if timer != nil {
- t.Fatalf("Create update timer with negative threshold")
- }
- sim := &mclock.Simulated{}
- timer = NewUpdateTimer(sim, time.Second)
- if updated := timer.Update(func(diff time.Duration) bool { return true }); updated {
- t.Fatalf("Update the clock without reaching the threshold")
- }
- sim.Run(time.Second)
- if updated := timer.Update(func(diff time.Duration) bool { return true }); !updated {
- t.Fatalf("Doesn't update the clock when reaching the threshold")
- }
- if updated := timer.UpdateAt(sim.Now().Add(time.Second), func(diff time.Duration) bool { return true }); !updated {
- t.Fatalf("Doesn't update the clock when reaching the threshold")
- }
- timer = NewUpdateTimer(sim, 0)
- if updated := timer.Update(func(diff time.Duration) bool { return true }); !updated {
- t.Fatalf("Doesn't update the clock without threshold limitaion")
- }
-}
diff --git a/les/utils/weighted_select.go b/les/utils/weighted_select.go
deleted file mode 100644
index 486b00820a23..000000000000
--- a/les/utils/weighted_select.go
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "math"
- "math/rand"
-
- "github.com/ethereum/go-ethereum/log"
-)
-
-type (
- // WeightedRandomSelect is capable of weighted random selection from a set of items
- WeightedRandomSelect struct {
- root *wrsNode
- idx map[WrsItem]int
- wfn WeightFn
- }
- WrsItem interface{}
- WeightFn func(interface{}) uint64
-)
-
-// NewWeightedRandomSelect returns a new WeightedRandomSelect structure
-func NewWeightedRandomSelect(wfn WeightFn) *WeightedRandomSelect {
- return &WeightedRandomSelect{root: &wrsNode{maxItems: wrsBranches}, idx: make(map[WrsItem]int), wfn: wfn}
-}
-
-// Update updates an item's weight, adds it if it was non-existent or removes it if
-// the new weight is zero. Note that explicitly updating decreasing weights is not necessary.
-func (w *WeightedRandomSelect) Update(item WrsItem) {
- w.setWeight(item, w.wfn(item))
-}
-
-// Remove removes an item from the set
-func (w *WeightedRandomSelect) Remove(item WrsItem) {
- w.setWeight(item, 0)
-}
-
-// IsEmpty returns true if the set is empty
-func (w *WeightedRandomSelect) IsEmpty() bool {
- return w.root.sumCost == 0
-}
-
-// setWeight sets an item's weight to a specific value (removes it if zero)
-func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) {
- if weight > math.MaxInt64-w.root.sumCost {
- // old weight is still included in sumCost, remove and check again
- w.setWeight(item, 0)
- if weight > math.MaxInt64-w.root.sumCost {
- log.Error("WeightedRandomSelect overflow", "sumCost", w.root.sumCost, "new weight", weight)
- weight = math.MaxInt64 - w.root.sumCost
- }
- }
- idx, ok := w.idx[item]
- if ok {
- w.root.setWeight(idx, weight)
- if weight == 0 {
- delete(w.idx, item)
- }
- } else {
- if weight != 0 {
- if w.root.itemCnt == w.root.maxItems {
- // add a new level
- newRoot := &wrsNode{sumCost: w.root.sumCost, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches}
- newRoot.items[0] = w.root
- newRoot.weights[0] = w.root.sumCost
- w.root = newRoot
- }
- w.idx[item] = w.root.insert(item, weight)
- }
- }
-}
-
-// Choose randomly selects an item from the set, with a chance proportional to its
-// current weight. If the weight of the chosen element has been decreased since the
-// last stored value, returns it with a newWeight/oldWeight chance, otherwise just
-// updates its weight and selects another one
-func (w *WeightedRandomSelect) Choose() WrsItem {
- for {
- if w.root.sumCost == 0 {
- return nil
- }
- val := uint64(rand.Int63n(int64(w.root.sumCost)))
- choice, lastWeight := w.root.choose(val)
- weight := w.wfn(choice)
- if weight != lastWeight {
- w.setWeight(choice, weight)
- }
- if weight >= lastWeight || uint64(rand.Int63n(int64(lastWeight))) < weight {
- return choice
- }
- }
-}
-
-const wrsBranches = 8 // max number of branches in the wrsNode tree
-
-// wrsNode is a node of a tree structure that can store WrsItems or further wrsNodes.
-type wrsNode struct {
- items [wrsBranches]interface{}
- weights [wrsBranches]uint64
- sumCost uint64
- level, itemCnt, maxItems int
-}
-
-// insert recursively inserts a new item to the tree and returns the item index
-func (n *wrsNode) insert(item WrsItem, weight uint64) int {
- branch := 0
- for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) {
- branch++
- if branch == wrsBranches {
- panic(nil)
- }
- }
- n.itemCnt++
- n.sumCost += weight
- n.weights[branch] += weight
- if n.level == 0 {
- n.items[branch] = item
- return branch
- }
- var subNode *wrsNode
- if n.items[branch] == nil {
- subNode = &wrsNode{maxItems: n.maxItems / wrsBranches, level: n.level - 1}
- n.items[branch] = subNode
- } else {
- subNode = n.items[branch].(*wrsNode)
- }
- subIdx := subNode.insert(item, weight)
- return subNode.maxItems*branch + subIdx
-}
-
-// setWeight updates the weight of a certain item (which should exist) and returns
-// the change of the last weight value stored in the tree
-func (n *wrsNode) setWeight(idx int, weight uint64) uint64 {
- if n.level == 0 {
- oldWeight := n.weights[idx]
- n.weights[idx] = weight
- diff := weight - oldWeight
- n.sumCost += diff
- if weight == 0 {
- n.items[idx] = nil
- n.itemCnt--
- }
- return diff
- }
- branchItems := n.maxItems / wrsBranches
- branch := idx / branchItems
- diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight)
- n.weights[branch] += diff
- n.sumCost += diff
- if weight == 0 {
- n.itemCnt--
- }
- return diff
-}
-
-// choose recursively selects an item from the tree and returns it along with its weight
-func (n *wrsNode) choose(val uint64) (WrsItem, uint64) {
- for i, w := range n.weights {
- if val < w {
- if n.level == 0 {
- return n.items[i].(WrsItem), n.weights[i]
- }
- return n.items[i].(*wrsNode).choose(val)
- }
- val -= w
- }
- panic(nil)
-}
diff --git a/les/utils/weighted_select_test.go b/les/utils/weighted_select_test.go
deleted file mode 100644
index 3e1c0ad9873a..000000000000
--- a/les/utils/weighted_select_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package utils
-
-import (
- "math/rand"
- "testing"
-)
-
-type testWrsItem struct {
- idx int
- widx *int
-}
-
-func testWeight(i interface{}) uint64 {
- t := i.(*testWrsItem)
- w := *t.widx
- if w == -1 || w == t.idx {
- return uint64(t.idx + 1)
- }
- return 0
-}
-
-func TestWeightedRandomSelect(t *testing.T) {
- testFn := func(cnt int) {
- s := NewWeightedRandomSelect(testWeight)
- w := -1
- list := make([]testWrsItem, cnt)
- for i := range list {
- list[i] = testWrsItem{idx: i, widx: &w}
- s.Update(&list[i])
- }
- w = rand.Intn(cnt)
- c := s.Choose()
- if c == nil {
- t.Errorf("expected item, got nil")
- } else {
- if c.(*testWrsItem).idx != w {
- t.Errorf("expected another item")
- }
- }
- w = -2
- if s.Choose() != nil {
- t.Errorf("expected nil, got item")
- }
- }
- testFn(1)
- testFn(10)
- testFn(100)
- testFn(1000)
- testFn(10000)
- testFn(100000)
- testFn(1000000)
-}
diff --git a/les/vflux/client/api.go b/les/vflux/client/api.go
deleted file mode 100644
index 135273ef96ec..000000000000
--- a/les/vflux/client/api.go
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
-)
-
-// PrivateClientAPI implements the vflux client side API
-type PrivateClientAPI struct {
- vt *ValueTracker
-}
-
-// NewPrivateClientAPI creates a PrivateClientAPI
-func NewPrivateClientAPI(vt *ValueTracker) *PrivateClientAPI {
- return &PrivateClientAPI{vt}
-}
-
-// parseNodeStr converts either an enode address or a plain hex node id to enode.ID
-func parseNodeStr(nodeStr string) (enode.ID, error) {
- if id, err := enode.ParseID(nodeStr); err == nil {
- return id, nil
- }
- if node, err := enode.Parse(enode.ValidSchemes, nodeStr); err == nil {
- return node.ID(), nil
- } else {
- return enode.ID{}, err
- }
-}
-
-// RequestStats returns the current contents of the reference request basket, with
-// request values meaning average per request rather than total.
-func (api *PrivateClientAPI) RequestStats() []RequestStatsItem {
- return api.vt.RequestStats()
-}
-
-// Distribution returns a distribution as a series of (X, Y) chart coordinates,
-// where the X axis is the response time in seconds while the Y axis is the amount of
-// service value received with a response time close to the X coordinate.
-// The distribution is optionally normalized to a sum of 1.
-// If nodeStr == "" then the global distribution is returned, otherwise the individual
-// distribution of the specified server node.
-func (api *PrivateClientAPI) Distribution(nodeStr string, normalized bool) (RtDistribution, error) {
- var expFactor utils.ExpirationFactor
- if !normalized {
- expFactor = utils.ExpFactor(api.vt.StatsExpirer().LogOffset(mclock.Now()))
- }
- if nodeStr == "" {
- return api.vt.RtStats().Distribution(normalized, expFactor), nil
- }
- if id, err := parseNodeStr(nodeStr); err == nil {
- return api.vt.GetNode(id).RtStats().Distribution(normalized, expFactor), nil
- } else {
- return RtDistribution{}, err
- }
-}
-
-// Timeout suggests a timeout value based on either the global distribution or the
-// distribution of the specified node. The parameter is the desired rate of timeouts
-// assuming a similar distribution in the future.
-// Note that the actual timeout should have a sensible minimum bound so that operating
-// under ideal working conditions for a long time (for example, using a local server
-// with very low response times) will not make it very hard for the system to accommodate
-// longer response times in the future.
-func (api *PrivateClientAPI) Timeout(nodeStr string, failRate float64) (float64, error) {
- if nodeStr == "" {
- return float64(api.vt.RtStats().Timeout(failRate)) / float64(time.Second), nil
- }
- if id, err := parseNodeStr(nodeStr); err == nil {
- return float64(api.vt.GetNode(id).RtStats().Timeout(failRate)) / float64(time.Second), nil
- } else {
- return 0, err
- }
-}
-
-// Value calculates the total service value provided either globally or by the specified
-// server node, using a weight function based on the given timeout.
-func (api *PrivateClientAPI) Value(nodeStr string, timeout float64) (float64, error) {
- wt := TimeoutWeights(time.Duration(timeout * float64(time.Second)))
- expFactor := utils.ExpFactor(api.vt.StatsExpirer().LogOffset(mclock.Now()))
- if nodeStr == "" {
- return api.vt.RtStats().Value(wt, expFactor), nil
- }
- if id, err := parseNodeStr(nodeStr); err == nil {
- return api.vt.GetNode(id).RtStats().Value(wt, expFactor), nil
- } else {
- return 0, err
- }
-}
diff --git a/les/vflux/client/fillset.go b/les/vflux/client/fillset.go
deleted file mode 100644
index 0da850bcac44..000000000000
--- a/les/vflux/client/fillset.go
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-// FillSet tries to read nodes from an input iterator and add them to a node set by
-// setting the specified node state flag(s) until the size of the set reaches the target.
-// Note that other mechanisms (like other FillSet instances reading from different inputs)
-// can also set the same flag(s) and FillSet will always care about the total number of
-// nodes having those flags.
-type FillSet struct {
- lock sync.Mutex
- cond *sync.Cond
- ns *nodestate.NodeStateMachine
- input enode.Iterator
- closed bool
- flags nodestate.Flags
- count, target int
-}
-
-// NewFillSet creates a new FillSet
-func NewFillSet(ns *nodestate.NodeStateMachine, input enode.Iterator, flags nodestate.Flags) *FillSet {
- fs := &FillSet{
- ns: ns,
- input: input,
- flags: flags,
- }
- fs.cond = sync.NewCond(&fs.lock)
-
- ns.SubscribeState(flags, func(n *enode.Node, oldState, newState nodestate.Flags) {
- fs.lock.Lock()
- if oldState.Equals(flags) {
- fs.count--
- }
- if newState.Equals(flags) {
- fs.count++
- }
- if fs.target > fs.count {
- fs.cond.Signal()
- }
- fs.lock.Unlock()
- })
-
- go fs.readLoop()
- return fs
-}
-
-// readLoop keeps reading nodes from the input and setting the specified flags for them
-// whenever the node set size is under the current target
-func (fs *FillSet) readLoop() {
- for {
- fs.lock.Lock()
- for fs.target <= fs.count && !fs.closed {
- fs.cond.Wait()
- }
-
- fs.lock.Unlock()
- if !fs.input.Next() {
- return
- }
- fs.ns.SetState(fs.input.Node(), fs.flags, nodestate.Flags{}, 0)
- }
-}
-
-// SetTarget sets the current target for node set size. If the previous target was not
-// reached and FillSet was still waiting for the next node from the input then the next
-// incoming node will be added to the set regardless of the target. This ensures that
-// all nodes coming from the input are eventually added to the set.
-func (fs *FillSet) SetTarget(target int) {
- fs.lock.Lock()
- defer fs.lock.Unlock()
-
- fs.target = target
- if fs.target > fs.count {
- fs.cond.Signal()
- }
-}
-
-// Close shuts FillSet down and closes the input iterator
-func (fs *FillSet) Close() {
- fs.lock.Lock()
- defer fs.lock.Unlock()
-
- fs.closed = true
- fs.input.Close()
- fs.cond.Signal()
-}
diff --git a/les/vflux/client/fillset_test.go b/les/vflux/client/fillset_test.go
deleted file mode 100644
index 9a5a2a98a857..000000000000
--- a/les/vflux/client/fillset_test.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "crypto/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-type testIter struct {
- waitCh chan struct{}
- nodeCh chan *enode.Node
- node *enode.Node
-}
-
-func (i *testIter) Next() bool {
- if _, ok := <-i.waitCh; !ok {
- return false
- }
- i.node = <-i.nodeCh
- return true
-}
-
-func (i *testIter) Node() *enode.Node {
- return i.node
-}
-
-func (i *testIter) Close() {
- close(i.waitCh)
-}
-
-func (i *testIter) push() {
- var id enode.ID
- rand.Read(id[:])
- i.nodeCh <- enode.SignNull(new(enr.Record), id)
-}
-
-func (i *testIter) waiting(timeout time.Duration) bool {
- select {
- case i.waitCh <- struct{}{}:
- return true
- case <-time.After(timeout):
- return false
- }
-}
-
-func TestFillSet(t *testing.T) {
- t.Parallel()
-
- ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
- iter := &testIter{
- waitCh: make(chan struct{}),
- nodeCh: make(chan *enode.Node),
- }
- fs := NewFillSet(ns, iter, sfTest1)
- ns.Start()
-
- expWaiting := func(i int, push bool) {
- for ; i > 0; i-- {
- if !iter.waiting(time.Second * 10) {
- t.Fatalf("FillSet not waiting for new nodes")
- }
- if push {
- iter.push()
- }
- }
- }
-
- expNotWaiting := func() {
- if iter.waiting(time.Millisecond * 100) {
- t.Fatalf("FillSet unexpectedly waiting for new nodes")
- }
- }
-
- expNotWaiting()
- fs.SetTarget(3)
- expWaiting(3, true)
- expNotWaiting()
- fs.SetTarget(100)
- expWaiting(2, true)
- expWaiting(1, false)
- // lower the target before the previous one has been filled up
- fs.SetTarget(0)
- iter.push()
- expNotWaiting()
- fs.SetTarget(10)
- expWaiting(4, true)
- expNotWaiting()
- // remove all previously set flags
- ns.ForEach(sfTest1, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) {
- ns.SetState(node, nodestate.Flags{}, sfTest1, 0)
- })
- // now expect FillSet to fill the set up again with 10 new nodes
- expWaiting(10, true)
- expNotWaiting()
-
- fs.Close()
- ns.Stop()
-}
diff --git a/les/vflux/client/queueiterator.go b/les/vflux/client/queueiterator.go
deleted file mode 100644
index ad3f8df5bbd1..000000000000
--- a/les/vflux/client/queueiterator.go
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-// QueueIterator returns nodes from the specified selectable set in the same order as
-// they entered the set.
-type QueueIterator struct {
- lock sync.Mutex
- cond *sync.Cond
-
- ns *nodestate.NodeStateMachine
- queue []*enode.Node
- nextNode *enode.Node
- waitCallback func(bool)
- fifo, closed bool
-}
-
-// NewQueueIterator creates a new QueueIterator. Nodes are selectable if they have all the required
-// and none of the disabled flags set. When a node is selected the selectedFlag is set which also
-// disables further selectability until it is removed or times out.
-func NewQueueIterator(ns *nodestate.NodeStateMachine, requireFlags, disableFlags nodestate.Flags, fifo bool, waitCallback func(bool)) *QueueIterator {
- qi := &QueueIterator{
- ns: ns,
- fifo: fifo,
- waitCallback: waitCallback,
- }
- qi.cond = sync.NewCond(&qi.lock)
-
- ns.SubscribeState(requireFlags.Or(disableFlags), func(n *enode.Node, oldState, newState nodestate.Flags) {
- oldMatch := oldState.HasAll(requireFlags) && oldState.HasNone(disableFlags)
- newMatch := newState.HasAll(requireFlags) && newState.HasNone(disableFlags)
- if newMatch == oldMatch {
- return
- }
-
- qi.lock.Lock()
- defer qi.lock.Unlock()
-
- if newMatch {
- qi.queue = append(qi.queue, n)
- } else {
- id := n.ID()
- for i, qn := range qi.queue {
- if qn.ID() == id {
- copy(qi.queue[i:len(qi.queue)-1], qi.queue[i+1:])
- qi.queue = qi.queue[:len(qi.queue)-1]
- break
- }
- }
- }
- qi.cond.Signal()
- })
- return qi
-}
-
-// Next moves to the next selectable node.
-func (qi *QueueIterator) Next() bool {
- qi.lock.Lock()
- if !qi.closed && len(qi.queue) == 0 {
- if qi.waitCallback != nil {
- qi.waitCallback(true)
- }
- for !qi.closed && len(qi.queue) == 0 {
- qi.cond.Wait()
- }
- if qi.waitCallback != nil {
- qi.waitCallback(false)
- }
- }
- if qi.closed {
- qi.nextNode = nil
- qi.lock.Unlock()
- return false
- }
- // Move to the next node in queue.
- if qi.fifo {
- qi.nextNode = qi.queue[0]
- copy(qi.queue[:len(qi.queue)-1], qi.queue[1:])
- qi.queue = qi.queue[:len(qi.queue)-1]
- } else {
- qi.nextNode = qi.queue[len(qi.queue)-1]
- qi.queue = qi.queue[:len(qi.queue)-1]
- }
- qi.lock.Unlock()
- return true
-}
-
-// Close ends the iterator.
-func (qi *QueueIterator) Close() {
- qi.lock.Lock()
- qi.closed = true
- qi.lock.Unlock()
- qi.cond.Signal()
-}
-
-// Node returns the current node.
-func (qi *QueueIterator) Node() *enode.Node {
- qi.lock.Lock()
- defer qi.lock.Unlock()
-
- return qi.nextNode
-}
diff --git a/les/vflux/client/queueiterator_test.go b/les/vflux/client/queueiterator_test.go
deleted file mode 100644
index c7cb6490824d..000000000000
--- a/les/vflux/client/queueiterator_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-func testNode(i int) *enode.Node {
- return enode.SignNull(new(enr.Record), testNodeID(i))
-}
-
-func TestQueueIteratorFIFO(t *testing.T) {
- t.Parallel()
-
- testQueueIterator(t, true)
-}
-
-func TestQueueIteratorLIFO(t *testing.T) {
- t.Parallel()
-
- testQueueIterator(t, false)
-}
-
-func testQueueIterator(t *testing.T, fifo bool) {
- ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
- qi := NewQueueIterator(ns, sfTest2, sfTest3.Or(sfTest4), fifo, nil)
- ns.Start()
- for i := 1; i <= iterTestNodeCount; i++ {
- ns.SetState(testNode(i), sfTest1, nodestate.Flags{}, 0)
- }
- next := func() int {
- ch := make(chan struct{})
- go func() {
- qi.Next()
- close(ch)
- }()
- select {
- case <-ch:
- case <-time.After(time.Second * 5):
- t.Fatalf("Iterator.Next() timeout")
- }
- node := qi.Node()
- ns.SetState(node, sfTest4, nodestate.Flags{}, 0)
- return testNodeIndex(node.ID())
- }
- exp := func(i int) {
- n := next()
- if n != i {
- t.Errorf("Wrong item returned by iterator (expected %d, got %d)", i, n)
- }
- }
- explist := func(list []int) {
- for i := range list {
- if fifo {
- exp(list[i])
- } else {
- exp(list[len(list)-1-i])
- }
- }
- }
-
- ns.SetState(testNode(1), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(2), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(3), sfTest2, nodestate.Flags{}, 0)
- explist([]int{1, 2, 3})
- ns.SetState(testNode(4), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(5), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(6), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(5), sfTest3, nodestate.Flags{}, 0)
- explist([]int{4, 6})
- ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(2), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(2), sfTest3, nodestate.Flags{}, 0)
- ns.SetState(testNode(2), nodestate.Flags{}, sfTest3, 0)
- explist([]int{1, 3, 2})
- ns.Stop()
-}
diff --git a/les/vflux/client/requestbasket.go b/les/vflux/client/requestbasket.go
deleted file mode 100644
index 55d4b165dfd5..000000000000
--- a/les/vflux/client/requestbasket.go
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "io"
-
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const basketFactor = 1000000 // reference basket amount and value scale factor
-
-// referenceBasket keeps track of global request usage statistics and the usual prices
-// of each used request type relative to each other. The amounts in the basket are scaled
-// up by basketFactor because of the exponential expiration of long-term statistical data.
-// Values are scaled so that the sum of all amounts and the sum of all values are equal.
-//
-// reqValues represent the internal relative value estimates for each request type and are
-// calculated as value / amount. The average reqValue of all used requests is 1.
-// In other words: SUM(refBasket[type].amount * reqValue[type]) = SUM(refBasket[type].amount)
-type referenceBasket struct {
- basket requestBasket
- reqValues []float64 // contents are read only, new slice is created for each update
-}
-
-// serverBasket collects served request amount and value statistics for a single server.
-//
-// Values are gradually transferred to the global reference basket with a long time
-// constant so that each server basket represents long term usage and price statistics.
-// When the transferred part is added to the reference basket the values are scaled so
-// that their sum equals the total value calculated according to the previous reqValues.
-// The ratio of request values coming from the server basket represent the pricing of
-// the specific server and modify the global estimates with a weight proportional to
-// the amount of service provided by the server.
-type serverBasket struct {
- basket requestBasket
- rvFactor float64
-}
-
-type (
- // requestBasket holds amounts and values for each request type.
- // These values are exponentially expired (see utils.ExpiredValue). The power of 2
- // exponent is applicable to all values within.
- requestBasket struct {
- items []basketItem
- exp uint64
- }
- // basketItem holds amount and value for a single request type. Value is the total
- // relative request value accumulated for served requests while amount is the counter
- // for each request type.
- // Note that these values are both scaled up by basketFactor because of the exponential
- // expiration.
- basketItem struct {
- amount, value uint64
- }
-)
-
-// setExp sets the power of 2 exponent of the structure, scaling base values (the amounts
-// and request values) up or down if necessary.
-func (b *requestBasket) setExp(exp uint64) {
- if exp > b.exp {
- shift := exp - b.exp
- for i, item := range b.items {
- item.amount >>= shift
- item.value >>= shift
- b.items[i] = item
- }
- b.exp = exp
- }
- if exp < b.exp {
- shift := b.exp - exp
- for i, item := range b.items {
- item.amount <<= shift
- item.value <<= shift
- b.items[i] = item
- }
- b.exp = exp
- }
-}
-
-// init initializes a new server basket with the given service vector size (number of
-// different request types)
-func (s *serverBasket) init(size int) {
- if s.basket.items == nil {
- s.basket.items = make([]basketItem, size)
- }
-}
-
-// add adds the give type and amount of requests to the basket. Cost is calculated
-// according to the server's own cost table.
-func (s *serverBasket) add(reqType, reqAmount uint32, reqCost uint64, expFactor utils.ExpirationFactor) {
- s.basket.setExp(expFactor.Exp)
- i := &s.basket.items[reqType]
- i.amount += uint64(float64(uint64(reqAmount)*basketFactor) * expFactor.Factor)
- i.value += uint64(float64(reqCost) * s.rvFactor * expFactor.Factor)
-}
-
-// updateRvFactor updates the request value factor that scales server costs into the
-// local value dimensions.
-func (s *serverBasket) updateRvFactor(rvFactor float64) {
- s.rvFactor = rvFactor
-}
-
-// transfer decreases amounts and values in the basket with the given ratio and
-// moves the removed amounts into a new basket which is returned and can be added
-// to the global reference basket.
-func (s *serverBasket) transfer(ratio float64) requestBasket {
- res := requestBasket{
- items: make([]basketItem, len(s.basket.items)),
- exp: s.basket.exp,
- }
- for i, v := range s.basket.items {
- ta := uint64(float64(v.amount) * ratio)
- tv := uint64(float64(v.value) * ratio)
- if ta > v.amount {
- ta = v.amount
- }
- if tv > v.value {
- tv = v.value
- }
- s.basket.items[i] = basketItem{v.amount - ta, v.value - tv}
- res.items[i] = basketItem{ta, tv}
- }
- return res
-}
-
-// init initializes the reference basket with the given service vector size (number of
-// different request types)
-func (r *referenceBasket) init(size int) {
- r.reqValues = make([]float64, size)
- r.normalize()
- r.updateReqValues()
-}
-
-// add adds the transferred part of a server basket to the reference basket while scaling
-// value amounts so that their sum equals the total value calculated according to the
-// previous reqValues.
-func (r *referenceBasket) add(newBasket requestBasket) {
- r.basket.setExp(newBasket.exp)
- // scale newBasket to match service unit value
- var (
- totalCost uint64
- totalValue float64
- )
- for i, v := range newBasket.items {
- totalCost += v.value
- totalValue += float64(v.amount) * r.reqValues[i]
- }
- if totalCost > 0 {
- // add to reference with scaled values
- scaleValues := totalValue / float64(totalCost)
- for i, v := range newBasket.items {
- r.basket.items[i].amount += v.amount
- r.basket.items[i].value += uint64(float64(v.value) * scaleValues)
- }
- }
- r.updateReqValues()
-}
-
-// updateReqValues recalculates reqValues after adding transferred baskets. Note that
-// values should be normalized first.
-func (r *referenceBasket) updateReqValues() {
- r.reqValues = make([]float64, len(r.reqValues))
- for i, b := range r.basket.items {
- if b.amount > 0 {
- r.reqValues[i] = float64(b.value) / float64(b.amount)
- } else {
- r.reqValues[i] = 0
- }
- }
-}
-
-// normalize ensures that the sum of values equal the sum of amounts in the basket.
-func (r *referenceBasket) normalize() {
- var sumAmount, sumValue uint64
- for _, b := range r.basket.items {
- sumAmount += b.amount
- sumValue += b.value
- }
- add := float64(int64(sumAmount-sumValue)) / float64(sumValue)
- for i, b := range r.basket.items {
- b.value += uint64(int64(float64(b.value) * add))
- r.basket.items[i] = b
- }
-}
-
-// reqValueFactor calculates the request value factor applicable to the server with
-// the given announced request cost list
-func (r *referenceBasket) reqValueFactor(costList []uint64) float64 {
- var (
- totalCost float64
- totalValue uint64
- )
- for i, b := range r.basket.items {
- totalCost += float64(costList[i]) * float64(b.amount) // use floats to avoid overflow
- totalValue += b.value
- }
- if totalCost < 1 {
- return 0
- }
- return float64(totalValue) * basketFactor / totalCost
-}
-
-// EncodeRLP implements rlp.Encoder
-func (b *basketItem) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{b.amount, b.value})
-}
-
-// DecodeRLP implements rlp.Decoder
-func (b *basketItem) DecodeRLP(s *rlp.Stream) error {
- var item struct {
- Amount, Value uint64
- }
- if err := s.Decode(&item); err != nil {
- return err
- }
- b.amount, b.value = item.Amount, item.Value
- return nil
-}
-
-// EncodeRLP implements rlp.Encoder
-func (r *requestBasket) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{r.items, r.exp})
-}
-
-// DecodeRLP implements rlp.Decoder
-func (r *requestBasket) DecodeRLP(s *rlp.Stream) error {
- var enc struct {
- Items []basketItem
- Exp uint64
- }
- if err := s.Decode(&enc); err != nil {
- return err
- }
- r.items, r.exp = enc.Items, enc.Exp
- return nil
-}
-
-// convertMapping converts a basket loaded from the database into the current format.
-// If the available request types and their mapping into the service vector differ from
-// the one used when saving the basket then this function reorders old fields and fills
-// in previously unknown fields by scaling up amounts and values taken from the
-// initialization basket.
-func (r requestBasket) convertMapping(oldMapping, newMapping []string, initBasket requestBasket) requestBasket {
- nameMap := make(map[string]int)
- for i, name := range oldMapping {
- nameMap[name] = i
- }
- rc := requestBasket{items: make([]basketItem, len(newMapping))}
- var scale, oldScale, newScale float64
- for i, name := range newMapping {
- if ii, ok := nameMap[name]; ok {
- rc.items[i] = r.items[ii]
- oldScale += float64(initBasket.items[i].amount) * float64(initBasket.items[i].amount)
- newScale += float64(rc.items[i].amount) * float64(initBasket.items[i].amount)
- }
- }
- if oldScale > 1e-10 {
- scale = newScale / oldScale
- } else {
- scale = 1
- }
- for i, name := range newMapping {
- if _, ok := nameMap[name]; !ok {
- rc.items[i].amount = uint64(float64(initBasket.items[i].amount) * scale)
- rc.items[i].value = uint64(float64(initBasket.items[i].value) * scale)
- }
- }
- return rc
-}
diff --git a/les/vflux/client/requestbasket_test.go b/les/vflux/client/requestbasket_test.go
deleted file mode 100644
index 320d1b4b3e29..000000000000
--- a/les/vflux/client/requestbasket_test.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "math/rand"
- "testing"
-
- "github.com/ethereum/go-ethereum/les/utils"
-)
-
-func checkU64(t *testing.T, name string, value, exp uint64) {
- if value != exp {
- t.Errorf("Incorrect value for %s: got %d, expected %d", name, value, exp)
- }
-}
-
-func checkF64(t *testing.T, name string, value, exp, tol float64) {
- if value < exp-tol || value > exp+tol {
- t.Errorf("Incorrect value for %s: got %f, expected %f", name, value, exp)
- }
-}
-
-func TestServerBasket(t *testing.T) {
- t.Parallel()
-
- var s serverBasket
- s.init(2)
- // add some requests with different request value factors
- s.updateRvFactor(1)
- noexp := utils.ExpirationFactor{Factor: 1}
- s.add(0, 1000, 10000, noexp)
- s.add(1, 3000, 60000, noexp)
- s.updateRvFactor(10)
- s.add(0, 4000, 4000, noexp)
- s.add(1, 2000, 4000, noexp)
- s.updateRvFactor(10)
- // check basket contents directly
- checkU64(t, "s.basket[0].amount", s.basket.items[0].amount, 5000*basketFactor)
- checkU64(t, "s.basket[0].value", s.basket.items[0].value, 50000)
- checkU64(t, "s.basket[1].amount", s.basket.items[1].amount, 5000*basketFactor)
- checkU64(t, "s.basket[1].value", s.basket.items[1].value, 100000)
- // transfer 50% of the contents of the basket
- transfer1 := s.transfer(0.5)
- checkU64(t, "transfer1[0].amount", transfer1.items[0].amount, 2500*basketFactor)
- checkU64(t, "transfer1[0].value", transfer1.items[0].value, 25000)
- checkU64(t, "transfer1[1].amount", transfer1.items[1].amount, 2500*basketFactor)
- checkU64(t, "transfer1[1].value", transfer1.items[1].value, 50000)
- // add more requests
- s.updateRvFactor(100)
- s.add(0, 1000, 100, noexp)
- // transfer 25% of the contents of the basket
- transfer2 := s.transfer(0.25)
- checkU64(t, "transfer2[0].amount", transfer2.items[0].amount, (2500+1000)/4*basketFactor)
- checkU64(t, "transfer2[0].value", transfer2.items[0].value, (25000+10000)/4)
- checkU64(t, "transfer2[1].amount", transfer2.items[1].amount, 2500/4*basketFactor)
- checkU64(t, "transfer2[1].value", transfer2.items[1].value, 50000/4)
-}
-
-func TestConvertMapping(t *testing.T) {
- t.Parallel()
-
- b := requestBasket{items: []basketItem{{3, 3}, {1, 1}, {2, 2}}}
- oldMap := []string{"req3", "req1", "req2"}
- newMap := []string{"req1", "req2", "req3", "req4"}
- init := requestBasket{items: []basketItem{{2, 2}, {4, 4}, {6, 6}, {8, 8}}}
- bc := b.convertMapping(oldMap, newMap, init)
- checkU64(t, "bc[0].amount", bc.items[0].amount, 1)
- checkU64(t, "bc[1].amount", bc.items[1].amount, 2)
- checkU64(t, "bc[2].amount", bc.items[2].amount, 3)
- checkU64(t, "bc[3].amount", bc.items[3].amount, 4) // 8 should be scaled down to 4
-}
-
-func TestReqValueFactor(t *testing.T) {
- t.Parallel()
-
- var ref referenceBasket
- ref.basket = requestBasket{items: make([]basketItem, 4)}
- for i := range ref.basket.items {
- ref.basket.items[i].amount = uint64(i+1) * basketFactor
- ref.basket.items[i].value = uint64(i+1) * basketFactor
- }
- ref.init(4)
- rvf := ref.reqValueFactor([]uint64{1000, 2000, 3000, 4000})
- // expected value is (1000000+2000000+3000000+4000000) / (1*1000+2*2000+3*3000+4*4000) = 10000000/30000 = 333.333
- checkF64(t, "reqValueFactor", rvf, 333.333, 1)
-}
-
-func TestNormalize(t *testing.T) {
- t.Parallel()
-
- for cycle := 0; cycle < 100; cycle += 1 {
- // Initialize data for testing
- valueRange, lower := 1000000, 1000000
- ref := referenceBasket{basket: requestBasket{items: make([]basketItem, 10)}}
- for i := 0; i < 10; i++ {
- ref.basket.items[i].amount = uint64(rand.Intn(valueRange) + lower)
- ref.basket.items[i].value = uint64(rand.Intn(valueRange) + lower)
- }
- ref.normalize()
-
- // Check whether SUM(amount) ~= SUM(value)
- var sumAmount, sumValue uint64
- for i := 0; i < 10; i++ {
- sumAmount += ref.basket.items[i].amount
- sumValue += ref.basket.items[i].value
- }
- var epsilon = 0.01
- if float64(sumAmount)*(1+epsilon) < float64(sumValue) || float64(sumAmount)*(1-epsilon) > float64(sumValue) {
- t.Fatalf("Failed to normalize sumAmount: %d sumValue: %d", sumAmount, sumValue)
- }
- }
-}
-
-func TestReqValueAdjustment(t *testing.T) {
- t.Parallel()
-
- var s1, s2 serverBasket
- s1.init(3)
- s2.init(3)
- cost1 := []uint64{30000, 60000, 90000}
- cost2 := []uint64{100000, 200000, 300000}
- var ref referenceBasket
- ref.basket = requestBasket{items: make([]basketItem, 3)}
- for i := range ref.basket.items {
- ref.basket.items[i].amount = 123 * basketFactor
- ref.basket.items[i].value = 123 * basketFactor
- }
- ref.init(3)
- // initial reqValues are expected to be {1, 1, 1}
- checkF64(t, "reqValues[0]", ref.reqValues[0], 1, 0.01)
- checkF64(t, "reqValues[1]", ref.reqValues[1], 1, 0.01)
- checkF64(t, "reqValues[2]", ref.reqValues[2], 1, 0.01)
- var logOffset utils.Fixed64
- for period := 0; period < 1000; period++ {
- exp := utils.ExpFactor(logOffset)
- s1.updateRvFactor(ref.reqValueFactor(cost1))
- s2.updateRvFactor(ref.reqValueFactor(cost2))
- // throw in random requests into each basket using their internal pricing
- for i := 0; i < 1000; i++ {
- reqType, reqAmount := uint32(rand.Intn(3)), uint32(rand.Intn(10)+1)
- reqCost := uint64(reqAmount) * cost1[reqType]
- s1.add(reqType, reqAmount, reqCost, exp)
- reqType, reqAmount = uint32(rand.Intn(3)), uint32(rand.Intn(10)+1)
- reqCost = uint64(reqAmount) * cost2[reqType]
- s2.add(reqType, reqAmount, reqCost, exp)
- }
- ref.add(s1.transfer(0.1))
- ref.add(s2.transfer(0.1))
- ref.normalize()
- ref.updateReqValues()
- logOffset += utils.Float64ToFixed64(0.1)
- }
- checkF64(t, "reqValues[0]", ref.reqValues[0], 0.5, 0.01)
- checkF64(t, "reqValues[1]", ref.reqValues[1], 1, 0.01)
- checkF64(t, "reqValues[2]", ref.reqValues[2], 1.5, 0.01)
-}
diff --git a/les/vflux/client/serverpool.go b/les/vflux/client/serverpool.go
deleted file mode 100644
index 271d6e022447..000000000000
--- a/les/vflux/client/serverpool.go
+++ /dev/null
@@ -1,605 +0,0 @@
-// Copyright 2021 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 .
-
-package client
-
-import (
- "errors"
- "math/rand"
- "reflect"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/metrics"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const (
- minTimeout = time.Millisecond * 500 // minimum request timeout suggested by the server pool
- timeoutRefresh = time.Second * 5 // recalculate timeout if older than this
- dialCost = 10000 // cost of a TCP dial (used for known node selection weight calculation)
- dialWaitStep = 1.5 // exponential multiplier of redial wait time when no value was provided by the server
- queryCost = 500 // cost of a UDP pre-negotiation query
- queryWaitStep = 1.02 // exponential multiplier of redial wait time when no value was provided by the server
- waitThreshold = time.Hour * 2000 // drop node if waiting time is over the threshold
- nodeWeightMul = 1000000 // multiplier constant for node weight calculation
- nodeWeightThreshold = 100 // minimum weight for keeping a node in the known (valuable) set
- minRedialWait = 10 // minimum redial wait time in seconds
- preNegLimit = 5 // maximum number of simultaneous pre-negotiation queries
- warnQueryFails = 20 // number of consecutive UDP query failures before we print a warning
- maxQueryFails = 100 // number of consecutive UDP query failures when then chance of skipping a query reaches 50%
-)
-
-// ServerPool provides a node iterator for dial candidates. The output is a mix of newly discovered
-// nodes, a weighted random selection of known (previously valuable) nodes and trusted/paid nodes.
-type ServerPool struct {
- clock mclock.Clock
- unixTime func() int64
- db ethdb.KeyValueStore
-
- ns *nodestate.NodeStateMachine
- vt *ValueTracker
- mixer *enode.FairMix
- mixSources []enode.Iterator
- dialIterator enode.Iterator
- validSchemes enr.IdentityScheme
- trustedURLs []string
- fillSet *FillSet
- started, queryFails uint32
-
- timeoutLock sync.RWMutex
- timeout time.Duration
- timeWeights ResponseTimeWeights
- timeoutRefreshed mclock.AbsTime
-
- suggestedTimeoutGauge, totalValueGauge metrics.Gauge
- sessionValueMeter metrics.Meter
-}
-
-// nodeHistory keeps track of dial costs which determine node weight together with the
-// service value calculated by ValueTracker.
-type nodeHistory struct {
- dialCost utils.ExpiredValue
- redialWaitStart, redialWaitEnd int64 // unix time (seconds)
-}
-
-type nodeHistoryEnc struct {
- DialCost utils.ExpiredValue
- RedialWaitStart, RedialWaitEnd uint64
-}
-
-// QueryFunc sends a pre-negotiation query and blocks until a response arrives or timeout occurs.
-// It returns 1 if the remote node has confirmed that connection is possible, 0 if not
-// possible and -1 if no response arrived (timeout).
-type QueryFunc func(*enode.Node) int
-
-var (
- clientSetup = &nodestate.Setup{Version: 2}
- sfHasValue = clientSetup.NewPersistentFlag("hasValue")
- sfQuery = clientSetup.NewFlag("query")
- sfCanDial = clientSetup.NewFlag("canDial")
- sfDialing = clientSetup.NewFlag("dialed")
- sfWaitDialTimeout = clientSetup.NewFlag("dialTimeout")
- sfConnected = clientSetup.NewFlag("connected")
- sfRedialWait = clientSetup.NewFlag("redialWait")
- sfAlwaysConnect = clientSetup.NewFlag("alwaysConnect")
- sfDialProcess = nodestate.MergeFlags(sfQuery, sfCanDial, sfDialing, sfConnected, sfRedialWait)
-
- sfiNodeHistory = clientSetup.NewPersistentField("nodeHistory", reflect.TypeOf(nodeHistory{}),
- func(field interface{}) ([]byte, error) {
- if n, ok := field.(nodeHistory); ok {
- ne := nodeHistoryEnc{
- DialCost: n.dialCost,
- RedialWaitStart: uint64(n.redialWaitStart),
- RedialWaitEnd: uint64(n.redialWaitEnd),
- }
- enc, err := rlp.EncodeToBytes(&ne)
- return enc, err
- }
- return nil, errors.New("invalid field type")
- },
- func(enc []byte) (interface{}, error) {
- var ne nodeHistoryEnc
- err := rlp.DecodeBytes(enc, &ne)
- n := nodeHistory{
- dialCost: ne.DialCost,
- redialWaitStart: int64(ne.RedialWaitStart),
- redialWaitEnd: int64(ne.RedialWaitEnd),
- }
- return n, err
- },
- )
- sfiNodeWeight = clientSetup.NewField("nodeWeight", reflect.TypeOf(uint64(0)))
- sfiConnectedStats = clientSetup.NewField("connectedStats", reflect.TypeOf(ResponseTimeStats{}))
- sfiLocalAddress = clientSetup.NewPersistentField("localAddress", reflect.TypeOf(&enr.Record{}),
- func(field interface{}) ([]byte, error) {
- if enr, ok := field.(*enr.Record); ok {
- enc, err := rlp.EncodeToBytes(enr)
- return enc, err
- }
- return nil, errors.New("invalid field type")
- },
- func(enc []byte) (interface{}, error) {
- var enr enr.Record
- if err := rlp.DecodeBytes(enc, &enr); err != nil {
- return nil, err
- }
- return &enr, nil
- },
- )
-)
-
-// NewServerPool creates a new server pool
-func NewServerPool(db ethdb.KeyValueStore, dbKey []byte, mixTimeout time.Duration, query QueryFunc, clock mclock.Clock, trustedURLs []string, requestList []RequestInfo) (*ServerPool, enode.Iterator) {
- s := &ServerPool{
- db: db,
- clock: clock,
- unixTime: func() int64 { return time.Now().Unix() },
- validSchemes: enode.ValidSchemes,
- trustedURLs: trustedURLs,
- vt: NewValueTracker(db, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)),
- ns: nodestate.NewNodeStateMachine(db, []byte(string(dbKey)+"ns:"), clock, clientSetup),
- }
- s.recalTimeout()
- s.mixer = enode.NewFairMix(mixTimeout)
- knownSelector := NewWrsIterator(s.ns, sfHasValue, sfDialProcess, sfiNodeWeight)
- alwaysConnect := NewQueueIterator(s.ns, sfAlwaysConnect, sfDialProcess, true, nil)
- s.mixSources = append(s.mixSources, knownSelector)
- s.mixSources = append(s.mixSources, alwaysConnect)
-
- s.dialIterator = s.mixer
- if query != nil {
- s.dialIterator = s.addPreNegFilter(s.dialIterator, query)
- }
-
- s.ns.SubscribeState(nodestate.MergeFlags(sfWaitDialTimeout, sfConnected), func(n *enode.Node, oldState, newState nodestate.Flags) {
- if oldState.Equals(sfWaitDialTimeout) && newState.IsEmpty() {
- // dial timeout, no connection
- s.setRedialWait(n, dialCost, dialWaitStep)
- s.ns.SetStateSub(n, nodestate.Flags{}, sfDialing, 0)
- }
- })
-
- return s, &serverPoolIterator{
- dialIterator: s.dialIterator,
- nextFn: func(node *enode.Node) {
- s.ns.Operation(func() {
- s.ns.SetStateSub(node, sfDialing, sfCanDial, 0)
- s.ns.SetStateSub(node, sfWaitDialTimeout, nodestate.Flags{}, time.Second*10)
- })
- },
- nodeFn: s.DialNode,
- }
-}
-
-type serverPoolIterator struct {
- dialIterator enode.Iterator
- nextFn func(*enode.Node)
- nodeFn func(*enode.Node) *enode.Node
-}
-
-// Next implements enode.Iterator
-func (s *serverPoolIterator) Next() bool {
- if s.dialIterator.Next() {
- s.nextFn(s.dialIterator.Node())
- return true
- }
- return false
-}
-
-// Node implements enode.Iterator
-func (s *serverPoolIterator) Node() *enode.Node {
- return s.nodeFn(s.dialIterator.Node())
-}
-
-// Close implements enode.Iterator
-func (s *serverPoolIterator) Close() {
- s.dialIterator.Close()
-}
-
-// AddMetrics adds metrics to the server pool. Should be called before Start().
-func (s *ServerPool) AddMetrics(
- suggestedTimeoutGauge, totalValueGauge, serverSelectableGauge, serverConnectedGauge metrics.Gauge,
- sessionValueMeter, serverDialedMeter metrics.Meter) {
- s.suggestedTimeoutGauge = suggestedTimeoutGauge
- s.totalValueGauge = totalValueGauge
- s.sessionValueMeter = sessionValueMeter
- if serverSelectableGauge != nil {
- s.ns.AddLogMetrics(sfHasValue, sfDialProcess, "selectable", nil, nil, serverSelectableGauge)
- }
- if serverDialedMeter != nil {
- s.ns.AddLogMetrics(sfDialing, nodestate.Flags{}, "dialed", serverDialedMeter, nil, nil)
- }
- if serverConnectedGauge != nil {
- s.ns.AddLogMetrics(sfConnected, nodestate.Flags{}, "connected", nil, nil, serverConnectedGauge)
- }
-}
-
-// AddSource adds a node discovery source to the server pool (should be called before start)
-func (s *ServerPool) AddSource(source enode.Iterator) {
- if source != nil {
- s.mixSources = append(s.mixSources, source)
- }
-}
-
-// addPreNegFilter installs a node filter mechanism that performs a pre-negotiation query.
-// Nodes that are filtered out and does not appear on the output iterator are put back
-// into redialWait state.
-func (s *ServerPool) addPreNegFilter(input enode.Iterator, query QueryFunc) enode.Iterator {
- s.fillSet = NewFillSet(s.ns, input, sfQuery)
- s.ns.SubscribeState(sfDialProcess, func(n *enode.Node, oldState, newState nodestate.Flags) {
- if !newState.Equals(sfQuery) {
- if newState.HasAll(sfQuery) {
- // remove query flag if the node is already somewhere in the dial process
- s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0)
- }
- return
- }
- fails := atomic.LoadUint32(&s.queryFails)
- failMax := fails
- if failMax > maxQueryFails {
- failMax = maxQueryFails
- }
- if rand.Intn(maxQueryFails*2) < int(failMax) {
- // skip pre-negotiation with increasing chance, max 50%
- // this ensures that the client can operate even if UDP is not working at all
- s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10)
- // set canDial before resetting queried so that FillSet will not read more
- // candidates unnecessarily
- s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0)
- return
- }
- go func() {
- q := query(n)
- if q == -1 {
- atomic.AddUint32(&s.queryFails, 1)
- fails++
- if fails%warnQueryFails == 0 {
- // warn if a large number of consecutive queries have failed
- log.Warn("UDP connection queries failed", "count", fails)
- }
- } else {
- atomic.StoreUint32(&s.queryFails, 0)
- }
- s.ns.Operation(func() {
- // we are no longer running in the operation that the callback belongs to, start a new one because of setRedialWait
- if q == 1 {
- s.ns.SetStateSub(n, sfCanDial, nodestate.Flags{}, time.Second*10)
- } else {
- s.setRedialWait(n, queryCost, queryWaitStep)
- }
- s.ns.SetStateSub(n, nodestate.Flags{}, sfQuery, 0)
- })
- }()
- })
- return NewQueueIterator(s.ns, sfCanDial, nodestate.Flags{}, false, func(waiting bool) {
- if waiting {
- s.fillSet.SetTarget(preNegLimit)
- } else {
- s.fillSet.SetTarget(0)
- }
- })
-}
-
-// Start starts the server pool. Note that NodeStateMachine should be started first.
-func (s *ServerPool) Start() {
- s.ns.Start()
- for _, iter := range s.mixSources {
- // add sources to mixer at startup because the mixer instantly tries to read them
- // which should only happen after NodeStateMachine has been started
- s.mixer.AddSource(iter)
- }
- for _, url := range s.trustedURLs {
- if node, err := enode.Parse(s.validSchemes, url); err == nil {
- s.ns.SetState(node, sfAlwaysConnect, nodestate.Flags{}, 0)
- } else {
- log.Error("Invalid trusted server URL", "url", url, "error", err)
- }
- }
- unixTime := s.unixTime()
- s.ns.Operation(func() {
- s.ns.ForEach(sfHasValue, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) {
- s.calculateWeight(node)
- if n, ok := s.ns.GetField(node, sfiNodeHistory).(nodeHistory); ok && n.redialWaitEnd > unixTime {
- wait := n.redialWaitEnd - unixTime
- lastWait := n.redialWaitEnd - n.redialWaitStart
- if wait > lastWait {
- // if the time until expiration is larger than the last suggested
- // waiting time then the system clock was probably adjusted
- wait = lastWait
- }
- s.ns.SetStateSub(node, sfRedialWait, nodestate.Flags{}, time.Duration(wait)*time.Second)
- }
- })
- })
- atomic.StoreUint32(&s.started, 1)
-}
-
-// Stop stops the server pool
-func (s *ServerPool) Stop() {
- if s.fillSet != nil {
- s.fillSet.Close()
- }
- s.ns.Operation(func() {
- s.ns.ForEach(sfConnected, nodestate.Flags{}, func(n *enode.Node, state nodestate.Flags) {
- // recalculate weight of connected nodes in order to update hasValue flag if necessary
- s.calculateWeight(n)
- })
- })
- s.ns.Stop()
- s.vt.Stop()
-}
-
-// RegisterNode implements serverPeerSubscriber
-func (s *ServerPool) RegisterNode(node *enode.Node) (*NodeValueTracker, error) {
- if atomic.LoadUint32(&s.started) == 0 {
- return nil, errors.New("server pool not started yet")
- }
- nvt := s.vt.Register(node.ID())
- s.ns.Operation(func() {
- s.ns.SetStateSub(node, sfConnected, sfDialing.Or(sfWaitDialTimeout), 0)
- s.ns.SetFieldSub(node, sfiConnectedStats, nvt.RtStats())
- if node.IP().IsLoopback() {
- s.ns.SetFieldSub(node, sfiLocalAddress, node.Record())
- }
- })
- return nvt, nil
-}
-
-// UnregisterNode implements serverPeerSubscriber
-func (s *ServerPool) UnregisterNode(node *enode.Node) {
- s.ns.Operation(func() {
- s.setRedialWait(node, dialCost, dialWaitStep)
- s.ns.SetStateSub(node, nodestate.Flags{}, sfConnected, 0)
- s.ns.SetFieldSub(node, sfiConnectedStats, nil)
- })
- s.vt.Unregister(node.ID())
-}
-
-// recalTimeout calculates the current recommended timeout. This value is used by
-// the client as a "soft timeout" value. It also affects the service value calculation
-// of individual nodes.
-func (s *ServerPool) recalTimeout() {
- // Use cached result if possible, avoid recalculating too frequently.
- s.timeoutLock.RLock()
- refreshed := s.timeoutRefreshed
- s.timeoutLock.RUnlock()
- now := s.clock.Now()
- if refreshed != 0 && time.Duration(now-refreshed) < timeoutRefresh {
- return
- }
- // Cached result is stale, recalculate a new one.
- rts := s.vt.RtStats()
-
- // Add a fake statistic here. It is an easy way to initialize with some
- // conservative values when the database is new. As soon as we have a
- // considerable amount of real stats this small value won't matter.
- rts.Add(time.Second*2, 10, s.vt.StatsExpFactor())
-
- // Use either 10% failure rate timeout or twice the median response time
- // as the recommended timeout.
- timeout := minTimeout
- if t := rts.Timeout(0.1); t > timeout {
- timeout = t
- }
- if t := rts.Timeout(0.5) * 2; t > timeout {
- timeout = t
- }
- s.timeoutLock.Lock()
- if s.timeout != timeout {
- s.timeout = timeout
- s.timeWeights = TimeoutWeights(s.timeout)
-
- if s.suggestedTimeoutGauge != nil {
- s.suggestedTimeoutGauge.Update(int64(s.timeout / time.Millisecond))
- }
- if s.totalValueGauge != nil {
- s.totalValueGauge.Update(int64(rts.Value(s.timeWeights, s.vt.StatsExpFactor())))
- }
- }
- s.timeoutRefreshed = now
- s.timeoutLock.Unlock()
-}
-
-// GetTimeout returns the recommended request timeout.
-func (s *ServerPool) GetTimeout() time.Duration {
- s.recalTimeout()
- s.timeoutLock.RLock()
- defer s.timeoutLock.RUnlock()
- return s.timeout
-}
-
-// getTimeoutAndWeight returns the recommended request timeout as well as the
-// response time weight which is necessary to calculate service value.
-func (s *ServerPool) getTimeoutAndWeight() (time.Duration, ResponseTimeWeights) {
- s.recalTimeout()
- s.timeoutLock.RLock()
- defer s.timeoutLock.RUnlock()
- return s.timeout, s.timeWeights
-}
-
-// addDialCost adds the given amount of dial cost to the node history and returns the current
-// amount of total dial cost
-func (s *ServerPool) addDialCost(n *nodeHistory, amount int64) uint64 {
- logOffset := s.vt.StatsExpirer().LogOffset(s.clock.Now())
- if amount > 0 {
- n.dialCost.Add(amount, logOffset)
- }
- totalDialCost := n.dialCost.Value(logOffset)
- if totalDialCost < dialCost {
- totalDialCost = dialCost
- }
- return totalDialCost
-}
-
-// serviceValue returns the service value accumulated in this session and in total
-func (s *ServerPool) serviceValue(node *enode.Node) (sessionValue, totalValue float64) {
- nvt := s.vt.GetNode(node.ID())
- if nvt == nil {
- return 0, 0
- }
- currentStats := nvt.RtStats()
- _, timeWeights := s.getTimeoutAndWeight()
- expFactor := s.vt.StatsExpFactor()
-
- totalValue = currentStats.Value(timeWeights, expFactor)
- if connStats, ok := s.ns.GetField(node, sfiConnectedStats).(ResponseTimeStats); ok {
- diff := currentStats
- diff.SubStats(&connStats)
- sessionValue = diff.Value(timeWeights, expFactor)
- if s.sessionValueMeter != nil {
- s.sessionValueMeter.Mark(int64(sessionValue))
- }
- }
- return
-}
-
-// updateWeight calculates the node weight and updates the nodeWeight field and the
-// hasValue flag. It also saves the node state if necessary.
-// Note: this function should run inside a NodeStateMachine operation
-func (s *ServerPool) updateWeight(node *enode.Node, totalValue float64, totalDialCost uint64) {
- weight := uint64(totalValue * nodeWeightMul / float64(totalDialCost))
- if weight >= nodeWeightThreshold {
- s.ns.SetStateSub(node, sfHasValue, nodestate.Flags{}, 0)
- s.ns.SetFieldSub(node, sfiNodeWeight, weight)
- } else {
- s.ns.SetStateSub(node, nodestate.Flags{}, sfHasValue, 0)
- s.ns.SetFieldSub(node, sfiNodeWeight, nil)
- s.ns.SetFieldSub(node, sfiNodeHistory, nil)
- s.ns.SetFieldSub(node, sfiLocalAddress, nil)
- }
- s.ns.Persist(node) // saved if node history or hasValue changed
-}
-
-// setRedialWait calculates and sets the redialWait timeout based on the service value
-// and dial cost accumulated during the last session/attempt and in total.
-// The waiting time is raised exponentially if no service value has been received in order
-// to prevent dialing an unresponsive node frequently for a very long time just because it
-// was useful in the past. It can still be occasionally dialed though and once it provides
-// a significant amount of service value again its waiting time is quickly reduced or reset
-// to the minimum.
-// Note: node weight is also recalculated and updated by this function.
-// Note 2: this function should run inside a NodeStateMachine operation
-func (s *ServerPool) setRedialWait(node *enode.Node, addDialCost int64, waitStep float64) {
- n, _ := s.ns.GetField(node, sfiNodeHistory).(nodeHistory)
- sessionValue, totalValue := s.serviceValue(node)
- totalDialCost := s.addDialCost(&n, addDialCost)
-
- // if the current dial session has yielded at least the average value/dial cost ratio
- // then the waiting time should be reset to the minimum. If the session value
- // is below average but still positive then timeout is limited to the ratio of
- // average / current service value multiplied by the minimum timeout. If the attempt
- // was unsuccessful then timeout is raised exponentially without limitation.
- // Note: dialCost is used in the formula below even if dial was not attempted at all
- // because the pre-negotiation query did not return a positive result. In this case
- // the ratio has no meaning anyway and waitFactor is always raised, though in smaller
- // steps because queries are cheaper and therefore we can allow more failed attempts.
- unixTime := s.unixTime()
- plannedTimeout := float64(n.redialWaitEnd - n.redialWaitStart) // last planned redialWait timeout
- var actualWait float64 // actual waiting time elapsed
- if unixTime > n.redialWaitEnd {
- // the planned timeout has elapsed
- actualWait = plannedTimeout
- } else {
- // if the node was redialed earlier then we do not raise the planned timeout
- // exponentially because that could lead to the timeout rising very high in
- // a short amount of time
- // Note that in case of an early redial actualWait also includes the dial
- // timeout or connection time of the last attempt but it still serves its
- // purpose of preventing the timeout rising quicker than linearly as a function
- // of total time elapsed without a successful connection.
- actualWait = float64(unixTime - n.redialWaitStart)
- }
- // raise timeout exponentially if the last planned timeout has elapsed
- // (use at least the last planned timeout otherwise)
- nextTimeout := actualWait * waitStep
- if plannedTimeout > nextTimeout {
- nextTimeout = plannedTimeout
- }
- // we reduce the waiting time if the server has provided service value during the
- // connection (but never under the minimum)
- a := totalValue * dialCost * float64(minRedialWait)
- b := float64(totalDialCost) * sessionValue
- if a < b*nextTimeout {
- nextTimeout = a / b
- }
- if nextTimeout < minRedialWait {
- nextTimeout = minRedialWait
- }
- wait := time.Duration(float64(time.Second) * nextTimeout)
- if wait < waitThreshold {
- n.redialWaitStart = unixTime
- n.redialWaitEnd = unixTime + int64(nextTimeout)
- s.ns.SetFieldSub(node, sfiNodeHistory, n)
- s.ns.SetStateSub(node, sfRedialWait, nodestate.Flags{}, wait)
- s.updateWeight(node, totalValue, totalDialCost)
- } else {
- // discard known node statistics if waiting time is very long because the node
- // hasn't been responsive for a very long time
- s.ns.SetFieldSub(node, sfiNodeHistory, nil)
- s.ns.SetFieldSub(node, sfiNodeWeight, nil)
- s.ns.SetStateSub(node, nodestate.Flags{}, sfHasValue, 0)
- }
-}
-
-// calculateWeight calculates and sets the node weight without altering the node history.
-// This function should be called during startup and shutdown only, otherwise setRedialWait
-// will keep the weights updated as the underlying statistics are adjusted.
-// Note: this function should run inside a NodeStateMachine operation
-func (s *ServerPool) calculateWeight(node *enode.Node) {
- n, _ := s.ns.GetField(node, sfiNodeHistory).(nodeHistory)
- _, totalValue := s.serviceValue(node)
- totalDialCost := s.addDialCost(&n, 0)
- s.updateWeight(node, totalValue, totalDialCost)
-}
-
-// API returns the vflux client API
-func (s *ServerPool) API() *PrivateClientAPI {
- return NewPrivateClientAPI(s.vt)
-}
-
-type dummyIdentity enode.ID
-
-func (id dummyIdentity) Verify(r *enr.Record, sig []byte) error { return nil }
-func (id dummyIdentity) NodeAddr(r *enr.Record) []byte { return id[:] }
-
-// DialNode replaces the given enode with a locally generated one containing the ENR
-// stored in the sfiLocalAddress field if present. This workaround ensures that nodes
-// on the local network can be dialed at the local address if a connection has been
-// successfully established previously.
-// Note that NodeStateMachine always remembers the enode with the latest version of
-// the remote signed ENR. ENR filtering should be performed on that version while
-// dialNode should be used for dialing the node over TCP or UDP.
-func (s *ServerPool) DialNode(n *enode.Node) *enode.Node {
- if enr, ok := s.ns.GetField(n, sfiLocalAddress).(*enr.Record); ok {
- n, _ := enode.New(dummyIdentity(n.ID()), enr)
- return n
- }
- return n
-}
-
-// Persist immediately stores the state of a node in the node database
-func (s *ServerPool) Persist(n *enode.Node) {
- s.ns.Persist(n)
-}
diff --git a/les/vflux/client/serverpool_test.go b/les/vflux/client/serverpool_test.go
deleted file mode 100644
index 19d4fe6630f4..000000000000
--- a/les/vflux/client/serverpool_test.go
+++ /dev/null
@@ -1,424 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "math/rand"
- "strconv"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
-)
-
-const (
- spTestNodes = 1000
- spTestTarget = 5
- spTestLength = 10000
- spMinTotal = 40000
- spMaxTotal = 50000
-)
-
-func testNodeID(i int) enode.ID {
- return enode.ID{42, byte(i % 256), byte(i / 256)}
-}
-
-func testNodeIndex(id enode.ID) int {
- if id[0] != 42 {
- return -1
- }
- return int(id[1]) + int(id[2])*256
-}
-
-type ServerPoolTest struct {
- db ethdb.KeyValueStore
- clock *mclock.Simulated
- quit chan chan struct{}
- preNeg, preNegFail bool
- sp *ServerPool
- spi enode.Iterator
- input enode.Iterator
- testNodes []spTestNode
- trusted []string
- waitCount, waitEnded int32
-
- // preNegLock protects the cycle counter, testNodes list and its connected field
- // (accessed from both the main thread and the preNeg callback)
- preNegLock sync.Mutex
- queryWg *sync.WaitGroup // a new wait group is created each time the simulation is started
- stopping bool // stopping avoid calling queryWg.Add after queryWg.Wait
-
- cycle, conn, servedConn int
- serviceCycles, dialCount int
- disconnect map[int][]int
-}
-
-type spTestNode struct {
- connectCycles, waitCycles int
- nextConnCycle, totalConn int
- connected, service bool
- node *enode.Node
-}
-
-func newServerPoolTest(preNeg, preNegFail bool) *ServerPoolTest {
- nodes := make([]*enode.Node, spTestNodes)
- for i := range nodes {
- nodes[i] = enode.SignNull(&enr.Record{}, testNodeID(i))
- }
- return &ServerPoolTest{
- clock: &mclock.Simulated{},
- db: memorydb.New(),
- input: enode.CycleNodes(nodes),
- testNodes: make([]spTestNode, spTestNodes),
- preNeg: preNeg,
- preNegFail: preNegFail,
- }
-}
-
-func (s *ServerPoolTest) beginWait() {
- // ensure that dialIterator and the maximal number of pre-neg queries are not all stuck in a waiting state
- for atomic.AddInt32(&s.waitCount, 1) > preNegLimit {
- atomic.AddInt32(&s.waitCount, -1)
- s.clock.Run(time.Second)
- }
-}
-
-func (s *ServerPoolTest) endWait() {
- atomic.AddInt32(&s.waitCount, -1)
- atomic.AddInt32(&s.waitEnded, 1)
-}
-
-func (s *ServerPoolTest) addTrusted(i int) {
- s.trusted = append(s.trusted, enode.SignNull(&enr.Record{}, testNodeID(i)).String())
-}
-
-func (s *ServerPoolTest) start() {
- var testQuery QueryFunc
- s.queryWg = new(sync.WaitGroup)
- if s.preNeg {
- testQuery = func(node *enode.Node) int {
- s.preNegLock.Lock()
- if s.stopping {
- s.preNegLock.Unlock()
- return 0
- }
- s.queryWg.Add(1)
- idx := testNodeIndex(node.ID())
- n := &s.testNodes[idx]
- canConnect := !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle
- s.preNegLock.Unlock()
- defer s.queryWg.Done()
-
- if s.preNegFail {
- // simulate a scenario where UDP queries never work
- s.beginWait()
- s.clock.Sleep(time.Second * 5)
- s.endWait()
- return -1
- }
- switch idx % 3 {
- case 0:
- // pre-neg returns true only if connection is possible
- if canConnect {
- return 1
- }
- return 0
- case 1:
- // pre-neg returns true but connection might still fail
- return 1
- case 2:
- // pre-neg returns true if connection is possible, otherwise timeout (node unresponsive)
- if canConnect {
- return 1
- }
- s.beginWait()
- s.clock.Sleep(time.Second * 5)
- s.endWait()
- return -1
- }
- return -1
- }
- }
-
- requestList := make([]RequestInfo, testReqTypes)
- for i := range requestList {
- requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
- }
-
- s.sp, s.spi = NewServerPool(s.db, []byte("sp:"), 0, testQuery, s.clock, s.trusted, requestList)
- s.sp.AddSource(s.input)
- s.sp.validSchemes = enode.ValidSchemesForTesting
- s.sp.unixTime = func() int64 { return int64(s.clock.Now()) / int64(time.Second) }
- s.disconnect = make(map[int][]int)
- s.sp.Start()
- s.quit = make(chan chan struct{})
- go func() {
- last := int32(-1)
- for {
- select {
- case <-time.After(time.Millisecond * 100):
- c := atomic.LoadInt32(&s.waitEnded)
- if c == last {
- // advance clock if test is stuck (might happen in rare cases)
- s.clock.Run(time.Second)
- }
- last = c
- case quit := <-s.quit:
- close(quit)
- return
- }
- }
- }()
-}
-
-func (s *ServerPoolTest) stop() {
- // disable further queries and wait if one is currently running
- s.preNegLock.Lock()
- s.stopping = true
- s.preNegLock.Unlock()
- s.queryWg.Wait()
-
- quit := make(chan struct{})
- s.quit <- quit
- <-quit
- s.sp.Stop()
- s.spi.Close()
- s.preNegLock.Lock()
- s.stopping = false
- s.preNegLock.Unlock()
- for i := range s.testNodes {
- n := &s.testNodes[i]
- if n.connected {
- n.totalConn += s.cycle
- }
- n.connected = false
- n.node = nil
- n.nextConnCycle = 0
- }
- s.conn, s.servedConn = 0, 0
-}
-
-func (s *ServerPoolTest) run() {
- for count := spTestLength; count > 0; count-- {
- if dcList := s.disconnect[s.cycle]; dcList != nil {
- for _, idx := range dcList {
- n := &s.testNodes[idx]
- s.sp.UnregisterNode(n.node)
- n.totalConn += s.cycle
- s.preNegLock.Lock()
- n.connected = false
- s.preNegLock.Unlock()
- n.node = nil
- s.conn--
- if n.service {
- s.servedConn--
- }
- n.nextConnCycle = s.cycle + n.waitCycles
- }
- delete(s.disconnect, s.cycle)
- }
- if s.conn < spTestTarget {
- s.dialCount++
- s.beginWait()
- s.spi.Next()
- s.endWait()
- dial := s.spi.Node()
- id := dial.ID()
- idx := testNodeIndex(id)
- n := &s.testNodes[idx]
- if !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle {
- s.conn++
- if n.service {
- s.servedConn++
- }
- n.totalConn -= s.cycle
- s.preNegLock.Lock()
- n.connected = true
- s.preNegLock.Unlock()
- dc := s.cycle + n.connectCycles
- s.disconnect[dc] = append(s.disconnect[dc], idx)
- n.node = dial
- nv, _ := s.sp.RegisterNode(n.node)
- if n.service {
- nv.Served([]ServedRequest{{ReqType: 0, Amount: 100}}, 0)
- }
- }
- }
- s.serviceCycles += s.servedConn
- s.clock.Run(time.Second)
- s.preNegLock.Lock()
- s.cycle++
- s.preNegLock.Unlock()
- }
-}
-
-func (s *ServerPoolTest) setNodes(count, conn, wait int, service, trusted bool) (res []int) {
- for ; count > 0; count-- {
- idx := rand.Intn(spTestNodes)
- for s.testNodes[idx].connectCycles != 0 || s.testNodes[idx].connected {
- idx = rand.Intn(spTestNodes)
- }
- res = append(res, idx)
- s.preNegLock.Lock()
- s.testNodes[idx] = spTestNode{
- connectCycles: conn,
- waitCycles: wait,
- service: service,
- }
- s.preNegLock.Unlock()
- if trusted {
- s.addTrusted(idx)
- }
- }
- return
-}
-
-func (s *ServerPoolTest) resetNodes() {
- for i, n := range s.testNodes {
- if n.connected {
- n.totalConn += s.cycle
- s.sp.UnregisterNode(n.node)
- }
- s.preNegLock.Lock()
- s.testNodes[i] = spTestNode{totalConn: n.totalConn}
- s.preNegLock.Unlock()
- }
- s.conn, s.servedConn = 0, 0
- s.disconnect = make(map[int][]int)
- s.trusted = nil
-}
-
-func (s *ServerPoolTest) checkNodes(t *testing.T, nodes []int) {
- var sum int
- for _, idx := range nodes {
- n := &s.testNodes[idx]
- if n.connected {
- n.totalConn += s.cycle
- }
- sum += n.totalConn
- n.totalConn = 0
- if n.connected {
- n.totalConn -= s.cycle
- }
- }
- if sum < spMinTotal || sum > spMaxTotal {
- t.Errorf("Total connection amount %d outside expected range %d to %d", sum, spMinTotal, spMaxTotal)
- }
-}
-
-func TestServerPool(t *testing.T) {
- t.Parallel()
-
- testServerPool(t, false, false)
-}
-func TestServerPoolWithPreNeg(t *testing.T) {
- t.Parallel()
-
- testServerPool(t, true, false)
-}
-func TestServerPoolWithPreNegFail(t *testing.T) {
- t.Parallel()
-
- testServerPool(t, true, true)
-}
-func testServerPool(t *testing.T, preNeg, fail bool) {
- s := newServerPoolTest(preNeg, fail)
- nodes := s.setNodes(100, 200, 200, true, false)
- s.setNodes(100, 20, 20, false, false)
- s.start()
- s.run()
- s.stop()
- s.checkNodes(t, nodes)
-}
-
-func TestServerPoolChangedNodes(t *testing.T) {
- t.Parallel()
-
- testServerPoolChangedNodes(t, false)
-}
-func TestServerPoolChangedNodesWithPreNeg(t *testing.T) {
- t.Parallel()
-
- testServerPoolChangedNodes(t, true)
-}
-func testServerPoolChangedNodes(t *testing.T, preNeg bool) {
- s := newServerPoolTest(preNeg, false)
- nodes := s.setNodes(100, 200, 200, true, false)
- s.setNodes(100, 20, 20, false, false)
- s.start()
- s.run()
- s.checkNodes(t, nodes)
- for i := 0; i < 3; i++ {
- s.resetNodes()
- nodes := s.setNodes(100, 200, 200, true, false)
- s.setNodes(100, 20, 20, false, false)
- s.run()
- s.checkNodes(t, nodes)
- }
- s.stop()
-}
-
-func TestServerPoolRestartNoDiscovery(t *testing.T) {
- t.Parallel()
-
- testServerPoolRestartNoDiscovery(t, false)
-}
-func TestServerPoolRestartNoDiscoveryWithPreNeg(t *testing.T) {
- t.Parallel()
-
- testServerPoolRestartNoDiscovery(t, true)
-}
-func testServerPoolRestartNoDiscovery(t *testing.T, preNeg bool) {
- s := newServerPoolTest(preNeg, false)
- nodes := s.setNodes(100, 200, 200, true, false)
- s.setNodes(100, 20, 20, false, false)
- s.start()
- s.run()
- s.stop()
- s.checkNodes(t, nodes)
- s.input = nil
- s.start()
- s.run()
- s.stop()
- s.checkNodes(t, nodes)
-}
-
-func TestServerPoolTrustedNoDiscovery(t *testing.T) {
- t.Parallel()
-
- testServerPoolTrustedNoDiscovery(t, false)
-}
-func TestServerPoolTrustedNoDiscoveryWithPreNeg(t *testing.T) {
- t.Parallel()
-
- testServerPoolTrustedNoDiscovery(t, true)
-}
-func testServerPoolTrustedNoDiscovery(t *testing.T, preNeg bool) {
- s := newServerPoolTest(preNeg, false)
- trusted := s.setNodes(200, 200, 200, true, true)
- s.input = nil
- s.start()
- s.run()
- s.stop()
- s.checkNodes(t, trusted)
-}
diff --git a/les/vflux/client/timestats.go b/les/vflux/client/timestats.go
deleted file mode 100644
index 7f1ffdbe2615..000000000000
--- a/les/vflux/client/timestats.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "io"
- "math"
- "time"
-
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const (
- minResponseTime = time.Millisecond * 50
- maxResponseTime = time.Second * 10
- timeStatLength = 32
- weightScaleFactor = 1000000
-)
-
-// ResponseTimeStats is the response time distribution of a set of answered requests,
-// weighted with request value, either served by a single server or aggregated for
-// multiple servers.
-// It it a fixed length (timeStatLength) distribution vector with linear interpolation.
-// The X axis (the time values) are not linear, they should be transformed with
-// TimeToStatScale and StatScaleToTime.
-type (
- ResponseTimeStats struct {
- stats [timeStatLength]uint64
- exp uint64
- }
- ResponseTimeWeights [timeStatLength]float64
-)
-
-var timeStatsLogFactor = (timeStatLength - 1) / (math.Log(float64(maxResponseTime)/float64(minResponseTime)) + 1)
-
-// TimeToStatScale converts a response time to a distribution vector index. The index
-// is represented by a float64 so that linear interpolation can be applied.
-func TimeToStatScale(d time.Duration) float64 {
- if d < 0 {
- return 0
- }
- r := float64(d) / float64(minResponseTime)
- if r > 1 {
- r = math.Log(r) + 1
- }
- r *= timeStatsLogFactor
- if r > timeStatLength-1 {
- return timeStatLength - 1
- }
- return r
-}
-
-// StatScaleToTime converts a distribution vector index to a response time. The index
-// is represented by a float64 so that linear interpolation can be applied.
-func StatScaleToTime(r float64) time.Duration {
- r /= timeStatsLogFactor
- if r > 1 {
- r = math.Exp(r - 1)
- }
- return time.Duration(r * float64(minResponseTime))
-}
-
-// TimeoutWeights calculates the weight function used for calculating service value
-// based on the response time distribution of the received service.
-// It is based on the request timeout value of the system. It consists of a half cosine
-// function starting with 1, crossing zero at timeout and reaching -1 at 2*timeout.
-// After 2*timeout the weight is constant -1.
-func TimeoutWeights(timeout time.Duration) (res ResponseTimeWeights) {
- for i := range res {
- t := StatScaleToTime(float64(i))
- if t < 2*timeout {
- res[i] = math.Cos(math.Pi / 2 * float64(t) / float64(timeout))
- } else {
- res[i] = -1
- }
- }
- return
-}
-
-// EncodeRLP implements rlp.Encoder
-func (rt *ResponseTimeStats) EncodeRLP(w io.Writer) error {
- enc := struct {
- Stats [timeStatLength]uint64
- Exp uint64
- }{rt.stats, rt.exp}
- return rlp.Encode(w, &enc)
-}
-
-// DecodeRLP implements rlp.Decoder
-func (rt *ResponseTimeStats) DecodeRLP(s *rlp.Stream) error {
- var enc struct {
- Stats [timeStatLength]uint64
- Exp uint64
- }
- if err := s.Decode(&enc); err != nil {
- return err
- }
- rt.stats, rt.exp = enc.Stats, enc.Exp
- return nil
-}
-
-// Add adds a new response time with the given weight to the distribution.
-func (rt *ResponseTimeStats) Add(respTime time.Duration, weight float64, expFactor utils.ExpirationFactor) {
- rt.setExp(expFactor.Exp)
- weight *= expFactor.Factor * weightScaleFactor
- r := TimeToStatScale(respTime)
- i := int(r)
- r -= float64(i)
- rt.stats[i] += uint64(weight * (1 - r))
- if i < timeStatLength-1 {
- rt.stats[i+1] += uint64(weight * r)
- }
-}
-
-// setExp sets the power of 2 exponent of the structure, scaling base values (the vector
-// itself) up or down if necessary.
-func (rt *ResponseTimeStats) setExp(exp uint64) {
- if exp > rt.exp {
- shift := exp - rt.exp
- for i, v := range rt.stats {
- rt.stats[i] = v >> shift
- }
- rt.exp = exp
- }
- if exp < rt.exp {
- shift := rt.exp - exp
- for i, v := range rt.stats {
- rt.stats[i] = v << shift
- }
- rt.exp = exp
- }
-}
-
-// Value calculates the total service value based on the given distribution, using the
-// specified weight function.
-func (rt ResponseTimeStats) Value(weights ResponseTimeWeights, expFactor utils.ExpirationFactor) float64 {
- var v float64
- for i, s := range rt.stats {
- v += float64(s) * weights[i]
- }
- if v < 0 {
- return 0
- }
- return expFactor.Value(v, rt.exp) / weightScaleFactor
-}
-
-// AddStats adds the given ResponseTimeStats to the current one.
-func (rt *ResponseTimeStats) AddStats(s *ResponseTimeStats) {
- rt.setExp(s.exp)
- for i, v := range s.stats {
- rt.stats[i] += v
- }
-}
-
-// SubStats subtracts the given ResponseTimeStats from the current one.
-func (rt *ResponseTimeStats) SubStats(s *ResponseTimeStats) {
- rt.setExp(s.exp)
- for i, v := range s.stats {
- if v < rt.stats[i] {
- rt.stats[i] -= v
- } else {
- rt.stats[i] = 0
- }
- }
-}
-
-// Timeout suggests a timeout value based on the previous distribution. The parameter
-// is the desired rate of timeouts assuming a similar distribution in the future.
-// Note that the actual timeout should have a sensible minimum bound so that operating
-// under ideal working conditions for a long time (for example, using a local server
-// with very low response times) will not make it very hard for the system to accommodate
-// longer response times in the future.
-func (rt ResponseTimeStats) Timeout(failRatio float64) time.Duration {
- var sum uint64
- for _, v := range rt.stats {
- sum += v
- }
- s := uint64(float64(sum) * failRatio)
- i := timeStatLength - 1
- for i > 0 && s >= rt.stats[i] {
- s -= rt.stats[i]
- i--
- }
- r := float64(i) + 0.5
- if rt.stats[i] > 0 {
- r -= float64(s) / float64(rt.stats[i])
- }
- if r < 0 {
- r = 0
- }
- th := StatScaleToTime(r)
- if th > maxResponseTime {
- th = maxResponseTime
- }
- return th
-}
-
-// RtDistribution represents a distribution as a series of (X, Y) chart coordinates,
-// where the X axis is the response time in seconds while the Y axis is the amount of
-// service value received with a response time close to the X coordinate.
-type RtDistribution [timeStatLength][2]float64
-
-// Distribution returns a RtDistribution, optionally normalized to a sum of 1.
-func (rt ResponseTimeStats) Distribution(normalized bool, expFactor utils.ExpirationFactor) (res RtDistribution) {
- var mul float64
- if normalized {
- var sum uint64
- for _, v := range rt.stats {
- sum += v
- }
- if sum > 0 {
- mul = 1 / float64(sum)
- }
- } else {
- mul = expFactor.Value(float64(1)/weightScaleFactor, rt.exp)
- }
- for i, v := range rt.stats {
- res[i][0] = float64(StatScaleToTime(float64(i))) / float64(time.Second)
- res[i][1] = float64(v) * mul
- }
- return
-}
diff --git a/les/vflux/client/timestats_test.go b/les/vflux/client/timestats_test.go
deleted file mode 100644
index 80ea2047c620..000000000000
--- a/les/vflux/client/timestats_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "math"
- "math/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/les/utils"
-)
-
-func TestTransition(t *testing.T) {
- t.Parallel()
-
- var epsilon = 0.01
- var cases = []time.Duration{
- time.Millisecond, minResponseTime,
- time.Second, time.Second * 5, maxResponseTime,
- }
- for _, c := range cases {
- got := StatScaleToTime(TimeToStatScale(c))
- if float64(got)*(1+epsilon) < float64(c) || float64(got)*(1-epsilon) > float64(c) {
- t.Fatalf("Failed to transition back")
- }
- }
- // If the time is too large(exceeds the max response time.
- got := StatScaleToTime(TimeToStatScale(2 * maxResponseTime))
- if float64(got)*(1+epsilon) < float64(maxResponseTime) || float64(got)*(1-epsilon) > float64(maxResponseTime) {
- t.Fatalf("Failed to transition back")
- }
-}
-
-var maxResponseWeights = TimeoutWeights(maxResponseTime)
-
-func TestValue(t *testing.T) {
- t.Parallel()
-
- noexp := utils.ExpirationFactor{Factor: 1}
- for i := 0; i < 1000; i++ {
- max := minResponseTime + time.Duration(rand.Int63n(int64(maxResponseTime-minResponseTime)))
- min := minResponseTime + time.Duration(rand.Int63n(int64(max-minResponseTime)))
- timeout := max/2 + time.Duration(rand.Int63n(int64(maxResponseTime-max/2)))
- s := makeRangeStats(min, max, 1000, noexp)
- value := s.Value(TimeoutWeights(timeout), noexp)
- // calculate the average weight (the average of the given range of the half cosine
- // weight function).
- minx := math.Pi / 2 * float64(min) / float64(timeout)
- maxx := math.Pi / 2 * float64(max) / float64(timeout)
- avgWeight := (math.Sin(maxx) - math.Sin(minx)) / (maxx - minx)
- expv := 1000 * avgWeight
- if expv < 0 {
- expv = 0
- }
- if value < expv-10 || value > expv+10 {
- t.Errorf("Value failed (expected %v, got %v)", expv, value)
- }
- }
-}
-
-func TestAddSubExpire(t *testing.T) {
- t.Parallel()
-
- var (
- sum1, sum2 ResponseTimeStats
- sum1ValueExp, sum2ValueExp float64
- logOffset utils.Fixed64
- )
- for i := 0; i < 1000; i++ {
- exp := utils.ExpFactor(logOffset)
- max := minResponseTime + time.Duration(rand.Int63n(int64(maxResponseTime-minResponseTime)))
- min := minResponseTime + time.Duration(rand.Int63n(int64(max-minResponseTime)))
- s := makeRangeStats(min, max, 1000, exp)
- value := s.Value(maxResponseWeights, exp)
- sum1.AddStats(&s)
- sum1ValueExp += value
- if rand.Intn(2) == 1 {
- sum2.AddStats(&s)
- sum2ValueExp += value
- }
- logOffset += utils.Float64ToFixed64(0.001 / math.Log(2))
- sum1ValueExp -= sum1ValueExp * 0.001
- sum2ValueExp -= sum2ValueExp * 0.001
- }
- exp := utils.ExpFactor(logOffset)
- sum1Value := sum1.Value(maxResponseWeights, exp)
- if sum1Value < sum1ValueExp*0.99 || sum1Value > sum1ValueExp*1.01 {
- t.Errorf("sum1Value failed (expected %v, got %v)", sum1ValueExp, sum1Value)
- }
- sum2Value := sum2.Value(maxResponseWeights, exp)
- if sum2Value < sum2ValueExp*0.99 || sum2Value > sum2ValueExp*1.01 {
- t.Errorf("sum2Value failed (expected %v, got %v)", sum2ValueExp, sum2Value)
- }
- diff := sum1
- diff.SubStats(&sum2)
- diffValue := diff.Value(maxResponseWeights, exp)
- diffValueExp := sum1ValueExp - sum2ValueExp
- if diffValue < diffValueExp*0.99 || diffValue > diffValueExp*1.01 {
- t.Errorf("diffValue failed (expected %v, got %v)", diffValueExp, diffValue)
- }
-}
-
-func TestTimeout(t *testing.T) {
- t.Parallel()
-
- testTimeoutRange(t, 0, time.Second)
- testTimeoutRange(t, time.Second, time.Second*2)
- testTimeoutRange(t, time.Second, maxResponseTime)
-}
-
-func testTimeoutRange(t *testing.T, min, max time.Duration) {
- s := makeRangeStats(min, max, 1000, utils.ExpirationFactor{Factor: 1})
- for i := 2; i < 9; i++ {
- to := s.Timeout(float64(i) / 10)
- exp := max - (max-min)*time.Duration(i)/10
- tol := (max - min) / 50
- if to < exp-tol || to > exp+tol {
- t.Errorf("Timeout failed (expected %v, got %v)", exp, to)
- }
- }
-}
-
-func makeRangeStats(min, max time.Duration, amount float64, exp utils.ExpirationFactor) ResponseTimeStats {
- var s ResponseTimeStats
- amount /= 1000
- for i := 0; i < 1000; i++ {
- s.Add(min+(max-min)*time.Duration(i)/999, amount, exp)
- }
- return s
-}
diff --git a/les/vflux/client/valuetracker.go b/les/vflux/client/valuetracker.go
deleted file mode 100644
index e0d1010ffe93..000000000000
--- a/les/vflux/client/valuetracker.go
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "bytes"
- "fmt"
- "math"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const (
- vtVersion = 1 // database encoding format for ValueTracker
- nvtVersion = 1 // database encoding format for NodeValueTracker
-)
-
-var (
- vtKey = []byte("vt:")
- vtNodeKey = []byte("vtNode:")
-)
-
-// NodeValueTracker collects service value statistics for a specific server node
-type NodeValueTracker struct {
- lock sync.Mutex
-
- vt *ValueTracker
- rtStats, lastRtStats ResponseTimeStats
- lastTransfer mclock.AbsTime
- basket serverBasket
- reqCosts []uint64
- reqValues []float64
-}
-
-// UpdateCosts updates the node value tracker's request cost table
-func (nv *NodeValueTracker) UpdateCosts(reqCosts []uint64) {
- nv.vt.lock.Lock()
- defer nv.vt.lock.Unlock()
-
- nv.updateCosts(reqCosts, nv.vt.refBasket.reqValues, nv.vt.refBasket.reqValueFactor(reqCosts))
-}
-
-// updateCosts updates the request cost table of the server. The request value factor
-// is also updated based on the given cost table and the current reference basket.
-// Note that the contents of the referenced reqValues slice will not change; a new
-// reference is passed if the values are updated by ValueTracker.
-func (nv *NodeValueTracker) updateCosts(reqCosts []uint64, reqValues []float64, rvFactor float64) {
- nv.lock.Lock()
- defer nv.lock.Unlock()
-
- nv.reqCosts = reqCosts
- nv.reqValues = reqValues
- nv.basket.updateRvFactor(rvFactor)
-}
-
-// transferStats returns request basket and response time statistics that should be
-// added to the global statistics. The contents of the server's own request basket are
-// gradually transferred to the main reference basket and removed from the server basket
-// with the specified transfer rate.
-// The response time statistics are retained at both places and therefore the global
-// distribution is always the sum of the individual server distributions.
-func (nv *NodeValueTracker) transferStats(now mclock.AbsTime, transferRate float64) (requestBasket, ResponseTimeStats) {
- nv.lock.Lock()
- defer nv.lock.Unlock()
-
- dt := now - nv.lastTransfer
- nv.lastTransfer = now
- if dt < 0 {
- dt = 0
- }
- recentRtStats := nv.rtStats
- recentRtStats.SubStats(&nv.lastRtStats)
- nv.lastRtStats = nv.rtStats
- return nv.basket.transfer(-math.Expm1(-transferRate * float64(dt))), recentRtStats
-}
-
-type ServedRequest struct {
- ReqType, Amount uint32
-}
-
-// Served adds a served request to the node's statistics. An actual request may be composed
-// of one or more request types (service vector indices).
-func (nv *NodeValueTracker) Served(reqs []ServedRequest, respTime time.Duration) {
- nv.vt.statsExpLock.RLock()
- expFactor := nv.vt.statsExpFactor
- nv.vt.statsExpLock.RUnlock()
-
- nv.lock.Lock()
- defer nv.lock.Unlock()
-
- var value float64
- for _, r := range reqs {
- nv.basket.add(r.ReqType, r.Amount, nv.reqCosts[r.ReqType]*uint64(r.Amount), expFactor)
- value += nv.reqValues[r.ReqType] * float64(r.Amount)
- }
- nv.rtStats.Add(respTime, value, expFactor)
-}
-
-// RtStats returns the node's own response time distribution statistics
-func (nv *NodeValueTracker) RtStats() ResponseTimeStats {
- nv.lock.Lock()
- defer nv.lock.Unlock()
-
- return nv.rtStats
-}
-
-// ValueTracker coordinates service value calculation for individual servers and updates
-// global statistics
-type ValueTracker struct {
- clock mclock.Clock
- lock sync.Mutex
- quit chan chan struct{}
- db ethdb.KeyValueStore
- connected map[enode.ID]*NodeValueTracker
- reqTypeCount int
-
- refBasket referenceBasket
- mappings [][]string
- currentMapping int
- initRefBasket requestBasket
- rtStats ResponseTimeStats
-
- transferRate float64
- statsExpLock sync.RWMutex
- statsExpRate, offlineExpRate float64
- statsExpirer utils.Expirer
- statsExpFactor utils.ExpirationFactor
-}
-
-type valueTrackerEncV1 struct {
- Mappings [][]string
- RefBasketMapping uint
- RefBasket requestBasket
- RtStats ResponseTimeStats
- ExpOffset, SavedAt uint64
-}
-
-type nodeValueTrackerEncV1 struct {
- RtStats ResponseTimeStats
- ServerBasketMapping uint
- ServerBasket requestBasket
-}
-
-// RequestInfo is an initializer structure for the service vector.
-type RequestInfo struct {
- // Name identifies the request type and is used for re-mapping the service vector if necessary
- Name string
- // InitAmount and InitValue are used to initialize the reference basket
- InitAmount, InitValue float64
-}
-
-// NewValueTracker creates a new ValueTracker and loads its previously saved state from
-// the database if possible.
-func NewValueTracker(db ethdb.KeyValueStore, clock mclock.Clock, reqInfo []RequestInfo, updatePeriod time.Duration, transferRate, statsExpRate, offlineExpRate float64) *ValueTracker {
- now := clock.Now()
-
- initRefBasket := requestBasket{items: make([]basketItem, len(reqInfo))}
- mapping := make([]string, len(reqInfo))
-
- var sumAmount, sumValue float64
- for _, req := range reqInfo {
- sumAmount += req.InitAmount
- sumValue += req.InitAmount * req.InitValue
- }
- scaleValues := sumAmount * basketFactor / sumValue
- for i, req := range reqInfo {
- mapping[i] = req.Name
- initRefBasket.items[i].amount = uint64(req.InitAmount * basketFactor)
- initRefBasket.items[i].value = uint64(req.InitAmount * req.InitValue * scaleValues)
- }
-
- vt := &ValueTracker{
- clock: clock,
- connected: make(map[enode.ID]*NodeValueTracker),
- quit: make(chan chan struct{}),
- db: db,
- reqTypeCount: len(initRefBasket.items),
- initRefBasket: initRefBasket,
- transferRate: transferRate,
- statsExpRate: statsExpRate,
- offlineExpRate: offlineExpRate,
- }
- if vt.loadFromDb(mapping) != nil {
- // previous state not saved or invalid, init with default values
- vt.refBasket.basket = initRefBasket
- vt.mappings = [][]string{mapping}
- vt.currentMapping = 0
- }
- vt.statsExpirer.SetRate(now, statsExpRate)
- vt.refBasket.init(vt.reqTypeCount)
- vt.periodicUpdate()
-
- go func() {
- for {
- select {
- case <-clock.After(updatePeriod):
- vt.lock.Lock()
- vt.periodicUpdate()
- vt.lock.Unlock()
- case quit := <-vt.quit:
- close(quit)
- return
- }
- }
- }()
- return vt
-}
-
-// StatsExpirer returns the statistics expirer so that other values can be expired
-// with the same rate as the service value statistics.
-func (vt *ValueTracker) StatsExpirer() *utils.Expirer {
- return &vt.statsExpirer
-}
-
-// StatsExpFactor returns the current expiration factor so that other values can be expired
-// with the same rate as the service value statistics.
-func (vt *ValueTracker) StatsExpFactor() utils.ExpirationFactor {
- vt.statsExpLock.RLock()
- defer vt.statsExpLock.RUnlock()
-
- return vt.statsExpFactor
-}
-
-// loadFromDb loads the value tracker's state from the database and converts saved
-// request basket index mapping if it does not match the specified index to name mapping.
-func (vt *ValueTracker) loadFromDb(mapping []string) error {
- enc, err := vt.db.Get(vtKey)
- if err != nil {
- return err
- }
- r := bytes.NewReader(enc)
- var version uint
- if err := rlp.Decode(r, &version); err != nil {
- log.Error("Decoding value tracker state failed", "err", err)
- return err
- }
- if version != vtVersion {
- log.Error("Unknown ValueTracker version", "stored", version, "current", nvtVersion)
- return fmt.Errorf("unknown ValueTracker version %d (current version is %d)", version, vtVersion)
- }
- var vte valueTrackerEncV1
- if err := rlp.Decode(r, &vte); err != nil {
- log.Error("Decoding value tracker state failed", "err", err)
- return err
- }
- logOffset := utils.Fixed64(vte.ExpOffset)
- dt := time.Now().UnixNano() - int64(vte.SavedAt)
- if dt > 0 {
- logOffset += utils.Float64ToFixed64(float64(dt) * vt.offlineExpRate / math.Log(2))
- }
- vt.statsExpirer.SetLogOffset(vt.clock.Now(), logOffset)
- vt.rtStats = vte.RtStats
- vt.mappings = vte.Mappings
- vt.currentMapping = -1
-loop:
- for i, m := range vt.mappings {
- if len(m) != len(mapping) {
- continue loop
- }
- for j, s := range mapping {
- if m[j] != s {
- continue loop
- }
- }
- vt.currentMapping = i
- break
- }
- if vt.currentMapping == -1 {
- vt.currentMapping = len(vt.mappings)
- vt.mappings = append(vt.mappings, mapping)
- }
- if int(vte.RefBasketMapping) == vt.currentMapping {
- vt.refBasket.basket = vte.RefBasket
- } else {
- if vte.RefBasketMapping >= uint(len(vt.mappings)) {
- log.Error("Unknown request basket mapping", "stored", vte.RefBasketMapping, "current", vt.currentMapping)
- return fmt.Errorf("unknown request basket mapping %d (current version is %d)", vte.RefBasketMapping, vt.currentMapping)
- }
- vt.refBasket.basket = vte.RefBasket.convertMapping(vt.mappings[vte.RefBasketMapping], mapping, vt.initRefBasket)
- }
- return nil
-}
-
-// saveToDb saves the value tracker's state to the database
-func (vt *ValueTracker) saveToDb() {
- vte := valueTrackerEncV1{
- Mappings: vt.mappings,
- RefBasketMapping: uint(vt.currentMapping),
- RefBasket: vt.refBasket.basket,
- RtStats: vt.rtStats,
- ExpOffset: uint64(vt.statsExpirer.LogOffset(vt.clock.Now())),
- SavedAt: uint64(time.Now().UnixNano()),
- }
- enc1, err := rlp.EncodeToBytes(uint(vtVersion))
- if err != nil {
- log.Error("Encoding value tracker state failed", "err", err)
- return
- }
- enc2, err := rlp.EncodeToBytes(&vte)
- if err != nil {
- log.Error("Encoding value tracker state failed", "err", err)
- return
- }
- if err := vt.db.Put(vtKey, append(enc1, enc2...)); err != nil {
- log.Error("Saving value tracker state failed", "err", err)
- }
-}
-
-// Stop saves the value tracker's state and each loaded node's individual state and
-// returns after shutting the internal goroutines down.
-func (vt *ValueTracker) Stop() {
- quit := make(chan struct{})
- vt.quit <- quit
- <-quit
- vt.lock.Lock()
- vt.periodicUpdate()
- for id, nv := range vt.connected {
- vt.saveNode(id, nv)
- }
- vt.connected = nil
- vt.saveToDb()
- vt.lock.Unlock()
-}
-
-// Register adds a server node to the value tracker
-func (vt *ValueTracker) Register(id enode.ID) *NodeValueTracker {
- vt.lock.Lock()
- defer vt.lock.Unlock()
-
- if vt.connected == nil {
- // ValueTracker has already been stopped
- return nil
- }
- nv := vt.loadOrNewNode(id)
- reqTypeCount := len(vt.refBasket.reqValues)
- nv.reqCosts = make([]uint64, reqTypeCount)
- nv.lastTransfer = vt.clock.Now()
- nv.reqValues = vt.refBasket.reqValues
- nv.basket.init(reqTypeCount)
-
- vt.connected[id] = nv
- return nv
-}
-
-// Unregister removes a server node from the value tracker
-func (vt *ValueTracker) Unregister(id enode.ID) {
- vt.lock.Lock()
- defer vt.lock.Unlock()
-
- if nv := vt.connected[id]; nv != nil {
- vt.saveNode(id, nv)
- delete(vt.connected, id)
- }
-}
-
-// GetNode returns an individual server node's value tracker. If it did not exist before
-// then a new node is created.
-func (vt *ValueTracker) GetNode(id enode.ID) *NodeValueTracker {
- vt.lock.Lock()
- defer vt.lock.Unlock()
-
- return vt.loadOrNewNode(id)
-}
-
-// loadOrNewNode returns an individual server node's value tracker. If it did not exist before
-// then a new node is created.
-func (vt *ValueTracker) loadOrNewNode(id enode.ID) *NodeValueTracker {
- if nv, ok := vt.connected[id]; ok {
- return nv
- }
- nv := &NodeValueTracker{vt: vt, lastTransfer: vt.clock.Now()}
- enc, err := vt.db.Get(append(vtNodeKey, id[:]...))
- if err != nil {
- return nv
- }
- r := bytes.NewReader(enc)
- var version uint
- if err := rlp.Decode(r, &version); err != nil {
- log.Error("Failed to decode node value tracker", "id", id, "err", err)
- return nv
- }
- if version != nvtVersion {
- log.Error("Unknown NodeValueTracker version", "stored", version, "current", nvtVersion)
- return nv
- }
- var nve nodeValueTrackerEncV1
- if err := rlp.Decode(r, &nve); err != nil {
- log.Error("Failed to decode node value tracker", "id", id, "err", err)
- return nv
- }
- nv.rtStats = nve.RtStats
- nv.lastRtStats = nve.RtStats
- if int(nve.ServerBasketMapping) == vt.currentMapping {
- nv.basket.basket = nve.ServerBasket
- } else {
- if nve.ServerBasketMapping >= uint(len(vt.mappings)) {
- log.Error("Unknown request basket mapping", "stored", nve.ServerBasketMapping, "current", vt.currentMapping)
- return nv
- }
- nv.basket.basket = nve.ServerBasket.convertMapping(vt.mappings[nve.ServerBasketMapping], vt.mappings[vt.currentMapping], vt.initRefBasket)
- }
- return nv
-}
-
-// saveNode saves a server node's value tracker to the database
-func (vt *ValueTracker) saveNode(id enode.ID, nv *NodeValueTracker) {
- recentRtStats := nv.rtStats
- recentRtStats.SubStats(&nv.lastRtStats)
- vt.rtStats.AddStats(&recentRtStats)
- nv.lastRtStats = nv.rtStats
-
- nve := nodeValueTrackerEncV1{
- RtStats: nv.rtStats,
- ServerBasketMapping: uint(vt.currentMapping),
- ServerBasket: nv.basket.basket,
- }
- enc1, err := rlp.EncodeToBytes(uint(nvtVersion))
- if err != nil {
- log.Error("Failed to encode service value information", "id", id, "err", err)
- return
- }
- enc2, err := rlp.EncodeToBytes(&nve)
- if err != nil {
- log.Error("Failed to encode service value information", "id", id, "err", err)
- return
- }
- if err := vt.db.Put(append(vtNodeKey, id[:]...), append(enc1, enc2...)); err != nil {
- log.Error("Failed to save service value information", "id", id, "err", err)
- }
-}
-
-// RtStats returns the global response time distribution statistics
-func (vt *ValueTracker) RtStats() ResponseTimeStats {
- vt.lock.Lock()
- defer vt.lock.Unlock()
-
- vt.periodicUpdate()
- return vt.rtStats
-}
-
-// periodicUpdate transfers individual node data to the global statistics, normalizes
-// the reference basket and updates request values. The global state is also saved to
-// the database with each update.
-func (vt *ValueTracker) periodicUpdate() {
- now := vt.clock.Now()
- vt.statsExpLock.Lock()
- vt.statsExpFactor = utils.ExpFactor(vt.statsExpirer.LogOffset(now))
- vt.statsExpLock.Unlock()
-
- for _, nv := range vt.connected {
- basket, rtStats := nv.transferStats(now, vt.transferRate)
- vt.refBasket.add(basket)
- vt.rtStats.AddStats(&rtStats)
- }
- vt.refBasket.normalize()
- vt.refBasket.updateReqValues()
- for _, nv := range vt.connected {
- nv.updateCosts(nv.reqCosts, vt.refBasket.reqValues, vt.refBasket.reqValueFactor(nv.reqCosts))
- }
- vt.saveToDb()
-}
-
-type RequestStatsItem struct {
- Name string
- ReqAmount, ReqValue float64
-}
-
-// RequestStats returns the current contents of the reference request basket, with
-// request values meaning average per request rather than total.
-func (vt *ValueTracker) RequestStats() []RequestStatsItem {
- vt.statsExpLock.RLock()
- expFactor := vt.statsExpFactor
- vt.statsExpLock.RUnlock()
- vt.lock.Lock()
- defer vt.lock.Unlock()
-
- vt.periodicUpdate()
- res := make([]RequestStatsItem, len(vt.refBasket.basket.items))
- for i, item := range vt.refBasket.basket.items {
- res[i].Name = vt.mappings[vt.currentMapping][i]
- res[i].ReqAmount = expFactor.Value(float64(item.amount)/basketFactor, vt.refBasket.basket.exp)
- res[i].ReqValue = vt.refBasket.reqValues[i]
- }
- return res
-}
diff --git a/les/vflux/client/valuetracker_test.go b/les/vflux/client/valuetracker_test.go
deleted file mode 100644
index 332d65ee5122..000000000000
--- a/les/vflux/client/valuetracker_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "math"
- "math/rand"
- "strconv"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
- "github.com/ethereum/go-ethereum/p2p/enode"
-
- "github.com/ethereum/go-ethereum/les/utils"
-)
-
-const (
- testReqTypes = 3
- testNodeCount = 5
- testReqCount = 10000
- testRounds = 10
-)
-
-func TestValueTracker(t *testing.T) {
- t.Parallel()
-
- db := memorydb.New()
- clock := &mclock.Simulated{}
- requestList := make([]RequestInfo, testReqTypes)
- relPrices := make([]float64, testReqTypes)
- totalAmount := make([]uint64, testReqTypes)
- for i := range requestList {
- requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
- totalAmount[i] = 1
- relPrices[i] = rand.Float64() + 0.1
- }
- nodes := make([]*NodeValueTracker, testNodeCount)
- for round := 0; round < testRounds; round++ {
- makeRequests := round < testRounds-2
- useExpiration := round == testRounds-1
- var expRate float64
- if useExpiration {
- expRate = math.Log(2) / float64(time.Hour*100)
- }
-
- vt := NewValueTracker(db, clock, requestList, time.Minute, 1/float64(time.Hour), expRate, expRate)
- updateCosts := func(i int) {
- costList := make([]uint64, testReqTypes)
- baseCost := rand.Float64()*10000000 + 100000
- for j := range costList {
- costList[j] = uint64(baseCost * relPrices[j])
- }
- nodes[i].UpdateCosts(costList)
- }
- for i := range nodes {
- nodes[i] = vt.Register(enode.ID{byte(i)})
- updateCosts(i)
- }
- if makeRequests {
- for i := 0; i < testReqCount; i++ {
- reqType := rand.Intn(testReqTypes)
- reqAmount := rand.Intn(10) + 1
- node := rand.Intn(testNodeCount)
- respTime := time.Duration((rand.Float64() + 1) * float64(time.Second) * float64(node+1) / testNodeCount)
- totalAmount[reqType] += uint64(reqAmount)
- nodes[node].Served([]ServedRequest{{uint32(reqType), uint32(reqAmount)}}, respTime)
- clock.Run(time.Second)
- }
- } else {
- clock.Run(time.Hour * 100)
- if useExpiration {
- for i, a := range totalAmount {
- totalAmount[i] = a / 2
- }
- }
- }
- vt.Stop()
- var sumrp, sumrv float64
- for i, rp := range relPrices {
- sumrp += rp
- sumrv += vt.refBasket.reqValues[i]
- }
- for i, rp := range relPrices {
- ratio := vt.refBasket.reqValues[i] * sumrp / (rp * sumrv)
- if ratio < 0.99 || ratio > 1.01 {
- t.Errorf("reqValues (%v) does not match relPrices (%v)", vt.refBasket.reqValues, relPrices)
- break
- }
- }
- exp := utils.ExpFactor(vt.StatsExpirer().LogOffset(clock.Now()))
- basketAmount := make([]uint64, testReqTypes)
- for i, bi := range vt.refBasket.basket.items {
- basketAmount[i] += uint64(exp.Value(float64(bi.amount), vt.refBasket.basket.exp))
- }
- if makeRequests {
- // if we did not make requests in this round then we expect all amounts to be
- // in the reference basket
- for _, node := range nodes {
- for i, bi := range node.basket.basket.items {
- basketAmount[i] += uint64(exp.Value(float64(bi.amount), node.basket.basket.exp))
- }
- }
- }
- for i, a := range basketAmount {
- amount := a / basketFactor
- if amount+10 < totalAmount[i] || amount > totalAmount[i]+10 {
- t.Errorf("totalAmount[%d] mismatch in round %d (expected %d, got %d)", i, round, totalAmount[i], amount)
- }
- }
- var sumValue float64
- for _, node := range nodes {
- s := node.RtStats()
- sumValue += s.Value(maxResponseWeights, exp)
- }
- s := vt.RtStats()
- mainValue := s.Value(maxResponseWeights, exp)
- if sumValue < mainValue-10 || sumValue > mainValue+10 {
- t.Errorf("Main rtStats value does not match sum of node rtStats values in round %d (main %v, sum %v)", round, mainValue, sumValue)
- }
- }
-}
diff --git a/les/vflux/client/wrsiterator.go b/les/vflux/client/wrsiterator.go
deleted file mode 100644
index 1b37cba6e5de..000000000000
--- a/les/vflux/client/wrsiterator.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-// WrsIterator returns nodes from the specified selectable set with a weighted random
-// selection. Selection weights are provided by a callback function.
-type WrsIterator struct {
- lock sync.Mutex
- cond *sync.Cond
-
- ns *nodestate.NodeStateMachine
- wrs *utils.WeightedRandomSelect
- nextNode *enode.Node
- closed bool
-}
-
-// NewWrsIterator creates a new WrsIterator. Nodes are selectable if they have all the required
-// and none of the disabled flags set. When a node is selected the selectedFlag is set which also
-// disables further selectability until it is removed or times out.
-func NewWrsIterator(ns *nodestate.NodeStateMachine, requireFlags, disableFlags nodestate.Flags, weightField nodestate.Field) *WrsIterator {
- wfn := func(i interface{}) uint64 {
- n := ns.GetNode(i.(enode.ID))
- if n == nil {
- return 0
- }
- wt, _ := ns.GetField(n, weightField).(uint64)
- return wt
- }
-
- w := &WrsIterator{
- ns: ns,
- wrs: utils.NewWeightedRandomSelect(wfn),
- }
- w.cond = sync.NewCond(&w.lock)
-
- ns.SubscribeField(weightField, func(n *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- if state.HasAll(requireFlags) && state.HasNone(disableFlags) {
- w.lock.Lock()
- w.wrs.Update(n.ID())
- w.lock.Unlock()
- w.cond.Signal()
- }
- })
-
- ns.SubscribeState(requireFlags.Or(disableFlags), func(n *enode.Node, oldState, newState nodestate.Flags) {
- oldMatch := oldState.HasAll(requireFlags) && oldState.HasNone(disableFlags)
- newMatch := newState.HasAll(requireFlags) && newState.HasNone(disableFlags)
- if newMatch == oldMatch {
- return
- }
-
- w.lock.Lock()
- if newMatch {
- w.wrs.Update(n.ID())
- } else {
- w.wrs.Remove(n.ID())
- }
- w.lock.Unlock()
- w.cond.Signal()
- })
- return w
-}
-
-// Next selects the next node.
-func (w *WrsIterator) Next() bool {
- w.nextNode = w.chooseNode()
- return w.nextNode != nil
-}
-
-func (w *WrsIterator) chooseNode() *enode.Node {
- w.lock.Lock()
- defer w.lock.Unlock()
-
- for {
- for !w.closed && w.wrs.IsEmpty() {
- w.cond.Wait()
- }
- if w.closed {
- return nil
- }
- // Choose the next node at random. Even though w.wrs is guaranteed
- // non-empty here, Choose might return nil if all items have weight
- // zero.
- if c := w.wrs.Choose(); c != nil {
- id := c.(enode.ID)
- w.wrs.Remove(id)
- return w.ns.GetNode(id)
- }
- }
-}
-
-// Close ends the iterator.
-func (w *WrsIterator) Close() {
- w.lock.Lock()
- w.closed = true
- w.lock.Unlock()
- w.cond.Signal()
-}
-
-// Node returns the current node.
-func (w *WrsIterator) Node() *enode.Node {
- w.lock.Lock()
- defer w.lock.Unlock()
- return w.nextNode
-}
diff --git a/les/vflux/client/wrsiterator_test.go b/les/vflux/client/wrsiterator_test.go
deleted file mode 100644
index f6eb2d8813e3..000000000000
--- a/les/vflux/client/wrsiterator_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package client
-
-import (
- "reflect"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-var (
- testSetup = &nodestate.Setup{}
- sfTest1 = testSetup.NewFlag("test1")
- sfTest2 = testSetup.NewFlag("test2")
- sfTest3 = testSetup.NewFlag("test3")
- sfTest4 = testSetup.NewFlag("test4")
- sfiTestWeight = testSetup.NewField("nodeWeight", reflect.TypeOf(uint64(0)))
-)
-
-const iterTestNodeCount = 6
-
-func TestWrsIterator(t *testing.T) {
- t.Parallel()
-
- ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
- w := NewWrsIterator(ns, sfTest2, sfTest3.Or(sfTest4), sfiTestWeight)
- ns.Start()
- for i := 1; i <= iterTestNodeCount; i++ {
- ns.SetState(testNode(i), sfTest1, nodestate.Flags{}, 0)
- ns.SetField(testNode(i), sfiTestWeight, uint64(1))
- }
- next := func() int {
- ch := make(chan struct{})
- go func() {
- w.Next()
- close(ch)
- }()
- select {
- case <-ch:
- case <-time.After(time.Second * 5):
- t.Fatalf("Iterator.Next() timeout")
- }
- node := w.Node()
- ns.SetState(node, sfTest4, nodestate.Flags{}, 0)
- return testNodeIndex(node.ID())
- }
- set := make(map[int]bool)
- expset := func() {
- for len(set) > 0 {
- n := next()
- if !set[n] {
- t.Errorf("Item returned by iterator not in the expected set (got %d)", n)
- }
- delete(set, n)
- }
- }
-
- ns.SetState(testNode(1), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(2), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(3), sfTest2, nodestate.Flags{}, 0)
- set[1] = true
- set[2] = true
- set[3] = true
- expset()
- ns.SetState(testNode(4), sfTest2, nodestate.Flags{}, 0)
- ns.SetState(testNode(5), sfTest2.Or(sfTest3), nodestate.Flags{}, 0)
- ns.SetState(testNode(6), sfTest2, nodestate.Flags{}, 0)
- set[4] = true
- set[6] = true
- expset()
- ns.SetField(testNode(2), sfiTestWeight, uint64(0))
- ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(2), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
- set[1] = true
- set[3] = true
- expset()
- ns.SetField(testNode(2), sfiTestWeight, uint64(1))
- ns.SetState(testNode(2), nodestate.Flags{}, sfTest2, 0)
- ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
- ns.SetState(testNode(2), sfTest2, sfTest4, 0)
- ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
- set[1] = true
- set[2] = true
- set[3] = true
- expset()
- ns.Stop()
-}
diff --git a/les/vflux/requests.go b/les/vflux/requests.go
deleted file mode 100644
index 5abae2f537c2..000000000000
--- a/les/vflux/requests.go
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2021 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 .
-
-package vflux
-
-import (
- "errors"
- "math"
- "math/big"
-
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var ErrNoReply = errors.New("no reply for given request")
-
-const (
- MaxRequestLength = 16 // max number of individual requests in a batch
- CapacityQueryName = "cq"
- CapacityQueryMaxLen = 16
-)
-
-type (
- // Request describes a single vflux request inside a batch. Service and request
- // type are identified by strings, parameters are RLP encoded.
- Request struct {
- Service, Name string
- Params []byte
- }
- // Requests are a batch of vflux requests
- Requests []Request
-
- // Replies are the replies to a batch of requests
- Replies [][]byte
-
- // CapacityQueryReq is the encoding format of the capacity query
- CapacityQueryReq struct {
- Bias uint64 // seconds
- AddTokens []IntOrInf
- }
- // CapacityQueryReply is the encoding format of the response to the capacity query
- CapacityQueryReply []uint64
-)
-
-// Add encodes and adds a new request to the batch
-func (r *Requests) Add(service, name string, val interface{}) (int, error) {
- enc, err := rlp.EncodeToBytes(val)
- if err != nil {
- return -1, err
- }
- *r = append(*r, Request{
- Service: service,
- Name: name,
- Params: enc,
- })
- return len(*r) - 1, nil
-}
-
-// Get decodes the reply to the i-th request in the batch
-func (r Replies) Get(i int, val interface{}) error {
- if i < 0 || i >= len(r) {
- return ErrNoReply
- }
- return rlp.DecodeBytes(r[i], val)
-}
-
-const (
- IntNonNegative = iota
- IntNegative
- IntPlusInf
- IntMinusInf
-)
-
-// IntOrInf is the encoding format for arbitrary length signed integers that can also
-// hold the values of +Inf or -Inf
-type IntOrInf struct {
- Type uint8
- Value big.Int
-}
-
-// BigInt returns the value as a big.Int or panics if the value is infinity
-func (i *IntOrInf) BigInt() *big.Int {
- switch i.Type {
- case IntNonNegative:
- return new(big.Int).Set(&i.Value)
- case IntNegative:
- return new(big.Int).Neg(&i.Value)
- case IntPlusInf:
- panic(nil) // caller should check Inf() before trying to convert to big.Int
- case IntMinusInf:
- panic(nil)
- }
- return &big.Int{} // invalid type decodes to 0 value
-}
-
-// Inf returns 1 if the value is +Inf, -1 if it is -Inf, 0 otherwise
-func (i *IntOrInf) Inf() int {
- switch i.Type {
- case IntPlusInf:
- return 1
- case IntMinusInf:
- return -1
- }
- return 0 // invalid type decodes to 0 value
-}
-
-// Int64 limits the value between MinInt64 and MaxInt64 (even if it is +-Inf) and returns an int64 type
-func (i *IntOrInf) Int64() int64 {
- switch i.Type {
- case IntNonNegative:
- if i.Value.IsInt64() {
- return i.Value.Int64()
- } else {
- return math.MaxInt64
- }
- case IntNegative:
- if i.Value.IsInt64() {
- return -i.Value.Int64()
- } else {
- return math.MinInt64
- }
- case IntPlusInf:
- return math.MaxInt64
- case IntMinusInf:
- return math.MinInt64
- }
- return 0 // invalid type decodes to 0 value
-}
-
-// SetBigInt sets the value to the given big.Int
-func (i *IntOrInf) SetBigInt(v *big.Int) {
- if v.Sign() >= 0 {
- i.Type = IntNonNegative
- i.Value.Set(v)
- } else {
- i.Type = IntNegative
- i.Value.Neg(v)
- }
-}
-
-// SetInt64 sets the value to the given int64. Note that MaxInt64 translates to +Inf
-// while MinInt64 translates to -Inf.
-func (i *IntOrInf) SetInt64(v int64) {
- if v >= 0 {
- if v == math.MaxInt64 {
- i.Type = IntPlusInf
- } else {
- i.Type = IntNonNegative
- i.Value.SetInt64(v)
- }
- } else {
- if v == math.MinInt64 {
- i.Type = IntMinusInf
- } else {
- i.Type = IntNegative
- i.Value.SetInt64(-v)
- }
- }
-}
-
-// SetInf sets the value to +Inf or -Inf
-func (i *IntOrInf) SetInf(sign int) {
- if sign == 1 {
- i.Type = IntPlusInf
- } else {
- i.Type = IntMinusInf
- }
-}
diff --git a/les/vflux/server/balance.go b/les/vflux/server/balance.go
deleted file mode 100644
index b09f7bb5012b..000000000000
--- a/les/vflux/server/balance.go
+++ /dev/null
@@ -1,693 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "errors"
- "math"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-var errBalanceOverflow = errors.New("balance overflow")
-
-const maxBalance = math.MaxInt64 // maximum allowed balance value
-
-const (
- balanceCallbackUpdate = iota // called when priority drops below the last minimum estimate
- balanceCallbackZero // called when priority drops to zero (positive balance exhausted)
- balanceCallbackCount // total number of balance callbacks
-)
-
-// PriceFactors determine the pricing policy (may apply either to positive or
-// negative balances which may have different factors).
-// - TimeFactor is cost unit per nanosecond of connection time
-// - CapacityFactor is cost unit per nanosecond of connection time per 1000000 capacity
-// - RequestFactor is cost unit per request "realCost" unit
-type PriceFactors struct {
- TimeFactor, CapacityFactor, RequestFactor float64
-}
-
-// connectionPrice returns the price of connection per nanosecond at the given capacity
-// and the estimated average request cost.
-func (p PriceFactors) connectionPrice(cap uint64, avgReqCost float64) float64 {
- return p.TimeFactor + float64(cap)*p.CapacityFactor/1000000 + p.RequestFactor*avgReqCost
-}
-
-type (
- // nodePriority interface provides current and estimated future priorities on demand
- nodePriority interface {
- // priority should return the current priority of the node (higher is better)
- priority(cap uint64) int64
- // estimatePriority should return a lower estimate for the minimum of the node priority
- // value starting from the current moment until the given time. If the priority goes
- // under the returned estimate before the specified moment then it is the caller's
- // responsibility to signal with updateFlag.
- estimatePriority(cap uint64, addBalance int64, future, bias time.Duration, update bool) int64
- }
-
- // ReadOnlyBalance provides read-only operations on the node balance
- ReadOnlyBalance interface {
- nodePriority
- GetBalance() (uint64, uint64)
- GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue)
- GetPriceFactors() (posFactor, negFactor PriceFactors)
- }
-
- // ConnectedBalance provides operations permitted on connected nodes (non-read-only
- // operations are not permitted inside a BalanceOperation)
- ConnectedBalance interface {
- ReadOnlyBalance
- SetPriceFactors(posFactor, negFactor PriceFactors)
- RequestServed(cost uint64) uint64
- }
-
- // AtomicBalanceOperator provides operations permitted in an atomic BalanceOperation
- AtomicBalanceOperator interface {
- ReadOnlyBalance
- AddBalance(amount int64) (uint64, uint64, error)
- SetBalance(pos, neg uint64) error
- }
-)
-
-// nodeBalance keeps track of the positive and negative balances of a connected
-// client and calculates actual and projected future priority values.
-// Implements nodePriority interface.
-type nodeBalance struct {
- bt *balanceTracker
- lock sync.RWMutex
- node *enode.Node
- connAddress string
- active, hasPriority, setFlags bool
- capacity uint64
- balance balance
- posFactor, negFactor PriceFactors
- sumReqCost uint64
- lastUpdate, nextUpdate, initTime mclock.AbsTime
- updateEvent mclock.Timer
- // since only a limited and fixed number of callbacks are needed, they are
- // stored in a fixed size array ordered by priority threshold.
- callbacks [balanceCallbackCount]balanceCallback
- // callbackIndex maps balanceCallback constants to callbacks array indexes (-1 if not active)
- callbackIndex [balanceCallbackCount]int
- callbackCount int // number of active callbacks
-}
-
-// balance represents a pair of positive and negative balances
-type balance struct {
- pos, neg utils.ExpiredValue
- posExp, negExp utils.ValueExpirer
-}
-
-// posValue returns the value of positive balance at a given timestamp.
-func (b balance) posValue(now mclock.AbsTime) uint64 {
- return b.pos.Value(b.posExp.LogOffset(now))
-}
-
-// negValue returns the value of negative balance at a given timestamp.
-func (b balance) negValue(now mclock.AbsTime) uint64 {
- return b.neg.Value(b.negExp.LogOffset(now))
-}
-
-// addValue adds the value of a given amount to the balance. The original value and
-// updated value will also be returned if the addition is successful.
-// Returns the error if the given value is too large and the value overflows.
-func (b *balance) addValue(now mclock.AbsTime, amount int64, pos bool, force bool) (uint64, uint64, int64, error) {
- var (
- val utils.ExpiredValue
- offset utils.Fixed64
- )
- if pos {
- offset, val = b.posExp.LogOffset(now), b.pos
- } else {
- offset, val = b.negExp.LogOffset(now), b.neg
- }
- old := val.Value(offset)
- if amount > 0 && (amount > maxBalance || old > maxBalance-uint64(amount)) {
- if !force {
- return old, 0, 0, errBalanceOverflow
- }
- val = utils.ExpiredValue{}
- amount = maxBalance
- }
- net := val.Add(amount, offset)
- if pos {
- b.pos = val
- } else {
- b.neg = val
- }
- return old, val.Value(offset), net, nil
-}
-
-// setValue sets the internal balance amount to the given values. Returns the
-// error if the given value is too large.
-func (b *balance) setValue(now mclock.AbsTime, pos uint64, neg uint64) error {
- if pos > maxBalance || neg > maxBalance {
- return errBalanceOverflow
- }
- var pb, nb utils.ExpiredValue
- pb.Add(int64(pos), b.posExp.LogOffset(now))
- nb.Add(int64(neg), b.negExp.LogOffset(now))
- b.pos = pb
- b.neg = nb
- return nil
-}
-
-// balanceCallback represents a single callback that is activated when client priority
-// reaches the given threshold
-type balanceCallback struct {
- id int
- threshold int64
- callback func()
-}
-
-// GetBalance returns the current positive and negative balance.
-func (n *nodeBalance) GetBalance() (uint64, uint64) {
- n.lock.Lock()
- defer n.lock.Unlock()
-
- now := n.bt.clock.Now()
- n.updateBalance(now)
- return n.balance.posValue(now), n.balance.negValue(now)
-}
-
-// GetRawBalance returns the current positive and negative balance
-// but in the raw(expired value) format.
-func (n *nodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) {
- n.lock.Lock()
- defer n.lock.Unlock()
-
- now := n.bt.clock.Now()
- n.updateBalance(now)
- return n.balance.pos, n.balance.neg
-}
-
-// AddBalance adds the given amount to the positive balance and returns the balance
-// before and after the operation. Exceeding maxBalance results in an error (balance is
-// unchanged) while adding a negative amount higher than the current balance results in
-// zero balance.
-// Note: this function should run inside a NodeStateMachine operation
-func (n *nodeBalance) AddBalance(amount int64) (uint64, uint64, error) {
- var (
- err error
- old, new uint64
- now = n.bt.clock.Now()
- callbacks []func()
- setPriority bool
- )
- // Operation with holding the lock
- n.bt.updateTotalBalance(n, func() bool {
- n.updateBalance(now)
- if old, new, _, err = n.balance.addValue(now, amount, true, false); err != nil {
- return false
- }
- callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus()
- n.storeBalance(true, false)
- return true
- })
- if err != nil {
- return old, old, err
- }
- // Operation without holding the lock
- for _, cb := range callbacks {
- cb()
- }
- if n.setFlags {
- if setPriority {
- n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0)
- }
- // Note: priority flag is automatically removed by the zero priority callback if necessary
- n.signalPriorityUpdate()
- }
- return old, new, nil
-}
-
-// SetBalance sets the positive and negative balance to the given values
-// Note: this function should run inside a NodeStateMachine operation
-func (n *nodeBalance) SetBalance(pos, neg uint64) error {
- var (
- now = n.bt.clock.Now()
- callbacks []func()
- setPriority bool
- )
- // Operation with holding the lock
- n.bt.updateTotalBalance(n, func() bool {
- n.updateBalance(now)
- if err := n.balance.setValue(now, pos, neg); err != nil {
- return false
- }
- callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus()
- n.storeBalance(true, true)
- return true
- })
- // Operation without holding the lock
- for _, cb := range callbacks {
- cb()
- }
- if n.setFlags {
- if setPriority {
- n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0)
- }
- // Note: priority flag is automatically removed by the zero priority callback if necessary
- n.signalPriorityUpdate()
- }
- return nil
-}
-
-// RequestServed should be called after serving a request for the given peer
-func (n *nodeBalance) RequestServed(cost uint64) (newBalance uint64) {
- n.lock.Lock()
-
- var (
- check bool
- fcost = float64(cost)
- now = n.bt.clock.Now()
- )
- n.updateBalance(now)
- if !n.balance.pos.IsZero() {
- posCost := -int64(fcost * n.posFactor.RequestFactor)
- if posCost == 0 {
- fcost = 0
- newBalance = n.balance.posValue(now)
- } else {
- var net int64
- _, newBalance, net, _ = n.balance.addValue(now, posCost, true, false)
- if posCost == net {
- fcost = 0
- } else {
- fcost *= 1 - float64(net)/float64(posCost)
- }
- check = true
- }
- }
- if fcost > 0 && n.negFactor.RequestFactor != 0 {
- n.balance.addValue(now, int64(fcost*n.negFactor.RequestFactor), false, false)
- check = true
- }
- n.sumReqCost += cost
-
- var callbacks []func()
- if check {
- callbacks = n.checkCallbacks(now)
- }
- n.lock.Unlock()
-
- if callbacks != nil {
- n.bt.ns.Operation(func() {
- for _, cb := range callbacks {
- cb()
- }
- })
- }
- return
-}
-
-// priority returns the actual priority based on the current balance
-func (n *nodeBalance) priority(capacity uint64) int64 {
- n.lock.Lock()
- defer n.lock.Unlock()
-
- now := n.bt.clock.Now()
- n.updateBalance(now)
- return n.balanceToPriority(now, n.balance, capacity)
-}
-
-// EstMinPriority gives a lower estimate for the priority at a given time in the future.
-// An average request cost per time is assumed that is twice the average cost per time
-// in the current session.
-// If update is true then a priority callback is added that turns updateFlag on and off
-// in case the priority goes below the estimated minimum.
-func (n *nodeBalance) estimatePriority(capacity uint64, addBalance int64, future, bias time.Duration, update bool) int64 {
- n.lock.Lock()
- defer n.lock.Unlock()
-
- now := n.bt.clock.Now()
- n.updateBalance(now)
-
- b := n.balance // copy the balance
- if addBalance != 0 {
- b.addValue(now, addBalance, true, true)
- }
- if future > 0 {
- var avgReqCost float64
- dt := time.Duration(n.lastUpdate - n.initTime)
- if dt > time.Second {
- avgReqCost = float64(n.sumReqCost) * 2 / float64(dt)
- }
- b = n.reducedBalance(b, now, future, capacity, avgReqCost)
- }
- if bias > 0 {
- b = n.reducedBalance(b, now.Add(future), bias, capacity, 0)
- }
- pri := n.balanceToPriority(now, b, capacity)
- // Ensure that biased estimates are always lower than actual priorities, even if
- // the bias is very small.
- // This ensures that two nodes will not ping-pong update signals forever if both of
- // them have zero estimated priority drop in the projected future.
- current := n.balanceToPriority(now, n.balance, capacity)
- if pri >= current {
- pri = current - 1
- }
- if update {
- n.addCallback(balanceCallbackUpdate, pri, n.signalPriorityUpdate)
- }
- return pri
-}
-
-// SetPriceFactors sets the price factors. TimeFactor is the price of a nanosecond of
-// connection while RequestFactor is the price of a request cost unit.
-func (n *nodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) {
- n.lock.Lock()
- now := n.bt.clock.Now()
- n.updateBalance(now)
- n.posFactor, n.negFactor = posFactor, negFactor
- callbacks := n.checkCallbacks(now)
- n.lock.Unlock()
- if callbacks != nil {
- n.bt.ns.Operation(func() {
- for _, cb := range callbacks {
- cb()
- }
- })
- }
-}
-
-// GetPriceFactors returns the price factors
-func (n *nodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) {
- n.lock.Lock()
- defer n.lock.Unlock()
-
- return n.posFactor, n.negFactor
-}
-
-// activate starts time/capacity cost deduction.
-func (n *nodeBalance) activate() {
- n.bt.updateTotalBalance(n, func() bool {
- if n.active {
- return false
- }
- n.active = true
- n.lastUpdate = n.bt.clock.Now()
- return true
- })
-}
-
-// deactivate stops time/capacity cost deduction and saves the balances in the database
-func (n *nodeBalance) deactivate() {
- n.bt.updateTotalBalance(n, func() bool {
- if !n.active {
- return false
- }
- n.updateBalance(n.bt.clock.Now())
- if n.updateEvent != nil {
- n.updateEvent.Stop()
- n.updateEvent = nil
- }
- n.storeBalance(true, true)
- n.active = false
- return true
- })
-}
-
-// updateBalance updates balance based on the time factor
-func (n *nodeBalance) updateBalance(now mclock.AbsTime) {
- if n.active && now > n.lastUpdate {
- n.balance = n.reducedBalance(n.balance, n.lastUpdate, time.Duration(now-n.lastUpdate), n.capacity, 0)
- n.lastUpdate = now
- }
-}
-
-// storeBalance stores the positive and/or negative balance of the node in the database
-func (n *nodeBalance) storeBalance(pos, neg bool) {
- if pos {
- n.bt.storeBalance(n.node.ID().Bytes(), false, n.balance.pos)
- }
- if neg {
- n.bt.storeBalance([]byte(n.connAddress), true, n.balance.neg)
- }
-}
-
-// addCallback sets up a one-time callback to be called when priority reaches
-// the threshold. If it has already reached the threshold the callback is called
-// immediately.
-// Note: should be called while n.lock is held
-// Note 2: the callback function runs inside a NodeStateMachine operation
-func (n *nodeBalance) addCallback(id int, threshold int64, callback func()) {
- n.removeCallback(id)
- idx := 0
- for idx < n.callbackCount && threshold > n.callbacks[idx].threshold {
- idx++
- }
- for i := n.callbackCount - 1; i >= idx; i-- {
- n.callbackIndex[n.callbacks[i].id]++
- n.callbacks[i+1] = n.callbacks[i]
- }
- n.callbackCount++
- n.callbackIndex[id] = idx
- n.callbacks[idx] = balanceCallback{id, threshold, callback}
- now := n.bt.clock.Now()
- n.updateBalance(now)
- n.scheduleCheck(now)
-}
-
-// removeCallback removes the given callback and returns true if it was active
-// Note: should be called while n.lock is held
-func (n *nodeBalance) removeCallback(id int) bool {
- idx := n.callbackIndex[id]
- if idx == -1 {
- return false
- }
- n.callbackIndex[id] = -1
- for i := idx; i < n.callbackCount-1; i++ {
- n.callbackIndex[n.callbacks[i+1].id]--
- n.callbacks[i] = n.callbacks[i+1]
- }
- n.callbackCount--
- return true
-}
-
-// checkCallbacks checks whether the threshold of any of the active callbacks
-// have been reached and returns triggered callbacks.
-// Note: checkCallbacks assumes that the balance has been recently updated.
-func (n *nodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) {
- if n.callbackCount == 0 || n.capacity == 0 {
- return
- }
- pri := n.balanceToPriority(now, n.balance, n.capacity)
- for n.callbackCount != 0 && n.callbacks[n.callbackCount-1].threshold >= pri {
- n.callbackCount--
- n.callbackIndex[n.callbacks[n.callbackCount].id] = -1
- callbacks = append(callbacks, n.callbacks[n.callbackCount].callback)
- }
- n.scheduleCheck(now)
- return
-}
-
-// scheduleCheck sets up or updates a scheduled event to ensure that it will be called
-// again just after the next threshold has been reached.
-func (n *nodeBalance) scheduleCheck(now mclock.AbsTime) {
- if n.callbackCount != 0 {
- d, ok := n.timeUntil(n.callbacks[n.callbackCount-1].threshold)
- if !ok {
- n.nextUpdate = 0
- n.updateAfter(0)
- return
- }
- if n.nextUpdate == 0 || n.nextUpdate > now.Add(d) {
- if d > time.Second {
- // Note: if the scheduled update is not in the very near future then we
- // schedule the update a bit earlier. This way we do need to update a few
- // extra times but don't need to reschedule every time a processed request
- // brings the expected firing time a little bit closer.
- d = ((d - time.Second) * 7 / 8) + time.Second
- }
- n.nextUpdate = now.Add(d)
- n.updateAfter(d)
- }
- } else {
- n.nextUpdate = 0
- n.updateAfter(0)
- }
-}
-
-// updateAfter schedules a balance update and callback check in the future
-func (n *nodeBalance) updateAfter(dt time.Duration) {
- if n.updateEvent == nil || n.updateEvent.Stop() {
- if dt == 0 {
- n.updateEvent = nil
- } else {
- n.updateEvent = n.bt.clock.AfterFunc(dt, func() {
- var callbacks []func()
- n.lock.Lock()
- if n.callbackCount != 0 {
- now := n.bt.clock.Now()
- n.updateBalance(now)
- callbacks = n.checkCallbacks(now)
- }
- n.lock.Unlock()
- if callbacks != nil {
- n.bt.ns.Operation(func() {
- for _, cb := range callbacks {
- cb()
- }
- })
- }
- })
- }
- }
-}
-
-// balanceExhausted should be called when the positive balance is exhausted (priority goes to zero/negative)
-// Note: this function should run inside a NodeStateMachine operation
-func (n *nodeBalance) balanceExhausted() {
- n.lock.Lock()
- n.storeBalance(true, false)
- n.hasPriority = false
- n.lock.Unlock()
- if n.setFlags {
- n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.priorityFlag, 0)
- }
-}
-
-// checkPriorityStatus checks whether the node has gained priority status and sets the priority
-// callback and flag if necessary. It assumes that the balance has been recently updated.
-// Note that the priority flag has to be set by the caller after the mutex has been released.
-func (n *nodeBalance) checkPriorityStatus() bool {
- if !n.hasPriority && !n.balance.pos.IsZero() {
- n.hasPriority = true
- n.addCallback(balanceCallbackZero, 0, func() { n.balanceExhausted() })
- return true
- }
- return false
-}
-
-// signalPriorityUpdate signals that the priority fell below the previous minimum estimate
-// Note: this function should run inside a NodeStateMachine operation
-func (n *nodeBalance) signalPriorityUpdate() {
- n.bt.ns.SetStateSub(n.node, n.bt.setup.updateFlag, nodestate.Flags{}, 0)
- n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.updateFlag, 0)
-}
-
-// setCapacity updates the capacity value used for priority calculation
-// Note: capacity should never be zero
-// Note 2: this function should run inside a NodeStateMachine operation
-func (n *nodeBalance) setCapacity(capacity uint64) {
- n.lock.Lock()
- now := n.bt.clock.Now()
- n.updateBalance(now)
- n.capacity = capacity
- callbacks := n.checkCallbacks(now)
- n.lock.Unlock()
- for _, cb := range callbacks {
- cb()
- }
-}
-
-// balanceToPriority converts a balance to a priority value. Lower priority means
-// first to disconnect. Positive balance translates to positive priority. If positive
-// balance is zero then negative balance translates to a negative priority.
-func (n *nodeBalance) balanceToPriority(now mclock.AbsTime, b balance, capacity uint64) int64 {
- pos := b.posValue(now)
- if pos > 0 {
- return int64(pos / capacity)
- }
- return -int64(b.negValue(now))
-}
-
-// priorityToBalance converts a target priority to a requested balance value.
-// If the priority is negative, then minimal negative balance is returned;
-// otherwise the minimal positive balance is returned.
-func (n *nodeBalance) priorityToBalance(priority int64, capacity uint64) (uint64, uint64) {
- if priority > 0 {
- return uint64(priority) * n.capacity, 0
- }
- return 0, uint64(-priority)
-}
-
-// reducedBalance estimates the reduced balance at a given time in the future based
-// on the given balance, the time factor and an estimated average request cost per time ratio
-func (n *nodeBalance) reducedBalance(b balance, start mclock.AbsTime, dt time.Duration, capacity uint64, avgReqCost float64) balance {
- // since the costs are applied continuously during the dt time period we calculate
- // the expiration offset at the middle of the period
- var (
- at = start.Add(dt / 2)
- dtf = float64(dt)
- )
- if !b.pos.IsZero() {
- factor := n.posFactor.connectionPrice(capacity, avgReqCost)
- diff := -int64(dtf * factor)
- _, _, net, _ := b.addValue(at, diff, true, false)
- if net == diff {
- dtf = 0
- } else {
- dtf += float64(net) / factor
- }
- }
- if dtf > 0 {
- factor := n.negFactor.connectionPrice(capacity, avgReqCost)
- b.addValue(at, int64(dtf*factor), false, false)
- }
- return b
-}
-
-// timeUntil calculates the remaining time needed to reach a given priority level
-// assuming that no requests are processed until then. If the given level is never
-// reached then (0, false) is returned. If it has already been reached then (0, true)
-// is returned.
-// Note: the function assumes that the balance has been recently updated and
-// calculates the time starting from the last update.
-func (n *nodeBalance) timeUntil(priority int64) (time.Duration, bool) {
- var (
- now = n.bt.clock.Now()
- pos = n.balance.posValue(now)
- targetPos, targetNeg = n.priorityToBalance(priority, n.capacity)
- diffTime float64
- )
- if pos > 0 {
- timePrice := n.posFactor.connectionPrice(n.capacity, 0)
- if timePrice < 1e-100 {
- return 0, false
- }
- if targetPos > 0 {
- if targetPos > pos {
- return 0, true
- }
- diffTime = float64(pos-targetPos) / timePrice
- return time.Duration(diffTime), true
- } else {
- diffTime = float64(pos) / timePrice
- }
- } else {
- if targetPos > 0 {
- return 0, true
- }
- }
- neg := n.balance.negValue(now)
- if targetNeg > neg {
- timePrice := n.negFactor.connectionPrice(n.capacity, 0)
- if timePrice < 1e-100 {
- return 0, false
- }
- diffTime += float64(targetNeg-neg) / timePrice
- }
- return time.Duration(diffTime), true
-}
diff --git a/les/vflux/server/balance_test.go b/les/vflux/server/balance_test.go
deleted file mode 100644
index e1ff7bf4e9a3..000000000000
--- a/les/vflux/server/balance_test.go
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "math"
- "math/rand"
- "reflect"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-type zeroExpirer struct{}
-
-func (z zeroExpirer) SetRate(now mclock.AbsTime, rate float64) {}
-func (z zeroExpirer) SetLogOffset(now mclock.AbsTime, logOffset utils.Fixed64) {}
-func (z zeroExpirer) LogOffset(now mclock.AbsTime) utils.Fixed64 { return 0 }
-
-type balanceTestClient struct{}
-
-func (client balanceTestClient) FreeClientId() string { return "" }
-
-type balanceTestSetup struct {
- clock *mclock.Simulated
- db ethdb.KeyValueStore
- ns *nodestate.NodeStateMachine
- setup *serverSetup
- bt *balanceTracker
-}
-
-func newBalanceTestSetup(db ethdb.KeyValueStore, posExp, negExp utils.ValueExpirer) *balanceTestSetup {
- // Initialize and customize the setup for the balance testing
- clock := &mclock.Simulated{}
- setup := newServerSetup()
- setup.clientField = setup.setup.NewField("balanceTestClient", reflect.TypeOf(balanceTestClient{}))
-
- ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup)
- if posExp == nil {
- posExp = zeroExpirer{}
- }
- if negExp == nil {
- negExp = zeroExpirer{}
- }
- if db == nil {
- db = memorydb.New()
- }
- bt := newBalanceTracker(ns, setup, db, clock, posExp, negExp)
- ns.Start()
- return &balanceTestSetup{
- clock: clock,
- db: db,
- ns: ns,
- setup: setup,
- bt: bt,
- }
-}
-
-func (b *balanceTestSetup) newNode(capacity uint64) *nodeBalance {
- node := enode.SignNull(&enr.Record{}, enode.ID{})
- b.ns.SetField(node, b.setup.clientField, balanceTestClient{})
- if capacity != 0 {
- b.ns.SetField(node, b.setup.capacityField, capacity)
- }
- n, _ := b.ns.GetField(node, b.setup.balanceField).(*nodeBalance)
- return n
-}
-
-func (b *balanceTestSetup) setBalance(node *nodeBalance, pos, neg uint64) (err error) {
- b.bt.BalanceOperation(node.node.ID(), node.connAddress, func(balance AtomicBalanceOperator) {
- err = balance.SetBalance(pos, neg)
- })
- return
-}
-
-func (b *balanceTestSetup) addBalance(node *nodeBalance, add int64) (old, new uint64, err error) {
- b.bt.BalanceOperation(node.node.ID(), node.connAddress, func(balance AtomicBalanceOperator) {
- old, new, err = balance.AddBalance(add)
- })
- return
-}
-
-func (b *balanceTestSetup) stop() {
- b.bt.stop()
- b.ns.Stop()
-}
-
-func TestAddBalance(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
-
- node := b.newNode(1000)
- var inputs = []struct {
- delta int64
- expect [2]uint64
- total uint64
- expectErr bool
- }{
- {100, [2]uint64{0, 100}, 100, false},
- {-100, [2]uint64{100, 0}, 0, false},
- {-100, [2]uint64{0, 0}, 0, false},
- {1, [2]uint64{0, 1}, 1, false},
- {maxBalance, [2]uint64{0, 0}, 0, true},
- }
- for _, i := range inputs {
- old, new, err := b.addBalance(node, i.delta)
- if i.expectErr {
- if err == nil {
- t.Fatalf("Expect get error but nil")
- }
- continue
- } else if err != nil {
- t.Fatalf("Expect get no error but %v", err)
- }
- if old != i.expect[0] || new != i.expect[1] {
- t.Fatalf("Positive balance mismatch, got %v -> %v", old, new)
- }
- if b.bt.TotalTokenAmount() != i.total {
- t.Fatalf("Total positive balance mismatch, want %v, got %v", i.total, b.bt.TotalTokenAmount())
- }
- }
-}
-
-func TestSetBalance(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000)
-
- var inputs = []struct {
- pos, neg uint64
- }{
- {1000, 0},
- {0, 1000},
- {1000, 1000},
- }
- for _, i := range inputs {
- b.setBalance(node, i.pos, i.neg)
- pos, neg := node.GetBalance()
- if pos != i.pos {
- t.Fatalf("Positive balance mismatch, want %v, got %v", i.pos, pos)
- }
- if neg != i.neg {
- t.Fatalf("Negative balance mismatch, want %v, got %v", i.neg, neg)
- }
- }
-}
-
-func TestBalanceTimeCost(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000)
-
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
- b.setBalance(node, uint64(time.Minute), 0) // 1 minute time allowance
-
- var inputs = []struct {
- runTime time.Duration
- expPos uint64
- expNeg uint64
- }{
- {time.Second, uint64(time.Second * 59), 0},
- {0, uint64(time.Second * 59), 0},
- {time.Second * 59, 0, 0},
- {time.Second, 0, uint64(time.Second)},
- }
- for _, i := range inputs {
- b.clock.Run(i.runTime)
- if pos, _ := node.GetBalance(); pos != i.expPos {
- t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
- }
- if _, neg := node.GetBalance(); neg != i.expNeg {
- t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
- }
- }
-
- b.setBalance(node, uint64(time.Minute), 0) // Refill 1 minute time allowance
- for _, i := range inputs {
- b.clock.Run(i.runTime)
- if pos, _ := node.GetBalance(); pos != i.expPos {
- t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
- }
- if _, neg := node.GetBalance(); neg != i.expNeg {
- t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
- }
- }
-}
-
-func TestBalanceReqCost(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
-
- b.setBalance(node, uint64(time.Minute), 0) // 1 minute time serving time allowance
- var inputs = []struct {
- reqCost uint64
- expPos uint64
- expNeg uint64
- }{
- {uint64(time.Second), uint64(time.Second * 59), 0},
- {0, uint64(time.Second * 59), 0},
- {uint64(time.Second * 59), 0, 0},
- {uint64(time.Second), 0, uint64(time.Second)},
- }
- for _, i := range inputs {
- node.RequestServed(i.reqCost)
- if pos, _ := node.GetBalance(); pos != i.expPos {
- t.Fatalf("Positive balance mismatch, want %v, got %v", i.expPos, pos)
- }
- if _, neg := node.GetBalance(); neg != i.expNeg {
- t.Fatalf("Negative balance mismatch, want %v, got %v", i.expNeg, neg)
- }
- }
-}
-
-func TestBalanceToPriority(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
-
- var inputs = []struct {
- pos uint64
- neg uint64
- priority int64
- }{
- {1000, 0, 1},
- {2000, 0, 2}, // Higher balance, higher priority value
- {0, 0, 0},
- {0, 1000, -1000},
- }
- for _, i := range inputs {
- b.setBalance(node, i.pos, i.neg)
- priority := node.priority(1000)
- if priority != i.priority {
- t.Fatalf("priority mismatch, want %v, got %v", i.priority, priority)
- }
- }
-}
-
-func TestEstimatedPriority(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000000000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
- b.setBalance(node, uint64(time.Minute), 0)
- var inputs = []struct {
- runTime time.Duration // time cost
- futureTime time.Duration // diff of future time
- reqCost uint64 // single request cost
- priority int64 // expected estimated priority
- }{
- {time.Second, time.Second, 0, 58},
- {0, time.Second, 0, 58},
-
- // 2 seconds time cost, 1 second estimated time cost, 10^9 request cost,
- // 10^9 estimated request cost per second.
- {time.Second, time.Second, 1000000000, 55},
-
- // 3 seconds time cost, 3 second estimated time cost, 10^9*2 request cost,
- // 4*10^9 estimated request cost.
- {time.Second, 3 * time.Second, 1000000000, 48},
-
- // All positive balance is used up
- {time.Second * 55, 0, 0, -1},
-
- // 1 minute estimated time cost, 4/58 * 10^9 estimated request cost per sec.
- {0, time.Minute, 0, -int64(time.Minute) - int64(time.Second)*120/29},
- }
- for _, i := range inputs {
- b.clock.Run(i.runTime)
- node.RequestServed(i.reqCost)
- priority := node.estimatePriority(1000000000, 0, i.futureTime, 0, false)
- if priority != i.priority {
- t.Fatalf("Estimated priority mismatch, want %v, got %v", i.priority, priority)
- }
- }
-}
-
-func TestPositiveBalanceCounting(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
-
- var nodes []*nodeBalance
- for i := 0; i < 100; i += 1 {
- node := b.newNode(1000000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
- nodes = append(nodes, node)
- }
-
- // Allocate service token
- var sum uint64
- for i := 0; i < 100; i += 1 {
- amount := int64(rand.Intn(100) + 100)
- b.addBalance(nodes[i], amount)
- sum += uint64(amount)
- }
- if b.bt.TotalTokenAmount() != sum {
- t.Fatalf("Invalid token amount")
- }
-
- // Change client status
- for i := 0; i < 100; i += 1 {
- if rand.Intn(2) == 0 {
- b.ns.SetField(nodes[i].node, b.setup.capacityField, uint64(1))
- }
- }
- if b.bt.TotalTokenAmount() != sum {
- t.Fatalf("Invalid token amount")
- }
- for i := 0; i < 100; i += 1 {
- if rand.Intn(2) == 0 {
- b.ns.SetField(nodes[i].node, b.setup.capacityField, uint64(1))
- }
- }
- if b.bt.TotalTokenAmount() != sum {
- t.Fatalf("Invalid token amount")
- }
-}
-
-func TestCallbackChecking(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
-
- var inputs = []struct {
- priority int64
- expDiff time.Duration
- }{
- {500, time.Millisecond * 500},
- {0, time.Second},
- {-int64(time.Second), 2 * time.Second},
- }
- b.setBalance(node, uint64(time.Second), 0)
- for _, i := range inputs {
- diff, _ := node.timeUntil(i.priority)
- if diff != i.expDiff {
- t.Fatalf("Time difference mismatch, want %v, got %v", i.expDiff, diff)
- }
- }
-}
-
-func TestCallback(t *testing.T) {
- t.Parallel()
-
- b := newBalanceTestSetup(nil, nil, nil)
- defer b.stop()
- node := b.newNode(1000)
- node.SetPriceFactors(PriceFactors{1, 0, 1}, PriceFactors{1, 0, 1})
-
- callCh := make(chan struct{}, 1)
- b.setBalance(node, uint64(time.Minute), 0)
- node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
-
- b.clock.Run(time.Minute)
- select {
- case <-callCh:
- case <-time.NewTimer(time.Second).C:
- t.Fatalf("Callback hasn't been called yet")
- }
-
- b.setBalance(node, uint64(time.Minute), 0)
- node.addCallback(balanceCallbackZero, 0, func() { callCh <- struct{}{} })
- node.removeCallback(balanceCallbackZero)
-
- b.clock.Run(time.Minute)
- select {
- case <-callCh:
- t.Fatalf("Callback shouldn't be called")
- case <-time.NewTimer(time.Millisecond * 100).C:
- }
-}
-
-func TestBalancePersistence(t *testing.T) {
- t.Parallel()
-
- posExp := &utils.Expirer{}
- negExp := &utils.Expirer{}
- posExp.SetRate(0, math.Log(2)/float64(time.Hour*2)) // halves every two hours
- negExp.SetRate(0, math.Log(2)/float64(time.Hour)) // halves every hour
- setup := newBalanceTestSetup(nil, posExp, negExp)
-
- exp := func(balance *nodeBalance, expPos, expNeg uint64) {
- pos, neg := balance.GetBalance()
- if pos != expPos {
- t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos)
- }
- if neg != expNeg {
- t.Fatalf("Positive balance incorrect, want %v, got %v", expPos, pos)
- }
- }
- expTotal := func(expTotal uint64) {
- total := setup.bt.TotalTokenAmount()
- if total != expTotal {
- t.Fatalf("Total token amount incorrect, want %v, got %v", expTotal, total)
- }
- }
-
- expTotal(0)
- balance := setup.newNode(0)
- expTotal(0)
- setup.setBalance(balance, 16000000000, 16000000000)
- exp(balance, 16000000000, 16000000000)
- expTotal(16000000000)
-
- setup.clock.Run(time.Hour * 2)
- exp(balance, 8000000000, 4000000000)
- expTotal(8000000000)
- setup.stop()
-
- // Test the functionalities after restart
- setup = newBalanceTestSetup(setup.db, posExp, negExp)
- expTotal(8000000000)
- balance = setup.newNode(0)
- exp(balance, 8000000000, 4000000000)
- expTotal(8000000000)
- setup.clock.Run(time.Hour * 2)
- exp(balance, 4000000000, 1000000000)
- expTotal(4000000000)
- setup.stop()
-}
diff --git a/les/vflux/server/balance_tracker.go b/les/vflux/server/balance_tracker.go
deleted file mode 100644
index 9695e796385b..000000000000
--- a/les/vflux/server/balance_tracker.go
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-const (
- posThreshold = 1000000 // minimum positive balance that is persisted in the database
- negThreshold = 1000000 // minimum negative balance that is persisted in the database
- persistExpirationRefresh = time.Minute * 5 // refresh period of the token expiration persistence
-)
-
-// balanceTracker tracks positive and negative balances for connected nodes.
-// After clientField is set externally, a nodeBalance is created and previous
-// balance values are loaded from the database. Both balances are exponentially expired
-// values. Costs are deducted from the positive balance if present, otherwise added to
-// the negative balance. If the capacity is non-zero then a time cost is applied
-// continuously while individual request costs are applied immediately.
-// The two balances are translated into a single priority value that also depends
-// on the actual capacity.
-type balanceTracker struct {
- setup *serverSetup
- clock mclock.Clock
- lock sync.Mutex
- ns *nodestate.NodeStateMachine
- ndb *nodeDB
- posExp, negExp utils.ValueExpirer
-
- posExpTC, negExpTC uint64
- defaultPosFactors, defaultNegFactors PriceFactors
-
- active, inactive utils.ExpiredValue
- balanceTimer *utils.UpdateTimer
- quit chan struct{}
-}
-
-// newBalanceTracker creates a new balanceTracker
-func newBalanceTracker(ns *nodestate.NodeStateMachine, setup *serverSetup, db ethdb.KeyValueStore, clock mclock.Clock, posExp, negExp utils.ValueExpirer) *balanceTracker {
- ndb := newNodeDB(db, clock)
- bt := &balanceTracker{
- ns: ns,
- setup: setup,
- ndb: ndb,
- clock: clock,
- posExp: posExp,
- negExp: negExp,
- balanceTimer: utils.NewUpdateTimer(clock, time.Second*10),
- quit: make(chan struct{}),
- }
- posOffset, negOffset := bt.ndb.getExpiration()
- posExp.SetLogOffset(clock.Now(), posOffset)
- negExp.SetLogOffset(clock.Now(), negOffset)
-
- // Load all persisted balance entries of priority nodes,
- // calculate the total number of issued service tokens.
- bt.ndb.forEachBalance(false, func(id enode.ID, balance utils.ExpiredValue) bool {
- bt.inactive.AddExp(balance)
- return true
- })
-
- ns.SubscribeField(bt.setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- n, _ := ns.GetField(node, bt.setup.balanceField).(*nodeBalance)
- if n == nil {
- return
- }
-
- ov, _ := oldValue.(uint64)
- nv, _ := newValue.(uint64)
- if ov == 0 && nv != 0 {
- n.activate()
- }
- if nv != 0 {
- n.setCapacity(nv)
- }
- if ov != 0 && nv == 0 {
- n.deactivate()
- }
- })
- ns.SubscribeField(bt.setup.clientField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- type peer interface {
- FreeClientId() string
- }
- if newValue != nil {
- n := bt.newNodeBalance(node, newValue.(peer).FreeClientId(), true)
- bt.lock.Lock()
- n.SetPriceFactors(bt.defaultPosFactors, bt.defaultNegFactors)
- bt.lock.Unlock()
- ns.SetFieldSub(node, bt.setup.balanceField, n)
- } else {
- ns.SetStateSub(node, nodestate.Flags{}, bt.setup.priorityFlag, 0)
- if b, _ := ns.GetField(node, bt.setup.balanceField).(*nodeBalance); b != nil {
- b.deactivate()
- }
- ns.SetFieldSub(node, bt.setup.balanceField, nil)
- }
- })
-
- // The positive and negative balances of clients are stored in database
- // and both of these decay exponentially over time. Delete them if the
- // value is small enough.
- bt.ndb.evictCallBack = bt.canDropBalance
-
- go func() {
- for {
- select {
- case <-clock.After(persistExpirationRefresh):
- now := clock.Now()
- bt.ndb.setExpiration(posExp.LogOffset(now), negExp.LogOffset(now))
- case <-bt.quit:
- return
- }
- }
- }()
- return bt
-}
-
-// Stop saves expiration offset and unsaved node balances and shuts balanceTracker down
-func (bt *balanceTracker) stop() {
- now := bt.clock.Now()
- bt.ndb.setExpiration(bt.posExp.LogOffset(now), bt.negExp.LogOffset(now))
- close(bt.quit)
- bt.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) {
- if n, ok := bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance); ok {
- n.lock.Lock()
- n.storeBalance(true, true)
- n.lock.Unlock()
- bt.ns.SetField(node, bt.setup.balanceField, nil)
- }
- })
- bt.ndb.close()
-}
-
-// TotalTokenAmount returns the current total amount of service tokens in existence
-func (bt *balanceTracker) TotalTokenAmount() uint64 {
- bt.lock.Lock()
- defer bt.lock.Unlock()
-
- bt.balanceTimer.Update(func(_ time.Duration) bool {
- bt.active = utils.ExpiredValue{}
- bt.ns.ForEach(nodestate.Flags{}, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) {
- if n, ok := bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance); ok && n.active {
- pos, _ := n.GetRawBalance()
- bt.active.AddExp(pos)
- }
- })
- return true
- })
- total := bt.active
- total.AddExp(bt.inactive)
- return total.Value(bt.posExp.LogOffset(bt.clock.Now()))
-}
-
-// GetPosBalanceIDs lists node IDs with an associated positive balance
-func (bt *balanceTracker) GetPosBalanceIDs(start, stop enode.ID, maxCount int) (result []enode.ID) {
- return bt.ndb.getPosBalanceIDs(start, stop, maxCount)
-}
-
-// SetDefaultFactors sets the default price factors applied to subsequently connected clients
-func (bt *balanceTracker) SetDefaultFactors(posFactors, negFactors PriceFactors) {
- bt.lock.Lock()
- bt.defaultPosFactors = posFactors
- bt.defaultNegFactors = negFactors
- bt.lock.Unlock()
-}
-
-// SetExpirationTCs sets positive and negative token expiration time constants.
-// Specified in seconds, 0 means infinite (no expiration).
-func (bt *balanceTracker) SetExpirationTCs(pos, neg uint64) {
- bt.lock.Lock()
- defer bt.lock.Unlock()
-
- bt.posExpTC, bt.negExpTC = pos, neg
- now := bt.clock.Now()
- if pos > 0 {
- bt.posExp.SetRate(now, 1/float64(pos*uint64(time.Second)))
- } else {
- bt.posExp.SetRate(now, 0)
- }
- if neg > 0 {
- bt.negExp.SetRate(now, 1/float64(neg*uint64(time.Second)))
- } else {
- bt.negExp.SetRate(now, 0)
- }
-}
-
-// GetExpirationTCs returns the current positive and negative token expiration
-// time constants
-func (bt *balanceTracker) GetExpirationTCs() (pos, neg uint64) {
- bt.lock.Lock()
- defer bt.lock.Unlock()
-
- return bt.posExpTC, bt.negExpTC
-}
-
-// BalanceOperation allows atomic operations on the balance of a node regardless of whether
-// it is currently connected or not
-func (bt *balanceTracker) BalanceOperation(id enode.ID, connAddress string, cb func(AtomicBalanceOperator)) {
- bt.ns.Operation(func() {
- var nb *nodeBalance
- if node := bt.ns.GetNode(id); node != nil {
- nb, _ = bt.ns.GetField(node, bt.setup.balanceField).(*nodeBalance)
- }
- if nb == nil {
- node := enode.SignNull(&enr.Record{}, id)
- nb = bt.newNodeBalance(node, connAddress, false)
- }
- cb(nb)
- })
-}
-
-// newNodeBalance loads balances from the database and creates a nodeBalance instance
-// for the given node. It also sets the priorityFlag and adds balanceCallbackZero if
-// the node has a positive balance.
-// Note: this function should run inside a NodeStateMachine operation
-func (bt *balanceTracker) newNodeBalance(node *enode.Node, connAddress string, setFlags bool) *nodeBalance {
- pb := bt.ndb.getOrNewBalance(node.ID().Bytes(), false)
- nb := bt.ndb.getOrNewBalance([]byte(connAddress), true)
- n := &nodeBalance{
- bt: bt,
- node: node,
- setFlags: setFlags,
- connAddress: connAddress,
- balance: balance{pos: pb, neg: nb, posExp: bt.posExp, negExp: bt.negExp},
- initTime: bt.clock.Now(),
- lastUpdate: bt.clock.Now(),
- }
- for i := range n.callbackIndex {
- n.callbackIndex[i] = -1
- }
- if setFlags && n.checkPriorityStatus() {
- n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0)
- }
- return n
-}
-
-// storeBalance stores either a positive or a negative balance in the database
-func (bt *balanceTracker) storeBalance(id []byte, neg bool, value utils.ExpiredValue) {
- if bt.canDropBalance(bt.clock.Now(), neg, value) {
- bt.ndb.delBalance(id, neg) // balance is small enough, drop it directly.
- } else {
- bt.ndb.setBalance(id, neg, value)
- }
-}
-
-// canDropBalance tells whether a positive or negative balance is below the threshold
-// and therefore can be dropped from the database
-func (bt *balanceTracker) canDropBalance(now mclock.AbsTime, neg bool, b utils.ExpiredValue) bool {
- if neg {
- return b.Value(bt.negExp.LogOffset(now)) <= negThreshold
- }
- return b.Value(bt.posExp.LogOffset(now)) <= posThreshold
-}
-
-// updateTotalBalance adjusts the total balance after executing given callback.
-func (bt *balanceTracker) updateTotalBalance(n *nodeBalance, callback func() bool) {
- bt.lock.Lock()
- defer bt.lock.Unlock()
-
- n.lock.Lock()
- defer n.lock.Unlock()
-
- original, active := n.balance.pos, n.active
- if !callback() {
- return
- }
- if active {
- bt.active.SubExp(original)
- } else {
- bt.inactive.SubExp(original)
- }
- if n.active {
- bt.active.AddExp(n.balance.pos)
- } else {
- bt.inactive.AddExp(n.balance.pos)
- }
-}
diff --git a/les/vflux/server/clientdb.go b/les/vflux/server/clientdb.go
deleted file mode 100644
index a39cbec36a92..000000000000
--- a/les/vflux/server/clientdb.go
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "bytes"
- "encoding/binary"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/lru"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-const (
- balanceCacheLimit = 8192 // the maximum number of cached items in service token balance queue
-
- // nodeDBVersion is the version identifier of the node data in db
- //
- // Changelog:
- // Version 0 => 1
- // * Replace `lastTotal` with `meta` in positive balance: version 0=>1
- //
- // Version 1 => 2
- // * Positive Balance and negative balance is changed:
- // * Cumulative time is replaced with expiration
- nodeDBVersion = 2
-
- // dbCleanupCycle is the cycle of db for useless data cleanup
- dbCleanupCycle = time.Hour
-)
-
-var (
- positiveBalancePrefix = []byte("pb:") // dbVersion(uint16 big endian) + positiveBalancePrefix + id -> balance
- negativeBalancePrefix = []byte("nb:") // dbVersion(uint16 big endian) + negativeBalancePrefix + ip -> balance
- expirationKey = []byte("expiration:") // dbVersion(uint16 big endian) + expirationKey -> posExp, negExp
-)
-
-type nodeDB struct {
- db ethdb.KeyValueStore
- cache *lru.Cache[string, utils.ExpiredValue]
- auxbuf []byte // 37-byte auxiliary buffer for key encoding
- verbuf [2]byte // 2-byte auxiliary buffer for db version
- evictCallBack func(mclock.AbsTime, bool, utils.ExpiredValue) bool // Callback to determine whether the balance can be evicted.
- clock mclock.Clock
- closeCh chan struct{}
- cleanupHook func() // Test hook used for testing
-}
-
-func newNodeDB(db ethdb.KeyValueStore, clock mclock.Clock) *nodeDB {
- ndb := &nodeDB{
- db: db,
- cache: lru.NewCache[string, utils.ExpiredValue](balanceCacheLimit),
- auxbuf: make([]byte, 37),
- clock: clock,
- closeCh: make(chan struct{}),
- }
- binary.BigEndian.PutUint16(ndb.verbuf[:], uint16(nodeDBVersion))
- go ndb.expirer()
- return ndb
-}
-
-func (db *nodeDB) close() {
- close(db.closeCh)
-}
-
-func (db *nodeDB) getPrefix(neg bool) []byte {
- prefix := positiveBalancePrefix
- if neg {
- prefix = negativeBalancePrefix
- }
- return append(db.verbuf[:], prefix...)
-}
-
-func (db *nodeDB) key(id []byte, neg bool) []byte {
- prefix := positiveBalancePrefix
- if neg {
- prefix = negativeBalancePrefix
- }
- if len(prefix)+len(db.verbuf)+len(id) > len(db.auxbuf) {
- db.auxbuf = append(db.auxbuf, make([]byte, len(prefix)+len(db.verbuf)+len(id)-len(db.auxbuf))...)
- }
- copy(db.auxbuf[:len(db.verbuf)], db.verbuf[:])
- copy(db.auxbuf[len(db.verbuf):len(db.verbuf)+len(prefix)], prefix)
- copy(db.auxbuf[len(prefix)+len(db.verbuf):len(prefix)+len(db.verbuf)+len(id)], id)
- return db.auxbuf[:len(prefix)+len(db.verbuf)+len(id)]
-}
-
-func (db *nodeDB) getExpiration() (utils.Fixed64, utils.Fixed64) {
- blob, err := db.db.Get(append(db.verbuf[:], expirationKey...))
- if err != nil || len(blob) != 16 {
- return 0, 0
- }
- return utils.Fixed64(binary.BigEndian.Uint64(blob[:8])), utils.Fixed64(binary.BigEndian.Uint64(blob[8:16]))
-}
-
-func (db *nodeDB) setExpiration(pos, neg utils.Fixed64) {
- var buff [16]byte
- binary.BigEndian.PutUint64(buff[:8], uint64(pos))
- binary.BigEndian.PutUint64(buff[8:16], uint64(neg))
- db.db.Put(append(db.verbuf[:], expirationKey...), buff[:16])
-}
-
-func (db *nodeDB) getOrNewBalance(id []byte, neg bool) utils.ExpiredValue {
- key := db.key(id, neg)
- item, exist := db.cache.Get(string(key))
- if exist {
- return item
- }
-
- var b utils.ExpiredValue
- enc, err := db.db.Get(key)
- if err != nil || len(enc) == 0 {
- return b
- }
- if err := rlp.DecodeBytes(enc, &b); err != nil {
- log.Crit("Failed to decode positive balance", "err", err)
- }
- db.cache.Add(string(key), b)
- return b
-}
-
-func (db *nodeDB) setBalance(id []byte, neg bool, b utils.ExpiredValue) {
- key := db.key(id, neg)
- enc, err := rlp.EncodeToBytes(&(b))
- if err != nil {
- log.Crit("Failed to encode positive balance", "err", err)
- }
- db.db.Put(key, enc)
- db.cache.Add(string(key), b)
-}
-
-func (db *nodeDB) delBalance(id []byte, neg bool) {
- key := db.key(id, neg)
- db.db.Delete(key)
- db.cache.Remove(string(key))
-}
-
-// getPosBalanceIDs returns a lexicographically ordered list of IDs of accounts
-// with a positive balance
-func (db *nodeDB) getPosBalanceIDs(start, stop enode.ID, maxCount int) (result []enode.ID) {
- if maxCount <= 0 {
- return
- }
- prefix := db.getPrefix(false)
- keylen := len(prefix) + len(enode.ID{})
-
- it := db.db.NewIterator(prefix, start.Bytes())
- defer it.Release()
-
- for it.Next() {
- var id enode.ID
- if len(it.Key()) != keylen {
- return
- }
- copy(id[:], it.Key()[keylen-len(id):])
- if bytes.Compare(id.Bytes(), stop.Bytes()) >= 0 {
- return
- }
- result = append(result, id)
- if len(result) == maxCount {
- return
- }
- }
- return
-}
-
-// forEachBalance iterates all balances and passes values to callback.
-func (db *nodeDB) forEachBalance(neg bool, callback func(id enode.ID, balance utils.ExpiredValue) bool) {
- prefix := db.getPrefix(neg)
- keylen := len(prefix) + len(enode.ID{})
-
- it := db.db.NewIterator(prefix, nil)
- defer it.Release()
-
- for it.Next() {
- var id enode.ID
- if len(it.Key()) != keylen {
- return
- }
- copy(id[:], it.Key()[keylen-len(id):])
-
- var b utils.ExpiredValue
- if err := rlp.DecodeBytes(it.Value(), &b); err != nil {
- continue
- }
- if !callback(id, b) {
- return
- }
- }
-}
-
-func (db *nodeDB) expirer() {
- for {
- select {
- case <-db.clock.After(dbCleanupCycle):
- db.expireNodes()
- case <-db.closeCh:
- return
- }
- }
-}
-
-// expireNodes iterates the whole node db and checks whether the
-// token balances can be deleted.
-func (db *nodeDB) expireNodes() {
- var (
- visited int
- deleted int
- start = time.Now()
- )
- for _, neg := range []bool{false, true} {
- iter := db.db.NewIterator(db.getPrefix(neg), nil)
- for iter.Next() {
- visited++
- var balance utils.ExpiredValue
- if err := rlp.DecodeBytes(iter.Value(), &balance); err != nil {
- log.Crit("Failed to decode negative balance", "err", err)
- }
- if db.evictCallBack != nil && db.evictCallBack(db.clock.Now(), neg, balance) {
- deleted++
- db.db.Delete(iter.Key())
- }
- }
- }
- // Invoke testing hook if it's not nil.
- if db.cleanupHook != nil {
- db.cleanupHook()
- }
- log.Debug("Expire nodes", "visited", visited, "deleted", deleted, "elapsed", common.PrettyDuration(time.Since(start)))
-}
diff --git a/les/vflux/server/clientdb_test.go b/les/vflux/server/clientdb_test.go
deleted file mode 100644
index caa4384e19e2..000000000000
--- a/les/vflux/server/clientdb_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "reflect"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/p2p/enode"
-)
-
-func expval(v uint64) utils.ExpiredValue {
- return utils.ExpiredValue{Base: v}
-}
-
-func TestNodeDB(t *testing.T) {
- t.Parallel()
-
- ndb := newNodeDB(rawdb.NewMemoryDatabase(), mclock.System{})
- defer ndb.close()
-
- var cases = []struct {
- id enode.ID
- ip string
- balance utils.ExpiredValue
- positive bool
- }{
- {enode.ID{0x00, 0x01, 0x02}, "", expval(100), true},
- {enode.ID{0x00, 0x01, 0x02}, "", expval(200), true},
- {enode.ID{}, "127.0.0.1", expval(100), false},
- {enode.ID{}, "127.0.0.1", expval(200), false},
- }
- for _, c := range cases {
- if c.positive {
- ndb.setBalance(c.id.Bytes(), false, c.balance)
- if pb := ndb.getOrNewBalance(c.id.Bytes(), false); !reflect.DeepEqual(pb, c.balance) {
- t.Fatalf("Positive balance mismatch, want %v, got %v", c.balance, pb)
- }
- } else {
- ndb.setBalance([]byte(c.ip), true, c.balance)
- if nb := ndb.getOrNewBalance([]byte(c.ip), true); !reflect.DeepEqual(nb, c.balance) {
- t.Fatalf("Negative balance mismatch, want %v, got %v", c.balance, nb)
- }
- }
- }
- for _, c := range cases {
- if c.positive {
- ndb.delBalance(c.id.Bytes(), false)
- if pb := ndb.getOrNewBalance(c.id.Bytes(), false); !reflect.DeepEqual(pb, utils.ExpiredValue{}) {
- t.Fatalf("Positive balance mismatch, want %v, got %v", utils.ExpiredValue{}, pb)
- }
- } else {
- ndb.delBalance([]byte(c.ip), true)
- if nb := ndb.getOrNewBalance([]byte(c.ip), true); !reflect.DeepEqual(nb, utils.ExpiredValue{}) {
- t.Fatalf("Negative balance mismatch, want %v, got %v", utils.ExpiredValue{}, nb)
- }
- }
- }
- posExp, negExp := utils.Fixed64(1000), utils.Fixed64(2000)
- ndb.setExpiration(posExp, negExp)
- if pos, neg := ndb.getExpiration(); pos != posExp || neg != negExp {
- t.Fatalf("Expiration mismatch, want %v / %v, got %v / %v", posExp, negExp, pos, neg)
- }
- /* curBalance := currencyBalance{typ: "ETH", amount: 10000}
- ndb.setCurrencyBalance(enode.ID{0x01, 0x02}, curBalance)
- if got := ndb.getCurrencyBalance(enode.ID{0x01, 0x02}); !reflect.DeepEqual(got, curBalance) {
- t.Fatalf("Currency balance mismatch, want %v, got %v", curBalance, got)
- }*/
-}
-
-func TestNodeDBExpiration(t *testing.T) {
- t.Parallel()
-
- var (
- iterated int
- done = make(chan struct{}, 1)
- )
- callback := func(now mclock.AbsTime, neg bool, b utils.ExpiredValue) bool {
- iterated += 1
- return true
- }
- clock := &mclock.Simulated{}
- ndb := newNodeDB(rawdb.NewMemoryDatabase(), clock)
- defer ndb.close()
- ndb.evictCallBack = callback
- ndb.cleanupHook = func() { done <- struct{}{} }
-
- var cases = []struct {
- id []byte
- neg bool
- balance utils.ExpiredValue
- }{
- {[]byte{0x01, 0x02}, false, expval(1)},
- {[]byte{0x03, 0x04}, false, expval(1)},
- {[]byte{0x05, 0x06}, false, expval(1)},
- {[]byte{0x07, 0x08}, false, expval(1)},
-
- {[]byte("127.0.0.1"), true, expval(1)},
- {[]byte("127.0.0.2"), true, expval(1)},
- {[]byte("127.0.0.3"), true, expval(1)},
- {[]byte("127.0.0.4"), true, expval(1)},
- }
- for _, c := range cases {
- ndb.setBalance(c.id, c.neg, c.balance)
- }
- clock.WaitForTimers(1)
- clock.Run(time.Hour + time.Minute)
- select {
- case <-done:
- case <-time.NewTimer(time.Second).C:
- t.Fatalf("timeout")
- }
- if iterated != 8 {
- t.Fatalf("Failed to evict useless balances, want %v, got %d", 8, iterated)
- }
-
- for _, c := range cases {
- ndb.setBalance(c.id, c.neg, c.balance)
- }
- clock.WaitForTimers(1)
- clock.Run(time.Hour + time.Minute)
- select {
- case <-done:
- case <-time.NewTimer(time.Second).C:
- t.Fatalf("timeout")
- }
- if iterated != 16 {
- t.Fatalf("Failed to evict useless balances, want %v, got %d", 16, iterated)
- }
-}
diff --git a/les/vflux/server/clientpool.go b/les/vflux/server/clientpool.go
deleted file mode 100644
index a525f86368d2..000000000000
--- a/les/vflux/server/clientpool.go
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright 2021 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 .
-
-package server
-
-import (
- "errors"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/les/vflux"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var (
- ErrNotConnected = errors.New("client not connected")
- ErrNoPriority = errors.New("priority too low to raise capacity")
- ErrCantFindMaximum = errors.New("unable to find maximum allowed capacity")
-)
-
-// ClientPool implements a client database that assigns a priority to each client
-// based on a positive and negative balance. Positive balance is externally assigned
-// to prioritized clients and is decreased with connection time and processed
-// requests (unless the price factors are zero). If the positive balance is zero
-// then negative balance is accumulated.
-//
-// Balance tracking and priority calculation for connected clients is done by
-// balanceTracker. PriorityQueue ensures that clients with the lowest positive or
-// highest negative balance get evicted when the total capacity allowance is full
-// and new clients with a better balance want to connect.
-//
-// Already connected nodes receive a small bias in their favor in order to avoid
-// accepting and instantly kicking out clients. In theory, we try to ensure that
-// each client can have several minutes of connection time.
-//
-// Balances of disconnected clients are stored in nodeDB including positive balance
-// and negative balance. Both positive balance and negative balance will decrease
-// exponentially. If the balance is low enough, then the record will be dropped.
-type ClientPool struct {
- *priorityPool
- *balanceTracker
-
- setup *serverSetup
- clock mclock.Clock
- ns *nodestate.NodeStateMachine
- synced func() bool
-
- lock sync.RWMutex
- connectedBias time.Duration
-
- minCap uint64 // the minimal capacity value allowed for any client
- capReqNode *enode.Node // node that is requesting capacity change; only used inside NSM operation
-}
-
-// clientPeer represents a peer in the client pool. None of the callbacks should block.
-type clientPeer interface {
- Node() *enode.Node
- FreeClientId() string // unique id for non-priority clients (typically a prefix of the network address)
- InactiveAllowance() time.Duration // disconnection timeout for inactive non-priority peers
- UpdateCapacity(newCap uint64, requested bool) // signals a capacity update (requested is true if it is a result of a SetCapacity call on the given peer
- Disconnect() // initiates disconnection (Unregister should always be called)
-}
-
-// NewClientPool creates a new client pool
-func NewClientPool(balanceDb ethdb.KeyValueStore, minCap uint64, connectedBias time.Duration, clock mclock.Clock, synced func() bool) *ClientPool {
- setup := newServerSetup()
- ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup)
- cp := &ClientPool{
- priorityPool: newPriorityPool(ns, setup, clock, minCap, connectedBias, 4, 100),
- balanceTracker: newBalanceTracker(ns, setup, balanceDb, clock, &utils.Expirer{}, &utils.Expirer{}),
- setup: setup,
- ns: ns,
- clock: clock,
- minCap: minCap,
- connectedBias: connectedBias,
- synced: synced,
- }
-
- ns.SubscribeState(nodestate.MergeFlags(setup.activeFlag, setup.inactiveFlag, setup.priorityFlag), func(node *enode.Node, oldState, newState nodestate.Flags) {
- if newState.Equals(setup.inactiveFlag) {
- // set timeout for non-priority inactive client
- var timeout time.Duration
- if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok {
- timeout = c.InactiveAllowance()
- }
- ns.AddTimeout(node, setup.inactiveFlag, timeout)
- }
- if oldState.Equals(setup.inactiveFlag) && newState.Equals(setup.inactiveFlag.Or(setup.priorityFlag)) {
- ns.SetStateSub(node, setup.inactiveFlag, nodestate.Flags{}, 0) // priority gained; remove timeout
- }
- if newState.Equals(setup.activeFlag) {
- // active with no priority; limit capacity to minCap
- cap, _ := ns.GetField(node, setup.capacityField).(uint64)
- if cap > minCap {
- cp.requestCapacity(node, minCap, minCap, 0)
- }
- }
- if newState.Equals(nodestate.Flags{}) {
- if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok {
- c.Disconnect()
- }
- }
- })
-
- ns.SubscribeField(setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- if c, ok := ns.GetField(node, setup.clientField).(clientPeer); ok {
- newCap, _ := newValue.(uint64)
- c.UpdateCapacity(newCap, node == cp.capReqNode)
- }
- })
-
- // add metrics
- cp.ns.SubscribeState(nodestate.MergeFlags(cp.setup.activeFlag, cp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) {
- if oldState.IsEmpty() && !newState.IsEmpty() {
- clientConnectedMeter.Mark(1)
- }
- if !oldState.IsEmpty() && newState.IsEmpty() {
- clientDisconnectedMeter.Mark(1)
- }
- if oldState.HasNone(cp.setup.activeFlag) && oldState.HasAll(cp.setup.activeFlag) {
- clientActivatedMeter.Mark(1)
- }
- if oldState.HasAll(cp.setup.activeFlag) && oldState.HasNone(cp.setup.activeFlag) {
- clientDeactivatedMeter.Mark(1)
- }
- activeCount, activeCap := cp.Active()
- totalActiveCountGauge.Update(int64(activeCount))
- totalActiveCapacityGauge.Update(int64(activeCap))
- totalInactiveCountGauge.Update(int64(cp.Inactive()))
- })
- return cp
-}
-
-// Start starts the client pool. Should be called before Register/Unregister.
-func (cp *ClientPool) Start() {
- cp.ns.Start()
-}
-
-// Stop shuts the client pool down. The clientPeer interface callbacks will not be called
-// after Stop. Register calls will return nil.
-func (cp *ClientPool) Stop() {
- cp.balanceTracker.stop()
- cp.ns.Stop()
-}
-
-// Register registers the peer into the client pool. If the peer has insufficient
-// priority and remains inactive for longer than the allowed timeout then it will be
-// disconnected by calling the Disconnect function of the clientPeer interface.
-func (cp *ClientPool) Register(peer clientPeer) ConnectedBalance {
- cp.ns.SetField(peer.Node(), cp.setup.clientField, peerWrapper{peer})
- balance, _ := cp.ns.GetField(peer.Node(), cp.setup.balanceField).(*nodeBalance)
- return balance
-}
-
-// Unregister removes the peer from the client pool
-func (cp *ClientPool) Unregister(peer clientPeer) {
- cp.ns.SetField(peer.Node(), cp.setup.clientField, nil)
-}
-
-// SetConnectedBias sets the connection bias, which is applied to already connected clients
-// So that already connected client won't be kicked out very soon and we can ensure all
-// connected clients can have enough time to request or sync some data.
-func (cp *ClientPool) SetConnectedBias(bias time.Duration) {
- cp.lock.Lock()
- cp.connectedBias = bias
- cp.setActiveBias(bias)
- cp.lock.Unlock()
-}
-
-// SetCapacity sets the assigned capacity of a connected client
-func (cp *ClientPool) SetCapacity(node *enode.Node, reqCap uint64, bias time.Duration, requested bool) (capacity uint64, err error) {
- cp.lock.RLock()
- if cp.connectedBias > bias {
- bias = cp.connectedBias
- }
- cp.lock.RUnlock()
-
- cp.ns.Operation(func() {
- balance, _ := cp.ns.GetField(node, cp.setup.balanceField).(*nodeBalance)
- if balance == nil {
- err = ErrNotConnected
- return
- }
- capacity, _ = cp.ns.GetField(node, cp.setup.capacityField).(uint64)
- if capacity == 0 {
- // if the client is inactive then it has insufficient priority for the minimal capacity
- // (will be activated automatically with minCap when possible)
- return
- }
- if reqCap < cp.minCap {
- // can't request less than minCap; switching between 0 (inactive state) and minCap is
- // performed by the server automatically as soon as necessary/possible
- reqCap = cp.minCap
- }
- if reqCap > cp.minCap && cp.ns.GetState(node).HasNone(cp.setup.priorityFlag) {
- err = ErrNoPriority
- return
- }
- if reqCap == capacity {
- return
- }
- if requested {
- // mark the requested node so that the UpdateCapacity callback can signal
- // whether the update is the direct result of a SetCapacity call on the given node
- cp.capReqNode = node
- defer func() {
- cp.capReqNode = nil
- }()
- }
-
- var minTarget, maxTarget uint64
- if reqCap > capacity {
- // Estimate maximum available capacity at the current priority level and request
- // the estimated amount.
- // Note: requestCapacity could find the highest available capacity between the
- // current and the requested capacity but it could cost a lot of iterations with
- // fine step adjustment if the requested capacity is very high. By doing a quick
- // estimation of the maximum available capacity based on the capacity curve we
- // can limit the number of required iterations.
- curve := cp.getCapacityCurve().exclude(node.ID())
- maxTarget = curve.maxCapacity(func(capacity uint64) int64 {
- return balance.estimatePriority(capacity, 0, 0, bias, false)
- })
- if maxTarget < reqCap {
- return
- }
- maxTarget = reqCap
-
- // Specify a narrow target range that allows a limited number of fine step
- // iterations
- minTarget = maxTarget - maxTarget/20
- if minTarget < capacity {
- minTarget = capacity
- }
- } else {
- minTarget, maxTarget = reqCap, reqCap
- }
- if newCap := cp.requestCapacity(node, minTarget, maxTarget, bias); newCap >= minTarget && newCap <= maxTarget {
- capacity = newCap
- return
- }
- // we should be able to find the maximum allowed capacity in a few iterations
- log.Error("Unable to find maximum allowed capacity")
- err = ErrCantFindMaximum
- })
- return
-}
-
-// serveCapQuery serves a vflux capacity query. It receives multiple token amount values
-// and a bias time value. For each given token amount it calculates the maximum achievable
-// capacity in case the amount is added to the balance.
-func (cp *ClientPool) serveCapQuery(id enode.ID, freeID string, data []byte) []byte {
- var req vflux.CapacityQueryReq
- if rlp.DecodeBytes(data, &req) != nil {
- return nil
- }
- if l := len(req.AddTokens); l == 0 || l > vflux.CapacityQueryMaxLen {
- return nil
- }
- result := make(vflux.CapacityQueryReply, len(req.AddTokens))
- if !cp.synced() {
- capacityQueryZeroMeter.Mark(1)
- reply, _ := rlp.EncodeToBytes(&result)
- return reply
- }
-
- bias := time.Second * time.Duration(req.Bias)
- cp.lock.RLock()
- if cp.connectedBias > bias {
- bias = cp.connectedBias
- }
- cp.lock.RUnlock()
-
- // use capacityCurve to answer request for multiple newly bought token amounts
- curve := cp.getCapacityCurve().exclude(id)
- cp.BalanceOperation(id, freeID, func(balance AtomicBalanceOperator) {
- pb, _ := balance.GetBalance()
- for i, addTokens := range req.AddTokens {
- add := addTokens.Int64()
- result[i] = curve.maxCapacity(func(capacity uint64) int64 {
- return balance.estimatePriority(capacity, add, 0, bias, false) / int64(capacity)
- })
- if add <= 0 && uint64(-add) >= pb && result[i] > cp.minCap {
- result[i] = cp.minCap
- }
- if result[i] < cp.minCap {
- result[i] = 0
- }
- }
- })
- // add first result to metrics (don't care about priority client multi-queries yet)
- if result[0] == 0 {
- capacityQueryZeroMeter.Mark(1)
- } else {
- capacityQueryNonZeroMeter.Mark(1)
- }
- reply, _ := rlp.EncodeToBytes(&result)
- return reply
-}
-
-// Handle implements Service
-func (cp *ClientPool) Handle(id enode.ID, address string, name string, data []byte) []byte {
- switch name {
- case vflux.CapacityQueryName:
- return cp.serveCapQuery(id, address, data)
- default:
- return nil
- }
-}
diff --git a/les/vflux/server/clientpool_test.go b/les/vflux/server/clientpool_test.go
deleted file mode 100644
index 7319be08242f..000000000000
--- a/les/vflux/server/clientpool_test.go
+++ /dev/null
@@ -1,640 +0,0 @@
-// Copyright 2021 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 .
-
-package server
-
-import (
- "fmt"
- "math/rand"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-const defaultConnectedBias = time.Minute * 3
-
-func TestClientPoolL10C100Free(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 10, 100, 0, true)
-}
-
-func TestClientPoolL40C200Free(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 40, 200, 0, true)
-}
-
-func TestClientPoolL100C300Free(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 100, 300, 0, true)
-}
-
-func TestClientPoolL10C100P4(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 10, 100, 4, false)
-}
-
-func TestClientPoolL40C200P30(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 40, 200, 30, false)
-}
-
-func TestClientPoolL100C300P20(t *testing.T) {
- t.Parallel()
-
- testClientPool(t, 100, 300, 20, false)
-}
-
-const testClientPoolTicks = 100000
-
-type poolTestPeer struct {
- node *enode.Node
- index int
- disconnCh chan int
- cap uint64
- inactiveAllowed bool
-}
-
-func newPoolTestPeer(i int, disconnCh chan int) *poolTestPeer {
- return &poolTestPeer{
- index: i,
- disconnCh: disconnCh,
- node: enode.SignNull(&enr.Record{}, enode.ID{byte(i % 256), byte(i >> 8)}),
- }
-}
-
-func (i *poolTestPeer) Node() *enode.Node {
- return i.node
-}
-
-func (i *poolTestPeer) FreeClientId() string {
- return fmt.Sprintf("addr #%d", i.index)
-}
-
-func (i *poolTestPeer) InactiveAllowance() time.Duration {
- if i.inactiveAllowed {
- return time.Second * 10
- }
- return 0
-}
-
-func (i *poolTestPeer) UpdateCapacity(capacity uint64, requested bool) {
- i.cap = capacity
-}
-
-func (i *poolTestPeer) Disconnect() {
- if i.disconnCh == nil {
- return
- }
- id := i.node.ID()
- i.disconnCh <- int(id[0]) + int(id[1])<<8
-}
-
-func getBalance(pool *ClientPool, p *poolTestPeer) (pos, neg uint64) {
- pool.BalanceOperation(p.node.ID(), p.FreeClientId(), func(nb AtomicBalanceOperator) {
- pos, neg = nb.GetBalance()
- })
- return
-}
-
-func addBalance(pool *ClientPool, id enode.ID, amount int64) {
- pool.BalanceOperation(id, "", func(nb AtomicBalanceOperator) {
- nb.AddBalance(amount)
- })
-}
-
-func checkDiff(a, b uint64) bool {
- maxDiff := (a + b) / 2000
- if maxDiff < 1 {
- maxDiff = 1
- }
- return a > b+maxDiff || b > a+maxDiff
-}
-
-func connect(pool *ClientPool, peer *poolTestPeer) uint64 {
- pool.Register(peer)
- return peer.cap
-}
-
-func disconnect(pool *ClientPool, peer *poolTestPeer) {
- pool.Unregister(peer)
-}
-
-func alwaysTrueFn() bool {
- return true
-}
-
-func testClientPool(t *testing.T, activeLimit, clientCount, paidCount int, randomDisconnect bool) {
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- connected = make([]bool, clientCount)
- connTicks = make([]int, clientCount)
- disconnCh = make(chan int, clientCount)
- pool = NewClientPool(db, 1, 0, &clock, alwaysTrueFn)
- )
- pool.Start()
- pool.SetExpirationTCs(0, 1000)
-
- pool.SetLimits(uint64(activeLimit), uint64(activeLimit))
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- // pool should accept new peers up to its connected limit
- for i := 0; i < activeLimit; i++ {
- if cap := connect(pool, newPoolTestPeer(i, disconnCh)); cap != 0 {
- connected[i] = true
- } else {
- t.Fatalf("Test peer #%d rejected", i)
- }
- }
- // randomly connect and disconnect peers, expect to have a similar total connection time at the end
- for tickCounter := 0; tickCounter < testClientPoolTicks; tickCounter++ {
- clock.Run(1 * time.Second)
-
- if tickCounter == testClientPoolTicks/4 {
- // give a positive balance to some of the peers
- amount := testClientPoolTicks / 2 * int64(time.Second) // enough for half of the simulation period
- for i := 0; i < paidCount; i++ {
- addBalance(pool, newPoolTestPeer(i, disconnCh).node.ID(), amount)
- }
- }
-
- i := rand.Intn(clientCount)
- if connected[i] {
- if randomDisconnect {
- disconnect(pool, newPoolTestPeer(i, disconnCh))
- connected[i] = false
- connTicks[i] += tickCounter
- }
- } else {
- if cap := connect(pool, newPoolTestPeer(i, disconnCh)); cap != 0 {
- connected[i] = true
- connTicks[i] -= tickCounter
- } else {
- disconnect(pool, newPoolTestPeer(i, disconnCh))
- }
- }
- pollDisconnects:
- for {
- select {
- case i := <-disconnCh:
- disconnect(pool, newPoolTestPeer(i, disconnCh))
- if connected[i] {
- connTicks[i] += tickCounter
- connected[i] = false
- }
- default:
- break pollDisconnects
- }
- }
- }
-
- expTicks := testClientPoolTicks/2*activeLimit/clientCount + testClientPoolTicks/2*(activeLimit-paidCount)/(clientCount-paidCount)
- expMin := expTicks - expTicks/5
- expMax := expTicks + expTicks/5
- paidTicks := testClientPoolTicks/2*activeLimit/clientCount + testClientPoolTicks/2
- paidMin := paidTicks - paidTicks/5
- paidMax := paidTicks + paidTicks/5
-
- // check if the total connected time of peers are all in the expected range
- for i, c := range connected {
- if c {
- connTicks[i] += testClientPoolTicks
- }
- min, max := expMin, expMax
- if i < paidCount {
- // expect a higher amount for clients with a positive balance
- min, max = paidMin, paidMax
- }
- if connTicks[i] < min || connTicks[i] > max {
- t.Errorf("Total connected time of test node #%d (%d) outside expected range (%d to %d)", i, connTicks[i], min, max)
- }
- }
- pool.Stop()
-}
-
-func testPriorityConnect(t *testing.T, pool *ClientPool, p *poolTestPeer, cap uint64, expSuccess bool) {
- if cap := connect(pool, p); cap == 0 {
- if expSuccess {
- t.Fatalf("Failed to connect paid client")
- } else {
- return
- }
- }
- if newCap, _ := pool.SetCapacity(p.node, cap, defaultConnectedBias, true); newCap != cap {
- if expSuccess {
- t.Fatalf("Failed to raise capacity of paid client")
- } else {
- return
- }
- }
- if !expSuccess {
- t.Fatalf("Should reject high capacity paid client")
- }
-}
-
-func TestConnectPaidClient(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10))
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- // Add balance for an external client and mark it as paid client
- addBalance(pool, newPoolTestPeer(0, nil).node.ID(), int64(time.Minute))
- testPriorityConnect(t, pool, newPoolTestPeer(0, nil), 10, true)
-}
-
-func TestConnectPaidClientToSmallPool(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- // Add balance for an external client and mark it as paid client
- addBalance(pool, newPoolTestPeer(0, nil).node.ID(), int64(time.Minute))
-
- // connect a fat paid client to pool, should reject it.
- testPriorityConnect(t, pool, newPoolTestPeer(0, nil), 100, false)
-}
-
-func TestConnectPaidClientToFullPool(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- for i := 0; i < 10; i++ {
- addBalance(pool, newPoolTestPeer(i, nil).node.ID(), int64(time.Second*20))
- connect(pool, newPoolTestPeer(i, nil))
- }
- addBalance(pool, newPoolTestPeer(11, nil).node.ID(), int64(time.Second*2)) // Add low balance to new paid client
- if cap := connect(pool, newPoolTestPeer(11, nil)); cap != 0 {
- t.Fatalf("Low balance paid client should be rejected")
- }
- clock.Run(time.Second)
- addBalance(pool, newPoolTestPeer(12, nil).node.ID(), int64(time.Minute*5)) // Add high balance to new paid client
- if cap := connect(pool, newPoolTestPeer(12, nil)); cap == 0 {
- t.Fatalf("High balance paid client should be accepted")
- }
-}
-
-func TestPaidClientKickedOut(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- kickedCh = make(chan int, 100)
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- pool.SetExpirationTCs(0, 0)
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- for i := 0; i < 10; i++ {
- addBalance(pool, newPoolTestPeer(i, kickedCh).node.ID(), 10000000000) // 10 second allowance
- connect(pool, newPoolTestPeer(i, kickedCh))
- clock.Run(time.Millisecond)
- }
- clock.Run(defaultConnectedBias + time.Second*11)
- if cap := connect(pool, newPoolTestPeer(11, kickedCh)); cap == 0 {
- t.Fatalf("Free client should be accepted")
- }
- clock.Run(0)
- select {
- case id := <-kickedCh:
- if id != 0 {
- t.Fatalf("Kicked client mismatch, want %v, got %v", 0, id)
- }
- default:
- t.Fatalf("timeout")
- }
-}
-
-func TestConnectFreeClient(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10))
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
- if cap := connect(pool, newPoolTestPeer(0, nil)); cap == 0 {
- t.Fatalf("Failed to connect free client")
- }
- testPriorityConnect(t, pool, newPoolTestPeer(0, nil), 2, false)
-}
-
-func TestConnectFreeClientToFullPool(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- for i := 0; i < 10; i++ {
- connect(pool, newPoolTestPeer(i, nil))
- }
- if cap := connect(pool, newPoolTestPeer(11, nil)); cap != 0 {
- t.Fatalf("New free client should be rejected")
- }
- clock.Run(time.Minute)
- if cap := connect(pool, newPoolTestPeer(12, nil)); cap != 0 {
- t.Fatalf("New free client should be rejected")
- }
- clock.Run(time.Millisecond)
- clock.Run(4 * time.Minute)
- if cap := connect(pool, newPoolTestPeer(13, nil)); cap == 0 {
- t.Fatalf("Old client connects more than 5min should be kicked")
- }
-}
-
-func TestFreeClientKickedOut(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- kicked = make(chan int, 100)
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- for i := 0; i < 10; i++ {
- connect(pool, newPoolTestPeer(i, kicked))
- clock.Run(time.Millisecond)
- }
- if cap := connect(pool, newPoolTestPeer(10, kicked)); cap != 0 {
- t.Fatalf("New free client should be rejected")
- }
- clock.Run(0)
- select {
- case <-kicked:
- default:
- t.Fatalf("timeout")
- }
- disconnect(pool, newPoolTestPeer(10, kicked))
- clock.Run(5 * time.Minute)
- for i := 0; i < 10; i++ {
- connect(pool, newPoolTestPeer(i+10, kicked))
- }
- clock.Run(0)
-
- for i := 0; i < 10; i++ {
- select {
- case id := <-kicked:
- if id >= 10 {
- t.Fatalf("Old client should be kicked, now got: %d", id)
- }
- default:
- t.Fatalf("timeout")
- }
- }
-}
-
-func TestPositiveBalanceCalculation(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- kicked = make(chan int, 10)
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- addBalance(pool, newPoolTestPeer(0, kicked).node.ID(), int64(time.Minute*3))
- testPriorityConnect(t, pool, newPoolTestPeer(0, kicked), 10, true)
- clock.Run(time.Minute)
-
- disconnect(pool, newPoolTestPeer(0, kicked))
- pb, _ := getBalance(pool, newPoolTestPeer(0, kicked))
- if checkDiff(pb, uint64(time.Minute*2)) {
- t.Fatalf("Positive balance mismatch, want %v, got %v", uint64(time.Minute*2), pb)
- }
-}
-
-func TestDowngradePriorityClient(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- kicked = make(chan int, 10)
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 1})
-
- p := newPoolTestPeer(0, kicked)
- addBalance(pool, p.node.ID(), int64(time.Minute))
- testPriorityConnect(t, pool, p, 10, true)
- if p.cap != 10 {
- t.Fatalf("The capacity of priority peer hasn't been updated, got: %d", p.cap)
- }
-
- clock.Run(time.Minute) // All positive balance should be used up.
- time.Sleep(300 * time.Millisecond) // Ensure the callback is called
- if p.cap != 1 {
- t.Fatalf("The capcacity of peer should be downgraded, got: %d", p.cap)
- }
- pb, _ := getBalance(pool, newPoolTestPeer(0, kicked))
- if pb != 0 {
- t.Fatalf("Positive balance mismatch, want %v, got %v", 0, pb)
- }
-
- addBalance(pool, newPoolTestPeer(0, kicked).node.ID(), int64(time.Minute))
- pb, _ = getBalance(pool, newPoolTestPeer(0, kicked))
- if checkDiff(pb, uint64(time.Minute)) {
- t.Fatalf("Positive balance mismatch, want %v, got %v", uint64(time.Minute), pb)
- }
-}
-
-func TestNegativeBalanceCalculation(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetExpirationTCs(0, 3600)
- pool.SetLimits(10, uint64(10)) // Total capacity limit is 10
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1}, PriceFactors{TimeFactor: 1e-3, CapacityFactor: 0, RequestFactor: 1})
-
- for i := 0; i < 10; i++ {
- connect(pool, newPoolTestPeer(i, nil))
- }
- clock.Run(time.Second)
-
- for i := 0; i < 10; i++ {
- disconnect(pool, newPoolTestPeer(i, nil))
- _, nb := getBalance(pool, newPoolTestPeer(i, nil))
- if nb != 0 {
- t.Fatalf("Short connection shouldn't be recorded")
- }
- }
- for i := 0; i < 10; i++ {
- connect(pool, newPoolTestPeer(i, nil))
- }
- clock.Run(time.Minute)
- for i := 0; i < 10; i++ {
- disconnect(pool, newPoolTestPeer(i, nil))
- _, nb := getBalance(pool, newPoolTestPeer(i, nil))
- exp := uint64(time.Minute) / 1000
- exp -= exp / 120 // correct for negative balance expiration
- if checkDiff(nb, exp) {
- t.Fatalf("Negative balance mismatch, want %v, got %v", exp, nb)
- }
- }
-}
-
-func TestInactiveClient(t *testing.T) {
- t.Parallel()
-
- var (
- clock mclock.Simulated
- db = rawdb.NewMemoryDatabase()
- )
- pool := NewClientPool(db, 1, defaultConnectedBias, &clock, alwaysTrueFn)
- pool.Start()
- defer pool.Stop()
- pool.SetLimits(2, uint64(2))
-
- p1 := newPoolTestPeer(1, nil)
- p1.inactiveAllowed = true
- p2 := newPoolTestPeer(2, nil)
- p2.inactiveAllowed = true
- p3 := newPoolTestPeer(3, nil)
- p3.inactiveAllowed = true
- addBalance(pool, p1.node.ID(), 1000*int64(time.Second))
- addBalance(pool, p3.node.ID(), 2000*int64(time.Second))
- // p1: 1000 p2: 0 p3: 2000
- p1.cap = connect(pool, p1)
- if p1.cap != 1 {
- t.Fatalf("Failed to connect peer #1")
- }
- p2.cap = connect(pool, p2)
- if p2.cap != 1 {
- t.Fatalf("Failed to connect peer #2")
- }
- p3.cap = connect(pool, p3)
- if p3.cap != 1 {
- t.Fatalf("Failed to connect peer #3")
- }
- if p2.cap != 0 {
- t.Fatalf("Failed to deactivate peer #2")
- }
- addBalance(pool, p2.node.ID(), 3000*int64(time.Second))
- // p1: 1000 p2: 3000 p3: 2000
- if p2.cap != 1 {
- t.Fatalf("Failed to activate peer #2")
- }
- if p1.cap != 0 {
- t.Fatalf("Failed to deactivate peer #1")
- }
- addBalance(pool, p2.node.ID(), -2500*int64(time.Second))
- // p1: 1000 p2: 500 p3: 2000
- if p1.cap != 1 {
- t.Fatalf("Failed to activate peer #1")
- }
- if p2.cap != 0 {
- t.Fatalf("Failed to deactivate peer #2")
- }
- pool.SetDefaultFactors(PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0}, PriceFactors{TimeFactor: 1, CapacityFactor: 0, RequestFactor: 0})
- p4 := newPoolTestPeer(4, nil)
- addBalance(pool, p4.node.ID(), 1500*int64(time.Second))
- // p1: 1000 p2: 500 p3: 2000 p4: 1500
- p4.cap = connect(pool, p4)
- if p4.cap != 1 {
- t.Fatalf("Failed to activate peer #4")
- }
- if p1.cap != 0 {
- t.Fatalf("Failed to deactivate peer #1")
- }
- clock.Run(time.Second * 600)
- // manually trigger a check to avoid a long real-time wait
- pool.ns.SetState(p1.node, pool.setup.updateFlag, nodestate.Flags{}, 0)
- pool.ns.SetState(p1.node, nodestate.Flags{}, pool.setup.updateFlag, 0)
- // p1: 1000 p2: 500 p3: 2000 p4: 900
- if p1.cap != 1 {
- t.Fatalf("Failed to activate peer #1")
- }
- if p4.cap != 0 {
- t.Fatalf("Failed to deactivate peer #4")
- }
- disconnect(pool, p2)
- disconnect(pool, p4)
- addBalance(pool, p1.node.ID(), -1000*int64(time.Second))
- if p1.cap != 1 {
- t.Fatalf("Should not deactivate peer #1")
- }
- if p2.cap != 0 {
- t.Fatalf("Should not activate peer #2")
- }
-}
diff --git a/les/vflux/server/metrics.go b/les/vflux/server/metrics.go
deleted file mode 100644
index 680aebe2eab6..000000000000
--- a/les/vflux/server/metrics.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 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 .
-
-package server
-
-import (
- "github.com/ethereum/go-ethereum/metrics"
-)
-
-var (
- totalActiveCapacityGauge = metrics.NewRegisteredGauge("vflux/server/active/capacity", nil)
- totalActiveCountGauge = metrics.NewRegisteredGauge("vflux/server/active/count", nil)
- totalInactiveCountGauge = metrics.NewRegisteredGauge("vflux/server/inactive/count", nil)
-
- clientConnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/connected", nil)
- clientActivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/activated", nil)
- clientDeactivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/deactivated", nil)
- clientDisconnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/disconnected", nil)
-
- capacityQueryZeroMeter = metrics.NewRegisteredMeter("vflux/server/capQueryZero", nil)
- capacityQueryNonZeroMeter = metrics.NewRegisteredMeter("vflux/server/capQueryNonZero", nil)
-)
diff --git a/les/vflux/server/prioritypool.go b/les/vflux/server/prioritypool.go
deleted file mode 100644
index 766026a80832..000000000000
--- a/les/vflux/server/prioritypool.go
+++ /dev/null
@@ -1,695 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "math"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/common/prque"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-const (
- lazyQueueRefresh = time.Second * 10 // refresh period of the active queue
-)
-
-// priorityPool handles a set of nodes where each node has a capacity (a scalar value)
-// and a priority (which can change over time and can also depend on the capacity).
-// A node is active if it has at least the necessary minimal amount of capacity while
-// inactive nodes have 0 capacity (values between 0 and the minimum are not allowed).
-// The pool ensures that the number and total capacity of all active nodes are limited
-// and the highest priority nodes are active at all times (limits can be changed
-// during operation with immediate effect).
-//
-// When activating clients a priority bias is applied in favor of the already active
-// nodes in order to avoid nodes quickly alternating between active and inactive states
-// when their priorities are close to each other. The bias is specified in terms of
-// duration (time) because priorities are expected to usually get lower over time and
-// therefore a future minimum prediction (see EstMinPriority) should monotonously
-// decrease with the specified time parameter.
-// This time bias can be interpreted as minimum expected active time at the given
-// capacity (if the threshold priority stays the same).
-//
-// Nodes in the pool always have either inactiveFlag or activeFlag set. A new node is
-// added to the pool by externally setting inactiveFlag. priorityPool can switch a node
-// between inactiveFlag and activeFlag at any time. Nodes can be removed from the pool
-// by externally resetting both flags. activeFlag should not be set externally.
-//
-// The highest priority nodes in "inactive" state are moved to "active" state as soon as
-// the minimum capacity can be granted for them. The capacity of lower priority active
-// nodes is reduced or they are demoted to "inactive" state if their priority is
-// insufficient even at minimal capacity.
-type priorityPool struct {
- setup *serverSetup
- ns *nodestate.NodeStateMachine
- clock mclock.Clock
- lock sync.Mutex
- maxCount, maxCap uint64
- minCap uint64
- activeBias time.Duration
- capacityStepDiv, fineStepDiv uint64
-
- // The snapshot of priority pool for query.
- cachedCurve *capacityCurve
- ccUpdatedAt mclock.AbsTime
- ccUpdateForced bool
-
- // Runtime status of prioritypool, represents the
- // temporary state if tempState is not empty
- tempState []*ppNodeInfo
- activeCount, activeCap uint64
- activeQueue *prque.LazyQueue[int64, *ppNodeInfo]
- inactiveQueue *prque.Prque[int64, *ppNodeInfo]
-}
-
-// ppNodeInfo is the internal node descriptor of priorityPool
-type ppNodeInfo struct {
- nodePriority nodePriority
- node *enode.Node
- connected bool
- capacity uint64 // only changed when temporary state is committed
- activeIndex, inactiveIndex int
-
- tempState bool // should only be true while the priorityPool lock is held
- tempCapacity uint64 // equals capacity when tempState is false
-
- // the following fields only affect the temporary state and they are set to their
- // default value when leaving the temp state
- minTarget, stepDiv uint64
- bias time.Duration
-}
-
-// newPriorityPool creates a new priorityPool
-func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock mclock.Clock, minCap uint64, activeBias time.Duration, capacityStepDiv, fineStepDiv uint64) *priorityPool {
- pp := &priorityPool{
- setup: setup,
- ns: ns,
- clock: clock,
- inactiveQueue: prque.New[int64, *ppNodeInfo](inactiveSetIndex),
- minCap: minCap,
- activeBias: activeBias,
- capacityStepDiv: capacityStepDiv,
- fineStepDiv: fineStepDiv,
- }
- if pp.activeBias < time.Duration(1) {
- pp.activeBias = time.Duration(1)
- }
- pp.activeQueue = prque.NewLazyQueue(activeSetIndex, activePriority, pp.activeMaxPriority, clock, lazyQueueRefresh)
-
- ns.SubscribeField(pp.setup.balanceField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- if newValue != nil {
- c := &ppNodeInfo{
- node: node,
- nodePriority: newValue.(nodePriority),
- activeIndex: -1,
- inactiveIndex: -1,
- }
- ns.SetFieldSub(node, pp.setup.queueField, c)
- ns.SetStateSub(node, setup.inactiveFlag, nodestate.Flags{}, 0)
- } else {
- ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0)
- if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil {
- pp.disconnectNode(n)
- }
- ns.SetFieldSub(node, pp.setup.capacityField, nil)
- ns.SetFieldSub(node, pp.setup.queueField, nil)
- }
- })
- ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) {
- if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil {
- if oldState.IsEmpty() {
- pp.connectNode(c)
- }
- if newState.IsEmpty() {
- pp.disconnectNode(c)
- }
- }
- })
- ns.SubscribeState(pp.setup.updateFlag, func(node *enode.Node, oldState, newState nodestate.Flags) {
- if !newState.IsEmpty() {
- pp.updatePriority(node)
- }
- })
- return pp
-}
-
-// requestCapacity tries to set the capacity of a connected node to the highest possible
-// value inside the given target range. If maxTarget is not reachable then the capacity is
-// iteratively reduced in fine steps based on the fineStepDiv parameter until minTarget is reached.
-// The function returns the new capacity if successful and the original capacity otherwise.
-// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) requestCapacity(node *enode.Node, minTarget, maxTarget uint64, bias time.Duration) uint64 {
- pp.lock.Lock()
- pp.activeQueue.Refresh()
-
- if minTarget < pp.minCap {
- minTarget = pp.minCap
- }
- if maxTarget < minTarget {
- maxTarget = minTarget
- }
- if bias < pp.activeBias {
- bias = pp.activeBias
- }
- c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo)
- if c == nil {
- log.Error("requestCapacity called for unknown node", "id", node.ID())
- pp.lock.Unlock()
- return 0
- }
- pp.setTempState(c)
- if maxTarget > c.capacity {
- pp.setTempStepDiv(c, pp.fineStepDiv)
- pp.setTempBias(c, bias)
- }
- pp.setTempCapacity(c, maxTarget)
- c.minTarget = minTarget
- pp.removeFromQueues(c)
- pp.activeQueue.Push(c)
- pp.enforceLimits()
- updates := pp.finalizeChanges(c.tempCapacity >= minTarget && c.tempCapacity <= maxTarget && c.tempCapacity != c.capacity)
- pp.lock.Unlock()
- pp.updateFlags(updates)
- return c.capacity
-}
-
-// SetLimits sets the maximum number and total capacity of simultaneously active nodes
-func (pp *priorityPool) SetLimits(maxCount, maxCap uint64) {
- pp.lock.Lock()
- pp.activeQueue.Refresh()
- inc := (maxCount > pp.maxCount) || (maxCap > pp.maxCap)
- dec := (maxCount < pp.maxCount) || (maxCap < pp.maxCap)
- pp.maxCount, pp.maxCap = maxCount, maxCap
-
- var updates []capUpdate
- if dec {
- pp.enforceLimits()
- updates = pp.finalizeChanges(true)
- }
- if inc {
- updates = append(updates, pp.tryActivate(false)...)
- }
- pp.lock.Unlock()
- pp.ns.Operation(func() { pp.updateFlags(updates) })
-}
-
-// setActiveBias sets the bias applied when trying to activate inactive nodes
-func (pp *priorityPool) setActiveBias(bias time.Duration) {
- pp.lock.Lock()
- pp.activeBias = bias
- if pp.activeBias < time.Duration(1) {
- pp.activeBias = time.Duration(1)
- }
- updates := pp.tryActivate(false)
- pp.lock.Unlock()
- pp.ns.Operation(func() { pp.updateFlags(updates) })
-}
-
-// Active returns the number and total capacity of currently active nodes
-func (pp *priorityPool) Active() (uint64, uint64) {
- pp.lock.Lock()
- defer pp.lock.Unlock()
-
- return pp.activeCount, pp.activeCap
-}
-
-// Inactive returns the number of currently inactive nodes
-func (pp *priorityPool) Inactive() int {
- pp.lock.Lock()
- defer pp.lock.Unlock()
-
- return pp.inactiveQueue.Size()
-}
-
-// Limits returns the maximum allowed number and total capacity of active nodes
-func (pp *priorityPool) Limits() (uint64, uint64) {
- pp.lock.Lock()
- defer pp.lock.Unlock()
-
- return pp.maxCount, pp.maxCap
-}
-
-// inactiveSetIndex callback updates ppNodeInfo item index in inactiveQueue
-func inactiveSetIndex(a *ppNodeInfo, index int) {
- a.inactiveIndex = index
-}
-
-// activeSetIndex callback updates ppNodeInfo item index in activeQueue
-func activeSetIndex(a *ppNodeInfo, index int) {
- a.activeIndex = index
-}
-
-// invertPriority inverts a priority value. The active queue uses inverted priorities
-// because the node on the top is the first to be deactivated.
-func invertPriority(p int64) int64 {
- if p == math.MinInt64 {
- return math.MaxInt64
- }
- return -p
-}
-
-// activePriority callback returns actual priority of ppNodeInfo item in activeQueue
-func activePriority(c *ppNodeInfo) int64 {
- if c.bias == 0 {
- return invertPriority(c.nodePriority.priority(c.tempCapacity))
- } else {
- return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, 0, c.bias, true))
- }
-}
-
-// activeMaxPriority callback returns estimated maximum priority of ppNodeInfo item in activeQueue
-func (pp *priorityPool) activeMaxPriority(c *ppNodeInfo, until mclock.AbsTime) int64 {
- future := time.Duration(until - pp.clock.Now())
- if future < 0 {
- future = 0
- }
- return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, future, c.bias, false))
-}
-
-// inactivePriority callback returns actual priority of ppNodeInfo item in inactiveQueue
-func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 {
- return p.nodePriority.priority(pp.minCap)
-}
-
-// removeFromQueues removes the node from the active/inactive queues
-func (pp *priorityPool) removeFromQueues(c *ppNodeInfo) {
- if c.activeIndex >= 0 {
- pp.activeQueue.Remove(c.activeIndex)
- }
- if c.inactiveIndex >= 0 {
- pp.inactiveQueue.Remove(c.inactiveIndex)
- }
-}
-
-// connectNode is called when a new node has been added to the pool (inactiveFlag set)
-// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) connectNode(c *ppNodeInfo) {
- pp.lock.Lock()
- pp.activeQueue.Refresh()
- if c.connected {
- pp.lock.Unlock()
- return
- }
- c.connected = true
- pp.inactiveQueue.Push(c, pp.inactivePriority(c))
- updates := pp.tryActivate(false)
- pp.lock.Unlock()
- pp.updateFlags(updates)
-}
-
-// disconnectNode is called when a node has been removed from the pool (both inactiveFlag
-// and activeFlag reset)
-// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) disconnectNode(c *ppNodeInfo) {
- pp.lock.Lock()
- pp.activeQueue.Refresh()
- if !c.connected {
- pp.lock.Unlock()
- return
- }
- c.connected = false
- pp.removeFromQueues(c)
-
- var updates []capUpdate
- if c.capacity != 0 {
- pp.setTempState(c)
- pp.setTempCapacity(c, 0)
- updates = pp.tryActivate(true)
- }
- pp.lock.Unlock()
- pp.updateFlags(updates)
-}
-
-// setTempState internally puts a node in a temporary state that can either be reverted
-// or confirmed later. This temporary state allows changing the capacity of a node and
-// moving it between the active and inactive queue. activeFlag/inactiveFlag and
-// capacityField are not changed while the changes are still temporary.
-func (pp *priorityPool) setTempState(c *ppNodeInfo) {
- if c.tempState {
- return
- }
- c.tempState = true
- if c.tempCapacity != c.capacity { // should never happen
- log.Error("tempCapacity != capacity when entering tempState")
- }
- // Assign all the defaults to the temp state.
- c.minTarget = pp.minCap
- c.stepDiv = pp.capacityStepDiv
- c.bias = 0
- pp.tempState = append(pp.tempState, c)
-}
-
-// unsetTempState revokes the temp status of the node and reset all internal
-// fields to the default value.
-func (pp *priorityPool) unsetTempState(c *ppNodeInfo) {
- if !c.tempState {
- return
- }
- c.tempState = false
- if c.tempCapacity != c.capacity { // should never happen
- log.Error("tempCapacity != capacity when leaving tempState")
- }
- c.minTarget = pp.minCap
- c.stepDiv = pp.capacityStepDiv
- c.bias = 0
-}
-
-// setTempCapacity changes the capacity of a node in the temporary state and adjusts
-// activeCap and activeCount accordingly. Since this change is performed in the temporary
-// state it should be called after setTempState and before finalizeChanges.
-func (pp *priorityPool) setTempCapacity(c *ppNodeInfo, cap uint64) {
- if !c.tempState { // should never happen
- log.Error("Node is not in temporary state")
- return
- }
- pp.activeCap += cap - c.tempCapacity
- if c.tempCapacity == 0 {
- pp.activeCount++
- }
- if cap == 0 {
- pp.activeCount--
- }
- c.tempCapacity = cap
-}
-
-// setTempBias changes the connection bias of a node in the temporary state.
-func (pp *priorityPool) setTempBias(c *ppNodeInfo, bias time.Duration) {
- if !c.tempState { // should never happen
- log.Error("Node is not in temporary state")
- return
- }
- c.bias = bias
-}
-
-// setTempStepDiv changes the capacity divisor of a node in the temporary state.
-func (pp *priorityPool) setTempStepDiv(c *ppNodeInfo, stepDiv uint64) {
- if !c.tempState { // should never happen
- log.Error("Node is not in temporary state")
- return
- }
- c.stepDiv = stepDiv
-}
-
-// enforceLimits enforces active node count and total capacity limits. It returns the
-// lowest active node priority. Note that this function is performed on the temporary
-// internal state.
-func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) {
- if pp.activeCap <= pp.maxCap && pp.activeCount <= pp.maxCount {
- return nil, math.MinInt64
- }
- var (
- lastNode *ppNodeInfo
- maxActivePriority int64
- )
- pp.activeQueue.MultiPop(func(c *ppNodeInfo, priority int64) bool {
- lastNode = c
- pp.setTempState(c)
- maxActivePriority = priority
- if c.tempCapacity == c.minTarget || pp.activeCount > pp.maxCount {
- pp.setTempCapacity(c, 0)
- } else {
- sub := c.tempCapacity / c.stepDiv
- if sub == 0 {
- sub = 1
- }
- if c.tempCapacity-sub < c.minTarget {
- sub = c.tempCapacity - c.minTarget
- }
- pp.setTempCapacity(c, c.tempCapacity-sub)
- pp.activeQueue.Push(c)
- }
- return pp.activeCap > pp.maxCap || pp.activeCount > pp.maxCount
- })
- return lastNode, invertPriority(maxActivePriority)
-}
-
-// finalizeChanges either commits or reverts temporary changes. The necessary capacity
-// field and according flag updates are not performed here but returned in a list because
-// they should be performed while the mutex is not held.
-func (pp *priorityPool) finalizeChanges(commit bool) (updates []capUpdate) {
- for _, c := range pp.tempState {
- // always remove and push back in order to update biased priority
- pp.removeFromQueues(c)
- oldCapacity := c.capacity
- if commit {
- c.capacity = c.tempCapacity
- } else {
- pp.setTempCapacity(c, c.capacity) // revert activeCount/activeCap
- }
- pp.unsetTempState(c)
-
- if c.connected {
- if c.capacity != 0 {
- pp.activeQueue.Push(c)
- } else {
- pp.inactiveQueue.Push(c, pp.inactivePriority(c))
- }
- if c.capacity != oldCapacity {
- updates = append(updates, capUpdate{c.node, oldCapacity, c.capacity})
- }
- }
- }
- pp.tempState = nil
- if commit {
- pp.ccUpdateForced = true
- }
- return
-}
-
-// capUpdate describes a capacityField and activeFlag/inactiveFlag update
-type capUpdate struct {
- node *enode.Node
- oldCap, newCap uint64
-}
-
-// updateFlags performs capacityField and activeFlag/inactiveFlag updates while the
-// pool mutex is not held
-// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) updateFlags(updates []capUpdate) {
- for _, f := range updates {
- if f.oldCap == 0 {
- pp.ns.SetStateSub(f.node, pp.setup.activeFlag, pp.setup.inactiveFlag, 0)
- }
- if f.newCap == 0 {
- pp.ns.SetStateSub(f.node, pp.setup.inactiveFlag, pp.setup.activeFlag, 0)
- pp.ns.SetFieldSub(f.node, pp.setup.capacityField, nil)
- } else {
- pp.ns.SetFieldSub(f.node, pp.setup.capacityField, f.newCap)
- }
- }
-}
-
-// tryActivate tries to activate inactive nodes if possible
-func (pp *priorityPool) tryActivate(commit bool) []capUpdate {
- for pp.inactiveQueue.Size() > 0 {
- c := pp.inactiveQueue.PopItem()
- pp.setTempState(c)
- pp.setTempBias(c, pp.activeBias)
- pp.setTempCapacity(c, pp.minCap)
- pp.activeQueue.Push(c)
- pp.enforceLimits()
- if c.tempCapacity > 0 {
- commit = true
- pp.setTempBias(c, 0)
- } else {
- break
- }
- }
- pp.ccUpdateForced = true
- return pp.finalizeChanges(commit)
-}
-
-// updatePriority gets the current priority value of the given node from the nodePriority
-// interface and performs the necessary changes. It is triggered by updateFlag.
-// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) updatePriority(node *enode.Node) {
- pp.lock.Lock()
- pp.activeQueue.Refresh()
- c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo)
- if c == nil || !c.connected {
- pp.lock.Unlock()
- return
- }
- pp.removeFromQueues(c)
- if c.capacity != 0 {
- pp.activeQueue.Push(c)
- } else {
- pp.inactiveQueue.Push(c, pp.inactivePriority(c))
- }
- updates := pp.tryActivate(false)
- pp.lock.Unlock()
- pp.updateFlags(updates)
-}
-
-// capacityCurve is a snapshot of the priority pool contents in a format that can efficiently
-// estimate how much capacity could be granted to a given node at a given priority level.
-type capacityCurve struct {
- points []curvePoint // curve points sorted in descending order of priority
- index map[enode.ID][]int // curve point indexes belonging to each node
- excludeList []int // curve point indexes of excluded node
- excludeFirst bool // true if activeCount == maxCount
-}
-
-type curvePoint struct {
- freeCap uint64 // available capacity and node count at the current priority level
- nextPri int64 // next priority level where more capacity will be available
-}
-
-// getCapacityCurve returns a new or recently cached capacityCurve based on the contents of the pool
-func (pp *priorityPool) getCapacityCurve() *capacityCurve {
- pp.lock.Lock()
- defer pp.lock.Unlock()
-
- now := pp.clock.Now()
- dt := time.Duration(now - pp.ccUpdatedAt)
- if !pp.ccUpdateForced && pp.cachedCurve != nil && dt < time.Second*10 {
- return pp.cachedCurve
- }
-
- pp.ccUpdateForced = false
- pp.ccUpdatedAt = now
- curve := &capacityCurve{
- index: make(map[enode.ID][]int),
- }
- pp.cachedCurve = curve
-
- var excludeID enode.ID
- excludeFirst := pp.maxCount == pp.activeCount
- // reduce node capacities or remove nodes until nothing is left in the queue;
- // record the available capacity and the necessary priority after each step
- lastPri := int64(math.MinInt64)
- for pp.activeCap > 0 {
- cp := curvePoint{}
- if pp.activeCap > pp.maxCap {
- log.Error("Active capacity is greater than allowed maximum", "active", pp.activeCap, "maximum", pp.maxCap)
- } else {
- cp.freeCap = pp.maxCap - pp.activeCap
- }
- // temporarily increase activeCap to enforce reducing or removing a node capacity
- tempCap := cp.freeCap + 1
- pp.activeCap += tempCap
- var next *ppNodeInfo
- // enforceLimits removes the lowest priority node if it has minimal capacity,
- // otherwise reduces its capacity
- next, cp.nextPri = pp.enforceLimits()
- if cp.nextPri < lastPri {
- // enforce monotonicity which may be broken by continuously changing priorities
- cp.nextPri = lastPri
- } else {
- lastPri = cp.nextPri
- }
- pp.activeCap -= tempCap
- if next == nil {
- log.Error("getCapacityCurve: cannot remove next element from the priority queue")
- break
- }
- id := next.node.ID()
- if excludeFirst {
- // if the node count limit is already reached then mark the node with the
- // lowest priority for exclusion
- curve.excludeFirst = true
- excludeID = id
- excludeFirst = false
- }
- // multiple curve points and therefore multiple indexes may belong to a node
- // if it was removed in multiple steps (if its capacity was more than the minimum)
- curve.index[id] = append(curve.index[id], len(curve.points))
- curve.points = append(curve.points, cp)
- }
- // restore original state of the queue
- pp.finalizeChanges(false)
- curve.points = append(curve.points, curvePoint{
- freeCap: pp.maxCap,
- nextPri: math.MaxInt64,
- })
- if curve.excludeFirst {
- curve.excludeList = curve.index[excludeID]
- }
- return curve
-}
-
-// exclude returns a capacityCurve with the given node excluded from the original curve
-func (cc *capacityCurve) exclude(id enode.ID) *capacityCurve {
- if excludeList, ok := cc.index[id]; ok {
- // return a new version of the curve (only one excluded node can be selected)
- // Note: if the first node was excluded by default (excludeFirst == true) then
- // we can forget about that and exclude the node with the given id instead.
- return &capacityCurve{
- points: cc.points,
- index: cc.index,
- excludeList: excludeList,
- }
- }
- return cc
-}
-
-func (cc *capacityCurve) getPoint(i int) curvePoint {
- cp := cc.points[i]
- if i == 0 && cc.excludeFirst {
- cp.freeCap = 0
- return cp
- }
- for ii := len(cc.excludeList) - 1; ii >= 0; ii-- {
- ei := cc.excludeList[ii]
- if ei < i {
- break
- }
- e1, e2 := cc.points[ei], cc.points[ei+1]
- cp.freeCap += e2.freeCap - e1.freeCap
- }
- return cp
-}
-
-// maxCapacity calculates the maximum capacity available for a node with a given
-// (monotonically decreasing) priority vs. capacity function. Note that if the requesting
-// node is already in the pool then it should be excluded from the curve in order to get
-// the correct result.
-func (cc *capacityCurve) maxCapacity(priority func(cap uint64) int64) uint64 {
- min, max := 0, len(cc.points)-1 // the curve always has at least one point
- for min < max {
- mid := (min + max) / 2
- cp := cc.getPoint(mid)
- if cp.freeCap == 0 || priority(cp.freeCap) > cp.nextPri {
- min = mid + 1
- } else {
- max = mid
- }
- }
- cp2 := cc.getPoint(min)
- if cp2.freeCap == 0 || min == 0 {
- return cp2.freeCap
- }
- cp1 := cc.getPoint(min - 1)
- if priority(cp2.freeCap) > cp1.nextPri {
- return cp2.freeCap
- }
- minc, maxc := cp1.freeCap, cp2.freeCap-1
- for minc < maxc {
- midc := (minc + maxc + 1) / 2
- if midc == 0 || priority(midc) > cp1.nextPri {
- minc = midc
- } else {
- maxc = midc - 1
- }
- }
- return maxc
-}
diff --git a/les/vflux/server/prioritypool_test.go b/les/vflux/server/prioritypool_test.go
deleted file mode 100644
index 60b7b83bbcb5..000000000000
--- a/les/vflux/server/prioritypool_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package server
-
-import (
- "math/rand"
- "reflect"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-const (
- testCapacityStepDiv = 100
- testCapacityToleranceDiv = 10
- testMinCap = 100
-)
-
-type ppTestClient struct {
- node *enode.Node
- balance, cap uint64
-}
-
-func (c *ppTestClient) priority(cap uint64) int64 {
- return int64(c.balance / cap)
-}
-
-func (c *ppTestClient) estimatePriority(cap uint64, addBalance int64, future, bias time.Duration, update bool) int64 {
- return int64(c.balance / cap)
-}
-
-func TestPriorityPool(t *testing.T) {
- t.Parallel()
-
- clock := &mclock.Simulated{}
- setup := newServerSetup()
- setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{}))
- ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup)
-
- ns.SubscribeField(setup.capacityField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
- if n := ns.GetField(node, setup.balanceField); n != nil {
- c := n.(*ppTestClient)
- c.cap = newValue.(uint64)
- }
- })
- pp := newPriorityPool(ns, setup, clock, testMinCap, 0, testCapacityStepDiv, testCapacityStepDiv)
- ns.Start()
- pp.SetLimits(100, 1000000)
- clients := make([]*ppTestClient, 100)
- raise := func(c *ppTestClient) {
- for {
- var ok bool
- ns.Operation(func() {
- newCap := c.cap + c.cap/testCapacityStepDiv
- ok = pp.requestCapacity(c.node, newCap, newCap, 0) == newCap
- })
- if !ok {
- return
- }
- }
- }
- var sumBalance uint64
- check := func(c *ppTestClient) {
- expCap := 1000000 * c.balance / sumBalance
- capTol := expCap / testCapacityToleranceDiv
- if c.cap < expCap-capTol || c.cap > expCap+capTol {
- t.Errorf("Wrong node capacity (expected %d, got %d)", expCap, c.cap)
- }
- }
-
- for i := range clients {
- c := &ppTestClient{
- node: enode.SignNull(&enr.Record{}, enode.ID{byte(i)}),
- balance: 100000000000,
- cap: 1000,
- }
- sumBalance += c.balance
- clients[i] = c
- ns.SetField(c.node, setup.balanceField, c)
- ns.SetState(c.node, setup.inactiveFlag, nodestate.Flags{}, 0)
- raise(c)
- check(c)
- }
-
- for count := 0; count < 100; count++ {
- c := clients[rand.Intn(len(clients))]
- oldBalance := c.balance
- c.balance = uint64(rand.Int63n(100000000000) + 100000000000)
- sumBalance += c.balance - oldBalance
- pp.ns.SetState(c.node, setup.updateFlag, nodestate.Flags{}, 0)
- pp.ns.SetState(c.node, nodestate.Flags{}, setup.updateFlag, 0)
- if c.balance > oldBalance {
- raise(c)
- } else {
- for _, c := range clients {
- raise(c)
- }
- }
- // check whether capacities are proportional to balances
- for _, c := range clients {
- check(c)
- }
- if count%10 == 0 {
- // test available capacity calculation with capacity curve
- c = clients[rand.Intn(len(clients))]
- curve := pp.getCapacityCurve().exclude(c.node.ID())
-
- add := uint64(rand.Int63n(10000000000000))
- c.balance += add
- sumBalance += add
- expCap := curve.maxCapacity(func(cap uint64) int64 {
- return int64(c.balance / cap)
- })
- var ok bool
- expFail := expCap + 10
- if expFail < testMinCap {
- expFail = testMinCap
- }
- ns.Operation(func() {
- ok = pp.requestCapacity(c.node, expFail, expFail, 0) == expFail
- })
- if ok {
- t.Errorf("Request for more than expected available capacity succeeded")
- }
- if expCap >= testMinCap {
- ns.Operation(func() {
- ok = pp.requestCapacity(c.node, expCap, expCap, 0) == expCap
- })
- if !ok {
- t.Errorf("Request for expected available capacity failed")
- }
- }
- c.balance -= add
- sumBalance -= add
- pp.ns.SetState(c.node, setup.updateFlag, nodestate.Flags{}, 0)
- pp.ns.SetState(c.node, nodestate.Flags{}, setup.updateFlag, 0)
- for _, c := range clients {
- raise(c)
- }
- }
- }
-
- ns.Stop()
-}
-
-func TestCapacityCurve(t *testing.T) {
- t.Parallel()
-
- clock := &mclock.Simulated{}
- setup := newServerSetup()
- setup.balanceField = setup.setup.NewField("ppTestClient", reflect.TypeOf(&ppTestClient{}))
- ns := nodestate.NewNodeStateMachine(nil, nil, clock, setup.setup)
-
- pp := newPriorityPool(ns, setup, clock, 400000, 0, 2, 2)
- ns.Start()
- pp.SetLimits(10, 10000000)
- clients := make([]*ppTestClient, 10)
-
- for i := range clients {
- c := &ppTestClient{
- node: enode.SignNull(&enr.Record{}, enode.ID{byte(i)}),
- balance: 100000000000 * uint64(i+1),
- cap: 1000000,
- }
- clients[i] = c
- ns.SetField(c.node, setup.balanceField, c)
- ns.SetState(c.node, setup.inactiveFlag, nodestate.Flags{}, 0)
- ns.Operation(func() {
- pp.requestCapacity(c.node, c.cap, c.cap, 0)
- })
- }
-
- curve := pp.getCapacityCurve()
- check := func(balance, expCap uint64) {
- cap := curve.maxCapacity(func(cap uint64) int64 {
- return int64(balance / cap)
- })
- var fail bool
- if cap == 0 || expCap == 0 {
- fail = cap != expCap
- } else {
- pri := balance / cap
- expPri := balance / expCap
- fail = pri != expPri && pri != expPri+1
- }
- if fail {
- t.Errorf("Incorrect capacity for %d balance (got %d, expected %d)", balance, cap, expCap)
- }
- }
-
- check(0, 0)
- check(10000000000, 100000)
- check(50000000000, 500000)
- check(100000000000, 1000000)
- check(200000000000, 1000000)
- check(300000000000, 1500000)
- check(450000000000, 1500000)
- check(600000000000, 2000000)
- check(800000000000, 2000000)
- check(1000000000000, 2500000)
-
- pp.SetLimits(11, 10000000)
- curve = pp.getCapacityCurve()
-
- check(0, 0)
- check(10000000000, 100000)
- check(50000000000, 500000)
- check(150000000000, 750000)
- check(200000000000, 1000000)
- check(220000000000, 1100000)
- check(275000000000, 1100000)
- check(375000000000, 1500000)
- check(450000000000, 1500000)
- check(600000000000, 2000000)
- check(800000000000, 2000000)
- check(1000000000000, 2500000)
-
- ns.Stop()
-}
diff --git a/les/vflux/server/service.go b/les/vflux/server/service.go
deleted file mode 100644
index 40515f072eb7..000000000000
--- a/les/vflux/server/service.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2021 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 .
-
-package server
-
-import (
- "net"
- "strings"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/les/utils"
- "github.com/ethereum/go-ethereum/les/vflux"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-type (
- // Server serves vflux requests
- Server struct {
- limiter *utils.Limiter
- lock sync.Mutex
- services map[string]*serviceEntry
- delayPerRequest time.Duration
- }
-
- // Service is a service registered at the Server and identified by a string id
- Service interface {
- Handle(id enode.ID, address string, name string, data []byte) []byte // never called concurrently
- }
-
- serviceEntry struct {
- id, desc string
- backend Service
- }
-)
-
-// NewServer creates a new Server
-func NewServer(delayPerRequest time.Duration) *Server {
- return &Server{
- limiter: utils.NewLimiter(1000),
- delayPerRequest: delayPerRequest,
- services: make(map[string]*serviceEntry),
- }
-}
-
-// Register registers a Service
-func (s *Server) Register(b Service, id, desc string) {
- srv := &serviceEntry{backend: b, id: id, desc: desc}
- if strings.Contains(srv.id, ":") {
- // srv.id + ":" will be used as a service database prefix
- log.Error("Service ID contains ':'", "id", srv.id)
- return
- }
- s.lock.Lock()
- s.services[srv.id] = srv
- s.lock.Unlock()
-}
-
-// Serve serves a vflux request batch
-// Note: requests are served by the Handle functions of the registered services. Serve
-// may be called concurrently but the Handle functions are called sequentially and
-// therefore thread safety is guaranteed.
-func (s *Server) Serve(id enode.ID, address string, requests vflux.Requests) vflux.Replies {
- reqLen := uint(len(requests))
- if reqLen == 0 || reqLen > vflux.MaxRequestLength {
- return nil
- }
- // Note: the value parameter will be supplied by the token sale module (total amount paid)
- ch := <-s.limiter.Add(id, address, 0, reqLen)
- if ch == nil {
- return nil
- }
- // Note: the limiter ensures that the following section is not running concurrently,
- // the lock only protects against contention caused by new service registration
- s.lock.Lock()
- results := make(vflux.Replies, len(requests))
- for i, req := range requests {
- if service := s.services[req.Service]; service != nil {
- results[i] = service.backend.Handle(id, address, req.Name, req.Params)
- }
- }
- s.lock.Unlock()
- time.Sleep(s.delayPerRequest * time.Duration(reqLen))
- close(ch)
- return results
-}
-
-// ServeEncoded serves an encoded vflux request batch and returns the encoded replies
-func (s *Server) ServeEncoded(id enode.ID, addr *net.UDPAddr, req []byte) []byte {
- var requests vflux.Requests
- if err := rlp.DecodeBytes(req, &requests); err != nil {
- return nil
- }
- results := s.Serve(id, addr.String(), requests)
- if results == nil {
- return nil
- }
- res, _ := rlp.EncodeToBytes(&results)
- return res
-}
-
-// Stop shuts down the server
-func (s *Server) Stop() {
- s.limiter.Stop()
-}
diff --git a/les/vflux/server/status.go b/les/vflux/server/status.go
deleted file mode 100644
index 2d7e25b68461..000000000000
--- a/les/vflux/server/status.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2021 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 .
-
-package server
-
-import (
- "reflect"
-
- "github.com/ethereum/go-ethereum/p2p/nodestate"
-)
-
-type peerWrapper struct{ clientPeer } // the NodeStateMachine type system needs this wrapper
-
-// serverSetup is a wrapper of the node state machine setup, which contains
-// all the created flags and fields used in the vflux server side.
-type serverSetup struct {
- setup *nodestate.Setup
- clientField nodestate.Field // Field contains the client peer handler
-
- // Flags and fields controlled by balance tracker. BalanceTracker
- // is responsible for setting/deleting these flags or fields.
- priorityFlag nodestate.Flags // Flag is set if the node has a positive balance
- updateFlag nodestate.Flags // Flag is set whenever the node balance is changed(priority changed)
- balanceField nodestate.Field // Field contains the client balance for priority calculation
-
- // Flags and fields controlled by priority queue. Priority queue
- // is responsible for setting/deleting these flags or fields.
- activeFlag nodestate.Flags // Flag is set if the node is active
- inactiveFlag nodestate.Flags // Flag is set if the node is inactive
- capacityField nodestate.Field // Field contains the capacity of the node
- queueField nodestate.Field // Field contains the information in the priority queue
-}
-
-// newServerSetup initializes the setup for state machine and returns the flags/fields group.
-func newServerSetup() *serverSetup {
- setup := &serverSetup{setup: &nodestate.Setup{}}
- setup.clientField = setup.setup.NewField("client", reflect.TypeOf(peerWrapper{}))
- setup.priorityFlag = setup.setup.NewFlag("priority")
- setup.updateFlag = setup.setup.NewFlag("update")
- setup.balanceField = setup.setup.NewField("balance", reflect.TypeOf(&nodeBalance{}))
- setup.activeFlag = setup.setup.NewFlag("active")
- setup.inactiveFlag = setup.setup.NewFlag("inactive")
- setup.capacityField = setup.setup.NewField("capacity", reflect.TypeOf(uint64(0)))
- setup.queueField = setup.setup.NewField("queue", reflect.TypeOf(&ppNodeInfo{}))
- return setup
-}
diff --git a/tests/fuzzers/les/les-fuzzer.go b/tests/fuzzers/les/les-fuzzer.go
deleted file mode 100644
index 209dda0bb915..000000000000
--- a/tests/fuzzers/les/les-fuzzer.go
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright 2021 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 .
-
-package les
-
-import (
- "bytes"
- "encoding/binary"
- "io"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/txpool/legacypool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- l "github.com/ethereum/go-ethereum/les"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-var (
- bankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey)
- bankFunds = new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether))
-
- testChainLen = 256
- testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
-
- chain *core.BlockChain
- addresses []common.Address
- txHashes []common.Hash
-
- chtTrie *trie.Trie
- bloomTrie *trie.Trie
- chtKeys [][]byte
- bloomKeys [][]byte
-)
-
-func makechain() (bc *core.BlockChain, addresses []common.Address, txHashes []common.Hash) {
- gspec := &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
- GasLimit: 100000000,
- }
- signer := types.HomesteadSigner{}
- _, blocks, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), testChainLen,
- func(i int, gen *core.BlockGen) {
- var (
- tx *types.Transaction
- addr common.Address
- )
- nonce := uint64(i)
- if i%4 == 0 {
- tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(params.GWei), testContractCode), signer, bankKey)
- addr = crypto.CreateAddress(bankAddr, nonce)
- } else {
- addr = common.BigToAddress(big.NewInt(int64(i)))
- tx, _ = types.SignTx(types.NewTransaction(nonce, addr, big.NewInt(10000), params.TxGas, big.NewInt(params.GWei), nil), signer, bankKey)
- }
- gen.AddTx(tx)
- addresses = append(addresses, addr)
- txHashes = append(txHashes, tx.Hash())
- })
- bc, _ = core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
- if _, err := bc.InsertChain(blocks); err != nil {
- panic(err)
- }
- return
-}
-
-func makeTries() (chtTrie *trie.Trie, bloomTrie *trie.Trie, chtKeys, bloomKeys [][]byte) {
- chtTrie = trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), trie.HashDefaults))
- bloomTrie = trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), trie.HashDefaults))
- for i := 0; i < testChainLen; i++ {
- // The element in CHT is ->
- key := make([]byte, 8)
- binary.BigEndian.PutUint64(key, uint64(i+1))
- chtTrie.MustUpdate(key, []byte{0x1, 0xf})
- chtKeys = append(chtKeys, key)
-
- // The element in Bloom trie is <2 byte bit index> + -> bloom
- key2 := make([]byte, 10)
- binary.BigEndian.PutUint64(key2[2:], uint64(i+1))
- bloomTrie.MustUpdate(key2, []byte{0x2, 0xe})
- bloomKeys = append(bloomKeys, key2)
- }
- return
-}
-
-func init() {
- chain, addresses, txHashes = makechain()
- chtTrie, bloomTrie, chtKeys, bloomKeys = makeTries()
-}
-
-type fuzzer struct {
- chain *core.BlockChain
- pool *txpool.TxPool
-
- chainLen int
- addresses []common.Address
- txs []common.Hash
- nonce uint64
-
- chtKeys [][]byte
- bloomKeys [][]byte
- chtTrie *trie.Trie
- bloomTrie *trie.Trie
-
- input io.Reader
- exhausted bool
-}
-
-func newFuzzer(input []byte) *fuzzer {
- pool := legacypool.New(legacypool.DefaultConfig, chain)
- txpool, _ := txpool.New(new(big.Int).SetUint64(legacypool.DefaultConfig.PriceLimit), chain, []txpool.SubPool{pool})
-
- return &fuzzer{
- chain: chain,
- chainLen: testChainLen,
- addresses: addresses,
- txs: txHashes,
- chtTrie: chtTrie,
- bloomTrie: bloomTrie,
- chtKeys: chtKeys,
- bloomKeys: bloomKeys,
- nonce: uint64(len(txHashes)),
- pool: txpool,
- input: bytes.NewReader(input),
- }
-}
-
-func (f *fuzzer) read(size int) []byte {
- out := make([]byte, size)
- if _, err := f.input.Read(out); err != nil {
- f.exhausted = true
- }
- return out
-}
-
-func (f *fuzzer) randomByte() byte {
- d := f.read(1)
- return d[0]
-}
-
-func (f *fuzzer) randomBool() bool {
- d := f.read(1)
- return d[0]&1 == 1
-}
-
-func (f *fuzzer) randomInt(max int) int {
- if max == 0 {
- return 0
- }
- if max <= 256 {
- return int(f.randomByte()) % max
- }
- var a uint16
- if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
- f.exhausted = true
- }
- return int(a % uint16(max))
-}
-
-func (f *fuzzer) randomX(max int) uint64 {
- var a uint16
- if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
- f.exhausted = true
- }
- if a < 0x8000 {
- return uint64(a%uint16(max+1)) - 1
- }
- return (uint64(1)<<(a%64+1) - 1) & (uint64(a) * 343897772345826595)
-}
-
-func (f *fuzzer) randomBlockHash() common.Hash {
- h := f.chain.GetCanonicalHash(uint64(f.randomInt(3 * f.chainLen)))
- if h != (common.Hash{}) {
- return h
- }
- return common.BytesToHash(f.read(common.HashLength))
-}
-
-func (f *fuzzer) randomAddress() []byte {
- i := f.randomInt(3 * len(f.addresses))
- if i < len(f.addresses) {
- return f.addresses[i].Bytes()
- }
- return f.read(common.AddressLength)
-}
-
-func (f *fuzzer) randomCHTTrieKey() []byte {
- i := f.randomInt(3 * len(f.chtKeys))
- if i < len(f.chtKeys) {
- return f.chtKeys[i]
- }
- return f.read(8)
-}
-
-func (f *fuzzer) randomBloomTrieKey() []byte {
- i := f.randomInt(3 * len(f.bloomKeys))
- if i < len(f.bloomKeys) {
- return f.bloomKeys[i]
- }
- return f.read(10)
-}
-
-func (f *fuzzer) randomTxHash() common.Hash {
- i := f.randomInt(3 * len(f.txs))
- if i < len(f.txs) {
- return f.txs[i]
- }
- return common.BytesToHash(f.read(common.HashLength))
-}
-
-func (f *fuzzer) BlockChain() *core.BlockChain {
- return f.chain
-}
-
-func (f *fuzzer) TxPool() *txpool.TxPool {
- return f.pool
-}
-
-func (f *fuzzer) ArchiveMode() bool {
- return false
-}
-
-func (f *fuzzer) AddTxsSync() bool {
- return false
-}
-
-func (f *fuzzer) GetHelperTrie(typ uint, index uint64) *trie.Trie {
- if typ == 0 {
- return f.chtTrie
- } else if typ == 1 {
- return f.bloomTrie
- }
- return nil
-}
-
-type dummyMsg struct {
- data []byte
-}
-
-func (d dummyMsg) Decode(val interface{}) error {
- return rlp.DecodeBytes(d.data, val)
-}
-
-func (f *fuzzer) doFuzz(msgCode uint64, packet interface{}) {
- enc, err := rlp.EncodeToBytes(packet)
- if err != nil {
- panic(err)
- }
- version := f.randomInt(3) + 2 // [LES2, LES3, LES4]
- peer, closeFn := l.NewFuzzerPeer(version)
- defer closeFn()
- fn, _, _, err := l.Les3[msgCode].Handle(dummyMsg{enc})
- if err != nil {
- panic(err)
- }
- fn(f, peer, func() bool { return true })
-}
-
-func fuzz(input []byte) int {
- // We expect some large inputs
- if len(input) < 100 {
- return -1
- }
- f := newFuzzer(input)
- if f.exhausted {
- return -1
- }
- for !f.exhausted {
- switch f.randomInt(8) {
- case 0:
- req := &l.GetBlockHeadersPacket{
- Query: l.GetBlockHeadersData{
- Amount: f.randomX(l.MaxHeaderFetch + 1),
- Skip: f.randomX(10),
- Reverse: f.randomBool(),
- },
- }
- if f.randomBool() {
- req.Query.Origin.Hash = f.randomBlockHash()
- } else {
- req.Query.Origin.Number = uint64(f.randomInt(f.chainLen * 2))
- }
- f.doFuzz(l.GetBlockHeadersMsg, req)
-
- case 1:
- req := &l.GetBlockBodiesPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxBodyFetch+1))}
- for i := range req.Hashes {
- req.Hashes[i] = f.randomBlockHash()
- }
- f.doFuzz(l.GetBlockBodiesMsg, req)
-
- case 2:
- req := &l.GetCodePacket{Reqs: make([]l.CodeReq, f.randomInt(l.MaxCodeFetch+1))}
- for i := range req.Reqs {
- req.Reqs[i] = l.CodeReq{
- BHash: f.randomBlockHash(),
- AccountAddress: f.randomAddress(),
- }
- }
- f.doFuzz(l.GetCodeMsg, req)
-
- case 3:
- req := &l.GetReceiptsPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxReceiptFetch+1))}
- for i := range req.Hashes {
- req.Hashes[i] = f.randomBlockHash()
- }
- f.doFuzz(l.GetReceiptsMsg, req)
-
- case 4:
- req := &l.GetProofsPacket{Reqs: make([]l.ProofReq, f.randomInt(l.MaxProofsFetch+1))}
- for i := range req.Reqs {
- if f.randomBool() {
- req.Reqs[i] = l.ProofReq{
- BHash: f.randomBlockHash(),
- AccountAddress: f.randomAddress(),
- Key: f.randomAddress(),
- FromLevel: uint(f.randomX(3)),
- }
- } else {
- req.Reqs[i] = l.ProofReq{
- BHash: f.randomBlockHash(),
- Key: f.randomAddress(),
- FromLevel: uint(f.randomX(3)),
- }
- }
- }
- f.doFuzz(l.GetProofsV2Msg, req)
-
- case 5:
- req := &l.GetHelperTrieProofsPacket{Reqs: make([]l.HelperTrieReq, f.randomInt(l.MaxHelperTrieProofsFetch+1))}
- for i := range req.Reqs {
- switch f.randomInt(3) {
- case 0:
- // Canonical hash trie
- req.Reqs[i] = l.HelperTrieReq{
- Type: 0,
- TrieIdx: f.randomX(3),
- Key: f.randomCHTTrieKey(),
- FromLevel: uint(f.randomX(3)),
- AuxReq: uint(2),
- }
- case 1:
- // Bloom trie
- req.Reqs[i] = l.HelperTrieReq{
- Type: 1,
- TrieIdx: f.randomX(3),
- Key: f.randomBloomTrieKey(),
- FromLevel: uint(f.randomX(3)),
- AuxReq: 0,
- }
- default:
- // Random trie
- req.Reqs[i] = l.HelperTrieReq{
- Type: 2,
- TrieIdx: f.randomX(3),
- Key: f.randomCHTTrieKey(),
- FromLevel: uint(f.randomX(3)),
- AuxReq: 0,
- }
- }
- }
- f.doFuzz(l.GetHelperTrieProofsMsg, req)
-
- case 6:
- req := &l.SendTxPacket{Txs: make([]*types.Transaction, f.randomInt(l.MaxTxSend+1))}
- signer := types.HomesteadSigner{}
- for i := range req.Txs {
- var nonce uint64
- if f.randomBool() {
- nonce = uint64(f.randomByte())
- } else {
- nonce = f.nonce
- f.nonce += 1
- }
- req.Txs[i], _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(10000), params.TxGas, big.NewInt(1000000000*int64(f.randomByte())), nil), signer, bankKey)
- }
- f.doFuzz(l.SendTxV2Msg, req)
-
- case 7:
- req := &l.GetTxStatusPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxTxStatus+1))}
- for i := range req.Hashes {
- req.Hashes[i] = f.randomTxHash()
- }
- f.doFuzz(l.GetTxStatusMsg, req)
- }
- }
- return 0
-}
diff --git a/tests/fuzzers/les/les_test.go b/tests/fuzzers/les/les_test.go
deleted file mode 100644
index 53af45ceb48d..000000000000
--- a/tests/fuzzers/les/les_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2023 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 .
-
-package les
-
-import "testing"
-
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
- })
-}
diff --git a/tests/fuzzers/vflux/clientpool-fuzzer.go b/tests/fuzzers/vflux/clientpool-fuzzer.go
deleted file mode 100644
index de694a7b3f2a..000000000000
--- a/tests/fuzzers/vflux/clientpool-fuzzer.go
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2021 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 .
-
-package vflux
-
-import (
- "bytes"
- "encoding/binary"
- "io"
- "math"
- "math/big"
- "time"
-
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/ethdb/memorydb"
- "github.com/ethereum/go-ethereum/les/vflux"
- vfs "github.com/ethereum/go-ethereum/les/vflux/server"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
- "github.com/ethereum/go-ethereum/p2p/enr"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var (
- debugMode = false
- doLog = func(msg string, ctx ...interface{}) {
- if !debugMode {
- return
- }
- log.Info(msg, ctx...)
- }
-)
-
-type fuzzer struct {
- peers [256]*clientPeer
- disconnectList []*clientPeer
- input io.Reader
- exhausted bool
- activeCount, activeCap uint64
- maxCount, maxCap uint64
-}
-
-type clientPeer struct {
- fuzzer *fuzzer
- node *enode.Node
- freeID string
- timeout time.Duration
-
- balance vfs.ConnectedBalance
- capacity uint64
-}
-
-func (p *clientPeer) Node() *enode.Node {
- return p.node
-}
-
-func (p *clientPeer) FreeClientId() string {
- return p.freeID
-}
-
-func (p *clientPeer) InactiveAllowance() time.Duration {
- return p.timeout
-}
-
-func (p *clientPeer) UpdateCapacity(newCap uint64, requested bool) {
- origin, originTotal := p.capacity, p.fuzzer.activeCap
- p.fuzzer.activeCap -= p.capacity
- if p.capacity != 0 {
- p.fuzzer.activeCount--
- }
- p.capacity = newCap
- p.fuzzer.activeCap += p.capacity
- if p.capacity != 0 {
- p.fuzzer.activeCount++
- }
- doLog("Update capacity", "peer", p.node.ID(), "origin", origin, "cap", newCap, "origintotal", originTotal, "total", p.fuzzer.activeCap, "requested", requested)
-}
-
-func (p *clientPeer) Disconnect() {
- origin, originTotal := p.capacity, p.fuzzer.activeCap
- p.fuzzer.disconnectList = append(p.fuzzer.disconnectList, p)
- p.fuzzer.activeCap -= p.capacity
- if p.capacity != 0 {
- p.fuzzer.activeCount--
- }
- p.capacity = 0
- p.balance = nil
- doLog("Disconnect", "peer", p.node.ID(), "origin", origin, "origintotal", originTotal, "total", p.fuzzer.activeCap)
-}
-
-func newFuzzer(input []byte) *fuzzer {
- f := &fuzzer{
- input: bytes.NewReader(input),
- }
- for i := range f.peers {
- f.peers[i] = &clientPeer{
- fuzzer: f,
- node: enode.SignNull(new(enr.Record), enode.ID{byte(i)}),
- freeID: string([]byte{byte(i)}),
- timeout: f.randomDelay(),
- }
- }
- return f
-}
-
-func (f *fuzzer) read(size int) []byte {
- out := make([]byte, size)
- if _, err := f.input.Read(out); err != nil {
- f.exhausted = true
- }
- return out
-}
-
-func (f *fuzzer) randomByte() byte {
- d := f.read(1)
- return d[0]
-}
-
-func (f *fuzzer) randomBool() bool {
- d := f.read(1)
- return d[0]&1 == 1
-}
-
-func (f *fuzzer) randomInt(max int) int {
- if max == 0 {
- return 0
- }
- if max <= 256 {
- return int(f.randomByte()) % max
- }
- var a uint16
- if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
- f.exhausted = true
- }
- return int(a % uint16(max))
-}
-
-func (f *fuzzer) randomTokenAmount(signed bool) int64 {
- x := uint64(f.randomInt(65000))
- x = x * x * x * x
-
- if signed && (x&1) == 1 {
- if x <= math.MaxInt64 {
- return -int64(x)
- }
- return math.MinInt64
- }
- if x <= math.MaxInt64 {
- return int64(x)
- }
- return math.MaxInt64
-}
-
-func (f *fuzzer) randomDelay() time.Duration {
- delay := f.randomByte()
- if delay < 128 {
- return time.Duration(delay) * time.Second
- }
- return 0
-}
-
-func (f *fuzzer) randomFactors() vfs.PriceFactors {
- return vfs.PriceFactors{
- TimeFactor: float64(f.randomByte()) / 25500,
- CapacityFactor: float64(f.randomByte()) / 255,
- RequestFactor: float64(f.randomByte()) / 255,
- }
-}
-
-func (f *fuzzer) connectedBalanceOp(balance vfs.ConnectedBalance, id enode.ID) {
- switch f.randomInt(3) {
- case 0:
- cost := uint64(f.randomTokenAmount(false))
- balance.RequestServed(cost)
- doLog("Serve request cost", "id", id, "amount", cost)
- case 1:
- posFactor, negFactor := f.randomFactors(), f.randomFactors()
- balance.SetPriceFactors(posFactor, negFactor)
- doLog("Set price factor", "pos", posFactor, "neg", negFactor)
- case 2:
- balance.GetBalance()
- balance.GetRawBalance()
- balance.GetPriceFactors()
- }
-}
-
-func (f *fuzzer) atomicBalanceOp(balance vfs.AtomicBalanceOperator, id enode.ID) {
- switch f.randomInt(3) {
- case 0:
- amount := f.randomTokenAmount(true)
- balance.AddBalance(amount)
- doLog("Add balance", "id", id, "amount", amount)
- case 1:
- pos, neg := uint64(f.randomTokenAmount(false)), uint64(f.randomTokenAmount(false))
- balance.SetBalance(pos, neg)
- doLog("Set balance", "id", id, "pos", pos, "neg", neg)
- case 2:
- balance.GetBalance()
- balance.GetRawBalance()
- balance.GetPriceFactors()
- }
-}
-
-func fuzzClientPool(input []byte) int {
- if len(input) > 10000 {
- return -1
- }
- f := newFuzzer(input)
- if f.exhausted {
- return 0
- }
- clock := &mclock.Simulated{}
- db := memorydb.New()
- pool := vfs.NewClientPool(db, 10, f.randomDelay(), clock, func() bool { return true })
- pool.Start()
- defer pool.Stop()
-
- count := 0
- for !f.exhausted && count < 1000 {
- count++
- switch f.randomInt(11) {
- case 0:
- i := int(f.randomByte())
- f.peers[i].balance = pool.Register(f.peers[i])
- doLog("Register peer", "id", f.peers[i].node.ID())
- case 1:
- i := int(f.randomByte())
- f.peers[i].Disconnect()
- doLog("Disconnect peer", "id", f.peers[i].node.ID())
- case 2:
- f.maxCount = uint64(f.randomByte())
- f.maxCap = uint64(f.randomByte())
- f.maxCap *= f.maxCap
-
- count, cap := pool.Limits()
- pool.SetLimits(f.maxCount, f.maxCap)
- doLog("Set limits", "maxcount", f.maxCount, "maxcap", f.maxCap, "origincount", count, "oricap", cap)
- case 3:
- bias := f.randomDelay()
- pool.SetConnectedBias(f.randomDelay())
- doLog("Set connection bias", "bias", bias)
- case 4:
- pos, neg := f.randomFactors(), f.randomFactors()
- pool.SetDefaultFactors(pos, neg)
- doLog("Set default factors", "pos", pos, "neg", neg)
- case 5:
- pos, neg := uint64(f.randomInt(50000)), uint64(f.randomInt(50000))
- pool.SetExpirationTCs(pos, neg)
- doLog("Set expiration constants", "pos", pos, "neg", neg)
- case 6:
- var (
- index = f.randomByte()
- reqCap = uint64(f.randomByte())
- bias = f.randomDelay()
- requested = f.randomBool()
- )
- pool.SetCapacity(f.peers[index].node, reqCap, bias, requested)
- doLog("Set capacity", "id", f.peers[index].node.ID(), "reqcap", reqCap, "bias", bias, "requested", requested)
- case 7:
- index := f.randomByte()
- if balance := f.peers[index].balance; balance != nil {
- f.connectedBalanceOp(balance, f.peers[index].node.ID())
- }
- case 8:
- index := f.randomByte()
- pool.BalanceOperation(f.peers[index].node.ID(), f.peers[index].freeID, func(balance vfs.AtomicBalanceOperator) {
- count := f.randomInt(4)
- for i := 0; i < count; i++ {
- f.atomicBalanceOp(balance, f.peers[index].node.ID())
- }
- })
- case 9:
- pool.TotalTokenAmount()
- pool.GetExpirationTCs()
- pool.Active()
- pool.Limits()
- pool.GetPosBalanceIDs(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].node.ID(), f.randomInt(100))
- case 10:
- req := vflux.CapacityQueryReq{
- Bias: uint64(f.randomByte()),
- AddTokens: make([]vflux.IntOrInf, f.randomInt(vflux.CapacityQueryMaxLen+1)),
- }
- for i := range req.AddTokens {
- v := vflux.IntOrInf{Type: uint8(f.randomInt(4))}
- if v.Type < 2 {
- v.Value = *big.NewInt(f.randomTokenAmount(false))
- }
- req.AddTokens[i] = v
- }
- reqEnc, err := rlp.EncodeToBytes(&req)
- if err != nil {
- panic(err)
- }
- p := int(f.randomByte())
- if p < len(reqEnc) {
- reqEnc[p] = f.randomByte()
- }
- pool.Handle(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].freeID, vflux.CapacityQueryName, reqEnc)
- }
-
- for _, peer := range f.disconnectList {
- pool.Unregister(peer)
- doLog("Unregister peer", "id", peer.node.ID())
- }
- f.disconnectList = nil
- if d := f.randomDelay(); d > 0 {
- clock.Run(d)
- }
- doLog("Clientpool stats in fuzzer", "count", f.activeCap, "maxcount", f.maxCount, "cap", f.activeCap, "maxcap", f.maxCap)
- activeCount, activeCap := pool.Active()
- doLog("Clientpool stats in pool", "count", activeCount, "cap", activeCap)
- if activeCount != f.activeCount || activeCap != f.activeCap {
- panic(nil)
- }
- if f.activeCount > f.maxCount || f.activeCap > f.maxCap {
- panic(nil)
- }
- }
- return 0
-}
diff --git a/tests/fuzzers/vflux/clientpool_test.go b/tests/fuzzers/vflux/clientpool_test.go
deleted file mode 100644
index 40c5f229056f..000000000000
--- a/tests/fuzzers/vflux/clientpool_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2023 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 .
-
-package vflux
-
-import "testing"
-
-func FuzzClientPool(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzzClientPool(data)
- })
-}
From 2a2013014c46844728421a1acc5ad40ca823414b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?=
Date: Fri, 24 Nov 2023 13:26:42 +0200
Subject: [PATCH 035/103] eth, internal/ethapi: drop some weird indirection
(#28597)
---
eth/api_backend.go | 4 ++--
internal/ethapi/api.go | 6 +++---
internal/ethapi/api_test.go | 5 ++---
internal/ethapi/backend.go | 2 +-
internal/ethapi/transaction_args_test.go | 4 ++--
5 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 601e55515857..84eb200095cd 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -249,7 +249,7 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
return nil
}
-func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error) {
+func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
if vmConfig == nil {
vmConfig = b.eth.blockchain.GetVMConfig()
}
@@ -260,7 +260,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
} else {
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
}
- return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error
+ return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig)
}
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 38a7924124a6..89c132ddeed8 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -1083,7 +1083,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
if blockOverrides != nil {
blockOverrides.Apply(&blockCtx)
}
- evm, vmError := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
+ evm := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
@@ -1095,7 +1095,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
// Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, err := core.ApplyMessage(evm, msg, gp)
- if err := vmError(); err != nil {
+ if err := state.Error(); err != nil {
return nil, err
}
@@ -1640,7 +1640,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
// Apply the transaction with the access list tracer
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
config := vm.Config{Tracer: tracer, NoBaseFee: true}
- vmenv, _ := b.GetEVM(ctx, msg, statedb, header, &config, nil)
+ vmenv := b.GetEVM(ctx, msg, statedb, header, &config, nil)
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go
index a67bd1203b9a..4a7694737ec0 100644
--- a/internal/ethapi/api_test.go
+++ b/internal/ethapi/api_test.go
@@ -536,8 +536,7 @@ func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
}
return big.NewInt(1)
}
-func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) (*vm.EVM, func() error) {
- vmError := func() error { return nil }
+func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM {
if vmConfig == nil {
vmConfig = b.chain.GetVMConfig()
}
@@ -546,7 +545,7 @@ func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state
if blockContext != nil {
context = *blockContext
}
- return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig), vmError
+ return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig)
}
func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
panic("implement me")
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 458fb811edae..50f338f5cab3 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -68,7 +68,7 @@ type Backend interface {
PendingBlockAndReceipts() (*types.Block, types.Receipts)
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int
- GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error)
+ GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go
index 9161d5e681f2..9dc58bdeb525 100644
--- a/internal/ethapi/transaction_args_test.go
+++ b/internal/ethapi/transaction_args_test.go
@@ -305,8 +305,8 @@ func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number
return nil, nil
}
func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil }
-func (b *backendMock) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error) {
- return nil, nil
+func (b *backendMock) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
+ return nil
}
func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }
func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
From 333dd956bfdf1d5086d38cceedbba25a366fb6ac Mon Sep 17 00:00:00 2001
From: Ng Wei Han <47109095+weiihann@users.noreply.github.com>
Date: Sat, 25 Nov 2023 21:56:22 +0800
Subject: [PATCH 036/103] trie: fix random test generator early terminate
(#28590)
This change fixes a minor bug in the `randTest.Generate` function, which caused the `quick.Check` to be a no-op.
---
trie/trie_test.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 6af0f67b9f53..c5bd3faf53a0 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -372,6 +372,9 @@ func TestRandomCases(t *testing.T) {
// Instances of this test are created by Generate.
type randTest []randTestStep
+// compile-time interface check
+var _ quick.Generator = (randTest)(nil)
+
type randTestStep struct {
op int
key []byte // for opUpdate, opDelete, opGet
@@ -394,7 +397,7 @@ const (
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
var finishedFn = func() bool {
size--
- return size > 0
+ return size == 0
}
return reflect.ValueOf(generateSteps(finishedFn, r))
}
From 1e28e0bb03c6911a691d7125cb8c5e41faac2eab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?=
Date: Mon, 27 Nov 2023 16:20:09 +0200
Subject: [PATCH 037/103] eth/gasestimator, internal/ethapi: move gas estimator
out of rpc (#28600)
---
eth/gasestimator/gasestimator.go | 194 +++++++++++++++++++++++++++++++
internal/ethapi/api.go | 149 ++++--------------------
internal/ethapi/api_test.go | 8 +-
3 files changed, 222 insertions(+), 129 deletions(-)
create mode 100644 eth/gasestimator/gasestimator.go
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
new file mode 100644
index 000000000000..3e74b5b08b5c
--- /dev/null
+++ b/eth/gasestimator/gasestimator.go
@@ -0,0 +1,194 @@
+// Copyright 2023 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 .
+
+package gasestimator
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// Options are the contextual parameters to execute the requested call.
+//
+// Whilst it would be possible to pass a blockchain object that aggregates all
+// these together, it would be excessively hard to test. Splitting the parts out
+// allows testing without needing a proper live chain.
+type Options struct {
+ Config *params.ChainConfig // Chain configuration for hard fork selection
+ Chain core.ChainContext // Chain context to access past block hashes
+ Header *types.Header // Header defining the block context to execute in
+ State *state.StateDB // Pre-state on top of which to estimate the gas
+}
+
+// Estimate returns the lowest possible gas limit that allows the transaction to
+// run successfully with the provided context optons. It returns an error if the
+// transaction would always revert, or if there are unexpected failures.
+func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uint64) (uint64, []byte, error) {
+ // Binary search the gas limit, as it may need to be higher than the amount used
+ var (
+ lo uint64 // lowest-known gas limit where tx execution fails
+ hi uint64 // lowest-known gas limit where tx execution succeeds
+ )
+ // Determine the highest gas limit can be used during the estimation.
+ hi = opts.Header.GasLimit
+ if call.GasLimit >= params.TxGas {
+ hi = call.GasLimit
+ }
+ // Normalize the max fee per gas the call is willing to spend.
+ var feeCap *big.Int
+ if call.GasFeeCap != nil {
+ feeCap = call.GasFeeCap
+ } else if call.GasPrice != nil {
+ feeCap = call.GasPrice
+ } else {
+ feeCap = common.Big0
+ }
+ // Recap the highest gas limit with account's available balance.
+ if feeCap.BitLen() != 0 {
+ balance := opts.State.GetBalance(call.From)
+
+ available := new(big.Int).Set(balance)
+ if call.Value != nil {
+ if call.Value.Cmp(available) >= 0 {
+ return 0, nil, core.ErrInsufficientFundsForTransfer
+ }
+ available.Sub(available, call.Value)
+ }
+ allowance := new(big.Int).Div(available, feeCap)
+
+ // If the allowance is larger than maximum uint64, skip checking
+ if allowance.IsUint64() && hi > allowance.Uint64() {
+ transfer := call.Value
+ if transfer == nil {
+ transfer = new(big.Int)
+ }
+ log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
+ "sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance)
+ hi = allowance.Uint64()
+ }
+ }
+ // Recap the highest gas allowance with specified gascap.
+ if gasCap != 0 && hi > gasCap {
+ log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
+ hi = gasCap
+ }
+ // We first execute the transaction at the highest allowable gas limit, since if this fails we
+ // can return error immediately.
+ failed, result, err := execute(ctx, call, opts, hi)
+ if err != nil {
+ return 0, nil, err
+ }
+ if failed {
+ if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) {
+ return 0, result.Revert(), result.Err
+ }
+ return 0, nil, fmt.Errorf("gas required exceeds allowance (%d)", hi)
+ }
+ // For almost any transaction, the gas consumed by the unconstrained execution
+ // above lower-bounds the gas limit required for it to succeed. One exception
+ // is those that explicitly check gas remaining in order to execute within a
+ // given limit, but we probably don't want to return the lowest possible gas
+ // limit for these cases anyway.
+ lo = result.UsedGas - 1
+
+ // Binary search for the smallest gas limit that allows the tx to execute successfully.
+ for lo+1 < hi {
+ mid := (hi + lo) / 2
+ if mid > lo*2 {
+ // Most txs don't need much higher gas limit than their gas used, and most txs don't
+ // require near the full block limit of gas, so the selection of where to bisect the
+ // range here is skewed to favor the low side.
+ mid = lo * 2
+ }
+ failed, _, err = execute(ctx, call, opts, mid)
+ if err != nil {
+ // This should not happen under normal conditions since if we make it this far the
+ // transaction had run without error at least once before.
+ log.Error("Execution error in estimate gas", "err", err)
+ return 0, nil, err
+ }
+ if failed {
+ lo = mid
+ } else {
+ hi = mid
+ }
+ }
+ return hi, nil, nil
+}
+
+// execute is a helper that executes the transaction under a given gas limit and
+// returns true if the transaction fails for a reason that might be related to
+// not enough gas. A non-nil error means execution failed due to reasons unrelated
+// to the gas limit.
+func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit uint64) (bool, *core.ExecutionResult, error) {
+ // Configure the call for this specific execution (and revert the change after)
+ defer func(gas uint64) { call.GasLimit = gas }(call.GasLimit)
+ call.GasLimit = gasLimit
+
+ // Execute the call and separate execution faults caused by a lack of gas or
+ // other non-fixable conditions
+ result, err := run(ctx, call, opts)
+ if err != nil {
+ if errors.Is(err, core.ErrIntrinsicGas) {
+ return true, nil, nil // Special case, raise gas limit
+ }
+ return true, nil, err // Bail out
+ }
+ return result.Failed(), result, nil
+}
+
+// run assembles the EVM as defined by the consensus rules and runs the requested
+// call invocation.
+func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) {
+ // Assemble the call and the call context
+ var (
+ msgContext = core.NewEVMTxContext(call)
+ evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)
+
+ dirtyState = opts.State.Copy()
+ evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
+ )
+ // Monitor the outer context and interrupt the EVM upon cancellation. To avoid
+ // a dangling goroutine until the outer estimation finishes, create an internal
+ // context for the lifetime of this method call.
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ go func() {
+ <-ctx.Done()
+ evm.Cancel()
+ }()
+ // Execute the call, returning a wrapped error or the result
+ result, err := core.ApplyMessage(evm, call, new(core.GasPool).AddGas(math.MaxUint64))
+ if vmerr := dirtyState.Error(); vmerr != nil {
+ return nil, vmerr
+ }
+ if err != nil {
+ return result, fmt.Errorf("failed with %d gas: %w", call.GasLimit, err)
+ }
+ return result, nil
+}
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 89c132ddeed8..f322132769af 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -40,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
@@ -1120,15 +1121,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
return doCall(ctx, b, args, state, header, overrides, blockOverrides, timeout, globalGasCap)
}
-func newRevertError(result *core.ExecutionResult) *revertError {
- reason, errUnpack := abi.UnpackRevert(result.Revert())
- err := errors.New("execution reverted")
+func newRevertError(revert []byte) *revertError {
+ err := vm.ErrExecutionReverted
+
+ reason, errUnpack := abi.UnpackRevert(revert)
if errUnpack == nil {
- err = fmt.Errorf("execution reverted: %v", reason)
+ err = fmt.Errorf("%w: %v", vm.ErrExecutionReverted, reason)
}
return &revertError{
error: err,
- reason: hexutil.Encode(result.Revert()),
+ reason: hexutil.Encode(revert),
}
}
@@ -1167,147 +1169,44 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
}
// If the result contains a revert reason, try to unpack and return it.
if len(result.Revert()) > 0 {
- return nil, newRevertError(result)
+ return nil, newRevertError(result.Revert())
}
return result.Return(), result.Err
}
-// executeEstimate is a helper that executes the transaction under a given gas limit and returns
-// true if the transaction fails for a reason that might be related to not enough gas. A non-nil
-// error means execution failed due to reasons unrelated to the gas limit.
-func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, gasCap uint64, gasLimit uint64) (bool, *core.ExecutionResult, error) {
- args.Gas = (*hexutil.Uint64)(&gasLimit)
- result, err := doCall(ctx, b, args, state, header, nil, nil, 0, gasCap)
- if err != nil {
- if errors.Is(err, core.ErrIntrinsicGas) {
- return true, nil, nil // Special case, raise gas limit
- }
- return true, nil, err // Bail out
- }
- return result.Failed(), result, nil
-}
-
// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run
// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
// non-zero) and `gasCap` (if non-zero).
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
- // Binary search the gas limit, as it may need to be higher than the amount used
- var (
- lo uint64 // lowest-known gas limit where tx execution fails
- hi uint64 // lowest-known gas limit where tx execution succeeds
- )
- // Use zero address if sender unspecified.
- if args.From == nil {
- args.From = new(common.Address)
- }
- // Determine the highest gas limit can be used during the estimation.
- if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
- hi = uint64(*args.Gas)
- } else {
- // Retrieve the block to act as the gas ceiling
- block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
- if err != nil {
- return 0, err
- }
- if block == nil {
- return 0, errors.New("block not found")
- }
- hi = block.GasLimit()
- }
- // Normalize the max fee per gas the call is willing to spend.
- var feeCap *big.Int
- if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
- return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
- } else if args.GasPrice != nil {
- feeCap = args.GasPrice.ToInt()
- } else if args.MaxFeePerGas != nil {
- feeCap = args.MaxFeePerGas.ToInt()
- } else {
- feeCap = common.Big0
- }
-
+ // Retrieve the base state and mutate it with any overrides
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return 0, err
}
- if err := overrides.Apply(state); err != nil {
+ if err = overrides.Apply(state); err != nil {
return 0, err
}
-
- // Recap the highest gas limit with account's available balance.
- if feeCap.BitLen() != 0 {
- balance := state.GetBalance(*args.From) // from can't be nil
- available := new(big.Int).Set(balance)
- if args.Value != nil {
- if args.Value.ToInt().Cmp(available) >= 0 {
- return 0, core.ErrInsufficientFundsForTransfer
- }
- available.Sub(available, args.Value.ToInt())
- }
- allowance := new(big.Int).Div(available, feeCap)
-
- // If the allowance is larger than maximum uint64, skip checking
- if allowance.IsUint64() && hi > allowance.Uint64() {
- transfer := args.Value
- if transfer == nil {
- transfer = new(hexutil.Big)
- }
- log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
- "sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance)
- hi = allowance.Uint64()
- }
+ // Construct the gas estimator option from the user input
+ opts := &gasestimator.Options{
+ Config: b.ChainConfig(),
+ Chain: NewChainContext(ctx, b),
+ Header: header,
+ State: state,
}
- // Recap the highest gas allowance with specified gascap.
- if gasCap != 0 && hi > gasCap {
- log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
- hi = gasCap
- }
-
- // We first execute the transaction at the highest allowable gas limit, since if this fails we
- // can return error immediately.
- failed, result, err := executeEstimate(ctx, b, args, state.Copy(), header, gasCap, hi)
+ // Run the gas estimation andwrap any revertals into a custom return
+ call, err := args.ToMessage(gasCap, header.BaseFee)
if err != nil {
return 0, err
}
- if failed {
- if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) {
- if len(result.Revert()) > 0 {
- return 0, newRevertError(result)
- }
- return 0, result.Err
- }
- return 0, fmt.Errorf("gas required exceeds allowance (%d)", hi)
- }
- // For almost any transaction, the gas consumed by the unconstrained execution above
- // lower-bounds the gas limit required for it to succeed. One exception is those txs that
- // explicitly check gas remaining in order to successfully execute within a given limit, but we
- // probably don't want to return a lowest possible gas limit for these cases anyway.
- lo = result.UsedGas - 1
-
- // Binary search for the smallest gas limit that allows the tx to execute successfully.
- for lo+1 < hi {
- mid := (hi + lo) / 2
- if mid > lo*2 {
- // Most txs don't need much higher gas limit than their gas used, and most txs don't
- // require near the full block limit of gas, so the selection of where to bisect the
- // range here is skewed to favor the low side.
- mid = lo * 2
- }
- failed, _, err = executeEstimate(ctx, b, args, state.Copy(), header, gasCap, mid)
- if err != nil {
- // This should not happen under normal conditions since if we make it this far the
- // transaction had run without error at least once before.
- log.Error("execution error in estimate gas", "err", err)
- return 0, err
- }
- if failed {
- lo = mid
- } else {
- hi = mid
+ estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
+ if err != nil {
+ if len(revert) > 0 {
+ return 0, newRevertError(revert)
}
+ return 0, err
}
- return hexutil.Uint64(hi), nil
+ return hexutil.Uint64(estimate), nil
}
// EstimateGas returns the lowest possible gas limit that allows the transaction to run
diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go
index 4a7694737ec0..9b08fd8d42c8 100644
--- a/internal/ethapi/api_test.go
+++ b/internal/ethapi/api_test.go
@@ -910,18 +910,18 @@ func TestCall(t *testing.T) {
}
}
-type Account struct {
+type account struct {
key *ecdsa.PrivateKey
addr common.Address
}
-func newAccounts(n int) (accounts []Account) {
+func newAccounts(n int) (accounts []account) {
for i := 0; i < n; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
- accounts = append(accounts, Account{key: key, addr: addr})
+ accounts = append(accounts, account{key: key, addr: addr})
}
- slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
+ slices.SortFunc(accounts, func(a, b account) int { return a.addr.Cmp(b.addr) })
return accounts
}
From 5b57727d6de25493245ead838412188ffb6f1324 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Mon, 27 Nov 2023 15:39:28 +0100
Subject: [PATCH 038/103] go.mod: update uint256 to v1.2.4 (#28612)
---
go.mod | 2 +-
go.sum | 10 ++--------
2 files changed, 3 insertions(+), 9 deletions(-)
diff --git a/go.mod b/go.mod
index 32cfe26b1471..042d7155bed0 100644
--- a/go.mod
+++ b/go.mod
@@ -40,7 +40,7 @@ require (
github.com/hashicorp/go-bexpr v0.1.10
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7
github.com/holiman/bloomfilter/v2 v2.0.3
- github.com/holiman/uint256 v1.2.3
+ github.com/holiman/uint256 v1.2.4
github.com/huin/goupnp v1.3.0
github.com/influxdata/influxdb-client-go/v2 v2.4.0
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
diff --git a/go.sum b/go.sum
index e62d7d36ab29..d5e7901e5abe 100644
--- a/go.sum
+++ b/go.sum
@@ -97,8 +97,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
-github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
@@ -147,8 +145,6 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58 h1:PwUlswsGOrLB677lW4XrlWLeszY3BaDGbvZ6dYk28tQ=
-github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58/go.mod h1:J+gsi6D4peY0kyhaklyXFRVHOQWI2I5uU0c2+/90HYc=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
@@ -205,8 +201,6 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
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-verkle v0.1.1-0.20231004173727-0a4e93ed640b h1:LHeiiSTL2FEGCP1ov6FqkikiViqygeVo1ZwJ1x3nYSE=
-github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b/go.mod h1:7JamHhSTnnHDhcI3G8r4sWaD9XlleriqVlC3FeAQJKM=
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
@@ -347,8 +341,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
-github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
-github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
+github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
+github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
From 71817f318edc8c37161f38aa7a0407d4efc2429f Mon Sep 17 00:00:00 2001
From: rjl493456442
Date: Tue, 28 Nov 2023 15:38:30 +0800
Subject: [PATCH 039/103] eth/catalyst, eth/downloader: expose more sync
information (#28584)
This change exposes more information from sync module internally
---
eth/catalyst/api.go | 7 ++---
eth/downloader/skeleton.go | 46 ++++++++++++++++-----------------
eth/downloader/skeleton_test.go | 12 ++++-----
trie/verkle_test.go | 8 +++---
4 files changed, 37 insertions(+), 36 deletions(-)
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index d1e19914140d..37b0248f28cd 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -611,7 +611,8 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadS
// Although we don't want to trigger a sync, if there is one already in
// progress, try to extend if with the current payload request to relieve
// some strain from the forkchoice update.
- if err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header()); err == nil {
+ err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header())
+ if err == nil {
log.Debug("Payload accepted for sync extension", "number", block.NumberU64(), "hash", block.Hash())
return engine.PayloadStatusV1{Status: engine.SYNCING}, nil
}
@@ -623,12 +624,12 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadS
// In full sync mode, failure to import a well-formed block can only mean
// that the parent state is missing and the syncer rejected extending the
// current cycle with the new payload.
- log.Warn("Ignoring payload with missing parent", "number", block.NumberU64(), "hash", block.Hash(), "parent", block.ParentHash())
+ log.Warn("Ignoring payload with missing parent", "number", block.NumberU64(), "hash", block.Hash(), "parent", block.ParentHash(), "reason", err)
} else {
// In non-full sync mode (i.e. snap sync) all payloads are rejected until
// snap sync terminates as snap sync relies on direct database injections
// and cannot afford concurrent out-if-band modifications via imports.
- log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash())
+ log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash(), "reason", err)
}
return engine.PayloadStatusV1{Status: engine.SYNCING}, nil
}
diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go
index 4f1f4620481d..f40ca24d9958 100644
--- a/eth/downloader/skeleton.go
+++ b/eth/downloader/skeleton.go
@@ -69,9 +69,17 @@ var errSyncReorged = errors.New("sync reorged")
// might still be propagating.
var errTerminated = errors.New("terminated")
-// errReorgDenied is returned if an attempt is made to extend the beacon chain
-// with a new header, but it does not link up to the existing sync.
-var errReorgDenied = errors.New("non-forced head reorg denied")
+// errChainReorged is an internal helper error to signal that the header chain
+// of the current sync cycle was (partially) reorged.
+var errChainReorged = errors.New("chain reorged")
+
+// errChainGapped is an internal helper error to signal that the header chain
+// of the current sync cycle is gaped with the one advertised by consensus client.
+var errChainGapped = errors.New("chain gapped")
+
+// errChainForked is an internal helper error to signal that the header chain
+// of the current sync cycle is forked with the one advertised by consensus client.
+var errChainForked = errors.New("chain forked")
func init() {
// Tuning parameters is nice, but the scratch space must be assignable in
@@ -271,9 +279,9 @@ func (s *skeleton) startup() {
newhead, err := s.sync(head)
switch {
case err == errSyncLinked:
- // Sync cycle linked up to the genesis block. Tear down the loop
- // and restart it so, it can properly notify the backfiller. Don't
- // account a new head.
+ // Sync cycle linked up to the genesis block, or the existent chain
+ // segment. Tear down the loop and restart it so, it can properly
+ // notify the backfiller. Don't account a new head.
head = nil
case err == errSyncMerged:
@@ -457,15 +465,16 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) {
// we don't seamlessly integrate reorgs to keep things simple. If the
// network starts doing many mini reorgs, it might be worthwhile handling
// a limited depth without an error.
- if reorged := s.processNewHead(event.header, event.final, event.force); reorged {
+ if err := s.processNewHead(event.header, event.final); err != nil {
// If a reorg is needed, and we're forcing the new head, signal
// the syncer to tear down and start over. Otherwise, drop the
// non-force reorg.
if event.force {
event.errc <- nil // forced head reorg accepted
+ log.Info("Restarting sync cycle", "reason", err)
return event.header, errSyncReorged
}
- event.errc <- errReorgDenied
+ event.errc <- err
continue
}
event.errc <- nil // head extension accepted
@@ -610,7 +619,7 @@ func (s *skeleton) saveSyncStatus(db ethdb.KeyValueWriter) {
// accepts and integrates it into the skeleton or requests a reorg. Upon reorg,
// the syncer will tear itself down and restart with a fresh head. It is simpler
// to reconstruct the sync state than to mutate it and hope for the best.
-func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force bool) bool {
+func (s *skeleton) processNewHead(head *types.Header, final *types.Header) error {
// If a new finalized block was announced, update the sync process independent
// of what happens with the sync head below
if final != nil {
@@ -631,26 +640,17 @@ func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force
// once more, ignore it instead of tearing down sync for a noop.
if lastchain.Head == lastchain.Tail {
if current := rawdb.ReadSkeletonHeader(s.db, number); current.Hash() == head.Hash() {
- return false
+ return nil
}
}
// Not a noop / double head announce, abort with a reorg
- if force {
- log.Warn("Beacon chain reorged", "tail", lastchain.Tail, "head", lastchain.Head, "newHead", number)
- }
- return true
+ return fmt.Errorf("%w, tail: %d, head: %d, newHead: %d", errChainReorged, lastchain.Tail, lastchain.Head, number)
}
if lastchain.Head+1 < number {
- if force {
- log.Warn("Beacon chain gapped", "head", lastchain.Head, "newHead", number)
- }
- return true
+ return fmt.Errorf("%w, head: %d, newHead: %d", errChainGapped, lastchain.Head, number)
}
if parent := rawdb.ReadSkeletonHeader(s.db, number-1); parent.Hash() != head.ParentHash {
- if force {
- log.Warn("Beacon chain forked", "ancestor", number-1, "hash", parent.Hash(), "want", head.ParentHash)
- }
- return true
+ return fmt.Errorf("%w, ancestor: %d, hash: %s, want: %s", errChainForked, number-1, parent.Hash(), head.ParentHash)
}
// New header seems to be in the last subchain range. Unwind any extra headers
// from the chain tip and insert the new head. We won't delete any trimmed
@@ -666,7 +666,7 @@ func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force
if err := batch.Write(); err != nil {
log.Crit("Failed to write skeleton sync status", "err", err)
}
- return false
+ return nil
}
// assignTasks attempts to match idle peers to pending header retrievals.
diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go
index c31007765a8b..aceadd00d3af 100644
--- a/eth/downloader/skeleton_test.go
+++ b/eth/downloader/skeleton_test.go
@@ -434,7 +434,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
newstate: []*subchain{
{Head: 49, Tail: 49},
},
- err: errReorgDenied,
+ err: errChainReorged,
},
// Initialize a sync and try to extend it with a number-wise sequential
// header, but a hash wise non-linking one.
@@ -444,7 +444,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
newstate: []*subchain{
{Head: 49, Tail: 49},
},
- err: errReorgDenied,
+ err: errChainForked,
},
// Initialize a sync and try to extend it with a non-linking future block.
{
@@ -453,7 +453,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
newstate: []*subchain{
{Head: 49, Tail: 49},
},
- err: errReorgDenied,
+ err: errChainGapped,
},
// Initialize a sync and try to extend it with a past canonical block.
{
@@ -462,7 +462,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
newstate: []*subchain{
{Head: 50, Tail: 50},
},
- err: errReorgDenied,
+ err: errChainReorged,
},
// Initialize a sync and try to extend it with a past sidechain block.
{
@@ -471,7 +471,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
newstate: []*subchain{
{Head: 50, Tail: 50},
},
- err: errReorgDenied,
+ err: errChainReorged,
},
}
for i, tt := range tests {
@@ -487,7 +487,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
skeleton.Sync(tt.head, nil, true)
<-wait
- if err := skeleton.Sync(tt.extend, nil, false); err != tt.err {
+ if err := skeleton.Sync(tt.extend, nil, false); !errors.Is(err, tt.err) {
t.Errorf("test %d: extension failure mismatch: have %v, want %v", i, err, tt.err)
}
skeleton.Terminate()
diff --git a/trie/verkle_test.go b/trie/verkle_test.go
index 44fb7dc29e9b..bd31ea387954 100644
--- a/trie/verkle_test.go
+++ b/trie/verkle_test.go
@@ -31,24 +31,24 @@ import (
var (
accounts = map[common.Address]*types.StateAccount{
- common.Address{1}: {
+ {1}: {
Nonce: 100,
Balance: big.NewInt(100),
CodeHash: common.Hash{0x1}.Bytes(),
},
- common.Address{2}: {
+ {2}: {
Nonce: 200,
Balance: big.NewInt(200),
CodeHash: common.Hash{0x2}.Bytes(),
},
}
storages = map[common.Address]map[common.Hash][]byte{
- common.Address{1}: {
+ {1}: {
common.Hash{10}: []byte{10},
common.Hash{11}: []byte{11},
common.MaxHash: []byte{0xff},
},
- common.Address{2}: {
+ {2}: {
common.Hash{20}: []byte{20},
common.Hash{21}: []byte{21},
common.MaxHash: []byte{0xff},
From 58297e339b26d09a0c21e550ee4b6ed6205cedcd Mon Sep 17 00:00:00 2001
From: lightclient <14004106+lightclient@users.noreply.github.com>
Date: Tue, 28 Nov 2023 01:01:23 -0700
Subject: [PATCH 040/103] light: remove package light(#28614)
This changes removes the package 'light', which is currently unused.
---
light/lightchain.go | 531 -------------------------------------
light/lightchain_test.go | 358 -------------------------
light/odr.go | 196 --------------
light/odr_test.go | 339 ------------------------
light/odr_util.go | 275 -------------------
light/postprocess.go | 538 -------------------------------------
light/trie.go | 319 ----------------------
light/trie_test.go | 95 -------
light/txpool.go | 556 ---------------------------------------
light/txpool_test.go | 147 -----------
10 files changed, 3354 deletions(-)
delete mode 100644 light/lightchain.go
delete mode 100644 light/lightchain_test.go
delete mode 100644 light/odr.go
delete mode 100644 light/odr_test.go
delete mode 100644 light/odr_util.go
delete mode 100644 light/postprocess.go
delete mode 100644 light/trie.go
delete mode 100644 light/trie_test.go
delete mode 100644 light/txpool.go
delete mode 100644 light/txpool_test.go
diff --git a/light/lightchain.go b/light/lightchain.go
deleted file mode 100644
index 617658b85b98..000000000000
--- a/light/lightchain.go
+++ /dev/null
@@ -1,531 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package light implements on-demand retrieval capable state and chain objects
-// for the Ethereum Light Client.
-package light
-
-import (
- "context"
- "errors"
- "math/big"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/lru"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-var (
- bodyCacheLimit = 256
- blockCacheLimit = 256
-)
-
-// LightChain represents a canonical chain that by default only handles block
-// headers, downloading block bodies and receipts on demand through an ODR
-// interface. It only does header validation during chain insertion.
-type LightChain struct {
- hc *core.HeaderChain
- indexerConfig *IndexerConfig
- chainDb ethdb.Database
- engine consensus.Engine
- odr OdrBackend
- chainFeed event.Feed
- chainSideFeed event.Feed
- chainHeadFeed event.Feed
- scope event.SubscriptionScope
- genesisBlock *types.Block
- forker *core.ForkChoice
-
- bodyCache *lru.Cache[common.Hash, *types.Body]
- bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
- blockCache *lru.Cache[common.Hash, *types.Block]
-
- chainmu sync.RWMutex // protects header inserts
- quit chan struct{}
- wg sync.WaitGroup
-
- // Atomic boolean switches:
- stopped atomic.Bool // whether LightChain is stopped or running
- procInterrupt atomic.Bool // interrupts chain insert
-}
-
-// NewLightChain returns a fully initialised light chain using information
-// available in the database. It initialises the default Ethereum header
-// validator.
-func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) {
- bc := &LightChain{
- chainDb: odr.Database(),
- indexerConfig: odr.IndexerConfig(),
- odr: odr,
- quit: make(chan struct{}),
- bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
- bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
- blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
- engine: engine,
- }
- bc.forker = core.NewForkChoice(bc, nil)
- var err error
- bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
- if err != nil {
- return nil, err
- }
- bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0)
- if bc.genesisBlock == nil {
- return nil, core.ErrNoGenesis
- }
- if err := bc.loadLastState(); err != nil {
- return nil, err
- }
- // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
- for hash := range core.BadHashes {
- if header := bc.GetHeaderByHash(hash); header != nil {
- log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
- bc.SetHead(header.Number.Uint64() - 1)
- log.Info("Chain rewind was successful, resuming normal operation")
- }
- }
- return bc, nil
-}
-
-func (lc *LightChain) getProcInterrupt() bool {
- return lc.procInterrupt.Load()
-}
-
-// Odr returns the ODR backend of the chain
-func (lc *LightChain) Odr() OdrBackend {
- return lc.odr
-}
-
-// HeaderChain returns the underlying header chain.
-func (lc *LightChain) HeaderChain() *core.HeaderChain {
- return lc.hc
-}
-
-// loadLastState loads the last known chain state from the database. This method
-// assumes that the chain manager mutex is held.
-func (lc *LightChain) loadLastState() error {
- if head := rawdb.ReadHeadHeaderHash(lc.chainDb); head == (common.Hash{}) {
- // Corrupt or empty database, init from scratch
- lc.Reset()
- } else {
- header := lc.GetHeaderByHash(head)
- if header == nil {
- // Corrupt or empty database, init from scratch
- lc.Reset()
- } else {
- lc.hc.SetCurrentHeader(header)
- }
- }
- // Issue a status log and return
- header := lc.hc.CurrentHeader()
- headerTd := lc.GetTd(header.Hash(), header.Number.Uint64())
- log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(header.Time), 0)))
- return nil
-}
-
-// SetHead rewinds the local chain to a new head. Everything above the new
-// head will be deleted and the new one set.
-func (lc *LightChain) SetHead(head uint64) error {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.hc.SetHead(head, nil, nil)
- return lc.loadLastState()
-}
-
-// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
-// the given timestamp. Everything above the new head will be deleted and the
-// new one set.
-func (lc *LightChain) SetHeadWithTimestamp(timestamp uint64) error {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.hc.SetHeadWithTimestamp(timestamp, nil, nil)
- return lc.loadLastState()
-}
-
-// GasLimit returns the gas limit of the current HEAD block.
-func (lc *LightChain) GasLimit() uint64 {
- return lc.hc.CurrentHeader().GasLimit
-}
-
-// Reset purges the entire blockchain, restoring it to its genesis state.
-func (lc *LightChain) Reset() {
- lc.ResetWithGenesisBlock(lc.genesisBlock)
-}
-
-// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
-// specified genesis state.
-func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
- // Dump the entire block chain and purge the caches
- lc.SetHead(0)
-
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- // Prepare the genesis block and reinitialise the chain
- batch := lc.chainDb.NewBatch()
- rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
- rawdb.WriteBlock(batch, genesis)
- rawdb.WriteHeadHeaderHash(batch, genesis.Hash())
- if err := batch.Write(); err != nil {
- log.Crit("Failed to reset genesis block", "err", err)
- }
- lc.genesisBlock = genesis
- lc.hc.SetGenesis(lc.genesisBlock.Header())
- lc.hc.SetCurrentHeader(lc.genesisBlock.Header())
-}
-
-// Accessors
-
-// Engine retrieves the light chain's consensus engine.
-func (lc *LightChain) Engine() consensus.Engine { return lc.engine }
-
-// Genesis returns the genesis block
-func (lc *LightChain) Genesis() *types.Block {
- return lc.genesisBlock
-}
-
-func (lc *LightChain) StateCache() state.Database {
- panic("not implemented")
-}
-
-// GetBody retrieves a block body (transactions and uncles) from the database
-// or ODR service by hash, caching it if found.
-func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := lc.bodyCache.Get(hash); ok {
- return cached, nil
- }
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- body, err := GetBody(ctx, lc.odr, hash, *number)
- if err != nil {
- return nil, err
- }
- // Cache the found body for next time and return
- lc.bodyCache.Add(hash, body)
- return body, nil
-}
-
-// GetBodyRLP retrieves a block body in RLP encoding from the database or
-// ODR service by hash, caching it if found.
-func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := lc.bodyRLPCache.Get(hash); ok {
- return cached, nil
- }
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- body, err := GetBodyRLP(ctx, lc.odr, hash, *number)
- if err != nil {
- return nil, err
- }
- // Cache the found body for next time and return
- lc.bodyRLPCache.Add(hash, body)
- return body, nil
-}
-
-// HasBlock checks if a block is fully present in the database or not, caching
-// it if present.
-func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool {
- blk, _ := lc.GetBlock(NoOdr, hash, number)
- return blk != nil
-}
-
-// GetBlock retrieves a block from the database or ODR service by hash and number,
-// caching it if found.
-func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) {
- // Short circuit if the block's already in the cache, retrieve otherwise
- if block, ok := lc.blockCache.Get(hash); ok {
- return block, nil
- }
- block, err := GetBlock(ctx, lc.odr, hash, number)
- if err != nil {
- return nil, err
- }
- // Cache the found block for next time and return
- lc.blockCache.Add(block.Hash(), block)
- return block, nil
-}
-
-// GetBlockByHash retrieves a block from the database or ODR service by hash,
-// caching it if found.
-func (lc *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- return lc.GetBlock(ctx, hash, *number)
-}
-
-// GetBlockByNumber retrieves a block from the database or ODR service by
-// number, caching it (associated with its hash) if found.
-func (lc *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
- hash, err := GetCanonicalHash(ctx, lc.odr, number)
- if hash == (common.Hash{}) || err != nil {
- return nil, err
- }
- return lc.GetBlock(ctx, hash, number)
-}
-
-// Stop stops the blockchain service. If any imports are currently in progress
-// it will abort them using the procInterrupt.
-func (lc *LightChain) Stop() {
- if !lc.stopped.CompareAndSwap(false, true) {
- return
- }
- close(lc.quit)
- lc.StopInsert()
- lc.wg.Wait()
- log.Info("Blockchain stopped")
-}
-
-// StopInsert interrupts all insertion methods, causing them to return
-// errInsertionInterrupted as soon as possible. Insertion is permanently disabled after
-// calling this method.
-func (lc *LightChain) StopInsert() {
- lc.procInterrupt.Store(true)
-}
-
-// Rollback is designed to remove a chain of links from the database that aren't
-// certain enough to be valid.
-func (lc *LightChain) Rollback(chain []common.Hash) {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- batch := lc.chainDb.NewBatch()
- for i := len(chain) - 1; i >= 0; i-- {
- hash := chain[i]
-
- // Degrade the chain markers if they are explicitly reverted.
- // In theory we should update all in-memory markers in the
- // last step, however the direction of rollback is from high
- // to low, so it's safe the update in-memory markers directly.
- if head := lc.hc.CurrentHeader(); head.Hash() == hash {
- rawdb.WriteHeadHeaderHash(batch, head.ParentHash)
- lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1))
- }
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to rollback light chain", "error", err)
- }
-}
-
-func (lc *LightChain) InsertHeader(header *types.Header) error {
- // Verify the header first before obtaining the lock
- headers := []*types.Header{header}
- if _, err := lc.hc.ValidateHeaderChain(headers); err != nil {
- return err
- }
- // Make sure only one thread manipulates the chain at once
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.wg.Add(1)
- defer lc.wg.Done()
-
- _, err := lc.hc.WriteHeaders(headers)
- log.Info("Inserted header", "number", header.Number, "hash", header.Hash())
- return err
-}
-
-func (lc *LightChain) SetCanonical(header *types.Header) error {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.wg.Add(1)
- defer lc.wg.Done()
-
- if err := lc.hc.Reorg([]*types.Header{header}); err != nil {
- return err
- }
- // Emit events
- block := types.NewBlockWithHeader(header)
- lc.chainFeed.Send(core.ChainEvent{Block: block, Hash: block.Hash()})
- lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: block})
- log.Info("Set the chain head", "number", block.Number(), "hash", block.Hash())
- return nil
-}
-
-// InsertHeaderChain attempts to insert the given header chain in to the local
-// chain, possibly creating a reorg. If an error is returned, it will return the
-// index number of the failing header as well an error describing what went wrong.
-
-// In the case of a light chain, InsertHeaderChain also creates and posts light
-// chain events when necessary.
-func (lc *LightChain) InsertHeaderChain(chain []*types.Header) (int, error) {
- if len(chain) == 0 {
- return 0, nil
- }
- start := time.Now()
- if i, err := lc.hc.ValidateHeaderChain(chain); err != nil {
- return i, err
- }
-
- // Make sure only one thread manipulates the chain at once
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.wg.Add(1)
- defer lc.wg.Done()
-
- status, err := lc.hc.InsertHeaderChain(chain, start, lc.forker)
- if err != nil || len(chain) == 0 {
- return 0, err
- }
-
- // Create chain event for the new head block of this insertion.
- var (
- lastHeader = chain[len(chain)-1]
- block = types.NewBlockWithHeader(lastHeader)
- )
- switch status {
- case core.CanonStatTy:
- lc.chainFeed.Send(core.ChainEvent{Block: block, Hash: block.Hash()})
- lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: block})
- case core.SideStatTy:
- lc.chainSideFeed.Send(core.ChainSideEvent{Block: block})
- }
- return 0, err
-}
-
-// CurrentHeader retrieves the current head header of the canonical chain. The
-// header is retrieved from the HeaderChain's internal cache.
-func (lc *LightChain) CurrentHeader() *types.Header {
- return lc.hc.CurrentHeader()
-}
-
-// GetTd retrieves a block's total difficulty in the canonical chain from the
-// database by hash and number, caching it if found.
-func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
- return lc.hc.GetTd(hash, number)
-}
-
-// GetTdOdr retrieves the total difficult from the database or
-// network by hash and number, caching it (associated with its hash) if found.
-func (lc *LightChain) GetTdOdr(ctx context.Context, hash common.Hash, number uint64) *big.Int {
- td := lc.GetTd(hash, number)
- if td != nil {
- return td
- }
- td, _ = GetTd(ctx, lc.odr, hash, number)
- return td
-}
-
-// GetHeader retrieves a block header from the database by hash and number,
-// caching it if found.
-func (lc *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header {
- return lc.hc.GetHeader(hash, number)
-}
-
-// GetHeaderByHash retrieves a block header from the database by hash, caching it if
-// found.
-func (lc *LightChain) GetHeaderByHash(hash common.Hash) *types.Header {
- return lc.hc.GetHeaderByHash(hash)
-}
-
-// HasHeader checks if a block header is present in the database or not, caching
-// it if present.
-func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
- return lc.hc.HasHeader(hash, number)
-}
-
-// GetCanonicalHash returns the canonical hash for a given block number
-func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash {
- return bc.hc.GetCanonicalHash(number)
-}
-
-// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
-// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
-// number of blocks to be individually checked before we reach the canonical chain.
-//
-// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
-func (lc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
- return lc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
-}
-
-// GetHeaderByNumber retrieves a block header from the database by number,
-// caching it (associated with its hash) if found.
-func (lc *LightChain) GetHeaderByNumber(number uint64) *types.Header {
- return lc.hc.GetHeaderByNumber(number)
-}
-
-// GetHeaderByNumberOdr retrieves a block header from the database or network
-// by number, caching it (associated with its hash) if found.
-func (lc *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) {
- if header := lc.hc.GetHeaderByNumber(number); header != nil {
- return header, nil
- }
- return GetHeaderByNumber(ctx, lc.odr, number)
-}
-
-// Config retrieves the header chain's chain configuration.
-func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }
-
-// LockChain locks the chain mutex for reading so that multiple canonical hashes can be
-// retrieved while it is guaranteed that they belong to the same version of the chain
-func (lc *LightChain) LockChain() {
- lc.chainmu.RLock()
-}
-
-// UnlockChain unlocks the chain mutex
-func (lc *LightChain) UnlockChain() {
- lc.chainmu.RUnlock()
-}
-
-// SubscribeChainEvent registers a subscription of ChainEvent.
-func (lc *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
- return lc.scope.Track(lc.chainFeed.Subscribe(ch))
-}
-
-// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
-func (lc *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
- return lc.scope.Track(lc.chainHeadFeed.Subscribe(ch))
-}
-
-// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
-func (lc *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
- return lc.scope.Track(lc.chainSideFeed.Subscribe(ch))
-}
-
-// SubscribeLogsEvent implements the interface of filters.Backend
-// LightChain does not send logs events, so return an empty subscription.
-func (lc *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return lc.scope.Track(new(event.Feed).Subscribe(ch))
-}
-
-// SubscribeRemovedLogsEvent implements the interface of filters.Backend
-// LightChain does not send core.RemovedLogsEvent, so return an empty subscription.
-func (lc *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
- return lc.scope.Track(new(event.Feed).Subscribe(ch))
-}
diff --git a/light/lightchain_test.go b/light/lightchain_test.go
deleted file mode 100644
index 5694ca72c266..000000000000
--- a/light/lightchain_test.go
+++ /dev/null
@@ -1,358 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-// So we can deterministically seed different blockchains
-var (
- canonicalSeed = 1
- forkSeed = 2
-)
-
-// makeHeaderChain creates a deterministic chain of headers rooted at parent.
-func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header {
- blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) {
- b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
- })
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- return headers
-}
-
-// newCanonical creates a chain database, and injects a deterministic canonical
-// chain. Depending on the full flag, if creates either a full block chain or a
-// header only chain.
-func newCanonical(n int) (ethdb.Database, *LightChain, error) {
- db := rawdb.NewMemoryDatabase()
- gspec := core.Genesis{Config: params.TestChainConfig}
- genesis := gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
- blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker())
-
- // Create and inject the requested chain
- if n == 0 {
- return db, blockchain, nil
- }
- // Header-only chain requested
- headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
- _, err := blockchain.InsertHeaderChain(headers)
- return db, blockchain, err
-}
-
-// newTestLightChain creates a LightChain that doesn't validate anything.
-func newTestLightChain() *LightChain {
- db := rawdb.NewMemoryDatabase()
- gspec := &core.Genesis{
- Difficulty: big.NewInt(1),
- Config: params.TestChainConfig,
- }
- gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
- lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker())
- if err != nil {
- panic(err)
- }
- return lc
-}
-
-// Test fork of length N starting from block i
-func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) {
- // Copy old chain up to #i into a new db
- db, LightChain2, err := newCanonical(i)
- if err != nil {
- t.Fatal("could not make new canonical in testFork", err)
- }
- // Assert the chains have the same header/block at #i
- var hash1, hash2 common.Hash
- hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash()
- hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash()
- if hash1 != hash2 {
- t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
- }
- // Extend the newly created chain
- headerChainB := makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed)
- if _, err := LightChain2.InsertHeaderChain(headerChainB); err != nil {
- t.Fatalf("failed to insert forking chain: %v", err)
- }
- // Sanity check that the forked chain can be imported into the original
- var tdPre, tdPost *big.Int
- cur := LightChain.CurrentHeader()
- tdPre = LightChain.GetTd(cur.Hash(), cur.Number.Uint64())
- if err := testHeaderChainImport(headerChainB, LightChain); err != nil {
- t.Fatalf("failed to import forked header chain: %v", err)
- }
- last := headerChainB[len(headerChainB)-1]
- tdPost = LightChain.GetTd(last.Hash(), last.Number.Uint64())
- // Compare the total difficulties of the chains
- comparator(tdPre, tdPost)
-}
-
-// testHeaderChainImport tries to process a chain of header, writing them into
-// the database if successful.
-func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error {
- for _, header := range chain {
- // Try and validate the header
- if err := lightchain.engine.VerifyHeader(lightchain.hc, header); err != nil {
- return err
- }
- // Manually insert the header into the database, but don't reorganize (allows subsequent testing)
- lightchain.chainmu.Lock()
- rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(),
- new(big.Int).Add(header.Difficulty, lightchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
- rawdb.WriteHeader(lightchain.chainDb, header)
- lightchain.chainmu.Unlock()
- }
- return nil
-}
-
-// Tests that given a starting canonical chain of a given size, it can be extended
-// with various length chains.
-func TestExtendCanonicalHeaders(t *testing.T) {
- length := 5
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Start fork from current height
- testFork(t, processor, length, 1, better)
- testFork(t, processor, length, 2, better)
- testFork(t, processor, length, 5, better)
- testFork(t, processor, length, 10, better)
-}
-
-// Tests that given a starting canonical chain of a given size, creating shorter
-// forks do not take canonical ownership.
-func TestShorterForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- worse := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) >= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1)
- }
- }
- // Sum of numbers must be less than `length` for this to be a shorter fork
- testFork(t, processor, 0, 3, worse)
- testFork(t, processor, 0, 7, worse)
- testFork(t, processor, 1, 1, worse)
- testFork(t, processor, 1, 7, worse)
- testFork(t, processor, 5, 3, worse)
- testFork(t, processor, 5, 4, worse)
-}
-
-// Tests that given a starting canonical chain of a given size, creating longer
-// forks do take canonical ownership.
-func TestLongerForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Sum of numbers must be greater than `length` for this to be a longer fork
- testFork(t, processor, 0, 11, better)
- testFork(t, processor, 0, 15, better)
- testFork(t, processor, 1, 10, better)
- testFork(t, processor, 1, 12, better)
- testFork(t, processor, 5, 6, better)
- testFork(t, processor, 5, 8, better)
-}
-
-// Tests that given a starting canonical chain of a given size, creating equal
-// forks do take canonical ownership.
-func TestEqualForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- equal := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1)
- }
- }
- // Sum of numbers must be equal to `length` for this to be an equal fork
- testFork(t, processor, 0, 10, equal)
- testFork(t, processor, 1, 9, equal)
- testFork(t, processor, 2, 8, equal)
- testFork(t, processor, 5, 5, equal)
- testFork(t, processor, 6, 4, equal)
- testFork(t, processor, 9, 1, equal)
-}
-
-// Tests that chains missing links do not get accepted by the processor.
-func TestBrokenHeaderChain(t *testing.T) {
- // Make chain starting from genesis
- db, LightChain, err := newCanonical(10)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Create a forked chain, and try to insert with a missing link
- chain := makeHeaderChain(LightChain.CurrentHeader(), 5, db, forkSeed)[1:]
- if err := testHeaderChainImport(chain, LightChain); err == nil {
- t.Errorf("broken header chain not reported")
- }
-}
-
-func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
- var chain []*types.Header
- for i, difficulty := range d {
- header := &types.Header{
- Coinbase: common.Address{seed},
- Number: big.NewInt(int64(i + 1)),
- Difficulty: big.NewInt(int64(difficulty)),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- }
- if i == 0 {
- header.ParentHash = genesis.Hash()
- } else {
- header.ParentHash = chain[i-1].Hash()
- }
- chain = append(chain, types.CopyHeader(header))
- }
- return chain
-}
-
-type dummyOdr struct {
- OdrBackend
- db ethdb.Database
- indexerConfig *IndexerConfig
-}
-
-func (odr *dummyOdr) Database() ethdb.Database {
- return odr.db
-}
-
-func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error {
- return nil
-}
-
-func (odr *dummyOdr) IndexerConfig() *IndexerConfig {
- return odr.indexerConfig
-}
-
-// Tests that reorganizing a long difficult chain after a short easy one
-// overwrites the canonical numbers and links in the database.
-func TestReorgLongHeaders(t *testing.T) {
- testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10)
-}
-
-// Tests that reorganizing a short difficult chain after a long easy one
-// overwrites the canonical numbers and links in the database.
-func TestReorgShortHeaders(t *testing.T) {
- testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11)
-}
-
-func testReorg(t *testing.T, first, second []int, td int64) {
- bc := newTestLightChain()
-
- // Insert an easy and a difficult chain afterwards
- bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11))
- bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22))
- // Check that the chain is valid number and link wise
- prev := bc.CurrentHeader()
- for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) {
- if prev.ParentHash != header.Hash() {
- t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
- }
- }
- // Make sure the chain total difficulty is the correct one
- want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
- if have := bc.GetTd(bc.CurrentHeader().Hash(), bc.CurrentHeader().Number.Uint64()); have.Cmp(want) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
- }
-}
-
-// Tests that the insertion functions detect banned hashes.
-func TestBadHeaderHashes(t *testing.T) {
- bc := newTestLightChain()
-
- // Create a chain, ban a hash and try to import
- var err error
- headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
- core.BadHashes[headers[2].Hash()] = true
- if _, err = bc.InsertHeaderChain(headers); !errors.Is(err, core.ErrBannedHash) {
- t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBannedHash)
- }
-}
-
-// Tests that bad hashes are detected on boot, and the chan rolled back to a
-// good state prior to the bad hash.
-func TestReorgBadHeaderHashes(t *testing.T) {
- bc := newTestLightChain()
-
- // Create a chain, import and ban afterwards
- headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
-
- if _, err := bc.InsertHeaderChain(headers); err != nil {
- t.Fatalf("failed to import headers: %v", err)
- }
- if bc.CurrentHeader().Hash() != headers[3].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash())
- }
- core.BadHashes[headers[3].Hash()] = true
- defer func() { delete(core.BadHashes, headers[3].Hash()) }()
-
- // Create a new LightChain and check that it rolled back the state.
- ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker())
- if err != nil {
- t.Fatalf("failed to create new chain manager: %v", err)
- }
- if ncm.CurrentHeader().Hash() != headers[2].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
- }
-}
diff --git a/light/odr.go b/light/odr.go
deleted file mode 100644
index 39f626ee2c53..000000000000
--- a/light/odr.go
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-// NoOdr is the default context passed to an ODR capable function when the ODR
-// service is not required.
-var NoOdr = context.Background()
-
-// ErrNoPeers is returned if no peers capable of serving a queued request are available
-var ErrNoPeers = errors.New("no suitable peers available")
-
-// OdrBackend is an interface to a backend service that handles ODR retrievals type
-type OdrBackend interface {
- Database() ethdb.Database
- ChtIndexer() *core.ChainIndexer
- BloomTrieIndexer() *core.ChainIndexer
- BloomIndexer() *core.ChainIndexer
- Retrieve(ctx context.Context, req OdrRequest) error
- RetrieveTxStatus(ctx context.Context, req *TxStatusRequest) error
- IndexerConfig() *IndexerConfig
-}
-
-// OdrRequest is an interface for retrieval requests
-type OdrRequest interface {
- StoreResult(db ethdb.Database)
-}
-
-// TrieID identifies a state or account storage trie
-type TrieID struct {
- BlockHash common.Hash
- BlockNumber uint64
- StateRoot common.Hash
- Root common.Hash
- AccountAddress []byte
-}
-
-// StateTrieID returns a TrieID for a state trie belonging to a certain block
-// header.
-func StateTrieID(header *types.Header) *TrieID {
- return &TrieID{
- BlockHash: header.Hash(),
- BlockNumber: header.Number.Uint64(),
- StateRoot: header.Root,
- Root: header.Root,
- AccountAddress: nil,
- }
-}
-
-// StorageTrieID returns a TrieID for a contract storage trie at a given account
-// of a given state trie. It also requires the root hash of the trie for
-// checking Merkle proofs.
-func StorageTrieID(state *TrieID, address common.Address, root common.Hash) *TrieID {
- return &TrieID{
- BlockHash: state.BlockHash,
- BlockNumber: state.BlockNumber,
- StateRoot: state.StateRoot,
- AccountAddress: address[:],
- Root: root,
- }
-}
-
-// TrieRequest is the ODR request type for state/storage trie entries
-type TrieRequest struct {
- Id *TrieID
- Key []byte
- Proof *trienode.ProofSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *TrieRequest) StoreResult(db ethdb.Database) {
- req.Proof.Store(db)
-}
-
-// CodeRequest is the ODR request type for retrieving contract code
-type CodeRequest struct {
- Id *TrieID // references storage trie of the account
- Hash common.Hash
- Data []byte
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *CodeRequest) StoreResult(db ethdb.Database) {
- rawdb.WriteCode(db, req.Hash, req.Data)
-}
-
-// BlockRequest is the ODR request type for retrieving block bodies
-type BlockRequest struct {
- Hash common.Hash
- Number uint64
- Header *types.Header
- Rlp []byte
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *BlockRequest) StoreResult(db ethdb.Database) {
- rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp)
-}
-
-// ReceiptsRequest is the ODR request type for retrieving receipts.
-type ReceiptsRequest struct {
- Hash common.Hash
- Number uint64
- Header *types.Header
- Receipts types.Receipts
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
- rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts)
-}
-
-// ChtRequest is the ODR request type for retrieving header by Canonical Hash Trie
-type ChtRequest struct {
- Config *IndexerConfig
- ChtNum, BlockNum uint64
- ChtRoot common.Hash
- Header *types.Header
- Td *big.Int
- Proof *trienode.ProofSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *ChtRequest) StoreResult(db ethdb.Database) {
- hash, num := req.Header.Hash(), req.Header.Number.Uint64()
- rawdb.WriteHeader(db, req.Header)
- rawdb.WriteTd(db, hash, num, req.Td)
- rawdb.WriteCanonicalHash(db, hash, num)
-}
-
-// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
-type BloomRequest struct {
- OdrRequest
- Config *IndexerConfig
- BloomTrieNum uint64
- BitIdx uint
- SectionIndexList []uint64
- BloomTrieRoot common.Hash
- BloomBits [][]byte
- Proofs *trienode.ProofSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *BloomRequest) StoreResult(db ethdb.Database) {
- for i, sectionIdx := range req.SectionIndexList {
- sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*req.Config.BloomTrieSize-1)
- // if we don't have the canonical hash stored for this section head number, we'll still store it under
- // a key with a zero sectionHead. GetBloomBits will look there too if we still don't have the canonical
- // hash. In the unlikely case we've retrieved the section head hash since then, we'll just retrieve the
- // bit vector again from the network.
- rawdb.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i])
- }
-}
-
-// TxStatus describes the status of a transaction
-type TxStatus struct {
- Status txpool.TxStatus
- Lookup *rawdb.LegacyTxLookupEntry `rlp:"nil"`
- Error string
-}
-
-// TxStatusRequest is the ODR request type for retrieving transaction status
-type TxStatusRequest struct {
- Hashes []common.Hash
- Status []TxStatus
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *TxStatusRequest) StoreResult(db ethdb.Database) {}
diff --git a/light/odr_test.go b/light/odr_test.go
deleted file mode 100644
index de12f9b7ef0e..000000000000
--- a/light/odr_test.go
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "errors"
- "math/big"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-var (
- testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
- testBankFunds = big.NewInt(1_000_000_000_000_000_000)
-
- acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
- acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
- acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
-
- testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
- testContractAddr common.Address
-)
-
-type testOdr struct {
- OdrBackend
- indexerConfig *IndexerConfig
- sdb, ldb ethdb.Database
- serverState state.Database
- disable bool
-}
-
-func (odr *testOdr) Database() ethdb.Database {
- return odr.ldb
-}
-
-var ErrOdrDisabled = errors.New("ODR disabled")
-
-func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
- if odr.disable {
- return ErrOdrDisabled
- }
- switch req := req.(type) {
- case *BlockRequest:
- number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
- if number != nil {
- req.Rlp = rawdb.ReadBodyRLP(odr.sdb, req.Hash, *number)
- }
- case *ReceiptsRequest:
- number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
- if number != nil {
- req.Receipts = rawdb.ReadRawReceipts(odr.sdb, req.Hash, *number)
- }
- case *TrieRequest:
- var (
- err error
- t state.Trie
- )
- if len(req.Id.AccountAddress) > 0 {
- t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root, nil)
- } else {
- t, err = odr.serverState.OpenTrie(req.Id.Root)
- }
- if err != nil {
- panic(err)
- }
- nodes := trienode.NewProofSet()
- t.Prove(req.Key, nodes)
- req.Proof = nodes
- case *CodeRequest:
- req.Data = rawdb.ReadCode(odr.sdb, req.Hash)
- }
- req.StoreResult(odr.ldb)
- return nil
-}
-
-func (odr *testOdr) IndexerConfig() *IndexerConfig {
- return odr.indexerConfig
-}
-
-type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error)
-
-func TestOdrGetBlockLes2(t *testing.T) { testChainOdr(t, 1, odrGetBlock) }
-
-func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- var block *types.Block
- if bc != nil {
- block = bc.GetBlockByHash(bhash)
- } else {
- block, _ = lc.GetBlockByHash(ctx, bhash)
- }
- if block == nil {
- return nil, nil
- }
- rlp, _ := rlp.EncodeToBytes(block)
- return rlp, nil
-}
-
-func TestOdrGetReceiptsLes2(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) }
-
-func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- var receipts types.Receipts
- if bc != nil {
- if number := rawdb.ReadHeaderNumber(db, bhash); number != nil {
- if header := rawdb.ReadHeader(db, bhash, *number); header != nil {
- receipts = rawdb.ReadReceipts(db, bhash, *number, header.Time, bc.Config())
- }
- }
- } else {
- number := rawdb.ReadHeaderNumber(db, bhash)
- if number != nil {
- receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, *number)
- }
- }
- if receipts == nil {
- return nil, nil
- }
- rlp, _ := rlp.EncodeToBytes(receipts)
- return rlp, nil
-}
-
-func TestOdrAccountsLes2(t *testing.T) { testChainOdr(t, 1, odrAccounts) }
-
-func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
- acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
-
- var st *state.StateDB
- if bc == nil {
- header := lc.GetHeaderByHash(bhash)
- st = NewState(ctx, header, lc.Odr())
- } else {
- header := bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, bc.StateCache(), nil)
- }
-
- var res []byte
- for _, addr := range acc {
- bal := st.GetBalance(addr)
- rlp, _ := rlp.EncodeToBytes(bal)
- res = append(res, rlp...)
- }
- return res, st.Error()
-}
-
-func TestOdrContractCallLes2(t *testing.T) { testChainOdr(t, 1, odrContractCall) }
-
-func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
- config := params.TestChainConfig
-
- var res []byte
- for i := 0; i < 3; i++ {
- data[35] = byte(i)
-
- var (
- st *state.StateDB
- header *types.Header
- chain core.ChainContext
- )
- if bc == nil {
- chain = lc
- header = lc.GetHeaderByHash(bhash)
- st = NewState(ctx, header, lc.Odr())
- } else {
- chain = bc
- header = bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, bc.StateCache(), nil)
- }
-
- // Perform read-only call.
- st.SetBalance(testBankAddress, math.MaxBig256)
- msg := &core.Message{
- From: testBankAddress,
- To: &testContractAddr,
- Value: new(big.Int),
- GasLimit: 1000000,
- GasPrice: big.NewInt(params.InitialBaseFee),
- GasFeeCap: big.NewInt(params.InitialBaseFee),
- GasTipCap: new(big.Int),
- Data: data,
- SkipAccountChecks: true,
- }
- txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(header, chain, nil)
- vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
- gp := new(core.GasPool).AddGas(math.MaxUint64)
- result, _ := core.ApplyMessage(vmenv, msg, gp)
- res = append(res, result.Return()...)
- if st.Error() != nil {
- return res, st.Error()
- }
- }
- return res, nil
-}
-
-func testChainGen(i int, block *core.BlockGen) {
- signer := types.HomesteadSigner{}
- switch i {
- case 0:
- // In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
- block.AddTx(tx)
- case 1:
- // In block 2, the test bank sends some more ether to account #1.
- // acc1Addr passes it on to account #2.
- // acc1Addr creates a test contract.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
- nonce := block.TxNonce(acc1Addr)
- tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key)
- nonce++
- tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, block.BaseFee(), testContractCode), signer, acc1Key)
- testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
- block.AddTx(tx1)
- block.AddTx(tx2)
- block.AddTx(tx3)
- case 2:
- // Block 3 is empty but was mined by account #2.
- block.SetCoinbase(acc2Addr)
- block.SetExtra([]byte("yeehaw"))
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
- block.AddTx(tx)
- case 3:
- // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
- b2 := block.PrevBlock(1).Header()
- b2.Extra = []byte("foo")
- block.AddUncle(b2)
- b3 := block.PrevBlock(2).Header()
- b3.Extra = []byte("foo")
- block.AddUncle(b3)
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
- block.AddTx(tx)
- }
-}
-
-func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
- var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- )
- // Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- t.Fatal(err)
- }
-
- gspec.MustCommit(ldb, trie.NewDatabase(ldb, trie.HashDefaults))
- odr := &testOdr{sdb: sdb, ldb: ldb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
- lightchain, err := NewLightChain(odr, gspec.Config, ethash.NewFullFaker())
- if err != nil {
- t.Fatal(err)
- }
- headers := make([]*types.Header, len(gchain))
- for i, block := range gchain {
- headers[i] = block.Header()
- }
- if _, err := lightchain.InsertHeaderChain(headers); err != nil {
- t.Fatal(err)
- }
-
- test := func(expFail int) {
- for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ {
- bhash := rawdb.ReadCanonicalHash(sdb, i)
- b1, err := fn(NoOdr, sdb, blockchain, nil, bhash)
- if err != nil {
- t.Fatalf("error in full-node test for block %d: %v", i, err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
- defer cancel()
-
- exp := i < uint64(expFail)
- b2, err := fn(ctx, ldb, nil, lightchain, bhash)
- if err != nil && exp {
- t.Errorf("error in ODR test for block %d: %v", i, err)
- }
-
- eq := bytes.Equal(b1, b2)
- if exp && !eq {
- t.Errorf("ODR test output for block %d doesn't match full node", i)
- }
- }
- }
-
- // expect retrievals to fail (except genesis block) without a les peer
- t.Log("checking without ODR")
- odr.disable = true
- test(1)
-
- // expect all retrievals to pass with ODR enabled
- t.Log("checking with ODR")
- odr.disable = false
- test(len(gchain))
-
- // still expect all retrievals to pass, now data should be cached locally
- t.Log("checking without ODR, should be cached")
- odr.disable = true
- test(len(gchain))
-}
diff --git a/light/odr_util.go b/light/odr_util.go
deleted file mode 100644
index 9cac7df4fa98..000000000000
--- a/light/odr_util.go
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// errNonCanonicalHash is returned if the requested chain data doesn't belong
-// to the canonical chain. ODR can only retrieve the canonical chain data covered
-// by the CHT or Bloom trie for verification.
-var errNonCanonicalHash = errors.New("hash is not currently canonical")
-
-// GetHeaderByNumber retrieves the canonical block header corresponding to the
-// given number. The returned header is proven by local CHT.
-func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
- // Try to find it in the local database first.
- db := odr.Database()
- hash := rawdb.ReadCanonicalHash(db, number)
-
- // If there is a canonical hash, there should have a header too.
- // But if it's pruned, re-fetch from network again.
- if (hash != common.Hash{}) {
- if header := rawdb.ReadHeader(db, hash, number); header != nil {
- return header, nil
- }
- }
- // Retrieve the header via ODR, ensure the requested header is covered
- // by local trusted CHT.
- chts, _, chtHead := odr.ChtIndexer().Sections()
- if number >= chts*odr.IndexerConfig().ChtSize {
- return nil, errNoTrustedCht
- }
- r := &ChtRequest{
- ChtRoot: GetChtRoot(db, chts-1, chtHead),
- ChtNum: chts - 1,
- BlockNum: number,
- Config: odr.IndexerConfig(),
- }
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- return r.Header, nil
-}
-
-// GetCanonicalHash retrieves the canonical block hash corresponding to the number.
-func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
- hash := rawdb.ReadCanonicalHash(odr.Database(), number)
- if hash != (common.Hash{}) {
- return hash, nil
- }
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return common.Hash{}, err
- }
- // number -> canonical mapping already be stored in db, get it.
- return header.Hash(), nil
-}
-
-// GetTd retrieves the total difficulty corresponding to the number and hash.
-func GetTd(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*big.Int, error) {
- td := rawdb.ReadTd(odr.Database(), hash, number)
- if td != nil {
- return td, nil
- }
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return nil, err
- }
- if header.Hash() != hash {
- return nil, errNonCanonicalHash
- }
- // -> td mapping already be stored in db, get it.
- return rawdb.ReadTd(odr.Database(), hash, number), nil
-}
-
-// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
-func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) {
- if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil {
- return data, nil
- }
- // Retrieve the block header first and pass it for verification.
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return nil, errNoHeader
- }
- if header.Hash() != hash {
- return nil, errNonCanonicalHash
- }
- r := &BlockRequest{Hash: hash, Number: number, Header: header}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- return r.Rlp, nil
-}
-
-// GetBody retrieves the block body (transactions, uncles) corresponding to the
-// hash.
-func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) {
- data, err := GetBodyRLP(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- body := new(types.Body)
- if err := rlp.DecodeBytes(data, body); err != nil {
- return nil, err
- }
- return body, nil
-}
-
-// GetBlock retrieves an entire block corresponding to the hash, assembling it
-// back from the stored header and body.
-func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) {
- // Retrieve the block header and body contents
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return nil, errNoHeader
- }
- body, err := GetBody(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- // Reassemble the block and return
- return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil
-}
-
-// GetBlockReceipts retrieves the receipts generated by the transactions included
-// in a block given by its hash. Receipts will be filled in with context data.
-func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
- // Assume receipts are already stored locally and attempt to retrieve.
- receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number)
- if receipts == nil {
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return nil, errNoHeader
- }
- if header.Hash() != hash {
- return nil, errNonCanonicalHash
- }
- r := &ReceiptsRequest{Hash: hash, Number: number, Header: header}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- receipts = r.Receipts
- }
- // If the receipts are incomplete, fill the derived fields
- if len(receipts) > 0 && receipts[0].TxHash == (common.Hash{}) {
- block, err := GetBlock(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
- config := rawdb.ReadChainConfig(odr.Database(), genesis)
-
- var blobGasPrice *big.Int
- excessBlobGas := block.ExcessBlobGas()
- if excessBlobGas != nil {
- blobGasPrice = eip4844.CalcBlobFee(*excessBlobGas)
- }
-
- if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, block.Transactions()); err != nil {
- return nil, err
- }
- rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
- }
- return receipts, nil
-}
-
-// GetBlockLogs retrieves the logs generated by the transactions included in a
-// block given by its hash. Logs will be filled in with context data.
-func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) {
- receipts, err := GetBlockReceipts(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- logs := make([][]*types.Log, len(receipts))
- for i, receipt := range receipts {
- logs[i] = receipt.Logs
- }
- return logs, nil
-}
-
-// GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to
-// the given bit index and section indexes.
-func GetBloomBits(ctx context.Context, odr OdrBackend, bit uint, sections []uint64) ([][]byte, error) {
- var (
- reqIndex []int
- reqSections []uint64
- db = odr.Database()
- result = make([][]byte, len(sections))
- )
- blooms, _, sectionHead := odr.BloomTrieIndexer().Sections()
- for i, section := range sections {
- sectionHead := rawdb.ReadCanonicalHash(db, (section+1)*odr.IndexerConfig().BloomSize-1)
- // If we don't have the canonical hash stored for this section head number,
- // we'll still look for an entry with a zero sectionHead (we store it with
- // zero section head too if we don't know it at the time of the retrieval)
- if bloomBits, _ := rawdb.ReadBloomBits(db, bit, section, sectionHead); len(bloomBits) != 0 {
- result[i] = bloomBits
- continue
- }
- // TODO(rjl493456442) Convert sectionIndex to BloomTrie relative index
- if section >= blooms {
- return nil, errNoTrustedBloomTrie
- }
- reqSections = append(reqSections, section)
- reqIndex = append(reqIndex, i)
- }
- // Find all bloombits in database, nothing to query via odr, return.
- if reqSections == nil {
- return result, nil
- }
- // Send odr request to retrieve missing bloombits.
- r := &BloomRequest{
- BloomTrieRoot: GetBloomTrieRoot(db, blooms-1, sectionHead),
- BloomTrieNum: blooms - 1,
- BitIdx: bit,
- SectionIndexList: reqSections,
- Config: odr.IndexerConfig(),
- }
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- for i, idx := range reqIndex {
- result[idx] = r.BloomBits[i]
- }
- return result, nil
-}
-
-// GetTransaction retrieves a canonical transaction by hash and also returns
-// its position in the chain. There is no guarantee in the LES protocol that
-// the mined transaction will be retrieved back for sure because of different
-// reasons(the transaction is unindexed, the malicious server doesn't reply it
-// deliberately, etc). Therefore, unretrieved transactions will receive a certain
-// number of retries, thus giving a weak guarantee.
-func GetTransaction(ctx context.Context, odr OdrBackend, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
- r := &TxStatusRequest{Hashes: []common.Hash{txHash}}
- if err := odr.RetrieveTxStatus(ctx, r); err != nil || r.Status[0].Status != txpool.TxStatusIncluded {
- return nil, common.Hash{}, 0, 0, err
- }
- pos := r.Status[0].Lookup
- // first ensure that we have the header, otherwise block body retrieval will fail
- // also verify if this is a canonical block by getting the header by number and checking its hash
- if header, err := GetHeaderByNumber(ctx, odr, pos.BlockIndex); err != nil || header.Hash() != pos.BlockHash {
- return nil, common.Hash{}, 0, 0, err
- }
- body, err := GetBody(ctx, odr, pos.BlockHash, pos.BlockIndex)
- if err != nil || uint64(len(body.Transactions)) <= pos.Index || body.Transactions[pos.Index].Hash() != txHash {
- return nil, common.Hash{}, 0, 0, err
- }
- return body.Transactions[pos.Index], pos.BlockHash, pos.BlockIndex, pos.Index, nil
-}
diff --git a/light/postprocess.go b/light/postprocess.go
deleted file mode 100644
index a317e30b90a4..000000000000
--- a/light/postprocess.go
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "math/big"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/bitutil"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-// IndexerConfig includes a set of configs for chain indexers.
-type IndexerConfig struct {
- // The block frequency for creating CHTs.
- ChtSize uint64
-
- // The number of confirmations needed to generate/accept a canonical hash help trie.
- ChtConfirms uint64
-
- // The block frequency for creating new bloom bits.
- BloomSize uint64
-
- // The number of confirmation needed before a bloom section is considered probably final and its rotated bits
- // are calculated.
- BloomConfirms uint64
-
- // The block frequency for creating BloomTrie.
- BloomTrieSize uint64
-
- // The number of confirmations needed to generate/accept a bloom trie.
- BloomTrieConfirms uint64
-}
-
-var (
- // DefaultServerIndexerConfig wraps a set of configs as a default indexer config for server side.
- DefaultServerIndexerConfig = &IndexerConfig{
- ChtSize: params.CHTFrequency,
- ChtConfirms: params.HelperTrieProcessConfirmations,
- BloomSize: params.BloomBitsBlocks,
- BloomConfirms: params.BloomConfirms,
- BloomTrieSize: params.BloomTrieFrequency,
- BloomTrieConfirms: params.HelperTrieProcessConfirmations,
- }
- // DefaultClientIndexerConfig wraps a set of configs as a default indexer config for client side.
- DefaultClientIndexerConfig = &IndexerConfig{
- ChtSize: params.CHTFrequency,
- ChtConfirms: params.HelperTrieConfirmations,
- BloomSize: params.BloomBitsBlocksClient,
- BloomConfirms: params.HelperTrieConfirmations,
- BloomTrieSize: params.BloomTrieFrequency,
- BloomTrieConfirms: params.HelperTrieConfirmations,
- }
- // TestServerIndexerConfig wraps a set of configs as a test indexer config for server side.
- TestServerIndexerConfig = &IndexerConfig{
- ChtSize: 128,
- ChtConfirms: 1,
- BloomSize: 16,
- BloomConfirms: 1,
- BloomTrieSize: 128,
- BloomTrieConfirms: 1,
- }
- // TestClientIndexerConfig wraps a set of configs as a test indexer config for client side.
- TestClientIndexerConfig = &IndexerConfig{
- ChtSize: 128,
- ChtConfirms: 8,
- BloomSize: 128,
- BloomConfirms: 8,
- BloomTrieSize: 128,
- BloomTrieConfirms: 8,
- }
-)
-
-var (
- errNoTrustedCht = errors.New("no trusted canonical hash trie")
- errNoTrustedBloomTrie = errors.New("no trusted bloom trie")
- errNoHeader = errors.New("header not found")
-)
-
-// ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format
-type ChtNode struct {
- Hash common.Hash
- Td *big.Int
-}
-
-// GetChtRoot reads the CHT root associated to the given section from the database
-func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(rawdb.ChtPrefix, encNumber[:]...), sectionHead.Bytes()...))
- return common.BytesToHash(data)
-}
-
-// StoreChtRoot writes the CHT root associated to the given section into the database
-func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(rawdb.ChtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
-}
-
-// ChtIndexerBackend implements core.ChainIndexerBackend.
-type ChtIndexerBackend struct {
- disablePruning bool
- diskdb, trieTable ethdb.Database
- odr OdrBackend
- triedb *trie.Database
- section, sectionSize uint64
- lastHash common.Hash
- trie *trie.Trie
- originRoot common.Hash
-}
-
-// NewChtIndexer creates a Cht chain indexer
-func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, disablePruning bool) *core.ChainIndexer {
- trieTable := rawdb.NewTable(db, string(rawdb.ChtTablePrefix))
- backend := &ChtIndexerBackend{
- diskdb: db,
- odr: odr,
- trieTable: trieTable,
- triedb: trie.NewDatabase(trieTable, trie.HashDefaults),
- sectionSize: size,
- disablePruning: disablePruning,
- }
- return core.NewChainIndexer(db, rawdb.NewTable(db, string(rawdb.ChtIndexTablePrefix)), backend, size, confirms, time.Millisecond*100, "cht")
-}
-
-// fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the
-// ODR backend in order to be able to add new entries and calculate subsequent root hashes
-func (c *ChtIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error {
- batch := c.trieTable.NewBatch()
- r := &ChtRequest{ChtRoot: root, ChtNum: section - 1, BlockNum: section*c.sectionSize - 1, Config: c.odr.IndexerConfig()}
- for {
- err := c.odr.Retrieve(ctx, r)
- switch err {
- case nil:
- r.Proof.Store(batch)
- return batch.Write()
- case ErrNoPeers:
- // if there are no peers to serve, retry later
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-time.After(time.Second * 10):
- // stay in the loop and try again
- }
- default:
- return err
- }
- }
-}
-
-// Reset implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
- root := types.EmptyRootHash
- if section > 0 {
- root = GetChtRoot(c.diskdb, section-1, lastSectionHead)
- }
- var err error
- c.trie, err = trie.New(trie.TrieID(root), c.triedb)
-
- if err != nil && c.odr != nil {
- err = c.fetchMissingNodes(ctx, section, root)
- if err == nil {
- c.trie, err = trie.New(trie.TrieID(root), c.triedb)
- }
- }
- c.section = section
- c.originRoot = root
- return err
-}
-
-// Process implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error {
- hash, num := header.Hash(), header.Number.Uint64()
- c.lastHash = hash
-
- td := rawdb.ReadTd(c.diskdb, hash, num)
- if td == nil {
- panic(nil)
- }
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], num)
- data, _ := rlp.EncodeToBytes(ChtNode{hash, td})
- return c.trie.Update(encNumber[:], data)
-}
-
-// Commit implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Commit() error {
- root, nodes, err := c.trie.Commit(false)
- if err != nil {
- return err
- }
- // Commit trie changes into trie database in case it's not nil.
- if nodes != nil {
- if err := c.triedb.Update(root, c.originRoot, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
- return err
- }
- if err := c.triedb.Commit(root, false); err != nil {
- return err
- }
- }
- // Re-create trie with newly generated root and updated database.
- c.trie, err = trie.New(trie.TrieID(root), c.triedb)
- if err != nil {
- return err
- }
- // Pruning historical trie nodes if necessary.
- if !c.disablePruning {
- it := c.trieTable.NewIterator(nil, nil)
- defer it.Release()
-
- var (
- deleted int
- batch = c.trieTable.NewBatch()
- t = time.Now()
- )
- hashes := make(map[common.Hash]struct{})
- if nodes != nil {
- for _, hash := range nodes.Hashes() {
- hashes[hash] = struct{}{}
- }
- }
- for it.Next() {
- trimmed := bytes.TrimPrefix(it.Key(), rawdb.ChtTablePrefix)
- if len(trimmed) == common.HashLength {
- if _, ok := hashes[common.BytesToHash(trimmed)]; !ok {
- batch.Delete(trimmed)
- deleted += 1
- }
- }
- }
- if err := batch.Write(); err != nil {
- return err
- }
- log.Debug("Prune historical CHT trie nodes", "deleted", deleted, "remaining", len(hashes), "elapsed", common.PrettyDuration(time.Since(t)))
- }
- log.Info("Storing CHT", "section", c.section, "head", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root))
- StoreChtRoot(c.diskdb, c.section, c.lastHash, root)
- return nil
-}
-
-// Prune implements core.ChainIndexerBackend which deletes all chain data
-// (except hash<->number mappings) older than the specified threshold.
-func (c *ChtIndexerBackend) Prune(threshold uint64) error {
- // Short circuit if the light pruning is disabled.
- if c.disablePruning {
- return nil
- }
- t := time.Now()
- // Always keep genesis header in database.
- start, end := uint64(1), (threshold+1)*c.sectionSize
-
- var batch = c.diskdb.NewBatch()
- for {
- numbers, hashes := rawdb.ReadAllCanonicalHashes(c.diskdb, start, end, 10240)
- if len(numbers) == 0 {
- break
- }
- for i := 0; i < len(numbers); i++ {
- // Keep hash<->number mapping in database otherwise the hash based
- // API(e.g. GetReceipt, GetLogs) will be broken.
- //
- // Storage size wise, the size of a mapping is ~41bytes. For one
- // section is about 1.3MB which is acceptable.
- //
- // In order to totally get rid of this index, we need an additional
- // flag to specify how many historical data light client can serve.
- rawdb.DeleteCanonicalHash(batch, numbers[i])
- rawdb.DeleteBlockWithoutNumber(batch, hashes[i], numbers[i])
- }
- if batch.ValueSize() > ethdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- return err
- }
- batch.Reset()
- }
- start = numbers[len(numbers)-1] + 1
- }
- if err := batch.Write(); err != nil {
- return err
- }
- log.Debug("Prune history headers", "threshold", threshold, "elapsed", common.PrettyDuration(time.Since(t)))
- return nil
-}
-
-// GetBloomTrieRoot reads the BloomTrie root associated to the given section from the database
-func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(rawdb.BloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...))
- return common.BytesToHash(data)
-}
-
-// StoreBloomTrieRoot writes the BloomTrie root associated to the given section into the database
-func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(rawdb.BloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
-}
-
-// BloomTrieIndexerBackend implements core.ChainIndexerBackend
-type BloomTrieIndexerBackend struct {
- disablePruning bool
- diskdb, trieTable ethdb.Database
- triedb *trie.Database
- odr OdrBackend
- section uint64
- parentSize uint64
- size uint64
- bloomTrieRatio uint64
- trie *trie.Trie
- originRoot common.Hash
- sectionHeads []common.Hash
-}
-
-// NewBloomTrieIndexer creates a BloomTrie chain indexer
-func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64, disablePruning bool) *core.ChainIndexer {
- trieTable := rawdb.NewTable(db, string(rawdb.BloomTrieTablePrefix))
- backend := &BloomTrieIndexerBackend{
- diskdb: db,
- odr: odr,
- trieTable: trieTable,
- triedb: trie.NewDatabase(trieTable, trie.HashDefaults),
- parentSize: parentSize,
- size: size,
- disablePruning: disablePruning,
- }
- backend.bloomTrieRatio = size / parentSize
- backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio)
- return core.NewChainIndexer(db, rawdb.NewTable(db, string(rawdb.BloomTrieIndexPrefix)), backend, size, 0, time.Millisecond*100, "bloomtrie")
-}
-
-// fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the
-// ODR backend in order to be able to add new entries and calculate subsequent root hashes
-func (b *BloomTrieIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error {
- indexCh := make(chan uint, types.BloomBitLength)
- type res struct {
- nodes *trienode.ProofSet
- err error
- }
- resCh := make(chan res, types.BloomBitLength)
- for i := 0; i < 20; i++ {
- go func() {
- for bitIndex := range indexCh {
- r := &BloomRequest{BloomTrieRoot: root, BloomTrieNum: section - 1, BitIdx: bitIndex, SectionIndexList: []uint64{section - 1}, Config: b.odr.IndexerConfig()}
- for {
- if err := b.odr.Retrieve(ctx, r); err == ErrNoPeers {
- // if there are no peers to serve, retry later
- select {
- case <-ctx.Done():
- resCh <- res{nil, ctx.Err()}
- return
- case <-time.After(time.Second * 10):
- // stay in the loop and try again
- }
- } else {
- resCh <- res{r.Proofs, err}
- break
- }
- }
- }
- }()
- }
- for i := uint(0); i < types.BloomBitLength; i++ {
- indexCh <- i
- }
- close(indexCh)
- batch := b.trieTable.NewBatch()
- for i := uint(0); i < types.BloomBitLength; i++ {
- res := <-resCh
- if res.err != nil {
- return res.err
- }
- res.nodes.Store(batch)
- }
- return batch.Write()
-}
-
-// Reset implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
- root := types.EmptyRootHash
- if section > 0 {
- root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead)
- }
- var err error
- b.trie, err = trie.New(trie.TrieID(root), b.triedb)
- if err != nil && b.odr != nil {
- err = b.fetchMissingNodes(ctx, section, root)
- if err == nil {
- b.trie, err = trie.New(trie.TrieID(root), b.triedb)
- }
- }
- b.section = section
- b.originRoot = root
- return err
-}
-
-// Process implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Process(ctx context.Context, header *types.Header) error {
- num := header.Number.Uint64() - b.section*b.size
- if (num+1)%b.parentSize == 0 {
- b.sectionHeads[num/b.parentSize] = header.Hash()
- }
- return nil
-}
-
-// Commit implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Commit() error {
- var compSize, decompSize uint64
-
- for i := uint(0); i < types.BloomBitLength; i++ {
- var encKey [10]byte
- binary.BigEndian.PutUint16(encKey[0:2], uint16(i))
- binary.BigEndian.PutUint64(encKey[2:10], b.section)
- var decomp []byte
- for j := uint64(0); j < b.bloomTrieRatio; j++ {
- data, err := rawdb.ReadBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j])
- if err != nil {
- return err
- }
- decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSize/8))
- if err2 != nil {
- return err2
- }
- decomp = append(decomp, decompData...)
- }
- comp := bitutil.CompressBytes(decomp)
-
- decompSize += uint64(len(decomp))
- compSize += uint64(len(comp))
-
- var terr error
- if len(comp) > 0 {
- terr = b.trie.Update(encKey[:], comp)
- } else {
- terr = b.trie.Delete(encKey[:])
- }
- if terr != nil {
- return terr
- }
- }
- root, nodes, err := b.trie.Commit(false)
- if err != nil {
- return err
- }
- // Commit trie changes into trie database in case it's not nil.
- if nodes != nil {
- if err := b.triedb.Update(root, b.originRoot, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
- return err
- }
- if err := b.triedb.Commit(root, false); err != nil {
- return err
- }
- }
- // Re-create trie with newly generated root and updated database.
- b.trie, err = trie.New(trie.TrieID(root), b.triedb)
- if err != nil {
- return err
- }
- // Pruning historical trie nodes if necessary.
- if !b.disablePruning {
- it := b.trieTable.NewIterator(nil, nil)
- defer it.Release()
-
- var (
- deleted int
- batch = b.trieTable.NewBatch()
- t = time.Now()
- )
- hashes := make(map[common.Hash]struct{})
- if nodes != nil {
- for _, hash := range nodes.Hashes() {
- hashes[hash] = struct{}{}
- }
- }
- for it.Next() {
- trimmed := bytes.TrimPrefix(it.Key(), rawdb.BloomTrieTablePrefix)
- if len(trimmed) == common.HashLength {
- if _, ok := hashes[common.BytesToHash(trimmed)]; !ok {
- batch.Delete(trimmed)
- deleted += 1
- }
- }
- }
- if err := batch.Write(); err != nil {
- return err
- }
- log.Debug("Prune historical bloom trie nodes", "deleted", deleted, "remaining", len(hashes), "elapsed", common.PrettyDuration(time.Since(t)))
- }
- sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
- StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root)
- log.Info("Storing bloom trie", "section", b.section, "head", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression", float64(compSize)/float64(decompSize))
-
- return nil
-}
-
-// Prune implements core.ChainIndexerBackend which deletes all
-// bloombits which older than the specified threshold.
-func (b *BloomTrieIndexerBackend) Prune(threshold uint64) error {
- // Short circuit if the light pruning is disabled.
- if b.disablePruning {
- return nil
- }
- start := time.Now()
- for i := uint(0); i < types.BloomBitLength; i++ {
- rawdb.DeleteBloombits(b.diskdb, i, 0, threshold*b.bloomTrieRatio+b.bloomTrieRatio)
- }
- log.Debug("Prune history bloombits", "threshold", threshold, "elapsed", common.PrettyDuration(time.Since(start)))
- return nil
-}
diff --git a/light/trie.go b/light/trie.go
deleted file mode 100644
index 1d93bdf41592..000000000000
--- a/light/trie.go
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/trienode"
-)
-
-var (
- sha3Nil = crypto.Keccak256Hash(nil)
-)
-
-func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB {
- state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr), nil)
- return state
-}
-
-func NewStateDatabase(ctx context.Context, head *types.Header, odr OdrBackend) state.Database {
- return &odrDatabase{ctx, StateTrieID(head), odr}
-}
-
-type odrDatabase struct {
- ctx context.Context
- id *TrieID
- backend OdrBackend
-}
-
-func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
- return &odrTrie{db: db, id: db.id}, nil
-}
-
-func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ state.Trie) (state.Trie, error) {
- return &odrTrie{db: db, id: StorageTrieID(db.id, address, root)}, nil
-}
-
-func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
- switch t := t.(type) {
- case *odrTrie:
- cpy := &odrTrie{db: t.db, id: t.id}
- if t.trie != nil {
- cpy.trie = t.trie.Copy()
- }
- return cpy
- default:
- panic(fmt.Errorf("unknown trie type %T", t))
- }
-}
-
-func (db *odrDatabase) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
- if codeHash == sha3Nil {
- return nil, nil
- }
- code := rawdb.ReadCode(db.backend.Database(), codeHash)
- if len(code) != 0 {
- return code, nil
- }
- id := *db.id
- id.AccountAddress = addr[:]
- req := &CodeRequest{Id: &id, Hash: codeHash}
- err := db.backend.Retrieve(db.ctx, req)
- return req.Data, err
-}
-
-func (db *odrDatabase) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
- code, err := db.ContractCode(addr, codeHash)
- return len(code), err
-}
-
-func (db *odrDatabase) TrieDB() *trie.Database {
- return nil
-}
-
-func (db *odrDatabase) DiskDB() ethdb.KeyValueStore {
- panic("not implemented")
-}
-
-type odrTrie struct {
- db *odrDatabase
- id *TrieID
- trie *trie.Trie
-}
-
-func (t *odrTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
- key = crypto.Keccak256(key)
- var enc []byte
- err := t.do(key, func() (err error) {
- enc, err = t.trie.Get(key)
- return err
- })
- if err != nil || len(enc) == 0 {
- return nil, err
- }
- _, content, _, err := rlp.Split(enc)
- return content, err
-}
-
-func (t *odrTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
- var (
- enc []byte
- key = crypto.Keccak256(address.Bytes())
- )
- err := t.do(key, func() (err error) {
- enc, err = t.trie.Get(key)
- return err
- })
- if err != nil || len(enc) == 0 {
- return nil, err
- }
- acct := new(types.StateAccount)
- if err := rlp.DecodeBytes(enc, acct); err != nil {
- return nil, err
- }
- return acct, nil
-}
-
-func (t *odrTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error {
- key := crypto.Keccak256(address.Bytes())
- value, err := rlp.EncodeToBytes(acc)
- if err != nil {
- return fmt.Errorf("decoding error in account update: %w", err)
- }
- return t.do(key, func() error {
- return t.trie.Update(key, value)
- })
-}
-
-func (t *odrTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error {
- return nil
-}
-
-func (t *odrTrie) UpdateStorage(_ common.Address, key, value []byte) error {
- key = crypto.Keccak256(key)
- v, _ := rlp.EncodeToBytes(value)
- return t.do(key, func() error {
- return t.trie.Update(key, v)
- })
-}
-
-func (t *odrTrie) DeleteStorage(_ common.Address, key []byte) error {
- key = crypto.Keccak256(key)
- return t.do(key, func() error {
- return t.trie.Delete(key)
- })
-}
-
-// DeleteAccount abstracts an account deletion from the trie.
-func (t *odrTrie) DeleteAccount(address common.Address) error {
- key := crypto.Keccak256(address.Bytes())
- return t.do(key, func() error {
- return t.trie.Delete(key)
- })
-}
-
-func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) {
- if t.trie == nil {
- return t.id.Root, nil, nil
- }
- return t.trie.Commit(collectLeaf)
-}
-
-func (t *odrTrie) Hash() common.Hash {
- if t.trie == nil {
- return t.id.Root
- }
- return t.trie.Hash()
-}
-
-func (t *odrTrie) NodeIterator(startkey []byte) (trie.NodeIterator, error) {
- return newNodeIterator(t, startkey), nil
-}
-
-func (t *odrTrie) GetKey(sha []byte) []byte {
- return nil
-}
-
-func (t *odrTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
- return errors.New("not implemented, needs client/server interface split")
-}
-
-// do tries and retries to execute a function until it returns with no error or
-// an error type other than MissingNodeError
-func (t *odrTrie) do(key []byte, fn func() error) error {
- for {
- var err error
- if t.trie == nil {
- var id *trie.ID
- if len(t.id.AccountAddress) > 0 {
- id = trie.StorageTrieID(t.id.StateRoot, crypto.Keccak256Hash(t.id.AccountAddress), t.id.Root)
- } else {
- id = trie.StateTrieID(t.id.StateRoot)
- }
- triedb := trie.NewDatabase(t.db.backend.Database(), trie.HashDefaults)
- t.trie, err = trie.New(id, triedb)
- }
- if err == nil {
- err = fn()
- }
- if _, ok := err.(*trie.MissingNodeError); !ok {
- return err
- }
- r := &TrieRequest{Id: t.id, Key: key}
- if err := t.db.backend.Retrieve(t.db.ctx, r); err != nil {
- return err
- }
- }
-}
-
-type nodeIterator struct {
- trie.NodeIterator
- t *odrTrie
- err error
-}
-
-func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator {
- it := &nodeIterator{t: t}
- // Open the actual non-ODR trie if that hasn't happened yet.
- if t.trie == nil {
- it.do(func() error {
- var id *trie.ID
- if len(t.id.AccountAddress) > 0 {
- id = trie.StorageTrieID(t.id.StateRoot, crypto.Keccak256Hash(t.id.AccountAddress), t.id.Root)
- } else {
- id = trie.StateTrieID(t.id.StateRoot)
- }
- triedb := trie.NewDatabase(t.db.backend.Database(), trie.HashDefaults)
- t, err := trie.New(id, triedb)
- if err == nil {
- it.t.trie = t
- }
- return err
- })
- }
- it.do(func() error {
- var err error
- it.NodeIterator, err = it.t.trie.NodeIterator(startkey)
- if err != nil {
- return err
- }
- return it.NodeIterator.Error()
- })
- return it
-}
-
-func (it *nodeIterator) Next(descend bool) bool {
- var ok bool
- it.do(func() error {
- ok = it.NodeIterator.Next(descend)
- return it.NodeIterator.Error()
- })
- return ok
-}
-
-// do runs fn and attempts to fill in missing nodes by retrieving.
-func (it *nodeIterator) do(fn func() error) {
- var lasthash common.Hash
- for {
- it.err = fn()
- missing, ok := it.err.(*trie.MissingNodeError)
- if !ok {
- return
- }
- if missing.NodeHash == lasthash {
- it.err = fmt.Errorf("retrieve loop for trie node %x", missing.NodeHash)
- return
- }
- lasthash = missing.NodeHash
- r := &TrieRequest{Id: it.t.id, Key: nibblesToKey(missing.Path)}
- if it.err = it.t.db.backend.Retrieve(it.t.db.ctx, r); it.err != nil {
- return
- }
- }
-}
-
-func (it *nodeIterator) Error() error {
- if it.err != nil {
- return it.err
- }
- return it.NodeIterator.Error()
-}
-
-func nibblesToKey(nib []byte) []byte {
- if len(nib) > 0 && nib[len(nib)-1] == 0x10 {
- nib = nib[:len(nib)-1] // drop terminator
- }
- if len(nib)&1 == 1 {
- nib = append(nib, 0) // make even
- }
- key := make([]byte, len(nib)/2)
- for bi, ni := 0, 0; ni < len(nib); bi, ni = bi+1, ni+2 {
- key[bi] = nib[ni]<<4 | nib[ni+1]
- }
- return key
-}
diff --git a/light/trie_test.go b/light/trie_test.go
deleted file mode 100644
index fe724e9eea56..000000000000
--- a/light/trie_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "math/big"
- "testing"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
-)
-
-func TestNodeIterator(t *testing.T) {
- var (
- fulldb = rawdb.NewMemoryDatabase()
- lightdb = rawdb.NewMemoryDatabase()
- gspec = &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- )
- blockchain, _ := core.NewBlockChain(fulldb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- panic(err)
- }
-
- gspec.MustCommit(lightdb, trie.NewDatabase(lightdb, trie.HashDefaults))
- ctx := context.Background()
- odr := &testOdr{sdb: fulldb, ldb: lightdb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
- head := blockchain.CurrentHeader()
- lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root)
- fullTrie, _ := blockchain.StateCache().OpenTrie(head.Root)
- if err := diffTries(fullTrie, lightTrie); err != nil {
- t.Fatal(err)
- }
-}
-
-func diffTries(t1, t2 state.Trie) error {
- trieIt1, err := t1.NodeIterator(nil)
- if err != nil {
- return err
- }
- trieIt2, err := t2.NodeIterator(nil)
- if err != nil {
- return err
- }
- i1 := trie.NewIterator(trieIt1)
- i2 := trie.NewIterator(trieIt2)
- for i1.Next() && i2.Next() {
- if !bytes.Equal(i1.Key, i2.Key) {
- spew.Dump(i2)
- return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key)
- }
- if !bytes.Equal(i1.Value, i2.Value) {
- return fmt.Errorf("tries differ at key %x", i1.Key)
- }
- }
- switch {
- case i1.Err != nil:
- return fmt.Errorf("full trie iterator error: %v", i1.Err)
- case i2.Err != nil:
- return fmt.Errorf("light trie iterator error: %v", i2.Err)
- case i1.Next():
- return errors.New("full trie iterator has more k/v pairs")
- case i2.Next():
- return errors.New("light trie iterator has more k/v pairs")
- }
- return nil
-}
diff --git a/light/txpool.go b/light/txpool.go
deleted file mode 100644
index b792d70b14b8..000000000000
--- a/light/txpool.go
+++ /dev/null
@@ -1,556 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "fmt"
- "math/big"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/txpool"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
-)
-
-const (
- // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
- chainHeadChanSize = 10
-)
-
-// txPermanent is the number of mined blocks after a mined transaction is
-// considered permanent and no rollback is expected
-var txPermanent = uint64(500)
-
-// TxPool implements the transaction pool for light clients, which keeps track
-// of the status of locally created transactions, detecting if they are included
-// in a block (mined) or rolled back. There are no queued transactions since we
-// always receive all locally signed transactions in the same order as they are
-// created.
-type TxPool struct {
- config *params.ChainConfig
- signer types.Signer
- quit chan bool
- txFeed event.Feed
- scope event.SubscriptionScope
- chainHeadCh chan core.ChainHeadEvent
- chainHeadSub event.Subscription
- mu sync.RWMutex
- chain *LightChain
- odr OdrBackend
- chainDb ethdb.Database
- relay TxRelayBackend
- head common.Hash
- nonce map[common.Address]uint64 // "pending" nonce
- pending map[common.Hash]*types.Transaction // pending transactions by tx hash
- mined map[common.Hash][]*types.Transaction // mined transactions by block hash
- clearIdx uint64 // earliest block nr that can contain mined tx info
-
- istanbul bool // Fork indicator whether we are in the istanbul stage.
- eip2718 bool // Fork indicator whether we are in the eip2718 stage.
- shanghai bool // Fork indicator whether we are in the shanghai stage.
-}
-
-// TxRelayBackend provides an interface to the mechanism that forwards transactions to the
-// ETH network. The implementations of the functions should be non-blocking.
-//
-// Send instructs backend to forward new transactions NewHead notifies backend about a new
-// head after processed by the tx pool, including mined and rolled back transactions since
-// the last event.
-//
-// Discard notifies backend about transactions that should be discarded either because
-// they have been replaced by a re-send or because they have been mined long ago and no
-// rollback is expected.
-type TxRelayBackend interface {
- Send(txs types.Transactions)
- NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash)
- Discard(hashes []common.Hash)
-}
-
-// NewTxPool creates a new light transaction pool
-func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool {
- pool := &TxPool{
- config: config,
- signer: types.LatestSigner(config),
- nonce: make(map[common.Address]uint64),
- pending: make(map[common.Hash]*types.Transaction),
- mined: make(map[common.Hash][]*types.Transaction),
- quit: make(chan bool),
- chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
- chain: chain,
- relay: relay,
- odr: chain.Odr(),
- chainDb: chain.Odr().Database(),
- head: chain.CurrentHeader().Hash(),
- clearIdx: chain.CurrentHeader().Number.Uint64(),
- }
- // Subscribe events from blockchain
- pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh)
- go pool.eventLoop()
-
- return pool
-}
-
-// currentState returns the light state of the current head header
-func (pool *TxPool) currentState(ctx context.Context) *state.StateDB {
- return NewState(ctx, pool.chain.CurrentHeader(), pool.odr)
-}
-
-// GetNonce returns the "pending" nonce of a given address. It always queries
-// the nonce belonging to the latest header too in order to detect if another
-// client using the same key sent a transaction.
-func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
- state := pool.currentState(ctx)
- nonce := state.GetNonce(addr)
- if state.Error() != nil {
- return 0, state.Error()
- }
- sn, ok := pool.nonce[addr]
- if ok && sn > nonce {
- nonce = sn
- }
- if !ok || sn < nonce {
- pool.nonce[addr] = nonce
- }
- return nonce, nil
-}
-
-// txStateChanges stores the recent changes between pending/mined states of
-// transactions. True means mined, false means rolled back, no entry means no change
-type txStateChanges map[common.Hash]bool
-
-// setState sets the status of a tx to either recently mined or recently rolled back
-func (txc txStateChanges) setState(txHash common.Hash, mined bool) {
- val, ent := txc[txHash]
- if ent && (val != mined) {
- delete(txc, txHash)
- } else {
- txc[txHash] = mined
- }
-}
-
-// getLists creates lists of mined and rolled back tx hashes
-func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Hash) {
- for hash, val := range txc {
- if val {
- mined = append(mined, hash)
- } else {
- rollback = append(rollback, hash)
- }
- }
- return
-}
-
-// checkMinedTxs checks newly added blocks for the currently pending transactions
-// and marks them as mined if necessary. It also stores block position in the db
-// and adds them to the received txStateChanges map.
-func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error {
- // If no transactions are pending, we don't care about anything
- if len(pool.pending) == 0 {
- return nil
- }
- block, err := GetBlock(ctx, pool.odr, hash, number)
- if err != nil {
- return err
- }
- // Gather all the local transaction mined in this block
- list := pool.mined[hash]
- for _, tx := range block.Transactions() {
- if _, ok := pool.pending[tx.Hash()]; ok {
- list = append(list, tx)
- }
- }
- // If some transactions have been mined, write the needed data to disk and update
- if list != nil {
- // Retrieve all the receipts belonging to this block and write the lookup table
- if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results
- return err
- }
- rawdb.WriteTxLookupEntriesByBlock(pool.chainDb, block)
-
- // Update the transaction pool's state
- for _, tx := range list {
- delete(pool.pending, tx.Hash())
- txc.setState(tx.Hash(), true)
- }
- pool.mined[hash] = list
- }
- return nil
-}
-
-// rollbackTxs marks the transactions contained in recently rolled back blocks
-// as rolled back. It also removes any positional lookup entries.
-func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
- batch := pool.chainDb.NewBatch()
- if list, ok := pool.mined[hash]; ok {
- for _, tx := range list {
- txHash := tx.Hash()
- rawdb.DeleteTxLookupEntry(batch, txHash)
- pool.pending[txHash] = tx
- txc.setState(txHash, false)
- }
- delete(pool.mined, hash)
- }
- batch.Write()
-}
-
-// reorgOnNewHead sets a new head header, processing (and rolling back if necessary)
-// the blocks since the last known head and returns a txStateChanges map containing
-// the recently mined and rolled back transaction hashes. If an error (context
-// timeout) occurs during checking new blocks, it leaves the locally known head
-// at the latest checked block and still returns a valid txStateChanges, making it
-// possible to continue checking the missing blocks at the next chain head event
-func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) {
- txc := make(txStateChanges)
- oldh := pool.chain.GetHeaderByHash(pool.head)
- newh := newHeader
- // find common ancestor, create list of rolled back and new block hashes
- var oldHashes, newHashes []common.Hash
- for oldh.Hash() != newh.Hash() {
- if oldh.Number.Uint64() >= newh.Number.Uint64() {
- oldHashes = append(oldHashes, oldh.Hash())
- oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1)
- }
- if oldh.Number.Uint64() < newh.Number.Uint64() {
- newHashes = append(newHashes, newh.Hash())
- newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
- if newh == nil {
- // happens when CHT syncing, nothing to do
- newh = oldh
- }
- }
- }
- if oldh.Number.Uint64() < pool.clearIdx {
- pool.clearIdx = oldh.Number.Uint64()
- }
- // roll back old blocks
- for _, hash := range oldHashes {
- pool.rollbackTxs(hash, txc)
- }
- pool.head = oldh.Hash()
- // check mined txs of new blocks (array is in reversed order)
- for i := len(newHashes) - 1; i >= 0; i-- {
- hash := newHashes[i]
- if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil {
- return txc, err
- }
- pool.head = hash
- }
-
- // clear old mined tx entries of old blocks
- if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent {
- idx2 := idx - txPermanent
- if len(pool.mined) > 0 {
- for i := pool.clearIdx; i < idx2; i++ {
- hash := rawdb.ReadCanonicalHash(pool.chainDb, i)
- if list, ok := pool.mined[hash]; ok {
- hashes := make([]common.Hash, len(list))
- for i, tx := range list {
- hashes[i] = tx.Hash()
- }
- pool.relay.Discard(hashes)
- delete(pool.mined, hash)
- }
- }
- }
- pool.clearIdx = idx2
- }
-
- return txc, nil
-}
-
-// blockCheckTimeout is the time limit for checking new blocks for mined
-// transactions. Checking resumes at the next chain head event if timed out.
-const blockCheckTimeout = time.Second * 3
-
-// eventLoop processes chain head events and also notifies the tx relay backend
-// about the new head hash and tx state changes
-func (pool *TxPool) eventLoop() {
- for {
- select {
- case ev := <-pool.chainHeadCh:
- pool.setNewHead(ev.Block.Header())
- // hack in order to avoid hogging the lock; this part will
- // be replaced by a subsequent PR.
- time.Sleep(time.Millisecond)
-
- // System stopped
- case <-pool.chainHeadSub.Err():
- return
- }
- }
-}
-
-func (pool *TxPool) setNewHead(head *types.Header) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
-
- ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout)
- defer cancel()
-
- txc, _ := pool.reorgOnNewHead(ctx, head)
- m, r := txc.getLists()
- pool.relay.NewHead(pool.head, m, r)
-
- // Update fork indicator by next pending block number
- next := new(big.Int).Add(head.Number, big.NewInt(1))
- pool.istanbul = pool.config.IsIstanbul(next)
- pool.eip2718 = pool.config.IsBerlin(next)
- pool.shanghai = pool.config.IsShanghai(next, uint64(time.Now().Unix()))
-}
-
-// Stop stops the light transaction pool
-func (pool *TxPool) Stop() {
- // Unsubscribe all subscriptions registered from txpool
- pool.scope.Close()
- // Unsubscribe subscriptions registered from blockchain
- pool.chainHeadSub.Unsubscribe()
- close(pool.quit)
- log.Info("Transaction pool stopped")
-}
-
-// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and
-// starts sending event to the given channel.
-func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return pool.scope.Track(pool.txFeed.Subscribe(ch))
-}
-
-// Stats returns the number of currently pending (locally created) transactions
-func (pool *TxPool) Stats() (pending int) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
- pending = len(pool.pending)
- return
-}
-
-// validateTx checks whether a transaction is valid according to the consensus rules.
-func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
- // Validate sender
- var (
- from common.Address
- err error
- )
-
- // Validate the transaction sender and it's sig. Throw
- // if the from fields is invalid.
- if from, err = types.Sender(pool.signer, tx); err != nil {
- return txpool.ErrInvalidSender
- }
- // Last but not least check for nonce errors
- currentState := pool.currentState(ctx)
- if n := currentState.GetNonce(from); n > tx.Nonce() {
- return core.ErrNonceTooLow
- }
-
- // Check the transaction doesn't exceed the current
- // block limit gas.
- header := pool.chain.GetHeaderByHash(pool.head)
- if header.GasLimit < tx.Gas() {
- return txpool.ErrGasLimit
- }
-
- // Transactions can't be negative. This may never happen
- // using RLP decoded transactions but may occur if you create
- // a transaction using the RPC for example.
- if tx.Value().Sign() < 0 {
- return txpool.ErrNegativeValue
- }
-
- // Transactor should have enough funds to cover the costs
- // cost == V + GP * GL
- if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
- return core.ErrInsufficientFunds
- }
-
- // Should supply enough intrinsic gas
- gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
- if err != nil {
- return err
- }
- if tx.Gas() < gas {
- return core.ErrIntrinsicGas
- }
- return currentState.Error()
-}
-
-// add validates a new transaction and sets its state pending if processable.
-// It also updates the locally stored nonce if necessary.
-func (pool *TxPool) add(ctx context.Context, tx *types.Transaction) error {
- hash := tx.Hash()
-
- if pool.pending[hash] != nil {
- return fmt.Errorf("known transaction (%x)", hash[:4])
- }
- err := pool.validateTx(ctx, tx)
- if err != nil {
- return err
- }
-
- if _, ok := pool.pending[hash]; !ok {
- pool.pending[hash] = tx
-
- nonce := tx.Nonce() + 1
-
- addr, _ := types.Sender(pool.signer, tx)
- if nonce > pool.nonce[addr] {
- pool.nonce[addr] = nonce
- }
-
- // Notify the subscribers. This event is posted in a goroutine
- // because it's possible that somewhere during the post "Remove transaction"
- // gets called which will then wait for the global tx pool lock and deadlock.
- go pool.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
- }
-
- // Print a log message if low enough level is set
- log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(pool.signer, tx); return from }}, "to", tx.To())
- return nil
-}
-
-// Add adds a transaction to the pool if valid and passes it to the tx relay
-// backend
-func (pool *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
- pool.mu.Lock()
- defer pool.mu.Unlock()
- data, err := tx.MarshalBinary()
- if err != nil {
- return err
- }
-
- if err := pool.add(ctx, tx); err != nil {
- return err
- }
- //fmt.Println("Send", tx.Hash())
- pool.relay.Send(types.Transactions{tx})
-
- pool.chainDb.Put(tx.Hash().Bytes(), data)
- return nil
-}
-
-// AddBatch adds all valid transactions to the pool and passes them to
-// the tx relay backend
-func (pool *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
- var sendTx types.Transactions
-
- for _, tx := range txs {
- if err := pool.add(ctx, tx); err == nil {
- sendTx = append(sendTx, tx)
- }
- }
- if len(sendTx) > 0 {
- pool.relay.Send(sendTx)
- }
-}
-
-// GetTransaction returns a transaction if it is contained in the pool
-// and nil otherwise.
-func (pool *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
- // check the txs first
- if tx, ok := pool.pending[hash]; ok {
- return tx
- }
- return nil
-}
-
-// GetTransactions returns all currently processable transactions.
-// The returned slice may be modified by the caller.
-func (pool *TxPool) GetTransactions() (txs types.Transactions, err error) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
- txs = make(types.Transactions, len(pool.pending))
- i := 0
- for _, tx := range pool.pending {
- txs[i] = tx
- i++
- }
- return txs, nil
-}
-
-// Content retrieves the data content of the transaction pool, returning all the
-// pending as well as queued transactions, grouped by account and nonce.
-func (pool *TxPool) Content() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
- // Retrieve all the pending transactions and sort by account and by nonce
- pending := make(map[common.Address][]*types.Transaction)
- for _, tx := range pool.pending {
- account, _ := types.Sender(pool.signer, tx)
- pending[account] = append(pending[account], tx)
- }
- // There are no queued transactions in a light pool, just return an empty map
- queued := make(map[common.Address][]*types.Transaction)
- return pending, queued
-}
-
-// ContentFrom retrieves the data content of the transaction pool, returning the
-// pending as well as queued transactions of this address, grouped by nonce.
-func (pool *TxPool) ContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
- // Retrieve the pending transactions and sort by nonce
- var pending []*types.Transaction
- for _, tx := range pool.pending {
- account, _ := types.Sender(pool.signer, tx)
- if account != addr {
- continue
- }
- pending = append(pending, tx)
- }
- // There are no queued transactions in a light pool, just return an empty map
- return pending, []*types.Transaction{}
-}
-
-// RemoveTransactions removes all given transactions from the pool.
-func (pool *TxPool) RemoveTransactions(txs types.Transactions) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
-
- var hashes []common.Hash
- batch := pool.chainDb.NewBatch()
- for _, tx := range txs {
- hash := tx.Hash()
- delete(pool.pending, hash)
- batch.Delete(hash.Bytes())
- hashes = append(hashes, hash)
- }
- batch.Write()
- pool.relay.Discard(hashes)
-}
-
-// RemoveTx removes the transaction with the given hash from the pool.
-func (pool *TxPool) RemoveTx(hash common.Hash) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
- // delete from pending pool
- delete(pool.pending, hash)
- pool.chainDb.Delete(hash[:])
- pool.relay.Discard([]common.Hash{hash})
-}
diff --git a/light/txpool_test.go b/light/txpool_test.go
deleted file mode 100644
index 1eec7bc42779..000000000000
--- a/light/txpool_test.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "math"
- "math/big"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "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"
-)
-
-type testTxRelay struct {
- send, discard, mined chan int
-}
-
-func (r *testTxRelay) Send(txs types.Transactions) {
- r.send <- len(txs)
-}
-
-func (r *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
- m := len(mined)
- if m != 0 {
- r.mined <- m
- }
-}
-
-func (r *testTxRelay) Discard(hashes []common.Hash) {
- r.discard <- len(hashes)
-}
-
-const poolTestTxs = 1000
-const poolTestBlocks = 100
-
-// test tx 0..n-1
-var testTx [poolTestTxs]*types.Transaction
-
-// txs sent before block i
-func sentTx(i int) int {
- return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs)
-}
-
-// txs included in block i or before that (minedTx(i) <= sentTx(i))
-func minedTx(i int) int {
- return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs)
-}
-
-func txPoolTestChainGen(i int, block *core.BlockGen) {
- s := minedTx(i)
- e := minedTx(i + 1)
- for i := s; i < e; i++ {
- block.AddTx(testTx[i])
- }
-}
-
-func TestTxPool(t *testing.T) {
- for i := range testTx {
- testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
- }
-
- var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- )
- // Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
- _, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), poolTestBlocks, txPoolTestChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- panic(err)
- }
-
- gspec.MustCommit(ldb, trie.NewDatabase(ldb, trie.HashDefaults))
- odr := &testOdr{sdb: sdb, ldb: ldb, serverState: blockchain.StateCache(), indexerConfig: TestClientIndexerConfig}
- relay := &testTxRelay{
- send: make(chan int, 1),
- discard: make(chan int, 1),
- mined: make(chan int, 1),
- }
- lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
- txPermanent = 50
- pool := NewTxPool(params.TestChainConfig, lightchain, relay)
- ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
- defer cancel()
-
- for ii, block := range gchain {
- i := ii + 1
- s := sentTx(i - 1)
- e := sentTx(i)
- for i := s; i < e; i++ {
- pool.Add(ctx, testTx[i])
- got := <-relay.send
- exp := 1
- if got != exp {
- t.Errorf("relay.Send expected len = %d, got %d", exp, got)
- }
- }
-
- if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}); err != nil {
- panic(err)
- }
-
- got := <-relay.mined
- exp := minedTx(i) - minedTx(i-1)
- if got != exp {
- t.Errorf("relay.NewHead expected len(mined) = %d, got %d", exp, got)
- }
-
- exp = 0
- if i > int(txPermanent)+1 {
- exp = minedTx(i-int(txPermanent)-1) - minedTx(i-int(txPermanent)-2)
- }
- if exp != 0 {
- got = <-relay.discard
- if got != exp {
- t.Errorf("relay.Discard expected len = %d, got %d", exp, got)
- }
- }
- }
-}
From 63979bc9cc461d1f05e6be4646d4dc788de1698f Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Tue, 28 Nov 2023 13:54:17 +0100
Subject: [PATCH 041/103] cmd/evm, core/state: fix post-exec dump of state
(statetests, blockchaintests) (#28504)
There were several problems related to dumping state.
- If a preimage was missing, even if we had set the `OnlyWithAddresses` to `false`, to export them anyway, the way the mapping was constructed (using `common.Address` as key) made the entries get lost anyway. Concerns both state- and blockchain tests.
- Blockchain test execution was not configured to store preimages.
This changes makes it so that the block test executor takes a callback, just like the state test executor already does. This callback can be used to examine the post-execution state, e.g. to aid debugging of test failures.
---
cmd/evm/blockrunner.go | 9 +++-
cmd/geth/chaincmd.go | 5 --
cmd/geth/snapshot.go | 10 ++--
core/state/dump.go | 100 ++++++++++++++++-----------------------
core/state/state_test.go | 3 ++
eth/api_debug.go | 18 +++----
eth/api_debug_test.go | 25 +++++-----
tests/block_test.go | 8 ++--
tests/block_test_util.go | 13 +++--
9 files changed, 92 insertions(+), 99 deletions(-)
diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go
index caed9b65fc6d..c5d836e0ea61 100644
--- a/cmd/evm/blockrunner.go
+++ b/cmd/evm/blockrunner.go
@@ -24,6 +24,7 @@ import (
"regexp"
"sort"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
@@ -85,7 +86,13 @@ func blockTestCmd(ctx *cli.Context) error {
continue
}
test := tests[name]
- if err := test.Run(false, rawdb.HashScheme, tracer); err != nil {
+ if err := test.Run(false, rawdb.HashScheme, tracer, func(res error, chain *core.BlockChain) {
+ if ctx.Bool(DumpFlag.Name) {
+ if state, _ := chain.State(); state != nil {
+ fmt.Println(string(state.Dump(nil)))
+ }
+ }
+ }); err != nil {
return fmt.Errorf("test %v: %w", name, err)
}
}
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 4e528d6502fc..3b4f516af7b4 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -473,11 +473,6 @@ func dump(ctx *cli.Context) error {
if ctx.Bool(utils.IterativeOutputFlag.Name) {
state.IterativeDump(conf, json.NewEncoder(os.Stdout))
} else {
- if conf.OnlyWithAddresses {
- fmt.Fprintf(os.Stderr, "If you want to include accounts with missing preimages, you need iterative output, since"+
- " otherwise the accounts will overwrite each other in the resulting mapping.")
- return errors.New("incompatible options")
- }
fmt.Println(string(state.Dump(conf)))
}
return nil
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index 80d946b894d8..4284005a0221 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -580,11 +580,11 @@ func dumpState(ctx *cli.Context) error {
return err
}
da := &state.DumpAccount{
- Balance: account.Balance.String(),
- Nonce: account.Nonce,
- Root: account.Root.Bytes(),
- CodeHash: account.CodeHash,
- SecureKey: accIt.Hash().Bytes(),
+ Balance: account.Balance.String(),
+ Nonce: account.Nonce,
+ Root: account.Root.Bytes(),
+ CodeHash: account.CodeHash,
+ AddressHash: accIt.Hash().Bytes(),
}
if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash))
diff --git a/core/state/dump.go b/core/state/dump.go
index 9ce6cd394b88..cf4662114448 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -49,21 +49,24 @@ type DumpCollector interface {
// DumpAccount represents an account in the state.
type DumpAccount struct {
- Balance string `json:"balance"`
- Nonce uint64 `json:"nonce"`
- Root hexutil.Bytes `json:"root"`
- CodeHash hexutil.Bytes `json:"codeHash"`
- Code hexutil.Bytes `json:"code,omitempty"`
- Storage map[common.Hash]string `json:"storage,omitempty"`
- Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
- SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
+ Balance string `json:"balance"`
+ Nonce uint64 `json:"nonce"`
+ Root hexutil.Bytes `json:"root"`
+ CodeHash hexutil.Bytes `json:"codeHash"`
+ Code hexutil.Bytes `json:"code,omitempty"`
+ Storage map[common.Hash]string `json:"storage,omitempty"`
+ Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
+ AddressHash hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
}
// Dump represents the full dump in a collected format, as one large map.
type Dump struct {
- Root string `json:"root"`
- Accounts map[common.Address]DumpAccount `json:"accounts"`
+ Root string `json:"root"`
+ Accounts map[string]DumpAccount `json:"accounts"`
+ // Next can be set to represent that this dump is only partial, and Next
+ // is where an iterator should be positioned in order to continue the dump.
+ Next []byte `json:"next,omitempty"` // nil if no more accounts
}
// OnRoot implements DumpCollector interface
@@ -73,27 +76,11 @@ func (d *Dump) OnRoot(root common.Hash) {
// OnAccount implements DumpCollector interface
func (d *Dump) OnAccount(addr *common.Address, account DumpAccount) {
- if addr != nil {
- d.Accounts[*addr] = account
+ if addr == nil {
+ d.Accounts[fmt.Sprintf("pre(%s)", account.AddressHash)] = account
}
-}
-
-// IteratorDump is an implementation for iterating over data.
-type IteratorDump struct {
- Root string `json:"root"`
- Accounts map[common.Address]DumpAccount `json:"accounts"`
- Next []byte `json:"next,omitempty"` // nil if no more accounts
-}
-
-// OnRoot implements DumpCollector interface
-func (d *IteratorDump) OnRoot(root common.Hash) {
- d.Root = fmt.Sprintf("%x", root)
-}
-
-// OnAccount implements DumpCollector interface
-func (d *IteratorDump) OnAccount(addr *common.Address, account DumpAccount) {
if addr != nil {
- d.Accounts[*addr] = account
+ d.Accounts[(*addr).String()] = account
}
}
@@ -105,14 +92,14 @@ type iterativeDump struct {
// OnAccount implements DumpCollector interface
func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{
- Balance: account.Balance,
- Nonce: account.Nonce,
- Root: account.Root,
- CodeHash: account.CodeHash,
- Code: account.Code,
- Storage: account.Storage,
- SecureKey: account.SecureKey,
- Address: addr,
+ Balance: account.Balance,
+ Nonce: account.Nonce,
+ Root: account.Root,
+ CodeHash: account.CodeHash,
+ Code: account.Code,
+ Storage: account.Storage,
+ AddressHash: account.AddressHash,
+ Address: addr,
}
d.Encode(dumpAccount)
}
@@ -150,26 +137,27 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err)
}
- account := DumpAccount{
- Balance: data.Balance.String(),
- Nonce: data.Nonce,
- Root: data.Root[:],
- CodeHash: data.CodeHash,
- SecureKey: it.Key,
- }
var (
- addrBytes = s.trie.GetKey(it.Key)
- addr = common.BytesToAddress(addrBytes)
+ account = DumpAccount{
+ Balance: data.Balance.String(),
+ Nonce: data.Nonce,
+ Root: data.Root[:],
+ CodeHash: data.CodeHash,
+ AddressHash: it.Key,
+ }
address *common.Address
+ addr common.Address
+ addrBytes = s.trie.GetKey(it.Key)
)
if addrBytes == nil {
- // Preimage missing
missingPreimages++
if conf.OnlyWithAddresses {
continue
}
} else {
+ addr = common.BytesToAddress(addrBytes)
address = &addr
+ account.Address = address
}
obj := newObject(s, addr, &data)
if !conf.SkipCode {
@@ -220,12 +208,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
return nextKey
}
-// RawDump returns the entire state an a single large object
+// RawDump returns the state. If the processing is aborted e.g. due to options
+// reaching Max, the `Next` key is set on the returned Dump.
func (s *StateDB) RawDump(opts *DumpConfig) Dump {
dump := &Dump{
- Accounts: make(map[common.Address]DumpAccount),
+ Accounts: make(map[string]DumpAccount),
}
- s.DumpToCollector(dump, opts)
+ dump.Next = s.DumpToCollector(dump, opts)
return *dump
}
@@ -234,7 +223,7 @@ func (s *StateDB) Dump(opts *DumpConfig) []byte {
dump := s.RawDump(opts)
json, err := json.MarshalIndent(dump, "", " ")
if err != nil {
- fmt.Println("Dump err", err)
+ log.Error("Error dumping state", "err", err)
}
return json
}
@@ -243,12 +232,3 @@ func (s *StateDB) Dump(opts *DumpConfig) []byte {
func (s *StateDB) IterativeDump(opts *DumpConfig, output *json.Encoder) {
s.DumpToCollector(iterativeDump{output}, opts)
}
-
-// IteratorDump dumps out a batch of accounts starts with the given start key
-func (s *StateDB) IteratorDump(opts *DumpConfig) IteratorDump {
- iterator := &IteratorDump{
- Accounts: make(map[common.Address]DumpAccount),
- }
- iterator.Next = s.DumpToCollector(iterator, opts)
- return *iterator
-}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 2553133dea55..2f45ba44b4e4 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -71,6 +71,7 @@ func TestDump(t *testing.T) {
"nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "address": "0x0000000000000000000000000000000000000001",
"key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
},
"0x0000000000000000000000000000000000000002": {
@@ -78,6 +79,7 @@ func TestDump(t *testing.T) {
"nonce": 0,
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "address": "0x0000000000000000000000000000000000000002",
"key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
},
"0x0000000000000000000000000000000000000102": {
@@ -86,6 +88,7 @@ func TestDump(t *testing.T) {
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
"code": "0x03030303030303",
+ "address": "0x0000000000000000000000000000000000000102",
"key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"
}
}
diff --git a/eth/api_debug.go b/eth/api_debug.go
index dc9f56814699..05010a3969c6 100644
--- a/eth/api_debug.go
+++ b/eth/api_debug.go
@@ -133,7 +133,7 @@ func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error)
const AccountRangeMaxResults = 256
// AccountRange enumerates all accounts in the given block and start point in paging request
-func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hexutil.Bytes, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
+func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hexutil.Bytes, maxResults int, nocode, nostorage, incompletes bool) (state.Dump, error) {
var stateDb *state.StateDB
var err error
@@ -144,7 +144,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
if stateDb == nil {
- return state.IteratorDump{}, errors.New("pending state is not available")
+ return state.Dump{}, errors.New("pending state is not available")
}
} else {
var header *types.Header
@@ -158,29 +158,29 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
default:
block := api.eth.blockchain.GetBlockByNumber(uint64(number))
if block == nil {
- return state.IteratorDump{}, fmt.Errorf("block #%d not found", number)
+ return state.Dump{}, fmt.Errorf("block #%d not found", number)
}
header = block.Header()
}
if header == nil {
- return state.IteratorDump{}, fmt.Errorf("block #%d not found", number)
+ return state.Dump{}, fmt.Errorf("block #%d not found", number)
}
stateDb, err = api.eth.BlockChain().StateAt(header.Root)
if err != nil {
- return state.IteratorDump{}, err
+ return state.Dump{}, err
}
}
} else if hash, ok := blockNrOrHash.Hash(); ok {
block := api.eth.blockchain.GetBlockByHash(hash)
if block == nil {
- return state.IteratorDump{}, fmt.Errorf("block %s not found", hash.Hex())
+ return state.Dump{}, fmt.Errorf("block %s not found", hash.Hex())
}
stateDb, err = api.eth.BlockChain().StateAt(block.Root())
if err != nil {
- return state.IteratorDump{}, err
+ return state.Dump{}, err
}
} else {
- return state.IteratorDump{}, errors.New("either block number or block hash must be specified")
+ return state.Dump{}, errors.New("either block number or block hash must be specified")
}
opts := &state.DumpConfig{
@@ -193,7 +193,7 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
if maxResults > AccountRangeMaxResults || maxResults <= 0 {
opts.Max = AccountRangeMaxResults
}
- return stateDb.IteratorDump(opts), nil
+ return stateDb.RawDump(opts), nil
}
// StorageRangeResult is the result of a debug_storageRangeAt API call.
diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go
index 3d3444a87163..184b90dd09ad 100644
--- a/eth/api_debug_test.go
+++ b/eth/api_debug_test.go
@@ -21,6 +21,7 @@ import (
"fmt"
"math/big"
"reflect"
+ "strings"
"testing"
"github.com/davecgh/go-spew/spew"
@@ -35,8 +36,8 @@ import (
var dumper = spew.ConfigState{Indent: " "}
-func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.IteratorDump {
- result := statedb.IteratorDump(&state.DumpConfig{
+func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
+ result := statedb.RawDump(&state.DumpConfig{
SkipCode: true,
SkipStorage: true,
OnlyWithAddresses: false,
@@ -47,12 +48,12 @@ func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, st
if len(result.Accounts) != expectedNum {
t.Fatalf("expected %d results, got %d", expectedNum, len(result.Accounts))
}
- for address := range result.Accounts {
- if address == (common.Address{}) {
- t.Fatalf("empty address returned")
+ for addr, acc := range result.Accounts {
+ if strings.HasSuffix(addr, "pre") || acc.Address == nil {
+ t.Fatalf("account without prestate (address) returned: %v", addr)
}
- if !statedb.Exist(address) {
- t.Fatalf("account not found in state %s", address.Hex())
+ if !statedb.Exist(*acc.Address) {
+ t.Fatalf("account not found in state %s", acc.Address.Hex())
}
}
return result
@@ -92,16 +93,16 @@ func TestAccountRange(t *testing.T) {
secondResult := accountRangeTest(t, &trie, sdb, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)
hList := make([]common.Hash, 0)
- for addr1 := range firstResult.Accounts {
- // If address is empty, then it makes no sense to compare
+ for addr1, acc := range firstResult.Accounts {
+ // If address is non-available, then it makes no sense to compare
// them as they might be two different accounts.
- if addr1 == (common.Address{}) {
+ if acc.Address == nil {
continue
}
if _, duplicate := secondResult.Accounts[addr1]; duplicate {
t.Fatalf("pagination test failed: results should not overlap")
}
- hList = append(hList, crypto.Keccak256Hash(addr1.Bytes()))
+ hList = append(hList, crypto.Keccak256Hash(acc.Address.Bytes()))
}
// Test to see if it's possible to recover from the middle of the previous
// set and get an even split between the first and second sets.
@@ -140,7 +141,7 @@ func TestEmptyAccountRange(t *testing.T) {
st.Commit(0, true)
st, _ = state.New(types.EmptyRootHash, statedb, nil)
- results := st.IteratorDump(&state.DumpConfig{
+ results := st.RawDump(&state.DumpConfig{
SkipCode: true,
SkipStorage: true,
OnlyWithAddresses: true,
diff --git a/tests/block_test.go b/tests/block_test.go
index e913ecbc9093..aa6f27b8f378 100644
--- a/tests/block_test.go
+++ b/tests/block_test.go
@@ -74,19 +74,19 @@ func TestExecutionSpec(t *testing.T) {
}
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
- if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil)); err != nil {
+ if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil, nil)); err != nil {
t.Errorf("test in hash mode without snapshotter failed: %v", err)
return
}
- if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil)); err != nil {
+ if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil, nil)); err != nil {
t.Errorf("test in hash mode with snapshotter failed: %v", err)
return
}
- if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil)); err != nil {
+ if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil, nil)); err != nil {
t.Errorf("test in path mode without snapshotter failed: %v", err)
return
}
- if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil)); err != nil {
+ if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil, nil)); err != nil {
t.Errorf("test in path mode with snapshotter failed: %v", err)
return
}
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index d7409f80005a..e0130be48a0b 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -108,7 +108,7 @@ type btHeaderMarshaling struct {
ExcessBlobGas *math.HexOrDecimal64
}
-func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger) error {
+func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger, postCheck func(error, *core.BlockChain)) (result error) {
config, ok := Forks[t.json.Network]
if !ok {
return UnsupportedForkError{t.json.Network}
@@ -116,7 +116,9 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger) er
// import pre accounts & construct test genesis block & state root
var (
db = rawdb.NewMemoryDatabase()
- tconf = &trie.Config{}
+ tconf = &trie.Config{
+ Preimages: true,
+ }
)
if scheme == rawdb.PathScheme {
tconf.PathDB = pathdb.Defaults
@@ -141,7 +143,7 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger) er
// Wrap the original engine within the beacon-engine
engine := beacon.New(ethash.NewFaker())
- cache := &core.CacheConfig{TrieCleanLimit: 0, StateScheme: scheme}
+ cache := &core.CacheConfig{TrieCleanLimit: 0, StateScheme: scheme, Preimages: true}
if snapshotter {
cache.SnapshotLimit = 1
cache.SnapshotWait = true
@@ -158,6 +160,11 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger) er
if err != nil {
return err
}
+ // Import succeeded: regardless of whether the _test_ succeeds or not, schedule
+ // the post-check to run
+ if postCheck != nil {
+ defer postCheck(result, chain)
+ }
cmlast := chain.CurrentBlock().Hash()
if common.Hash(t.json.BestBlock) != cmlast {
return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
From 248dc50ee81310a2ad45d52f091c70b6c1ecaa35 Mon Sep 17 00:00:00 2001
From: Shivam Sandbhor
Date: Tue, 28 Nov 2023 19:19:08 +0530
Subject: [PATCH 042/103] ethereum: remove TODO comment about subscription
(#28609)
---
interfaces.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/interfaces.go b/interfaces.go
index eb9af60076e0..c4948191d188 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -29,8 +29,6 @@ import (
// NotFound is returned by API methods if the requested item does not exist.
var NotFound = errors.New("not found")
-// TODO: move subscription to package event
-
// Subscription represents an event subscription where events are
// delivered on a data channel.
type Subscription interface {
From bbc5db840553a39141e1a8630181ff37a9cc85d8 Mon Sep 17 00:00:00 2001
From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com>
Date: Tue, 28 Nov 2023 17:30:35 +0330
Subject: [PATCH 043/103] eth/tracers/js: fix type inconsistencies (#28488)
This change fixes two type-inconsistencies in the JS tracer:
- In most places we return byte arrays as a `Uint8Array` to the tracer. However it seems we missed doing the conversion for `ctx` fields which are passed to the tracer during `result`. They are passed as simple arrays. I think Uint8Arrays are more suitable and we should change this inconsistency. Note: this will be a breaking-change. But I believe the effect is small. If we look at our tracers we see that these fields (`ctx.from`, `ctx.to`, etc.) are used in 2 ways. Passed to `toHex` which takes both array or buffer. Or the length was measured which is the same for both types.
- The `slice` taking in `int, int` params versus `memory.slice` taking `int64, int64` params. I suggest changing `slice` types to `int64`. This should have no effect almost in any case.
---
eth/tracers/js/goja.go | 58 +++++++++++++++++++++++++++++++++---------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index d22d140988fe..07c138bae47b 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -142,19 +142,29 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
vm: vm,
ctx: make(map[string]goja.Value),
}
+
+ t.setTypeConverters()
+ t.setBuiltinFunctions()
+
if ctx == nil {
ctx = new(tracers.Context)
}
if ctx.BlockHash != (common.Hash{}) {
- t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
+ blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes())
+ if err != nil {
+ return nil, err
+ }
+ t.ctx["blockHash"] = blockHash
if ctx.TxHash != (common.Hash{}) {
t.ctx["txIndex"] = vm.ToValue(ctx.TxIndex)
- t.ctx["txHash"] = vm.ToValue(ctx.TxHash.Bytes())
+ txHash, err := t.toBuf(vm, ctx.TxHash.Bytes())
+ if err != nil {
+ return nil, err
+ }
+ t.ctx["txHash"] = txHash
}
}
- t.setTypeConverters()
- t.setBuiltinFunctions()
ret, err := vm.RunString("(" + code + ")")
if err != nil {
return nil, err
@@ -224,6 +234,10 @@ func (t *jsTracer) CaptureTxEnd(restGas uint64) {
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ cancel := func(err error) {
+ t.err = err
+ t.env.Cancel()
+ }
t.env = env
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
t.dbValue = db.setupObject()
@@ -232,19 +246,34 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
} else {
t.ctx["type"] = t.vm.ToValue("CALL")
}
- t.ctx["from"] = t.vm.ToValue(from.Bytes())
- t.ctx["to"] = t.vm.ToValue(to.Bytes())
- t.ctx["input"] = t.vm.ToValue(input)
+ fromVal, err := t.toBuf(t.vm, from.Bytes())
+ if err != nil {
+ cancel(err)
+ return
+ }
+ t.ctx["from"] = fromVal
+ toVal, err := t.toBuf(t.vm, to.Bytes())
+ if err != nil {
+ cancel(err)
+ return
+ }
+ t.ctx["to"] = toVal
+ inputVal, err := t.toBuf(t.vm, input)
+ if err != nil {
+ cancel(err)
+ return
+ }
+ t.ctx["input"] = inputVal
t.ctx["gas"] = t.vm.ToValue(t.gasLimit)
gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String())
if err != nil {
- t.err = err
+ cancel(err)
return
}
t.ctx["gasPrice"] = gasPriceBig
valueBig, err := t.toBig(t.vm, value.String())
if err != nil {
- t.err = err
+ cancel(err)
return
}
t.ctx["value"] = valueBig
@@ -293,10 +322,15 @@ func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
- t.ctx["output"] = t.vm.ToValue(output)
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
+ outputVal, err := t.toBuf(t.vm, output)
+ if err != nil {
+ t.err = err
+ return
+ }
+ t.ctx["output"] = outputVal
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
@@ -465,13 +499,13 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return false
})
- vm.Set("slice", func(slice goja.Value, start, end int) goja.Value {
+ vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value {
b, err := t.fromBuf(vm, slice, false)
if err != nil {
vm.Interrupt(err)
return nil
}
- if start < 0 || start > end || end > len(b) {
+ if start < 0 || start > end || end > int64(len(b)) {
vm.Interrupt(fmt.Sprintf("Tracer accessed out of bound memory: available %d, offset %d, size %d", len(b), start, end-start))
return nil
}
From 34dcd749355f161f1ed7efe9e59c6c9d13402ff6 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 28 Nov 2023 19:16:50 +0100
Subject: [PATCH 044/103] crypto/secp256k1: fix 32-bit tests when CGO_ENABLED=0
(#28602)
---
crypto/secp256k1/secp256_test.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index ef2a3a3790b4..74408d06d2bf 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
+//go:build !gofuzz && cgo
+// +build !gofuzz,cgo
+
package secp256k1
import (
From e0c7ad01abb325e443e63ca15e09282bcd1b6909 Mon Sep 17 00:00:00 2001
From: Jakub Freebit <49676311+jakub-freebit@users.noreply.github.com>
Date: Wed, 29 Nov 2023 05:29:00 +0900
Subject: [PATCH 045/103] consensus: verify the nonexistence of shanghai- and
cancun-specific header fields (#28605)
---
consensus/clique/clique.go | 22 ++++++++++++++++++++++
consensus/ethash/consensus.go | 22 ++++++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index f708050abd13..c693189ea5ef 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -302,9 +302,22 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
if chain.Config().IsShanghai(header.Number, header.Time) {
return errors.New("clique does not support shanghai fork")
}
+ // Verify the non-existence of withdrawalsHash.
+ if header.WithdrawalsHash != nil {
+ return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
+ }
if chain.Config().IsCancun(header.Number, header.Time) {
return errors.New("clique does not support cancun fork")
}
+ // Verify the non-existence of cancun-specific header fields
+ switch {
+ case header.ExcessBlobGas != nil:
+ return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
+ case header.BlobGasUsed != nil:
+ return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
+ case header.ParentBeaconRoot != nil:
+ return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
+ }
// All basic checks passed, verify cascading fields
return c.verifyCascadingFields(chain, header, parents)
}
@@ -753,6 +766,15 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
if header.WithdrawalsHash != nil {
panic("unexpected withdrawal hash value in clique")
}
+ if header.ExcessBlobGas != nil {
+ panic("unexpected excess blob gas value in clique")
+ }
+ if header.BlobGasUsed != nil {
+ panic("unexpected blob gas used value in clique")
+ }
+ if header.ParentBeaconRoot != nil {
+ panic("unexpected parent beacon root value in clique")
+ }
if err := rlp.Encode(w, enc); err != nil {
panic("can't encode: " + err.Error())
}
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 8eb9863da1e2..130dfdf213bf 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -266,9 +266,22 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
if chain.Config().IsShanghai(header.Number, header.Time) {
return errors.New("ethash does not support shanghai fork")
}
+ // Verify the non-existence of withdrawalsHash.
+ if header.WithdrawalsHash != nil {
+ return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
+ }
if chain.Config().IsCancun(header.Number, header.Time) {
return errors.New("ethash does not support cancun fork")
}
+ // Verify the non-existence of cancun-specific header fields
+ switch {
+ case header.ExcessBlobGas != nil:
+ return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
+ case header.BlobGasUsed != nil:
+ return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
+ case header.ParentBeaconRoot != nil:
+ return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
+ }
// Add some fake checks for tests
if ethash.fakeDelay != nil {
time.Sleep(*ethash.fakeDelay)
@@ -533,6 +546,15 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
if header.WithdrawalsHash != nil {
panic("withdrawal hash set on ethash")
}
+ if header.ExcessBlobGas != nil {
+ panic("excess blob gas set on ethash")
+ }
+ if header.BlobGasUsed != nil {
+ panic("blob gas used set on ethash")
+ }
+ if header.ParentBeaconRoot != nil {
+ panic("parent beacon root set on ethash")
+ }
rlp.Encode(hasher, enc)
hasher.Sum(hash[:0])
return hash
From 61b844f2b2cf6b9233a5bcd43c68d9be6f7392b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?=
Date: Tue, 28 Nov 2023 22:31:47 +0200
Subject: [PATCH 046/103] eth/gasestimator: allow slight estimation error in
favor of less iterations (#28618)
* eth/gasestimator: early exit for plain transfer and error allowance
* core, eth/gasestimator: hard guess at a possible required gas
* internal/ethapi: update estimation tests with the error ratio
* eth/gasestimator: I hate you linter
* graphql: fix gas estimation test
---------
Co-authored-by: Oren
---
core/state_transition.go | 23 +++++++++-------
eth/gasestimator/gasestimator.go | 45 ++++++++++++++++++++++++++++++--
graphql/graphql_test.go | 2 +-
internal/ethapi/api.go | 13 ++++++---
internal/ethapi/api_test.go | 2 +-
5 files changed, 68 insertions(+), 17 deletions(-)
diff --git a/core/state_transition.go b/core/state_transition.go
index 612fdd781379..540f63fda7ea 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -32,9 +32,10 @@ import (
// ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not.
type ExecutionResult struct {
- UsedGas uint64 // Total used gas but include the refunded gas
- Err error // Any error encountered during the execution(listed in core/vm/errors.go)
- ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
+ UsedGas uint64 // Total used gas, not including the refunded gas
+ RefundedGas uint64 // Total gas refunded after execution
+ Err error // Any error encountered during the execution(listed in core/vm/errors.go)
+ ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
}
// Unwrap returns the internal evm error which allows us for further
@@ -419,12 +420,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value)
}
+ var gasRefund uint64
if !rules.IsLondon {
// Before EIP-3529: refunds were capped to gasUsed / 2
- st.refundGas(params.RefundQuotient)
+ gasRefund = st.refundGas(params.RefundQuotient)
} else {
// After EIP-3529: refunds are capped to gasUsed / 5
- st.refundGas(params.RefundQuotientEIP3529)
+ gasRefund = st.refundGas(params.RefundQuotientEIP3529)
}
effectiveTip := msg.GasPrice
if rules.IsLondon {
@@ -442,13 +444,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
return &ExecutionResult{
- UsedGas: st.gasUsed(),
- Err: vmerr,
- ReturnData: ret,
+ UsedGas: st.gasUsed(),
+ RefundedGas: gasRefund,
+ Err: vmerr,
+ ReturnData: ret,
}, nil
}
-func (st *StateTransition) refundGas(refundQuotient uint64) {
+func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
// Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient
if refund > st.state.GetRefund() {
@@ -463,6 +466,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
st.gp.AddGas(st.gasRemaining)
+
+ return refund
}
// gasUsed returns the amount of gas used up by the state transition.
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
index 3e74b5b08b5c..4a8e20dfed74 100644
--- a/eth/gasestimator/gasestimator.go
+++ b/eth/gasestimator/gasestimator.go
@@ -42,6 +42,8 @@ type Options struct {
Chain core.ChainContext // Chain context to access past block hashes
Header *types.Header // Header defining the block context to execute in
State *state.StateDB // Pre-state on top of which to estimate the gas
+
+ ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination
}
// Estimate returns the lowest possible gas limit that allows the transaction to
@@ -86,16 +88,28 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
if transfer == nil {
transfer = new(big.Int)
}
- log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
+ log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
// Recap the highest gas allowance with specified gascap.
if gasCap != 0 && hi > gasCap {
- log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
+ log.Debug("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
hi = gasCap
}
+ // If the transaction is a plain value transfer, short circuit estimation and
+ // directly try 21000. Returning 21000 without any execution is dangerous as
+ // some tx field combos might bump the price up even for plain transfers (e.g.
+ // unused access list items). Ever so slightly wasteful, but safer overall.
+ if len(call.Data) == 0 {
+ if call.To != nil && opts.State.GetCodeSize(*call.To) == 0 {
+ failed, _, err := execute(ctx, call, opts, params.TxGas)
+ if !failed && err == nil {
+ return params.TxGas, nil, nil
+ }
+ }
+ }
// We first execute the transaction at the highest allowable gas limit, since if this fails we
// can return error immediately.
failed, result, err := execute(ctx, call, opts, hi)
@@ -115,8 +129,35 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// limit for these cases anyway.
lo = result.UsedGas - 1
+ // There's a fairly high chance for the transaction to execute successfully
+ // with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
+ // check that gas amount and use as a limit for the binary search.
+ optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63
+ if optimisticGasLimit < hi {
+ failed, _, err = execute(ctx, call, opts, optimisticGasLimit)
+ if err != nil {
+ // This should not happen under normal conditions since if we make it this far the
+ // transaction had run without error at least once before.
+ log.Error("Execution error in estimate gas", "err", err)
+ return 0, nil, err
+ }
+ if failed {
+ lo = optimisticGasLimit
+ } else {
+ hi = optimisticGasLimit
+ }
+ }
// Binary search for the smallest gas limit that allows the tx to execute successfully.
for lo+1 < hi {
+ if opts.ErrorRatio > 0 {
+ // It is a bit pointless to return a perfect estimation, as changing
+ // network conditions require the caller to bump it up anyway. Since
+ // wallets tend to use 20-25% bump, allowing a small approximation
+ // error is fine (as long as it's upwards).
+ if float64(hi-lo)/float64(hi) < opts.ErrorRatio {
+ break
+ }
+ }
mid := (hi + lo) / 2
if mid > lo*2 {
// Most txs don't need much higher gas limit than their gas used, and most txs don't
diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go
index a83d6bbd467e..f91229d01597 100644
--- a/graphql/graphql_test.go
+++ b/graphql/graphql_test.go
@@ -139,7 +139,7 @@ func TestGraphQLBlockSerialization(t *testing.T) {
// should return `estimateGas` as decimal
{
body: `{"query": "{block{ estimateGas(data:{}) }}"}`,
- want: `{"data":{"block":{"estimateGas":"0xcf08"}}}`,
+ want: `{"data":{"block":{"estimateGas":"0xd221"}}}`,
code: 200,
},
// should return `status` as decimal
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index f322132769af..c0b28e4b69ea 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -51,6 +51,10 @@ import (
"github.com/tyler-smith/go-bip39"
)
+// estimateGasErrorRatio is the amount of overestimation eth_estimateGas is
+// allowed to produce in order to speed up calculations.
+const estimateGasErrorRatio = 0.015
+
// EthereumAPI provides an API to access Ethereum related information.
type EthereumAPI struct {
b Backend
@@ -1189,10 +1193,11 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
// Construct the gas estimator option from the user input
opts := &gasestimator.Options{
- Config: b.ChainConfig(),
- Chain: NewChainContext(ctx, b),
- Header: header,
- State: state,
+ Config: b.ChainConfig(),
+ Chain: NewChainContext(ctx, b),
+ Header: header,
+ State: state,
+ ErrorRatio: estimateGasErrorRatio,
}
// Run the gas estimation andwrap any revertals into a custom return
call, err := args.ToMessage(gasCap, header.BaseFee)
diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go
index 9b08fd8d42c8..c2490ac70315 100644
--- a/internal/ethapi/api_test.go
+++ b/internal/ethapi/api_test.go
@@ -735,7 +735,7 @@ func TestEstimateGas(t *testing.T) {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
- if uint64(result) != tc.want {
+ if float64(result) > float64(tc.want)*(1+estimateGasErrorRatio) {
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, uint64(result), tc.want)
}
}
From 28e73717016cdc9ebdb5fdb3474cfbd3bd2d2524 Mon Sep 17 00:00:00 2001
From: jwasinger
Date: Wed, 29 Nov 2023 15:33:50 +0800
Subject: [PATCH 047/103] all: replace log15 with slog (#28187)
This PR replaces Geth's logger package (a fork of [log15](https://github.com/inconshreveable/log15)) with an implementation using slog, a logging library included as part of the Go standard library as of Go1.21.
Main changes are as follows:
* removes any log handlers that were unused in the Geth codebase.
* Json, logfmt, and terminal formatters are now slog handlers.
* Verbosity level constants are changed to match slog constant values. Internal translation is done to make this opaque to the user and backwards compatible with existing `--verbosity` and `--vmodule` options.
* `--log.backtraceat` and `--log.debug` are removed.
The external-facing API is largely the same as the existing Geth logger. Logger method signatures remain unchanged.
A small semantic difference is that a `Handler` can only be set once per `Logger` and not changed dynamically. This just means that a new logger must be instantiated every time the handler of the root logger is changed.
----
For users of the `go-ethereum/log` module. If you were using this module for your own project, you will need to change the initialization. If you previously did
```golang
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
```
You now instead need to do
```golang
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
```
See more about reasoning here: https://github.com/ethereum/go-ethereum/issues/28558#issuecomment-1820606613
---
cmd/abigen/main.go | 2 +-
cmd/bootnode/main.go | 7 +-
cmd/clef/main.go | 3 +-
cmd/devp2p/runtest.go | 2 +-
cmd/evm/internal/t8ntool/block.go | 7 +-
cmd/evm/internal/t8ntool/transaction.go | 7 +-
cmd/evm/internal/t8ntool/transition.go | 8 +-
cmd/geth/logging_test.go | 48 ++
cmd/geth/logtestcmd_active.go | 5 +-
cmd/geth/main.go | 2 +
cmd/geth/testdata/logging/logtest-json.txt | 100 ++--
cmd/geth/testdata/logging/logtest-logfmt.txt | 102 ++--
.../testdata/logging/logtest-terminal.txt | 102 ++--
cmd/utils/flags.go | 7 +
cmd/utils/flags_legacy.go | 14 +
core/state/snapshot/generate_test.go | 2 +-
core/txpool/blobpool/blobpool_test.go | 10 +-
eth/catalyst/api_test.go | 2 +-
eth/downloader/queue_test.go | 4 +-
go.mod | 17 +-
go.sum | 34 +-
internal/debug/api.go | 9 +-
internal/debug/flags.go | 116 +++--
internal/testlog/testlog.go | 118 +++--
log/CONTRIBUTORS | 11 -
log/LICENSE | 13 -
log/README.md | 77 ---
log/README_ETHEREUM.md | 5 -
log/doc.go | 327 ------------
log/format.go | 337 +++----------
log/handler.go | 470 ++++++------------
log/handler_glog.go | 151 +++---
log/logger.go | 358 ++++++-------
log/logger_test.go | 52 +-
log/root.go | 49 +-
log/syslog.go | 58 ---
miner/stress/clique/main.go | 2 +-
p2p/discover/v4_udp_test.go | 7 +-
p2p/discover/v5_udp_test.go | 7 +-
p2p/simulations/adapters/exec.go | 20 +-
p2p/simulations/adapters/types.go | 5 +-
p2p/simulations/examples/ping-pong.go | 2 +-
p2p/simulations/http_test.go | 4 +-
signer/core/auditlog.go | 9 +-
signer/storage/aes_gcm_storage_test.go | 5 +-
45 files changed, 932 insertions(+), 1765 deletions(-)
delete mode 100644 log/CONTRIBUTORS
delete mode 100644 log/LICENSE
delete mode 100644 log/README.md
delete mode 100644 log/README_ETHEREUM.md
delete mode 100644 log/doc.go
delete mode 100644 log/syslog.go
diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go
index 221f45c07849..0149dec52772 100644
--- a/cmd/abigen/main.go
+++ b/cmd/abigen/main.go
@@ -232,7 +232,7 @@ func abigen(c *cli.Context) error {
}
func main() {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go
index 5c1635de39a1..1660b43b74b6 100644
--- a/cmd/bootnode/main.go
+++ b/cmd/bootnode/main.go
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
+ "golang.org/x/exp/slog"
)
func main() {
@@ -52,10 +53,10 @@ func main() {
)
flag.Parse()
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(*verbosity))
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(slog.Level(*verbosity))
glogger.Vmodule(*vmodule)
- log.Root().SetHandler(glogger)
+ log.SetDefault(log.NewLogger(glogger))
natm, err := nat.Parse(*natdesc)
if err != nil {
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index 63f34effb738..27b7b707714d 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -57,6 +57,7 @@ import (
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
+ "golang.org/x/exp/slog"
)
const legalWarning = `
@@ -492,7 +493,7 @@ func initialize(c *cli.Context) error {
if usecolor {
output = colorable.NewColorable(logOutput)
}
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, slog.Level(c.Int(logLevelFlag.Name)), usecolor)))
return nil
}
diff --git a/cmd/devp2p/runtest.go b/cmd/devp2p/runtest.go
index f72aa91119c5..76af53ee4dd9 100644
--- a/cmd/devp2p/runtest.go
+++ b/cmd/devp2p/runtest.go
@@ -54,7 +54,7 @@ func runTests(ctx *cli.Context, tests []utesting.Test) error {
}
// Disable logging unless explicitly enabled.
if !ctx.IsSet("verbosity") && !ctx.IsSet("vmodule") {
- log.Root().SetHandler(log.DiscardHandler())
+ log.SetDefault(log.NewLogger(log.DiscardHandler()))
}
// Run the tests.
var run = utesting.RunTests
diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go
index 5c0e28e284c0..429ae12c54fb 100644
--- a/cmd/evm/internal/t8ntool/block.go
+++ b/cmd/evm/internal/t8ntool/block.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/urfave/cli/v2"
+ "golang.org/x/exp/slog"
)
//go:generate go run github.com/fjl/gencodec -type header -field-override headerMarshaling -out gen_header.go
@@ -216,9 +217,9 @@ func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
// BuildBlock constructs a block from the given inputs.
func BuildBlock(ctx *cli.Context) error {
// Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
- log.Root().SetHandler(glogger)
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
+ log.SetDefault(log.NewLogger(glogger))
baseDir, err := createBasedir(ctx)
if err != nil {
diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go
index 03a2e2eb99c2..e1c98c7fe265 100644
--- a/cmd/evm/internal/t8ntool/transaction.go
+++ b/cmd/evm/internal/t8ntool/transaction.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2"
+ "golang.org/x/exp/slog"
)
type result struct {
@@ -66,9 +67,9 @@ func (r *result) MarshalJSON() ([]byte, error) {
func Transaction(ctx *cli.Context) error {
// Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
- log.Root().SetHandler(glogger)
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
+ log.SetDefault(log.NewLogger(glogger))
var (
err error
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index d517592e5cfb..a01dfedab9f6 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -24,6 +24,8 @@ import (
"os"
"path"
+ "golang.org/x/exp/slog"
+
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
@@ -81,9 +83,9 @@ type input struct {
func Transition(ctx *cli.Context) error {
// Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
- log.Root().SetHandler(glogger)
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
+ log.SetDefault(log.NewLogger(glogger))
var (
err error
diff --git a/cmd/geth/logging_test.go b/cmd/geth/logging_test.go
index 69fe0fadf7ee..50991554b427 100644
--- a/cmd/geth/logging_test.go
+++ b/cmd/geth/logging_test.go
@@ -28,6 +28,7 @@ import (
"os/exec"
"strings"
"testing"
+ "encoding/json"
"github.com/ethereum/go-ethereum/internal/reexec"
)
@@ -98,6 +99,53 @@ func testConsoleLogging(t *testing.T, format string, tStart, tEnd int) {
}
}
+func TestJsonLogging(t *testing.T) {
+ t.Parallel()
+ haveB, err := runSelf("--log.format", "json", "logtest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ readFile, err := os.Open("testdata/logging/logtest-json.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ wantLines := split(readFile)
+ haveLines := split(bytes.NewBuffer(haveB))
+ for i, wantLine := range wantLines {
+ if i > len(haveLines)-1 {
+ t.Fatalf("format %v, line %d missing, want:%v", "json", i, wantLine)
+ }
+ haveLine := haveLines[i]
+ for strings.Contains(haveLine, "Unknown config environment variable") {
+ // This can happen on CI runs. Drop it.
+ haveLines = append(haveLines[:i], haveLines[i+1:]...)
+ haveLine = haveLines[i]
+ }
+ var have, want []byte
+ {
+ var h map[string]any
+ if err := json.Unmarshal([]byte(haveLine), &h); err != nil {
+ t.Fatal(err)
+ }
+ h["t"] = "xxx"
+ have, _ = json.Marshal(h)
+ }
+ {
+ var w map[string]any
+ if err := json.Unmarshal([]byte(wantLine), &w); err != nil {
+ t.Fatal(err)
+ }
+ w["t"] = "xxx"
+ want, _ = json.Marshal(w)
+ }
+ if !bytes.Equal(have, want) {
+ // show an intelligent diff
+ t.Logf(nicediff(have, want))
+ t.Errorf("file content wrong")
+ }
+ }
+}
+
func TestVmodule(t *testing.T) {
t.Parallel()
checkOutput := func(level int, want, wantNot string) {
diff --git a/cmd/geth/logtestcmd_active.go b/cmd/geth/logtestcmd_active.go
index 0632f9ca4bac..0ca4cc621dc0 100644
--- a/cmd/geth/logtestcmd_active.go
+++ b/cmd/geth/logtestcmd_active.go
@@ -26,6 +26,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log"
"github.com/holiman/uint256"
"github.com/urfave/cli/v2"
@@ -49,7 +50,9 @@ func (c customQuotedStringer) String() string {
// logTest is an entry point which spits out some logs. This is used by testing
// to verify expected outputs
func logTest(ctx *cli.Context) error {
- log.ResetGlobalState()
+ // clear field padding map
+ debug.ResetLogging()
+
{ // big.Int
ba, _ := new(big.Int).SetString("111222333444555678999", 10) // "111,222,333,444,555,678,999"
bb, _ := new(big.Int).SetString("-111222333444555678999", 10) // "-111,222,333,444,555,678,999"
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index e5a17e45cf7b..0d5939bd2032 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -144,6 +144,8 @@ var (
utils.GpoMaxGasPriceFlag,
utils.GpoIgnoreGasPriceFlag,
configFileFlag,
+ utils.LogDebugFlag,
+ utils.LogBacktraceAtFlag,
}, utils.NetworkFlags, utils.DatabaseFlags)
rpcFlags = []cli.Flag{
diff --git a/cmd/geth/testdata/logging/logtest-json.txt b/cmd/geth/testdata/logging/logtest-json.txt
index 6cb2476dbd83..bdc1ae4de6c4 100644
--- a/cmd/geth/testdata/logging/logtest-json.txt
+++ b/cmd/geth/testdata/logging/logtest-json.txt
@@ -1,49 +1,51 @@
-{"111,222,333,444,555,678,999":"111222333444555678999","lvl":"info","msg":"big.Int","t":"2023-11-09T08:33:19.464383209+01:00"}
-{"-111,222,333,444,555,678,999":"-111222333444555678999","lvl":"info","msg":"-big.Int","t":"2023-11-09T08:33:19.46455928+01:00"}
-{"11,122,233,344,455,567,899,900":"11122233344455567899900","lvl":"info","msg":"big.Int","t":"2023-11-09T08:33:19.464582073+01:00"}
-{"-11,122,233,344,455,567,899,900":"-11122233344455567899900","lvl":"info","msg":"-big.Int","t":"2023-11-09T08:33:19.464594846+01:00"}
-{"111,222,333,444,555,678,999":"0x607851afc94ca2517","lvl":"info","msg":"uint256","t":"2023-11-09T08:33:19.464607873+01:00"}
-{"11,122,233,344,455,567,899,900":"0x25aeffe8aaa1ef67cfc","lvl":"info","msg":"uint256","t":"2023-11-09T08:33:19.464694639+01:00"}
-{"1,000,000":1000000,"lvl":"info","msg":"int64","t":"2023-11-09T08:33:19.464708835+01:00"}
-{"-1,000,000":-1000000,"lvl":"info","msg":"int64","t":"2023-11-09T08:33:19.464725054+01:00"}
-{"9,223,372,036,854,775,807":9223372036854775807,"lvl":"info","msg":"int64","t":"2023-11-09T08:33:19.464735773+01:00"}
-{"-9,223,372,036,854,775,808":-9223372036854775808,"lvl":"info","msg":"int64","t":"2023-11-09T08:33:19.464744532+01:00"}
-{"1,000,000":1000000,"lvl":"info","msg":"uint64","t":"2023-11-09T08:33:19.464752807+01:00"}
-{"18,446,744,073,709,551,615":18446744073709551615,"lvl":"info","msg":"uint64","t":"2023-11-09T08:33:19.464779296+01:00"}
-{"key":"special \r\n\t chars","lvl":"info","msg":"Special chars in value","t":"2023-11-09T08:33:19.464794181+01:00"}
-{"lvl":"info","msg":"Special chars in key","special \n\t chars":"value","t":"2023-11-09T08:33:19.464827197+01:00"}
-{"lvl":"info","msg":"nospace","nospace":"nospace","t":"2023-11-09T08:33:19.464841118+01:00"}
-{"lvl":"info","msg":"with space","t":"2023-11-09T08:33:19.464862818+01:00","with nospace":"with nospace"}
-{"key":"\u001b[1G\u001b[K\u001b[1A","lvl":"info","msg":"Bash escapes in value","t":"2023-11-09T08:33:19.464876802+01:00"}
-{"\u001b[1G\u001b[K\u001b[1A":"value","lvl":"info","msg":"Bash escapes in key","t":"2023-11-09T08:33:19.464885416+01:00"}
-{"key":"value","lvl":"info","msg":"Bash escapes in message \u001b[1G\u001b[K\u001b[1A end","t":"2023-11-09T08:33:19.464906946+01:00"}
-{"\u001b[35mColored\u001b[0m[":"\u001b[35mColored\u001b[0m[","lvl":"info","msg":"\u001b[35mColored\u001b[0m[","t":"2023-11-09T08:33:19.464921455+01:00"}
-{"2562047h47m16.854s":"2562047h47m16.854s","lvl":"info","msg":"Custom Stringer value","t":"2023-11-09T08:33:19.464943893+01:00"}
-{"key":"lazy value","lvl":"info","msg":"Lazy evaluation of value","t":"2023-11-09T08:33:19.465013552+01:00"}
-{"lvl":"info","msg":"A message with wonky 💩 characters","t":"2023-11-09T08:33:19.465069437+01:00"}
-{"lvl":"info","msg":"A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩","t":"2023-11-09T08:33:19.465083053+01:00"}
-{"lvl":"info","msg":"A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above","t":"2023-11-09T08:33:19.465104289+01:00"}
-{"false":"false","lvl":"info","msg":"boolean","t":"2023-11-09T08:33:19.465117185+01:00","true":"true"}
-{"foo":"beta","lvl":"info","msg":"repeated-key 1","t":"2023-11-09T08:33:19.465143425+01:00"}
-{"lvl":"info","msg":"repeated-key 2","t":"2023-11-09T08:33:19.465156323+01:00","xx":"longer"}
-{"lvl":"info","msg":"log at level info","t":"2023-11-09T08:33:19.465193158+01:00"}
-{"lvl":"warn","msg":"log at level warn","t":"2023-11-09T08:33:19.465228964+01:00"}
-{"lvl":"eror","msg":"log at level error","t":"2023-11-09T08:33:19.465240352+01:00"}
-{"a":"aligned left","bar":"short","lvl":"info","msg":"test","t":"2023-11-09T08:33:19.465247226+01:00"}
-{"a":1,"bar":"a long message","lvl":"info","msg":"test","t":"2023-11-09T08:33:19.465269028+01:00"}
-{"a":"aligned right","bar":"short","lvl":"info","msg":"test","t":"2023-11-09T08:33:19.465313611+01:00"}
-{"lvl":"info","msg":"The following logs should align so that the key-fields make 5 columns","t":"2023-11-09T08:33:19.465328188+01:00"}
-{"gas":1123123,"hash":"0x0000000000000000000000000000000000000000000000000000000000001234","lvl":"info","msg":"Inserted known block","number":1012,"other":"first","t":"2023-11-09T08:33:19.465350507+01:00","txs":200}
-{"gas":1123,"hash":"0x0000000000000000000000000000000000000000000000000000000000001235","lvl":"info","msg":"Inserted new block","number":1,"other":"second","t":"2023-11-09T08:33:19.465387952+01:00","txs":2}
-{"gas":1,"hash":"0x0000000000000000000000000000000000000000000000000000000000012322","lvl":"info","msg":"Inserted known block","number":99,"other":"third","t":"2023-11-09T08:33:19.465406687+01:00","txs":10}
-{"gas":99,"hash":"0x0000000000000000000000000000000000000000000000000000000000001234","lvl":"warn","msg":"Inserted known block","number":1012,"other":"fourth","t":"2023-11-09T08:33:19.465433025+01:00","txs":200}
-{"\u003cnil\u003e":"\u003cnil\u003e","lvl":"info","msg":"(*big.Int)(nil)","t":"2023-11-09T08:33:19.465450283+01:00"}
-{"\u003cnil\u003e":"nil","lvl":"info","msg":"(*uint256.Int)(nil)","t":"2023-11-09T08:33:19.465472953+01:00"}
-{"lvl":"info","msg":"(fmt.Stringer)(nil)","res":"\u003cnil\u003e","t":"2023-11-09T08:33:19.465538633+01:00"}
-{"lvl":"info","msg":"nil-concrete-stringer","res":"nil","t":"2023-11-09T08:33:19.465552355+01:00"}
-{"lvl":"info","msg":"error(nil) ","res":"\u003cnil\u003e","t":"2023-11-09T08:33:19.465601029+01:00"}
-{"lvl":"info","msg":"nil-concrete-error","res":"","t":"2023-11-09T08:33:19.46561622+01:00"}
-{"lvl":"info","msg":"nil-custom-struct","res":"\u003cnil\u003e","t":"2023-11-09T08:33:19.465638888+01:00"}
-{"lvl":"info","msg":"raw nil","res":"\u003cnil\u003e","t":"2023-11-09T08:33:19.465673664+01:00"}
-{"lvl":"info","msg":"(*uint64)(nil)","res":"\u003cnil\u003e","t":"2023-11-09T08:33:19.465700264+01:00"}
-{"level":"level","lvl":"lvl","msg":"msg","t":"t","time":"time"}
+{"t":"2023-11-22T15:42:00.407963+08:00","lvl":"info","msg":"big.Int","111,222,333,444,555,678,999":"111222333444555678999"}
+{"t":"2023-11-22T15:42:00.408084+08:00","lvl":"info","msg":"-big.Int","-111,222,333,444,555,678,999":"-111222333444555678999"}
+{"t":"2023-11-22T15:42:00.408092+08:00","lvl":"info","msg":"big.Int","11,122,233,344,455,567,899,900":"11122233344455567899900"}
+{"t":"2023-11-22T15:42:00.408097+08:00","lvl":"info","msg":"-big.Int","-11,122,233,344,455,567,899,900":"-11122233344455567899900"}
+{"t":"2023-11-22T15:42:00.408127+08:00","lvl":"info","msg":"uint256","111,222,333,444,555,678,999":"111222333444555678999"}
+{"t":"2023-11-22T15:42:00.408133+08:00","lvl":"info","msg":"uint256","11,122,233,344,455,567,899,900":"11122233344455567899900"}
+{"t":"2023-11-22T15:42:00.408137+08:00","lvl":"info","msg":"int64","1,000,000":1000000}
+{"t":"2023-11-22T15:42:00.408145+08:00","lvl":"info","msg":"int64","-1,000,000":-1000000}
+{"t":"2023-11-22T15:42:00.408149+08:00","lvl":"info","msg":"int64","9,223,372,036,854,775,807":9223372036854775807}
+{"t":"2023-11-22T15:42:00.408153+08:00","lvl":"info","msg":"int64","-9,223,372,036,854,775,808":-9223372036854775808}
+{"t":"2023-11-22T15:42:00.408156+08:00","lvl":"info","msg":"uint64","1,000,000":1000000}
+{"t":"2023-11-22T15:42:00.40816+08:00","lvl":"info","msg":"uint64","18,446,744,073,709,551,615":18446744073709551615}
+{"t":"2023-11-22T15:42:00.408164+08:00","lvl":"info","msg":"Special chars in value","key":"special \r\n\t chars"}
+{"t":"2023-11-22T15:42:00.408167+08:00","lvl":"info","msg":"Special chars in key","special \n\t chars":"value"}
+{"t":"2023-11-22T15:42:00.408171+08:00","lvl":"info","msg":"nospace","nospace":"nospace"}
+{"t":"2023-11-22T15:42:00.408174+08:00","lvl":"info","msg":"with space","with nospace":"with nospace"}
+{"t":"2023-11-22T15:42:00.408178+08:00","lvl":"info","msg":"Bash escapes in value","key":"\u001b[1G\u001b[K\u001b[1A"}
+{"t":"2023-11-22T15:42:00.408182+08:00","lvl":"info","msg":"Bash escapes in key","\u001b[1G\u001b[K\u001b[1A":"value"}
+{"t":"2023-11-22T15:42:00.408186+08:00","lvl":"info","msg":"Bash escapes in message \u001b[1G\u001b[K\u001b[1A end","key":"value"}
+{"t":"2023-11-22T15:42:00.408194+08:00","lvl":"info","msg":"\u001b[35mColored\u001b[0m[","\u001b[35mColored\u001b[0m[":"\u001b[35mColored\u001b[0m["}
+{"t":"2023-11-22T15:42:00.408197+08:00","lvl":"info","msg":"an error message with quotes","error":"this is an 'error'"}
+{"t":"2023-11-22T15:42:00.408202+08:00","lvl":"info","msg":"Custom Stringer value","2562047h47m16.854s":"2562047h47m16.854s"}
+{"t":"2023-11-22T15:42:00.408208+08:00","lvl":"info","msg":"a custom stringer that emits quoted text","output":"output with 'quotes'"}
+{"t":"2023-11-22T15:42:00.408215+08:00","lvl":"info","msg":"Lazy evaluation of value","key":"lazy value"}
+{"t":"2023-11-22T15:42:00.408219+08:00","lvl":"info","msg":"A message with wonky 💩 characters"}
+{"t":"2023-11-22T15:42:00.408222+08:00","lvl":"info","msg":"A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"}
+{"t":"2023-11-22T15:42:00.408226+08:00","lvl":"info","msg":"A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"}
+{"t":"2023-11-22T15:42:00.408229+08:00","lvl":"info","msg":"boolean","true":true,"false":false}
+{"t":"2023-11-22T15:42:00.408234+08:00","lvl":"info","msg":"repeated-key 1","foo":"alpha","foo":"beta"}
+{"t":"2023-11-22T15:42:00.408237+08:00","lvl":"info","msg":"repeated-key 2","xx":"short","xx":"longer"}
+{"t":"2023-11-22T15:42:00.408241+08:00","lvl":"info","msg":"log at level info"}
+{"t":"2023-11-22T15:42:00.408244+08:00","lvl":"warn","msg":"log at level warn"}
+{"t":"2023-11-22T15:42:00.408247+08:00","lvl":"eror","msg":"log at level error"}
+{"t":"2023-11-22T15:42:00.408251+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned left"}
+{"t":"2023-11-22T15:42:00.408254+08:00","lvl":"info","msg":"test","bar":"a long message","a":1}
+{"t":"2023-11-22T15:42:00.408258+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned right"}
+{"t":"2023-11-22T15:42:00.408261+08:00","lvl":"info","msg":"The following logs should align so that the key-fields make 5 columns"}
+{"t":"2023-11-22T15:42:00.408275+08:00","lvl":"info","msg":"Inserted known block","number":1012,"hash":"0x0000000000000000000000000000000000000000000000000000000000001234","txs":200,"gas":1123123,"other":"first"}
+{"t":"2023-11-22T15:42:00.408281+08:00","lvl":"info","msg":"Inserted new block","number":1,"hash":"0x0000000000000000000000000000000000000000000000000000000000001235","txs":2,"gas":1123,"other":"second"}
+{"t":"2023-11-22T15:42:00.408287+08:00","lvl":"info","msg":"Inserted known block","number":99,"hash":"0x0000000000000000000000000000000000000000000000000000000000012322","txs":10,"gas":1,"other":"third"}
+{"t":"2023-11-22T15:42:00.408296+08:00","lvl":"warn","msg":"Inserted known block","number":1012,"hash":"0x0000000000000000000000000000000000000000000000000000000000001234","txs":200,"gas":99,"other":"fourth"}
+{"t":"2023-11-22T15:42:00.4083+08:00","lvl":"info","msg":"(*big.Int)(nil)","":""}
+{"t":"2023-11-22T15:42:00.408303+08:00","lvl":"info","msg":"(*uint256.Int)(nil)","":""}
+{"t":"2023-11-22T15:42:00.408311+08:00","lvl":"info","msg":"(fmt.Stringer)(nil)","res":null}
+{"t":"2023-11-22T15:42:00.408318+08:00","lvl":"info","msg":"nil-concrete-stringer","res":""}
+{"t":"2023-11-22T15:42:00.408322+08:00","lvl":"info","msg":"error(nil) ","res":null}
+{"t":"2023-11-22T15:42:00.408326+08:00","lvl":"info","msg":"nil-concrete-error","res":""}
+{"t":"2023-11-22T15:42:00.408334+08:00","lvl":"info","msg":"nil-custom-struct","res":null}
+{"t":"2023-11-22T15:42:00.40835+08:00","lvl":"info","msg":"raw nil","res":null}
+{"t":"2023-11-22T15:42:00.408354+08:00","lvl":"info","msg":"(*uint64)(nil)","res":null}
+{"t":"2023-11-22T15:42:00.408361+08:00","lvl":"info","msg":"Using keys 't', 'lvl', 'time', 'level' and 'msg'","t":"t","time":"time","lvl":"lvl","level":"level","msg":"msg"}
diff --git a/cmd/geth/testdata/logging/logtest-logfmt.txt b/cmd/geth/testdata/logging/logtest-logfmt.txt
index f097143a5561..114569e467c8 100644
--- a/cmd/geth/testdata/logging/logtest-logfmt.txt
+++ b/cmd/geth/testdata/logging/logtest-logfmt.txt
@@ -1,51 +1,51 @@
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=big.Int 111,222,333,444,555,678,999=111,222,333,444,555,678,999
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=-big.Int -111,222,333,444,555,678,999=-111,222,333,444,555,678,999
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=big.Int 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=-big.Int -11,122,233,344,455,567,899,900=-11,122,233,344,455,567,899,900
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=uint256 111,222,333,444,555,678,999=111,222,333,444,555,678,999
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=uint256 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=int64 1,000,000=1,000,000
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=int64 -1,000,000=-1,000,000
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=int64 9,223,372,036,854,775,807=9,223,372,036,854,775,807
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=int64 -9,223,372,036,854,775,808=-9,223,372,036,854,775,808
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=uint64 1,000,000=1,000,000
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=uint64 18,446,744,073,709,551,615=18,446,744,073,709,551,615
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Special chars in value" key="special \r\n\t chars"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Special chars in key" "special \n\t chars"=value
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=nospace nospace=nospace
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="with space" "with nospace"="with nospace"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in value" key="\x1b[1G\x1b[K\x1b[1A"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in key" "\x1b[1G\x1b[K\x1b[1A"=value
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="an error message with quotes" error="this is an 'error'"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Custom Stringer value" 2562047h47m16.854s=2562047h47m16.854s
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="a custom stringer that emits quoted text" output="output with 'quotes'"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A message with wonky 💩 characters"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=boolean true=true false=false
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="repeated-key 1" foo=alpha foo=beta
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="repeated-key 2" xx=short xx=longer
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="log at level info"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=warn msg="log at level warn"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=eror msg="log at level error"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=test bar=short a="aligned left"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=test bar="a long message" a=1
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=test bar=short a="aligned right"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="The following logs should align so that the key-fields make 5 columns"
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Inserted known block" number=1012 hash=0x0000000000000000000000000000000000000000000000000000000000001234 txs=200 gas=1,123,123 other=first
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Inserted new block" number=1 hash=0x0000000000000000000000000000000000000000000000000000000000001235 txs=2 gas=1123 other=second
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Inserted known block" number=99 hash=0x0000000000000000000000000000000000000000000000000000000000012322 txs=10 gas=1 other=third
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=warn msg="Inserted known block" number=1012 hash=0x0000000000000000000000000000000000000000000000000000000000001234 txs=200 gas=99 other=fourth
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=(*big.Int)(nil) =
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=(*uint256.Int)(nil) =
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=(fmt.Stringer)(nil) res=nil
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=nil-concrete-stringer res=nil
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="error(nil) " res=nil
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=nil-concrete-error res=
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=nil-custom-struct res=
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="raw nil" res=nil
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg=(*uint64)(nil) res=
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Using keys 't', 'lvl', 'time', 'level' and 'msg'" t=t time=time lvl=lvl level=level msg=msg
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=big.Int 111,222,333,444,555,678,999=111222333444555678999
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=-big.Int -111,222,333,444,555,678,999=-111222333444555678999
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=big.Int 11,122,233,344,455,567,899,900=11122233344455567899900
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=-big.Int -11,122,233,344,455,567,899,900=-11122233344455567899900
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=uint256 111,222,333,444,555,678,999=111222333444555678999
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=uint256 11,122,233,344,455,567,899,900=11122233344455567899900
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=int64 1,000,000=1000000
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=int64 -1,000,000=-1000000
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=int64 9,223,372,036,854,775,807=9223372036854775807
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=int64 -9,223,372,036,854,775,808=-9223372036854775808
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=uint64 1,000,000=1000000
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=uint64 18,446,744,073,709,551,615=18446744073709551615
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Special chars in value" key="special \r\n\t chars"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Special chars in key" "special \n\t chars"=value
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=nospace nospace=nospace
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="with space" "with nospace"="with nospace"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Bash escapes in value" key="\x1b[1G\x1b[K\x1b[1A"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Bash escapes in key" "\x1b[1G\x1b[K\x1b[1A"=value
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="an error message with quotes" error="this is an 'error'"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Custom Stringer value" 2562047h47m16.854s=2562047h47m16.854s
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="a custom stringer that emits quoted text" output="output with 'quotes'"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A message with wonky 💩 characters"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=boolean true=true false=false
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 1" foo=alpha foo=beta
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 2" xx=short xx=longer
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="log at level info"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=warn msg="log at level warn"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=eror msg="log at level error"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned left"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar="a long message" a=1
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned right"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="The following logs should align so that the key-fields make 5 columns"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Inserted known block" number=1012 hash=0x0000000000000000000000000000000000000000000000000000000000001234 txs=200 gas=1123123 other=first
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Inserted new block" number=1 hash=0x0000000000000000000000000000000000000000000000000000000000001235 txs=2 gas=1123 other=second
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Inserted known block" number=99 hash=0x0000000000000000000000000000000000000000000000000000000000012322 txs=10 gas=1 other=third
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=warn msg="Inserted known block" number=1012 hash=0x0000000000000000000000000000000000000000000000000000000000001234 txs=200 gas=99 other=fourth
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=(*big.Int)(nil) =
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=(*uint256.Int)(nil) =
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=(fmt.Stringer)(nil) res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=nil-concrete-stringer res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="error(nil) " res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=nil-concrete-error res=""
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=nil-custom-struct res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="raw nil" res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=(*uint64)(nil) res=
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Using keys 't', 'lvl', 'time', 'level' and 'msg'" t=t time=time lvl=lvl level=level msg=msg
diff --git a/cmd/geth/testdata/logging/logtest-terminal.txt b/cmd/geth/testdata/logging/logtest-terminal.txt
index 051a6267fa03..4da3f49d46a4 100644
--- a/cmd/geth/testdata/logging/logtest-terminal.txt
+++ b/cmd/geth/testdata/logging/logtest-terminal.txt
@@ -1,52 +1,52 @@
-INFO [XX-XX|XX:XX:XX.XXX] big.Int 111,222,333,444,555,678,999=111,222,333,444,555,678,999
-INFO [XX-XX|XX:XX:XX.XXX] -big.Int -111,222,333,444,555,678,999=-111,222,333,444,555,678,999
-INFO [XX-XX|XX:XX:XX.XXX] big.Int 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
-INFO [XX-XX|XX:XX:XX.XXX] -big.Int -11,122,233,344,455,567,899,900=-11,122,233,344,455,567,899,900
-INFO [XX-XX|XX:XX:XX.XXX] uint256 111,222,333,444,555,678,999=111,222,333,444,555,678,999
-INFO [XX-XX|XX:XX:XX.XXX] uint256 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
-INFO [XX-XX|XX:XX:XX.XXX] int64 1,000,000=1,000,000
-INFO [XX-XX|XX:XX:XX.XXX] int64 -1,000,000=-1,000,000
-INFO [XX-XX|XX:XX:XX.XXX] int64 9,223,372,036,854,775,807=9,223,372,036,854,775,807
-INFO [XX-XX|XX:XX:XX.XXX] int64 -9,223,372,036,854,775,808=-9,223,372,036,854,775,808
-INFO [XX-XX|XX:XX:XX.XXX] uint64 1,000,000=1,000,000
-INFO [XX-XX|XX:XX:XX.XXX] uint64 18,446,744,073,709,551,615=18,446,744,073,709,551,615
-INFO [XX-XX|XX:XX:XX.XXX] Special chars in value key="special \r\n\t chars"
-INFO [XX-XX|XX:XX:XX.XXX] Special chars in key "special \n\t chars"=value
-INFO [XX-XX|XX:XX:XX.XXX] nospace nospace=nospace
-INFO [XX-XX|XX:XX:XX.XXX] with space "with nospace"="with nospace"
-INFO [XX-XX|XX:XX:XX.XXX] Bash escapes in value key="\x1b[1G\x1b[K\x1b[1A"
-INFO [XX-XX|XX:XX:XX.XXX] Bash escapes in key "\x1b[1G\x1b[K\x1b[1A"=value
-INFO [XX-XX|XX:XX:XX.XXX] "Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
-INFO [XX-XX|XX:XX:XX.XXX] "\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
-INFO [XX-XX|XX:XX:XX.XXX] an error message with quotes error="this is an 'error'"
-INFO [XX-XX|XX:XX:XX.XXX] Custom Stringer value 2562047h47m16.854s=2562047h47m16.854s
-INFO [XX-XX|XX:XX:XX.XXX] a custom stringer that emits quoted text output="output with 'quotes'"
-INFO [XX-XX|XX:XX:XX.XXX] Lazy evaluation of value key="lazy value"
-INFO [XX-XX|XX:XX:XX.XXX] "A message with wonky 💩 characters"
-INFO [XX-XX|XX:XX:XX.XXX] "A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
-INFO [XX-XX|XX:XX:XX.XXX] A multiline message
+INFO [xx-xx|xx:xx:xx.xxx] big.Int 111,222,333,444,555,678,999=111,222,333,444,555,678,999
+INFO [xx-xx|xx:xx:xx.xxx] -big.Int -111,222,333,444,555,678,999=-111,222,333,444,555,678,999
+INFO [xx-xx|xx:xx:xx.xxx] big.Int 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
+INFO [xx-xx|xx:xx:xx.xxx] -big.Int -11,122,233,344,455,567,899,900=-11,122,233,344,455,567,899,900
+INFO [xx-xx|xx:xx:xx.xxx] uint256 111,222,333,444,555,678,999=111,222,333,444,555,678,999
+INFO [xx-xx|xx:xx:xx.xxx] uint256 11,122,233,344,455,567,899,900=11,122,233,344,455,567,899,900
+INFO [xx-xx|xx:xx:xx.xxx] int64 1,000,000=1,000,000
+INFO [xx-xx|xx:xx:xx.xxx] int64 -1,000,000=-1,000,000
+INFO [xx-xx|xx:xx:xx.xxx] int64 9,223,372,036,854,775,807=9,223,372,036,854,775,807
+INFO [xx-xx|xx:xx:xx.xxx] int64 -9,223,372,036,854,775,808=-9,223,372,036,854,775,808
+INFO [xx-xx|xx:xx:xx.xxx] uint64 1,000,000=1,000,000
+INFO [xx-xx|xx:xx:xx.xxx] uint64 18,446,744,073,709,551,615=18,446,744,073,709,551,615
+INFO [xx-xx|xx:xx:xx.xxx] Special chars in value key="special \r\n\t chars"
+INFO [xx-xx|xx:xx:xx.xxx] Special chars in key "special \n\t chars"=value
+INFO [xx-xx|xx:xx:xx.xxx] nospace nospace=nospace
+INFO [xx-xx|xx:xx:xx.xxx] with space "with nospace"="with nospace"
+INFO [xx-xx|xx:xx:xx.xxx] Bash escapes in value key="\x1b[1G\x1b[K\x1b[1A"
+INFO [xx-xx|xx:xx:xx.xxx] Bash escapes in key "\x1b[1G\x1b[K\x1b[1A"=value
+INFO [xx-xx|xx:xx:xx.xxx] "Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
+INFO [xx-xx|xx:xx:xx.xxx] "\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
+INFO [xx-xx|xx:xx:xx.xxx] an error message with quotes error="this is an 'error'"
+INFO [xx-xx|xx:xx:xx.xxx] Custom Stringer value 2562047h47m16.854s=2562047h47m16.854s
+INFO [xx-xx|xx:xx:xx.xxx] a custom stringer that emits quoted text output="output with 'quotes'"
+INFO [xx-xx|xx:xx:xx.xxx] Lazy evaluation of value key="lazy value"
+INFO [xx-xx|xx:xx:xx.xxx] "A message with wonky 💩 characters"
+INFO [xx-xx|xx:xx:xx.xxx] "A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
+INFO [xx-xx|xx:xx:xx.xxx] A multiline message
LALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above
-INFO [XX-XX|XX:XX:XX.XXX] boolean true=true false=false
-INFO [XX-XX|XX:XX:XX.XXX] repeated-key 1 foo=alpha foo=beta
-INFO [XX-XX|XX:XX:XX.XXX] repeated-key 2 xx=short xx=longer
-INFO [XX-XX|XX:XX:XX.XXX] log at level info
-WARN [XX-XX|XX:XX:XX.XXX] log at level warn
-ERROR[XX-XX|XX:XX:XX.XXX] log at level error
-INFO [XX-XX|XX:XX:XX.XXX] test bar=short a="aligned left"
-INFO [XX-XX|XX:XX:XX.XXX] test bar="a long message" a=1
-INFO [XX-XX|XX:XX:XX.XXX] test bar=short a="aligned right"
-INFO [XX-XX|XX:XX:XX.XXX] The following logs should align so that the key-fields make 5 columns
-INFO [XX-XX|XX:XX:XX.XXX] Inserted known block number=1012 hash=000000..001234 txs=200 gas=1,123,123 other=first
-INFO [XX-XX|XX:XX:XX.XXX] Inserted new block number=1 hash=000000..001235 txs=2 gas=1123 other=second
-INFO [XX-XX|XX:XX:XX.XXX] Inserted known block number=99 hash=000000..012322 txs=10 gas=1 other=third
-WARN [XX-XX|XX:XX:XX.XXX] Inserted known block number=1012 hash=000000..001234 txs=200 gas=99 other=fourth
-INFO [XX-XX|XX:XX:XX.XXX] (*big.Int)(nil) =
-INFO [XX-XX|XX:XX:XX.XXX] (*uint256.Int)(nil) =
-INFO [XX-XX|XX:XX:XX.XXX] (fmt.Stringer)(nil) res=nil
-INFO [XX-XX|XX:XX:XX.XXX] nil-concrete-stringer res=nil
-INFO [XX-XX|XX:XX:XX.XXX] error(nil) res=nil
-INFO [XX-XX|XX:XX:XX.XXX] nil-concrete-error res=
-INFO [XX-XX|XX:XX:XX.XXX] nil-custom-struct res=
-INFO [XX-XX|XX:XX:XX.XXX] raw nil res=nil
-INFO [XX-XX|XX:XX:XX.XXX] (*uint64)(nil) res=
-INFO [XX-XX|XX:XX:XX.XXX] Using keys 't', 'lvl', 'time', 'level' and 'msg' t=t time=time lvl=lvl level=level msg=msg
+INFO [xx-xx|xx:xx:xx.xxx] boolean true=true false=false
+INFO [xx-xx|xx:xx:xx.xxx] repeated-key 1 foo=alpha foo=beta
+INFO [xx-xx|xx:xx:xx.xxx] repeated-key 2 xx=short xx=longer
+INFO [xx-xx|xx:xx:xx.xxx] log at level info
+WARN [xx-xx|xx:xx:xx.xxx] log at level warn
+ERROR[xx-xx|xx:xx:xx.xxx] log at level error
+INFO [xx-xx|xx:xx:xx.xxx] test bar=short a="aligned left"
+INFO [xx-xx|xx:xx:xx.xxx] test bar="a long message" a=1
+INFO [xx-xx|xx:xx:xx.xxx] test bar=short a="aligned right"
+INFO [xx-xx|xx:xx:xx.xxx] The following logs should align so that the key-fields make 5 columns
+INFO [xx-xx|xx:xx:xx.xxx] Inserted known block number=1012 hash=000000..001234 txs=200 gas=1,123,123 other=first
+INFO [xx-xx|xx:xx:xx.xxx] Inserted new block number=1 hash=000000..001235 txs=2 gas=1123 other=second
+INFO [xx-xx|xx:xx:xx.xxx] Inserted known block number=99 hash=000000..012322 txs=10 gas=1 other=third
+WARN [xx-xx|xx:xx:xx.xxx] Inserted known block number=1012 hash=000000..001234 txs=200 gas=99 other=fourth
+INFO [xx-xx|xx:xx:xx.xxx] (*big.Int)(nil) =
+INFO [xx-xx|xx:xx:xx.xxx] (*uint256.Int)(nil) =
+INFO [xx-xx|xx:xx:xx.xxx] (fmt.Stringer)(nil) res=
+INFO [xx-xx|xx:xx:xx.xxx] nil-concrete-stringer res=
+INFO [xx-xx|xx:xx:xx.xxx] error(nil) res=
+INFO [xx-xx|xx:xx:xx.xxx] nil-concrete-error res=
+INFO [xx-xx|xx:xx:xx.xxx] nil-custom-struct res=
+INFO [xx-xx|xx:xx:xx.xxx] raw nil res=
+INFO [xx-xx|xx:xx:xx.xxx] (*uint64)(nil) res=
+INFO [xx-xx|xx:xx:xx.xxx] Using keys 't', 'lvl', 'time', 'level' and 'msg' t=t time=time lvl=lvl level=level msg=msg
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index b49c7c36d5a7..27e1b3f623c4 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1389,6 +1389,13 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
log.Info(fmt.Sprintf("Using %s as db engine", dbEngine))
cfg.DBEngine = dbEngine
}
+ // deprecation notice for log debug flags (TODO: find a more appropriate place to put these?)
+ if ctx.IsSet(LogBacktraceAtFlag.Name) {
+ log.Warn("log.backtrace flag is deprecated")
+ }
+ if ctx.IsSet(LogDebugFlag.Name) {
+ log.Warn("log.debug flag is deprecated")
+ }
}
func setSmartCard(ctx *cli.Context, cfg *node.Config) {
diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go
index 00237fecaf27..243abd831105 100644
--- a/cmd/utils/flags_legacy.go
+++ b/cmd/utils/flags_legacy.go
@@ -45,6 +45,8 @@ var DeprecatedFlags = []cli.Flag{
LightMaxPeersFlag,
LightNoPruneFlag,
LightNoSyncServeFlag,
+ LogBacktraceAtFlag,
+ LogDebugFlag,
}
var (
@@ -118,6 +120,18 @@ var (
Usage: "Enables serving light clients before syncing (deprecated)",
Category: flags.LightCategory,
}
+ // Deprecated November 2023
+ LogBacktraceAtFlag = &cli.StringFlag{
+ Name: "log.backtrace",
+ Usage: "Request a stack trace at a specific logging statement (deprecated)",
+ Value: "",
+ Category: flags.DeprecatedCategory,
+ }
+ LogDebugFlag = &cli.BoolFlag{
+ Name: "log.debug",
+ Usage: "Prepends log messages with call-site location (deprecated)",
+ Category: flags.DeprecatedCategory,
+ }
)
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index 07016b675ce8..c25f3e7e8bc3 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -601,7 +601,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
}
func enableLogging() {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
}
// Tests that snapshot generation when an extra account with storage exists in the snap state.
diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go
index 8914301e14c3..fa3e8edc9055 100644
--- a/core/txpool/blobpool/blobpool_test.go
+++ b/core/txpool/blobpool/blobpool_test.go
@@ -319,7 +319,7 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) {
// - 3. All transactions after a nonce gap must be dropped
// - 4. All transactions after an underpriced one (including it) must be dropped
func TestOpenDrops(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
// Create a temporary folder for the persistent backend
storage, _ := os.MkdirTemp("", "blobpool-")
@@ -600,7 +600,7 @@ func TestOpenDrops(t *testing.T) {
// - 2. Eviction thresholds are calculated correctly for the sequences
// - 3. Balance usage of an account is totals across all transactions
func TestOpenIndex(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
// Create a temporary folder for the persistent backend
storage, _ := os.MkdirTemp("", "blobpool-")
@@ -689,7 +689,7 @@ func TestOpenIndex(t *testing.T) {
// Tests that after indexing all the loaded transactions from disk, a price heap
// is correctly constructed based on the head basefee and blobfee.
func TestOpenHeap(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
// Create a temporary folder for the persistent backend
storage, _ := os.MkdirTemp("", "blobpool-")
@@ -776,7 +776,7 @@ func TestOpenHeap(t *testing.T) {
// Tests that after the pool's previous state is loaded back, any transactions
// over the new storage cap will get dropped.
func TestOpenCap(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
// Create a temporary folder for the persistent backend
storage, _ := os.MkdirTemp("", "blobpool-")
@@ -868,7 +868,7 @@ func TestOpenCap(t *testing.T) {
// specific to the blob pool. It does not do an exhaustive transaction validity
// check.
func TestAdd(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
// seed is a helper tumpe to seed an initial state db and pool
type seed struct {
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 59f44fafea4e..c875c485dd86 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -1562,7 +1562,7 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
// This checks that beaconRoot is applied to the state from the engine API.
func TestParentBeaconBlockRoot(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), log.LevelTrace, true)))
genesis, blocks := generateMergeChain(10, true)
diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go
index a8b1b45e006a..50b9031a27c6 100644
--- a/eth/downloader/queue_test.go
+++ b/eth/downloader/queue_test.go
@@ -20,6 +20,7 @@ import (
"fmt"
"math/big"
"math/rand"
+ "os"
"sync"
"testing"
"time"
@@ -31,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
+ "golang.org/x/exp/slog"
)
// makeChain creates a chain of n blocks starting at and including parent.
@@ -271,7 +273,7 @@ func XTestDelivery(t *testing.T) {
world.chain = blo
world.progress(10)
if false {
- log.Root().SetHandler(log.StdoutHandler)
+ log.SetDefault(log.NewLogger(slog.NewTextHandler(os.Stdout, nil)))
}
q := newQueue(10, 10)
var wg sync.WaitGroup
diff --git a/go.mod b/go.mod
index 042d7155bed0..75c2b899d690 100644
--- a/go.mod
+++ b/go.mod
@@ -28,7 +28,6 @@ require (
github.com/fsnotify/fsnotify v1.6.0
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
- github.com/go-stack/stack v1.8.1
github.com/gofrs/flock v0.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/protobuf v1.5.3
@@ -64,13 +63,13 @@ require (
github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.25.7
go.uber.org/automaxprocs v1.5.2
- golang.org/x/crypto v0.14.0
- golang.org/x/exp v0.0.0-20230905200255-921286631fa9
- golang.org/x/sync v0.4.0
- golang.org/x/sys v0.13.0
- golang.org/x/text v0.13.0
+ golang.org/x/crypto v0.15.0
+ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
+ golang.org/x/sync v0.5.0
+ golang.org/x/sys v0.14.0
+ golang.org/x/text v0.14.0
golang.org/x/time v0.3.0
- golang.org/x/tools v0.13.0
+ golang.org/x/tools v0.15.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -136,8 +135,8 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
- golang.org/x/mod v0.12.0 // indirect
- golang.org/x/net v0.17.0 // indirect
+ golang.org/x/mod v0.14.0 // indirect
+ golang.org/x/net v0.18.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
diff --git a/go.sum b/go.sum
index d5e7901e5abe..f89adbe571be 100644
--- a/go.sum
+++ b/go.sum
@@ -228,8 +228,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
-github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
@@ -616,8 +614,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -628,8 +626,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
-golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -651,8 +649,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -692,8 +690,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -712,8 +710,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -776,8 +774,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -790,8 +788,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -846,8 +844,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
-golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/debug/api.go b/internal/debug/api.go
index 42d0fa15edd6..482989e0d0f3 100644
--- a/internal/debug/api.go
+++ b/internal/debug/api.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/hashicorp/go-bexpr"
+ "golang.org/x/exp/slog"
)
// Handler is the global debugging handler.
@@ -56,7 +57,7 @@ type HandlerT struct {
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
// and source files can be raised using Vmodule.
func (*HandlerT) Verbosity(level int) {
- glogger.Verbosity(log.Lvl(level))
+ glogger.Verbosity(slog.Level(level))
}
// Vmodule sets the log verbosity pattern. See package log for details on the
@@ -65,12 +66,6 @@ func (*HandlerT) Vmodule(pattern string) error {
return glogger.Vmodule(pattern)
}
-// BacktraceAt sets the log backtrace location. See package log for details on
-// the pattern syntax.
-func (*HandlerT) BacktraceAt(location string) error {
- return glogger.BacktraceAt(location)
-}
-
// MemStats returns detailed runtime memory statistics.
func (*HandlerT) MemStats() *runtime.MemStats {
s := new(runtime.MemStats)
diff --git a/internal/debug/flags.go b/internal/debug/flags.go
index 4f0f5fe86074..23e4745e8c80 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -34,6 +34,7 @@ import (
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
+ "golang.org/x/exp/slog"
"gopkg.in/natefinch/lumberjack.v2"
)
@@ -75,17 +76,6 @@ var (
Usage: "Write logs to a file",
Category: flags.LoggingCategory,
}
- backtraceAtFlag = &cli.StringFlag{
- Name: "log.backtrace",
- Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")",
- Value: "",
- Category: flags.LoggingCategory,
- }
- debugFlag = &cli.BoolFlag{
- Name: "log.debug",
- Usage: "Prepends log messages with call-site location (file and line number)",
- Category: flags.LoggingCategory,
- }
logRotateFlag = &cli.BoolFlag{
Name: "log.rotate",
Usage: "Enables log file rotation",
@@ -160,8 +150,6 @@ var Flags = []cli.Flag{
verbosityFlag,
logVmoduleFlag,
vmoduleFlag,
- backtraceAtFlag,
- debugFlag,
logjsonFlag,
logFormatFlag,
logFileFlag,
@@ -180,45 +168,34 @@ var Flags = []cli.Flag{
}
var (
- glogger *log.GlogHandler
- logOutputStream log.Handler
+ glogger *log.GlogHandler
+ logOutputFile io.WriteCloser
+ defaultTerminalHandler *log.TerminalHandler
)
func init() {
- glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
+ defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false)
+ glogger = log.NewGlogHandler(defaultTerminalHandler)
glogger.Verbosity(log.LvlInfo)
- log.Root().SetHandler(glogger)
+ log.SetDefault(log.NewLogger(glogger))
+}
+
+func ResetLogging() {
+ if defaultTerminalHandler != nil {
+ defaultTerminalHandler.ResetFieldPadding()
+ }
}
// Setup initializes profiling and logging based on the CLI flags.
// It should be called as early as possible in the program.
func Setup(ctx *cli.Context) error {
var (
- logfmt log.Format
- output = io.Writer(os.Stderr)
- logFmtFlag = ctx.String(logFormatFlag.Name)
+ handler slog.Handler
+ terminalOutput = io.Writer(os.Stderr)
+ output io.Writer
+ logFmtFlag = ctx.String(logFormatFlag.Name)
)
- switch {
- case ctx.Bool(logjsonFlag.Name):
- // 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")
- logfmt = log.JSONFormat()
- case logFmtFlag == "json":
- logfmt = log.JSONFormat()
- case logFmtFlag == "logfmt":
- logfmt = log.LogfmtFormat()
- case logFmtFlag == "", logFmtFlag == "terminal":
- useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
- if useColor {
- output = colorable.NewColorableStderr()
- }
- logfmt = log.TerminalFormat(useColor)
- default:
- // Unknown log format specified
- return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
- }
var (
- ostream = log.StreamHandler(output, logfmt)
logFile = ctx.String(logFileFlag.Name)
rotation = ctx.Bool(logRotateFlag.Name)
)
@@ -241,27 +218,55 @@ func Setup(ctx *cli.Context) error {
} else {
context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
}
- lumberWriter := &lumberjack.Logger{
+ logOutputFile = &lumberjack.Logger{
Filename: logFile,
MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
MaxAge: ctx.Int(logMaxAgeFlag.Name),
Compress: ctx.Bool(logCompressFlag.Name),
}
- ostream = log.StreamHandler(io.MultiWriter(output, lumberWriter), logfmt)
+ output = io.MultiWriter(terminalOutput, logOutputFile)
} else if logFile != "" {
- f, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
+ var err error
+ if logOutputFile, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
return err
}
- ostream = log.StreamHandler(io.MultiWriter(output, f), logfmt)
+ output = io.MultiWriter(logOutputFile, terminalOutput)
context = append(context, "location", logFile)
+ } else {
+ output = terminalOutput
}
- glogger.SetHandler(ostream)
+
+ switch {
+ case ctx.Bool(logjsonFlag.Name):
+ // 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")
+ handler = log.JSONHandler(output)
+ case logFmtFlag == "json":
+ handler = log.JSONHandler(output)
+ case logFmtFlag == "logfmt":
+ handler = log.LogfmtHandler(output)
+ case logFmtFlag == "", logFmtFlag == "terminal":
+ useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
+ if useColor {
+ terminalOutput = colorable.NewColorableStderr()
+ if logOutputFile != nil {
+ output = io.MultiWriter(logOutputFile, terminalOutput)
+ } else {
+ output = terminalOutput
+ }
+ }
+ handler = log.NewTerminalHandler(output, useColor)
+ default:
+ // Unknown log format specified
+ return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
+ }
+
+ glogger = log.NewGlogHandler(handler)
// logging
- verbosity := ctx.Int(verbosityFlag.Name)
- glogger.Verbosity(log.Lvl(verbosity))
+ verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
+ glogger.Verbosity(verbosity)
vmodule := ctx.String(logVmoduleFlag.Name)
if vmodule == "" {
// Retain backwards compatibility with `--vmodule` flag if `--log.vmodule` not set
@@ -272,16 +277,7 @@ func Setup(ctx *cli.Context) error {
}
glogger.Vmodule(vmodule)
- debug := ctx.Bool(debugFlag.Name)
- if ctx.IsSet(debugFlag.Name) {
- debug = ctx.Bool(debugFlag.Name)
- }
- log.PrintOrigins(debug)
-
- backtrace := ctx.String(backtraceAtFlag.Name)
- glogger.BacktraceAt(backtrace)
-
- log.Root().SetHandler(glogger)
+ log.SetDefault(log.NewLogger(glogger))
// profiling, tracing
runtime.MemProfileRate = memprofilerateFlag.Value
@@ -341,8 +337,8 @@ func StartPProf(address string, withMetrics bool) {
func Exit() {
Handler.StopCPUProfile()
Handler.StopGoTrace()
- if closer, ok := logOutputStream.(io.Closer); ok {
- closer.Close()
+ if logOutputFile != nil {
+ logOutputFile.Close()
}
}
diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go
index 684339f16d80..68b9fb19f8ba 100644
--- a/internal/testlog/testlog.go
+++ b/internal/testlog/testlog.go
@@ -18,26 +18,19 @@
package testlog
import (
+ "bytes"
+ "context"
+ "fmt"
"sync"
"testing"
"github.com/ethereum/go-ethereum/log"
+ "golang.org/x/exp/slog"
)
-// Handler returns a log handler which logs to the unit test log of t.
-func Handler(t *testing.T, level log.Lvl) log.Handler {
- return log.LvlFilterHandler(level, &handler{t, log.TerminalFormat(false)})
-}
-
-type handler struct {
- t *testing.T
- fmt log.Format
-}
-
-func (h *handler) Log(r *log.Record) error {
- h.t.Logf("%s", h.fmt.Format(r))
- return nil
-}
+const (
+ termTimeFormat = "01-02|15:04:05.000"
+)
// logger implements log.Logger such that all output goes to the unit test log via
// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test
@@ -51,27 +44,62 @@ type logger struct {
}
type bufHandler struct {
- buf []*log.Record
- fmt log.Format
+ buf []slog.Record
+ attrs []slog.Attr
+ level slog.Level
}
-func (h *bufHandler) Log(r *log.Record) error {
+func (h *bufHandler) Handle(_ context.Context, r slog.Record) error {
h.buf = append(h.buf, r)
return nil
}
+func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool {
+ return lvl <= h.level
+}
+
+func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ records := make([]slog.Record, len(h.buf))
+ copy(records[:], h.buf[:])
+ return &bufHandler{
+ records,
+ append(h.attrs, attrs...),
+ h.level,
+ }
+}
+
+func (h *bufHandler) WithGroup(_ string) slog.Handler {
+ panic("not implemented")
+}
+
// Logger returns a logger which logs to the unit test log of t.
-func Logger(t *testing.T, level log.Lvl) log.Logger {
- l := &logger{
+func Logger(t *testing.T, level slog.Level) log.Logger {
+ handler := bufHandler{
+ []slog.Record{},
+ []slog.Attr{},
+ level,
+ }
+ return &logger{
+ t: t,
+ l: log.NewLogger(&handler),
+ mu: new(sync.Mutex),
+ h: &handler,
+ }
+}
+
+// LoggerWithHandler returns
+func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger {
+ var bh bufHandler
+ return &logger{
t: t,
- l: log.New(),
+ l: log.NewLogger(handler),
mu: new(sync.Mutex),
- h: &bufHandler{fmt: log.TerminalFormat(false)},
+ h: &bh,
}
- l.l.SetHandler(log.LvlFilterHandler(level, l.h))
- return l
}
+func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {}
+
func (l *logger) Trace(msg string, ctx ...interface{}) {
l.t.Helper()
l.mu.Lock()
@@ -80,6 +108,14 @@ func (l *logger) Trace(msg string, ctx ...interface{}) {
l.flush()
}
+func (l *logger) Log(level slog.Level, msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Log(level, msg, ctx...)
+ l.flush()
+}
+
func (l *logger) Debug(msg string, ctx ...interface{}) {
l.t.Helper()
l.mu.Lock()
@@ -120,23 +156,45 @@ func (l *logger) Crit(msg string, ctx ...interface{}) {
l.flush()
}
-func (l *logger) New(ctx ...interface{}) log.Logger {
- return &logger{l.t, l.l.New(ctx...), l.mu, l.h}
+func (l *logger) With(ctx ...interface{}) log.Logger {
+ return &logger{l.t, l.l.With(ctx...), l.mu, l.h}
}
-func (l *logger) GetHandler() log.Handler {
- return l.l.GetHandler()
+func (l *logger) New(ctx ...interface{}) log.Logger {
+ return l.With(ctx...)
}
-func (l *logger) SetHandler(h log.Handler) {
- l.l.SetHandler(h)
+// terminalFormat formats a message similarly to the NewTerminalHandler in the log package.
+// The difference is that terminalFormat does not escape messages/attributes and does not pad attributes.
+func (h *bufHandler) terminalFormat(r slog.Record) string {
+ buf := &bytes.Buffer{}
+ lvl := log.LevelAlignedString(r.Level)
+ attrs := []slog.Attr{}
+ r.Attrs(func(attr slog.Attr) bool {
+ attrs = append(attrs, attr)
+ return true
+ })
+
+ attrs = append(h.attrs, attrs...)
+
+ fmt.Fprintf(buf, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Message)
+ if length := len(r.Message); length < 40 {
+ buf.Write(bytes.Repeat([]byte{' '}, 40-length))
+ }
+
+ for _, attr := range attrs {
+ rawVal := attr.Value.Any()
+ fmt.Fprintf(buf, " %s=%s", attr.Key, log.FormatLogfmtValue(rawVal, true))
+ }
+ buf.WriteByte('\n')
+ return buf.String()
}
// flush writes all buffered messages and clears the buffer.
func (l *logger) flush() {
l.t.Helper()
for _, r := range l.h.buf {
- l.t.Logf("%s", l.h.fmt.Format(r))
+ l.t.Logf("%s", l.h.terminalFormat(r))
}
l.h.buf = nil
}
diff --git a/log/CONTRIBUTORS b/log/CONTRIBUTORS
deleted file mode 100644
index a0866713be09..000000000000
--- a/log/CONTRIBUTORS
+++ /dev/null
@@ -1,11 +0,0 @@
-Contributors to log15:
-
-- Aaron L
-- Alan Shreve
-- Chris Hines
-- Ciaran Downey
-- Dmitry Chestnykh
-- Evan Shaw
-- Péter Szilágyi
-- Trevor Gattis
-- Vincent Vanackere
diff --git a/log/LICENSE b/log/LICENSE
deleted file mode 100644
index 5f0d1fb6a7bb..000000000000
--- a/log/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2014 Alan Shreve
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/log/README.md b/log/README.md
deleted file mode 100644
index 47426806dd95..000000000000
--- a/log/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-![obligatory xkcd](https://imgs.xkcd.com/comics/standards.png)
-
-# log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15)
-
-Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](https://golang.org/pkg/io/) and [`net/http`](https://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](https://golang.org/pkg/log/) package.
-
-## Features
-- A simple, easy-to-understand API
-- Promotes structured logging by encouraging use of key/value pairs
-- Child loggers which inherit and add their own private context
-- Lazy evaluation of expensive operations
-- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API.
-- Color terminal support
-- Built-in support for logging to files, streams, syslog, and the network
-- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more
-
-## Versioning
-The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API,
-you must vendor the library.
-
-## Importing
-
-```go
-import log "github.com/inconshreveable/log15"
-```
-
-## Examples
-
-```go
-// all loggers can have key/value context
-srvlog := log.New("module", "app/server")
-
-// all log messages can have key/value context
-srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
-
-// child loggers with inherited context
-connlog := srvlog.New("raddr", c.RemoteAddr())
-connlog.Info("connection open")
-
-// lazy evaluation
-connlog.Debug("ping remote", "latency", log.Lazy{pingRemote})
-
-// flexible configuration
-srvlog.SetHandler(log.MultiHandler(
- log.StreamHandler(os.Stderr, log.LogfmtFormat()),
- log.LvlFilterHandler(
- log.LvlError,
- log.Must.FileHandler("errors.json", log.JSONFormat()))))
-```
-
-Will result in output that looks like this:
-
-```
-WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800
-INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1
-```
-
-## Breaking API Changes
-The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version
-of log15.
-
-- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler
-- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack`
-- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors
-
-## FAQ
-
-### The varargs style is brittle and error prone! Can I have type safety please?
-Yes. Use `log.Ctx`:
-
-```go
-srvlog := log.New(log.Ctx{"module": "app/server"})
-srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
-```
-
-## License
-Apache
diff --git a/log/README_ETHEREUM.md b/log/README_ETHEREUM.md
deleted file mode 100644
index f6c42ccc03da..000000000000
--- a/log/README_ETHEREUM.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This package is a fork of https://github.com/inconshreveable/log15, with some
-minor modifications required by the go-ethereum codebase:
-
- * Support for log level `trace`
- * Modified behavior to exit on `critical` failure
diff --git a/log/doc.go b/log/doc.go
deleted file mode 100644
index d2e15140e4e0..000000000000
--- a/log/doc.go
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
-Package log15 provides an opinionated, simple toolkit for best-practice logging that is
-both human and machine readable. It is modeled after the standard library's io and net/http
-packages.
-
-This package enforces you to only log key/value pairs. Keys must be strings. Values may be
-any type that you like. The default output format is logfmt, but you may also choose to use
-JSON instead if that suits you. Here's how you log:
-
- log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
-
-This will output a line that looks like:
-
- lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
-
-# Getting Started
-
-To get started, you'll want to import the library:
-
- import log "github.com/inconshreveable/log15"
-
-Now you're ready to start logging:
-
- func main() {
- log.Info("Program starting", "args", os.Args())
- }
-
-# Convention
-
-Because recording a human-meaningful message is common and good practice, the first argument to every
-logging method is the value to the *implicit* key 'msg'.
-
-Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so
-will the current timestamp with key 't'.
-
-You may supply any additional context as a set of key/value pairs to the logging function. log15 allows
-you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for
-logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
-in the variadic argument list:
-
- log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
-
-If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
-
- log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
-
-# Context loggers
-
-Frequently, you want to add context to a logger so that you can track actions associated with it. An http
-request is a good example. You can easily create new loggers that have context that is automatically included
-with each log line:
-
- requestlogger := log.New("path", r.URL.Path)
-
- // later
- requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
-
-This will output a log line that includes the path context that is attached to the logger:
-
- lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
-
-# Handlers
-
-The Handler interface defines where log lines are printed to and how they are formatted. Handler is a
-single interface that is inspired by net/http's handler interface:
-
- type Handler interface {
- Log(r *Record) error
- }
-
-Handlers can filter records, format them, or dispatch to multiple other Handlers.
-This package implements a number of Handlers for common logging patterns that are
-easily composed to create flexible, custom logging structures.
-
-Here's an example handler that prints logfmt output to Stdout:
-
- handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
-
-Here's an example handler that defers to two other handlers. One handler only prints records
-from the rpc package in logfmt to standard out. The other prints records at Error level
-or above in JSON formatted output to the file /var/log/service.json
-
- handler := log.MultiHandler(
- log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
- log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
- )
-
-# Logging File Names and Line Numbers
-
-This package implements three Handlers that add debugging information to the
-context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
-an example that adds the source file and line number of each logging call to
-the context.
-
- h := log.CallerFileHandler(log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
-
-Here's an example that logs the call stack rather than just the call site.
-
- h := log.CallerStackHandler("%+v", log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
-
-The "%+v" format instructs the handler to include the path of the source file
-relative to the compile time GOPATH. The github.com/go-stack/stack package
-documents the full list of formatting verbs and modifiers available.
-
-# Custom Handlers
-
-The Handler interface is so simple that it's also trivial to write your own. Let's create an
-example handler which tries to write to one handler, but if that fails it falls back to
-writing to another handler and includes the error that it encountered when trying to write
-to the primary. This might be useful when trying to log over a network socket, but if that
-fails you want to log those records to a file on disk.
-
- type BackupHandler struct {
- Primary Handler
- Secondary Handler
- }
-
- func (h *BackupHandler) Log (r *Record) error {
- err := h.Primary.Log(r)
- if err != nil {
- r.Ctx = append(ctx, "primary_err", err)
- return h.Secondary.Log(r)
- }
- return nil
- }
-
-This pattern is so useful that a generic version that handles an arbitrary number of Handlers
-is included as part of this library called FailoverHandler.
-
-# Logging Expensive Operations
-
-Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
-the price of computing them if you haven't turned up your logging level to a high level of detail.
-
-This package provides a simple type to annotate a logging operation that you want to be evaluated
-lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
-filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
-
- func factorRSAKey() (factors []int) {
- // return the factors of a very large number
- }
-
- log.Debug("factors", log.Lazy{factorRSAKey})
-
-If this message is not logged for any reason (like logging at the Error level), then
-factorRSAKey is never evaluated.
-
-# Dynamic context values
-
-The same log.Lazy mechanism can be used to attach context to a logger which you want to be
-evaluated when the message is logged, but not when the logger is created. For example, let's imagine
-a game where you have Player objects:
-
- type Player struct {
- name string
- alive bool
- log.Logger
- }
-
-You always want to log a player's name and whether they're alive or dead, so when you create the player
-object, you might do:
-
- p := &Player{name: name, alive: true}
- p.Logger = log.New("name", p.name, "alive", p.alive)
-
-Only now, even after a player has died, the logger will still report they are alive because the logging
-context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
-of whether the player is alive or not to each log message, so that the log records will reflect the player's
-current state no matter when the log message is written:
-
- p := &Player{name: name, alive: true}
- isAlive := func() bool { return p.alive }
- player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
-
-# Terminal Format
-
-If log15 detects that stdout is a terminal, it will configure the default
-handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
-logs records nicely for your terminal, including color-coded output based
-on log level.
-
-# Error Handling
-
-Becasuse log15 allows you to step around the type system, there are a few ways you can specify
-invalid arguments to the logging functions. You could, for example, wrap something that is not
-a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries
-are typically the mechanism by which errors are reported, it would be onerous for the logging functions
-to return errors. Instead, log15 handles errors by making these guarantees to you:
-
-- Any log record containing an error will still be printed with the error explained to you as part of the log record.
-
-- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily
-(and if you like, automatically) detect if any of your logging calls are passing bad values.
-
-Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers
-are encouraged to return errors only if they fail to write their log records out to an external source like if the
-syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
-like the FailoverHandler.
-
-# Library Use
-
-log15 is intended to be useful for library authors as a way to provide configurable logging to
-users of their library. Best practice for use in a library is to always disable all output for your logger
-by default and to provide a public Logger instance that consumers of your library can configure. Like so:
-
- package yourlib
-
- import "github.com/inconshreveable/log15"
-
- var Log = log.New()
-
- func init() {
- Log.SetHandler(log.DiscardHandler())
- }
-
-Users of your library may then enable it if they like:
-
- import "github.com/inconshreveable/log15"
- import "example.com/yourlib"
-
- func main() {
- handler := // custom handler setup
- yourlib.Log.SetHandler(handler)
- }
-
-# Best practices attaching logger context
-
-The ability to attach context to a logger is a powerful one. Where should you do it and why?
-I favor embedding a Logger directly into any persistent object in my application and adding
-unique, tracing context keys to it. For instance, imagine I am writing a web browser:
-
- type Tab struct {
- url string
- render *RenderingContext
- // ...
-
- Logger
- }
-
- func NewTab(url string) *Tab {
- return &Tab {
- // ...
- url: url,
-
- Logger: log.New("url", url),
- }
- }
-
-When a new tab is created, I assign a logger to it with the url of
-the tab as context so it can easily be traced through the logs.
-Now, whenever we perform any operation with the tab, we'll log with its
-embedded logger and it will include the tab title automatically:
-
- tab.Debug("moved position", "idx", tab.idx)
-
-There's only one problem. What if the tab url changes? We could
-use log.Lazy to make sure the current url is always written, but that
-would mean that we couldn't trace a tab's full lifetime through our
-logs after the user navigate to a new URL.
-
-Instead, think about what values to attach to your loggers the
-same way you think about what to use as a key in a SQL database schema.
-If it's possible to use a natural key that is unique for the lifetime of the
-object, do so. But otherwise, log15's ext package has a handy RandId
-function to let you generate what you might call "surrogate keys"
-They're just random hex identifiers to use for tracing. Back to our
-Tab example, we would prefer to set up our Logger like so:
-
- import logext "github.com/inconshreveable/log15/ext"
-
- t := &Tab {
- // ...
- url: url,
- }
-
- t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
- return t
-
-Now we'll have a unique traceable identifier even across loading new urls, but
-we'll still be able to see the tab's current url in the log messages.
-
-# Must
-
-For all Handler functions which can return an error, there is a version of that
-function which will return no error but panics on failure. They are all available
-on the Must object. For example:
-
- log.Must.FileHandler("/path", log.JSONFormat)
- log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
-
-# Inspiration and Credit
-
-All of the following excellent projects inspired the design of this library:
-
-code.google.com/p/log4go
-
-github.com/op/go-logging
-
-github.com/technoweenie/grohl
-
-github.com/Sirupsen/logrus
-
-github.com/kr/logfmt
-
-github.com/spacemonkeygo/spacelog
-
-golang's stdlib, notably io and net/http
-
-# The Name
-
-https://xkcd.com/927/
-*/
-package log
diff --git a/log/format.go b/log/format.go
index 2fd1f2855815..5cbbe3341ed0 100644
--- a/log/format.go
+++ b/log/format.go
@@ -2,18 +2,15 @@ package log
import (
"bytes"
- "encoding/json"
"fmt"
"math/big"
"reflect"
"strconv"
- "strings"
- "sync"
- "sync/atomic"
"time"
"unicode/utf8"
"github.com/holiman/uint256"
+ "golang.org/x/exp/slog"
)
const (
@@ -24,61 +21,19 @@ const (
termCtxMaxPadding = 40
)
-// ResetGlobalState resets the fieldPadding, which is useful for producing
-// predictable output.
-func ResetGlobalState() {
- fieldPaddingLock.Lock()
- fieldPadding = make(map[string]int)
- fieldPaddingLock.Unlock()
-}
-
-// locationTrims are trimmed for display to avoid unwieldy log lines.
-var locationTrims = []string{
- "github.com/ethereum/go-ethereum/",
-}
-
-// PrintOrigins sets or unsets log location (file:line) printing for terminal
-// format output.
-func PrintOrigins(print bool) {
- locationEnabled.Store(print)
- if print {
- stackEnabled.Store(true)
- }
-}
-
-// stackEnabled is an atomic flag controlling whether the log handler needs
-// to store the callsite stack. This is needed in case any handler wants to
-// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt).
-var stackEnabled atomic.Bool
-
-// locationEnabled is an atomic flag controlling whether the terminal formatter
-// should append the log locations too when printing entries.
-var locationEnabled atomic.Bool
-
-// locationLength is the maxmimum path length encountered, which all logs are
-// padded to to aid in alignment.
-var locationLength atomic.Uint32
-
-// fieldPadding is a global map with maximum field value lengths seen until now
-// to allow padding log contexts in a bit smarter way.
-var fieldPadding = make(map[string]int)
-
-// fieldPaddingLock is a global mutex protecting the field padding map.
-var fieldPaddingLock sync.RWMutex
-
type Format interface {
- Format(r *Record) []byte
+ Format(r slog.Record) []byte
}
// FormatFunc returns a new Format object which uses
// the given function to perform record formatting.
-func FormatFunc(f func(*Record) []byte) Format {
+func FormatFunc(f func(slog.Record) []byte) Format {
return formatFunc(f)
}
-type formatFunc func(*Record) []byte
+type formatFunc func(slog.Record) []byte
-func (f formatFunc) Format(r *Record) []byte {
+func (f formatFunc) Format(r slog.Record) []byte {
return f(r)
}
@@ -89,263 +44,100 @@ type TerminalStringer interface {
TerminalString() string
}
-// TerminalFormat formats log records optimized for human readability on
-// a terminal with color-coded level output and terser human friendly timestamp.
-// This format should only be used for interactive programs or while developing.
-//
-// [LEVEL] [TIME] MESSAGE key=value key=value ...
-//
-// Example:
-//
-// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
-func TerminalFormat(usecolor bool) Format {
- return FormatFunc(func(r *Record) []byte {
- msg := escapeMessage(r.Msg)
- var color = 0
- if usecolor {
- switch r.Lvl {
- case LvlCrit:
- color = 35
- case LvlError:
- color = 31
- case LvlWarn:
- color = 33
- case LvlInfo:
- color = 32
- case LvlDebug:
- color = 36
- case LvlTrace:
- color = 34
- }
+func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte {
+ msg := escapeMessage(r.Message)
+ var color = 0
+ if usecolor {
+ switch r.Level {
+ case LevelCrit:
+ color = 35
+ case slog.LevelError:
+ color = 31
+ case slog.LevelWarn:
+ color = 33
+ case slog.LevelInfo:
+ color = 32
+ case slog.LevelDebug:
+ color = 36
+ case LevelTrace:
+ color = 34
}
+ }
- b := &bytes.Buffer{}
- lvl := r.Lvl.AlignedString()
- if locationEnabled.Load() {
- // Log origin printing was requested, format the location path and line number
- location := fmt.Sprintf("%+v", r.Call)
- for _, prefix := range locationTrims {
- location = strings.TrimPrefix(location, prefix)
- }
- // Maintain the maximum location length for fancyer alignment
- align := int(locationLength.Load())
- if align < len(location) {
- align = len(location)
- locationLength.Store(uint32(align))
- }
- padding := strings.Repeat(" ", align-len(location))
+ b := &bytes.Buffer{}
+ lvl := LevelAlignedString(r.Level)
+ if color > 0 {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
+ } else {
+ fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
+ }
+ // try to justify the log output for short messages
+ length := utf8.RuneCountInString(msg)
+ if r.NumAttrs() > 0 && length < termMsgJust {
+ b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
+ }
+ // print the keys logfmt style
+ h.logfmt(b, r, color)
- // Assemble and print the log heading
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, msg)
- } else {
- fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, msg)
- }
- } else {
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
- } else {
- fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
- }
- }
- // try to justify the log output for short messages
- length := utf8.RuneCountInString(msg)
- if len(r.Ctx) > 0 && length < termMsgJust {
- b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
- }
- // print the keys logfmt style
- logfmt(b, r.Ctx, color, true)
- return b.Bytes()
- })
+ return b.Bytes()
}
-// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable
-// format for key/value pairs.
-//
-// For more details see: http://godoc.org/github.com/kr/logfmt
-func LogfmtFormat() Format {
- return FormatFunc(func(r *Record) []byte {
- common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
- buf := &bytes.Buffer{}
- logfmt(buf, append(common, r.Ctx...), 0, false)
- return buf.Bytes()
+func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color int) {
+ attrs := []slog.Attr{}
+ r.Attrs(func(attr slog.Attr) bool {
+ attrs = append(attrs, attr)
+ return true
})
-}
-func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
- for i := 0; i < len(ctx); i += 2 {
+ attrs = append(h.attrs, attrs...)
+
+ for i, attr := range attrs {
if i != 0 {
buf.WriteByte(' ')
}
- k, ok := ctx[i].(string)
- v := formatLogfmtValue(ctx[i+1], term)
- if !ok {
- k, v = errorKey, fmt.Sprintf("%+T is not a string key", ctx[i])
- } else {
- k = escapeString(k)
- }
+ key := escapeString(attr.Key)
+ rawVal := attr.Value.Any()
+ val := FormatLogfmtValue(rawVal, true)
// XXX: we should probably check that all of your key bytes aren't invalid
- fieldPaddingLock.RLock()
- padding := fieldPadding[k]
- fieldPaddingLock.RUnlock()
+ // TODO (jwasinger) above comment was from log15 code. what does it mean? check that key bytes are ascii characters?
+ padding := h.fieldPadding[key]
- length := utf8.RuneCountInString(v)
+ length := utf8.RuneCountInString(val)
if padding < length && length <= termCtxMaxPadding {
padding = length
-
- fieldPaddingLock.Lock()
- fieldPadding[k] = padding
- fieldPaddingLock.Unlock()
+ h.fieldPadding[key] = padding
}
if color > 0 {
- fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
+ fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, key)
} else {
- buf.WriteString(k)
+ buf.WriteString(key)
buf.WriteByte('=')
}
- buf.WriteString(v)
- if i < len(ctx)-2 && padding > length {
+ buf.WriteString(val)
+ if i < r.NumAttrs()-1 && padding > length {
buf.Write(bytes.Repeat([]byte{' '}, padding-length))
}
}
buf.WriteByte('\n')
}
-// JSONFormat formats log records as JSON objects separated by newlines.
-// It is the equivalent of JSONFormatEx(false, true).
-func JSONFormat() Format {
- return JSONFormatEx(false, true)
-}
-
-// JSONFormatOrderedEx formats log records as JSON arrays. If pretty is true,
-// records will be pretty-printed. If lineSeparated is true, records
-// will be logged with a new line between each record.
-func JSONFormatOrderedEx(pretty, lineSeparated bool) Format {
- jsonMarshal := json.Marshal
- if pretty {
- jsonMarshal = func(v interface{}) ([]byte, error) {
- return json.MarshalIndent(v, "", " ")
- }
- }
- return FormatFunc(func(r *Record) []byte {
- props := map[string]interface{}{
- r.KeyNames.Time: r.Time,
- r.KeyNames.Lvl: r.Lvl.String(),
- r.KeyNames.Msg: r.Msg,
- }
-
- ctx := make([]string, len(r.Ctx))
- for i := 0; i < len(r.Ctx); i += 2 {
- if k, ok := r.Ctx[i].(string); ok {
- ctx[i] = k
- ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true)
- } else {
- props[errorKey] = fmt.Sprintf("%+T is not a string key,", r.Ctx[i])
- }
- }
- props[r.KeyNames.Ctx] = ctx
-
- b, err := jsonMarshal(props)
- if err != nil {
- b, _ = jsonMarshal(map[string]string{
- errorKey: err.Error(),
- })
- return b
- }
- if lineSeparated {
- b = append(b, '\n')
- }
- return b
- })
-}
-
-// JSONFormatEx formats log records as JSON objects. If pretty is true,
-// records will be pretty-printed. If lineSeparated is true, records
-// will be logged with a new line between each record.
-func JSONFormatEx(pretty, lineSeparated bool) Format {
- jsonMarshal := json.Marshal
- if pretty {
- jsonMarshal = func(v interface{}) ([]byte, error) {
- return json.MarshalIndent(v, "", " ")
- }
+// formatValue formats a value for serialization
+func FormatLogfmtValue(value interface{}, term bool) (result string) {
+ if value == nil {
+ return ""
}
-
- return FormatFunc(func(r *Record) []byte {
- props := map[string]interface{}{
- r.KeyNames.Time: r.Time,
- r.KeyNames.Lvl: r.Lvl.String(),
- r.KeyNames.Msg: r.Msg,
- }
-
- for i := 0; i < len(r.Ctx); i += 2 {
- k, ok := r.Ctx[i].(string)
- if !ok {
- props[errorKey] = fmt.Sprintf("%+T is not a string key", r.Ctx[i])
- } else {
- props[k] = formatJSONValue(r.Ctx[i+1])
- }
- }
-
- b, err := jsonMarshal(props)
- if err != nil {
- b, _ = jsonMarshal(map[string]string{
- errorKey: err.Error(),
- })
- return b
- }
-
- if lineSeparated {
- b = append(b, '\n')
- }
-
- return b
- })
-}
-
-func formatShared(value interface{}) (result interface{}) {
defer func() {
if err := recover(); err != nil {
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
- result = "nil"
+ result = ""
} else {
panic(err)
}
}
}()
- switch v := value.(type) {
- case time.Time:
- return v.Format(timeFormat)
-
- case error:
- return v.Error()
-
- case fmt.Stringer:
- return v.String()
-
- default:
- return v
- }
-}
-
-func formatJSONValue(value interface{}) interface{} {
- value = formatShared(value)
- switch value.(type) {
- case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
- return value
- default:
- return fmt.Sprintf("%+v", value)
- }
-}
-
-// formatValue formats a value for serialization
-func formatLogfmtValue(value interface{}, term bool) string {
- if value == nil {
- return "nil"
- }
-
switch v := value.(type) {
case time.Time:
// Performance optimization: No need for escaping since the provided
@@ -375,8 +167,11 @@ func formatLogfmtValue(value interface{}, term bool) string {
return escapeString(s.TerminalString())
}
}
- value = formatShared(value)
switch v := value.(type) {
+ case error:
+ return escapeString(v.Error())
+ case fmt.Stringer:
+ return escapeString(v.String())
case bool:
return strconv.FormatBool(v)
case float32:
diff --git a/log/handler.go b/log/handler.go
index 4a0cf578f6cd..ef1bcc1992d2 100644
--- a/log/handler.go
+++ b/log/handler.go
@@ -1,375 +1,223 @@
package log
import (
+ "context"
"fmt"
"io"
- "net"
- "os"
+ "math/big"
"reflect"
"sync"
- "sync/atomic"
+ "time"
- "github.com/go-stack/stack"
+ "github.com/holiman/uint256"
+ "golang.org/x/exp/slog"
)
-// Handler defines where and how log records are written.
-// A Logger prints its log records by writing to a Handler.
-// Handlers are composable, providing you great flexibility in combining
-// them to achieve the logging structure that suits your applications.
-type Handler interface {
- Log(r *Record) error
-}
-
-// FuncHandler returns a Handler that logs records with the given
-// function.
-func FuncHandler(fn func(r *Record) error) Handler {
- return funcHandler(fn)
-}
-
-type funcHandler func(r *Record) error
-
-func (h funcHandler) Log(r *Record) error {
- return h(r)
-}
-
-// StreamHandler writes log records to an io.Writer
-// with the given format. StreamHandler can be used
-// to easily begin writing log records to other
-// outputs.
+// Lazy allows you to defer calculation of a logged value that is expensive
+// to compute until it is certain that it must be evaluated with the given filters.
//
-// StreamHandler wraps itself with LazyHandler and SyncHandler
-// to evaluate Lazy objects and perform safe concurrent writes.
-func StreamHandler(wr io.Writer, fmtr Format) Handler {
- h := FuncHandler(func(r *Record) error {
- _, err := wr.Write(fmtr.Format(r))
- return err
- })
- return LazyHandler(SyncHandler(h))
+// You may wrap any function which takes no arguments to Lazy. It may return any
+// number of values of any type.
+type Lazy struct {
+ Fn interface{}
}
-// SyncHandler can be wrapped around a handler to guarantee that
-// only a single Log operation can proceed at a time. It's necessary
-// for thread-safe concurrent writes.
-func SyncHandler(h Handler) Handler {
- var mu sync.Mutex
- return FuncHandler(func(r *Record) error {
- mu.Lock()
- defer mu.Unlock()
-
- return h.Log(r)
- })
-}
+func evaluateLazy(lz Lazy) (interface{}, error) {
+ t := reflect.TypeOf(lz.Fn)
-// FileHandler returns a handler which writes log records to the give file
-// using the given format. If the path
-// already exists, FileHandler will append to the given file. If it does not,
-// FileHandler will create the file with mode 0644.
-func FileHandler(path string, fmtr Format) (Handler, error) {
- f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
- return nil, err
+ if t.Kind() != reflect.Func {
+ return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
}
- return closingHandler{f, StreamHandler(f, fmtr)}, nil
-}
-// NetHandler opens a socket to the given address and writes records
-// over the connection.
-func NetHandler(network, addr string, fmtr Format) (Handler, error) {
- conn, err := net.Dial(network, addr)
- if err != nil {
- return nil, err
+ if t.NumIn() > 0 {
+ return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
}
- return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
-}
-
-// XXX: closingHandler is essentially unused at the moment
-// it's meant for a future time when the Handler interface supports
-// a possible Close() operation
-type closingHandler struct {
- io.WriteCloser
- Handler
-}
+ if t.NumOut() == 0 {
+ return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
+ }
-func (h *closingHandler) Close() error {
- return h.WriteCloser.Close()
+ value := reflect.ValueOf(lz.Fn)
+ results := value.Call([]reflect.Value{})
+ if len(results) == 1 {
+ return results[0].Interface(), nil
+ }
+ values := make([]interface{}, len(results))
+ for i, v := range results {
+ values[i] = v.Interface()
+ }
+ return values, nil
}
-// CallerFileHandler returns a Handler that adds the line number and file of
-// the calling function to the context with key "caller".
-func CallerFileHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
- return h.Log(r)
- })
-}
+type discardHandler struct{}
-// CallerFuncHandler returns a Handler that adds the calling function name to
-// the context with key "fn".
-func CallerFuncHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
- return h.Log(r)
- })
+// DiscardHandler returns a no-op handler
+func DiscardHandler() slog.Handler {
+ return &discardHandler{}
}
-// This function is here to please go vet on Go < 1.8.
-func formatCall(format string, c stack.Call) string {
- return fmt.Sprintf(format, c)
+func (h *discardHandler) Handle(_ context.Context, r slog.Record) error {
+ return nil
}
-// CallerStackHandler returns a Handler that adds a stack trace to the context
-// with key "stack". The stack trace is formatted as a space separated list of
-// call sites inside matching []'s. The most recent call site is listed first.
-// Each call site is formatted according to format. See the documentation of
-// package github.com/go-stack/stack for the list of supported formats.
-func CallerStackHandler(format string, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
- if len(s) > 0 {
- r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
- }
- return h.Log(r)
- })
+func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool {
+ return false
}
-// FilterHandler returns a Handler that only writes records to the
-// wrapped Handler if the given function evaluates true. For example,
-// to only log records where the 'err' key is not nil:
-//
-// logger.SetHandler(FilterHandler(func(r *Record) bool {
-// for i := 0; i < len(r.Ctx); i += 2 {
-// if r.Ctx[i] == "err" {
-// return r.Ctx[i+1] != nil
-// }
-// }
-// return false
-// }, h))
-func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- if fn(r) {
- return h.Log(r)
- }
- return nil
- })
+func (h *discardHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
}
-// MatchFilterHandler returns a Handler that only writes records
-// to the wrapped Handler if the given key in the logged
-// context matches the value. For example, to only log records
-// from your ui package:
-//
-// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
-func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- switch key {
- case r.KeyNames.Lvl:
- return r.Lvl == value
- case r.KeyNames.Time:
- return r.Time == value
- case r.KeyNames.Msg:
- return r.Msg == value
- }
-
- for i := 0; i < len(r.Ctx); i += 2 {
- if r.Ctx[i] == key {
- return r.Ctx[i+1] == value
- }
- }
- return false
- }, h)
+func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return &discardHandler{}
}
-// LvlFilterHandler returns a Handler that only writes
-// records which are less than the given verbosity
-// level to the wrapped Handler. For example, to only
-// log Error/Crit records:
-//
-// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
-func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- return r.Lvl <= maxLvl
- }, h)
+type TerminalHandler struct {
+ mu sync.Mutex
+ wr io.Writer
+ lvl slog.Level
+ useColor bool
+ attrs []slog.Attr
+ // fieldPadding is a map with maximum field value lengths seen until now
+ // to allow padding log contexts in a bit smarter way.
+ fieldPadding map[string]int
}
-// MultiHandler dispatches any write to each of its handlers.
-// This is useful for writing different types of log information
-// to different locations. For example, to log to a file and
-// standard error:
+// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on
+// a terminal with color-coded level output and terser human friendly timestamp.
+// This format should only be used for interactive programs or while developing.
//
-// log.MultiHandler(
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StderrHandler)
-func MultiHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- for _, h := range hs {
- // what to do about failures?
- h.Log(r)
- }
- return nil
- })
-}
-
-// FailoverHandler writes all log records to the first handler
-// specified, but will failover and write to the second handler if
-// the first handler has failed, and so on for all handlers specified.
-// For example you might want to log to a network socket, but failover
-// to writing to a file if the network fails, and then to
-// standard out if the file write fails:
+// [LEVEL] [TIME] MESSAGE key=value key=value ...
//
-// log.FailoverHandler(
-// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StdoutHandler)
+// Example:
//
-// All writes that do not go to the first handler will add context with keys of
-// the form "failover_err_{idx}" which explain the error encountered while
-// trying to write to the handlers before them in the list.
-func FailoverHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- var err error
- for i, h := range hs {
- err = h.Log(r)
- if err == nil {
- return nil
- }
- r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
- }
-
- return err
- })
+// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
+func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler {
+ return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor)
+}
+
+// NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs
+// records which are less than or equal to the specified verbosity level.
+func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler {
+ return &TerminalHandler{
+ wr: wr,
+ lvl: lvl,
+ useColor: useColor,
+ fieldPadding: make(map[string]int),
+ }
}
-// ChannelHandler writes all records to the given channel.
-// It blocks if the channel is full. Useful for async processing
-// of log messages, it's used by BufferedHandler.
-func ChannelHandler(recs chan<- *Record) Handler {
- return FuncHandler(func(r *Record) error {
- recs <- r
- return nil
- })
+func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.wr.Write(h.TerminalFormat(r, h.useColor))
+ return nil
}
-// BufferedHandler writes all records to a buffered
-// channel of the given size which flushes into the wrapped
-// handler whenever it is available for writing. Since these
-// writes happen asynchronously, all writes to a BufferedHandler
-// never return an error and any errors from the wrapped handler are ignored.
-func BufferedHandler(bufSize int, h Handler) Handler {
- recs := make(chan *Record, bufSize)
- go func() {
- for m := range recs {
- _ = h.Log(m)
- }
- }()
- return ChannelHandler(recs)
+func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool {
+ return level >= h.lvl
}
-// LazyHandler writes all values to the wrapped handler after evaluating
-// any lazy functions in the record's context. It is already wrapped
-// around StreamHandler and SyslogHandler in this library, you'll only need
-// it if you write your own Handler.
-func LazyHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- // go through the values (odd indices) and reassign
- // the values of any lazy fn to the result of its execution
- hadErr := false
- for i := 1; i < len(r.Ctx); i += 2 {
- lz, ok := r.Ctx[i].(Lazy)
- if ok {
- v, err := evaluateLazy(lz)
- if err != nil {
- hadErr = true
- r.Ctx[i] = err
- } else {
- if cs, ok := v.(stack.CallStack); ok {
- v = cs.TrimBelow(r.Call).TrimRuntime()
- }
- r.Ctx[i] = v
- }
- }
- }
-
- if hadErr {
- r.Ctx = append(r.Ctx, errorKey, "bad lazy")
- }
-
- return h.Log(r)
- })
+func (h *TerminalHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
}
-func evaluateLazy(lz Lazy) (interface{}, error) {
- t := reflect.TypeOf(lz.Fn)
-
- if t.Kind() != reflect.Func {
- return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
- }
-
- if t.NumIn() > 0 {
- return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
+func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return &TerminalHandler{
+ wr: h.wr,
+ lvl: h.lvl,
+ useColor: h.useColor,
+ attrs: append(h.attrs, attrs...),
+ fieldPadding: make(map[string]int),
}
-
- if t.NumOut() == 0 {
- return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
- }
-
- value := reflect.ValueOf(lz.Fn)
- results := value.Call([]reflect.Value{})
- if len(results) == 1 {
- return results[0].Interface(), nil
- }
- values := make([]interface{}, len(results))
- for i, v := range results {
- values[i] = v.Interface()
- }
- return values, nil
}
-// DiscardHandler reports success for all writes but does nothing.
-// It is useful for dynamically disabling logging at runtime via
-// a Logger's SetHandler method.
-func DiscardHandler() Handler {
- return FuncHandler(func(r *Record) error {
- return nil
- })
+// ResetFieldPadding zeroes the field-padding for all attribute pairs.
+func (t *TerminalHandler) ResetFieldPadding() {
+ t.mu.Lock()
+ t.fieldPadding = make(map[string]int)
+ t.mu.Unlock()
}
-// Must provides the following Handler creation functions
-// which instead of returning an error parameter only return a Handler
-// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
-var Must muster
+type leveler struct{ minLevel slog.Level }
-func must(h Handler, err error) Handler {
- if err != nil {
- panic(err)
- }
- return h
+func (l *leveler) Level() slog.Level {
+ return l.minLevel
}
-type muster struct{}
-
-func (m muster) FileHandler(path string, fmtr Format) Handler {
- return must(FileHandler(path, fmtr))
+func JSONHandler(wr io.Writer) slog.Handler {
+ return slog.NewJSONHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceJSON,
+ })
}
-func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
- return must(NetHandler(network, addr, fmtr))
+// LogfmtHandler returns a handler which prints records in logfmt format, an easy machine-parseable but human-readable
+// format for key/value pairs.
+//
+// For more details see: http://godoc.org/github.com/kr/logfmt
+func LogfmtHandler(wr io.Writer) slog.Handler {
+ return slog.NewTextHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceLogfmt,
+ })
}
-// swapHandler wraps another handler that may be swapped out
-// dynamically at runtime in a thread-safe fashion.
-type swapHandler struct {
- handler atomic.Value
+// LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs
+// records which are less than or equal to the specified verbosity level.
+func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
+ return slog.NewTextHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceLogfmt,
+ Level: &leveler{level},
+ })
}
-func (h *swapHandler) Log(r *Record) error {
- return (*h.handler.Load().(*Handler)).Log(r)
+func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr {
+ return builtinReplace(nil, attr, true)
}
-func (h *swapHandler) Swap(newHandler Handler) {
- h.handler.Store(&newHandler)
+func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr {
+ return builtinReplace(nil, attr, false)
}
-func (h *swapHandler) Get() Handler {
- return *h.handler.Load().(*Handler)
+func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr {
+ switch attr.Key {
+ case slog.TimeKey:
+ if attr.Value.Kind() == slog.KindTime {
+ if logfmt {
+ return slog.String("t", attr.Value.Time().Format(timeFormat))
+ } else {
+ return slog.Attr{Key: "t", Value: attr.Value}
+ }
+ }
+ case slog.LevelKey:
+ if l, ok := attr.Value.Any().(slog.Level); ok {
+ attr = slog.Any("lvl", LevelString(l))
+ return attr
+ }
+ }
+
+ switch v := attr.Value.Any().(type) {
+ case time.Time:
+ if logfmt {
+ attr = slog.String(attr.Key, v.Format(timeFormat))
+ }
+ case *big.Int:
+ if v == nil {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.String())
+ }
+ case *uint256.Int:
+ if v == nil {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.Dec())
+ }
+ case fmt.Stringer:
+ if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.String())
+ }
+ }
+ return attr
}
diff --git a/log/handler_glog.go b/log/handler_glog.go
index afca0808b38a..fb1e03c5b532 100644
--- a/log/handler_glog.go
+++ b/log/handler_glog.go
@@ -17,6 +17,7 @@
package log
import (
+ "context"
"errors"
"fmt"
"regexp"
@@ -25,54 +26,47 @@ import (
"strings"
"sync"
"sync/atomic"
+
+ "golang.org/x/exp/slog"
)
// errVmoduleSyntax is returned when a user vmodule pattern is invalid.
var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N")
-// errTraceSyntax is returned when a user backtrace pattern is invalid.
-var errTraceSyntax = errors.New("expect file.go:234")
-
// GlogHandler is a log handler that mimics the filtering features of Google's
// glog logger: setting global log levels; overriding with callsite pattern
// matches; and requesting backtraces at certain positions.
type GlogHandler struct {
- origin Handler // The origin handler this wraps
+ origin slog.Handler // The origin handler this wraps
- level atomic.Uint32 // Current log level, atomically accessible
- override atomic.Bool // Flag whether overrides are used, atomically accessible
- backtrace atomic.Bool // Flag whether backtrace location is set
+ level atomic.Int32 // Current log level, atomically accessible
+ override atomic.Bool // Flag whether overrides are used, atomically accessible
- patterns []pattern // Current list of patterns to override with
- siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations
- location string // file:line location where to do a stackdump at
- lock sync.RWMutex // Lock protecting the override pattern list
+ patterns []pattern // Current list of patterns to override with
+ siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations
+ location string // file:line location where to do a stackdump at
+ lock sync.RWMutex // Lock protecting the override pattern list
}
// NewGlogHandler creates a new log handler with filtering functionality similar
// to Google's glog logger. The returned handler implements Handler.
-func NewGlogHandler(h Handler) *GlogHandler {
+func NewGlogHandler(h slog.Handler) *GlogHandler {
return &GlogHandler{
origin: h,
}
}
-// SetHandler updates the handler to write records to the specified sub-handler.
-func (h *GlogHandler) SetHandler(nh Handler) {
- h.origin = nh
-}
-
// pattern contains a filter for the Vmodule option, holding a verbosity level
// and a file pattern to match.
type pattern struct {
pattern *regexp.Regexp
- level Lvl
+ level slog.Level
}
// Verbosity sets the glog verbosity ceiling. The verbosity of individual packages
// and source files can be raised using Vmodule.
-func (h *GlogHandler) Verbosity(level Lvl) {
- h.level.Store(uint32(level))
+func (h *GlogHandler) Verbosity(level slog.Level) {
+ h.level.Store(int32(level))
}
// Vmodule sets the glog verbosity pattern.
@@ -108,11 +102,13 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
return errVmoduleSyntax
}
// Parse the level and if correct, assemble the filter rule
- level, err := strconv.Atoi(parts[1])
+ l, err := strconv.Atoi(parts[1])
if err != nil {
return errVmoduleSyntax
}
- if level <= 0 {
+ level := FromLegacyLevel(l)
+
+ if level == LevelCrit {
continue // Ignore. It's harmless but no point in paying the overhead.
}
// Compile the rule pattern into a regular expression
@@ -130,107 +126,84 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
matcher = matcher + "$"
re, _ := regexp.Compile(matcher)
- filter = append(filter, pattern{re, Lvl(level)})
+ filter = append(filter, pattern{re, level})
}
// Swap out the vmodule pattern for the new filter system
h.lock.Lock()
defer h.lock.Unlock()
h.patterns = filter
- h.siteCache = make(map[uintptr]Lvl)
+ h.siteCache = make(map[uintptr]slog.Level)
h.override.Store(len(filter) != 0)
- // Enable location storage (globally)
- if len(h.patterns) > 0 {
- stackEnabled.Store(true)
- }
+
return nil
}
-// BacktraceAt sets the glog backtrace location. When set to a file and line
-// number holding a logging statement, a stack trace will be written to the Info
-// log whenever execution hits that statement.
-//
-// Unlike with Vmodule, the ".go" must be present.
-func (h *GlogHandler) BacktraceAt(location string) error {
- // Ensure the backtrace location contains two non-empty elements
- parts := strings.Split(location, ":")
- if len(parts) != 2 {
- return errTraceSyntax
- }
- parts[0] = strings.TrimSpace(parts[0])
- parts[1] = strings.TrimSpace(parts[1])
- if len(parts[0]) == 0 || len(parts[1]) == 0 {
- return errTraceSyntax
- }
- // Ensure the .go prefix is present and the line is valid
- if !strings.HasSuffix(parts[0], ".go") {
- return errTraceSyntax
+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
+ return h.override.Load() || slog.Level(h.level.Load()) <= lvl
+}
+
+func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ h.lock.RLock()
+ siteCache := make(map[uintptr]slog.Level)
+ for k, v := range h.siteCache {
+ siteCache[k] = v
}
- if _, err := strconv.Atoi(parts[1]); err != nil {
- return errTraceSyntax
+ h.lock.RUnlock()
+
+ patterns := []pattern{}
+ patterns = append(patterns, h.patterns...)
+
+ res := GlogHandler{
+ origin: h.origin.WithAttrs(attrs),
+ patterns: patterns,
+ siteCache: siteCache,
+ location: h.location,
}
- // All seems valid
- h.lock.Lock()
- defer h.lock.Unlock()
- h.location = location
- h.backtrace.Store(len(location) > 0)
- // Enable location storage (globally)
- stackEnabled.Store(true)
- return nil
+ res.level.Store(h.level.Load())
+ res.override.Store(h.override.Load())
+ return &res
+}
+
+func (h *GlogHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
}
// Log implements Handler.Log, filtering a log record through the global, local
// and backtrace filters, finally emitting it if either allow it through.
-func (h *GlogHandler) Log(r *Record) error {
- // If backtracing is requested, check whether this is the callsite
- if h.backtrace.Load() {
- // Everything below here is slow. Although we could cache the call sites the
- // same way as for vmodule, backtracing is so rare it's not worth the extra
- // complexity.
- h.lock.RLock()
- match := h.location == r.Call.String()
- h.lock.RUnlock()
-
- if match {
- // Callsite matched, raise the log level to info and gather the stacks
- r.Lvl = LvlInfo
-
- buf := make([]byte, 1024*1024)
- buf = buf[:runtime.Stack(buf, true)]
- r.Msg += "\n\n" + string(buf)
- }
- }
+func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
// If the global log level allows, fast track logging
- if h.level.Load() >= uint32(r.Lvl) {
- return h.origin.Log(r)
- }
- // If no local overrides are present, fast track skipping
- if !h.override.Load() {
- return nil
+ if slog.Level(h.level.Load()) <= r.Level {
+ return h.origin.Handle(context.Background(), r)
}
+
// Check callsite cache for previously calculated log levels
h.lock.RLock()
- lvl, ok := h.siteCache[r.Call.Frame().PC]
+ lvl, ok := h.siteCache[r.PC]
h.lock.RUnlock()
// If we didn't cache the callsite yet, calculate it
if !ok {
h.lock.Lock()
+
+ fs := runtime.CallersFrames([]uintptr{r.PC})
+ frame, _ := fs.Next()
+
for _, rule := range h.patterns {
- if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
- h.siteCache[r.Call.Frame().PC], lvl, ok = rule.level, rule.level, true
- break
+ if rule.pattern.MatchString(fmt.Sprintf("%+s", frame.File)) {
+ h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true
}
}
// If no rule matched, remember to drop log the next time
if !ok {
- h.siteCache[r.Call.Frame().PC] = 0
+ h.siteCache[r.PC] = 0
}
h.lock.Unlock()
}
- if lvl >= r.Lvl {
- return h.origin.Log(r)
+ if lvl <= r.Level {
+ return h.origin.Handle(context.Background(), r)
}
return nil
}
diff --git a/log/logger.go b/log/logger.go
index 42e7e375d002..3e227745adf5 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -1,294 +1,222 @@
package log
import (
- "fmt"
+ "context"
+ "math"
"os"
+ "runtime"
"time"
- "github.com/go-stack/stack"
+ "golang.org/x/exp/slog"
)
-const timeKey = "t"
-const lvlKey = "lvl"
-const msgKey = "msg"
-const ctxKey = "ctx"
-const errorKey = "LOG15_ERROR"
-const skipLevel = 2
+const errorKey = "LOG_ERROR"
-type Lvl int
+const (
+ legacyLevelCrit = iota
+ legacyLevelError
+ legacyLevelWarn
+ legacyLevelInfo
+ legacyLevelDebug
+ legacyLevelTrace
+)
const (
- LvlCrit Lvl = iota
- LvlError
- LvlWarn
- LvlInfo
- LvlDebug
- LvlTrace
+ levelMaxVerbosity slog.Level = math.MinInt
+ LevelTrace slog.Level = -8
+ LevelDebug = slog.LevelDebug
+ LevelInfo = slog.LevelInfo
+ LevelWarn = slog.LevelWarn
+ LevelError = slog.LevelError
+ LevelCrit slog.Level = 12
+
+ // for backward-compatibility
+ LvlTrace = LevelTrace
+ LvlInfo = LevelInfo
+ LvlDebug = LevelDebug
)
-// AlignedString returns a 5-character string containing the name of a Lvl.
-func (l Lvl) AlignedString() string {
+// convert from old Geth verbosity level constants
+// to levels defined by slog
+func FromLegacyLevel(lvl int) slog.Level {
+ switch lvl {
+ case legacyLevelCrit:
+ return LevelCrit
+ case legacyLevelError:
+ return slog.LevelError
+ case legacyLevelWarn:
+ return slog.LevelWarn
+ case legacyLevelInfo:
+ return slog.LevelInfo
+ case legacyLevelDebug:
+ return slog.LevelDebug
+ case legacyLevelTrace:
+ return LevelTrace
+ default:
+ break
+ }
+
+ // TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here?
+ if lvl > legacyLevelTrace {
+ return LevelTrace
+ }
+ return LevelCrit
+}
+
+// LevelAlignedString returns a 5-character string containing the name of a Lvl.
+func LevelAlignedString(l slog.Level) string {
switch l {
- case LvlTrace:
+ case LevelTrace:
return "TRACE"
- case LvlDebug:
+ case slog.LevelDebug:
return "DEBUG"
- case LvlInfo:
+ case slog.LevelInfo:
return "INFO "
- case LvlWarn:
+ case slog.LevelWarn:
return "WARN "
- case LvlError:
+ case slog.LevelError:
return "ERROR"
- case LvlCrit:
+ case LevelCrit:
return "CRIT "
default:
- panic("bad level")
+ return "unknown level"
}
}
-// String returns the name of a Lvl.
-func (l Lvl) String() string {
+// LevelString returns a 5-character string containing the name of a Lvl.
+func LevelString(l slog.Level) string {
switch l {
- case LvlTrace:
- return "trce"
- case LvlDebug:
- return "dbug"
- case LvlInfo:
+ case LevelTrace:
+ return "trace"
+ case slog.LevelDebug:
+ return "debug"
+ case slog.LevelInfo:
return "info"
- case LvlWarn:
+ case slog.LevelWarn:
return "warn"
- case LvlError:
+ case slog.LevelError:
return "eror"
- case LvlCrit:
+ case LevelCrit:
return "crit"
default:
- panic("bad level")
+ return "unknown"
}
}
-// LvlFromString returns the appropriate Lvl from a string name.
-// Useful for parsing command line args and configuration files.
-func LvlFromString(lvlString string) (Lvl, error) {
- switch lvlString {
- case "trace", "trce":
- return LvlTrace, nil
- case "debug", "dbug":
- return LvlDebug, nil
- case "info":
- return LvlInfo, nil
- case "warn":
- return LvlWarn, nil
- case "error", "eror":
- return LvlError, nil
- case "crit":
- return LvlCrit, nil
- default:
- return LvlDebug, fmt.Errorf("unknown level: %v", lvlString)
- }
-}
-
-// A Record is what a Logger asks its handler to write
-type Record struct {
- Time time.Time
- Lvl Lvl
- Msg string
- Ctx []interface{}
- Call stack.Call
- KeyNames RecordKeyNames
-}
-
-// RecordKeyNames gets stored in a Record when the write function is executed.
-type RecordKeyNames struct {
- Time string
- Msg string
- Lvl string
- Ctx string
-}
-
// A Logger writes key/value pairs to a Handler
type Logger interface {
- // New returns a new Logger that has this logger's context plus the given context
- New(ctx ...interface{}) Logger
+ // With returns a new Logger that has this logger's attributes plus the given attributes
+ With(ctx ...interface{}) Logger
- // GetHandler gets the handler associated with the logger.
- GetHandler() Handler
+ // With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'.
+ New(ctx ...interface{}) Logger
- // SetHandler updates the logger to write records to the specified handler.
- SetHandler(h Handler)
+ // Log logs a message at the specified level with context key/value pairs
+ Log(level slog.Level, msg string, ctx ...interface{})
- // Log a message at the trace level with context key/value pairs
- //
- // # Usage
- //
- // log.Trace("msg")
- // log.Trace("msg", "key1", val1)
- // log.Trace("msg", "key1", val1, "key2", val2)
+ // Trace log a message at the trace level with context key/value pairs
Trace(msg string, ctx ...interface{})
- // Log a message at the debug level with context key/value pairs
- //
- // # Usage Examples
- //
- // log.Debug("msg")
- // log.Debug("msg", "key1", val1)
- // log.Debug("msg", "key1", val1, "key2", val2)
+ // Debug logs a message at the debug level with context key/value pairs
Debug(msg string, ctx ...interface{})
- // Log a message at the info level with context key/value pairs
- //
- // # Usage Examples
- //
- // log.Info("msg")
- // log.Info("msg", "key1", val1)
- // log.Info("msg", "key1", val1, "key2", val2)
+ // Info logs a message at the info level with context key/value pairs
Info(msg string, ctx ...interface{})
- // Log a message at the warn level with context key/value pairs
- //
- // # Usage Examples
- //
- // log.Warn("msg")
- // log.Warn("msg", "key1", val1)
- // log.Warn("msg", "key1", val1, "key2", val2)
+ // Warn logs a message at the warn level with context key/value pairs
Warn(msg string, ctx ...interface{})
- // Log a message at the error level with context key/value pairs
- //
- // # Usage Examples
- //
- // log.Error("msg")
- // log.Error("msg", "key1", val1)
- // log.Error("msg", "key1", val1, "key2", val2)
+ // Error logs a message at the error level with context key/value pairs
Error(msg string, ctx ...interface{})
- // Log a message at the crit level with context key/value pairs, and then exit.
- //
- // # Usage Examples
- //
- // log.Crit("msg")
- // log.Crit("msg", "key1", val1)
- // log.Crit("msg", "key1", val1, "key2", val2)
+ // Crit logs a message at the crit level with context key/value pairs, and exits
Crit(msg string, ctx ...interface{})
+
+ // Write logs a message at the specified level
+ Write(level slog.Level, msg string, attrs ...any)
}
type logger struct {
- ctx []interface{}
- h *swapHandler
+ inner *slog.Logger
}
-func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
- record := &Record{
- Time: time.Now(),
- Lvl: lvl,
- Msg: msg,
- Ctx: newContext(l.ctx, ctx),
- KeyNames: RecordKeyNames{
- Time: timeKey,
- Msg: msgKey,
- Lvl: lvlKey,
- Ctx: ctxKey,
- },
- }
- if stackEnabled.Load() {
- record.Call = stack.Caller(skip)
+// NewLogger returns a logger with the specified handler set
+func NewLogger(h slog.Handler) Logger {
+ return &logger{
+ slog.New(h),
}
- l.h.Log(record)
}
-func (l *logger) New(ctx ...interface{}) Logger {
- child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
- child.SetHandler(l.h)
- return child
-}
+// write logs a message at the specified level:
+func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
+ if !l.inner.Enabled(context.Background(), level) {
+ return
+ }
-func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
- normalizedSuffix := normalize(suffix)
- newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
- n := copy(newCtx, prefix)
- copy(newCtx[n:], normalizedSuffix)
- return newCtx
-}
+ var pcs [1]uintptr
+ runtime.Callers(3, pcs[:])
-func (l *logger) Trace(msg string, ctx ...interface{}) {
- l.write(msg, LvlTrace, ctx, skipLevel)
-}
+ if len(attrs)%2 != 0 {
+ attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
+ }
-func (l *logger) Debug(msg string, ctx ...interface{}) {
- l.write(msg, LvlDebug, ctx, skipLevel)
-}
+ // evaluate lazy values
+ var hadErr bool
+ for i := 1; i < len(attrs); i += 2 {
+ lz, ok := attrs[i].(Lazy)
+ if ok {
+ v, err := evaluateLazy(lz)
+ if err != nil {
+ hadErr = true
+ attrs[i] = err
+ } else {
+ attrs[i] = v
+ }
+ }
+ }
-func (l *logger) Info(msg string, ctx ...interface{}) {
- l.write(msg, LvlInfo, ctx, skipLevel)
-}
+ if hadErr {
+ attrs = append(attrs, errorKey, "bad lazy")
+ }
-func (l *logger) Warn(msg string, ctx ...interface{}) {
- l.write(msg, LvlWarn, ctx, skipLevel)
+ r := slog.NewRecord(time.Now(), level, msg, pcs[0])
+ r.Add(attrs...)
+ l.inner.Handler().Handle(context.Background(), r)
}
-func (l *logger) Error(msg string, ctx ...interface{}) {
- l.write(msg, LvlError, ctx, skipLevel)
+func (l *logger) Log(level slog.Level, msg string, attrs ...any) {
+ l.Write(level, msg, attrs...)
}
-func (l *logger) Crit(msg string, ctx ...interface{}) {
- l.write(msg, LvlCrit, ctx, skipLevel)
- os.Exit(1)
+func (l *logger) With(ctx ...interface{}) Logger {
+ return &logger{l.inner.With(ctx...)}
}
-func (l *logger) GetHandler() Handler {
- return l.h.Get()
+func (l *logger) New(ctx ...interface{}) Logger {
+ return l.With(ctx...)
}
-func (l *logger) SetHandler(h Handler) {
- l.h.Swap(h)
+func (l *logger) Trace(msg string, ctx ...interface{}) {
+ l.Write(LevelTrace, msg, ctx...)
}
-func normalize(ctx []interface{}) []interface{} {
- // if the caller passed a Ctx object, then expand it
- if len(ctx) == 1 {
- if ctxMap, ok := ctx[0].(Ctx); ok {
- ctx = ctxMap.toArray()
- }
- }
-
- // ctx needs to be even because it's a series of key/value pairs
- // no one wants to check for errors on logging functions,
- // so instead of erroring on bad input, we'll just make sure
- // that things are the right length and users can fix bugs
- // when they see the output looks wrong
- if len(ctx)%2 != 0 {
- ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
- }
-
- return ctx
+func (l *logger) Debug(msg string, ctx ...interface{}) {
+ l.Write(slog.LevelDebug, msg, ctx...)
}
-// Lazy allows you to defer calculation of a logged value that is expensive
-// to compute until it is certain that it must be evaluated with the given filters.
-//
-// Lazy may also be used in conjunction with a Logger's New() function
-// to generate a child logger which always reports the current value of changing
-// state.
-//
-// You may wrap any function which takes no arguments to Lazy. It may return any
-// number of values of any type.
-type Lazy struct {
- Fn interface{}
+func (l *logger) Info(msg string, ctx ...interface{}) {
+ l.Write(slog.LevelInfo, msg, ctx...)
}
-// Ctx is a map of key/value pairs to pass as context to a log function
-// Use this only if you really need greater safety around the arguments you pass
-// to the logging functions.
-type Ctx map[string]interface{}
-
-func (c Ctx) toArray() []interface{} {
- arr := make([]interface{}, len(c)*2)
+func (l *logger) Warn(msg string, ctx ...any) {
+ l.Write(slog.LevelWarn, msg, ctx...)
+}
- i := 0
- for k, v := range c {
- arr[i] = k
- arr[i+1] = v
- i += 2
- }
+func (l *logger) Error(msg string, ctx ...interface{}) {
+ l.Write(slog.LevelError, msg, ctx...)
+}
- return arr
+func (l *logger) Crit(msg string, ctx ...interface{}) {
+ l.Write(LevelCrit, msg, ctx...)
+ os.Exit(1)
}
diff --git a/log/logger_test.go b/log/logger_test.go
index 2e59b3fdf0b1..fca1f1680f05 100644
--- a/log/logger_test.go
+++ b/log/logger_test.go
@@ -5,61 +5,47 @@ import (
"os"
"strings"
"testing"
+
+ "golang.org/x/exp/slog"
)
-// TestLoggingWithTrace checks that if BackTraceAt is set, then the
-// gloghandler is capable of spitting out a stacktrace
-func TestLoggingWithTrace(t *testing.T) {
- defer stackEnabled.Store(stackEnabled.Load())
+// TestLoggingWithVmodule checks that vmodule works.
+func TestLoggingWithVmodule(t *testing.T) {
out := new(bytes.Buffer)
- logger := New()
- {
- glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
- glog.Verbosity(LvlTrace)
- if err := glog.BacktraceAt("logger_test.go:24"); err != nil {
- t.Fatal(err)
- }
- logger.SetHandler(glog)
- }
- logger.Trace("a message", "foo", "bar") // Will be bumped to INFO
+ glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false))
+ glog.Verbosity(LevelCrit)
+ logger := NewLogger(glog)
+ logger.Warn("This should not be seen", "ignored", "true")
+ glog.Vmodule("logger_test.go=5")
+ logger.Trace("a message", "foo", "bar")
have := out.String()
- if !strings.HasPrefix(have, "INFO") {
- t.Fatalf("backtraceat should bump level to info: %s", have)
- }
// The timestamp is locale-dependent, so we want to trim that off
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
have = strings.Split(have, "]")[1]
- wantPrefix := " a message\n\ngoroutine"
- if !strings.HasPrefix(have, wantPrefix) {
- t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix)
+ want := " a message foo=bar\n"
+ if have != want {
+ t.Errorf("\nhave: %q\nwant: %q\n", have, want)
}
}
-// TestLoggingWithVmodule checks that vmodule works.
-func TestLoggingWithVmodule(t *testing.T) {
- defer stackEnabled.Store(stackEnabled.Load())
+func TestTerminalHandlerWithAttrs(t *testing.T) {
out := new(bytes.Buffer)
- logger := New()
- {
- glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
- glog.Verbosity(LvlCrit)
- logger.SetHandler(glog)
- logger.Warn("This should not be seen", "ignored", "true")
- glog.Vmodule("logger_test.go=5")
- }
+ glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}))
+ glog.Verbosity(LevelTrace)
+ logger := NewLogger(glog)
logger.Trace("a message", "foo", "bar")
have := out.String()
// The timestamp is locale-dependent, so we want to trim that off
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
have = strings.Split(have, "]")[1]
- want := " a message foo=bar\n"
+ want := " a message baz=bat foo=bar\n"
if have != want {
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
}
}
func BenchmarkTraceLogging(b *testing.B) {
- Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true))))
+ SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
Trace("a message", "v", i)
diff --git a/log/root.go b/log/root.go
index 5a41723c3eeb..71040fff47c2 100644
--- a/log/root.go
+++ b/log/root.go
@@ -2,31 +2,33 @@ package log
import (
"os"
-)
+ "sync/atomic"
-var (
- root = &logger{[]interface{}{}, new(swapHandler)}
- StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat())
- StderrHandler = StreamHandler(os.Stderr, LogfmtFormat())
+ "golang.org/x/exp/slog"
)
+var root atomic.Value
+
func init() {
- root.SetHandler(DiscardHandler())
+ defaultLogger := &logger{slog.New(DiscardHandler())}
+ SetDefault(defaultLogger)
}
-// New returns a new logger with the given context.
-// New is a convenient alias for Root().New
-func New(ctx ...interface{}) Logger {
- return root.New(ctx...)
+// SetDefault sets the default global logger
+func SetDefault(l Logger) {
+ root.Store(l)
+ if lg, ok := l.(*logger); ok {
+ slog.SetDefault(lg.inner)
+ }
}
// Root returns the root logger
func Root() Logger {
- return root
+ return root.Load().(Logger)
}
// The following functions bypass the exported logger methods (logger.Debug,
-// etc.) to keep the call depth the same for all paths to logger.write so
+// etc.) to keep the call depth the same for all paths to logger.Write so
// runtime.Caller(2) always refers to the call site in client code.
// Trace is a convenient alias for Root().Trace
@@ -39,7 +41,7 @@ func Root() Logger {
// log.Trace("msg", "key1", val1)
// log.Trace("msg", "key1", val1, "key2", val2)
func Trace(msg string, ctx ...interface{}) {
- root.write(msg, LvlTrace, ctx, skipLevel)
+ Root().Write(LevelTrace, msg, ctx...)
}
// Debug is a convenient alias for Root().Debug
@@ -52,7 +54,7 @@ func Trace(msg string, ctx ...interface{}) {
// log.Debug("msg", "key1", val1)
// log.Debug("msg", "key1", val1, "key2", val2)
func Debug(msg string, ctx ...interface{}) {
- root.write(msg, LvlDebug, ctx, skipLevel)
+ Root().Write(slog.LevelDebug, msg, ctx...)
}
// Info is a convenient alias for Root().Info
@@ -65,7 +67,7 @@ func Debug(msg string, ctx ...interface{}) {
// log.Info("msg", "key1", val1)
// log.Info("msg", "key1", val1, "key2", val2)
func Info(msg string, ctx ...interface{}) {
- root.write(msg, LvlInfo, ctx, skipLevel)
+ Root().Write(slog.LevelInfo, msg, ctx...)
}
// Warn is a convenient alias for Root().Warn
@@ -78,7 +80,7 @@ func Info(msg string, ctx ...interface{}) {
// log.Warn("msg", "key1", val1)
// log.Warn("msg", "key1", val1, "key2", val2)
func Warn(msg string, ctx ...interface{}) {
- root.write(msg, LvlWarn, ctx, skipLevel)
+ Root().Write(slog.LevelWarn, msg, ctx...)
}
// Error is a convenient alias for Root().Error
@@ -91,7 +93,7 @@ func Warn(msg string, ctx ...interface{}) {
// log.Error("msg", "key1", val1)
// log.Error("msg", "key1", val1, "key2", val2)
func Error(msg string, ctx ...interface{}) {
- root.write(msg, LvlError, ctx, skipLevel)
+ Root().Write(slog.LevelError, msg, ctx...)
}
// Crit is a convenient alias for Root().Crit
@@ -104,15 +106,12 @@ func Error(msg string, ctx ...interface{}) {
// log.Crit("msg", "key1", val1)
// log.Crit("msg", "key1", val1, "key2", val2)
func Crit(msg string, ctx ...interface{}) {
- root.write(msg, LvlCrit, ctx, skipLevel)
+ Root().Write(LevelCrit, msg, ctx...)
os.Exit(1)
}
-// Output is a convenient alias for write, allowing for the modification of
-// the calldepth (number of stack frames to skip).
-// calldepth influences the reported line number of the log message.
-// A calldepth of zero reports the immediate caller of Output.
-// Non-zero calldepth skips as many stack frames.
-func Output(msg string, lvl Lvl, calldepth int, ctx ...interface{}) {
- root.write(msg, lvl, ctx, calldepth+skipLevel)
+// New returns a new logger with the given context.
+// New is a convenient alias for Root().New
+func New(ctx ...interface{}) Logger {
+ return Root().With(ctx...)
}
diff --git a/log/syslog.go b/log/syslog.go
deleted file mode 100644
index 451d831b6dd1..000000000000
--- a/log/syslog.go
+++ /dev/null
@@ -1,58 +0,0 @@
-//go:build !windows && !plan9
-// +build !windows,!plan9
-
-package log
-
-import (
- "log/syslog"
- "strings"
-)
-
-// SyslogHandler opens a connection to the system syslog daemon by calling
-// syslog.New and writes all records to it.
-func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.New(priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-// SyslogNetHandler opens a connection to a log daemon over the network and writes
-// all log records to it.
-func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.Dial(net, addr, priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) {
- if err != nil {
- return nil, err
- }
- h := FuncHandler(func(r *Record) error {
- var syslogFn = sysWr.Info
- switch r.Lvl {
- case LvlCrit:
- syslogFn = sysWr.Crit
- case LvlError:
- syslogFn = sysWr.Err
- case LvlWarn:
- syslogFn = sysWr.Warning
- case LvlInfo:
- syslogFn = sysWr.Info
- case LvlDebug:
- syslogFn = sysWr.Debug
- case LvlTrace:
- syslogFn = func(m string) error { return nil } // There's no syslog level for trace
- }
-
- s := strings.TrimSpace(string(fmtr.Format(r)))
- return syslogFn(s)
- })
- return LazyHandler(&closingHandler{sysWr, h}), nil
-}
-
-func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogHandler(priority, tag, fmtr))
-}
-
-func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogNetHandler(net, addr, priority, tag, fmtr))
-}
diff --git a/miner/stress/clique/main.go b/miner/stress/clique/main.go
index 7b29e63dfc69..13336cd83cb0 100644
--- a/miner/stress/clique/main.go
+++ b/miner/stress/clique/main.go
@@ -45,7 +45,7 @@ import (
)
func main() {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
fdlimit.Raise(2048)
// Generate a batch of accounts to seal and fund with
diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go
index 5add9cefa120..53ecb1bc6e29 100644
--- a/p2p/discover/v4_udp_test.go
+++ b/p2p/discover/v4_udp_test.go
@@ -557,12 +557,7 @@ func startLocalhostV4(t *testing.T, cfg Config) *UDPv4 {
// Prefix logs with node ID.
lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString())
- lfmt := log.TerminalFormat(false)
- cfg.Log = testlog.Logger(t, log.LvlTrace)
- cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error {
- t.Logf("%s %s", lprefix, lfmt.Format(r))
- return nil
- }))
+ cfg.Log = testlog.Logger(t, log.LevelTrace).With("node-id", lprefix)
// Listen.
socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}})
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 880b71a991ce..18d8aeac6dc7 100644
--- a/p2p/discover/v5_udp_test.go
+++ b/p2p/discover/v5_udp_test.go
@@ -79,12 +79,7 @@ func startLocalhostV5(t *testing.T, cfg Config) *UDPv5 {
// Prefix logs with node ID.
lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString())
- lfmt := log.TerminalFormat(false)
- cfg.Log = testlog.Logger(t, log.LvlTrace)
- cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error {
- t.Logf("%s %s", lprefix, lfmt.Format(r))
- return nil
- }))
+ cfg.Log = testlog.Logger(t, log.LevelTrace).With("node-id", lprefix)
// Listen.
socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}})
diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go
index 5ac3379393ed..63cc4936c1bd 100644
--- a/p2p/simulations/adapters/exec.go
+++ b/p2p/simulations/adapters/exec.go
@@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gorilla/websocket"
+ "golang.org/x/exp/slog"
)
func init() {
@@ -375,9 +376,11 @@ type execNodeConfig struct {
func initLogging() {
// Initialize the logging by default first.
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
- glogger.Verbosity(log.LvlInfo)
- log.Root().SetHandler(glogger)
+ var innerHandler slog.Handler
+ innerHandler = slog.NewTextHandler(os.Stderr, nil)
+ glogger := log.NewGlogHandler(innerHandler)
+ glogger.Verbosity(log.LevelInfo)
+ log.SetDefault(log.NewLogger(glogger))
confEnv := os.Getenv(envNodeConfig)
if confEnv == "" {
@@ -395,14 +398,15 @@ func initLogging() {
}
writer = logWriter
}
- var verbosity = log.LvlInfo
- if conf.Node.LogVerbosity <= log.LvlTrace && conf.Node.LogVerbosity >= log.LvlCrit {
- verbosity = conf.Node.LogVerbosity
+ var verbosity = log.LevelInfo
+ if conf.Node.LogVerbosity <= log.LevelTrace && conf.Node.LogVerbosity >= log.LevelCrit {
+ verbosity = log.FromLegacyLevel(int(conf.Node.LogVerbosity))
}
// Reinitialize the logger
- glogger = log.NewGlogHandler(log.StreamHandler(writer, log.TerminalFormat(true)))
+ innerHandler = log.NewTerminalHandler(writer, true)
+ glogger = log.NewGlogHandler(innerHandler)
glogger.Verbosity(verbosity)
- log.Root().SetHandler(glogger)
+ log.SetDefault(log.NewLogger(glogger))
}
// execP2PNode starts a simulation node when the current binary is executed with
diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go
index 098759599c70..fb8463d221eb 100644
--- a/p2p/simulations/adapters/types.go
+++ b/p2p/simulations/adapters/types.go
@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gorilla/websocket"
+ "golang.org/x/exp/slog"
)
// Node represents a node in a simulation network which is created by a
@@ -129,7 +130,7 @@ type NodeConfig struct {
// LogVerbosity is the log verbosity of the p2p node at runtime.
//
// The default verbosity is INFO.
- LogVerbosity log.Lvl
+ LogVerbosity slog.Level
}
// nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
@@ -197,7 +198,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
n.Port = confJSON.Port
n.EnableMsgEvents = confJSON.EnableMsgEvents
n.LogFile = confJSON.LogFile
- n.LogVerbosity = log.Lvl(confJSON.LogVerbosity)
+ n.LogVerbosity = slog.Level(confJSON.LogVerbosity)
return nil
}
diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go
index f6cf5113a6ee..70b35ad77742 100644
--- a/p2p/simulations/examples/ping-pong.go
+++ b/p2p/simulations/examples/ping-pong.go
@@ -41,7 +41,7 @@ func main() {
flag.Parse()
// set the log level to Trace
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
// register a single ping-pong service
services := map[string]adapters.LifecycleConstructor{
diff --git a/p2p/simulations/http_test.go b/p2p/simulations/http_test.go
index 05e43238abb5..c53a49797bd3 100644
--- a/p2p/simulations/http_test.go
+++ b/p2p/simulations/http_test.go
@@ -37,14 +37,14 @@ import (
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
"github.com/mattn/go-colorable"
+ "golang.org/x/exp/slog"
)
func TestMain(m *testing.M) {
loglevel := flag.Int("loglevel", 2, "verbosity of logs")
flag.Parse()
- log.PrintOrigins(true)
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), slog.Level(*loglevel), true)))
os.Exit(m.Run())
}
diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go
index a0b292bf714c..d2207c9eb8d5 100644
--- a/signer/core/auditlog.go
+++ b/signer/core/auditlog.go
@@ -19,12 +19,14 @@ package core
import (
"context"
"encoding/json"
+ "os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
+ "golang.org/x/exp/slog"
)
type AuditLogger struct {
@@ -113,12 +115,13 @@ func (l *AuditLogger) Version(ctx context.Context) (string, error) {
}
func NewAuditLogger(path string, api ExternalAPI) (*AuditLogger, error) {
- l := log.New("api", "signer")
- handler, err := log.FileHandler(path, log.LogfmtFormat())
+ f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
- l.SetHandler(handler)
+
+ handler := slog.NewTextHandler(f, nil)
+ l := log.NewLogger(handler).With("api", "signer")
l.Info("Configured", "audit log", path)
return &AuditLogger{l, api}, nil
}
diff --git a/signer/storage/aes_gcm_storage_test.go b/signer/storage/aes_gcm_storage_test.go
index 74d407e4311e..a223b1a6b4e7 100644
--- a/signer/storage/aes_gcm_storage_test.go
+++ b/signer/storage/aes_gcm_storage_test.go
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/mattn/go-colorable"
+ "golang.org/x/exp/slog"
)
func TestEncryption(t *testing.T) {
@@ -92,7 +93,7 @@ func TestFileStorage(t *testing.T) {
}
func TestEnd2End(t *testing.T) {
t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(3), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), slog.LevelInfo, true)))
d := t.TempDir()
@@ -115,7 +116,7 @@ func TestSwappedKeys(t *testing.T) {
t.Parallel()
// It should not be possible to swap the keys/values, so that
// K1:V1, K2:V2 can be swapped into K1:V2, K2:V1
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(3), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), slog.LevelInfo, true)))
d := t.TempDir()
From ab0eb46a8466f12f8a53f4591a9563d86d9af9f5 Mon Sep 17 00:00:00 2001
From: rjl493456442
Date: Wed, 29 Nov 2023 16:07:51 +0800
Subject: [PATCH 048/103] core/state: make stateobject.create selfcontain
(#28459)
---
core/state/state_object.go | 6 +++++-
core/state/statedb.go | 3 ---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/core/state/state_object.go b/core/state/state_object.go
index fc66b48114b4..9383b98e4497 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -98,7 +98,10 @@ func (s *stateObject) empty() bool {
// newObject creates a state object.
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
- origin := acct
+ var (
+ origin = acct
+ created = acct == nil // true if the account was not existent
+ )
if acct == nil {
acct = types.NewEmptyStateAccount()
}
@@ -111,6 +114,7 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
originStorage: make(Storage),
pendingStorage: make(Storage),
dirtyStorage: make(Storage),
+ created: created,
}
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 674227857c19..905944cbb5b9 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -658,9 +658,6 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
delete(s.accountsOrigin, prev.address)
delete(s.storagesOrigin, prev.address)
}
-
- newobj.created = true
-
s.setStateObject(newobj)
if prev != nil && !prev.deleted {
return newobj, prev
From fa0df76f3cfd186a1f06f2b80aa5dbb89555b009 Mon Sep 17 00:00:00 2001
From: Maciej Kulawik <10907694+magicxyyz@users.noreply.github.com>
Date: Thu, 30 Nov 2023 09:50:48 +0000
Subject: [PATCH 049/103] trie/triedb/hashdb: take lock around access to
dirties cache (#28542)
Add read locking of db lock around access to dirties cache in hashdb.Database to prevent
data race versus hashdb.Database.dereference which can modify the dirities map by deleting
an item.
Fixes #28541
---------
Co-authored-by: Gary Rong
---
trie/database.go | 11 ------
trie/triedb/hashdb/database.go | 69 ++++++++++++----------------------
2 files changed, 23 insertions(+), 57 deletions(-)
diff --git a/trie/database.go b/trie/database.go
index 321b4f8955b7..e20f7ef903b0 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -240,17 +240,6 @@ func (db *Database) Dereference(root common.Hash) error {
return nil
}
-// Node retrieves the rlp-encoded node blob with provided node hash. It's
-// only supported by hash-based database and will return an error for others.
-// Note, this function should be deprecated once ETH66 is deprecated.
-func (db *Database) Node(hash common.Hash) ([]byte, error) {
- hdb, ok := db.backend.(*hashdb.Database)
- if !ok {
- return nil, errors.New("not supported")
- }
- return hdb.Node(hash)
-}
-
// Recover rollbacks the database to a specified historical point. The state is
// supported as the rollback destination only if it's canonical state and the
// corresponding trie histories are existent. It's only supported by path-based
diff --git a/trie/triedb/hashdb/database.go b/trie/triedb/hashdb/database.go
index 764ab24ec8dd..e45ccdba32ca 100644
--- a/trie/triedb/hashdb/database.go
+++ b/trie/triedb/hashdb/database.go
@@ -82,11 +82,6 @@ var Defaults = &Config{
// Database is an intermediate write layer between the trie data structures and
// the disk database. The aim is to accumulate trie writes in-memory and only
// periodically flush a couple tries to disk, garbage collecting the remainder.
-//
-// Note, the trie Database is **not** thread safe in its mutations, but it **is**
-// thread safe in providing individual, independent node access. The rationale
-// behind this split design is to provide read access to RPC handlers and sync
-// servers even while the trie is executing expensive garbage collection.
type Database struct {
diskdb ethdb.Database // Persistent storage for matured trie nodes
resolver ChildResolver // The handler to resolve children of nodes
@@ -113,7 +108,7 @@ type Database struct {
// cachedNode is all the information we know about a single cached trie node
// in the memory database write layer.
type cachedNode struct {
- node []byte // Encoded node blob
+ node []byte // Encoded node blob, immutable
parents uint32 // Number of live nodes referencing this one
external map[common.Hash]struct{} // The set of external children
flushPrev common.Hash // Previous node in the flush-list
@@ -152,9 +147,9 @@ func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Databas
}
}
-// insert inserts a simplified trie node into the memory database.
-// All nodes inserted by this function will be reference tracked
-// and in theory should only used for **trie nodes** insertion.
+// insert inserts a trie node into the memory database. All nodes inserted by
+// this function will be reference tracked. This function assumes the lock is
+// already held.
func (db *Database) insert(hash common.Hash, node []byte) {
// If the node's already cached, skip
if _, ok := db.dirties[hash]; ok {
@@ -183,9 +178,9 @@ func (db *Database) insert(hash common.Hash, node []byte) {
db.dirtiesSize += common.StorageSize(common.HashLength + len(node))
}
-// Node retrieves an encoded cached trie node from memory. If it cannot be found
+// node retrieves an encoded cached trie node from memory. If it cannot be found
// cached, the method queries the persistent database for the content.
-func (db *Database) Node(hash common.Hash) ([]byte, error) {
+func (db *Database) node(hash common.Hash) ([]byte, error) {
// It doesn't make sense to retrieve the metaroot
if hash == (common.Hash{}) {
return nil, errors.New("not found")
@@ -198,11 +193,14 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
return enc, nil
}
}
- // Retrieve the node from the dirty cache if available
+ // Retrieve the node from the dirty cache if available.
db.lock.RLock()
dirty := db.dirties[hash]
db.lock.RUnlock()
+ // Return the cached node if it's found in the dirty set.
+ // The dirty.node field is immutable and safe to read it
+ // even without lock guard.
if dirty != nil {
memcacheDirtyHitMeter.Mark(1)
memcacheDirtyReadMeter.Mark(int64(len(dirty.node)))
@@ -223,20 +221,6 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
return nil, errors.New("not found")
}
-// Nodes retrieves the hashes of all the nodes cached within the memory database.
-// This method is extremely expensive and should only be used to validate internal
-// states in test code.
-func (db *Database) Nodes() []common.Hash {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- var hashes = make([]common.Hash, 0, len(db.dirties))
- for hash := range db.dirties {
- hashes = append(hashes, hash)
- }
- return hashes
-}
-
// Reference adds a new reference from a parent node to a child node.
// This function is used to add reference between internal trie node
// and external node(e.g. storage trie root), all internal trie nodes
@@ -344,16 +328,16 @@ func (db *Database) dereference(hash common.Hash) {
// Cap iteratively flushes old but still referenced trie nodes until the total
// memory usage goes below the given threshold.
-//
-// Note, this method is a non-synchronized mutator. It is unsafe to call this
-// concurrently with other mutators.
func (db *Database) Cap(limit common.StorageSize) error {
+ db.lock.Lock()
+ defer db.lock.Unlock()
+
// Create a database batch to flush persistent data out. It is important that
// outside code doesn't see an inconsistent state (referenced data removed from
// memory cache during commit but not yet in persistent storage). This is ensured
// by only uncaching existing data when the database write finalizes.
- nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now()
batch := db.diskdb.NewBatch()
+ nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now()
// db.dirtiesSize only contains the useful data in the cache, but when reporting
// the total memory consumption, the maintenance metadata is also needed to be
@@ -391,9 +375,6 @@ func (db *Database) Cap(limit common.StorageSize) error {
return err
}
// Write successful, clear out the flushed data
- db.lock.Lock()
- defer db.lock.Unlock()
-
for db.oldest != oldest {
node := db.dirties[db.oldest]
delete(db.dirties, db.oldest)
@@ -424,10 +405,10 @@ func (db *Database) Cap(limit common.StorageSize) error {
// Commit iterates over all the children of a particular node, writes them out
// to disk, forcefully tearing down all references in both directions. As a side
// effect, all pre-images accumulated up to this point are also written.
-//
-// Note, this method is a non-synchronized mutator. It is unsafe to call this
-// concurrently with other mutators.
func (db *Database) Commit(node common.Hash, report bool) error {
+ db.lock.Lock()
+ defer db.lock.Unlock()
+
// Create a database batch to flush persistent data out. It is important that
// outside code doesn't see an inconsistent state (referenced data removed from
// memory cache during commit but not yet in persistent storage). This is ensured
@@ -449,8 +430,6 @@ func (db *Database) Commit(node common.Hash, report bool) error {
return err
}
// Uncache any leftovers in the last batch
- db.lock.Lock()
- defer db.lock.Unlock()
if err := batch.Replay(uncacher); err != nil {
return err
}
@@ -499,13 +478,11 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleane
if err := batch.Write(); err != nil {
return err
}
- db.lock.Lock()
err := batch.Replay(uncacher)
- batch.Reset()
- db.lock.Unlock()
if err != nil {
return err
}
+ batch.Reset()
}
return nil
}
@@ -574,7 +551,7 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool {
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
// Ensure the parent state is present and signal a warning if not.
if parent != types.EmptyRootHash {
- if blob, _ := db.Node(parent); len(blob) == 0 {
+ if blob, _ := db.node(parent); len(blob) == 0 {
log.Error("parent state is not present")
}
}
@@ -655,7 +632,7 @@ func (db *Database) Scheme() string {
// Reader retrieves a node reader belonging to the given state root.
// An error will be returned if the requested state is not available.
func (db *Database) Reader(root common.Hash) (*reader, error) {
- if _, err := db.Node(root); err != nil {
+ if _, err := db.node(root); err != nil {
return nil, fmt.Errorf("state %#x is not available, %v", root, err)
}
return &reader{db: db}, nil
@@ -666,9 +643,9 @@ type reader struct {
db *Database
}
-// Node retrieves the trie node with the given node hash.
-// No error will be returned if the node is not found.
+// Node retrieves the trie node with the given node hash. No error will be
+// returned if the node is not found.
func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
- blob, _ := reader.db.Node(hash)
+ blob, _ := reader.db.node(hash)
return blob, nil
}
From f2b509d8a1ae877e2edb03560e57236eb74816b7 Mon Sep 17 00:00:00 2001
From: BorkBorked <107079055+BorkBorked@users.noreply.github.com>
Date: Fri, 1 Dec 2023 09:51:50 +0100
Subject: [PATCH 050/103] accounts/abi/bind: fix typo (#28630)
---
accounts/abi/bind/util_test.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go
index b34c5bc2261f..16110b5d27a4 100644
--- a/accounts/abi/bind/util_test.go
+++ b/accounts/abi/bind/util_test.go
@@ -119,9 +119,9 @@ func TestWaitDeployedCornerCases(t *testing.T) {
defer cancel()
backend.SendTransaction(ctx, tx)
backend.Commit()
- notContentCreation := errors.New("tx is not contract creation")
- if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
- t.Errorf("error mismatch: want %q, got %q, ", notContentCreation, err)
+ notContractCreation := errors.New("tx is not contract creation")
+ if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() {
+ t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err)
}
// Create a transaction that is not mined.
From dd0d0a2522ccad6bdeab1e84ff577bd5826540ee Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Fri, 1 Dec 2023 13:28:20 +0100
Subject: [PATCH 051/103] slog: faster and less memory-consumption (#28621)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These changes improves the performance of the non-coloured terminal formatting, _quite a lot_.
```
name old time/op new time/op delta
TerminalHandler-8 10.2µs ±15% 5.4µs ± 9% -47.02% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
TerminalHandler-8 2.17kB ± 0% 0.40kB ± 0% -81.46% (p=0.008 n=5+5)
name old allocs/op new allocs/op delta
TerminalHandler-8 33.0 ± 0% 5.0 ± 0% -84.85% (p=0.008 n=5+5)
```
I tried to _somewhat_ organize the commits, but the it might still be a bit chaotic. Some core insights:
- The function `terminalHandler.Handl` uses a mutex, and writes all output immediately to 'upstream'. Thus, it can reuse a scratch-buffer every time.
- This buffer can be propagated internally, making all the internal formatters either write directly to it,
- OR, make use of the `tmp := buf.AvailableBuffer()` in some cases, where a byte buffer "extra capacity" can be temporarily used.
- The `slog` package uses `Attr` by value. It makes sense to minimize operating on them, since iterating / collecting into a new slice, iterating again etc causes copy-on-heap. Better to operate on them only once.
- If we want to do padding, it's better to copy from a constant `space`-buffer than to invoke `bytes.Repeat` every single time.
---
internal/testlog/testlog.go | 3 +-
log/format.go | 350 ++++++++++++++++++++----------------
log/format_test.go | 8 +-
log/handler.go | 6 +-
log/logger_test.go | 121 +++++++++++++
5 files changed, 328 insertions(+), 160 deletions(-)
diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go
index 68b9fb19f8ba..a7899c81589a 100644
--- a/internal/testlog/testlog.go
+++ b/internal/testlog/testlog.go
@@ -183,8 +183,7 @@ func (h *bufHandler) terminalFormat(r slog.Record) string {
}
for _, attr := range attrs {
- rawVal := attr.Value.Any()
- fmt.Fprintf(buf, " %s=%s", attr.Key, log.FormatLogfmtValue(rawVal, true))
+ fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, true, nil)))
}
buf.WriteByte('\n')
return buf.String()
diff --git a/log/format.go b/log/format.go
index 5cbbe3341ed0..a2bbcce9c056 100644
--- a/log/format.go
+++ b/log/format.go
@@ -15,12 +15,14 @@ import (
const (
timeFormat = "2006-01-02T15:04:05-0700"
- termTimeFormat = "01-02|15:04:05.000"
floatFormat = 'f'
termMsgJust = 40
termCtxMaxPadding = 40
)
+// 40 spaces
+var spaces = []byte(" ")
+
type Format interface {
Format(r slog.Record) []byte
}
@@ -44,37 +46,47 @@ type TerminalStringer interface {
TerminalString() string
}
-func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte {
+func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor bool) []byte {
msg := escapeMessage(r.Message)
- var color = 0
+ var color = ""
if usecolor {
switch r.Level {
case LevelCrit:
- color = 35
+ color = "\x1b[35m"
case slog.LevelError:
- color = 31
+ color = "\x1b[31m"
case slog.LevelWarn:
- color = 33
+ color = "\x1b[33m"
case slog.LevelInfo:
- color = 32
+ color = "\x1b[32m"
case slog.LevelDebug:
- color = 36
+ color = "\x1b[36m"
case LevelTrace:
- color = 34
+ color = "\x1b[34m"
}
}
+ if buf == nil {
+ buf = make([]byte, 0, 30+termMsgJust)
+ }
+ b := bytes.NewBuffer(buf)
- b := &bytes.Buffer{}
- lvl := LevelAlignedString(r.Level)
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
+ if color != "" { // Start color
+ b.WriteString(color)
+ b.WriteString(LevelAlignedString(r.Level))
+ b.WriteString("\x1b[0m")
} else {
- fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
+ b.WriteString(LevelAlignedString(r.Level))
}
+ b.WriteString("[")
+ writeTimeTermFormat(b, r.Time)
+ b.WriteString("] ")
+ b.WriteString(msg)
+
// try to justify the log output for short messages
- length := utf8.RuneCountInString(msg)
- if r.NumAttrs() > 0 && length < termMsgJust {
- b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
+ //length := utf8.RuneCountInString(msg)
+ length := len(msg)
+ if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust {
+ b.Write(spaces[:termMsgJust-length])
}
// print the keys logfmt style
h.logfmt(b, r, color)
@@ -82,150 +94,139 @@ func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte {
return b.Bytes()
}
-func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color int) {
- attrs := []slog.Attr{}
- r.Attrs(func(attr slog.Attr) bool {
- attrs = append(attrs, attr)
- return true
- })
-
- attrs = append(h.attrs, attrs...)
-
- for i, attr := range attrs {
- if i != 0 {
- buf.WriteByte(' ')
+func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) {
+ // tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21)
+ // can be used.
+ var tmp = make([]byte, 40)
+ writeAttr := func(attr slog.Attr, first, last bool) {
+ buf.WriteByte(' ')
+
+ if color != "" {
+ buf.WriteString(color)
+ //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
+ buf.Write(appendEscapeString(tmp[:0], attr.Key))
+ buf.WriteString("\x1b[0m=")
+ } else {
+ //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
+ buf.Write(appendEscapeString(tmp[:0], attr.Key))
+ buf.WriteByte('=')
}
+ //val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer())
+ val := FormatSlogValue(attr.Value, true, tmp[:0])
- key := escapeString(attr.Key)
- rawVal := attr.Value.Any()
- val := FormatLogfmtValue(rawVal, true)
+ padding := h.fieldPadding[attr.Key]
- // XXX: we should probably check that all of your key bytes aren't invalid
- // TODO (jwasinger) above comment was from log15 code. what does it mean? check that key bytes are ascii characters?
- padding := h.fieldPadding[key]
-
- length := utf8.RuneCountInString(val)
+ length := utf8.RuneCount(val)
if padding < length && length <= termCtxMaxPadding {
padding = length
- h.fieldPadding[key] = padding
+ h.fieldPadding[attr.Key] = padding
}
- if color > 0 {
- fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, key)
- } else {
- buf.WriteString(key)
- buf.WriteByte('=')
- }
- buf.WriteString(val)
- if i < r.NumAttrs()-1 && padding > length {
- buf.Write(bytes.Repeat([]byte{' '}, padding-length))
+ buf.Write(val)
+ if !last && padding > length {
+ buf.Write(spaces[:padding-length])
}
}
+ var n = 0
+ var nAttrs = len(h.attrs) + r.NumAttrs()
+ for _, attr := range h.attrs {
+ writeAttr(attr, n == 0, n == nAttrs-1)
+ n++
+ }
+ r.Attrs(func(attr slog.Attr) bool {
+ writeAttr(attr, n == 0, n == nAttrs-1)
+ n++
+ return true
+ })
buf.WriteByte('\n')
}
-// formatValue formats a value for serialization
-func FormatLogfmtValue(value interface{}, term bool) (result string) {
- if value == nil {
- return ""
- }
+// FormatSlogValue formats a slog.Value for serialization
+func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) {
+ var value any
defer func() {
if err := recover(); err != nil {
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
- result = ""
+ result = []byte("")
} else {
panic(err)
}
}
}()
- switch v := value.(type) {
- case time.Time:
+ switch v.Kind() {
+ case slog.KindString:
+ return appendEscapeString(tmp, v.String())
+ case slog.KindAny:
+ value = v.Any()
+ case slog.KindInt64: // All int-types (int8 ,int16 etc) wind up here
+ return appendInt64(tmp, v.Int64())
+ case slog.KindUint64: // All uint-types (int8 ,int16 etc) wind up here
+ return appendUint64(tmp, v.Uint64(), false)
+ case slog.KindFloat64:
+ return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64)
+ case slog.KindBool:
+ return strconv.AppendBool(tmp, v.Bool())
+ case slog.KindDuration:
+ value = v.Duration()
+ case slog.KindTime:
// Performance optimization: No need for escaping since the provided
// timeFormat doesn't have any escape characters, and escaping is
// expensive.
- return v.Format(timeFormat)
-
+ return v.Time().AppendFormat(tmp, timeFormat)
+ default:
+ value = v.Any()
+ }
+ if value == nil {
+ return []byte("")
+ }
+ switch v := value.(type) {
case *big.Int:
// Big ints get consumed by the Stringer clause, so we need to handle
// them earlier on.
if v == nil {
- return ""
+ return append(tmp, []byte("")...)
}
- return formatLogfmtBigInt(v)
+ return appendBigInt(tmp, v)
case *uint256.Int:
// Uint256s get consumed by the Stringer clause, so we need to handle
// them earlier on.
if v == nil {
- return ""
- }
- return formatLogfmtUint256(v)
- }
- if term {
- if s, ok := value.(TerminalStringer); ok {
- // Custom terminal stringer provided, use that
- return escapeString(s.TerminalString())
+ return append(tmp, []byte("")...)
}
- }
- switch v := value.(type) {
+ return appendU256(tmp, v)
case error:
- return escapeString(v.Error())
+ return appendEscapeString(tmp, v.Error())
+ case TerminalStringer:
+ if term {
+ return appendEscapeString(tmp, v.TerminalString()) // Custom terminal stringer provided, use that
+ }
case fmt.Stringer:
- return escapeString(v.String())
- case bool:
- return strconv.FormatBool(v)
- case float32:
- return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
- case float64:
- return strconv.FormatFloat(v, floatFormat, 3, 64)
- case int8:
- return strconv.FormatInt(int64(v), 10)
- case uint8:
- return strconv.FormatInt(int64(v), 10)
- case int16:
- return strconv.FormatInt(int64(v), 10)
- case uint16:
- return strconv.FormatInt(int64(v), 10)
- // Larger integers get thousands separators.
- case int:
- return FormatLogfmtInt64(int64(v))
- case int32:
- return FormatLogfmtInt64(int64(v))
- case int64:
- return FormatLogfmtInt64(v)
- case uint:
- return FormatLogfmtUint64(uint64(v))
- case uint32:
- return FormatLogfmtUint64(uint64(v))
- case uint64:
- return FormatLogfmtUint64(v)
- case string:
- return escapeString(v)
- default:
- return escapeString(fmt.Sprintf("%+v", value))
+ return appendEscapeString(tmp, v.String())
}
+
+ // We can use the 'tmp' as a scratch-buffer, to first format the
+ // value, and in a second step do escaping.
+ internal := fmt.Appendf(tmp, "%+v", value)
+ return appendEscapeString(tmp, string(internal))
}
-// FormatLogfmtInt64 formats n with thousand separators.
-func FormatLogfmtInt64(n int64) string {
+// appendInt64 formats n with thousand separators and writes into buffer dst.
+func appendInt64(dst []byte, n int64) []byte {
if n < 0 {
- return formatLogfmtUint64(uint64(-n), true)
+ return appendUint64(dst, uint64(-n), true)
}
- return formatLogfmtUint64(uint64(n), false)
-}
-
-// FormatLogfmtUint64 formats n with thousand separators.
-func FormatLogfmtUint64(n uint64) string {
- return formatLogfmtUint64(n, false)
+ return appendUint64(dst, uint64(n), false)
}
-func formatLogfmtUint64(n uint64, neg bool) string {
+// appendUint64 formats n with thousand separators and writes into buffer dst.
+func appendUint64(dst []byte, n uint64, neg bool) []byte {
// Small numbers are fine as is
if n < 100000 {
if neg {
- return strconv.Itoa(-int(n))
+ return strconv.AppendInt(dst, -int64(n), 10)
} else {
- return strconv.Itoa(int(n))
+ return strconv.AppendInt(dst, int64(n), 10)
}
}
// Large numbers should be split
@@ -250,16 +251,21 @@ func formatLogfmtUint64(n uint64, neg bool) string {
out[i] = '-'
i--
}
- return string(out[i+1:])
+ return append(dst, out[i+1:]...)
}
-// formatLogfmtBigInt formats n with thousand separators.
-func formatLogfmtBigInt(n *big.Int) string {
+// FormatLogfmtUint64 formats n with thousand separators.
+func FormatLogfmtUint64(n uint64) string {
+ return string(appendUint64(nil, n, false))
+}
+
+// appendBigInt formats n with thousand separators and writes to dst.
+func appendBigInt(dst []byte, n *big.Int) []byte {
if n.IsUint64() {
- return FormatLogfmtUint64(n.Uint64())
+ return appendUint64(dst, n.Uint64(), false)
}
if n.IsInt64() {
- return FormatLogfmtInt64(n.Int64())
+ return appendInt64(dst, n.Int64())
}
var (
@@ -284,54 +290,48 @@ func formatLogfmtBigInt(n *big.Int) string {
comma++
}
}
- return string(buf[i+1:])
+ return append(dst, buf[i+1:]...)
}
-// formatLogfmtUint256 formats n with thousand separators.
-func formatLogfmtUint256(n *uint256.Int) string {
+// appendU256 formats n with thousand separators.
+func appendU256(dst []byte, n *uint256.Int) []byte {
if n.IsUint64() {
- return FormatLogfmtUint64(n.Uint64())
+ return appendUint64(dst, n.Uint64(), false)
}
- var (
- text = n.Dec()
- buf = make([]byte, len(text)+len(text)/3)
- comma = 0
- i = len(buf) - 1
- )
- for j := len(text) - 1; j >= 0; j, i = j-1, i-1 {
- c := text[j]
-
- switch {
- case c == '-':
- buf[i] = c
- case comma == 3:
- buf[i] = ','
- i--
- comma = 0
- fallthrough
- default:
- buf[i] = c
- comma++
- }
- }
- return string(buf[i+1:])
+ res := []byte(n.PrettyDec(','))
+ return append(dst, res...)
}
-// escapeString checks if the provided string needs escaping/quoting, and
-// calls strconv.Quote if needed
-func escapeString(s string) string {
+// appendEscapeString writes the string s to the given writer, with
+// escaping/quoting if needed.
+func appendEscapeString(dst []byte, s string) []byte {
needsQuoting := false
+ needsEscaping := false
for _, r := range s {
- // We quote everything below " (0x22) and above~ (0x7E), plus equal-sign
- if r <= '"' || r > '~' || r == '=' {
+ // If it contains spaces or equal-sign, we need to quote it.
+ if r == ' ' || r == '=' {
needsQuoting = true
+ continue
+ }
+ // We need to escape it, if it contains
+ // - character " (0x22) and lower (except space)
+ // - characters above ~ (0x7E), plus equal-sign
+ if r <= '"' || r > '~' {
+ needsEscaping = true
break
}
}
- if !needsQuoting {
- return s
+ if needsEscaping {
+ return strconv.AppendQuote(dst, s)
}
- return strconv.Quote(s)
+ // No escaping needed, but we might have to place within quote-marks, in case
+ // it contained a space
+ if needsQuoting {
+ dst = append(dst, '"')
+ dst = append(dst, []byte(s)...)
+ return append(dst, '"')
+ }
+ return append(dst, []byte(s)...)
}
// escapeMessage checks if the provided string needs escaping/quoting, similarly
@@ -356,3 +356,45 @@ func escapeMessage(s string) string {
}
return strconv.Quote(s)
}
+
+// writeTimeTermFormat writes on the format "01-02|15:04:05.000"
+func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) {
+ _, month, day := t.Date()
+ writePosIntWidth(buf, int(month), 2)
+ buf.WriteByte('-')
+ writePosIntWidth(buf, day, 2)
+ buf.WriteByte('|')
+ hour, min, sec := t.Clock()
+ writePosIntWidth(buf, hour, 2)
+ buf.WriteByte(':')
+ writePosIntWidth(buf, min, 2)
+ buf.WriteByte(':')
+ writePosIntWidth(buf, sec, 2)
+ ns := t.Nanosecond()
+ buf.WriteByte('.')
+ writePosIntWidth(buf, ns/1e6, 3)
+}
+
+// writePosIntWidth writes non-negative integer i to the buffer, padded on the left
+// by zeroes to the given width. Use a width of 0 to omit padding.
+// Adapted from golang.org/x/exp/slog/internal/buffer/buffer.go
+func writePosIntWidth(b *bytes.Buffer, i, width int) {
+ // Cheap integer to fixed-width decimal ASCII.
+ // Copied from log/log.go.
+ if i < 0 {
+ panic("negative int")
+ }
+ // Assemble decimal in reverse order.
+ var bb [20]byte
+ bp := len(bb) - 1
+ for i >= 10 || width > 1 {
+ width--
+ q := i / 10
+ bb[bp] = byte('0' + i - q*10)
+ bp--
+ i = q
+ }
+ // i < 10
+ bb[bp] = byte('0' + i)
+ b.Write(bb[bp:])
+}
diff --git a/log/format_test.go b/log/format_test.go
index 41e1809c38cd..d4c1df4abcf9 100644
--- a/log/format_test.go
+++ b/log/format_test.go
@@ -5,18 +5,20 @@ import (
"testing"
)
-var sink string
+var sink []byte
func BenchmarkPrettyInt64Logfmt(b *testing.B) {
+ buf := make([]byte, 100)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- sink = FormatLogfmtInt64(rand.Int63())
+ sink = appendInt64(buf, rand.Int63())
}
}
func BenchmarkPrettyUint64Logfmt(b *testing.B) {
+ buf := make([]byte, 100)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- sink = FormatLogfmtUint64(rand.Uint64())
+ sink = appendUint64(buf, rand.Uint64(), false)
}
}
diff --git a/log/handler.go b/log/handler.go
index ef1bcc1992d2..1a25577450e4 100644
--- a/log/handler.go
+++ b/log/handler.go
@@ -81,6 +81,8 @@ type TerminalHandler struct {
// fieldPadding is a map with maximum field value lengths seen until now
// to allow padding log contexts in a bit smarter way.
fieldPadding map[string]int
+
+ buf []byte
}
// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on
@@ -110,7 +112,9 @@ func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *T
func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
- h.wr.Write(h.TerminalFormat(r, h.useColor))
+ buf := h.TerminalFormat(h.buf, r, h.useColor)
+ h.wr.Write(buf)
+ h.buf = buf[:0]
return nil
}
diff --git a/log/logger_test.go b/log/logger_test.go
index fca1f1680f05..27e90c5fd203 100644
--- a/log/logger_test.go
+++ b/log/logger_test.go
@@ -2,10 +2,15 @@ package log
import (
"bytes"
+ "fmt"
+ "io"
+ "math/big"
"os"
"strings"
"testing"
+ "time"
+ "github.com/holiman/uint256"
"golang.org/x/exp/slog"
)
@@ -51,3 +56,119 @@ func BenchmarkTraceLogging(b *testing.B) {
Trace("a message", "v", i)
}
}
+
+func BenchmarkTerminalHandler(b *testing.B) {
+ l := NewLogger(NewTerminalHandler(io.Discard, false))
+ benchmarkLogger(b, l)
+}
+func BenchmarkLogfmtHandler(b *testing.B) {
+ l := NewLogger(LogfmtHandler(io.Discard))
+ benchmarkLogger(b, l)
+}
+
+func BenchmarkJSONHandler(b *testing.B) {
+ l := NewLogger(JSONHandler(io.Discard))
+ benchmarkLogger(b, l)
+}
+
+func benchmarkLogger(b *testing.B, l Logger) {
+ var (
+ bb = make([]byte, 10)
+ tt = time.Now()
+ bigint = big.NewInt(100)
+ nilbig *big.Int
+ err = fmt.Errorf("Oh nooes it's crap")
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ l.Info("This is a message",
+ "foo", int16(i),
+ "bytes", bb,
+ "bonk", "a string with text",
+ "time", tt,
+ "bigint", bigint,
+ "nilbig", nilbig,
+ "err", err)
+ }
+ b.StopTimer()
+}
+
+func TestLoggerOutput(t *testing.T) {
+ type custom struct {
+ A string
+ B int8
+ }
+ var (
+ customA = custom{"Foo", 12}
+ customB = custom{"Foo\nLinebreak", 122}
+ bb = make([]byte, 10)
+ tt = time.Time{}
+ bigint = big.NewInt(100)
+ nilbig *big.Int
+ err = fmt.Errorf("Oh nooes it's crap")
+ lazy = Lazy{Fn: func() interface{} { return "lazy value" }}
+ smallUint = uint256.NewInt(500_000)
+ bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff}
+ )
+
+ out := new(bytes.Buffer)
+ glogHandler := NewGlogHandler(NewTerminalHandler(out, false))
+ glogHandler.Verbosity(LevelInfo)
+ NewLogger(glogHandler).Info("This is a message",
+ "foo", int16(123),
+ "bytes", bb,
+ "bonk", "a string with text",
+ "time", tt,
+ "bigint", bigint,
+ "nilbig", nilbig,
+ "err", err,
+ "struct", customA,
+ "struct", customB,
+ "ptrstruct", &customA,
+ "lazy", lazy,
+ "smalluint", smallUint,
+ "bigUint", bigUint)
+
+ have := out.String()
+ t.Logf("output %v", out.String())
+ want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" lazy="lazy value" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
+`
+ if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) {
+ t.Errorf("Error\nhave: %q\nwant: %q", have, want)
+ }
+}
+
+const termTimeFormat = "01-02|15:04:05.000"
+
+func BenchmarkAppendFormat(b *testing.B) {
+ var now = time.Now()
+ b.Run("fmt time.Format", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat))
+ }
+ })
+ b.Run("time.AppendFormat", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ now.AppendFormat(nil, termTimeFormat)
+ }
+ })
+ var buf = new(bytes.Buffer)
+ b.Run("time.Custom", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ writeTimeTermFormat(buf, now)
+ buf.Reset()
+ }
+ })
+}
+
+func TestTermTimeFormat(t *testing.T) {
+ var now = time.Now()
+ want := now.AppendFormat(nil, termTimeFormat)
+ var b = new(bytes.Buffer)
+ writeTimeTermFormat(b, now)
+ have := b.Bytes()
+ if !bytes.Equal(have, want) {
+ t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want)
+ }
+}
From 5fb8ebc9ecb226b84181420b9871c5f61cf4f77d Mon Sep 17 00:00:00 2001
From: Delweng
Date: Fri, 1 Dec 2023 21:08:52 +0800
Subject: [PATCH 052/103] eth/tracers: tx-level state in debug_traceCall
(#28460)
---
eth/tracers/api.go | 18 +++++--
eth/tracers/api_test.go | 116 +++++++++++++++++++++++++++++++++++++---
2 files changed, 125 insertions(+), 9 deletions(-)
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 300d904a9970..7c0028601dfb 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -164,6 +164,7 @@ type TraceCallConfig struct {
TraceConfig
StateOverrides *ethapi.StateOverride
BlockOverrides *ethapi.BlockOverrides
+ TxIndex *hexutil.Uint
}
// StdTraceConfig holds extra parameters to standard-json trace functions.
@@ -863,11 +864,17 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
// TraceCall lets you trace a given eth_call. It collects the structured logs
// created during the execution of EVM if the given transaction was added on
// top of the provided block and returns them as a JSON object.
+// If no transaction index is specified, the trace will be conducted on the state
+// after executing the specified block. However, if a transaction index is provided,
+// the trace will be conducted on the state after executing the specified transaction
+// within the specified block.
func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
// Try to retrieve the specified block
var (
- err error
- block *types.Block
+ err error
+ block *types.Block
+ statedb *state.StateDB
+ release StateReleaseFunc
)
if hash, ok := blockNrOrHash.Hash(); ok {
block, err = api.blockByHash(ctx, hash)
@@ -892,7 +899,12 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
+
+ if config != nil && config.TxIndex != nil {
+ _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex), reexec)
+ } else {
+ statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
+ }
if err != nil {
return nil, err
}
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index 0f78af9a01e5..49c3ebb67d7f 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -200,13 +200,51 @@ func TestTraceCall(t *testing.T) {
}
genBlocks := 10
signer := types.HomesteadSigner{}
+ nonce := uint64(0)
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
- tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: nonce,
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
b.AddTx(tx)
+ nonce++
+
+ if i == genBlocks-2 {
+ // Transfer from account[0] to account[2]
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: nonce,
+ To: &accounts[2].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+ nonce++
+
+ // Transfer from account[0] to account[1] again
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: nonce,
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+ nonce++
+ }
})
+
+ uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x }
+
defer backend.teardown()
api := NewAPI(backend)
var testSuite = []struct {
@@ -240,6 +278,51 @@ func TestTraceCall(t *testing.T) {
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
+ // Upon the last state, default to the post block's state
+ {
+ blockNumber: rpc.BlockNumber(genBlocks - 1),
+ call: ethapi.TransactionArgs{
+ From: &accounts[2].addr,
+ To: &accounts[0].addr,
+ Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))),
+ },
+ config: nil,
+ expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
+ },
+ // Before the first transaction, should be failed
+ {
+ blockNumber: rpc.BlockNumber(genBlocks - 1),
+ call: ethapi.TransactionArgs{
+ From: &accounts[2].addr,
+ To: &accounts[0].addr,
+ Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))),
+ },
+ config: &TraceCallConfig{TxIndex: uintPtr(0)},
+ expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr),
+ },
+ // Before the target transaction, should be failed
+ {
+ blockNumber: rpc.BlockNumber(genBlocks - 1),
+ call: ethapi.TransactionArgs{
+ From: &accounts[2].addr,
+ To: &accounts[0].addr,
+ Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))),
+ },
+ config: &TraceCallConfig{TxIndex: uintPtr(1)},
+ expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr),
+ },
+ // After the target transaction, should be succeed
+ {
+ blockNumber: rpc.BlockNumber(genBlocks - 1),
+ call: ethapi.TransactionArgs{
+ From: &accounts[2].addr,
+ To: &accounts[0].addr,
+ Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))),
+ },
+ config: &TraceCallConfig{TxIndex: uintPtr(2)},
+ expectErr: nil,
+ expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
+ },
// Standard JSON trace upon the non-existent block, error expects
{
blockNumber: rpc.BlockNumber(genBlocks + 1),
@@ -297,8 +380,8 @@ func TestTraceCall(t *testing.T) {
t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr)
continue
}
- if !reflect.DeepEqual(err, testspec.expectErr) {
- t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err)
+ if !reflect.DeepEqual(err.Error(), testspec.expectErr.Error()) {
+ t.Errorf("test %d: error mismatch, want '%v', got '%v'", i, testspec.expectErr, err)
}
} else {
if err != nil {
@@ -338,7 +421,14 @@ func TestTraceTransaction(t *testing.T) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
- tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: uint64(i),
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
b.AddTx(tx)
target = tx.Hash()
})
@@ -388,7 +478,14 @@ func TestTraceBlock(t *testing.T) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
- tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: uint64(i),
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
b.AddTx(tx)
txHash = tx.Hash()
})
@@ -478,7 +575,14 @@ func TestTracingWithOverrides(t *testing.T) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
- tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: uint64(i),
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
b.AddTx(tx)
})
defer backend.chain.Stop()
From 6e488c244934ea84062cb35d3af26670e1d20e25 Mon Sep 17 00:00:00 2001
From: ddl
Date: Mon, 4 Dec 2023 17:52:55 +0800
Subject: [PATCH 053/103] cmd/evm: fix Env struct json tag (#28635)
---
cmd/evm/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/evm/README.md b/cmd/evm/README.md
index e6c6fe06ad09..41d8ced27844 100644
--- a/cmd/evm/README.md
+++ b/cmd/evm/README.md
@@ -88,7 +88,7 @@ type Env struct {
CurrentTimestamp uint64 `json:"currentTimestamp"`
Withdrawals []*Withdrawal `json:"withdrawals"`
// optional
- CurrentDifficulty *big.Int `json:"currentDifficuly"`
+ CurrentDifficulty *big.Int `json:"currentDifficulty"`
CurrentRandom *big.Int `json:"currentRandom"`
CurrentBaseFee *big.Int `json:"currentBaseFee"`
ParentDifficulty *big.Int `json:"parentDifficulty"`
From f04e5bde7487ce554930187e766164b18c37d867 Mon Sep 17 00:00:00 2001
From: BorkBorked <107079055+BorkBorked@users.noreply.github.com>
Date: Mon, 4 Dec 2023 10:53:42 +0100
Subject: [PATCH 054/103] accounts/abi/bind: fixed typos (#28634)
* Update auth.go
* Update backend.go
* Update bind.go
* Update bind_test.go
---
accounts/abi/bind/auth.go | 2 +-
accounts/abi/bind/backend.go | 2 +-
accounts/abi/bind/bind.go | 2 +-
accounts/abi/bind/bind_test.go | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
index 494dc88a57fa..91913ec3b26b 100644
--- a/accounts/abi/bind/auth.go
+++ b/accounts/abi/bind/auth.go
@@ -56,7 +56,7 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
}
// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
-// an decrypted key from a keystore.
+// a decrypted key from a keystore.
//
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
index d13b91964151..2e45e86ae2b4 100644
--- a/accounts/abi/bind/backend.go
+++ b/accounts/abi/bind/backend.go
@@ -75,7 +75,7 @@ type BlockHashContractCaller interface {
// CodeAtHash returns the code of the given account in the state at the specified block hash.
CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error)
- // CallContractAtHash executes an Ethereum contract all against the state at the specified block hash.
+ // CallContractAtHash executes an Ethereum contract call against the state at the specified block hash.
CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error)
}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index 8a54a0e6ef04..ec2801346311 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -363,7 +363,7 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
- // We only convert stringS and bytes to hash, still need to deal with
+ // We only convert strings and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
if bound == "string" || bound == "[]byte" {
bound = "common.Hash"
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 1069f3d396d4..3191167a00ed 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -1677,7 +1677,7 @@ var bindTests = []struct {
}
sim.Commit()
- // This test the existence of the free retreiver call for view and pure functions
+ // This test the existence of the free retriever call for view and pure functions
if num, err := pav.PureFunc(nil); err != nil {
t.Fatalf("Failed to call anonymous field retriever: %v", err)
} else if num.Cmp(big.NewInt(42)) != 0 {
From 70fd0b635e4198fe8695ff14f62c6736e71b5f27 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Mon, 4 Dec 2023 17:55:17 +0530
Subject: [PATCH 055/103] eth/fetcher: fix invalid tracking of received at time
for block (#28637)
eth/fetcher: fix invalid tracking of received at time
---
eth/fetcher/block_fetcher.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go
index 8751c4e3ea13..126eaaea7fad 100644
--- a/eth/fetcher/block_fetcher.go
+++ b/eth/fetcher/block_fetcher.go
@@ -483,7 +483,7 @@ func (f *BlockFetcher) loop() {
select {
case res := <-resCh:
res.Done <- nil
- f.FilterHeaders(peer, *res.Res.(*eth.BlockHeadersRequest), time.Now().Add(res.Time))
+ f.FilterHeaders(peer, *res.Res.(*eth.BlockHeadersRequest), time.Now())
case <-timeout.C:
// The peer didn't respond in time. The request
From 2e13b01046aade91e0fe2097a08b761acd413364 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?=
Date: Mon, 4 Dec 2023 14:55:06 +0100
Subject: [PATCH 056/103] accounts: run tests in parallel (#28544)
---
accounts/abi/abi_test.go | 24 ++++++++++++++++
accounts/abi/abifuzzer_test.go | 1 +
accounts/abi/bind/backends/simulated_test.go | 29 ++++++++++++++++++++
accounts/abi/bind/base_test.go | 10 +++++++
accounts/abi/bind/bind_test.go | 1 +
accounts/abi/bind/util_test.go | 2 ++
accounts/abi/event_test.go | 6 ++++
accounts/abi/method_test.go | 2 ++
accounts/abi/pack_test.go | 5 ++++
accounts/abi/reflect_test.go | 4 +++
accounts/abi/selector_parser_test.go | 1 +
accounts/abi/topics_test.go | 9 ++++++
accounts/abi/type_test.go | 5 ++++
accounts/abi/unpack_test.go | 12 ++++++++
accounts/accounts_test.go | 1 +
accounts/hd_test.go | 2 ++
accounts/keystore/account_cache_test.go | 3 ++
accounts/keystore/keystore_test.go | 7 +++++
accounts/keystore/passphrase_test.go | 1 +
accounts/keystore/plain_test.go | 4 +++
accounts/url_test.go | 5 ++++
21 files changed, 134 insertions(+)
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 84175df4bb93..bc76df0dc264 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -120,6 +120,7 @@ var methods = map[string]Method{
}
func TestReader(t *testing.T) {
+ t.Parallel()
abi := ABI{
Methods: methods,
}
@@ -151,6 +152,7 @@ func TestReader(t *testing.T) {
}
func TestInvalidABI(t *testing.T) {
+ t.Parallel()
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
_, err := JSON(strings.NewReader(json))
if err == nil {
@@ -170,6 +172,7 @@ func TestInvalidABI(t *testing.T) {
// constructor(uint256 a, uint256 b) public{}
// }
func TestConstructor(t *testing.T) {
+ t.Parallel()
json := `[{ "inputs": [{"internalType": "uint256","name": "a","type": "uint256" },{ "internalType": "uint256","name": "b","type": "uint256"}],"stateMutability": "nonpayable","type": "constructor"}]`
method := NewMethod("", "", Constructor, "nonpayable", false, false, []Argument{{"a", Uint256, false}, {"b", Uint256, false}}, nil)
// Test from JSON
@@ -199,6 +202,7 @@ func TestConstructor(t *testing.T) {
}
func TestTestNumbers(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Fatal(err)
@@ -236,6 +240,7 @@ func TestTestNumbers(t *testing.T) {
}
func TestMethodSignature(t *testing.T) {
+ t.Parallel()
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
exp := "foo(string,string)"
if m.Sig != exp {
@@ -274,6 +279,7 @@ func TestMethodSignature(t *testing.T) {
}
func TestOverloadedMethodSignature(t *testing.T) {
+ t.Parallel()
json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
@@ -297,6 +303,7 @@ func TestOverloadedMethodSignature(t *testing.T) {
}
func TestCustomErrors(t *testing.T) {
+ t.Parallel()
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
@@ -311,6 +318,7 @@ func TestCustomErrors(t *testing.T) {
}
func TestMultiPack(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Fatal(err)
@@ -348,6 +356,7 @@ func ExampleJSON() {
}
func TestInputVariableInputLength(t *testing.T) {
+ t.Parallel()
const definition = `[
{ "type" : "function", "name" : "strOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] },
{ "type" : "function", "name" : "bytesOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] },
@@ -476,6 +485,7 @@ func TestInputVariableInputLength(t *testing.T) {
}
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Error(err)
@@ -650,6 +660,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
}
func TestDefaultFunctionParsing(t *testing.T) {
+ t.Parallel()
const definition = `[{ "name" : "balance", "type" : "function" }]`
abi, err := JSON(strings.NewReader(definition))
@@ -663,6 +674,7 @@ func TestDefaultFunctionParsing(t *testing.T) {
}
func TestBareEvents(t *testing.T) {
+ t.Parallel()
const definition = `[
{ "type" : "event", "name" : "balance" },
{ "type" : "event", "name" : "anon", "anonymous" : true},
@@ -739,6 +751,7 @@ func TestBareEvents(t *testing.T) {
//
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestUnpackEvent(t *testing.T) {
+ t.Parallel()
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -777,6 +790,7 @@ func TestUnpackEvent(t *testing.T) {
}
func TestUnpackEventIntoMap(t *testing.T) {
+ t.Parallel()
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -827,6 +841,7 @@ func TestUnpackEventIntoMap(t *testing.T) {
}
func TestUnpackMethodIntoMap(t *testing.T) {
+ t.Parallel()
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -877,6 +892,7 @@ func TestUnpackMethodIntoMap(t *testing.T) {
}
func TestUnpackIntoMapNamingConflict(t *testing.T) {
+ t.Parallel()
// Two methods have the same name
var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
abi, err := JSON(strings.NewReader(abiJSON))
@@ -960,6 +976,7 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
}
func TestABI_MethodById(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Fatal(err)
@@ -992,6 +1009,7 @@ func TestABI_MethodById(t *testing.T) {
}
func TestABI_EventById(t *testing.T) {
+ t.Parallel()
tests := []struct {
name string
json string
@@ -1058,6 +1076,7 @@ func TestABI_EventById(t *testing.T) {
}
func TestABI_ErrorByID(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(`[
{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"MyError1","type":"error"},
{"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"string","name":"b","type":"string"},{"internalType":"address","name":"c","type":"address"}],"internalType":"struct MyError.MyStruct","name":"x","type":"tuple"},{"internalType":"address","name":"y","type":"address"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"string","name":"b","type":"string"},{"internalType":"address","name":"c","type":"address"}],"internalType":"struct MyError.MyStruct","name":"z","type":"tuple"}],"name":"MyError2","type":"error"},
@@ -1088,6 +1107,7 @@ func TestABI_ErrorByID(t *testing.T) {
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
// conflict and that the second transfer method will be renamed transfer1.
func TestDoubleDuplicateMethodNames(t *testing.T) {
+ t.Parallel()
abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer0","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -1117,6 +1137,7 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
// event send();
// }
func TestDoubleDuplicateEventNames(t *testing.T) {
+ t.Parallel()
abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -1144,6 +1165,7 @@ func TestDoubleDuplicateEventNames(t *testing.T) {
// event send(uint256, uint256);
// }
func TestUnnamedEventParam(t *testing.T) {
+ t.Parallel()
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
@@ -1177,7 +1199,9 @@ func TestUnpackRevert(t *testing.T) {
{"4e487b7100000000000000000000000000000000000000000000000000000000000000ff", "unknown panic code: 0xff", nil},
}
for index, c := range cases {
+ index, c := index, c
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
+ t.Parallel()
got, err := UnpackRevert(common.Hex2Bytes(c.input))
if c.expectErr != nil {
if err == nil {
diff --git a/accounts/abi/abifuzzer_test.go b/accounts/abi/abifuzzer_test.go
index 4b6794781571..dbf6ab6c543d 100644
--- a/accounts/abi/abifuzzer_test.go
+++ b/accounts/abi/abifuzzer_test.go
@@ -28,6 +28,7 @@ import (
// TestReplicate can be used to replicate crashers from the fuzzing tests.
// Just replace testString with the data in .quoted
func TestReplicate(t *testing.T) {
+ t.Parallel()
//t.Skip("Test only useful for reproducing issues")
fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
//fuzzAbi([]byte("asdfasdfkadsf;lasdf;lasd;lfk"))
diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go
index a41d16841197..a2acf7ead5c0 100644
--- a/accounts/abi/bind/backends/simulated_test.go
+++ b/accounts/abi/bind/backends/simulated_test.go
@@ -38,6 +38,7 @@ import (
)
func TestSimulatedBackend(t *testing.T) {
+ t.Parallel()
var gasLimit uint64 = 8000029
key, _ := crypto.GenerateKey() // nolint: gosec
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
@@ -121,6 +122,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
}
func TestNewSimulatedBackend(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr)
@@ -142,6 +144,7 @@ func TestNewSimulatedBackend(t *testing.T) {
}
func TestAdjustTime(t *testing.T) {
+ t.Parallel()
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
@@ -159,6 +162,7 @@ func TestAdjustTime(t *testing.T) {
}
func TestNewAdjustTimeFail(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.blockchain.Stop()
@@ -202,6 +206,7 @@ func TestNewAdjustTimeFail(t *testing.T) {
}
func TestBalanceAt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr)
@@ -219,6 +224,7 @@ func TestBalanceAt(t *testing.T) {
}
func TestBlockByHash(t *testing.T) {
+ t.Parallel()
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
@@ -240,6 +246,7 @@ func TestBlockByHash(t *testing.T) {
}
func TestBlockByNumber(t *testing.T) {
+ t.Parallel()
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
@@ -275,6 +282,7 @@ func TestBlockByNumber(t *testing.T) {
}
func TestNonceAt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -328,6 +336,7 @@ func TestNonceAt(t *testing.T) {
}
func TestSendTransaction(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -362,6 +371,7 @@ func TestSendTransaction(t *testing.T) {
}
func TestTransactionByHash(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
@@ -416,6 +426,7 @@ func TestTransactionByHash(t *testing.T) {
}
func TestEstimateGas(t *testing.T) {
+ t.Parallel()
/*
pragma solidity ^0.6.4;
contract GasEstimation {
@@ -535,6 +546,7 @@ func TestEstimateGas(t *testing.T) {
}
func TestEstimateGasWithPrice(t *testing.T) {
+ t.Parallel()
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -625,6 +637,7 @@ func TestEstimateGasWithPrice(t *testing.T) {
}
func TestHeaderByHash(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -646,6 +659,7 @@ func TestHeaderByHash(t *testing.T) {
}
func TestHeaderByNumber(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -692,6 +706,7 @@ func TestHeaderByNumber(t *testing.T) {
}
func TestTransactionCount(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -744,6 +759,7 @@ func TestTransactionCount(t *testing.T) {
}
func TestTransactionInBlock(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -809,6 +825,7 @@ func TestTransactionInBlock(t *testing.T) {
}
func TestPendingNonceAt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -874,6 +891,7 @@ func TestPendingNonceAt(t *testing.T) {
}
func TestTransactionReceipt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
@@ -908,6 +926,7 @@ func TestTransactionReceipt(t *testing.T) {
}
func TestSuggestGasPrice(t *testing.T) {
+ t.Parallel()
sim := NewSimulatedBackend(
core.GenesisAlloc{},
10000000,
@@ -924,6 +943,7 @@ func TestSuggestGasPrice(t *testing.T) {
}
func TestPendingCodeAt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -960,6 +980,7 @@ func TestPendingCodeAt(t *testing.T) {
}
func TestCodeAt(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -997,6 +1018,7 @@ func TestCodeAt(t *testing.T) {
}
func TestCodeAtHash(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1037,6 +1059,7 @@ func TestCodeAtHash(t *testing.T) {
//
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestPendingAndCallContract(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1138,6 +1161,7 @@ contract Reverter {
}
}*/
func TestCallContractRevert(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1233,6 +1257,7 @@ func TestCallContractRevert(t *testing.T) {
// Since Commit() was called 2n+1 times in total,
// having a chain length of just n+1 means that a reorg occurred.
func TestFork(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1286,6 +1311,7 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f
// 9. Re-send the transaction and mine a block.
// 10. Check that the event was reborn.
func TestForkLogsReborn(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1359,6 +1385,7 @@ func TestForkLogsReborn(t *testing.T) {
// 5. Mine a block, Re-send the transaction and mine another one.
// 6. Check that the TX is now included in block 2.
func TestForkResendTx(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1395,6 +1422,7 @@ func TestForkResendTx(t *testing.T) {
}
func TestCommitReturnValue(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
@@ -1436,6 +1464,7 @@ func TestCommitReturnValue(t *testing.T) {
// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
// block's parent rather than the canonical head's parent.
func TestAdjustTimeAfterFork(t *testing.T) {
+ t.Parallel()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
index 44552ab12171..f7eb7d14d3e2 100644
--- a/accounts/abi/bind/base_test.go
+++ b/accounts/abi/bind/base_test.go
@@ -135,6 +135,7 @@ func (mc *mockBlockHashCaller) CallContractAtHash(ctx context.Context, call ethe
}
func TestPassingBlockNumber(t *testing.T) {
+ t.Parallel()
mc := &mockPendingCaller{
mockCaller: &mockCaller{
codeAtBytes: []byte{1, 2, 3},
@@ -186,6 +187,7 @@ func TestPassingBlockNumber(t *testing.T) {
const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
+ t.Parallel()
hash := crypto.Keccak256Hash([]byte("testName"))
topics := []common.Hash{
crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
@@ -207,6 +209,7 @@ func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
}
func TestUnpackAnonymousLogIntoMap(t *testing.T) {
+ t.Parallel()
mockLog := newMockLog(nil, common.HexToHash("0x0"))
abiString := `[{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"received","type":"event"}]`
@@ -224,6 +227,7 @@ func TestUnpackAnonymousLogIntoMap(t *testing.T) {
}
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
+ t.Parallel()
sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
if err != nil {
t.Fatal(err)
@@ -249,6 +253,7 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
}
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
+ t.Parallel()
arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
if err != nil {
t.Fatal(err)
@@ -274,6 +279,7 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
}
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
+ t.Parallel()
mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
addrBytes := mockAddress.Bytes()
hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
@@ -300,6 +306,7 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
}
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
+ t.Parallel()
bytes := []byte{1, 2, 3, 4, 5}
hash := crypto.Keccak256Hash(bytes)
topics := []common.Hash{
@@ -322,6 +329,7 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
}
func TestTransactGasFee(t *testing.T) {
+ t.Parallel()
assert := assert.New(t)
// GasTipCap and GasFeeCap
@@ -397,6 +405,7 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
}
func TestCall(t *testing.T) {
+ t.Parallel()
var method, methodWithArg = "something", "somethingArrrrg"
tests := []struct {
name, method string
@@ -572,6 +581,7 @@ func TestCall(t *testing.T) {
// TestCrashers contains some strings which previously caused the abi codec to crash.
func TestCrashers(t *testing.T) {
+ t.Parallel()
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 3191167a00ed..a5f7afa73c9a 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -2067,6 +2067,7 @@ var bindTests = []struct {
// Tests that packages generated by the binder can be successfully compiled and
// the requested tester run against it.
func TestGolangBindings(t *testing.T) {
+ t.Parallel()
// Skip the test if no Go command can be found
gocmd := runtime.GOROOT() + "/bin/go"
if !common.FileExist(gocmd) {
diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go
index 16110b5d27a4..826426632cde 100644
--- a/accounts/abi/bind/util_test.go
+++ b/accounts/abi/bind/util_test.go
@@ -53,6 +53,7 @@ var waitDeployedTests = map[string]struct {
}
func TestWaitDeployed(t *testing.T) {
+ t.Parallel()
for name, test := range waitDeployedTests {
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
@@ -100,6 +101,7 @@ func TestWaitDeployed(t *testing.T) {
}
func TestWaitDeployedCornerCases(t *testing.T) {
+ t.Parallel()
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go
index 8f73419496ba..fffe28ea63a4 100644
--- a/accounts/abi/event_test.go
+++ b/accounts/abi/event_test.go
@@ -81,6 +81,7 @@ var pledgeData1 = "00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa
var mixedCaseData1 = "00000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000020489e8000000000000000000000000000000000000000000000000000000000000000f4241"
func TestEventId(t *testing.T) {
+ t.Parallel()
var table = []struct {
definition string
expectations map[string]common.Hash
@@ -112,6 +113,7 @@ func TestEventId(t *testing.T) {
}
func TestEventString(t *testing.T) {
+ t.Parallel()
var table = []struct {
definition string
expectations map[string]string
@@ -146,6 +148,7 @@ func TestEventString(t *testing.T) {
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
+ t.Parallel()
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err)
@@ -161,6 +164,7 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
}
func TestEventTupleUnpack(t *testing.T) {
+ t.Parallel()
type EventTransfer struct {
Value *big.Int
}
@@ -351,6 +355,7 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func TestEventUnpackIndexed(t *testing.T) {
+ t.Parallel()
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
type testStruct struct {
Value1 uint8 // indexed
@@ -368,6 +373,7 @@ func TestEventUnpackIndexed(t *testing.T) {
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
func TestEventIndexedWithArrayUnpack(t *testing.T) {
+ t.Parallel()
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
type testStruct struct {
Value1 [2]uint8 // indexed
diff --git a/accounts/abi/method_test.go b/accounts/abi/method_test.go
index 9230e307aa41..6322173920b5 100644
--- a/accounts/abi/method_test.go
+++ b/accounts/abi/method_test.go
@@ -35,6 +35,7 @@ const methoddata = `
]`
func TestMethodString(t *testing.T) {
+ t.Parallel()
var table = []struct {
method string
expectation string
@@ -99,6 +100,7 @@ func TestMethodString(t *testing.T) {
}
func TestMethodSig(t *testing.T) {
+ t.Parallel()
var cases = []struct {
method string
expect string
diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go
index 5c7cb1cc1a24..00bdae469e21 100644
--- a/accounts/abi/pack_test.go
+++ b/accounts/abi/pack_test.go
@@ -32,8 +32,11 @@ import (
// TestPack tests the general pack/unpack tests in packing_test.go
func TestPack(t *testing.T) {
+ t.Parallel()
for i, test := range packUnpackTests {
+ i, test := i, test
t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
encb, err := hex.DecodeString(test.packed)
if err != nil {
t.Fatalf("invalid hex %s: %v", test.packed, err)
@@ -57,6 +60,7 @@ func TestPack(t *testing.T) {
}
func TestMethodPack(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
t.Fatal(err)
@@ -177,6 +181,7 @@ func TestMethodPack(t *testing.T) {
}
func TestPackNumber(t *testing.T) {
+ t.Parallel()
tests := []struct {
value reflect.Value
packed []byte
diff --git a/accounts/abi/reflect_test.go b/accounts/abi/reflect_test.go
index 76ef1ad2aa39..6c7ae57087db 100644
--- a/accounts/abi/reflect_test.go
+++ b/accounts/abi/reflect_test.go
@@ -170,8 +170,11 @@ var reflectTests = []reflectTest{
}
func TestReflectNameToStruct(t *testing.T) {
+ t.Parallel()
for _, test := range reflectTests {
+ test := test
t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
m, err := mapArgNamesToStructFields(test.args, reflect.ValueOf(test.struc))
if len(test.err) > 0 {
if err == nil || err.Error() != test.err {
@@ -192,6 +195,7 @@ func TestReflectNameToStruct(t *testing.T) {
}
func TestConvertType(t *testing.T) {
+ t.Parallel()
// Test Basic Struct
type T struct {
X *big.Int
diff --git a/accounts/abi/selector_parser_test.go b/accounts/abi/selector_parser_test.go
index f6f134492bc5..6cb0ae0e70b5 100644
--- a/accounts/abi/selector_parser_test.go
+++ b/accounts/abi/selector_parser_test.go
@@ -24,6 +24,7 @@ import (
)
func TestParseSelector(t *testing.T) {
+ t.Parallel()
mkType := func(types ...interface{}) []ArgumentMarshaling {
var result []ArgumentMarshaling
for i, typeOrComponents := range types {
diff --git a/accounts/abi/topics_test.go b/accounts/abi/topics_test.go
index 30cf21d0b833..b31f58fba323 100644
--- a/accounts/abi/topics_test.go
+++ b/accounts/abi/topics_test.go
@@ -26,6 +26,7 @@ import (
)
func TestMakeTopics(t *testing.T) {
+ t.Parallel()
type args struct {
query [][]interface{}
}
@@ -117,7 +118,9 @@ func TestMakeTopics(t *testing.T) {
},
}
for _, tt := range tests {
+ tt := tt
t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
got, err := MakeTopics(tt.args.query...)
if (err != nil) != tt.wantErr {
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
@@ -347,10 +350,13 @@ func setupTopicsTests() []topicTest {
}
func TestParseTopics(t *testing.T) {
+ t.Parallel()
tests := setupTopicsTests()
for _, tt := range tests {
+ tt := tt
t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
createObj := tt.args.createObj()
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
@@ -364,10 +370,13 @@ func TestParseTopics(t *testing.T) {
}
func TestParseTopicsIntoMap(t *testing.T) {
+ t.Parallel()
tests := setupTopicsTests()
for _, tt := range tests {
+ tt := tt
t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
outMap := make(map[string]interface{})
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index a72531ba2797..ae69872ad8e0 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -31,6 +31,7 @@ type typeWithoutStringer Type
// Tests that all allowed types get recognized by the type parser.
func TestTypeRegexp(t *testing.T) {
+ t.Parallel()
tests := []struct {
blob string
components []ArgumentMarshaling
@@ -117,6 +118,7 @@ func TestTypeRegexp(t *testing.T) {
}
func TestTypeCheck(t *testing.T) {
+ t.Parallel()
for i, test := range []struct {
typ string
components []ArgumentMarshaling
@@ -308,6 +310,7 @@ func TestTypeCheck(t *testing.T) {
}
func TestInternalType(t *testing.T) {
+ t.Parallel()
components := []ArgumentMarshaling{{Name: "a", Type: "int64"}}
internalType := "struct a.b[]"
kind := Type{
@@ -332,6 +335,7 @@ func TestInternalType(t *testing.T) {
}
func TestGetTypeSize(t *testing.T) {
+ t.Parallel()
var testCases = []struct {
typ string
components []ArgumentMarshaling
@@ -368,6 +372,7 @@ func TestGetTypeSize(t *testing.T) {
}
func TestNewFixedBytesOver32(t *testing.T) {
+ t.Parallel()
_, err := NewType("bytes4096", "", nil)
if err == nil {
t.Errorf("fixed bytes with size over 32 is not spec'd")
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index a7ee1d920250..29891ec0a411 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -33,6 +33,7 @@ import (
// TestUnpack tests the general pack/unpack tests in packing_test.go
func TestUnpack(t *testing.T) {
+ t.Parallel()
for i, test := range packUnpackTests {
t.Run(strconv.Itoa(i)+" "+test.def, func(t *testing.T) {
//Unpack
@@ -224,6 +225,7 @@ var unpackTests = []unpackTest{
// TestLocalUnpackTests runs test specially designed only for unpacking.
// All test cases that can be used to test packing and unpacking should move to packing_test.go
func TestLocalUnpackTests(t *testing.T) {
+ t.Parallel()
for i, test := range unpackTests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
//Unpack
@@ -251,6 +253,7 @@ func TestLocalUnpackTests(t *testing.T) {
}
func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
+ t.Parallel()
abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`))
if err != nil {
t.Fatal(err)
@@ -321,6 +324,7 @@ func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOut
}
func TestMethodMultiReturn(t *testing.T) {
+ t.Parallel()
type reversed struct {
String string
Int *big.Int
@@ -400,6 +404,7 @@ func TestMethodMultiReturn(t *testing.T) {
}
func TestMultiReturnWithArray(t *testing.T) {
+ t.Parallel()
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
@@ -423,6 +428,7 @@ func TestMultiReturnWithArray(t *testing.T) {
}
func TestMultiReturnWithStringArray(t *testing.T) {
+ t.Parallel()
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
@@ -453,6 +459,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
}
func TestMultiReturnWithStringSlice(t *testing.T) {
+ t.Parallel()
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
@@ -485,6 +492,7 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
}
func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
+ t.Parallel()
// Similar to TestMultiReturnWithArray, but with a special case in mind:
// values of nested static arrays count towards the size as well, and any element following
// after such nested array argument should be read with the correct offset,
@@ -525,6 +533,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
}
func TestUnmarshal(t *testing.T) {
+ t.Parallel()
const definition = `[
{ "name" : "int", "type": "function", "outputs": [ { "type": "uint256" } ] },
{ "name" : "bool", "type": "function", "outputs": [ { "type": "bool" } ] },
@@ -774,6 +783,7 @@ func TestUnmarshal(t *testing.T) {
}
func TestUnpackTuple(t *testing.T) {
+ t.Parallel()
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
abi, err := JSON(strings.NewReader(simpleTuple))
if err != nil {
@@ -876,6 +886,7 @@ func TestUnpackTuple(t *testing.T) {
}
func TestOOMMaliciousInput(t *testing.T) {
+ t.Parallel()
oomTests := []unpackTest{
{
def: `[{"type": "uint8[]"}]`,
@@ -946,6 +957,7 @@ func TestOOMMaliciousInput(t *testing.T) {
}
func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
+ t.Parallel()
var encodeABI Arguments
uint256Ty, err := NewType("uint256", "", nil)
if err != nil {
diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go
index e8274f9f0408..2c4138aa7804 100644
--- a/accounts/accounts_test.go
+++ b/accounts/accounts_test.go
@@ -24,6 +24,7 @@ import (
)
func TestTextHash(t *testing.T) {
+ t.Parallel()
hash := TextHash([]byte("Hello Joe"))
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
if !bytes.Equal(hash, want) {
diff --git a/accounts/hd_test.go b/accounts/hd_test.go
index 0743bbe66628..118ec5187bae 100644
--- a/accounts/hd_test.go
+++ b/accounts/hd_test.go
@@ -25,6 +25,7 @@ import (
// Tests that HD derivation paths can be correctly parsed into our internal binary
// representation.
func TestHDPathParsing(t *testing.T) {
+ t.Parallel()
tests := []struct {
input string
output DerivationPath
@@ -89,6 +90,7 @@ func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
}
func TestHdPathIteration(t *testing.T) {
+ t.Parallel()
testDerive(t, DefaultIterator(DefaultBaseDerivationPath),
[]string{
"m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1",
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
index 371d274441e7..48a238048fec 100644
--- a/accounts/keystore/account_cache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -152,6 +152,7 @@ func TestWatchNoDir(t *testing.T) {
}
func TestCacheInitialReload(t *testing.T) {
+ t.Parallel()
cache, _ := newAccountCache(cachetestDir)
accounts := cache.accounts()
if !reflect.DeepEqual(accounts, cachetestAccounts) {
@@ -160,6 +161,7 @@ func TestCacheInitialReload(t *testing.T) {
}
func TestCacheAddDeleteOrder(t *testing.T) {
+ t.Parallel()
cache, _ := newAccountCache("testdata/no-such-dir")
cache.watcher.running = true // prevent unexpected reloads
@@ -244,6 +246,7 @@ func TestCacheAddDeleteOrder(t *testing.T) {
}
func TestCacheFind(t *testing.T) {
+ t.Parallel()
dir := filepath.Join("testdata", "dir")
cache, _ := newAccountCache(dir)
cache.watcher.running = true // prevent unexpected reloads
diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go
index deb7cae9f9e1..c9a23eddd6ca 100644
--- a/accounts/keystore/keystore_test.go
+++ b/accounts/keystore/keystore_test.go
@@ -36,6 +36,7 @@ import (
var testSigData = make([]byte, 32)
func TestKeyStore(t *testing.T) {
+ t.Parallel()
dir, ks := tmpKeyStore(t, true)
a, err := ks.NewAccount("foo")
@@ -70,6 +71,7 @@ func TestKeyStore(t *testing.T) {
}
func TestSign(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, true)
pass := "" // not used but required by API
@@ -86,6 +88,7 @@ func TestSign(t *testing.T) {
}
func TestSignWithPassphrase(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, true)
pass := "passwd"
@@ -280,6 +283,7 @@ type walletEvent struct {
// Tests that wallet notifications and correctly fired when accounts are added
// or deleted from the keystore.
func TestWalletNotifications(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, false)
// Subscribe to the wallet feed and collect events.
@@ -341,6 +345,7 @@ func TestWalletNotifications(t *testing.T) {
// TestImportExport tests the import functionality of a keystore.
func TestImportECDSA(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, true)
key, err := crypto.GenerateKey()
if err != nil {
@@ -359,6 +364,7 @@ func TestImportECDSA(t *testing.T) {
// TestImportECDSA tests the import and export functionality of a keystore.
func TestImportExport(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old")
if err != nil {
@@ -387,6 +393,7 @@ func TestImportExport(t *testing.T) {
// TestImportRace tests the keystore on races.
// This test should fail under -race if importing races.
func TestImportRace(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old")
if err != nil {
diff --git a/accounts/keystore/passphrase_test.go b/accounts/keystore/passphrase_test.go
index 1de43a96daba..20ec0f5519f7 100644
--- a/accounts/keystore/passphrase_test.go
+++ b/accounts/keystore/passphrase_test.go
@@ -30,6 +30,7 @@ const (
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
func TestKeyEncryptDecrypt(t *testing.T) {
+ t.Parallel()
keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
if err != nil {
t.Fatal(err)
diff --git a/accounts/keystore/plain_test.go b/accounts/keystore/plain_test.go
index 93165d5cd3ab..737eb7fd61bd 100644
--- a/accounts/keystore/plain_test.go
+++ b/accounts/keystore/plain_test.go
@@ -40,6 +40,7 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
}
func TestKeyStorePlain(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStoreIface(t, false)
pass := "" // not used but required by API
@@ -60,6 +61,7 @@ func TestKeyStorePlain(t *testing.T) {
}
func TestKeyStorePassphrase(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStoreIface(t, true)
pass := "foo"
@@ -80,6 +82,7 @@ func TestKeyStorePassphrase(t *testing.T) {
}
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
+ t.Parallel()
_, ks := tmpKeyStoreIface(t, true)
pass := "foo"
@@ -93,6 +96,7 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
}
func TestImportPreSaleKey(t *testing.T) {
+ t.Parallel()
dir, ks := tmpKeyStoreIface(t, true)
// file content of a presale key file generated with:
diff --git a/accounts/url_test.go b/accounts/url_test.go
index 52be4c558d41..f481a1016d5c 100644
--- a/accounts/url_test.go
+++ b/accounts/url_test.go
@@ -21,6 +21,7 @@ import (
)
func TestURLParsing(t *testing.T) {
+ t.Parallel()
url, err := parseURL("https://ethereum.org")
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -40,6 +41,7 @@ func TestURLParsing(t *testing.T) {
}
func TestURLString(t *testing.T) {
+ t.Parallel()
url := URL{Scheme: "https", Path: "ethereum.org"}
if url.String() != "https://ethereum.org" {
t.Errorf("expected: %v, got: %v", "https://ethereum.org", url.String())
@@ -52,6 +54,7 @@ func TestURLString(t *testing.T) {
}
func TestURLMarshalJSON(t *testing.T) {
+ t.Parallel()
url := URL{Scheme: "https", Path: "ethereum.org"}
json, err := url.MarshalJSON()
if err != nil {
@@ -63,6 +66,7 @@ func TestURLMarshalJSON(t *testing.T) {
}
func TestURLUnmarshalJSON(t *testing.T) {
+ t.Parallel()
url := &URL{}
err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
if err != nil {
@@ -77,6 +81,7 @@ func TestURLUnmarshalJSON(t *testing.T) {
}
func TestURLComparison(t *testing.T) {
+ t.Parallel()
tests := []struct {
urlA URL
urlB URL
From 3dc071e0367505f4234ef880b853115c4040cfad Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Tue, 5 Dec 2023 09:37:48 +0100
Subject: [PATCH 057/103] eth/tracers/logger: make structlog/json-log stack hex
again (#28628)
* common/hexutil: define hex wrappers for uint256.Int
* eth/tracers/logger: make structlog/json-log stack hex again
* common/hexutil: goimports
---
common/hexutil/json.go | 45 ++++++++++++++++++++++
common/hexutil/json_test.go | 60 +++++++++++++++++++++++++++++
eth/tracers/logger/gen_structlog.go | 16 ++++++--
eth/tracers/logger/logger.go | 1 +
4 files changed, 118 insertions(+), 4 deletions(-)
diff --git a/common/hexutil/json.go b/common/hexutil/json.go
index 50db208118ee..e0ac98f52d15 100644
--- a/common/hexutil/json.go
+++ b/common/hexutil/json.go
@@ -23,6 +23,8 @@ import (
"math/big"
"reflect"
"strconv"
+
+ "github.com/holiman/uint256"
)
var (
@@ -30,6 +32,7 @@ var (
bigT = reflect.TypeOf((*Big)(nil))
uintT = reflect.TypeOf(Uint(0))
uint64T = reflect.TypeOf(Uint64(0))
+ u256T = reflect.TypeOf((*uint256.Int)(nil))
)
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
@@ -225,6 +228,48 @@ func (b *Big) UnmarshalGraphQL(input interface{}) error {
return err
}
+// U256 marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type U256 uint256.Int
+
+// MarshalText implements encoding.TextMarshaler
+func (b U256) MarshalText() ([]byte, error) {
+ u256 := (*uint256.Int)(&b)
+ return []byte(u256.Hex()), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *U256) UnmarshalJSON(input []byte) error {
+ // The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be
+ // more strict, hence we check string and invoke SetFromHex directly.
+ if !isString(input) {
+ return errNonString(u256T)
+ }
+ // The hex decoder needs to accept empty string ("") as '0', which uint256.Int
+ // would reject.
+ if len(input) == 2 {
+ (*uint256.Int)(b).Clear()
+ return nil
+ }
+ err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1]))
+ if err != nil {
+ return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T}
+ }
+ return nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *U256) UnmarshalText(input []byte) error {
+ // The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be
+ // more strict, hence we check string and invoke SetFromHex directly.
+ return (*uint256.Int)(b).SetFromHex(string(input))
+}
+
+// String returns the hex encoding of b.
+func (b *U256) String() string {
+ return (*uint256.Int)(b).Hex()
+}
+
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
// The zero value marshals as "0x0".
type Uint64 uint64
diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go
index ed7d6fad1a8e..7cca300951cd 100644
--- a/common/hexutil/json_test.go
+++ b/common/hexutil/json_test.go
@@ -23,6 +23,8 @@ import (
"errors"
"math/big"
"testing"
+
+ "github.com/holiman/uint256"
)
func checkError(t *testing.T, input string, got, want error) bool {
@@ -176,6 +178,64 @@ func TestUnmarshalBig(t *testing.T) {
}
}
+var unmarshalU256Tests = []unmarshalTest{
+ // invalid encoding
+ {input: "", wantErr: errJSONEOF},
+ {input: "null", wantErr: errNonString(u256T)},
+ {input: "10", wantErr: errNonString(u256T)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, u256T)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, u256T)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, u256T)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, u256T)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, u256T)},
+ {
+ input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
+ wantErr: wrapTypeError(ErrBig256Range, u256T),
+ },
+
+ // valid encoding
+ {input: `""`, want: big.NewInt(0)},
+ {input: `"0x0"`, want: big.NewInt(0)},
+ {input: `"0x2"`, want: big.NewInt(0x2)},
+ {input: `"0x2F2"`, want: big.NewInt(0x2f2)},
+ {input: `"0X2F2"`, want: big.NewInt(0x2f2)},
+ {input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
+ {input: `"0xbBb"`, want: big.NewInt(0xbbb)},
+ {input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
+ {
+ input: `"0x112233445566778899aabbccddeeff"`,
+ want: referenceBig("112233445566778899aabbccddeeff"),
+ },
+ {
+ input: `"0xffffffffffffffffffffffffffffffffffff"`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
+ },
+ {
+ input: `"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
+ },
+}
+
+func TestUnmarshalU256(t *testing.T) {
+ for _, test := range unmarshalU256Tests {
+ var v U256
+ err := json.Unmarshal([]byte(test.input), &v)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if test.want == nil {
+ continue
+ }
+ want := new(uint256.Int)
+ want.SetFromBig(test.want.(*big.Int))
+ have := (*uint256.Int)(&v)
+ if want.Cmp(have) != 0 {
+ t.Errorf("input %s: value mismatch: have %x, want %x", test.input, have, want)
+ continue
+ }
+ }
+}
+
func BenchmarkUnmarshalBig(b *testing.B) {
input := []byte(`"0x123456789abcdef123456789abcdef"`)
for i := 0; i < b.N; i++ {
diff --git a/eth/tracers/logger/gen_structlog.go b/eth/tracers/logger/gen_structlog.go
index df06a9ee6b66..b406cb344546 100644
--- a/eth/tracers/logger/gen_structlog.go
+++ b/eth/tracers/logger/gen_structlog.go
@@ -23,7 +23,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory,omitempty"`
MemorySize int `json:"memSize"`
- Stack []uint256.Int `json:"stack"`
+ Stack []hexutil.U256 `json:"stack"`
ReturnData hexutil.Bytes `json:"returnData,omitempty"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
@@ -39,7 +39,12 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
enc.MemorySize = s.MemorySize
- enc.Stack = s.Stack
+ if s.Stack != nil {
+ enc.Stack = make([]hexutil.U256, len(s.Stack))
+ for k, v := range s.Stack {
+ enc.Stack[k] = hexutil.U256(v)
+ }
+ }
enc.ReturnData = s.ReturnData
enc.Storage = s.Storage
enc.Depth = s.Depth
@@ -59,7 +64,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory,omitempty"`
MemorySize *int `json:"memSize"`
- Stack []uint256.Int `json:"stack"`
+ Stack []hexutil.U256 `json:"stack"`
ReturnData *hexutil.Bytes `json:"returnData,omitempty"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
@@ -89,7 +94,10 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.MemorySize = *dec.MemorySize
}
if dec.Stack != nil {
- s.Stack = dec.Stack
+ s.Stack = make([]uint256.Int, len(dec.Stack))
+ for k, v := range dec.Stack {
+ s.Stack[k] = uint256.Int(v)
+ }
}
if dec.ReturnData != nil {
s.ReturnData = *dec.ReturnData
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
index 4c9b910a27f1..2b36f9f4922f 100644
--- a/eth/tracers/logger/logger.go
+++ b/eth/tracers/logger/logger.go
@@ -83,6 +83,7 @@ type structLogMarshaling struct {
GasCost math.HexOrDecimal64
Memory hexutil.Bytes
ReturnData hexutil.Bytes
+ Stack []hexutil.U256
OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
ErrorString string `json:"error,omitempty"` // adds call to ErrorString() in MarshalJSON
}
From b8d44ed98b9bc8320f1f64e82c13cf32918f1f6b Mon Sep 17 00:00:00 2001
From: Martin Holst Swende
Date: Tue, 5 Dec 2023 11:54:44 +0100
Subject: [PATCH 058/103] log: remove lazy, remove unused interfaces, unexport
methods (#28622)
This change
- Removes interface `log.Format`,
- Removes method `log.FormatFunc`,
- unexports `TerminalHandler.TerminalFormat` formatting methods (renamed to `TerminalHandler.format`)
- removes the notion of `log.Lazy` values
The lazy handler was useful in the old log package, since it
could defer the evaluation of costly attributes until later in the
log pipeline: thus, if the logging was done at 'Trace', we could
skip evaluation if logging only was set to 'Info'.
With the move to slog, this way of deferring evaluation is no longer
needed, since slog introduced 'Enabled': the caller can thus do
the evaluate-or-not decision at the callsite, which is much more
straight-forward than dealing with lazy reflect-based evaluation.
Also, lazy evaluation would not work with 'native' slog, as in, these
two statements would be evaluated differently:
```golang
log.Info("foo", "my lazy", lazyObj)
slog.Info("foo", "my lazy", lazyObj)
```
---
cmd/geth/logtestcmd_active.go | 10 ++--
cmd/geth/testdata/logging/logtest-json.txt | 3 +-
cmd/geth/testdata/logging/logtest-logfmt.txt | 3 +-
.../testdata/logging/logtest-terminal.txt | 5 +-
internal/testlog/testlog.go | 6 +-
log/format.go | 55 ++++---------------
log/handler.go | 39 +------------
log/logger.go | 28 +++-------
log/logger_test.go | 4 +-
p2p/discover/table.go | 7 ++-
p2p/msgrate/msgrate.go | 5 +-
11 files changed, 49 insertions(+), 116 deletions(-)
diff --git a/cmd/geth/logtestcmd_active.go b/cmd/geth/logtestcmd_active.go
index 0ca4cc621dc0..5cce1ec6abc6 100644
--- a/cmd/geth/logtestcmd_active.go
+++ b/cmd/geth/logtestcmd_active.go
@@ -43,6 +43,7 @@ This command is only meant for testing.
type customQuotedStringer struct {
}
+
func (c customQuotedStringer) String() string {
return "output with 'quotes'"
}
@@ -80,8 +81,6 @@ func logTest(ctx *cli.Context) error {
log.Info("uint64", "18,446,744,073,709,551,615", uint64(math.MaxUint64))
}
{ // Special characters
-
-
log.Info("Special chars in value", "key", "special \r\n\t chars")
log.Info("Special chars in key", "special \n\t chars", "value")
@@ -103,9 +102,6 @@ func logTest(ctx *cli.Context) error {
var c customQuotedStringer
log.Info("a custom stringer that emits quoted text", "output", c)
}
- { // Lazy eval
- log.Info("Lazy evaluation of value", "key", log.Lazy{Fn: func() interface{} { return "lazy value" }})
- }
{ // Multi-line message
log.Info("A message with wonky \U0001F4A9 characters")
log.Info("A multiline message \nINFO [10-18|14:11:31.106] with wonky characters \U0001F4A9")
@@ -166,6 +162,10 @@ func logTest(ctx *cli.Context) error {
{ // Logging with 'reserved' keys
log.Info("Using keys 't', 'lvl', 'time', 'level' and 'msg'", "t", "t", "time", "time", "lvl", "lvl", "level", "level", "msg", "msg")
}
+ { // Logging with wrong attr-value pairs
+ log.Info("Odd pair (1 attr)", "key")
+ log.Info("Odd pair (3 attr)", "key", "value", "key2")
+ }
return nil
}
diff --git a/cmd/geth/testdata/logging/logtest-json.txt b/cmd/geth/testdata/logging/logtest-json.txt
index bdc1ae4de6c4..3bfe718660c3 100644
--- a/cmd/geth/testdata/logging/logtest-json.txt
+++ b/cmd/geth/testdata/logging/logtest-json.txt
@@ -21,7 +21,6 @@
{"t":"2023-11-22T15:42:00.408197+08:00","lvl":"info","msg":"an error message with quotes","error":"this is an 'error'"}
{"t":"2023-11-22T15:42:00.408202+08:00","lvl":"info","msg":"Custom Stringer value","2562047h47m16.854s":"2562047h47m16.854s"}
{"t":"2023-11-22T15:42:00.408208+08:00","lvl":"info","msg":"a custom stringer that emits quoted text","output":"output with 'quotes'"}
-{"t":"2023-11-22T15:42:00.408215+08:00","lvl":"info","msg":"Lazy evaluation of value","key":"lazy value"}
{"t":"2023-11-22T15:42:00.408219+08:00","lvl":"info","msg":"A message with wonky 💩 characters"}
{"t":"2023-11-22T15:42:00.408222+08:00","lvl":"info","msg":"A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"}
{"t":"2023-11-22T15:42:00.408226+08:00","lvl":"info","msg":"A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"}
@@ -49,3 +48,5 @@
{"t":"2023-11-22T15:42:00.40835+08:00","lvl":"info","msg":"raw nil","res":null}
{"t":"2023-11-22T15:42:00.408354+08:00","lvl":"info","msg":"(*uint64)(nil)","res":null}
{"t":"2023-11-22T15:42:00.408361+08:00","lvl":"info","msg":"Using keys 't', 'lvl', 'time', 'level' and 'msg'","t":"t","time":"time","lvl":"lvl","level":"level","msg":"msg"}
+{"t":"2023-11-29T15:13:00.195655931+01:00","lvl":"info","msg":"Odd pair (1 attr)","key":null,"LOG_ERROR":"Normalized odd number of arguments by adding nil"}
+{"t":"2023-11-29T15:13:00.195681832+01:00","lvl":"info","msg":"Odd pair (3 attr)","key":"value","key2":null,"LOG_ERROR":"Normalized odd number of arguments by adding nil"}
diff --git a/cmd/geth/testdata/logging/logtest-logfmt.txt b/cmd/geth/testdata/logging/logtest-logfmt.txt
index 114569e467c8..f20d66635d36 100644
--- a/cmd/geth/testdata/logging/logtest-logfmt.txt
+++ b/cmd/geth/testdata/logging/logtest-logfmt.txt
@@ -21,7 +21,6 @@ t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="\x1b[35mColored\x1b[0m[" "\x1b[35mColor
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="an error message with quotes" error="this is an 'error'"
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Custom Stringer value" 2562047h47m16.854s=2562047h47m16.854s
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="a custom stringer that emits quoted text" output="output with 'quotes'"
-t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A message with wonky 💩 characters"
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"
@@ -49,3 +48,5 @@ t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=nil-custom-struct res=
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="raw nil" res=
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=(*uint64)(nil) res=
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Using keys 't', 'lvl', 'time', 'level' and 'msg'" t=t time=time lvl=lvl level=level msg=msg
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Odd pair (1 attr)" key= LOG_ERROR="Normalized odd number of arguments by adding nil"
+t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="Odd pair (3 attr)" key=value key2= LOG_ERROR="Normalized odd number of arguments by adding nil"
diff --git a/cmd/geth/testdata/logging/logtest-terminal.txt b/cmd/geth/testdata/logging/logtest-terminal.txt
index 4da3f49d46a4..e3b562117c42 100644
--- a/cmd/geth/testdata/logging/logtest-terminal.txt
+++ b/cmd/geth/testdata/logging/logtest-terminal.txt
@@ -21,8 +21,7 @@ INFO [xx-xx|xx:xx:xx.xxx] "\x1b[35mColored\x1b[0m[" "\x1b[35mColo
INFO [xx-xx|xx:xx:xx.xxx] an error message with quotes error="this is an 'error'"
INFO [xx-xx|xx:xx:xx.xxx] Custom Stringer value 2562047h47m16.854s=2562047h47m16.854s
INFO [xx-xx|xx:xx:xx.xxx] a custom stringer that emits quoted text output="output with 'quotes'"
-INFO [xx-xx|xx:xx:xx.xxx] Lazy evaluation of value key="lazy value"
-INFO [xx-xx|xx:xx:xx.xxx] "A message with wonky 💩 characters"
+INFO [xx-xx|xx:xx:xx.xxx] "A message with wonky 💩 characters"
INFO [xx-xx|xx:xx:xx.xxx] "A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
INFO [xx-xx|xx:xx:xx.xxx] A multiline message
LALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above
@@ -50,3 +49,5 @@ INFO [xx-xx|xx:xx:xx.xxx] nil-custom-struct res=
INFO [xx-xx|xx:xx:xx.xxx] raw nil res=
INFO [xx-xx|xx:xx:xx.xxx] (*uint64)(nil) res=
INFO [xx-xx|xx:xx:xx.xxx] Using keys 't', 'lvl', 'time', 'level' and 'msg' t=t time=time lvl=lvl level=level msg=msg
+INFO [xx-xx|xx:xx:xx.xxx] Odd pair (1 attr) key= LOG_ERROR="Normalized odd number of arguments by adding nil"
+INFO [xx-xx|xx:xx:xx.xxx] Odd pair (3 attr) key=value key2= LOG_ERROR="Normalized odd number of arguments by adding nil"
diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go
index a7899c81589a..037b7ee9c120 100644
--- a/internal/testlog/testlog.go
+++ b/internal/testlog/testlog.go
@@ -100,6 +100,10 @@ func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger {
func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {}
+func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
+ return l.l.Enabled(ctx, level)
+}
+
func (l *logger) Trace(msg string, ctx ...interface{}) {
l.t.Helper()
l.mu.Lock()
@@ -183,7 +187,7 @@ func (h *bufHandler) terminalFormat(r slog.Record) string {
}
for _, attr := range attrs {
- fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, true, nil)))
+ fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, nil)))
}
buf.WriteByte('\n')
return buf.String()
diff --git a/log/format.go b/log/format.go
index a2bbcce9c056..6447f3c1f1e9 100644
--- a/log/format.go
+++ b/log/format.go
@@ -23,22 +23,6 @@ const (
// 40 spaces
var spaces = []byte(" ")
-type Format interface {
- Format(r slog.Record) []byte
-}
-
-// FormatFunc returns a new Format object which uses
-// the given function to perform record formatting.
-func FormatFunc(f func(slog.Record) []byte) Format {
- return formatFunc(f)
-}
-
-type formatFunc func(slog.Record) []byte
-
-func (f formatFunc) Format(r slog.Record) []byte {
- return f(r)
-}
-
// TerminalStringer is an analogous interface to the stdlib stringer, allowing
// own types to have custom shortened serialization formats when printed to the
// screen.
@@ -46,7 +30,7 @@ type TerminalStringer interface {
TerminalString() string
}
-func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor bool) []byte {
+func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byte {
msg := escapeMessage(r.Message)
var color = ""
if usecolor {
@@ -88,13 +72,13 @@ func (h *TerminalHandler) TerminalFormat(buf []byte, r slog.Record, usecolor boo
if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust {
b.Write(spaces[:termMsgJust-length])
}
- // print the keys logfmt style
- h.logfmt(b, r, color)
+ // print the attributes
+ h.formatAttributes(b, r, color)
return b.Bytes()
}
-func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string) {
+func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) {
// tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21)
// can be used.
var tmp = make([]byte, 40)
@@ -112,7 +96,7 @@ func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string)
buf.WriteByte('=')
}
//val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer())
- val := FormatSlogValue(attr.Value, true, tmp[:0])
+ val := FormatSlogValue(attr.Value, tmp[:0])
padding := h.fieldPadding[attr.Key]
@@ -140,8 +124,8 @@ func (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color string)
buf.WriteByte('\n')
}
-// FormatSlogValue formats a slog.Value for serialization
-func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) {
+// FormatSlogValue formats a slog.Value for serialization to terminal.
+func FormatSlogValue(v slog.Value, tmp []byte) (result []byte) {
var value any
defer func() {
if err := recover(); err != nil {
@@ -156,11 +140,9 @@ func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) {
switch v.Kind() {
case slog.KindString:
return appendEscapeString(tmp, v.String())
- case slog.KindAny:
- value = v.Any()
- case slog.KindInt64: // All int-types (int8 ,int16 etc) wind up here
+ case slog.KindInt64: // All int-types (int8, int16 etc) wind up here
return appendInt64(tmp, v.Int64())
- case slog.KindUint64: // All uint-types (int8 ,int16 etc) wind up here
+ case slog.KindUint64: // All uint-types (uint8, uint16 etc) wind up here
return appendUint64(tmp, v.Uint64(), false)
case slog.KindFloat64:
return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64)
@@ -180,27 +162,14 @@ func FormatSlogValue(v slog.Value, term bool, tmp []byte) (result []byte) {
return []byte("")
}
switch v := value.(type) {
- case *big.Int:
- // Big ints get consumed by the Stringer clause, so we need to handle
- // them earlier on.
- if v == nil {
- return append(tmp, []byte("")...)
- }
+ case *big.Int: // Need to be before fmt.Stringer-clause
return appendBigInt(tmp, v)
-
- case *uint256.Int:
- // Uint256s get consumed by the Stringer clause, so we need to handle
- // them earlier on.
- if v == nil {
- return append(tmp, []byte("")...)
- }
+ case *uint256.Int: // Need to be before fmt.Stringer-clause
return appendU256(tmp, v)
case error:
return appendEscapeString(tmp, v.Error())
case TerminalStringer:
- if term {
- return appendEscapeString(tmp, v.TerminalString()) // Custom terminal stringer provided, use that
- }
+ return appendEscapeString(tmp, v.TerminalString())
case fmt.Stringer:
return appendEscapeString(tmp, v.String())
}
diff --git a/log/handler.go b/log/handler.go
index 1a25577450e4..7459aad8913b 100644
--- a/log/handler.go
+++ b/log/handler.go
@@ -13,42 +13,6 @@ import (
"golang.org/x/exp/slog"
)
-// Lazy allows you to defer calculation of a logged value that is expensive
-// to compute until it is certain that it must be evaluated with the given filters.
-//
-// You may wrap any function which takes no arguments to Lazy. It may return any
-// number of values of any type.
-type Lazy struct {
- Fn interface{}
-}
-
-func evaluateLazy(lz Lazy) (interface{}, error) {
- t := reflect.TypeOf(lz.Fn)
-
- if t.Kind() != reflect.Func {
- return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
- }
-
- if t.NumIn() > 0 {
- return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
- }
-
- if t.NumOut() == 0 {
- return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
- }
-
- value := reflect.ValueOf(lz.Fn)
- results := value.Call([]reflect.Value{})
- if len(results) == 1 {
- return results[0].Interface(), nil
- }
- values := make([]interface{}, len(results))
- for i, v := range results {
- values[i] = v.Interface()
- }
- return values, nil
-}
-
type discardHandler struct{}
// DiscardHandler returns a no-op handler
@@ -112,7 +76,7 @@ func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *T
func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
- buf := h.TerminalFormat(h.buf, r, h.useColor)
+ buf := h.format(h.buf, r, h.useColor)
h.wr.Write(buf)
h.buf = buf[:0]
return nil
@@ -149,6 +113,7 @@ func (l *leveler) Level() slog.Level {
return l.minLevel
}
+// JSONHandler returns a handler which prints records in JSON format.
func JSONHandler(wr io.Writer) slog.Handler {
return slog.NewJSONHandler(wr, &slog.HandlerOptions{
ReplaceAttr: builtinReplaceJSON,
diff --git a/log/logger.go b/log/logger.go
index 3e227745adf5..93d62f080b76 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -134,6 +134,9 @@ type Logger interface {
// Write logs a message at the specified level
Write(level slog.Level, msg string, attrs ...any)
+
+ // Enabled reports whether l emits log records at the given context and level.
+ Enabled(ctx context.Context, level slog.Level) bool
}
type logger struct {
@@ -159,26 +162,6 @@ func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
if len(attrs)%2 != 0 {
attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
}
-
- // evaluate lazy values
- var hadErr bool
- for i := 1; i < len(attrs); i += 2 {
- lz, ok := attrs[i].(Lazy)
- if ok {
- v, err := evaluateLazy(lz)
- if err != nil {
- hadErr = true
- attrs[i] = err
- } else {
- attrs[i] = v
- }
- }
- }
-
- if hadErr {
- attrs = append(attrs, errorKey, "bad lazy")
- }
-
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
r.Add(attrs...)
l.inner.Handler().Handle(context.Background(), r)
@@ -196,6 +179,11 @@ func (l *logger) New(ctx ...interface{}) Logger {
return l.With(ctx...)
}
+// Enabled reports whether l emits log records at the given context and level.
+func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
+ return l.inner.Enabled(ctx, level)
+}
+
func (l *logger) Trace(msg string, ctx ...interface{}) {
l.Write(LevelTrace, msg, ctx...)
}
diff --git a/log/logger_test.go b/log/logger_test.go
index 27e90c5fd203..a633f5ad7a4c 100644
--- a/log/logger_test.go
+++ b/log/logger_test.go
@@ -107,7 +107,6 @@ func TestLoggerOutput(t *testing.T) {
bigint = big.NewInt(100)
nilbig *big.Int
err = fmt.Errorf("Oh nooes it's crap")
- lazy = Lazy{Fn: func() interface{} { return "lazy value" }}
smallUint = uint256.NewInt(500_000)
bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff}
)
@@ -126,13 +125,12 @@ func TestLoggerOutput(t *testing.T) {
"struct", customA,
"struct", customB,
"ptrstruct", &customA,
- "lazy", lazy,
"smalluint", smallUint,
"bigUint", bigUint)
have := out.String()
t.Logf("output %v", out.String())
- want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" lazy="lazy value" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
+ want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
`
if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) {
t.Errorf("Error\nhave: %q\nwant: %q", have, want)
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index f476d2079f8c..e6dafb0dcaa8 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -23,6 +23,7 @@
package discover
import (
+ "context"
crand "crypto/rand"
"encoding/binary"
"fmt"
@@ -330,8 +331,10 @@ func (tab *Table) loadSeedNodes() {
seeds = append(seeds, tab.nursery...)
for i := range seeds {
seed := seeds[i]
- age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP())) }}
- tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
+ if tab.log.Enabled(context.Background(), log.LevelTrace) {
+ age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
+ tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
+ }
tab.addSeenNode(seed)
}
}
diff --git a/p2p/msgrate/msgrate.go b/p2p/msgrate/msgrate.go
index 4f08792242af..de1a3177db0f 100644
--- a/p2p/msgrate/msgrate.go
+++ b/p2p/msgrate/msgrate.go
@@ -18,6 +18,7 @@
package msgrate
import (
+ "context"
"errors"
"math"
"sort"
@@ -410,7 +411,9 @@ func (t *Trackers) tune() {
t.tuned = time.Now()
t.log.Debug("Recalculated msgrate QoS values", "rtt", t.roundtrip, "confidence", t.confidence, "ttl", t.targetTimeout(), "next", t.tuned.Add(t.roundtrip))
- t.log.Trace("Debug dump of mean capacities", "caps", log.Lazy{Fn: t.meanCapacities})
+ if t.log.Enabled(context.Background(), log.LevelTrace) {
+ t.log.Trace("Debug dump of mean capacities", "caps", t.meanCapacities())
+ }
}
// detune reduces the tracker's confidence in order to make fresh measurements
From 69576df2544d9a6c59c5659b82a064edc9845874 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 5 Dec 2023 14:45:40 +0100
Subject: [PATCH 059/103] .github: use github actions to run 32-bit linux tests
(#28549)
use github actions to run 32-bit linux tests
---
.github/workflows/go.yml | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 .github/workflows/go.yml
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 000000000000..7924c521e854
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,23 @@
+name: i386 linux tests
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: self-hosted
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Go
+ uses: actions/setup-go@v2
+ with:
+ go-version: 1.21.4
+ - name: Run tests
+ run: go test ./...
+ env:
+ GOOS: linux
+ GOARCH: 386
From 55b483d82aa23772f8a8b330e3837a07c4fd00df Mon Sep 17 00:00:00 2001
From: Marius van der Wijden
Date: Wed, 6 Dec 2023 11:41:04 +0100
Subject: [PATCH 060/103] ethdb/pebble: remove a dependency (#28627)
The dependency was not really used anyway, so we can get rid of it.
Co-authored-by: Felix Lange
---
ethdb/pebble/pebble.go | 3 +--
go.mod | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go
index d58329c6d60e..af4686cf5b72 100644
--- a/ethdb/pebble/pebble.go
+++ b/ethdb/pebble/pebble.go
@@ -25,7 +25,6 @@ import (
"sync/atomic"
"time"
- "github.com/cockroachdb/errors"
"github.com/cockroachdb/pebble"
"github.com/cockroachdb/pebble/bloom"
"github.com/ethereum/go-ethereum/common"
@@ -131,7 +130,7 @@ func (l panicLogger) Errorf(format string, args ...interface{}) {
}
func (l panicLogger) Fatalf(format string, args ...interface{}) {
- panic(errors.Errorf("fatal: "+format, args...))
+ panic(fmt.Errorf("fatal: "+format, args...))
}
// New returns a wrapped pebble DB object. The namespace is the prefix that the
diff --git a/go.mod b/go.mod
index 75c2b899d690..8f99a00754ed 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,6 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.2.0
github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.79.0
- github.com/cockroachdb/errors v1.8.1
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
github.com/consensys/gnark-crypto v0.12.1
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
@@ -91,6 +90,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cockroachdb/errors v1.8.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect
github.com/cockroachdb/redact v1.0.8 // indirect
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
From a113497dd7e5f5116996b34c514c1c9273bfcc97 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 7 Dec 2023 10:07:20 +0100
Subject: [PATCH 061/103] tests/fuzzers/bls12381: deactivate BLS fuzzer when
CGO_ENABLED=0 (#28653)
tests/fuzzers/bls12381: deactivate fuzzer when CGO_ENABLED=0
---
tests/fuzzers/bls12381/bls12381_fuzz.go | 3 +++
tests/fuzzers/bls12381/bls12381_test.go | 3 +++
2 files changed, 6 insertions(+)
diff --git a/tests/fuzzers/bls12381/bls12381_fuzz.go b/tests/fuzzers/bls12381/bls12381_fuzz.go
index f04524f76a9f..9a5c566540f7 100644
--- a/tests/fuzzers/bls12381/bls12381_fuzz.go
+++ b/tests/fuzzers/bls12381/bls12381_fuzz.go
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build cgo
+// +build cgo
+
package bls
import (
diff --git a/tests/fuzzers/bls12381/bls12381_test.go b/tests/fuzzers/bls12381/bls12381_test.go
index 59e4db31d5d7..3e88979d1607 100644
--- a/tests/fuzzers/bls12381/bls12381_test.go
+++ b/tests/fuzzers/bls12381/bls12381_test.go
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build cgo
+// +build cgo
+
package bls
import "testing"
From 77c4bbcaa5f554f4cd73bdb7033d17b1fec493e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20Kj=C3=A6rstad?=
Date: Thu, 7 Dec 2023 11:45:09 +0100
Subject: [PATCH 062/103] build: upgrade -dlgo version to Go 1.21.5 (#28648)
---
build/checksums.txt | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/build/checksums.txt b/build/checksums.txt
index c96bd8566786..8d735fdb3d3f 100644
--- a/build/checksums.txt
+++ b/build/checksums.txt
@@ -5,22 +5,22 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/
485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz
-# version:golang 1.21.4
+# version:golang 1.21.5
# https://go.dev/dl/
-47b26a83d2b65a3c1c1bcace273b69bee49a7a7b5168a7604ded3d26a37bd787 go1.21.4.src.tar.gz
-cd3bdcc802b759b70e8418bc7afbc4a65ca73a3fe576060af9fc8a2a5e71c3b8 go1.21.4.darwin-amd64.tar.gz
-8b7caf2ac60bdff457dba7d4ff2a01def889592b834453431ae3caecf884f6a5 go1.21.4.darwin-arm64.tar.gz
-f1e685d086eb36f4be5b8b953b52baf7752bc6235400d84bb7d87e500b65f03e go1.21.4.freebsd-386.tar.gz
-59f9b32187efb98d344a3818a631d3815ebb5c7bbefc367bab6515caaca544e9 go1.21.4.freebsd-amd64.tar.gz
-64d3e5d295806e137c9e39d1e1f10b00a30fcd5c2f230d72b3298f579bb3c89a go1.21.4.linux-386.tar.gz
-73cac0215254d0c7d1241fa40837851f3b9a8a742d0b54714cbdfb3feaf8f0af go1.21.4.linux-amd64.tar.gz
-ce1983a7289856c3a918e1fd26d41e072cc39f928adfb11ba1896440849b95da go1.21.4.linux-arm64.tar.gz
-6c62e89113750cc77c498194d13a03fadfda22bd2c7d44e8a826fd354db60252 go1.21.4.linux-armv6l.tar.gz
-2c63b36d2adcfb22013102a2ee730f058ec2f93b9f27479793c80b2e3641783f go1.21.4.linux-ppc64le.tar.gz
-7a75ba4afc7a96058ca65903d994cd862381825d7dca12b2183f087c757c26c0 go1.21.4.linux-s390x.tar.gz
-870a0e462b94671dc2d6cac707e9e19f7524fdc3c90711e6cd4450c3713a8ce0 go1.21.4.windows-386.zip
-79e5428e068c912d9cfa6cd115c13549856ec689c1332eac17f5d6122e19d595 go1.21.4.windows-amd64.zip
-58bc7c6f4d4c72da2df4d2650c8222fe03c9978070eb3c66be8bbaa2a4757ac1 go1.21.4.windows-arm64.zip
+285cbbdf4b6e6e62ed58f370f3f6d8c30825d6e56c5853c66d3c23bcdb09db19 go1.21.5.src.tar.gz
+a2e1d5743e896e5fe1e7d96479c0a769254aed18cf216cf8f4c3a2300a9b3923 go1.21.5.darwin-amd64.tar.gz
+d0f8ac0c4fb3efc223a833010901d02954e3923cfe2c9a2ff0e4254a777cc9cc go1.21.5.darwin-arm64.tar.gz
+2c05bbe0dc62456b90b7ddd354a54f373b7c377a98f8b22f52ab694b4f6cca58 go1.21.5.freebsd-386.tar.gz
+30b6c64e9a77129605bc12f836422bf09eec577a8c899ee46130aeff81567003 go1.21.5.freebsd-amd64.tar.gz
+8f4dba9cf5c61757bbd7e9ebdb93b6a30a1b03f4a636a1ba0cc2f27b907ab8e1 go1.21.5.linux-386.tar.gz
+e2bc0b3e4b64111ec117295c088bde5f00eeed1567999ff77bc859d7df70078e go1.21.5.linux-amd64.tar.gz
+841cced7ecda9b2014f139f5bab5ae31785f35399f236b8b3e75dff2a2978d96 go1.21.5.linux-arm64.tar.gz
+837f4bf4e22fcdf920ffeaa4abf3d02d1314e03725431065f4d44c46a01b42fe go1.21.5.linux-armv6l.tar.gz
+907b8c6ec4be9b184952e5d3493be66b1746442394a8bc78556c56834cd7c38b go1.21.5.linux-ppc64le.tar.gz
+9c4a81b72ebe44368813cd03684e1080a818bf915d84163abae2ed325a1b2dc0 go1.21.5.linux-s390x.tar.gz
+6da2418889dfb37763d0eb149c4a8d728c029e12f0cd54fbca0a31ae547e2d34 go1.21.5.windows-386.zip
+bbe603cde7c9dee658f45164b4d06de1eff6e6e6b800100824e7c00d56a9a92f go1.21.5.windows-amd64.zip
+9b7acca50e674294e43202df4fbc26d5af4d8bc3170a3342a1514f09a2dab5e9 go1.21.5.windows-arm64.zip
# version:golangci 1.51.1
# https://github.com/golangci/golangci-lint/releases/
From 5a45e7a631e0a2457178f346a03c4128efb7d009 Mon Sep 17 00:00:00 2001
From: Martin HS
Date: Fri, 8 Dec 2023 09:40:50 +0100
Subject: [PATCH 063/103] =?UTF-8?q?rpc:=20fix=20ns/=C2=B5s=20mismatch=20in?=
=?UTF-8?q?=20metrics=20(#28649)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The rpc/duration/all meter was in nanoseconds, the individual meter in microseconds.
This PR changes it so both of them use nanoseconds.
---
metrics/timer.go | 10 ++++------
rpc/metrics.go | 2 +-
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/metrics/timer.go b/metrics/timer.go
index 576ad8aa3e63..bb8def82fb29 100644
--- a/metrics/timer.go
+++ b/metrics/timer.go
@@ -106,20 +106,18 @@ func (t *StandardTimer) Time(f func()) {
t.Update(time.Since(ts))
}
-// Record the duration of an event.
+// Record the duration of an event, in nanoseconds.
func (t *StandardTimer) Update(d time.Duration) {
t.mutex.Lock()
defer t.mutex.Unlock()
- t.histogram.Update(int64(d))
+ t.histogram.Update(d.Nanoseconds())
t.meter.Mark(1)
}
// Record the duration of an event that started at a time and ends now.
+// The record uses nanoseconds.
func (t *StandardTimer) UpdateSince(ts time.Time) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- t.histogram.Update(int64(time.Since(ts)))
- t.meter.Mark(1)
+ t.Update(time.Since(ts))
}
// timerSnapshot is a read-only copy of another Timer.
diff --git a/rpc/metrics.go b/rpc/metrics.go
index b1f1284535e2..ef7449ce05e2 100644
--- a/rpc/metrics.go
+++ b/rpc/metrics.go
@@ -46,5 +46,5 @@ func updateServeTimeHistogram(method string, success bool, elapsed time.Duration
metrics.NewExpDecaySample(1028, 0.015),
)
}
- metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Microseconds())
+ metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds())
}
From 1048e2d6a36b8c3fb467d60fd33122bae0cc7ebe Mon Sep 17 00:00:00 2001
From: Martin HS
Date: Fri, 8 Dec 2023 11:06:01 +0100
Subject: [PATCH 064/103] cmd/evm: fix dump after state-test exec (#28650)
The dump after state-test didn't work, the problem was an error, "Already committed", which was silently ignored.
This change re-initialises the state, so the dumping works again.
---
cmd/evm/staterunner.go | 17 +++++++++--------
core/state/dump.go | 1 +
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 618ddf2ede13..6e751b630f58 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -100,18 +100,19 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
for _, st := range test.Subtests() {
// Run the test and aggregate the result
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
- test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
- if state != nil {
- root := state.IntermediateRoot(false)
+ test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, statedb *state.StateDB) {
+ var root common.Hash
+ if statedb != nil {
+ root = statedb.IntermediateRoot(false)
result.Root = &root
if jsonOut {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
}
- }
- // Dump any state to aid debugging
- if dump {
- dump := state.RawDump(nil)
- result.State = &dump
+ if dump { // Dump any state to aid debugging
+ cpy, _ := state.New(root, statedb.Database(), nil)
+ dump := cpy.RawDump(nil)
+ result.State = &dump
+ }
}
if err != nil {
// Test failed, mark as so
diff --git a/core/state/dump.go b/core/state/dump.go
index cf4662114448..55abb50f1c5a 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -129,6 +129,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
trieIt, err := s.trie.NodeIterator(conf.Start)
if err != nil {
+ log.Error("Trie dumping error", "err", err)
return nil
}
it := trie.NewIterator(trieIt)
From fff843cfafddb43aebad6e3e5ad8b0f68143759d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?=
Date: Fri, 8 Dec 2023 13:38:00 +0100
Subject: [PATCH 065/103] beacon/light: add CommitteeChain (#27766)
This change implements CommitteeChain which is a key component of the beacon light client. It is a passive data structure that can validate, hold and update a chain of beacon light sync committees and updates, starting from a checkpoint that proves the starting committee through a beacon block hash, header and corresponding state. Once synced to the current sync period, CommitteeChain can also validate signed beacon headers.
---
beacon/light/canonical.go | 125 ++++++
beacon/light/committee_chain.go | 514 ++++++++++++++++++++++
beacon/light/committee_chain_test.go | 356 +++++++++++++++
beacon/light/range.go | 78 ++++
beacon/light/test_helpers.go | 152 +++++++
beacon/types/{update.go => light_sync.go} | 18 +
core/rawdb/schema.go | 4 +
7 files changed, 1247 insertions(+)
create mode 100644 beacon/light/canonical.go
create mode 100644 beacon/light/committee_chain.go
create mode 100644 beacon/light/committee_chain_test.go
create mode 100644 beacon/light/range.go
create mode 100644 beacon/light/test_helpers.go
rename beacon/types/{update.go => light_sync.go} (88%)
diff --git a/beacon/light/canonical.go b/beacon/light/canonical.go
new file mode 100644
index 000000000000..b5371493b4c9
--- /dev/null
+++ b/beacon/light/canonical.go
@@ -0,0 +1,125 @@
+// Copyright 2023 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 .
+
+package light
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common/lru"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// canonicalStore stores instances of the given type in a database and caches
+// them in memory, associated with a continuous range of period numbers.
+// Note: canonicalStore is not thread safe and it is the caller's responsibility
+// to avoid concurrent access.
+type canonicalStore[T any] struct {
+ keyPrefix []byte
+ periods periodRange
+ cache *lru.Cache[uint64, T]
+}
+
+// newCanonicalStore creates a new canonicalStore and loads all keys associated
+// with the keyPrefix in order to determine the ranges available in the database.
+func newCanonicalStore[T any](db ethdb.Iteratee, keyPrefix []byte) (*canonicalStore[T], error) {
+ cs := &canonicalStore[T]{
+ keyPrefix: keyPrefix,
+ cache: lru.NewCache[uint64, T](100),
+ }
+ var (
+ iter = db.NewIterator(keyPrefix, nil)
+ kl = len(keyPrefix)
+ first = true
+ )
+ defer iter.Release()
+
+ for iter.Next() {
+ if len(iter.Key()) != kl+8 {
+ log.Warn("Invalid key length in the canonical chain database", "key", fmt.Sprintf("%#x", iter.Key()))
+ continue
+ }
+ period := binary.BigEndian.Uint64(iter.Key()[kl : kl+8])
+ if first {
+ cs.periods.Start = period
+ } else if cs.periods.End != period {
+ return nil, fmt.Errorf("gap in the canonical chain database between periods %d and %d", cs.periods.End, period-1)
+ }
+ first = false
+ cs.periods.End = period + 1
+ }
+ return cs, nil
+}
+
+// databaseKey returns the database key belonging to the given period.
+func (cs *canonicalStore[T]) databaseKey(period uint64) []byte {
+ return binary.BigEndian.AppendUint64(append([]byte{}, cs.keyPrefix...), period)
+}
+
+// add adds the given item to the database. It also ensures that the range remains
+// continuous. Can be used either with a batch or database backend.
+func (cs *canonicalStore[T]) add(backend ethdb.KeyValueWriter, period uint64, value T) error {
+ if !cs.periods.canExpand(period) {
+ return fmt.Errorf("period expansion is not allowed, first: %d, next: %d, period: %d", cs.periods.Start, cs.periods.End, period)
+ }
+ enc, err := rlp.EncodeToBytes(value)
+ if err != nil {
+ return err
+ }
+ if err := backend.Put(cs.databaseKey(period), enc); err != nil {
+ return err
+ }
+ cs.cache.Add(period, value)
+ cs.periods.expand(period)
+ return nil
+}
+
+// deleteFrom removes items starting from the given period.
+func (cs *canonicalStore[T]) deleteFrom(db ethdb.KeyValueWriter, fromPeriod uint64) (deleted periodRange) {
+ keepRange, deleteRange := cs.periods.split(fromPeriod)
+ deleteRange.each(func(period uint64) {
+ db.Delete(cs.databaseKey(period))
+ cs.cache.Remove(period)
+ })
+ cs.periods = keepRange
+ return deleteRange
+}
+
+// get returns the item at the given period or the null value of the given type
+// if no item is present.
+func (cs *canonicalStore[T]) get(backend ethdb.KeyValueReader, period uint64) (T, bool) {
+ var null, value T
+ if !cs.periods.contains(period) {
+ return null, false
+ }
+ if value, ok := cs.cache.Get(period); ok {
+ return value, true
+ }
+ enc, err := backend.Get(cs.databaseKey(period))
+ if err != nil {
+ log.Error("Canonical store value not found", "period", period, "start", cs.periods.Start, "end", cs.periods.End)
+ return null, false
+ }
+ if err := rlp.DecodeBytes(enc, &value); err != nil {
+ log.Error("Error decoding canonical store value", "error", err)
+ return null, false
+ }
+ cs.cache.Add(period, value)
+ return value, true
+}
diff --git a/beacon/light/committee_chain.go b/beacon/light/committee_chain.go
new file mode 100644
index 000000000000..d707f8cc34da
--- /dev/null
+++ b/beacon/light/committee_chain.go
@@ -0,0 +1,514 @@
+// Copyright 2023 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 .
+
+package light
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/beacon/params"
+ "github.com/ethereum/go-ethereum/beacon/types"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/lru"
+ "github.com/ethereum/go-ethereum/common/mclock"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+var (
+ ErrNeedCommittee = errors.New("sync committee required")
+ ErrInvalidUpdate = errors.New("invalid committee update")
+ ErrInvalidPeriod = errors.New("invalid update period")
+ ErrWrongCommitteeRoot = errors.New("wrong committee root")
+ ErrCannotReorg = errors.New("can not reorg committee chain")
+)
+
+// CommitteeChain is a passive data structure that can validate, hold and update
+// a chain of beacon light sync committees and updates. It requires at least one
+// externally set fixed committee root at the beginning of the chain which can
+// be set either based on a BootstrapData or a trusted source (a local beacon
+// full node). This makes the structure useful for both light client and light
+// server setups.
+//
+// It always maintains the following consistency constraints:
+// - a committee can only be present if its root hash matches an existing fixed
+// root or if it is proven by an update at the previous period
+// - an update can only be present if a committee is present at the same period
+// and the update signature is valid and has enough participants.
+// The committee at the next period (proven by the update) should also be
+// present (note that this means they can only be added together if neither
+// is present yet). If a fixed root is present at the next period then the
+// update can only be present if it proves the same committee root.
+//
+// Once synced to the current sync period, CommitteeChain can also validate
+// signed beacon headers.
+type CommitteeChain struct {
+ // chainmu guards against concurrent access to the canonicalStore structures
+ // (updates, committees, fixedCommitteeRoots) and ensures that they stay consistent
+ // with each other and with committeeCache.
+ chainmu sync.RWMutex
+ db ethdb.KeyValueStore
+ updates *canonicalStore[*types.LightClientUpdate]
+ committees *canonicalStore[*types.SerializedSyncCommittee]
+ fixedCommitteeRoots *canonicalStore[common.Hash]
+ committeeCache *lru.Cache[uint64, syncCommittee] // cache deserialized committees
+
+ clock mclock.Clock // monotonic clock (simulated clock in tests)
+ unixNano func() int64 // system clock (simulated clock in tests)
+ sigVerifier committeeSigVerifier // BLS sig verifier (dummy verifier in tests)
+
+ config *types.ChainConfig
+ signerThreshold int
+ minimumUpdateScore types.UpdateScore
+ enforceTime bool // enforceTime specifies whether the age of a signed header should be checked
+}
+
+// NewCommitteeChain creates a new CommitteeChain.
+func NewCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool) *CommitteeChain {
+ return newCommitteeChain(db, config, signerThreshold, enforceTime, blsVerifier{}, &mclock.System{}, func() int64 { return time.Now().UnixNano() })
+}
+
+// newCommitteeChain creates a new CommitteeChain with the option of replacing the
+// clock source and signature verification for testing purposes.
+func newCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool, sigVerifier committeeSigVerifier, clock mclock.Clock, unixNano func() int64) *CommitteeChain {
+ s := &CommitteeChain{
+ committeeCache: lru.NewCache[uint64, syncCommittee](10),
+ db: db,
+ sigVerifier: sigVerifier,
+ clock: clock,
+ unixNano: unixNano,
+ config: config,
+ signerThreshold: signerThreshold,
+ enforceTime: enforceTime,
+ minimumUpdateScore: types.UpdateScore{
+ SignerCount: uint32(signerThreshold),
+ SubPeriodIndex: params.SyncPeriodLength / 16,
+ },
+ }
+
+ var err1, err2, err3 error
+ if s.fixedCommitteeRoots, err1 = newCanonicalStore[common.Hash](db, rawdb.FixedCommitteeRootKey); err1 != nil {
+ log.Error("Error creating fixed committee root store", "error", err1)
+ }
+ if s.committees, err2 = newCanonicalStore[*types.SerializedSyncCommittee](db, rawdb.SyncCommitteeKey); err2 != nil {
+ log.Error("Error creating committee store", "error", err2)
+ }
+ if s.updates, err3 = newCanonicalStore[*types.LightClientUpdate](db, rawdb.BestUpdateKey); err3 != nil {
+ log.Error("Error creating update store", "error", err3)
+ }
+ if err1 != nil || err2 != nil || err3 != nil || !s.checkConstraints() {
+ log.Info("Resetting invalid committee chain")
+ s.Reset()
+ }
+ // roll back invalid updates (might be necessary if forks have been changed since last time)
+ for !s.updates.periods.isEmpty() {
+ update, ok := s.updates.get(s.db, s.updates.periods.End-1)
+ if !ok {
+ log.Error("Sync committee update missing", "period", s.updates.periods.End-1)
+ s.Reset()
+ break
+ }
+ if valid, err := s.verifyUpdate(update); err != nil {
+ log.Error("Error validating update", "period", s.updates.periods.End-1, "error", err)
+ } else if valid {
+ break
+ }
+ if err := s.rollback(s.updates.periods.End); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ }
+ }
+ if !s.committees.periods.isEmpty() {
+ log.Trace("Sync committee chain loaded", "first period", s.committees.periods.Start, "last period", s.committees.periods.End-1)
+ }
+ return s
+}
+
+// checkConstraints checks committee chain validity constraints
+func (s *CommitteeChain) checkConstraints() bool {
+ isNotInFixedCommitteeRootRange := func(r periodRange) bool {
+ return s.fixedCommitteeRoots.periods.isEmpty() ||
+ r.Start < s.fixedCommitteeRoots.periods.Start ||
+ r.Start >= s.fixedCommitteeRoots.periods.End
+ }
+
+ valid := true
+ if !s.updates.periods.isEmpty() {
+ if isNotInFixedCommitteeRootRange(s.updates.periods) {
+ log.Error("Start update is not in the fixed roots range")
+ valid = false
+ }
+ if s.committees.periods.Start > s.updates.periods.Start || s.committees.periods.End <= s.updates.periods.End {
+ log.Error("Missing committees in update range")
+ valid = false
+ }
+ }
+ if !s.committees.periods.isEmpty() {
+ if isNotInFixedCommitteeRootRange(s.committees.periods) {
+ log.Error("Start committee is not in the fixed roots range")
+ valid = false
+ }
+ if s.committees.periods.End > s.fixedCommitteeRoots.periods.End && s.committees.periods.End > s.updates.periods.End+1 {
+ log.Error("Last committee is neither in the fixed roots range nor proven by updates")
+ valid = false
+ }
+ }
+ return valid
+}
+
+// Reset resets the committee chain.
+func (s *CommitteeChain) Reset() {
+ s.chainmu.Lock()
+ defer s.chainmu.Unlock()
+
+ if err := s.rollback(0); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ }
+}
+
+// CheckpointInit initializes a CommitteeChain based on the checkpoint.
+// Note: if the chain is already initialized and the committees proven by the
+// checkpoint do match the existing chain then the chain is retained and the
+// new checkpoint becomes fixed.
+func (s *CommitteeChain) CheckpointInit(bootstrap *types.BootstrapData) error {
+ s.chainmu.Lock()
+ defer s.chainmu.Unlock()
+
+ if err := bootstrap.Validate(); err != nil {
+ return err
+ }
+
+ period := bootstrap.Header.SyncPeriod()
+ if err := s.deleteFixedCommitteeRootsFrom(period + 2); err != nil {
+ s.Reset()
+ return err
+ }
+ if s.addFixedCommitteeRoot(period, bootstrap.CommitteeRoot) != nil {
+ s.Reset()
+ if err := s.addFixedCommitteeRoot(period, bootstrap.CommitteeRoot); err != nil {
+ s.Reset()
+ return err
+ }
+ }
+ if err := s.addFixedCommitteeRoot(period+1, common.Hash(bootstrap.CommitteeBranch[0])); err != nil {
+ s.Reset()
+ return err
+ }
+ if err := s.addCommittee(period, bootstrap.Committee); err != nil {
+ s.Reset()
+ return err
+ }
+ return nil
+}
+
+// addFixedCommitteeRoot sets a fixed committee root at the given period.
+// Note that the period where the first committee is added has to have a fixed
+// root which can either come from a BootstrapData or a trusted source.
+func (s *CommitteeChain) addFixedCommitteeRoot(period uint64, root common.Hash) error {
+ if root == (common.Hash{}) {
+ return ErrWrongCommitteeRoot
+ }
+
+ batch := s.db.NewBatch()
+ oldRoot := s.getCommitteeRoot(period)
+ if !s.fixedCommitteeRoots.periods.canExpand(period) {
+ // Note: the fixed committee root range should always be continuous and
+ // therefore the expected syncing method is to forward sync and optionally
+ // backward sync periods one by one, starting from a checkpoint. The only
+ // case when a root that is not adjacent to the already fixed ones can be
+ // fixed is when the same root has already been proven by an update chain.
+ // In this case the all roots in between can and should be fixed.
+ // This scenario makes sense when a new trusted checkpoint is added to an
+ // existing chain, ensuring that it will not be rolled back (might be
+ // important in case of low signer participation rate).
+ if root != oldRoot {
+ return ErrInvalidPeriod
+ }
+ // if the old root exists and matches the new one then it is guaranteed
+ // that the given period is after the existing fixed range and the roots
+ // in between can also be fixed.
+ for p := s.fixedCommitteeRoots.periods.End; p < period; p++ {
+ if err := s.fixedCommitteeRoots.add(batch, p, s.getCommitteeRoot(p)); err != nil {
+ return err
+ }
+ }
+ }
+ if oldRoot != (common.Hash{}) && (oldRoot != root) {
+ // existing old root was different, we have to reorg the chain
+ if err := s.rollback(period); err != nil {
+ return err
+ }
+ }
+ if err := s.fixedCommitteeRoots.add(batch, period, root); err != nil {
+ return err
+ }
+ if err := batch.Write(); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ return err
+ }
+ return nil
+}
+
+// deleteFixedCommitteeRootsFrom deletes fixed roots starting from the given period.
+// It also maintains chain consistency, meaning that it also deletes updates and
+// committees if they are no longer supported by a valid update chain.
+func (s *CommitteeChain) deleteFixedCommitteeRootsFrom(period uint64) error {
+ if period >= s.fixedCommitteeRoots.periods.End {
+ return nil
+ }
+ batch := s.db.NewBatch()
+ s.fixedCommitteeRoots.deleteFrom(batch, period)
+ if s.updates.periods.isEmpty() || period <= s.updates.periods.Start {
+ // Note: the first period of the update chain should always be fixed so if
+ // the fixed root at the first update is removed then the entire update chain
+ // and the proven committees have to be removed. Earlier committees in the
+ // remaining fixed root range can stay.
+ s.updates.deleteFrom(batch, period)
+ s.deleteCommitteesFrom(batch, period)
+ } else {
+ // The update chain stays intact, some previously fixed committee roots might
+ // get unfixed but are still proven by the update chain. If there were
+ // committees present after the range proven by updates, those should be
+ // removed if the belonging fixed roots are also removed.
+ fromPeriod := s.updates.periods.End + 1 // not proven by updates
+ if period > fromPeriod {
+ fromPeriod = period // also not justified by fixed roots
+ }
+ s.deleteCommitteesFrom(batch, fromPeriod)
+ }
+ if err := batch.Write(); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ return err
+ }
+ return nil
+}
+
+// deleteCommitteesFrom deletes committees starting from the given period.
+func (s *CommitteeChain) deleteCommitteesFrom(batch ethdb.Batch, period uint64) {
+ deleted := s.committees.deleteFrom(batch, period)
+ for period := deleted.Start; period < deleted.End; period++ {
+ s.committeeCache.Remove(period)
+ }
+}
+
+// addCommittee adds a committee at the given period if possible.
+func (s *CommitteeChain) addCommittee(period uint64, committee *types.SerializedSyncCommittee) error {
+ if !s.committees.periods.canExpand(period) {
+ return ErrInvalidPeriod
+ }
+ root := s.getCommitteeRoot(period)
+ if root == (common.Hash{}) {
+ return ErrInvalidPeriod
+ }
+ if root != committee.Root() {
+ return ErrWrongCommitteeRoot
+ }
+ if !s.committees.periods.contains(period) {
+ if err := s.committees.add(s.db, period, committee); err != nil {
+ return err
+ }
+ s.committeeCache.Remove(period)
+ }
+ return nil
+}
+
+// InsertUpdate adds a new update if possible.
+func (s *CommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error {
+ s.chainmu.Lock()
+ defer s.chainmu.Unlock()
+
+ period := update.AttestedHeader.Header.SyncPeriod()
+ if !s.updates.periods.canExpand(period) || !s.committees.periods.contains(period) {
+ return ErrInvalidPeriod
+ }
+ if s.minimumUpdateScore.BetterThan(update.Score()) {
+ return ErrInvalidUpdate
+ }
+ oldRoot := s.getCommitteeRoot(period + 1)
+ reorg := oldRoot != (common.Hash{}) && oldRoot != update.NextSyncCommitteeRoot
+ if oldUpdate, ok := s.updates.get(s.db, period); ok && !update.Score().BetterThan(oldUpdate.Score()) {
+ // a better or equal update already exists; no changes, only fail if new one tried to reorg
+ if reorg {
+ return ErrCannotReorg
+ }
+ return nil
+ }
+ if s.fixedCommitteeRoots.periods.contains(period+1) && reorg {
+ return ErrCannotReorg
+ }
+ if ok, err := s.verifyUpdate(update); err != nil {
+ return err
+ } else if !ok {
+ return ErrInvalidUpdate
+ }
+ addCommittee := !s.committees.periods.contains(period+1) || reorg
+ if addCommittee {
+ if nextCommittee == nil {
+ return ErrNeedCommittee
+ }
+ if nextCommittee.Root() != update.NextSyncCommitteeRoot {
+ return ErrWrongCommitteeRoot
+ }
+ }
+ if reorg {
+ if err := s.rollback(period + 1); err != nil {
+ return err
+ }
+ }
+ batch := s.db.NewBatch()
+ if addCommittee {
+ if err := s.committees.add(batch, period+1, nextCommittee); err != nil {
+ return err
+ }
+ s.committeeCache.Remove(period + 1)
+ }
+ if err := s.updates.add(batch, period, update); err != nil {
+ return err
+ }
+ if err := batch.Write(); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ return err
+ }
+ log.Info("Inserted new committee update", "period", period, "next committee root", update.NextSyncCommitteeRoot)
+ return nil
+}
+
+// NextSyncPeriod returns the next period where an update can be added and also
+// whether the chain is initialized at all.
+func (s *CommitteeChain) NextSyncPeriod() (uint64, bool) {
+ s.chainmu.RLock()
+ defer s.chainmu.RUnlock()
+
+ if s.committees.periods.isEmpty() {
+ return 0, false
+ }
+ if !s.updates.periods.isEmpty() {
+ return s.updates.periods.End, true
+ }
+ return s.committees.periods.End - 1, true
+}
+
+// rollback removes all committees and fixed roots from the given period and updates
+// starting from the previous period.
+func (s *CommitteeChain) rollback(period uint64) error {
+ max := s.updates.periods.End + 1
+ if s.committees.periods.End > max {
+ max = s.committees.periods.End
+ }
+ if s.fixedCommitteeRoots.periods.End > max {
+ max = s.fixedCommitteeRoots.periods.End
+ }
+ for max > period {
+ max--
+ batch := s.db.NewBatch()
+ s.deleteCommitteesFrom(batch, max)
+ s.fixedCommitteeRoots.deleteFrom(batch, max)
+ if max > 0 {
+ s.updates.deleteFrom(batch, max-1)
+ }
+ if err := batch.Write(); err != nil {
+ log.Error("Error writing batch into chain database", "error", err)
+ return err
+ }
+ }
+ return nil
+}
+
+// getCommitteeRoot returns the committee root at the given period, either fixed,
+// proven by a previous update or both. It returns an empty hash if the committee
+// root is unknown.
+func (s *CommitteeChain) getCommitteeRoot(period uint64) common.Hash {
+ if root, ok := s.fixedCommitteeRoots.get(s.db, period); ok || period == 0 {
+ return root
+ }
+ if update, ok := s.updates.get(s.db, period-1); ok {
+ return update.NextSyncCommitteeRoot
+ }
+ return common.Hash{}
+}
+
+// getSyncCommittee returns the deserialized sync committee at the given period.
+func (s *CommitteeChain) getSyncCommittee(period uint64) (syncCommittee, error) {
+ if c, ok := s.committeeCache.Get(period); ok {
+ return c, nil
+ }
+ if sc, ok := s.committees.get(s.db, period); ok {
+ c, err := s.sigVerifier.deserializeSyncCommittee(sc)
+ if err != nil {
+ return nil, fmt.Errorf("Sync committee #%d deserialization error: %v", period, err)
+ }
+ s.committeeCache.Add(period, c)
+ return c, nil
+ }
+ return nil, fmt.Errorf("Missing serialized sync committee #%d", period)
+}
+
+// VerifySignedHeader returns true if the given signed header has a valid signature
+// according to the local committee chain. The caller should ensure that the
+// committees advertised by the same source where the signed header came from are
+// synced before verifying the signature.
+// The age of the header is also returned (the time elapsed since the beginning
+// of the given slot, according to the local system clock). If enforceTime is
+// true then negative age (future) headers are rejected.
+func (s *CommitteeChain) VerifySignedHeader(head types.SignedHeader) (bool, time.Duration, error) {
+ s.chainmu.RLock()
+ defer s.chainmu.RUnlock()
+
+ return s.verifySignedHeader(head)
+}
+
+func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time.Duration, error) {
+ var age time.Duration
+ now := s.unixNano()
+ if head.Header.Slot < (uint64(now-math.MinInt64)/uint64(time.Second)-s.config.GenesisTime)/12 {
+ age = time.Duration(now - int64(time.Second)*int64(s.config.GenesisTime+head.Header.Slot*12))
+ } else {
+ age = time.Duration(math.MinInt64)
+ }
+ if s.enforceTime && age < 0 {
+ return false, age, nil
+ }
+ committee, err := s.getSyncCommittee(types.SyncPeriod(head.SignatureSlot))
+ if err != nil {
+ return false, 0, err
+ }
+ if committee == nil {
+ return false, age, nil
+ }
+ if signingRoot, err := s.config.Forks.SigningRoot(head.Header); err == nil {
+ return s.sigVerifier.verifySignature(committee, signingRoot, &head.Signature), age, nil
+ }
+ return false, age, nil
+}
+
+// verifyUpdate checks whether the header signature is correct and the update
+// fits into the specified constraints (assumes that the update has been
+// successfully validated previously)
+func (s *CommitteeChain) verifyUpdate(update *types.LightClientUpdate) (bool, error) {
+ // Note: SignatureSlot determines the sync period of the committee used for signature
+ // verification. Though in reality SignatureSlot is always bigger than update.Header.Slot,
+ // setting them as equal here enforces the rule that they have to be in the same sync
+ // period in order for the light client update proof to be meaningful.
+ ok, age, err := s.verifySignedHeader(update.AttestedHeader)
+ if age < 0 {
+ log.Warn("Future committee update received", "age", age)
+ }
+ return ok, err
+}
diff --git a/beacon/light/committee_chain_test.go b/beacon/light/committee_chain_test.go
new file mode 100644
index 000000000000..60ea2a0efdbf
--- /dev/null
+++ b/beacon/light/committee_chain_test.go
@@ -0,0 +1,356 @@
+// Copyright 2022 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package light
+
+import (
+ "crypto/rand"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/beacon/params"
+ "github.com/ethereum/go-ethereum/beacon/types"
+ "github.com/ethereum/go-ethereum/common/mclock"
+ "github.com/ethereum/go-ethereum/ethdb/memorydb"
+)
+
+var (
+ testGenesis = newTestGenesis()
+ testGenesis2 = newTestGenesis()
+
+ tfBase = newTestForks(testGenesis, types.Forks{
+ &types.Fork{Epoch: 0, Version: []byte{0}},
+ })
+ tfAlternative = newTestForks(testGenesis, types.Forks{
+ &types.Fork{Epoch: 0, Version: []byte{0}},
+ &types.Fork{Epoch: 0x700, Version: []byte{1}},
+ })
+ tfAnotherGenesis = newTestForks(testGenesis2, types.Forks{
+ &types.Fork{Epoch: 0, Version: []byte{0}},
+ })
+
+ tcBase = newTestCommitteeChain(nil, tfBase, true, 0, 10, 400, false)
+ tcBaseWithInvalidUpdates = newTestCommitteeChain(tcBase, tfBase, false, 5, 10, 200, false) // signer count too low
+ tcBaseWithBetterUpdates = newTestCommitteeChain(tcBase, tfBase, false, 5, 10, 440, false)
+ tcReorgWithWorseUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 400, false)
+ tcReorgWithWorseUpdates2 = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 380, false)
+ tcReorgWithBetterUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 420, false)
+ tcReorgWithFinalizedUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 400, true)
+ tcFork = newTestCommitteeChain(tcBase, tfAlternative, true, 7, 10, 400, false)
+ tcAnotherGenesis = newTestCommitteeChain(nil, tfAnotherGenesis, true, 0, 10, 400, false)
+)
+
+func TestCommitteeChainFixedCommitteeRoots(t *testing.T) {
+ for _, reload := range []bool{false, true} {
+ c := newCommitteeChainTest(t, tfBase, 300, true)
+ c.setClockPeriod(7)
+ c.addFixedCommitteeRoot(tcBase, 4, nil)
+ c.addFixedCommitteeRoot(tcBase, 5, nil)
+ c.addFixedCommitteeRoot(tcBase, 6, nil)
+ c.addFixedCommitteeRoot(tcBase, 8, ErrInvalidPeriod) // range has to be continuous
+ c.addFixedCommitteeRoot(tcBase, 3, nil)
+ c.addFixedCommitteeRoot(tcBase, 2, nil)
+ if reload {
+ c.reloadChain()
+ }
+ c.addCommittee(tcBase, 4, nil)
+ c.addCommittee(tcBase, 6, ErrInvalidPeriod) // range has to be continuous
+ c.addCommittee(tcBase, 5, nil)
+ c.addCommittee(tcBase, 6, nil)
+ c.addCommittee(tcAnotherGenesis, 3, ErrWrongCommitteeRoot)
+ c.addCommittee(tcBase, 3, nil)
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcBase, 3, 6)
+ }
+}
+
+func TestCommitteeChainCheckpointSync(t *testing.T) {
+ for _, enforceTime := range []bool{false, true} {
+ for _, reload := range []bool{false, true} {
+ c := newCommitteeChainTest(t, tfBase, 300, enforceTime)
+ if enforceTime {
+ c.setClockPeriod(6)
+ }
+ c.insertUpdate(tcBase, 3, true, ErrInvalidPeriod)
+ c.addFixedCommitteeRoot(tcBase, 3, nil)
+ c.addFixedCommitteeRoot(tcBase, 4, nil)
+ c.insertUpdate(tcBase, 4, true, ErrInvalidPeriod) // still no committee
+ c.addCommittee(tcBase, 3, nil)
+ c.addCommittee(tcBase, 4, nil)
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcBase, 3, 4)
+ c.insertUpdate(tcBase, 3, false, nil) // update can be added without committee here
+ c.insertUpdate(tcBase, 4, false, ErrNeedCommittee) // but not here as committee 5 is not there yet
+ c.insertUpdate(tcBase, 4, true, nil)
+ c.verifyRange(tcBase, 3, 5)
+ c.insertUpdate(tcBaseWithInvalidUpdates, 5, true, ErrInvalidUpdate) // signer count too low
+ c.insertUpdate(tcBase, 5, true, nil)
+ if reload {
+ c.reloadChain()
+ }
+ if enforceTime {
+ c.insertUpdate(tcBase, 6, true, ErrInvalidUpdate) // future update rejected
+ c.setClockPeriod(7)
+ }
+ c.insertUpdate(tcBase, 6, true, nil) // when the time comes it's accepted
+ if reload {
+ c.reloadChain()
+ }
+ if enforceTime {
+ c.verifyRange(tcBase, 3, 6) // committee 7 is there but still in the future
+ c.setClockPeriod(8)
+ }
+ c.verifyRange(tcBase, 3, 7) // now period 7 can also be verified
+ // try reverse syncing an update
+ c.insertUpdate(tcBase, 2, false, ErrInvalidPeriod) // fixed committee is needed first
+ c.addFixedCommitteeRoot(tcBase, 2, nil)
+ c.addCommittee(tcBase, 2, nil)
+ c.insertUpdate(tcBase, 2, false, nil)
+ c.verifyRange(tcBase, 2, 7)
+ }
+ }
+}
+
+func TestCommitteeChainReorg(t *testing.T) {
+ for _, reload := range []bool{false, true} {
+ for _, addBetterUpdates := range []bool{false, true} {
+ c := newCommitteeChainTest(t, tfBase, 300, true)
+ c.setClockPeriod(11)
+ c.addFixedCommitteeRoot(tcBase, 3, nil)
+ c.addFixedCommitteeRoot(tcBase, 4, nil)
+ c.addCommittee(tcBase, 3, nil)
+ for period := uint64(3); period < 10; period++ {
+ c.insertUpdate(tcBase, period, true, nil)
+ }
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcBase, 3, 10)
+ c.insertUpdate(tcReorgWithWorseUpdates, 5, true, ErrCannotReorg)
+ c.insertUpdate(tcReorgWithWorseUpdates2, 5, true, ErrCannotReorg)
+ if addBetterUpdates {
+ // add better updates for the base chain and expect first reorg to fail
+ // (only add updates as committees should be the same)
+ for period := uint64(5); period < 10; period++ {
+ c.insertUpdate(tcBaseWithBetterUpdates, period, false, nil)
+ }
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcBase, 3, 10) // still on the same chain
+ c.insertUpdate(tcReorgWithBetterUpdates, 5, true, ErrCannotReorg)
+ } else {
+ // reorg with better updates
+ c.insertUpdate(tcReorgWithBetterUpdates, 5, false, ErrNeedCommittee)
+ c.verifyRange(tcBase, 3, 10) // no success yet, still on the base chain
+ c.verifyRange(tcReorgWithBetterUpdates, 3, 5)
+ c.insertUpdate(tcReorgWithBetterUpdates, 5, true, nil)
+ // successful reorg, base chain should only match before the reorg period
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcBase, 3, 5)
+ c.verifyRange(tcReorgWithBetterUpdates, 3, 6)
+ for period := uint64(6); period < 10; period++ {
+ c.insertUpdate(tcReorgWithBetterUpdates, period, true, nil)
+ }
+ c.verifyRange(tcReorgWithBetterUpdates, 3, 10)
+ }
+ // reorg with finalized updates; should succeed even if base chain updates
+ // have been improved because a finalized update beats everything else
+ c.insertUpdate(tcReorgWithFinalizedUpdates, 5, false, ErrNeedCommittee)
+ c.insertUpdate(tcReorgWithFinalizedUpdates, 5, true, nil)
+ if reload {
+ c.reloadChain()
+ }
+ c.verifyRange(tcReorgWithFinalizedUpdates, 3, 6)
+ for period := uint64(6); period < 10; period++ {
+ c.insertUpdate(tcReorgWithFinalizedUpdates, period, true, nil)
+ }
+ c.verifyRange(tcReorgWithFinalizedUpdates, 3, 10)
+ }
+ }
+}
+
+func TestCommitteeChainFork(t *testing.T) {
+ c := newCommitteeChainTest(t, tfAlternative, 300, true)
+ c.setClockPeriod(11)
+ // trying to sync a chain on an alternative fork with the base chain data
+ c.addFixedCommitteeRoot(tcBase, 0, nil)
+ c.addFixedCommitteeRoot(tcBase, 1, nil)
+ c.addCommittee(tcBase, 0, nil)
+ // shared section should sync without errors
+ for period := uint64(0); period < 7; period++ {
+ c.insertUpdate(tcBase, period, true, nil)
+ }
+ c.insertUpdate(tcBase, 7, true, ErrInvalidUpdate) // wrong fork
+ // committee root #7 is still the same but signatures are already signed with
+ // a different fork id so period 7 should only verify on the alternative fork
+ c.verifyRange(tcBase, 0, 6)
+ c.verifyRange(tcFork, 0, 7)
+ for period := uint64(7); period < 10; period++ {
+ c.insertUpdate(tcFork, period, true, nil)
+ }
+ c.verifyRange(tcFork, 0, 10)
+ // reload the chain while switching to the base fork
+ c.config = tfBase
+ c.reloadChain()
+ // updates 7..9 should be rolled back now
+ c.verifyRange(tcFork, 0, 6) // again, period 7 only verifies on the right fork
+ c.verifyRange(tcBase, 0, 7)
+ c.insertUpdate(tcFork, 7, true, ErrInvalidUpdate) // wrong fork
+ for period := uint64(7); period < 10; period++ {
+ c.insertUpdate(tcBase, period, true, nil)
+ }
+ c.verifyRange(tcBase, 0, 10)
+}
+
+type committeeChainTest struct {
+ t *testing.T
+ db *memorydb.Database
+ clock *mclock.Simulated
+ config types.ChainConfig
+ signerThreshold int
+ enforceTime bool
+ chain *CommitteeChain
+}
+
+func newCommitteeChainTest(t *testing.T, config types.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest {
+ c := &committeeChainTest{
+ t: t,
+ db: memorydb.New(),
+ clock: &mclock.Simulated{},
+ config: config,
+ signerThreshold: signerThreshold,
+ enforceTime: enforceTime,
+ }
+ c.chain = newCommitteeChain(c.db, &config, signerThreshold, enforceTime, dummyVerifier{}, c.clock, func() int64 { return int64(c.clock.Now()) })
+ return c
+}
+
+func (c *committeeChainTest) reloadChain() {
+ c.chain = newCommitteeChain(c.db, &c.config, c.signerThreshold, c.enforceTime, dummyVerifier{}, c.clock, func() int64 { return int64(c.clock.Now()) })
+}
+
+func (c *committeeChainTest) setClockPeriod(period float64) {
+ target := mclock.AbsTime(period * float64(time.Second*12*params.SyncPeriodLength))
+ wait := time.Duration(target - c.clock.Now())
+ if wait < 0 {
+ c.t.Fatalf("Invalid setClockPeriod")
+ }
+ c.clock.Run(wait)
+}
+
+func (c *committeeChainTest) addFixedCommitteeRoot(tc *testCommitteeChain, period uint64, expErr error) {
+ if err := c.chain.addFixedCommitteeRoot(period, tc.periods[period].committee.Root()); err != expErr {
+ c.t.Errorf("Incorrect error output from addFixedCommitteeRoot at period %d (expected %v, got %v)", period, expErr, err)
+ }
+}
+
+func (c *committeeChainTest) addCommittee(tc *testCommitteeChain, period uint64, expErr error) {
+ if err := c.chain.addCommittee(period, tc.periods[period].committee); err != expErr {
+ c.t.Errorf("Incorrect error output from addCommittee at period %d (expected %v, got %v)", period, expErr, err)
+ }
+}
+
+func (c *committeeChainTest) insertUpdate(tc *testCommitteeChain, period uint64, addCommittee bool, expErr error) {
+ var committee *types.SerializedSyncCommittee
+ if addCommittee {
+ committee = tc.periods[period+1].committee
+ }
+ if err := c.chain.InsertUpdate(tc.periods[period].update, committee); err != expErr {
+ c.t.Errorf("Incorrect error output from InsertUpdate at period %d (expected %v, got %v)", period, expErr, err)
+ }
+}
+
+func (c *committeeChainTest) verifySignedHeader(tc *testCommitteeChain, period float64, expOk bool) {
+ slot := uint64(period * float64(params.SyncPeriodLength))
+ signedHead := GenerateTestSignedHeader(types.Header{Slot: slot}, &tc.config, tc.periods[types.SyncPeriod(slot)].committee, slot+1, 400)
+ if ok, _, _ := c.chain.VerifySignedHeader(signedHead); ok != expOk {
+ c.t.Errorf("Incorrect output from VerifySignedHeader at period %f (expected %v, got %v)", period, expOk, ok)
+ }
+}
+
+func (c *committeeChainTest) verifyRange(tc *testCommitteeChain, begin, end uint64) {
+ if begin > 0 {
+ c.verifySignedHeader(tc, float64(begin)-0.5, false)
+ }
+ for period := begin; period <= end; period++ {
+ c.verifySignedHeader(tc, float64(period)+0.5, true)
+ }
+ c.verifySignedHeader(tc, float64(end)+1.5, false)
+}
+
+func newTestGenesis() types.ChainConfig {
+ var config types.ChainConfig
+ rand.Read(config.GenesisValidatorsRoot[:])
+ return config
+}
+
+func newTestForks(config types.ChainConfig, forks types.Forks) types.ChainConfig {
+ for _, fork := range forks {
+ config.AddFork(fork.Name, fork.Epoch, fork.Version)
+ }
+ return config
+}
+
+func newTestCommitteeChain(parent *testCommitteeChain, config types.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain {
+ tc := &testCommitteeChain{
+ config: config,
+ }
+ if parent != nil {
+ tc.periods = make([]testPeriod, len(parent.periods))
+ copy(tc.periods, parent.periods)
+ }
+ if newCommittees {
+ if begin == 0 {
+ tc.fillCommittees(begin, end+1)
+ } else {
+ tc.fillCommittees(begin+1, end+1)
+ }
+ }
+ tc.fillUpdates(begin, end, signerCount, finalizedHeader)
+ return tc
+}
+
+type testPeriod struct {
+ committee *types.SerializedSyncCommittee
+ update *types.LightClientUpdate
+}
+
+type testCommitteeChain struct {
+ periods []testPeriod
+ config types.ChainConfig
+}
+
+func (tc *testCommitteeChain) fillCommittees(begin, end int) {
+ if len(tc.periods) <= end {
+ tc.periods = append(tc.periods, make([]testPeriod, end+1-len(tc.periods))...)
+ }
+ for i := begin; i <= end; i++ {
+ tc.periods[i].committee = GenerateTestCommittee()
+ }
+}
+
+func (tc *testCommitteeChain) fillUpdates(begin, end int, signerCount int, finalizedHeader bool) {
+ for i := begin; i <= end; i++ {
+ tc.periods[i].update = GenerateTestUpdate(&tc.config, uint64(i), tc.periods[i].committee, tc.periods[i+1].committee, signerCount, finalizedHeader)
+ }
+}
diff --git a/beacon/light/range.go b/beacon/light/range.go
new file mode 100644
index 000000000000..76ebe2381ae9
--- /dev/null
+++ b/beacon/light/range.go
@@ -0,0 +1,78 @@
+// Copyright 2023 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 .
+
+package light
+
+// periodRange represents a (possibly zero-length) range of integers (sync periods).
+type periodRange struct {
+ Start, End uint64
+}
+
+// isEmpty returns true if the length of the range is zero.
+func (a periodRange) isEmpty() bool {
+ return a.End == a.Start
+}
+
+// contains returns true if the range includes the given period.
+func (a periodRange) contains(period uint64) bool {
+ return period >= a.Start && period < a.End
+}
+
+// canExpand returns true if the range includes or can be expanded with the given
+// period (either the range is empty or the given period is inside, right before or
+// right after the range).
+func (a periodRange) canExpand(period uint64) bool {
+ return a.isEmpty() || (period+1 >= a.Start && period <= a.End)
+}
+
+// expand expands the range with the given period.
+// This method assumes that canExpand returned true: otherwise this is a no-op.
+func (a *periodRange) expand(period uint64) {
+ if a.isEmpty() {
+ a.Start, a.End = period, period+1
+ return
+ }
+ if a.Start == period+1 {
+ a.Start--
+ }
+ if a.End == period {
+ a.End++
+ }
+}
+
+// split splits the range into two ranges. The 'fromPeriod' will be the first
+// element in the second range (if present).
+// The original range is unchanged by this operation
+func (a *periodRange) split(fromPeriod uint64) (periodRange, periodRange) {
+ if fromPeriod <= a.Start {
+ // First range empty, everything in second range,
+ return periodRange{}, *a
+ }
+ if fromPeriod >= a.End {
+ // Second range empty, everything in first range,
+ return *a, periodRange{}
+ }
+ x := periodRange{a.Start, fromPeriod}
+ y := periodRange{fromPeriod, a.End}
+ return x, y
+}
+
+// each invokes the supplied function fn once per period in range
+func (a *periodRange) each(fn func(uint64)) {
+ for p := a.Start; p < a.End; p++ {
+ fn(p)
+ }
+}
diff --git a/beacon/light/test_helpers.go b/beacon/light/test_helpers.go
new file mode 100644
index 000000000000..f537d963a667
--- /dev/null
+++ b/beacon/light/test_helpers.go
@@ -0,0 +1,152 @@
+// Copyright 2023 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 .
+
+package light
+
+import (
+ "crypto/rand"
+ "crypto/sha256"
+ mrand "math/rand"
+
+ "github.com/ethereum/go-ethereum/beacon/merkle"
+ "github.com/ethereum/go-ethereum/beacon/params"
+ "github.com/ethereum/go-ethereum/beacon/types"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func GenerateTestCommittee() *types.SerializedSyncCommittee {
+ s := new(types.SerializedSyncCommittee)
+ rand.Read(s[:32])
+ return s
+}
+
+func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate {
+ update := new(types.LightClientUpdate)
+ update.NextSyncCommitteeRoot = nextCommittee.Root()
+ var attestedHeader types.Header
+ if finalizedHeader {
+ update.FinalizedHeader = new(types.Header)
+ *update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
+ attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock, merkle.Value(update.FinalizedHeader.Hash()))
+ } else {
+ attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
+ }
+ update.AttestedHeader = GenerateTestSignedHeader(attestedHeader, config, committee, attestedHeader.Slot+1, signerCount)
+ return update
+}
+
+func GenerateTestSignedHeader(header types.Header, config *types.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader {
+ bitmask := makeBitmask(signerCount)
+ signingRoot, _ := config.Forks.SigningRoot(header)
+ c, _ := dummyVerifier{}.deserializeSyncCommittee(committee)
+ return types.SignedHeader{
+ Header: header,
+ Signature: types.SyncAggregate{
+ Signers: bitmask,
+ Signature: makeDummySignature(c.(dummySyncCommittee), signingRoot, bitmask),
+ },
+ SignatureSlot: signatureSlot,
+ }
+}
+
+func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *types.BootstrapData {
+ header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee, merkle.Value(committee.Root()))
+ return &types.BootstrapData{
+ Header: header,
+ Committee: committee,
+ CommitteeRoot: committee.Root(),
+ CommitteeBranch: branch,
+ }
+}
+
+func makeBitmask(signerCount int) (bitmask [params.SyncCommitteeBitmaskSize]byte) {
+ for i := 0; i < params.SyncCommitteeSize; i++ {
+ if mrand.Intn(params.SyncCommitteeSize-i) < signerCount {
+ bitmask[i/8] += byte(1) << (i & 7)
+ signerCount--
+ }
+ }
+ return
+}
+
+func makeTestHeaderWithMerkleProof(slot, index uint64, value merkle.Value) (types.Header, merkle.Values) {
+ var branch merkle.Values
+ hasher := sha256.New()
+ for index > 1 {
+ var proofHash merkle.Value
+ rand.Read(proofHash[:])
+ hasher.Reset()
+ if index&1 == 0 {
+ hasher.Write(value[:])
+ hasher.Write(proofHash[:])
+ } else {
+ hasher.Write(proofHash[:])
+ hasher.Write(value[:])
+ }
+ hasher.Sum(value[:0])
+ index >>= 1
+ branch = append(branch, proofHash)
+ }
+ return types.Header{Slot: slot, StateRoot: common.Hash(value)}, branch
+}
+
+// syncCommittee holds either a blsSyncCommittee or a fake dummySyncCommittee used for testing
+type syncCommittee interface{}
+
+// committeeSigVerifier verifies sync committee signatures (either proper BLS
+// signatures or fake signatures used for testing)
+type committeeSigVerifier interface {
+ deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error)
+ verifySignature(committee syncCommittee, signedRoot common.Hash, aggregate *types.SyncAggregate) bool
+}
+
+// blsVerifier implements committeeSigVerifier
+type blsVerifier struct{}
+
+// deserializeSyncCommittee implements committeeSigVerifier
+func (blsVerifier) deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error) {
+ return s.Deserialize()
+}
+
+// verifySignature implements committeeSigVerifier
+func (blsVerifier) verifySignature(committee syncCommittee, signingRoot common.Hash, aggregate *types.SyncAggregate) bool {
+ return committee.(*types.SyncCommittee).VerifySignature(signingRoot, aggregate)
+}
+
+type dummySyncCommittee [32]byte
+
+// dummyVerifier implements committeeSigVerifier
+type dummyVerifier struct{}
+
+// deserializeSyncCommittee implements committeeSigVerifier
+func (dummyVerifier) deserializeSyncCommittee(s *types.SerializedSyncCommittee) (syncCommittee, error) {
+ var sc dummySyncCommittee
+ copy(sc[:], s[:32])
+ return sc, nil
+}
+
+// verifySignature implements committeeSigVerifier
+func (dummyVerifier) verifySignature(committee syncCommittee, signingRoot common.Hash, aggregate *types.SyncAggregate) bool {
+ return aggregate.Signature == makeDummySignature(committee.(dummySyncCommittee), signingRoot, aggregate.Signers)
+}
+
+func makeDummySignature(committee dummySyncCommittee, signingRoot common.Hash, bitmask [params.SyncCommitteeBitmaskSize]byte) (sig [params.BLSSignatureSize]byte) {
+ for i, b := range committee[:] {
+ sig[i] = b ^ signingRoot[i]
+ }
+ copy(sig[32:], bitmask[:])
+ return
+}
diff --git a/beacon/types/update.go b/beacon/types/light_sync.go
similarity index 88%
rename from beacon/types/update.go
rename to beacon/types/light_sync.go
index 06c1b6179256..3284081e4d49 100644
--- a/beacon/types/update.go
+++ b/beacon/types/light_sync.go
@@ -25,6 +25,24 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// BootstrapData contains a sync committee where light sync can be started,
+// together with a proof through a beacon header and corresponding state.
+// Note: BootstrapData is fetched from a server based on a known checkpoint hash.
+type BootstrapData struct {
+ Header Header
+ CommitteeRoot common.Hash
+ Committee *SerializedSyncCommittee `rlp:"-"`
+ CommitteeBranch merkle.Values
+}
+
+// Validate verifies the proof included in BootstrapData.
+func (c *BootstrapData) Validate() error {
+ if c.CommitteeRoot != c.Committee.Root() {
+ return errors.New("wrong committee root")
+ }
+ return merkle.VerifyProof(c.Header.StateRoot, params.StateIndexSyncCommittee, c.CommitteeBranch, merkle.Value(c.CommitteeRoot))
+}
+
// LightClientUpdate is a proof of the next sync committee root based on a header
// signed by the sync committee of the given period. Optionally, the update can
// prove quasi-finality by the signed header referring to a previous, finalized
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
index 8e82459e8225..be037235533a 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -132,6 +132,10 @@ var (
CliqueSnapshotPrefix = []byte("clique-")
+ BestUpdateKey = []byte("update-") // bigEndian64(syncPeriod) -> RLP(types.LightClientUpdate) (nextCommittee only referenced by root hash)
+ FixedCommitteeRootKey = []byte("fixedRoot-") // bigEndian64(syncPeriod) -> committee root hash
+ SyncCommitteeKey = []byte("committee-") // bigEndian64(syncPeriod) -> serialized committee
+
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
)
From d98d70f670297a4bfa86db1a67a9c024f7186f43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?=
Date: Fri, 8 Dec 2023 15:16:04 +0200
Subject: [PATCH 066/103] cmd/utils, eth: disallow invalid snap sync / snapshot
flag combos (#28657)
* eth: prevent startup in snap mode without snapshots
* cmd/utils: try to fix bad flag combos wrt snap sync and snapshot generation
---
cmd/utils/flags.go | 10 ++++++++--
eth/handler.go | 4 ++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 27e1b3f623c4..d4c918bf4fdb 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1677,10 +1677,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(CacheLogSizeFlag.Name) {
cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name)
}
- if !ctx.Bool(SnapshotFlag.Name) {
+ if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 {
// If snap-sync is requested, this flag is also required
if cfg.SyncMode == downloader.SnapSync {
- log.Info("Snap sync requested, enabling --snapshot")
+ if !ctx.Bool(SnapshotFlag.Name) {
+ log.Warn("Snap sync requested, enabling --snapshot")
+ }
+ if cfg.SnapshotCache == 0 {
+ log.Warn("Snap sync requested, resetting --cache.snapshot")
+ cfg.SnapshotCache = ctx.Int(CacheFlag.Name) * CacheSnapshotFlag.Value / 100
+ }
} else {
cfg.TrieCleanCache += cfg.SnapshotCache
cfg.SnapshotCache = 0 // Disabled
diff --git a/eth/handler.go b/eth/handler.go
index f0021e56446c..a327af61131f 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -178,6 +178,10 @@ func newHandler(config *handlerConfig) (*handler, error) {
log.Info("Enabled snap sync", "head", head.Number, "hash", head.Hash())
}
}
+ // If snap sync is requested but snapshots are disabled, fail loudly
+ if h.snapSync.Load() && config.Chain.Snapshots() == nil {
+ return nil, errors.New("snap sync not supported with snapshots disabled")
+ }
// Construct the downloader (long sync)
h.downloader = downloader.New(config.Database, h.eventMux, h.chain, nil, h.removePeer, h.enableSyncedFeatures)
if ttd := h.chain.Config().TerminalTotalDifficulty; ttd != nil {
From e206d3f8975bd98cc86d14055dca40f996bacc60 Mon Sep 17 00:00:00 2001
From: rjl493456442
Date: Fri, 8 Dec 2023 21:28:23 +0800
Subject: [PATCH 067/103] trie: remove inconsistent trie nodes during sync in
path mode (#28595)
This fixes a database corruption issue that could occur during state healing.
When sync is aborted while certain modifications were already committed, and a
reorg occurs, the database would contain incorrect trie nodes stored by path.
These nodes need to detected/deleted in order to obtain a complete and fully correct state
after state healing.
---------
Co-authored-by: Felix Lange
---
ethdb/dbtest/testsuite.go | 6 +-
trie/sync.go | 223 ++++++++++++++++++++++++--------------
trie/sync_test.go | 125 ++++++++++++++++++++-
3 files changed, 270 insertions(+), 84 deletions(-)
diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go
index 0d3d5f5aa694..29bd24364e3f 100644
--- a/ethdb/dbtest/testsuite.go
+++ b/ethdb/dbtest/testsuite.go
@@ -273,9 +273,13 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) {
b.Put([]byte("5"), nil)
b.Delete([]byte("1"))
b.Put([]byte("6"), nil)
- b.Delete([]byte("3"))
+
+ b.Delete([]byte("3")) // delete then put
b.Put([]byte("3"), nil)
+ b.Put([]byte("7"), nil) // put then delete
+ b.Delete([]byte("7"))
+
if err := b.Write(); err != nil {
t.Fatal(err)
}
diff --git a/trie/sync.go b/trie/sync.go
index 8eaed9f21ad2..589d28364b87 100644
--- a/trie/sync.go
+++ b/trie/sync.go
@@ -116,10 +116,9 @@ type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Ha
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
type nodeRequest struct {
- hash common.Hash // Hash of the trie node to retrieve
- path []byte // Merkle path leading to this node for prioritization
- data []byte // Data content of the node, cached until all subtrees complete
- deletes [][]byte // List of internal path segments for trie nodes to delete
+ hash common.Hash // Hash of the trie node to retrieve
+ path []byte // Merkle path leading to this node for prioritization
+ data []byte // Data content of the node, cached until all subtrees complete
parent *nodeRequest // Parent state node referencing this entry
deps int // Number of dependencies before allowed to commit this node
@@ -146,38 +145,85 @@ type CodeSyncResult struct {
Data []byte // Data content of the retrieved bytecode
}
+// nodeOp represents an operation upon the trie node. It can either represent a
+// deletion to the specific node or a node write for persisting retrieved node.
+type nodeOp struct {
+ owner common.Hash // identifier of the trie (empty for account trie)
+ path []byte // path from the root to the specified node.
+ blob []byte // the content of the node (nil for deletion)
+ hash common.Hash // hash of the node content (empty for node deletion)
+}
+
+// isDelete indicates if the operation is a database deletion.
+func (op *nodeOp) isDelete() bool {
+ return len(op.blob) == 0
+}
+
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
// persisted data items.
type syncMemBatch struct {
- nodes map[string][]byte // In-memory membatch of recently completed nodes
- hashes map[string]common.Hash // Hashes of recently completed nodes
- deletes map[string]struct{} // List of paths for trie node to delete
- codes map[common.Hash][]byte // In-memory membatch of recently completed codes
- size uint64 // Estimated batch-size of in-memory data.
+ scheme string // State scheme identifier
+ codes map[common.Hash][]byte // In-memory batch of recently completed codes
+ nodes []nodeOp // In-memory batch of recently completed/deleted nodes
+ size uint64 // Estimated batch-size of in-memory data.
}
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
-func newSyncMemBatch() *syncMemBatch {
+func newSyncMemBatch(scheme string) *syncMemBatch {
return &syncMemBatch{
- nodes: make(map[string][]byte),
- hashes: make(map[string]common.Hash),
- deletes: make(map[string]struct{}),
- codes: make(map[common.Hash][]byte),
+ scheme: scheme,
+ codes: make(map[common.Hash][]byte),
}
}
-// hasNode reports the trie node with specific path is already cached.
-func (batch *syncMemBatch) hasNode(path []byte) bool {
- _, ok := batch.nodes[string(path)]
- return ok
-}
-
// hasCode reports the contract code with specific hash is already cached.
func (batch *syncMemBatch) hasCode(hash common.Hash) bool {
_, ok := batch.codes[hash]
return ok
}
+// addCode caches a contract code database write operation.
+func (batch *syncMemBatch) addCode(hash common.Hash, code []byte) {
+ batch.codes[hash] = code
+ batch.size += common.HashLength + uint64(len(code))
+}
+
+// addNode caches a node database write operation.
+func (batch *syncMemBatch) addNode(owner common.Hash, path []byte, blob []byte, hash common.Hash) {
+ if batch.scheme == rawdb.PathScheme {
+ if owner == (common.Hash{}) {
+ batch.size += uint64(len(path) + len(blob))
+ } else {
+ batch.size += common.HashLength + uint64(len(path)+len(blob))
+ }
+ } else {
+ batch.size += common.HashLength + uint64(len(blob))
+ }
+ batch.nodes = append(batch.nodes, nodeOp{
+ owner: owner,
+ path: path,
+ blob: blob,
+ hash: hash,
+ })
+}
+
+// delNode caches a node database delete operation.
+func (batch *syncMemBatch) delNode(owner common.Hash, path []byte) {
+ if batch.scheme != rawdb.PathScheme {
+ log.Error("Unexpected node deletion", "owner", owner, "path", path, "scheme", batch.scheme)
+ return // deletion is not supported in hash mode.
+ }
+ if owner == (common.Hash{}) {
+ batch.size += uint64(len(path))
+ } else {
+ batch.size += common.HashLength + uint64(len(path))
+ }
+ batch.nodes = append(batch.nodes, nodeOp{
+ owner: owner,
+ path: path,
+ })
+}
+
// Sync is the main state trie synchronisation scheduler, which provides yet
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// and reconstructs the trie step by step until all is done.
@@ -196,7 +242,7 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
ts := &Sync{
scheme: scheme,
database: database,
- membatch: newSyncMemBatch(),
+ membatch: newSyncMemBatch(scheme),
nodeReqs: make(map[string]*nodeRequest),
codeReqs: make(map[common.Hash]*codeRequest),
queue: prque.New[int64, any](nil), // Ugh, can contain both string and hash, whyyy
@@ -210,16 +256,17 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
// parent for completion tracking. The given path is a unique node path in
// hex format and contain all the parent path if it's layered trie node.
func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, parentPath []byte, callback LeafCallback) {
- // Short circuit if the trie is empty or already known
if root == types.EmptyRootHash {
return
}
- if s.membatch.hasNode(path) {
- return
- }
owner, inner := ResolvePath(path)
- if rawdb.HasTrieNode(s.database, owner, inner, root, s.scheme) {
+ exist, inconsistent := s.hasNode(owner, inner, root)
+ if exist {
+ // The entire subtrie is already present in the database.
return
+ } else if inconsistent {
+ // There is a pre-existing node with the wrong hash in DB, remove it.
+ s.membatch.delNode(owner, inner)
}
// Assemble the new sub-trie sync request
req := &nodeRequest{
@@ -371,39 +418,42 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
}
// Commit flushes the data stored in the internal membatch out to persistent
-// storage, returning any occurred error.
+// storage, returning any occurred error. The whole data set will be flushed
+// in an atomic database batch.
func (s *Sync) Commit(dbw ethdb.Batch) error {
// Flush the pending node writes into database batch.
var (
account int
storage int
)
- for path, value := range s.membatch.nodes {
- owner, inner := ResolvePath([]byte(path))
- if owner == (common.Hash{}) {
- account += 1
+ for _, op := range s.membatch.nodes {
+ if op.isDelete() {
+ // node deletion is only supported in path mode.
+ if op.owner == (common.Hash{}) {
+ rawdb.DeleteAccountTrieNode(dbw, op.path)
+ } else {
+ rawdb.DeleteStorageTrieNode(dbw, op.owner, op.path)
+ }
+ deletionGauge.Inc(1)
} else {
- storage += 1
+ if op.owner == (common.Hash{}) {
+ account += 1
+ } else {
+ storage += 1
+ }
+ rawdb.WriteTrieNode(dbw, op.owner, op.path, op.hash, op.blob, s.scheme)
}
- rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
}
accountNodeSyncedGauge.Inc(int64(account))
storageNodeSyncedGauge.Inc(int64(storage))
- // Flush the pending node deletes into the database batch.
- // Please note that each written and deleted node has a
- // unique path, ensuring no duplication occurs.
- for path := range s.membatch.deletes {
- owner, inner := ResolvePath([]byte(path))
- rawdb.DeleteTrieNode(dbw, owner, inner, common.Hash{} /* unused */, s.scheme)
- }
// Flush the pending code writes into database batch.
for hash, value := range s.membatch.codes {
rawdb.WriteCode(dbw, hash, value)
}
codeSyncedGauge.Inc(int64(len(s.membatch.codes)))
- s.membatch = newSyncMemBatch() // reset the batch
+ s.membatch = newSyncMemBatch(s.scheme) // reset the batch
return nil
}
@@ -476,12 +526,15 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
// child as invalid. This is essential in the case of path mode
// scheme; otherwise, state healing might overwrite existing child
// nodes silently while leaving a dangling parent node within the
- // range of this internal path on disk. This would break the
- // guarantee for state healing.
+ // range of this internal path on disk and the persistent state
+ // ends up with a very weird situation that nodes on the same path
+ // are not inconsistent while they all present in disk. This property
+ // would break the guarantee for state healing.
//
// While it's possible for this shortNode to overwrite a previously
// existing full node, the other branches of the fullNode can be
- // retained as they remain untouched and complete.
+ // retained as they are not accessible with the new shortNode, and
+ // also the whole sub-trie is still untouched and complete.
//
// This step is only necessary for path mode, as there is no deletion
// in hash mode at all.
@@ -498,8 +551,7 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
exists = rawdb.ExistsStorageTrieNode(s.database, owner, append(inner, key[:i]...))
}
if exists {
- req.deletes = append(req.deletes, key[:i])
- deletionGauge.Inc(1)
+ s.membatch.delNode(owner, append(inner, key[:i]...))
log.Debug("Detected dangling node", "owner", owner, "path", append(inner, key[:i]...))
}
}
@@ -521,6 +573,7 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
var (
missing = make(chan *nodeRequest, len(children))
pending sync.WaitGroup
+ batchMu sync.Mutex
)
for _, child := range children {
// Notify any external watcher of a new key/value node
@@ -538,34 +591,32 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
}
}
}
- // If the child references another node, resolve or schedule
+ // If the child references another node, resolve or schedule.
+ // We check all children concurrently.
if node, ok := (child.node).(hashNode); ok {
- // Try to resolve the node from the local database
- if s.membatch.hasNode(child.path) {
- continue
- }
- // Check the presence of children concurrently
+ path := child.path
+ hash := common.BytesToHash(node)
pending.Add(1)
- go func(child childNode) {
+ go func() {
defer pending.Done()
-
- // If database says duplicate, then at least the trie node is present
- // and we hold the assumption that it's NOT legacy contract code.
- var (
- chash = common.BytesToHash(node)
- owner, inner = ResolvePath(child.path)
- )
- if rawdb.HasTrieNode(s.database, owner, inner, chash, s.scheme) {
+ owner, inner := ResolvePath(path)
+ exist, inconsistent := s.hasNode(owner, inner, hash)
+ if exist {
return
+ } else if inconsistent {
+ // There is a pre-existing node with the wrong hash in DB, remove it.
+ batchMu.Lock()
+ s.membatch.delNode(owner, inner)
+ batchMu.Unlock()
}
// Locally unknown node, schedule for retrieval
missing <- &nodeRequest{
- path: child.path,
- hash: chash,
+ path: path,
+ hash: hash,
parent: req,
callback: req.callback,
}
- }(child)
+ }()
}
}
pending.Wait()
@@ -587,21 +638,10 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
// committed themselves.
func (s *Sync) commitNodeRequest(req *nodeRequest) error {
// Write the node content to the membatch
- s.membatch.nodes[string(req.path)] = req.data
- s.membatch.hashes[string(req.path)] = req.hash
+ owner, path := ResolvePath(req.path)
+ s.membatch.addNode(owner, path, req.data, req.hash)
- // The size tracking refers to the db-batch, not the in-memory data.
- if s.scheme == rawdb.PathScheme {
- s.membatch.size += uint64(len(req.path) + len(req.data))
- } else {
- s.membatch.size += common.HashLength + uint64(len(req.data))
- }
- // Delete the internal nodes which are marked as invalid
- for _, segment := range req.deletes {
- path := append(req.path, segment...)
- s.membatch.deletes[string(path)] = struct{}{}
- s.membatch.size += uint64(len(path))
- }
+ // Removed the completed node request
delete(s.nodeReqs, string(req.path))
s.fetches[len(req.path)]--
@@ -622,8 +662,9 @@ func (s *Sync) commitNodeRequest(req *nodeRequest) error {
// committed themselves.
func (s *Sync) commitCodeRequest(req *codeRequest) error {
// Write the node content to the membatch
- s.membatch.codes[req.hash] = req.data
- s.membatch.size += common.HashLength + uint64(len(req.data))
+ s.membatch.addCode(req.hash, req.data)
+
+ // Removed the completed code request
delete(s.codeReqs, req.hash)
s.fetches[len(req.path)]--
@@ -639,6 +680,28 @@ func (s *Sync) commitCodeRequest(req *codeRequest) error {
return nil
}
+// hasNode reports whether the specified trie node is present in the database.
+// 'exists' is true when the node exists in the database and matches the given root
+// hash. The 'inconsistent' return value is true when the node exists but does not
+// match the expected hash.
+func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists bool, inconsistent bool) {
+ // If node is running with hash scheme, check the presence with node hash.
+ if s.scheme == rawdb.HashScheme {
+ return rawdb.HasLegacyTrieNode(s.database, hash), false
+ }
+ // If node is running with path scheme, check the presence with node path.
+ var blob []byte
+ var dbHash common.Hash
+ if owner == (common.Hash{}) {
+ blob, dbHash = rawdb.ReadAccountTrieNode(s.database, path)
+ } else {
+ blob, dbHash = rawdb.ReadStorageTrieNode(s.database, owner, path)
+ }
+ exists = hash == dbHash
+ inconsistent = !exists && len(blob) != 0
+ return exists, inconsistent
+}
+
// ResolvePath resolves the provided composite node path by separating the
// path in account trie if it's existent.
func ResolvePath(path []byte) (common.Hash, []byte) {
diff --git a/trie/sync_test.go b/trie/sync_test.go
index 5edfb32a3733..585181b48cd7 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -684,8 +684,11 @@ func testSyncOrdering(t *testing.T, scheme string) {
}
}
}
-
func syncWith(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database) {
+ syncWithHookWriter(t, root, db, srcDb, nil)
+}
+
+func syncWithHookWriter(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database, hookWriter ethdb.KeyValueWriter) {
// Create a destination trie and sync with the scheduler
sched := NewSync(root, db, nil, srcDb.Scheme())
@@ -723,8 +726,11 @@ func syncWith(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database
if err := sched.Commit(batch); err != nil {
t.Fatalf("failed to commit data: %v", err)
}
- batch.Write()
-
+ if hookWriter != nil {
+ batch.Replay(hookWriter)
+ } else {
+ batch.Write()
+ }
paths, nodes, _ = sched.Missing(0)
elements = elements[:0]
for i := 0; i < len(paths); i++ {
@@ -894,3 +900,116 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) {
syncWith(t, rootC, destDisk, srcTrieDB)
checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateC, true)
}
+
+func TestSyncAbort(t *testing.T) {
+ testSyncAbort(t, rawdb.PathScheme)
+ testSyncAbort(t, rawdb.HashScheme)
+}
+
+type hookWriter struct {
+ db ethdb.KeyValueStore
+ filter func(key []byte, value []byte) bool
+}
+
+// Put inserts the given value into the key-value data store.
+func (w *hookWriter) Put(key []byte, value []byte) error {
+ if w.filter != nil && w.filter(key, value) {
+ return nil
+ }
+ return w.db.Put(key, value)
+}
+
+// Delete removes the key from the key-value data store.
+func (w *hookWriter) Delete(key []byte) error {
+ return w.db.Delete(key)
+}
+
+func testSyncAbort(t *testing.T, scheme string) {
+ var (
+ srcDisk = rawdb.NewMemoryDatabase()
+ srcTrieDB = newTestDatabase(srcDisk, scheme)
+ srcTrie, _ = New(TrieID(types.EmptyRootHash), srcTrieDB)
+
+ deleteFn = func(key []byte, tr *Trie, states map[string][]byte) {
+ tr.Delete(key)
+ delete(states, string(key))
+ }
+ writeFn = func(key []byte, val []byte, tr *Trie, states map[string][]byte) {
+ if val == nil {
+ val = randBytes(32)
+ }
+ tr.Update(key, val)
+ states[string(key)] = common.CopyBytes(val)
+ }
+ copyStates = func(states map[string][]byte) map[string][]byte {
+ cpy := make(map[string][]byte)
+ for k, v := range states {
+ cpy[k] = v
+ }
+ return cpy
+ }
+ )
+ var (
+ stateA = make(map[string][]byte)
+ key = randBytes(32)
+ val = randBytes(32)
+ )
+ for i := 0; i < 256; i++ {
+ writeFn(randBytes(32), nil, srcTrie, stateA)
+ }
+ writeFn(key, val, srcTrie, stateA)
+
+ rootA, nodesA, _ := srcTrie.Commit(false)
+ if err := srcTrieDB.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil); err != nil {
+ panic(err)
+ }
+ if err := srcTrieDB.Commit(rootA, false); err != nil {
+ panic(err)
+ }
+ // Create a destination trie and sync with the scheduler
+ destDisk := rawdb.NewMemoryDatabase()
+ syncWith(t, rootA, destDisk, srcTrieDB)
+ checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateA, true)
+
+ // Delete the element from the trie
+ stateB := copyStates(stateA)
+ srcTrie, _ = New(TrieID(rootA), srcTrieDB)
+ deleteFn(key, srcTrie, stateB)
+
+ rootB, nodesB, _ := srcTrie.Commit(false)
+ if err := srcTrieDB.Update(rootB, rootA, 0, trienode.NewWithNodeSet(nodesB), nil); err != nil {
+ panic(err)
+ }
+ if err := srcTrieDB.Commit(rootB, false); err != nil {
+ panic(err)
+ }
+
+ // Sync the new state, but never persist the new root node. Before the
+ // fix #28595, the original old root node will still be left in database
+ // which breaks the next healing cycle.
+ syncWithHookWriter(t, rootB, destDisk, srcTrieDB, &hookWriter{db: destDisk, filter: func(key []byte, value []byte) bool {
+ if scheme == rawdb.HashScheme {
+ return false
+ }
+ if len(value) == 0 {
+ return false
+ }
+ ok, path := rawdb.ResolveAccountTrieNodeKey(key)
+ return ok && len(path) == 0
+ }})
+
+ // Add elements to expand trie
+ stateC := copyStates(stateB)
+ srcTrie, _ = New(TrieID(rootB), srcTrieDB)
+
+ writeFn(key, val, srcTrie, stateC)
+ rootC, nodesC, _ := srcTrie.Commit(false)
+ if err := srcTrieDB.Update(rootC, rootB, 0, trienode.NewWithNodeSet(nodesC), nil); err != nil {
+ panic(err)
+ }
+ if err := srcTrieDB.Commit(rootC, false); err != nil {
+ panic(err)
+ }
+ syncWith(t, rootC, destDisk, srcTrieDB)
+ checkTrieContents(t, destDisk, scheme, srcTrie.Hash().Bytes(), stateC, true)
+}
From a3ca1b28188f2f5747e05d23bfc4f0f7ce1007f0 Mon Sep 17 00:00:00 2001
From: Ng Wei Han <47109095+weiihann@users.noreply.github.com>
Date: Tue, 12 Dec 2023 21:40:50 +0800
Subject: [PATCH 068/103] cmd/utils: fix HTTPHost, WSHost flag priority
(#28669)
Co-authored-by: Felix Lange
---
cmd/utils/flags.go | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index d4c918bf4fdb..159c47ca0191 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1088,8 +1088,10 @@ func SplitAndTrim(input string) (ret []string) {
// setHTTP creates the HTTP RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
func setHTTP(ctx *cli.Context, cfg *node.Config) {
- if ctx.Bool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" {
- cfg.HTTPHost = "127.0.0.1"
+ if ctx.Bool(HTTPEnabledFlag.Name) {
+ if cfg.HTTPHost == "" {
+ cfg.HTTPHost = "127.0.0.1"
+ }
if ctx.IsSet(HTTPListenAddrFlag.Name) {
cfg.HTTPHost = ctx.String(HTTPListenAddrFlag.Name)
}
@@ -1153,8 +1155,10 @@ func setGraphQL(ctx *cli.Context, cfg *node.Config) {
// setWS creates the WebSocket RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
func setWS(ctx *cli.Context, cfg *node.Config) {
- if ctx.Bool(WSEnabledFlag.Name) && cfg.WSHost == "" {
- cfg.WSHost = "127.0.0.1"
+ if ctx.Bool(WSEnabledFlag.Name) {
+ if cfg.WSHost == "" {
+ cfg.WSHost = "127.0.0.1"
+ }
if ctx.IsSet(WSListenAddrFlag.Name) {
cfg.WSHost = ctx.String(WSListenAddrFlag.Name)
}
From 17c2b3c194162a4eeb92f90a950bc92b58660dc3 Mon Sep 17 00:00:00 2001
From: Ford <153042616+guerrierindien@users.noreply.github.com>
Date: Tue, 12 Dec 2023 14:47:59 +0100
Subject: [PATCH 069/103] eth/protocols/eth: fix typos in comments (#28652)
---
eth/protocols/eth/dispatcher.go | 4 ++--
eth/protocols/eth/peer.go | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/eth/protocols/eth/dispatcher.go b/eth/protocols/eth/dispatcher.go
index 3f81e045bae9..ae98820cd6ac 100644
--- a/eth/protocols/eth/dispatcher.go
+++ b/eth/protocols/eth/dispatcher.go
@@ -41,7 +41,7 @@ var (
// Request is a pending request to allow tracking it and delivering a response
// back to the requester on their chosen channel.
type Request struct {
- peer *Peer // Peer to which this request belogs for untracking
+ peer *Peer // Peer to which this request belongs for untracking
id uint64 // Request ID to match up replies to
sink chan *Response // Channel to deliver the response on
@@ -224,7 +224,7 @@ func (p *Peer) dispatcher() {
switch {
case res.Req == nil:
// Response arrived with an untracked ID. Since even cancelled
- // requests are tracked until fulfilment, a dangling response
+ // requests are tracked until fulfillment, a dangling response
// means the remote peer implements the protocol badly.
resOp.fail <- errDanglingResponse
diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go
index 938af0cab0df..98ad22a8cfa4 100644
--- a/eth/protocols/eth/peer.go
+++ b/eth/protocols/eth/peer.go
@@ -84,7 +84,7 @@ type Peer struct {
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
- reqDispatch chan *request // Dispatch channel to send requests and track then until fulfilment
+ reqDispatch chan *request // Dispatch channel to send requests and track then until fulfillment
reqCancel chan *cancel // Dispatch channel to cancel pending requests and untrack them
resDispatch chan *response // Dispatch channel to fulfil pending requests and untrack them
From 81fd1b3cf9c4c4c9f0e06f8bdcbaa8b29c81b052 Mon Sep 17 00:00:00 2001
From: ucwong
Date: Tue, 12 Dec 2023 15:23:36 +0000
Subject: [PATCH 070/103] core/txpool : small cleanup refactors (#28654)
---
cmd/geth/logging_test.go | 2 +-
core/txpool/legacypool/legacypool.go | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/cmd/geth/logging_test.go b/cmd/geth/logging_test.go
index 50991554b427..b5ce03f4b8db 100644
--- a/cmd/geth/logging_test.go
+++ b/cmd/geth/logging_test.go
@@ -21,6 +21,7 @@ package main
import (
"bufio"
"bytes"
+ "encoding/json"
"fmt"
"io"
"math/rand"
@@ -28,7 +29,6 @@ import (
"os/exec"
"strings"
"testing"
- "encoding/json"
"github.com/ethereum/go-ethereum/internal/reexec"
)
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index 8450d89a2cf2..f7d4a2e1e186 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -923,8 +923,7 @@ func (pool *LegacyPool) addLocals(txs []*types.Transaction) []error {
// addLocal enqueues a single local transaction into the pool if it is valid. This is
// a convenience wrapper around addLocals.
func (pool *LegacyPool) addLocal(tx *types.Transaction) error {
- errs := pool.addLocals([]*types.Transaction{tx})
- return errs[0]
+ return pool.addLocals([]*types.Transaction{tx})[0]
}
// addRemotes enqueues a batch of transactions into the pool if they are valid. If the
@@ -939,8 +938,7 @@ func (pool *LegacyPool) addRemotes(txs []*types.Transaction) []error {
// addRemote enqueues a single transaction into the pool if it is valid. This is a convenience
// wrapper around addRemotes.
func (pool *LegacyPool) addRemote(tx *types.Transaction) error {
- errs := pool.addRemotes([]*types.Transaction{tx})
- return errs[0]
+ return pool.addRemotes([]*types.Transaction{tx})[0]
}
// addRemotesSync is like addRemotes, but waits for pool reorganization. Tests use this method.
From b2ced97ac460110f9a1bf4088b27e0d5eba4f086 Mon Sep 17 00:00:00 2001
From: Ursulafe <152976968+Ursulafe@users.noreply.github.com>
Date: Wed, 13 Dec 2023 23:32:17 +0100
Subject: [PATCH 071/103] eth/fetcher, eth/gasestimator: fix typos in comments
(#28675)
---
eth/fetcher/tx_fetcher_test.go | 4 ++--
eth/gasestimator/gasestimator.go | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go
index 77b89085d317..4a62e579b635 100644
--- a/eth/fetcher/tx_fetcher_test.go
+++ b/eth/fetcher/tx_fetcher_test.go
@@ -186,7 +186,7 @@ func TestTransactionFetcherWaiting(t *testing.T) {
// waitlist, and none of them are scheduled for retrieval until the wait expires.
//
// This test is an extended version of TestTransactionFetcherWaiting. It's mostly
-// to cover the metadata checkes without bloating up the basic behavioral tests
+// to cover the metadata checks without bloating up the basic behavioral tests
// with all the useless extra fields.
func TestTransactionFetcherWaitingWithMeta(t *testing.T) {
testTransactionFetcherParallel(t, txFetcherTest{
@@ -1030,7 +1030,7 @@ func TestTransactionFetcherRateLimiting(t *testing.T) {
}
// Tests that if huge transactions are announced, only a small number of them will
-// be requested at a time, to keep the responses below a resonable level.
+// be requested at a time, to keep the responses below a reasonable level.
func TestTransactionFetcherBandwidthLimiting(t *testing.T) {
testTransactionFetcherParallel(t, txFetcherTest{
init: func() *TxFetcher {
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
index 4a8e20dfed74..a36c6707479d 100644
--- a/eth/gasestimator/gasestimator.go
+++ b/eth/gasestimator/gasestimator.go
@@ -47,7 +47,7 @@ type Options struct {
}
// Estimate returns the lowest possible gas limit that allows the transaction to
-// run successfully with the provided context optons. It returns an error if the
+// run successfully with the provided context options. It returns an error if the
// transaction would always revert, or if there are unexpected failures.
func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uint64) (uint64, []byte, error) {
// Binary search the gas limit, as it may need to be higher than the amount used
From 0f74aad6415dab225e5969e079a53d4844582720 Mon Sep 17 00:00:00 2001
From: Elias Rad <146735585+nnsW3@users.noreply.github.com>
Date: Thu, 14 Dec 2023 00:33:46 +0200
Subject: [PATCH 072/103] all: fix typos in comments (#28662)
Co-authored-by: Felix Lange
---
accounts/abi/abi.go | 2 +-
accounts/abi/bind/auth.go | 2 +-
accounts/abi/bind/base.go | 4 ++--
accounts/abi/bind/bind.go | 2 +-
accounts/abi/topics.go | 2 +-
cmd/clef/README.md | 2 +-
6 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 6e1075c715fd..4abf298068e7 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -251,7 +251,7 @@ var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
var panicSelector = crypto.Keccak256([]byte("Panic(uint256)"))[:4]
// panicReasons map is for readable panic codes
-// see this linkage for the deails
+// see this linkage for the details
// https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require
// the reason string list is copied from ether.js
// https://github.com/ethers-io/ethers.js/blob/fa3a883ff7c88611ce766f58bdd4b8ac90814470/src.ts/abi/interface.ts#L207-L218
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
index 91913ec3b26b..0740c6951025 100644
--- a/accounts/abi/bind/auth.go
+++ b/accounts/abi/bind/auth.go
@@ -117,7 +117,7 @@ func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.I
}
// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from
-// an decrypted key from a keystore.
+// a decrypted key from a keystore.
func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) {
if chainID == nil {
return nil, ErrNoChainID
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 6da15f147ce9..96d284cdcc0c 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -238,7 +238,7 @@ func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...in
if err != nil {
return nil, err
}
- // todo(rjl493456442) check the method is payable or not,
+ // todo(rjl493456442) check whether the method is payable or not,
// reject invalid transaction at the first place
return c.transact(opts, &c.address, input)
}
@@ -246,7 +246,7 @@ func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...in
// RawTransact initiates a transaction with the given raw calldata as the input.
// It's usually used to initiate transactions for invoking **Fallback** function.
func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
- // todo(rjl493456442) check the method is payable or not,
+ // todo(rjl493456442) check whether the method is payable or not,
// reject invalid transaction at the first place
return c.transact(opts, &c.address, calldata)
}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index ec2801346311..e902345f090a 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -79,7 +79,7 @@ func isKeyWord(arg string) bool {
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
// to be used as is in client code, but rather as an intermediate struct which
-// enforces compile time type safety and naming convention opposed to having to
+// enforces compile time type safety and naming convention as opposed to having to
// manually maintain hard coded strings that break on runtime.
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
var (
diff --git a/accounts/abi/topics.go b/accounts/abi/topics.go
index 360df7d5e846..60c71d88b2cc 100644
--- a/accounts/abi/topics.go
+++ b/accounts/abi/topics.go
@@ -75,7 +75,7 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
copy(topic[:], hash[:])
default:
- // todo(rjl493456442) according solidity documentation, indexed event
+ // todo(rjl493456442) according to solidity documentation, indexed event
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
diff --git a/cmd/clef/README.md b/cmd/clef/README.md
index 85c9c706069b..3a43db8c95a3 100644
--- a/cmd/clef/README.md
+++ b/cmd/clef/README.md
@@ -2,7 +2,7 @@
Clef can be used to sign transactions and data and is meant as a(n eventual) replacement for Geth's account management. This allows DApps to not depend on Geth's account management. When a DApp wants to sign data (or a transaction), it can send the content to Clef, which will then provide the user with context and asks for permission to sign the content. If the users grants the signing request, Clef will send the signature back to the DApp.
-This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can help in situations when a DApp is connected to an untrusted remote Ethereum node, because a local one is not available, not synchronised with the chain, or is a node that has no built-in (or limited) account management.
+This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can help in situations when a DApp is connected to an untrusted remote Ethereum node, because a local one is not available, not synchronized with the chain, or is a node that has no built-in (or limited) account management.
Clef can run as a daemon on the same machine, off a usb-stick like [USB armory](https://inversepath.com/usbarmory), or even a separate VM in a [QubesOS](https://www.qubes-os.org/) type setup.
From f1794ba2788baf34489847bfa9ca00e067507db0 Mon Sep 17 00:00:00 2001
From: FletcherMan
Date: Fri, 15 Dec 2023 11:48:55 +0800
Subject: [PATCH 073/103] miner: eliminate the dead loop possibility for
`newWorkLoop` and `mainLoop` (#28677)
discard the intervalAdjust message if the channel is full
---
miner/worker.go | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/miner/worker.go b/miner/worker.go
index f68070281454..2ed91cc18781 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -1074,7 +1074,7 @@ func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
case err == nil:
// The entire block is filled, decrease resubmit interval in case
// of current interval is larger than the user-specified one.
- w.resubmitAdjustCh <- &intervalAdjust{inc: false}
+ w.adjustResubmitInterval(&intervalAdjust{inc: false})
case errors.Is(err, errBlockInterruptedByRecommit):
// Notify resubmit loop to increase resubmitting interval if the
@@ -1084,10 +1084,10 @@ func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
if ratio < 0.1 {
ratio = 0.1
}
- w.resubmitAdjustCh <- &intervalAdjust{
+ w.adjustResubmitInterval(&intervalAdjust{
ratio: ratio,
inc: true,
- }
+ })
case errors.Is(err, errBlockInterruptedByNewHead):
// If the block building is interrupted by newhead event, discard it
@@ -1169,6 +1169,15 @@ func (w *worker) isTTDReached(header *types.Header) bool {
return td != nil && ttd != nil && td.Cmp(ttd) >= 0
}
+// adjustResubmitInterval adjusts the resubmit interval.
+func (w *worker) adjustResubmitInterval(message *intervalAdjust) {
+ select {
+ case w.resubmitAdjustCh <- message:
+ default:
+ log.Warn("the resubmitAdjustCh is full, discard the message")
+ }
+}
+
// copyReceipts makes a deep copy of the given receipts.
func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
result := make([]*types.Receipt, len(receipts))
From edc864f9ba186fd307d9c98c42136db6c9411cf9 Mon Sep 17 00:00:00 2001
From: alex <152680487+bodhi-crypo@users.noreply.github.com>
Date: Mon, 18 Dec 2023 16:35:12 +0800
Subject: [PATCH 074/103] all: fix typos in comments (#28682)
chore(core,eth):fix a couple of typos
---
cmd/clef/pythonsigner.py | 2 +-
core/txpool/blobpool/blobpool.go | 4 ++--
core/txpool/blobpool/blobpool_test.go | 2 +-
core/txpool/blobpool/metrics.go | 2 +-
core/vm/runtime/runtime_test.go | 2 +-
eth/downloader/downloader.go | 2 +-
eth/downloader/resultstore.go | 2 +-
ethclient/gethclient/gethclient_test.go | 2 +-
p2p/rlpx/rlpx_test.go | 2 +-
p2p/simulations/network_test.go | 2 +-
10 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py
index b9ea1e406a89..5d0eb18dcc1f 100644
--- a/cmd/clef/pythonsigner.py
+++ b/cmd/clef/pythonsigner.py
@@ -91,7 +91,7 @@ def approveTx(self, req):
{"jsonrpc":"2.0","id":20,"method":"ui_approveTx","params":[{"transaction":{"from":"0xDEADbEeF000000000000000000000000DeaDbeEf","to":"0xDEADbEeF000000000000000000000000DeaDbeEf","gas":"0x3e8","gasPrice":"0x5","maxFeePerGas":null,"maxPriorityFeePerGas":null,"value":"0x6","nonce":"0x1","data":"0x"},"call_info":null,"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]}
:param transaction: transaction info
- :param call_info: info abou the call, e.g. if ABI info could not be
+ :param call_info: info about the call, e.g. if ABI info could not be
:param meta: metadata about the request, e.g. where the call comes from
:return:
""" # noqa: E501
diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go
index 32c6c0e8feef..195697a8f6f7 100644
--- a/core/txpool/blobpool/blobpool.go
+++ b/core/txpool/blobpool/blobpool.go
@@ -738,7 +738,7 @@ func (p *BlobPool) offload(addr common.Address, nonce uint64, id uint64, inclusi
}
// Reset implements txpool.SubPool, allowing the blob pool's internal state to be
-// kept in sync with the main transacion pool's internal state.
+// kept in sync with the main transaction pool's internal state.
func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
waitStart := time.Now()
p.lock.Lock()
@@ -972,7 +972,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error {
}
// SetGasTip implements txpool.SubPool, allowing the blob pool's gas requirements
-// to be kept in sync with the main transacion pool's gas requirements.
+// to be kept in sync with the main transaction pool's gas requirements.
func (p *BlobPool) SetGasTip(tip *big.Int) {
p.lock.Lock()
defer p.lock.Unlock()
diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go
index fa3e8edc9055..b709ad0e583f 100644
--- a/core/txpool/blobpool/blobpool_test.go
+++ b/core/txpool/blobpool/blobpool_test.go
@@ -594,7 +594,7 @@ func TestOpenDrops(t *testing.T) {
verifyPoolInternals(t, pool)
}
-// Tests that transactions loaded from disk are indexed corrently.
+// Tests that transactions loaded from disk are indexed correctly.
//
// - 1. Transactions must be groupped by sender, sorted by nonce
// - 2. Eviction thresholds are calculated correctly for the sequences
diff --git a/core/txpool/blobpool/metrics.go b/core/txpool/blobpool/metrics.go
index 070cc5ca4712..587804cc6114 100644
--- a/core/txpool/blobpool/metrics.go
+++ b/core/txpool/blobpool/metrics.go
@@ -65,7 +65,7 @@ var (
pooltipGauge = metrics.NewRegisteredGauge("blobpool/pooltip", nil)
// addwait/time, resetwait/time and getwait/time track the rough health of
- // the pool and wether or not it's capable of keeping up with the load from
+ // the pool and whether or not it's capable of keeping up with the load from
// the network.
addwaitHist = metrics.NewRegisteredHistogram("blobpool/addwait", nil, metrics.NewExpDecaySample(1028, 0.015))
addtimeHist = metrics.NewRegisteredHistogram("blobpool/addtime", nil, metrics.NewExpDecaySample(1028, 0.015))
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 796d3b443414..e71760bb235c 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -671,7 +671,7 @@ func TestColdAccountAccessCost(t *testing.T) {
for ii, op := range tracer.StructLogs() {
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
}
- t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
+ t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
}
}
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 2ca7e328c6c4..f1cfa92d5d69 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -576,7 +576,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd *
// For non-merged networks, if there is a checkpoint available, then calculate
// the ancientLimit through that. Otherwise calculate the ancient limit through
// the advertised height of the remote peer. This most is mostly a fallback for
- // legacy networks, but should eventually be droppped. TODO(karalabe).
+ // legacy networks, but should eventually be dropped. TODO(karalabe).
if beaconMode {
// Beacon sync, use the latest finalized block as the ancient limit
// or a reasonable height if no finalized block is yet announced.
diff --git a/eth/downloader/resultstore.go b/eth/downloader/resultstore.go
index 7f7f5a89e264..e4323c04ebc5 100644
--- a/eth/downloader/resultstore.go
+++ b/eth/downloader/resultstore.go
@@ -142,7 +142,7 @@ func (r *resultStore) HasCompletedItems() bool {
// countCompleted returns the number of items ready for delivery, stopping at
// the first non-complete item.
//
-// The mthod assumes (at least) rlock is held.
+// The method assumes (at least) rlock is held.
func (r *resultStore) countCompleted() int {
// We iterate from the already known complete point, and see
// if any more has completed since last count
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
index a718246bd0dc..fdd94a7d734d 100644
--- a/ethclient/gethclient/gethclient_test.go
+++ b/ethclient/gethclient/gethclient_test.go
@@ -450,7 +450,7 @@ func testCallContract(t *testing.T, client *rpc.Client) {
func TestOverrideAccountMarshal(t *testing.T) {
om := map[common.Address]OverrideAccount{
{0x11}: {
- // Zero-valued nonce is not overriddden, but simply dropped by the encoder.
+ // Zero-valued nonce is not overridden, but simply dropped by the encoder.
Nonce: 0,
},
{0xaa}: {
diff --git a/p2p/rlpx/rlpx_test.go b/p2p/rlpx/rlpx_test.go
index 28759f2b4948..136cb1b5bfca 100644
--- a/p2p/rlpx/rlpx_test.go
+++ b/p2p/rlpx/rlpx_test.go
@@ -421,7 +421,7 @@ func BenchmarkThroughput(b *testing.B) {
}
conn2.SetSnappy(true)
if err := <-handshakeDone; err != nil {
- b.Fatal("server hanshake error:", err)
+ b.Fatal("server handshake error:", err)
}
// Read N messages.
diff --git a/p2p/simulations/network_test.go b/p2p/simulations/network_test.go
index ab8cf19462e7..4ed1e4e6c33b 100644
--- a/p2p/simulations/network_test.go
+++ b/p2p/simulations/network_test.go
@@ -683,7 +683,7 @@ func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, i
}
}
-// \todo: refactor to implement shapshots
+// \todo: refactor to implement snapshots
// and connect configuration methods once these are moved from
// swarm/network/simulations/connect.go
func BenchmarkMinimalService(b *testing.B) {
From 5b22a472d6aaaa17daf0543b5914ca1f2f5518a7 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Mon, 18 Dec 2023 10:47:21 +0100
Subject: [PATCH 075/103] p2p/discover: add liveness check in collectTableNodes
(#28686)
* p2p/discover: add liveness check in collectTableNodes
* p2p/discover: fix test
* p2p/discover: rename to appendLiveNodes
* p2p/discover: add dedup logic back
* p2p/discover: simplify
* p2p/discover: fix issue found by test
---
p2p/discover/table.go | 20 ++++++++++++++++++++
p2p/discover/table_test.go | 2 +-
p2p/discover/table_util_test.go | 5 ++++-
p2p/discover/v4_lookup_test.go | 6 +++---
p2p/discover/v4_udp_test.go | 2 +-
p2p/discover/v5_udp.go | 17 ++++-------------
p2p/discover/v5_udp_test.go | 8 ++++----
7 files changed, 37 insertions(+), 23 deletions(-)
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index e6dafb0dcaa8..2b7a28708b8d 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -459,6 +459,26 @@ func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *
return nodes
}
+// appendLiveNodes adds nodes at the given distance to the result slice.
+func (tab *Table) appendLiveNodes(dist uint, result []*enode.Node) []*enode.Node {
+ if dist > 256 {
+ return result
+ }
+ if dist == 0 {
+ return append(result, tab.self())
+ }
+
+ tab.mutex.Lock()
+ defer tab.mutex.Unlock()
+ for _, n := range tab.bucketAtDistance(int(dist)).entries {
+ if n.livenessChecks >= 1 {
+ node := n.Node // avoid handing out pointer to struct field
+ result = append(result, &node)
+ }
+ }
+ return result
+}
+
// len returns the number of nodes in the table.
func (tab *Table) len() (n int) {
tab.mutex.Lock()
diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go
index 2781dd4225b6..3ba342225133 100644
--- a/p2p/discover/table_test.go
+++ b/p2p/discover/table_test.go
@@ -199,7 +199,7 @@ func TestTable_findnodeByID(t *testing.T) {
tab, db := newTestTable(transport)
defer db.Close()
defer tab.close()
- fillTable(tab, test.All)
+ fillTable(tab, test.All, true)
// check that closest(Target, N) returns nodes
result := tab.findnodeByID(test.Target, test.N, false).entries
diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go
index 8f3813bdcf0f..d6309dfd6c69 100644
--- a/p2p/discover/table_util_test.go
+++ b/p2p/discover/table_util_test.go
@@ -109,8 +109,11 @@ func fillBucket(tab *Table, n *node) (last *node) {
// fillTable adds nodes the table to the end of their corresponding bucket
// if the bucket is not full. The caller must not hold tab.mutex.
-func fillTable(tab *Table, nodes []*node) {
+func fillTable(tab *Table, nodes []*node, setLive bool) {
for _, n := range nodes {
+ if setLive {
+ n.livenessChecks = 1
+ }
tab.addSeenNode(n)
}
}
diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go
index 1f9ad69d0a23..8867a5a8ac75 100644
--- a/p2p/discover/v4_lookup_test.go
+++ b/p2p/discover/v4_lookup_test.go
@@ -40,7 +40,7 @@ func TestUDPv4_Lookup(t *testing.T) {
}
// Seed table with initial node.
- fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))})
+ fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))}, true)
// Start the lookup.
resultC := make(chan []*enode.Node, 1)
@@ -74,7 +74,7 @@ func TestUDPv4_LookupIterator(t *testing.T) {
for i := range lookupTestnet.dists[256] {
bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
}
- fillTable(test.table, bootnodes)
+ fillTable(test.table, bootnodes, true)
go serveTestnet(test, lookupTestnet)
// Create the iterator and collect the nodes it yields.
@@ -109,7 +109,7 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {
for i := range lookupTestnet.dists[256] {
bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
}
- fillTable(test.table, bootnodes)
+ fillTable(test.table, bootnodes, true)
go serveTestnet(test, lookupTestnet)
it := test.udp.RandomNodes()
diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go
index 53ecb1bc6e29..361e37962648 100644
--- a/p2p/discover/v4_udp_test.go
+++ b/p2p/discover/v4_udp_test.go
@@ -269,7 +269,7 @@ func TestUDPv4_findnode(t *testing.T) {
}
nodes.push(n, numCandidates)
}
- fillTable(test.table, nodes.entries)
+ fillTable(test.table, nodes.entries, false)
// ensure there's a bond with the test node,
// findnode won't be accepted otherwise.
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index 6ba7a9061882..8b3e33d37cf7 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -851,6 +851,7 @@ func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr *ne
// collectTableNodes creates a FINDNODE result set for the given distances.
func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*enode.Node {
+ var bn []*enode.Node
var nodes []*enode.Node
var processed = make(map[uint]struct{})
for _, dist := range distances {
@@ -859,21 +860,11 @@ func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*en
if seen || dist > 256 {
continue
}
-
- // Get the nodes.
- var bn []*enode.Node
- if dist == 0 {
- bn = []*enode.Node{t.Self()}
- } else if dist <= 256 {
- t.tab.mutex.Lock()
- bn = unwrapNodes(t.tab.bucketAtDistance(int(dist)).entries)
- t.tab.mutex.Unlock()
- }
processed[dist] = struct{}{}
- // Apply some pre-checks to avoid sending invalid nodes.
- for _, n := range bn {
- // TODO livenessChecks > 1
+ for _, n := range t.tab.appendLiveNodes(dist, bn[:0]) {
+ // Apply some pre-checks to avoid sending invalid nodes.
+ // Note liveness is checked by appendLiveNodes.
if netutil.CheckRelayIP(rip, n.IP()) != nil {
continue
}
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 18d8aeac6dc7..eaa969ea8b69 100644
--- a/p2p/discover/v5_udp_test.go
+++ b/p2p/discover/v5_udp_test.go
@@ -159,9 +159,9 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16)
nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4)
nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10)
- fillTable(test.table, wrapNodes(nodes253))
- fillTable(test.table, wrapNodes(nodes249))
- fillTable(test.table, wrapNodes(nodes248))
+ fillTable(test.table, wrapNodes(nodes253), true)
+ fillTable(test.table, wrapNodes(nodes249), true)
+ fillTable(test.table, wrapNodes(nodes248), true)
// Requesting with distance zero should return the node's own record.
test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}})
@@ -589,7 +589,7 @@ func TestUDPv5_lookup(t *testing.T) {
// Seed table with initial node.
initialNode := lookupTestnet.node(256, 0)
- fillTable(test.table, []*node{wrapNode(initialNode)})
+ fillTable(test.table, []*node{wrapNode(initialNode)}, true)
// Start the lookup.
resultC := make(chan []*enode.Node, 1)
From 02766d349a14171b781b0afe083f6d898cf58c3b Mon Sep 17 00:00:00 2001
From: Sebastian Stammler
Date: Mon, 18 Dec 2023 13:28:41 +0100
Subject: [PATCH 076/103] internal/flags: add missing flag types for
auto-env-var generation (#28692)
Certain flags, such as `--rpc.txfeecap` currently do not have an env-var auto-generated for them. This change adds three missing cli flag types to the auto env-var helper function to fix this.
---
internal/flags/helpers.go | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go
index d4b8e373cc45..d9d1f790365e 100644
--- a/internal/flags/helpers.go
+++ b/internal/flags/helpers.go
@@ -105,7 +105,7 @@ func MigrateGlobalFlags(ctx *cli.Context) {
func doMigrateFlags(ctx *cli.Context) {
// Figure out if there are any aliases of commands. If there are, we want
// to ignore them when iterating over the flags.
- var aliases = make(map[string]bool)
+ aliases := make(map[string]bool)
for _, fl := range ctx.Command.Flags {
for _, alias := range fl.Names()[1:] {
aliases[alias] = true
@@ -239,15 +239,24 @@ func AutoEnvVars(flags []cli.Flag, prefix string) {
case *cli.StringFlag:
flag.EnvVars = append(flag.EnvVars, envvar)
+ case *cli.StringSliceFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
case *cli.BoolFlag:
flag.EnvVars = append(flag.EnvVars, envvar)
case *cli.IntFlag:
flag.EnvVars = append(flag.EnvVars, envvar)
+ case *cli.Int64Flag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
case *cli.Uint64Flag:
flag.EnvVars = append(flag.EnvVars, envvar)
+ case *cli.Float64Flag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
case *cli.DurationFlag:
flag.EnvVars = append(flag.EnvVars, envvar)
From 05bbc56677129c759a28330a22e1e6dc3b8ce8f5 Mon Sep 17 00:00:00 2001
From: jwasinger
Date: Mon, 18 Dec 2023 20:56:27 +0800
Subject: [PATCH 077/103] cmd/evm: default to mirror mainnet forks enabled
(#28691)
cmd/evm: default to using dev chain config (all mainnet HFs activated at block/timestamp 0
---
cmd/evm/runner.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index c9a870022a4c..f3ffb3ed9f3e 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -144,7 +144,7 @@ func runCmd(ctx *cli.Context) error {
initialGas = genesisConfig.GasLimit
}
} else {
- genesisConfig.Config = params.AllEthashProtocolChanges
+ genesisConfig.Config = params.AllDevChainProtocolChanges
}
db := rawdb.NewMemoryDatabase()
From 553bafc12720d2a3eef396cfea20f0637fb41cc4 Mon Sep 17 00:00:00 2001
From: Martin HS
Date: Mon, 18 Dec 2023 14:11:27 +0100
Subject: [PATCH 078/103] cmd/evm, cmd/clef, cmd/bootnode: fix / unify logging
(#28696)
This change fixes a problem with our non-core binaries: evm, clef, bootnode.
First of all, they failed to convert from legacy loglevels 1 to 5, to the new slog loglevels -4 to 4.
Secondly, the logging was actually setup in the init phase, and then overridden in the main. This is not needed for evm, since it used the same flag name as the main geth verbosity. Better to let the flags/internal handle the logging init.
---
cmd/bootnode/main.go | 6 +++---
cmd/clef/main.go | 4 ++--
cmd/evm/internal/t8ntool/block.go | 7 -------
cmd/evm/internal/t8ntool/transaction.go | 7 -------
cmd/evm/internal/t8ntool/transition.go | 7 -------
cmd/evm/main.go | 3 ---
6 files changed, 5 insertions(+), 29 deletions(-)
diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go
index 1660b43b74b6..350b85df1e60 100644
--- a/cmd/bootnode/main.go
+++ b/cmd/bootnode/main.go
@@ -32,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
- "golang.org/x/exp/slog"
)
func main() {
@@ -45,7 +44,7 @@ func main() {
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|pmp:|extip:)")
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
- verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)")
+ verbosity = flag.Int("verbosity", 3, "log verbosity (0-5)")
vmodule = flag.String("vmodule", "", "log verbosity pattern")
nodeKey *ecdsa.PrivateKey
@@ -54,7 +53,8 @@ func main() {
flag.Parse()
glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
- glogger.Verbosity(slog.Level(*verbosity))
+ slogVerbosity := log.FromLegacyLevel(*verbosity)
+ glogger.Verbosity(slogVerbosity)
glogger.Vmodule(*vmodule)
log.SetDefault(log.NewLogger(glogger))
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index 27b7b707714d..234699136990 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -57,7 +57,6 @@ import (
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
- "golang.org/x/exp/slog"
)
const legalWarning = `
@@ -493,7 +492,8 @@ func initialize(c *cli.Context) error {
if usecolor {
output = colorable.NewColorable(logOutput)
}
- log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, slog.Level(c.Int(logLevelFlag.Name)), usecolor)))
+ verbosity := log.FromLegacyLevel(c.Int(logLevelFlag.Name))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor)))
return nil
}
diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go
index 429ae12c54fb..a2dc4734372b 100644
--- a/cmd/evm/internal/t8ntool/block.go
+++ b/cmd/evm/internal/t8ntool/block.go
@@ -30,10 +30,8 @@ import (
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/urfave/cli/v2"
- "golang.org/x/exp/slog"
)
//go:generate go run github.com/fjl/gencodec -type header -field-override headerMarshaling -out gen_header.go
@@ -216,11 +214,6 @@ func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
// BuildBlock constructs a block from the given inputs.
func BuildBlock(ctx *cli.Context) error {
- // Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
- glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
- log.SetDefault(log.NewLogger(glogger))
-
baseDir, err := createBasedir(ctx)
if err != nil {
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go
index e1c98c7fe265..8533b7863769 100644
--- a/cmd/evm/internal/t8ntool/transaction.go
+++ b/cmd/evm/internal/t8ntool/transaction.go
@@ -28,12 +28,10 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2"
- "golang.org/x/exp/slog"
)
type result struct {
@@ -66,11 +64,6 @@ func (r *result) MarshalJSON() ([]byte, error) {
}
func Transaction(ctx *cli.Context) error {
- // Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
- glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
- log.SetDefault(log.NewLogger(glogger))
-
var (
err error
)
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index a01dfedab9f6..0a9c555cff2d 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -24,8 +24,6 @@ import (
"os"
"path"
- "golang.org/x/exp/slog"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
@@ -82,11 +80,6 @@ type input struct {
}
func Transition(ctx *cli.Context) error {
- // Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
- glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
- log.SetDefault(log.NewLogger(glogger))
-
var (
err error
tracer vm.EVMLogger
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index ef5d25418d52..f4868568054e 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -158,7 +158,6 @@ var stateTransitionCommand = &cli.Command{
t8ntool.ForknameFlag,
t8ntool.ChainIDFlag,
t8ntool.RewardFlag,
- t8ntool.VerbosityFlag,
},
}
@@ -171,7 +170,6 @@ var transactionCommand = &cli.Command{
t8ntool.InputTxsFlag,
t8ntool.ChainIDFlag,
t8ntool.ForknameFlag,
- t8ntool.VerbosityFlag,
},
}
@@ -188,7 +186,6 @@ var blockBuilderCommand = &cli.Command{
t8ntool.InputWithdrawalsFlag,
t8ntool.InputTxsRlpFlag,
t8ntool.SealCliqueFlag,
- t8ntool.VerbosityFlag,
},
}
From c18c5c3d9297195a6f6b05076ae7940d45ab6846 Mon Sep 17 00:00:00 2001
From: Delweng
Date: Mon, 18 Dec 2023 22:16:25 +0800
Subject: [PATCH 079/103] cmd/evm: t8n support custom tracers (#28557)
This change implements ability for the `evm t8n` tool to use custom tracers; either 'native' golang tracers or javascript tracers.
---
cmd/evm/internal/t8ntool/execution.go | 2 +-
cmd/evm/internal/t8ntool/flags.go | 18 +++---
cmd/evm/internal/t8ntool/tracewriter.go | 81 +++++++++++++++++++++++++
cmd/evm/internal/t8ntool/transition.go | 61 +++++++------------
cmd/evm/main.go | 8 ++-
5 files changed, 119 insertions(+), 51 deletions(-)
create mode 100644 cmd/evm/internal/t8ntool/tracewriter.go
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index 5cac5f07f886..a4ffd09e4fef 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -117,7 +117,7 @@ type rejectedTx struct {
// Apply applies a set of transactions to a pre-state
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txIt txIterator, miningReward int64,
- getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, []byte, error) {
+ getTracerFn func(txIndex int, txHash common.Hash) (vm.EVMLogger, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
// required blockhashes
var hashError error
diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go
index de19dbc851ef..c2eca8cc217d 100644
--- a/cmd/evm/internal/t8ntool/flags.go
+++ b/cmd/evm/internal/t8ntool/flags.go
@@ -28,12 +28,15 @@ import (
var (
TraceFlag = &cli.BoolFlag{
Name: "trace",
- Usage: "Output full trace logs to files .jsonl",
+ Usage: "Configures the use of the JSON opcode tracer. This tracer emits traces to files as trace--.jsonl",
}
- TraceDisableMemoryFlag = &cli.BoolFlag{
- Name: "trace.nomemory",
- Value: true,
- Usage: "Disable full memory dump in traces (deprecated)",
+ TraceTracerFlag = &cli.StringFlag{
+ Name: "trace.tracer",
+ Usage: "Configures the use of a custom tracer, e.g native or js tracers. Examples are callTracer and 4byteTracer. These tracers emit results into files as trace--.json",
+ }
+ TraceTracerConfigFlag = &cli.StringFlag{
+ Name: "trace.jsonconfig",
+ Usage: "The configurations for the custom tracer specified by --trace.tracer. If provided, must be in JSON format",
}
TraceEnableMemoryFlag = &cli.BoolFlag{
Name: "trace.memory",
@@ -43,11 +46,6 @@ var (
Name: "trace.nostack",
Usage: "Disable stack output in traces",
}
- TraceDisableReturnDataFlag = &cli.BoolFlag{
- Name: "trace.noreturndata",
- Value: true,
- Usage: "Disable return data output in traces (deprecated)",
- }
TraceEnableReturnDataFlag = &cli.BoolFlag{
Name: "trace.returndata",
Usage: "Enable return data output in traces",
diff --git a/cmd/evm/internal/t8ntool/tracewriter.go b/cmd/evm/internal/t8ntool/tracewriter.go
new file mode 100644
index 000000000000..e4efad112f74
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/tracewriter.go
@@ -0,0 +1,81 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package t8ntool
+
+import (
+ "encoding/json"
+ "io"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// traceWriter is an vm.EVMLogger which also holds an inner logger/tracer.
+// When the TxEnd event happens, the inner tracer result is written to the file, and
+// the file is closed.
+type traceWriter struct {
+ inner vm.EVMLogger
+ f io.WriteCloser
+}
+
+// Compile-time interface check
+var _ = vm.EVMLogger((*traceWriter)(nil))
+
+func (t *traceWriter) CaptureTxEnd(restGas uint64) {
+ t.inner.CaptureTxEnd(restGas)
+ defer t.f.Close()
+
+ if tracer, ok := t.inner.(tracers.Tracer); ok {
+ result, err := tracer.GetResult()
+ if err != nil {
+ log.Warn("Error in tracer", "err", err)
+ return
+ }
+ err = json.NewEncoder(t.f).Encode(result)
+ if err != nil {
+ log.Warn("Error writing tracer output", "err", err)
+ return
+ }
+ }
+}
+
+func (t *traceWriter) CaptureTxStart(gasLimit uint64) { t.inner.CaptureTxStart(gasLimit) }
+func (t *traceWriter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.inner.CaptureStart(env, from, to, create, input, gas, value)
+}
+
+func (t *traceWriter) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ t.inner.CaptureEnd(output, gasUsed, err)
+}
+
+func (t *traceWriter) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ t.inner.CaptureEnter(typ, from, to, input, gas, value)
+}
+
+func (t *traceWriter) CaptureExit(output []byte, gasUsed uint64, err error) {
+ t.inner.CaptureExit(output, gasUsed, err)
+}
+
+func (t *traceWriter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ t.inner.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
+}
+func (t *traceWriter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+ t.inner.CaptureFault(pc, op, gas, cost, scope, depth, err)
+}
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 0a9c555cff2d..c8ba69f40f42 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
@@ -80,57 +81,43 @@ type input struct {
}
func Transition(ctx *cli.Context) error {
- var (
- err error
- tracer vm.EVMLogger
- )
- var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error)
+ var getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { return nil, nil }
baseDir, err := createBasedir(ctx)
if err != nil {
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
}
- if ctx.Bool(TraceFlag.Name) {
- if ctx.IsSet(TraceDisableMemoryFlag.Name) && ctx.IsSet(TraceEnableMemoryFlag.Name) {
- return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
- }
- if ctx.IsSet(TraceDisableReturnDataFlag.Name) && ctx.IsSet(TraceEnableReturnDataFlag.Name) {
- return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
- }
- if ctx.IsSet(TraceDisableMemoryFlag.Name) {
- log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
- }
- if ctx.IsSet(TraceDisableReturnDataFlag.Name) {
- log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
- }
+
+ if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
// Configure the EVM logger
logConfig := &logger.Config{
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
- EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name) || ctx.Bool(TraceEnableMemoryFlag.Name),
- EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name) || ctx.Bool(TraceEnableReturnDataFlag.Name),
+ EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name),
+ EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
Debug: true,
}
- var prevFile *os.File
- // This one closes the last file
- defer func() {
- if prevFile != nil {
- prevFile.Close()
- }
- }()
getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
- if prevFile != nil {
- prevFile.Close()
- }
traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
if err != nil {
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
}
- prevFile = traceFile
- return logger.NewJSONLogger(logConfig, traceFile), nil
+ return &traceWriter{logger.NewJSONLogger(logConfig, traceFile), traceFile}, nil
}
- } else {
- getTracer = func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error) {
- return nil, nil
+ } else if ctx.IsSet(TraceTracerFlag.Name) {
+ var config json.RawMessage
+ if ctx.IsSet(TraceTracerConfigFlag.Name) {
+ config = []byte(ctx.String(TraceTracerConfigFlag.Name))
+ }
+ getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
+ traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
+ if err != nil {
+ return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
+ }
+ tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config)
+ if err != nil {
+ return nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
+ }
+ return &traceWriter{tracer, traceFile}, nil
}
}
// We need to load three things: alloc, env and transactions. May be either in
@@ -169,9 +156,7 @@ func Transition(ctx *cli.Context) error {
}
prestate.Env = *inputData.Env
- vmConfig := vm.Config{
- Tracer: tracer,
- }
+ vmConfig := vm.Config{}
// Construct the chainconfig
var chainConfig *params.ChainConfig
if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index f4868568054e..c3e6a4af91ba 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -26,6 +26,10 @@ import (
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/urfave/cli/v2"
+
+ // Force-load the tracer engines to trigger registration
+ _ "github.com/ethereum/go-ethereum/eth/tracers/js"
+ _ "github.com/ethereum/go-ethereum/eth/tracers/native"
)
var (
@@ -143,10 +147,10 @@ var stateTransitionCommand = &cli.Command{
Action: t8ntool.Transition,
Flags: []cli.Flag{
t8ntool.TraceFlag,
- t8ntool.TraceDisableMemoryFlag,
+ t8ntool.TraceTracerFlag,
+ t8ntool.TraceTracerConfigFlag,
t8ntool.TraceEnableMemoryFlag,
t8ntool.TraceDisableStackFlag,
- t8ntool.TraceDisableReturnDataFlag,
t8ntool.TraceEnableReturnDataFlag,
t8ntool.OutputBasedir,
t8ntool.OutputAllocFlag,
From a18b845ecda84968125c09f054deb49773cd8cfe Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Mon, 18 Dec 2023 18:53:47 +0100
Subject: [PATCH 080/103] params: release go-ethereum v1.13.6 stable
---
params/version.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/params/version.go b/params/version.go
index bcab461a4358..636f95bad8eb 100644
--- a/params/version.go
+++ b/params/version.go
@@ -21,10 +21,10 @@ import (
)
const (
- VersionMajor = 1 // Major version component of the current release
- VersionMinor = 13 // Minor version component of the current release
- VersionPatch = 5 // Patch version component of the current release
- VersionMeta = "unstable" // Version metadata to append to the version string
+ VersionMajor = 1 // Major version component of the current release
+ VersionMinor = 13 // Minor version component of the current release
+ VersionPatch = 6 // Patch version component of the current release
+ VersionMeta = "stable" // Version metadata to append to the version string
)
// Version holds the textual version string.
From 3fc3de0b8c497d475d3dcca9be9d779ee6df3055 Mon Sep 17 00:00:00 2001
From: Jerry
Date: Thu, 28 Mar 2024 17:16:33 -0700
Subject: [PATCH 081/103] Fix build errors
---
cmd/geth/config.go | 1 +
go.mod | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index 717212410101..94aa99473843 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/eth/catalyst"
+ "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
diff --git a/go.mod b/go.mod
index 23e7f8a25f13..bc77f54d369c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/ethereum/go-ethereum
-go 1.21
+go 1.22
toolchain go1.22.1
From 98a2f47f9279fbb506be3685d829076bb62f64d1 Mon Sep 17 00:00:00 2001
From: Jerry
Date: Thu, 28 Mar 2024 20:30:01 -0700
Subject: [PATCH 082/103] Fix test-integration
---
tests/bor/bor_milestone_test.go | 24 +++++++++----------
.../bor_sprint_length_milestone_merge_test.go | 8 +++----
tests/bor/bor_test.go | 2 +-
3 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/tests/bor/bor_milestone_test.go b/tests/bor/bor_milestone_test.go
index 0dd0f310fd61..c39c77e61255 100644
--- a/tests/bor/bor_milestone_test.go
+++ b/tests/bor/bor_milestone_test.go
@@ -42,7 +42,7 @@ const (
)
func TestMiningAfterLocking(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -151,7 +151,7 @@ func TestReorgingAfterLockingSprint(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -271,7 +271,7 @@ func TestReorgingAfterWhitelisting(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -384,7 +384,7 @@ func TestPeerConnectionAfterWhitelisting(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -506,7 +506,7 @@ func TestReorgingFutureSprintAfterLocking(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -594,7 +594,7 @@ func TestReorgingFutureSprintAfterLockingOnSameHash(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -683,7 +683,7 @@ func TestReorgingAfterLockingOnDifferentHash(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -803,7 +803,7 @@ func TestReorgingAfterWhitelistingOnDifferentHash(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -925,7 +925,7 @@ func TestNonMinerNodeWithWhitelisting(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -1022,7 +1022,7 @@ func TestNonMinerNodeWithTryToLock(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -1114,7 +1114,7 @@ func TestRewind(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -1230,7 +1230,7 @@ func TestRewinding(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
diff --git a/tests/bor/bor_sprint_length_milestone_merge_test.go b/tests/bor/bor_sprint_length_milestone_merge_test.go
index c9105586b6f0..d94ded34da36 100644
--- a/tests/bor/bor_sprint_length_milestone_merge_test.go
+++ b/tests/bor/bor_sprint_length_milestone_merge_test.go
@@ -195,7 +195,7 @@ func TestSprintLengthMilestoneReorg2Nodes(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -242,7 +242,7 @@ func TestSprintLengthMilestoneReorg(t *testing.T) {
t.Skip()
// t.Parallel()
- log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -319,7 +319,7 @@ func SprintLengthMilestoneReorgIndividual2NodesHelper(t *testing.T, index int, t
func SetupValidatorsAndTest2NodesSprintLengthMilestone(t *testing.T, tt map[string]interface{}) (uint64, uint64) {
t.Helper()
- log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
@@ -461,7 +461,7 @@ func SetupValidatorsAndTest2NodesSprintLengthMilestone(t *testing.T, tt map[stri
func SetupValidatorsAndTestSprintLengthMilestone(t *testing.T, tt map[string]uint64) (uint64, uint64) {
t.Helper()
- log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
_, err := fdlimit.Raise(2048)
diff --git a/tests/bor/bor_test.go b/tests/bor/bor_test.go
index 264f5967a895..f6500fd5f231 100644
--- a/tests/bor/bor_test.go
+++ b/tests/bor/bor_test.go
@@ -53,7 +53,7 @@ var (
func TestValidatorWentOffline(t *testing.T) {
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
fdlimit.Raise(2048)
// Generate a batch of accounts to seal and fund with
From e28c2036fce7279b223bc1834a9726cf81b2776f Mon Sep 17 00:00:00 2001
From: Anshal Shukla <53994948+anshalshukla@users.noreply.github.com>
Date: Fri, 7 Jun 2024 01:43:43 +0800
Subject: [PATCH 083/103] remove additional bootnodes (#1255)
---
builder/files/config.toml | 2 +-
internal/cli/server/chains/mainnet.go | 2 --
packaging/templates/mainnet-v1/archive/config.toml | 2 +-
packaging/templates/mainnet-v1/sentry/validator/bor/config.toml | 2 +-
params/bootnodes.go | 2 --
5 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/builder/files/config.toml b/builder/files/config.toml
index 5700655966fc..41281bc7e066 100644
--- a/builder/files/config.toml
+++ b/builder/files/config.toml
@@ -40,7 +40,7 @@ syncmode = "full"
[p2p.discovery]
# v4disc = true
# v5disc = false
- bootnodes = ["enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303", "enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303", "enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303"]
+ bootnodes = ["enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303"]
# Uncomment below `bootnodes` field for Mumbai bootnode
# bootnodes = ["enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303", "enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303"]
# bootnodesv4 = []
diff --git a/internal/cli/server/chains/mainnet.go b/internal/cli/server/chains/mainnet.go
index f6c06b880719..35f57f762c50 100644
--- a/internal/cli/server/chains/mainnet.go
+++ b/internal/cli/server/chains/mainnet.go
@@ -95,7 +95,5 @@ var mainnetBor = &Chain{
Bootnodes: []string{
"enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303",
"enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303",
- "enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303",
- "enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303",
},
}
diff --git a/packaging/templates/mainnet-v1/archive/config.toml b/packaging/templates/mainnet-v1/archive/config.toml
index dce75da42621..9be166bac817 100644
--- a/packaging/templates/mainnet-v1/archive/config.toml
+++ b/packaging/templates/mainnet-v1/archive/config.toml
@@ -37,7 +37,7 @@ gcmode = "archive"
# [p2p.discovery]
# v4disc = true
# v5disc = false
- # bootnodes = ["enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303", "enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303", "enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303"]
+ # bootnodes = ["enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303"]
# bootnodesv4 = []
# bootnodesv5 = []
# static-nodes = []
diff --git a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
index 0171acfc429b..b9ad57f5c232 100644
--- a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
+++ b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
@@ -39,7 +39,7 @@ syncmode = "full"
# [p2p.discovery]
# v4disc = true
# v5disc = false
- # bootnodes = ["enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303", "enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303", "enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303"]
+ # bootnodes = ["enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303"]
# bootnodesv4 = []
# bootnodesv5 = []
# static-nodes = [":"] # Recommended setting with sentry <> validator architecture
diff --git a/params/bootnodes.go b/params/bootnodes.go
index e12d1540b8c9..651ccd75e6ea 100644
--- a/params/bootnodes.go
+++ b/params/bootnodes.go
@@ -87,8 +87,6 @@ var AmoyBootnodes = []string{
var BorMainnetBootnodes = []string{
"enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303",
"enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303",
- "enode://76316d1cb93c8ed407d3332d595233401250d48f8fbb1d9c65bd18c0495eca1b43ec38ee0ea1c257c0abb7d1f25d649d359cdfe5a805842159cfe36c5f66b7e8@52.78.36.216:30303",
- "enode://681ebac58d8dd2d8a6eef15329dfbad0ab960561524cf2dfde40ad646736fe5c244020f20b87e7c1520820bc625cfb487dd71d63a3a3bf0baea2dbb8ec7c79f1@34.240.245.39:30303",
}
var KilnBootnodes = []string{
From 1351a54287c1d83766c95f2492695622e0227467 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Thu, 23 May 2024 14:26:09 +0200
Subject: [PATCH 084/103] p2p/discover: improved node revalidation (#29572)
Node discovery periodically revalidates the nodes in its table by sending PING, checking
if they are still alive. I recently noticed some issues with the implementation of this
process, which can cause strange results such as nodes dropping unexpectedly, certain
nodes not getting revalidated often enough, and bad results being returned to incoming
FINDNODE queries.
In this change, the revalidation process is improved with the following logic:
- We maintain two 'revalidation lists' containing the table nodes, named 'fast' and 'slow'.
- The process chooses random nodes from each list on a randomized interval, the interval being
faster for the 'fast' list, and performs revalidation for the chosen node.
- Whenever a node is newly inserted into the table, it goes into the 'fast' list.
Once validation passes, it transfers to the 'slow' list. If a request fails, or the
node changes endpoint, it transfers back into 'fast'.
- livenessChecks is incremented by one for successful checks. Unlike the old implementation,
we will not drop the node on the first failing check. We instead quickly decay the
livenessChecks give it another chance.
- Order of nodes in bucket doesn't matter anymore.
I am also adding a debug API endpoint to dump the node table content.
Co-authored-by: Martin HS
---
cmd/devp2p/discv4cmd.go | 57 +++
internal/testlog/testlog.go | 2 +-
node/api.go | 17 +
p2p/discover/common.go | 49 ++-
p2p/discover/lookup.go | 32 +-
p2p/discover/node.go | 20 +-
p2p/discover/table.go | 726 +++++++++++++++-----------------
p2p/discover/table_reval.go | 223 ++++++++++
p2p/discover/table_test.go | 190 +++++----
p2p/discover/table_util_test.go | 119 +++++-
p2p/discover/v4_udp.go | 10 +-
p2p/discover/v4_udp_test.go | 2 +-
p2p/discover/v5_udp.go | 4 +-
p2p/discover/v5_udp_test.go | 2 +-
p2p/server.go | 38 +-
15 files changed, 921 insertions(+), 570 deletions(-)
create mode 100644 p2p/discover/table_reval.go
diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go
index 86dbf9fe0b10..007b442ec379 100644
--- a/cmd/devp2p/discv4cmd.go
+++ b/cmd/devp2p/discv4cmd.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"net"
+ "net/http"
"strconv"
"strings"
"time"
@@ -30,9 +31,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/flags"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
)
var (
@@ -46,6 +49,7 @@ var (
discv4ResolveJSONCommand,
discv4CrawlCommand,
discv4TestCommand,
+ discv4ListenCommand,
},
}
discv4PingCommand = &cli.Command{
@@ -76,6 +80,14 @@ var (
Flags: discoveryNodeFlags,
ArgsUsage: "",
}
+ discv4ListenCommand = &cli.Command{
+ Name: "listen",
+ Usage: "Runs a discovery node",
+ Action: discv4Listen,
+ Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{
+ httpAddrFlag,
+ }),
+ }
discv4CrawlCommand = &cli.Command{
Name: "crawl",
Usage: "Updates a nodes.json file with random nodes found in the DHT",
@@ -132,6 +144,10 @@ var (
Usage: "Enode of the remote node under test",
EnvVars: []string{"REMOTE_ENODE"},
}
+ httpAddrFlag = &cli.StringFlag{
+ Name: "rpc",
+ Usage: "HTTP server listening address",
+ }
)
var discoveryNodeFlags = []cli.Flag{
@@ -158,6 +174,27 @@ func discv4Ping(ctx *cli.Context) error {
return nil
}
+func discv4Listen(ctx *cli.Context) error {
+ disc, _ := startV4(ctx)
+ defer disc.Close()
+
+ fmt.Println(disc.Self())
+
+ httpAddr := ctx.String(httpAddrFlag.Name)
+ if httpAddr == "" {
+ // Non-HTTP mode.
+ select {}
+ }
+
+ api := &discv4API{disc}
+ log.Info("Starting RPC API server", "addr", httpAddr)
+ srv := rpc.NewServer("", 0, 0)
+ srv.RegisterName("discv4", api)
+ http.DefaultServeMux.Handle("/", srv)
+ httpsrv := http.Server{Addr: httpAddr, Handler: http.DefaultServeMux}
+ return httpsrv.ListenAndServe()
+}
+
func discv4RequestRecord(ctx *cli.Context) error {
n := getNodeArg(ctx)
disc, _ := startV4(ctx)
@@ -401,3 +438,23 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
return nodes, nil
}
+
+type discv4API struct {
+ host *discover.UDPv4
+}
+
+func (api *discv4API) LookupRandom(n int) (ns []*enode.Node) {
+ it := api.host.RandomNodes()
+ for len(ns) < n && it.Next() {
+ ns = append(ns, it.Node())
+ }
+ return ns
+}
+
+func (api *discv4API) Buckets() [][]discover.BucketNode {
+ return api.host.TableBuckets()
+}
+
+func (api *discv4API) Self() *enode.Node {
+ return api.host.Self()
+}
diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go
index 309f42a0b699..09fa90cd7dad 100644
--- a/internal/testlog/testlog.go
+++ b/internal/testlog/testlog.go
@@ -55,7 +55,7 @@ func (h *bufHandler) Handle(_ context.Context, r slog.Record) error {
}
func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool {
- return lvl <= h.level
+ return lvl >= h.level
}
func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
diff --git a/node/api.go b/node/api.go
index c66a66ab1123..c8817917a063 100644
--- a/node/api.go
+++ b/node/api.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -40,6 +41,9 @@ func (n *Node) apis() []rpc.API {
}, {
Namespace: "debug",
Service: debug.Handler,
+ }, {
+ Namespace: "debug",
+ Service: &p2pDebugAPI{n},
}, {
Namespace: "web3",
Service: &web3API{n},
@@ -477,3 +481,16 @@ func (api *adminAPI) SetHttpExecutionPoolSize(n int) *ExecutionPoolSize {
return api.GetExecutionPoolSize()
}
+
+// p2pDebugAPI provides access to p2p internals for debugging.
+type p2pDebugAPI struct {
+ stack *Node
+}
+
+func (s *p2pDebugAPI) DiscoveryV4Table() [][]discover.BucketNode {
+ disc := s.stack.server.DiscoveryV4()
+ if disc != nil {
+ return disc.TableBuckets()
+ }
+ return nil
+}
diff --git a/p2p/discover/common.go b/p2p/discover/common.go
index 18001bffb954..298d6436785c 100644
--- a/p2p/discover/common.go
+++ b/p2p/discover/common.go
@@ -18,7 +18,11 @@ package discover
import (
"crypto/ecdsa"
+ crand "crypto/rand"
+ "encoding/binary"
+ "math/rand"
"net"
+ "sync"
"time"
"github.com/ethereum/go-ethereum/common/mclock"
@@ -62,7 +66,7 @@ type Config struct {
func (cfg Config) withDefaults() Config {
// Node table configuration:
if cfg.PingInterval == 0 {
- cfg.PingInterval = 10 * time.Second
+ cfg.PingInterval = 3 * time.Second
}
if cfg.RefreshInterval == 0 {
cfg.RefreshInterval = 30 * time.Minute
@@ -96,10 +100,43 @@ type ReadPacket struct {
Addr *net.UDPAddr
}
-func min(x, y int) int {
- if x > y {
- return y
- }
+type randomSource interface {
+ Intn(int) int
+ Int63n(int64) int64
+ Shuffle(int, func(int, int))
+}
+
+// reseedingRandom is a random number generator that tracks when it was last re-seeded.
+type reseedingRandom struct {
+ mu sync.Mutex
+ cur *rand.Rand
+}
+
+func (r *reseedingRandom) seed() {
+ var b [8]byte
+ crand.Read(b[:])
+ seed := binary.BigEndian.Uint64(b[:])
+ new := rand.New(rand.NewSource(int64(seed)))
+
+ r.mu.Lock()
+ r.cur = new
+ r.mu.Unlock()
+}
+
+func (r *reseedingRandom) Intn(n int) int {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.cur.Intn(n)
+}
+
+func (r *reseedingRandom) Int63n(n int64) int64 {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.cur.Int63n(n)
+}
- return x
+func (r *reseedingRandom) Shuffle(n int, swap func(i, j int)) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ r.cur.Shuffle(n, swap)
}
diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go
index 2e284005a953..aec3b5086bb9 100644
--- a/p2p/discover/lookup.go
+++ b/p2p/discover/lookup.go
@@ -148,35 +148,13 @@ func (it *lookup) slowdown() {
}
func (it *lookup) query(n *node, reply chan<- []*node) {
- fails := it.tab.db.FindFails(n.ID(), n.IP())
r, err := it.queryfunc(n)
-
- if errors.Is(err, errClosed) {
- // Avoid recording failures on shutdown.
- reply <- nil
- return
- } else if len(r) == 0 {
- fails++
- it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails)
- // Remove the node from the local table if it fails to return anything useful too
- // many times, but only if there are enough other nodes in the bucket.
- dropped := false
- if fails >= maxFindnodeFailures && it.tab.bucketLen(n.ID()) >= bucketSize/2 {
- dropped = true
-
- it.tab.delete(n)
+ if !errors.Is(err, errClosed) { // avoid recording failures on shutdown.
+ success := len(r) > 0
+ it.tab.trackRequest(n, success, r)
+ if err != nil {
+ it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "err", err)
}
-
- it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "failcount", fails, "dropped", dropped, "err", err)
- } else if fails > 0 {
- // Reset failure counter because it counts _consecutive_ failures.
- it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0)
- }
-
- // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll
- // just remove those again during revalidation.
- for _, n := range r {
- it.tab.addSeenNode(n)
}
reply <- r
}
diff --git a/p2p/discover/node.go b/p2p/discover/node.go
index 22f8fb0bb620..8f7f9b50ef79 100644
--- a/p2p/discover/node.go
+++ b/p2p/discover/node.go
@@ -29,12 +29,22 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
)
+type BucketNode struct {
+ Node *enode.Node `json:"node"`
+ AddedToTable time.Time `json:"addedToTable"`
+ AddedToBucket time.Time `json:"addedToBucket"`
+ Checks int `json:"checks"`
+ Live bool `json:"live"`
+}
+
// node represents a host on the network.
// The fields of Node may not be modified.
type node struct {
- enode.Node
- addedAt time.Time // time when the node was added to the table
- livenessChecks uint // how often liveness was checked
+ *enode.Node
+ addedToTable time.Time // first time node was added to bucket or replacement list
+ addedToBucket time.Time // time it was added in the actual bucket
+ livenessChecks uint // how often liveness was checked
+ isValidatedLive bool // true if existence of node is considered validated right now
}
type encPubkey [64]byte
@@ -70,7 +80,7 @@ func (e encPubkey) id() enode.ID {
}
func wrapNode(n *enode.Node) *node {
- return &node{Node: *n}
+ return &node{Node: n}
}
func wrapNodes(ns []*enode.Node) []*node {
@@ -83,7 +93,7 @@ func wrapNodes(ns []*enode.Node) []*node {
}
func unwrapNode(n *node) *enode.Node {
- return &n.Node
+ return n.Node
}
func unwrapNodes(ns []*node) []*enode.Node {
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index 0c46742a9875..d5ae9c10f902 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -24,16 +24,15 @@ package discover
import (
"context"
- crand "crypto/rand"
- "encoding/binary"
"fmt"
- mrand "math/rand"
"net"
+ "slices"
"sort"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p/enode"
@@ -55,21 +54,21 @@ const (
bucketIPLimit, bucketSubnet = 2, 24 // at most 2 addresses from the same /24
tableIPLimit, tableSubnet = 10, 24
- copyNodesInterval = 30 * time.Second
- seedMinTableTime = 5 * time.Minute
- seedCount = 30
- seedMaxAge = 5 * 24 * time.Hour
+ seedMinTableTime = 5 * time.Minute
+ seedCount = 30
+ seedMaxAge = 5 * 24 * time.Hour
)
// Table is the 'node table', a Kademlia-like index of neighbor nodes. The table keeps
// itself up-to-date by verifying the liveness of neighbors and requesting their node
// records when announcements of a new record version are received.
type Table struct {
- mutex sync.Mutex // protects buckets, bucket content, nursery, rand
- buckets [nBuckets]*bucket // index of known nodes by distance
- nursery []*node // bootstrap nodes
- rand *mrand.Rand // source of randomness, periodically reseeded
- ips netutil.DistinctNetSet
+ mutex sync.Mutex // protects buckets, bucket content, nursery, rand
+ buckets [nBuckets]*bucket // index of known nodes by distance
+ nursery []*node // bootstrap nodes
+ rand reseedingRandom // source of randomness, periodically reseeded
+ ips netutil.DistinctNetSet
+ revalidation tableRevalidation
db *enode.DB // database of known nodes
net transport
@@ -77,10 +76,14 @@ type Table struct {
log log.Logger
// loop channels
- refreshReq chan chan struct{}
- initDone chan struct{}
- closeReq chan struct{}
- closed chan struct{}
+ refreshReq chan chan struct{}
+ revalResponseCh chan revalidationResponse
+ addNodeCh chan addNodeOp
+ addNodeHandled chan bool
+ trackRequestCh chan trackRequestOp
+ initDone chan struct{}
+ closeReq chan struct{}
+ closed chan struct{}
nodeAddedHook func(*bucket, *node)
nodeRemovedHook func(*bucket, *node)
@@ -104,22 +107,33 @@ type bucket struct {
index int
}
+type addNodeOp struct {
+ node *node
+ isInbound bool
+}
+
+type trackRequestOp struct {
+ node *node
+ foundNodes []*node
+ success bool
+}
+
func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
cfg = cfg.withDefaults()
tab := &Table{
- net: t,
- db: db,
- cfg: cfg,
- log: cfg.Log,
- refreshReq: make(chan chan struct{}),
- initDone: make(chan struct{}),
- closeReq: make(chan struct{}),
- closed: make(chan struct{}),
- rand: mrand.New(mrand.NewSource(0)),
- ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
- }
- if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
- return nil, err
+ net: t,
+ db: db,
+ cfg: cfg,
+ log: cfg.Log,
+ refreshReq: make(chan chan struct{}),
+ revalResponseCh: make(chan revalidationResponse),
+ addNodeCh: make(chan addNodeOp),
+ addNodeHandled: make(chan bool),
+ trackRequestCh: make(chan trackRequestOp),
+ initDone: make(chan struct{}),
+ closeReq: make(chan struct{}),
+ closed: make(chan struct{}),
+ ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
}
for i := range tab.buckets {
@@ -128,42 +142,34 @@ func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit},
}
}
+ tab.rand.seed()
+ tab.revalidation.init(&cfg)
- tab.seedRand()
- tab.loadSeedNodes()
-
- return tab, nil
-}
-
-func newMeteredTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
- tab, err := newTable(t, db, cfg)
- if err != nil {
+ // initial table content
+ if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
return nil, err
}
- if metrics.Enabled {
- tab.nodeAddedHook = func(b *bucket, n *node) {
- bucketsCounter[b.index].Inc(1)
- }
- tab.nodeRemovedHook = func(b *bucket, n *node) {
- bucketsCounter[b.index].Dec(1)
- }
- }
+ tab.loadSeedNodes()
+
return tab, nil
}
// Nodes returns all nodes contained in the table.
-func (tab *Table) Nodes() []*enode.Node {
- if !tab.isInitDone() {
- return nil
- }
-
+func (tab *Table) Nodes() [][]BucketNode {
tab.mutex.Lock()
defer tab.mutex.Unlock()
- var nodes []*enode.Node
- for _, b := range &tab.buckets {
- for _, n := range b.entries {
- nodes = append(nodes, unwrapNode(n))
+ nodes := make([][]BucketNode, len(tab.buckets))
+ for i, b := range &tab.buckets {
+ nodes[i] = make([]BucketNode, len(b.entries))
+ for j, n := range b.entries {
+ nodes[i][j] = BucketNode{
+ Node: n.Node,
+ Checks: int(n.livenessChecks),
+ Live: n.isValidatedLive,
+ AddedToTable: n.addedToTable,
+ AddedToBucket: n.addedToBucket,
+ }
}
}
return nodes
@@ -173,16 +179,6 @@ func (tab *Table) self() *enode.Node {
return tab.net.Self()
}
-func (tab *Table) seedRand() {
- var b [8]byte
-
- crand.Read(b[:])
-
- tab.mutex.Lock()
- tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:])))
- tab.mutex.Unlock()
-}
-
// getNode returns the node with the given ID or nil if it isn't in the table.
func (tab *Table) getNode(id enode.ID) *enode.Node {
tab.mutex.Lock()
@@ -244,53 +240,174 @@ func (tab *Table) refresh() <-chan struct{} {
return done
}
-// loop schedules runs of doRefresh, doRevalidate and copyLiveNodes.
+// findnodeByID returns the n nodes in the table that are closest to the given id.
+// This is used by the FINDNODE/v4 handler.
+//
+// The preferLive parameter says whether the caller wants liveness-checked results. If
+// preferLive is true and the table contains any verified nodes, the result will not
+// contain unverified nodes. However, if there are no verified nodes at all, the result
+// will contain unverified nodes.
+func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance {
+ tab.mutex.Lock()
+ defer tab.mutex.Unlock()
+
+ // Scan all buckets. There might be a better way to do this, but there aren't that many
+ // buckets, so this solution should be fine. The worst-case complexity of this loop
+ // is O(tab.len() * nresults).
+ nodes := &nodesByDistance{target: target}
+ liveNodes := &nodesByDistance{target: target}
+ for _, b := range &tab.buckets {
+ for _, n := range b.entries {
+ nodes.push(n, nresults)
+ if preferLive && n.isValidatedLive {
+ liveNodes.push(n, nresults)
+ }
+ }
+ }
+
+ if preferLive && len(liveNodes.entries) > 0 {
+ return liveNodes
+ }
+ return nodes
+}
+
+// appendLiveNodes adds nodes at the given distance to the result slice.
+// This is used by the FINDNODE/v5 handler.
+func (tab *Table) appendLiveNodes(dist uint, result []*enode.Node) []*enode.Node {
+ if dist > 256 {
+ return result
+ }
+ if dist == 0 {
+ return append(result, tab.self())
+ }
+
+ tab.mutex.Lock()
+ for _, n := range tab.bucketAtDistance(int(dist)).entries {
+ if n.isValidatedLive {
+ result = append(result, n.Node)
+ }
+ }
+ tab.mutex.Unlock()
+
+ // Shuffle result to avoid always returning same nodes in FINDNODE/v5.
+ tab.rand.Shuffle(len(result), func(i, j int) {
+ result[i], result[j] = result[j], result[i]
+ })
+ return result
+}
+
+// len returns the number of nodes in the table.
+func (tab *Table) len() (n int) {
+ tab.mutex.Lock()
+ defer tab.mutex.Unlock()
+
+ for _, b := range &tab.buckets {
+ n += len(b.entries)
+ }
+ return n
+}
+
+// addFoundNode adds a node which may not be live. If the bucket has space available,
+// adding the node succeeds immediately. Otherwise, the node is added to the replacements
+// list.
+//
+// The caller must not hold tab.mutex.
+func (tab *Table) addFoundNode(n *node) bool {
+ op := addNodeOp{node: n, isInbound: false}
+ select {
+ case tab.addNodeCh <- op:
+ return <-tab.addNodeHandled
+ case <-tab.closeReq:
+ return false
+ }
+}
+
+// addInboundNode adds a node from an inbound contact. If the bucket has no space, the
+// node is added to the replacements list.
+//
+// There is an additional safety measure: if the table is still initializing the node is
+// not added. This prevents an attack where the table could be filled by just sending ping
+// repeatedly.
+//
+// The caller must not hold tab.mutex.
+func (tab *Table) addInboundNode(n *node) bool {
+ op := addNodeOp{node: n, isInbound: true}
+ select {
+ case tab.addNodeCh <- op:
+ return <-tab.addNodeHandled
+ case <-tab.closeReq:
+ return false
+ }
+}
+
+func (tab *Table) trackRequest(n *node, success bool, foundNodes []*node) {
+ op := trackRequestOp{n, foundNodes, success}
+ select {
+ case tab.trackRequestCh <- op:
+ case <-tab.closeReq:
+ }
+}
+
+// loop is the main loop of Table.
func (tab *Table) loop() {
var (
- revalidate = time.NewTimer(tab.nextRevalidateTime())
- refresh = time.NewTimer(tab.nextRefreshTime())
- copyNodes = time.NewTicker(copyNodesInterval)
- refreshDone = make(chan struct{}) // where doRefresh reports completion
- revalidateDone chan struct{} // where doRevalidate reports completion
- waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs
+ refresh = time.NewTimer(tab.nextRefreshTime())
+ refreshDone = make(chan struct{}) // where doRefresh reports completion
+ waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs
+ revalTimer = mclock.NewAlarm(tab.cfg.Clock)
+ reseedRandTimer = time.NewTicker(10 * time.Minute)
)
defer refresh.Stop()
- defer revalidate.Stop()
- defer copyNodes.Stop()
+ defer revalTimer.Stop()
+ defer reseedRandTimer.Stop()
// Start initial refresh.
go tab.doRefresh(refreshDone)
loop:
for {
+ nextTime := tab.revalidation.run(tab, tab.cfg.Clock.Now())
+ revalTimer.Schedule(nextTime)
+
select {
+ case <-reseedRandTimer.C:
+ tab.rand.seed()
+
+ case <-revalTimer.C():
+
+ case r := <-tab.revalResponseCh:
+ tab.revalidation.handleResponse(tab, r)
+
+ case op := <-tab.addNodeCh:
+ tab.mutex.Lock()
+ ok := tab.handleAddNode(op)
+ tab.mutex.Unlock()
+ tab.addNodeHandled <- ok
+
+ case op := <-tab.trackRequestCh:
+ tab.handleTrackRequest(op)
+
case <-refresh.C:
- tab.seedRand()
if refreshDone == nil {
refreshDone = make(chan struct{})
go tab.doRefresh(refreshDone)
}
+
case req := <-tab.refreshReq:
waiting = append(waiting, req)
if refreshDone == nil {
refreshDone = make(chan struct{})
go tab.doRefresh(refreshDone)
}
+
case <-refreshDone:
for _, ch := range waiting {
close(ch)
}
waiting, refreshDone = nil, nil
refresh.Reset(tab.nextRefreshTime())
- case <-revalidate.C:
- revalidateDone = make(chan struct{})
- go tab.doRevalidate(revalidateDone)
- case <-revalidateDone:
- revalidate.Reset(tab.nextRevalidateTime())
- revalidateDone = nil
- case <-copyNodes.C:
- go tab.copyLiveNodes()
+
case <-tab.closeReq:
break loop
}
@@ -303,11 +420,6 @@ loop:
for _, ch := range waiting {
close(ch)
}
-
- if revalidateDone != nil {
- <-revalidateDone
- }
-
close(tab.closed)
}
@@ -345,177 +457,15 @@ func (tab *Table) loadSeedNodes() {
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
}
- tab.addSeenNode(seed)
+ tab.handleAddNode(addNodeOp{node: seed, isInbound: false})
}
}
-// doRevalidate checks that the last node in a random bucket is still live and replaces or
-// deletes the node if it isn't.
-func (tab *Table) doRevalidate(done chan<- struct{}) {
- defer func() { done <- struct{}{} }()
-
- last, bi := tab.nodeToRevalidate()
- if last == nil {
- // No non-empty bucket found.
- return
- }
-
- // Ping the selected node and wait for a pong.
- remoteSeq, err := tab.net.ping(unwrapNode(last))
-
- // Also fetch record if the node replied and returned a higher sequence number.
- if last.Seq() < remoteSeq {
- n, err := tab.net.RequestENR(unwrapNode(last))
- if err != nil {
- tab.log.Debug("ENR request failed", "id", last.ID(), "addr", last.addr(), "err", err)
- } else {
- last = &node{Node: *n, addedAt: last.addedAt, livenessChecks: last.livenessChecks}
- }
- }
-
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
- b := tab.buckets[bi]
-
- if err == nil {
- // The node responded, move it to the front.
- last.livenessChecks++
- tab.log.Debug("Revalidated node", "b", bi, "id", last.ID(), "checks", last.livenessChecks)
- tab.bumpInBucket(b, last)
-
- return
- }
- // No reply received, pick a replacement or delete the node if there aren't
- // any replacements.
- if r := tab.replace(b, last); r != nil {
- tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks, "r", r.ID(), "rip", r.IP())
- } else {
- tab.log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks)
- }
-}
-
-// nodeToRevalidate returns the last node in a random, non-empty bucket.
-func (tab *Table) nodeToRevalidate() (n *node, bi int) {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- for _, bi = range tab.rand.Perm(len(tab.buckets)) {
- b := tab.buckets[bi]
- if len(b.entries) > 0 {
- last := b.entries[len(b.entries)-1]
- return last, bi
- }
- }
-
- return nil, 0
-}
-
-func (tab *Table) nextRevalidateTime() time.Duration {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- return time.Duration(tab.rand.Int63n(int64(tab.cfg.PingInterval)))
-}
-
func (tab *Table) nextRefreshTime() time.Duration {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
half := tab.cfg.RefreshInterval / 2
return half + time.Duration(tab.rand.Int63n(int64(half)))
}
-// copyLiveNodes adds nodes from the table to the database if they have been in the table
-// longer than seedMinTableTime.
-func (tab *Table) copyLiveNodes() {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- now := time.Now()
-
- for _, b := range &tab.buckets {
- for _, n := range b.entries {
- if n.livenessChecks > 0 && now.Sub(n.addedAt) >= seedMinTableTime {
- tab.db.UpdateNode(unwrapNode(n))
- }
- }
- }
-}
-
-// findnodeByID returns the n nodes in the table that are closest to the given id.
-// This is used by the FINDNODE/v4 handler.
-//
-// The preferLive parameter says whether the caller wants liveness-checked results. If
-// preferLive is true and the table contains any verified nodes, the result will not
-// contain unverified nodes. However, if there are no verified nodes at all, the result
-// will contain unverified nodes.
-func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- // Scan all buckets. There might be a better way to do this, but there aren't that many
- // buckets, so this solution should be fine. The worst-case complexity of this loop
- // is O(tab.len() * nresults).
- nodes := &nodesByDistance{target: target}
- liveNodes := &nodesByDistance{target: target}
-
- for _, b := range &tab.buckets {
- for _, n := range b.entries {
- nodes.push(n, nresults)
-
- if preferLive && n.livenessChecks > 0 {
- liveNodes.push(n, nresults)
- }
- }
- }
-
- if preferLive && len(liveNodes.entries) > 0 {
- return liveNodes
- }
-
- return nodes
-}
-
-// appendLiveNodes adds nodes at the given distance to the result slice.
-func (tab *Table) appendLiveNodes(dist uint, result []*enode.Node) []*enode.Node {
- if dist > 256 {
- return result
- }
- if dist == 0 {
- return append(result, tab.self())
- }
-
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
- for _, n := range tab.bucketAtDistance(int(dist)).entries {
- if n.livenessChecks >= 1 {
- node := n.Node // avoid handing out pointer to struct field
- result = append(result, &node)
- }
- }
- return result
-}
-
-// len returns the number of nodes in the table.
-func (tab *Table) len() (n int) {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- for _, b := range &tab.buckets {
- n += len(b.entries)
- }
-
- return n
-}
-
-// bucketLen returns the number of nodes in the bucket for the given ID.
-func (tab *Table) bucketLen(id enode.ID) int {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- return len(tab.bucket(id).entries)
-}
-
// bucket returns the bucket for the given node ID hash.
func (tab *Table) bucket(id enode.ID) *bucket {
d := enode.LogDist(tab.self().ID(), id)
@@ -530,104 +480,6 @@ func (tab *Table) bucketAtDistance(d int) *bucket {
return tab.buckets[d-bucketMinDistance-1]
}
-// addSeenNode adds a node which may or may not be live to the end of a bucket. If the
-// bucket has space available, adding the node succeeds immediately. Otherwise, the node is
-// added to the replacements list.
-//
-// The caller must not hold tab.mutex.
-func (tab *Table) addSeenNode(n *node) {
- if n.ID() == tab.self().ID() {
- return
- }
-
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- b := tab.bucket(n.ID())
- if contains(b.entries, n.ID()) {
- // Already in bucket, don't add.
- return
- }
-
- if len(b.entries) >= bucketSize {
- // Bucket full, maybe add as replacement.
- tab.addReplacement(b, n)
- return
- }
-
- if !tab.addIP(b, n.IP()) {
- // Can't add: IP limit reached.
- return
- }
-
- // Add to end of bucket:
- b.entries = append(b.entries, n)
- b.replacements = deleteNode(b.replacements, n)
-
- n.addedAt = time.Now()
-
- if tab.nodeAddedHook != nil {
- tab.nodeAddedHook(b, n)
- }
-}
-
-// addVerifiedNode adds a node whose existence has been verified recently to the front of a
-// bucket. If the node is already in the bucket, it is moved to the front. If the bucket
-// has no space, the node is added to the replacements list.
-//
-// There is an additional safety measure: if the table is still initializing the node
-// is not added. This prevents an attack where the table could be filled by just sending
-// ping repeatedly.
-//
-// The caller must not hold tab.mutex.
-func (tab *Table) addVerifiedNode(n *node) {
- if !tab.isInitDone() {
- return
- }
-
- if n.ID() == tab.self().ID() {
- return
- }
-
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- b := tab.bucket(n.ID())
- if tab.bumpInBucket(b, n) {
- // Already in bucket, moved to front.
- return
- }
-
- if len(b.entries) >= bucketSize {
- // Bucket full, maybe add as replacement.
- tab.addReplacement(b, n)
- return
- }
-
- if !tab.addIP(b, n.IP()) {
- // Can't add: IP limit reached.
- return
- }
-
- // Add to front of bucket.
- b.entries, _ = pushNode(b.entries, n, bucketSize)
- b.replacements = deleteNode(b.replacements, n)
-
- n.addedAt = time.Now()
-
- if tab.nodeAddedHook != nil {
- tab.nodeAddedHook(b, n)
- }
-}
-
-// delete removes an entry from the node table. It is used to evacuate dead nodes.
-func (tab *Table) delete(node *node) {
- tab.mutex.Lock()
- defer tab.mutex.Unlock()
-
- tab.deleteInBucket(tab.bucket(node.ID()), node)
-}
-
func (tab *Table) addIP(b *bucket, ip net.IP) bool {
if len(ip) == 0 {
return false // Nodes without IP cannot be added.
@@ -661,17 +513,52 @@ func (tab *Table) removeIP(b *bucket, ip net.IP) {
b.ips.Remove(ip)
}
+// handleAddNode adds the node in the request to the table, if there is space.
+// The caller must hold tab.mutex.
+func (tab *Table) handleAddNode(req addNodeOp) bool {
+ if req.node.ID() == tab.self().ID() {
+ return false
+ }
+ // For nodes from inbound contact, there is an additional safety measure: if the table
+ // is still initializing the node is not added.
+ if req.isInbound && !tab.isInitDone() {
+ return false
+ }
+
+ b := tab.bucket(req.node.ID())
+ if tab.bumpInBucket(b, req.node.Node) {
+ // Already in bucket, update record.
+ return false
+ }
+ if len(b.entries) >= bucketSize {
+ // Bucket full, maybe add as replacement.
+ tab.addReplacement(b, req.node)
+ return false
+ }
+ if !tab.addIP(b, req.node.IP()) {
+ // Can't add: IP limit reached.
+ return false
+ }
+
+ // Add to bucket.
+ b.entries = append(b.entries, req.node)
+ b.replacements = deleteNode(b.replacements, req.node)
+ tab.nodeAdded(b, req.node)
+ return true
+}
+
+// addReplacement adds n to the replacement cache of bucket b.
func (tab *Table) addReplacement(b *bucket, n *node) {
- for _, e := range b.replacements {
- if e.ID() == n.ID() {
- return // already in list
- }
+ if contains(b.replacements, n.ID()) {
+ // TODO: update ENR
+ return
}
if !tab.addIP(b, n.IP()) {
return
}
+ n.addedToTable = time.Now()
var removed *node
b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
@@ -680,64 +567,107 @@ func (tab *Table) addReplacement(b *bucket, n *node) {
}
}
-// replace removes n from the replacement list and replaces 'last' with it if it is the
-// last entry in the bucket. If 'last' isn't the last entry, it has either been replaced
-// with someone else or became active.
-func (tab *Table) replace(b *bucket, last *node) *node {
- if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() {
- // Entry has moved, don't replace it.
- return nil
+func (tab *Table) nodeAdded(b *bucket, n *node) {
+ if n.addedToTable == (time.Time{}) {
+ n.addedToTable = time.Now()
}
- // Still the last entry.
- if len(b.replacements) == 0 {
- tab.deleteInBucket(b, last)
+ n.addedToBucket = time.Now()
+ tab.revalidation.nodeAdded(tab, n)
+ if tab.nodeAddedHook != nil {
+ tab.nodeAddedHook(b, n)
+ }
+ if metrics.Enabled {
+ bucketsCounter[b.index].Inc(1)
+ }
+}
+
+func (tab *Table) nodeRemoved(b *bucket, n *node) {
+ tab.revalidation.nodeRemoved(n)
+ if tab.nodeRemovedHook != nil {
+ tab.nodeRemovedHook(b, n)
+ }
+ if metrics.Enabled {
+ bucketsCounter[b.index].Dec(1)
+ }
+}
+
+// deleteInBucket removes node n from the table.
+// If there are replacement nodes in the bucket, the node is replaced.
+func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node {
+ index := slices.IndexFunc(b.entries, func(e *node) bool { return e.ID() == id })
+ if index == -1 {
+ // Entry has been removed already.
return nil
}
- r := b.replacements[tab.rand.Intn(len(b.replacements))]
- b.replacements = deleteNode(b.replacements, r)
- b.entries[len(b.entries)-1] = r
- tab.removeIP(b, last.IP())
+ // Remove the node.
+ n := b.entries[index]
+ b.entries = slices.Delete(b.entries, index, index+1)
+ tab.removeIP(b, n.IP())
+ tab.nodeRemoved(b, n)
- return r
+ // Add replacement.
+ if len(b.replacements) == 0 {
+ tab.log.Debug("Removed dead node", "b", b.index, "id", n.ID(), "ip", n.IP())
+ return nil
+ }
+ rindex := tab.rand.Intn(len(b.replacements))
+ rep := b.replacements[rindex]
+ b.replacements = slices.Delete(b.replacements, rindex, rindex+1)
+ b.entries = append(b.entries, rep)
+ tab.nodeAdded(b, rep)
+ tab.log.Debug("Replaced dead node", "b", b.index, "id", n.ID(), "ip", n.IP(), "r", rep.ID(), "rip", rep.IP())
+ return rep
}
-// bumpInBucket moves the given node to the front of the bucket entry list
-// if it is contained in that list.
-func (tab *Table) bumpInBucket(b *bucket, n *node) bool {
- for i := range b.entries {
- if b.entries[i].ID() == n.ID() {
- if !n.IP().Equal(b.entries[i].IP()) {
- // Endpoint has changed, ensure that the new IP fits into table limits.
- tab.removeIP(b, b.entries[i].IP())
-
- if !tab.addIP(b, n.IP()) {
- // It doesn't, put the previous one back.
- tab.addIP(b, b.entries[i].IP())
- return false
- }
- }
- // Move it to the front.
- copy(b.entries[1:], b.entries[:i])
- b.entries[0] = n
+// bumpInBucket updates the node record of n in the bucket.
+func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node) bool {
+ i := slices.IndexFunc(b.entries, func(elem *node) bool {
+ return elem.ID() == newRecord.ID()
+ })
+ if i == -1 {
+ return false
+ }
- return true
+ if !newRecord.IP().Equal(b.entries[i].IP()) {
+ // Endpoint has changed, ensure that the new IP fits into table limits.
+ tab.removeIP(b, b.entries[i].IP())
+ if !tab.addIP(b, newRecord.IP()) {
+ // It doesn't, put the previous one back.
+ tab.addIP(b, b.entries[i].IP())
+ return false
}
}
-
- return false
+ b.entries[i].Node = newRecord
+ return true
}
-func (tab *Table) deleteInBucket(b *bucket, n *node) {
- // Check if the node is actually in the bucket so the removed hook
- // isn't called multiple times for the same node.
- if !contains(b.entries, n.ID()) {
- return
+func (tab *Table) handleTrackRequest(op trackRequestOp) {
+ var fails int
+ if op.success {
+ // Reset failure counter because it counts _consecutive_ failures.
+ tab.db.UpdateFindFails(op.node.ID(), op.node.IP(), 0)
+ } else {
+ fails = tab.db.FindFails(op.node.ID(), op.node.IP())
+ fails++
+ tab.db.UpdateFindFails(op.node.ID(), op.node.IP(), fails)
}
- b.entries = deleteNode(b.entries, n)
- tab.removeIP(b, n.IP())
- if tab.nodeRemovedHook != nil {
- tab.nodeRemovedHook(b, n)
+
+ tab.mutex.Lock()
+ defer tab.mutex.Unlock()
+
+ b := tab.bucket(op.node.ID())
+ // Remove the node from the local table if it fails to return anything useful too
+ // many times, but only if there are enough other nodes in the bucket. This latter
+ // condition specifically exists to make bootstrapping in smaller test networks more
+ // reliable.
+ if fails >= maxFindnodeFailures && len(b.entries) >= bucketSize/4 {
+ tab.deleteInBucket(b, op.node.ID())
+ }
+
+ // Add found nodes.
+ for _, n := range op.foundNodes {
+ tab.handleAddNode(addNodeOp{n, false})
}
}
diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go
new file mode 100644
index 000000000000..9a13900ebca4
--- /dev/null
+++ b/p2p/discover/table_reval.go
@@ -0,0 +1,223 @@
+// 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 .
+
+package discover
+
+import (
+ "fmt"
+ "math"
+ "slices"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common/mclock"
+ "github.com/ethereum/go-ethereum/p2p/enode"
+)
+
+const never = mclock.AbsTime(math.MaxInt64)
+
+// tableRevalidation implements the node revalidation process.
+// It tracks all nodes contained in Table, and schedules sending PING to them.
+type tableRevalidation struct {
+ fast revalidationList
+ slow revalidationList
+ activeReq map[enode.ID]struct{}
+}
+
+type revalidationResponse struct {
+ n *node
+ newRecord *enode.Node
+ list *revalidationList
+ didRespond bool
+}
+
+func (tr *tableRevalidation) init(cfg *Config) {
+ tr.activeReq = make(map[enode.ID]struct{})
+ tr.fast.nextTime = never
+ tr.fast.interval = cfg.PingInterval
+ tr.fast.name = "fast"
+ tr.slow.nextTime = never
+ tr.slow.interval = cfg.PingInterval * 3
+ tr.slow.name = "slow"
+}
+
+// nodeAdded is called when the table receives a new node.
+func (tr *tableRevalidation) nodeAdded(tab *Table, n *node) {
+ tr.fast.push(n, tab.cfg.Clock.Now(), &tab.rand)
+}
+
+// nodeRemoved is called when a node was removed from the table.
+func (tr *tableRevalidation) nodeRemoved(n *node) {
+ if !tr.fast.remove(n) {
+ tr.slow.remove(n)
+ }
+}
+
+// run performs node revalidation.
+// It returns the next time it should be invoked, which is used in the Table main loop
+// to schedule a timer. However, run can be called at any time.
+func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) {
+ if n := tr.fast.get(now, &tab.rand, tr.activeReq); n != nil {
+ tr.startRequest(tab, &tr.fast, n)
+ tr.fast.schedule(now, &tab.rand)
+ }
+ if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil {
+ tr.startRequest(tab, &tr.slow, n)
+ tr.slow.schedule(now, &tab.rand)
+ }
+
+ return min(tr.fast.nextTime, tr.slow.nextTime)
+}
+
+// startRequest spawns a revalidation request for node n.
+func (tr *tableRevalidation) startRequest(tab *Table, list *revalidationList, n *node) {
+ if _, ok := tr.activeReq[n.ID()]; ok {
+ panic(fmt.Errorf("duplicate startRequest (list %q, node %v)", list.name, n.ID()))
+ }
+ tr.activeReq[n.ID()] = struct{}{}
+ resp := revalidationResponse{n: n, list: list}
+
+ // Fetch the node while holding lock.
+ tab.mutex.Lock()
+ node := n.Node
+ tab.mutex.Unlock()
+
+ go tab.doRevalidate(resp, node)
+}
+
+func (tab *Table) doRevalidate(resp revalidationResponse, node *enode.Node) {
+ // Ping the selected node and wait for a pong response.
+ remoteSeq, err := tab.net.ping(node)
+ resp.didRespond = err == nil
+
+ // Also fetch record if the node replied and returned a higher sequence number.
+ if remoteSeq > node.Seq() {
+ newrec, err := tab.net.RequestENR(node)
+ if err != nil {
+ tab.log.Debug("ENR request failed", "id", node.ID(), "err", err)
+ } else {
+ resp.newRecord = newrec
+ }
+ }
+
+ select {
+ case tab.revalResponseCh <- resp:
+ case <-tab.closed:
+ }
+}
+
+// handleResponse processes the result of a revalidation request.
+func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationResponse) {
+ now := tab.cfg.Clock.Now()
+ n := resp.n
+ b := tab.bucket(n.ID())
+ delete(tr.activeReq, n.ID())
+
+ tab.mutex.Lock()
+ defer tab.mutex.Unlock()
+
+ if !resp.didRespond {
+ // Revalidation failed.
+ n.livenessChecks /= 3
+ if n.livenessChecks <= 0 {
+ tab.deleteInBucket(b, n.ID())
+ } else {
+ tr.moveToList(&tr.fast, resp.list, n, now, &tab.rand)
+ }
+ return
+ }
+
+ // The node responded.
+ n.livenessChecks++
+ n.isValidatedLive = true
+ var endpointChanged bool
+ if resp.newRecord != nil {
+ endpointChanged = tab.bumpInBucket(b, resp.newRecord)
+ if endpointChanged {
+ // If the node changed its advertised endpoint, the updated ENR is not served
+ // until it has been revalidated.
+ n.isValidatedLive = false
+ }
+ }
+ tab.log.Debug("Revalidated node", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", resp.list.name)
+
+ // Move node over to slow queue after first validation.
+ if !endpointChanged {
+ tr.moveToList(&tr.slow, resp.list, n, now, &tab.rand)
+ } else {
+ tr.moveToList(&tr.fast, resp.list, n, now, &tab.rand)
+ }
+
+ // Store potential seeds in database.
+ if n.isValidatedLive && n.livenessChecks > 5 {
+ tab.db.UpdateNode(resp.n.Node)
+ }
+}
+
+func (tr *tableRevalidation) moveToList(dest, source *revalidationList, n *node, now mclock.AbsTime, rand randomSource) {
+ if source == dest {
+ return
+ }
+ if !source.remove(n) {
+ panic(fmt.Errorf("moveToList(%q -> %q): node %v not in source list", source.name, dest.name, n.ID()))
+ }
+ dest.push(n, now, rand)
+}
+
+// revalidationList holds a list nodes and the next revalidation time.
+type revalidationList struct {
+ nodes []*node
+ nextTime mclock.AbsTime
+ interval time.Duration
+ name string
+}
+
+// get returns a random node from the queue. Nodes in the 'exclude' map are not returned.
+func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *node {
+ if now < list.nextTime || len(list.nodes) == 0 {
+ return nil
+ }
+ for i := 0; i < len(list.nodes)*3; i++ {
+ n := list.nodes[rand.Intn(len(list.nodes))]
+ _, excluded := exclude[n.ID()]
+ if !excluded {
+ return n
+ }
+ }
+ return nil
+}
+
+func (list *revalidationList) schedule(now mclock.AbsTime, rand randomSource) {
+ list.nextTime = now.Add(time.Duration(rand.Int63n(int64(list.interval))))
+}
+
+func (list *revalidationList) push(n *node, now mclock.AbsTime, rand randomSource) {
+ list.nodes = append(list.nodes, n)
+ if list.nextTime == never {
+ list.schedule(now, rand)
+ }
+}
+
+func (list *revalidationList) remove(n *node) bool {
+ i := slices.Index(list.nodes, n)
+ if i == -1 {
+ return false
+ }
+ list.nodes = slices.Delete(list.nodes, i, i+1)
+ if len(list.nodes) == 0 {
+ list.nextTime = never
+ }
+ return true
+}
diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go
index 062b83214d69..770aee112c31 100644
--- a/p2p/discover/table_test.go
+++ b/p2p/discover/table_test.go
@@ -20,14 +20,16 @@ import (
"crypto/ecdsa"
"fmt"
"math/rand"
-
"net"
"reflect"
"testing"
"testing/quick"
"time"
+ "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/internal/testlog"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/netutil"
@@ -49,33 +51,60 @@ func TestTable_pingReplace(t *testing.T) {
}
func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding bool) {
+ simclock := new(mclock.Simulated)
transport := newPingRecorder()
-
- tab, db := newTestTable(transport)
+ tab, db := newTestTable(transport, Config{
+ Clock: simclock,
+ Log: testlog.Logger(t, log.LevelTrace),
+ })
defer db.Close()
defer tab.close()
<-tab.initDone
// Fill up the sender's bucket.
- pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
- pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99))
- last := fillBucket(tab, pingSender)
+ replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
+ replacementNode := wrapNode(enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99))
+ last := fillBucket(tab, replacementNode.ID())
+ tab.mutex.Lock()
+ nodeEvents := newNodeEventRecorder(128)
+ tab.nodeAddedHook = nodeEvents.nodeAdded
+ tab.nodeRemovedHook = nodeEvents.nodeRemoved
+ tab.mutex.Unlock()
- // Add the sender as if it just pinged us. Revalidate should replace the last node in
- // its bucket if it is unresponsive. Revalidate again to ensure that
+ // The revalidation process should replace
+ // this node in the bucket if it is unresponsive.
transport.dead[last.ID()] = !lastInBucketIsResponding
- transport.dead[pingSender.ID()] = !newNodeIsResponding
+ transport.dead[replacementNode.ID()] = !newNodeIsResponding
+
+ // Add replacement node to table.
+ tab.addFoundNode(replacementNode)
+
+ t.Log("last:", last.ID())
+ t.Log("replacement:", replacementNode.ID())
- tab.addSeenNode(pingSender)
- tab.doRevalidate(make(chan struct{}, 1))
- tab.doRevalidate(make(chan struct{}, 1))
+ // Wait until the last node was pinged.
+ waitForRevalidationPing(t, transport, tab, last.ID())
- if !transport.pinged[last.ID()] {
- // Oldest node in bucket is pinged to see whether it is still alive.
- t.Error("table did not ping last node in bucket")
+ if !lastInBucketIsResponding {
+ if !nodeEvents.waitNodeAbsent(last.ID(), 2*time.Second) {
+ t.Error("last node was not removed")
+ }
+ if !nodeEvents.waitNodePresent(replacementNode.ID(), 2*time.Second) {
+ t.Error("replacement node was not added")
+ }
+
+ // If a replacement is expected, we also need to wait until the replacement node
+ // was pinged and added/removed.
+ waitForRevalidationPing(t, transport, tab, replacementNode.ID())
+ if !newNodeIsResponding {
+ if !nodeEvents.waitNodeAbsent(replacementNode.ID(), 2*time.Second) {
+ t.Error("replacement node was not removed")
+ }
+ }
}
+ // Check bucket content.
tab.mutex.Lock()
defer tab.mutex.Unlock()
@@ -83,86 +112,50 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
if !lastInBucketIsResponding && !newNodeIsResponding {
wantSize--
}
-
- if l := len(tab.bucket(pingSender.ID()).entries); l != wantSize {
- t.Errorf("wrong bucket size after bond: got %d, want %d", l, wantSize)
+ bucket := tab.bucket(replacementNode.ID())
+ if l := len(bucket.entries); l != wantSize {
+ t.Errorf("wrong bucket size after revalidation: got %d, want %d", l, wantSize)
}
-
- if found := contains(tab.bucket(pingSender.ID()).entries, last.ID()); found != lastInBucketIsResponding {
- t.Errorf("last entry found: %t, want: %t", found, lastInBucketIsResponding)
+ if ok := contains(bucket.entries, last.ID()); ok != lastInBucketIsResponding {
+ t.Errorf("revalidated node found: %t, want: %t", ok, lastInBucketIsResponding)
}
wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
- if found := contains(tab.bucket(pingSender.ID()).entries, pingSender.ID()); found != wantNewEntry {
- t.Errorf("new entry found: %t, want: %t", found, wantNewEntry)
+ if ok := contains(bucket.entries, replacementNode.ID()); ok != wantNewEntry {
+ t.Errorf("replacement node found: %t, want: %t", ok, wantNewEntry)
}
}
-func TestBucket_bumpNoDuplicates(t *testing.T) {
- t.Parallel()
-
- cfg := &quick.Config{
- MaxCount: 1000,
- Rand: rand.New(rand.NewSource(time.Now().Unix())),
- Values: func(args []reflect.Value, rand *rand.Rand) {
- // generate a random list of nodes. this will be the content of the bucket.
- n := rand.Intn(bucketSize-1) + 1
- nodes := make([]*node, n)
- for i := range nodes {
- nodes[i] = nodeAtDistance(enode.ID{}, 200, intIP(200))
- }
- args[0] = reflect.ValueOf(nodes)
- // generate random bump positions.
- bumps := make([]int, rand.Intn(100))
- for i := range bumps {
- bumps[i] = rand.Intn(len(nodes))
- }
- args[1] = reflect.ValueOf(bumps)
- },
- }
-
- prop := func(nodes []*node, bumps []int) (ok bool) {
- tab, db := newTestTable(newPingRecorder())
- defer db.Close()
- defer tab.close()
-
- b := &bucket{entries: make([]*node, len(nodes))}
- copy(b.entries, nodes)
-
- for i, pos := range bumps {
- tab.bumpInBucket(b, b.entries[pos])
-
- if hasDuplicates(b.entries) {
- t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps))
-
- for _, n := range b.entries {
- t.Logf(" %p", n)
- }
+// waitForRevalidationPing waits until a PING message is sent to a node with the given id.
+func waitForRevalidationPing(t *testing.T, transport *pingRecorder, tab *Table, id enode.ID) *enode.Node {
+ t.Helper()
- return false
- }
+ simclock := tab.cfg.Clock.(*mclock.Simulated)
+ maxAttempts := tab.len() * 8
+ for i := 0; i < maxAttempts; i++ {
+ simclock.Run(tab.cfg.PingInterval)
+ p := transport.waitPing(2 * time.Second)
+ if p == nil {
+ t.Fatal("Table did not send revalidation ping")
+ }
+ if id == (enode.ID{}) || p.ID() == id {
+ return p
}
-
- checkIPLimitInvariant(t, tab)
-
- return true
- }
- if err := quick.Check(prop, cfg); err != nil {
- t.Error(err)
}
+ t.Fatalf("Table did not ping node %v (%d attempts)", id, maxAttempts)
+ return nil
}
// This checks that the table-wide IP limit is applied correctly.
func TestTable_IPLimit(t *testing.T) {
transport := newPingRecorder()
-
- tab, db := newTestTable(transport)
+ tab, db := newTestTable(transport, Config{})
defer db.Close()
defer tab.close()
for i := 0; i < tableIPLimit+1; i++ {
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
- tab.addSeenNode(n)
+ tab.addFoundNode(n)
}
if tab.len() > tableIPLimit {
@@ -175,15 +168,14 @@ func TestTable_IPLimit(t *testing.T) {
// This checks that the per-bucket IP limit is applied correctly.
func TestTable_BucketIPLimit(t *testing.T) {
transport := newPingRecorder()
-
- tab, db := newTestTable(transport)
+ tab, db := newTestTable(transport, Config{})
defer db.Close()
defer tab.close()
d := 3
for i := 0; i < bucketIPLimit+1; i++ {
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
- tab.addSeenNode(n)
+ tab.addFoundNode(n)
}
if tab.len() > bucketIPLimit {
@@ -217,8 +209,7 @@ func TestTable_findnodeByID(t *testing.T) {
test := func(test *closeTest) bool {
// for any node table, Target and N
transport := newPingRecorder()
-
- tab, db := newTestTable(transport)
+ tab, db := newTestTable(transport, Config{})
defer db.Close()
defer tab.close()
fillTable(tab, test.All, true)
@@ -300,7 +291,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
}
func TestTable_addVerifiedNode(t *testing.T) {
- tab, db := newTestTable(newPingRecorder())
+ tab, db := newTestTable(newPingRecorder(), Config{})
<-tab.initDone
defer db.Close()
@@ -309,32 +300,33 @@ func TestTable_addVerifiedNode(t *testing.T) {
// Insert two nodes.
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
- tab.addSeenNode(n1)
- tab.addSeenNode(n2)
+ tab.addFoundNode(n1)
+ tab.addFoundNode(n2)
+ bucket := tab.bucket(n1.ID())
// Verify bucket content:
bcontent := []*node{n1, n2}
- if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) {
- t.Fatalf("wrong bucket content: %v", tab.bucket(n1.ID()).entries)
+ if !reflect.DeepEqual(unwrapNodes(bucket.entries), unwrapNodes(bcontent)) {
+ t.Fatalf("wrong bucket content: %v", bucket.entries)
}
// Add a changed version of n2.
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
newn2 := wrapNode(enode.SignNull(newrec, n2.ID()))
- tab.addVerifiedNode(newn2)
+ tab.addInboundNode(newn2)
// Check that bucket is updated correctly.
- newBcontent := []*node{newn2, n1}
- if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, newBcontent) {
- t.Fatalf("wrong bucket content after update: %v", tab.bucket(n1.ID()).entries)
+ newBcontent := []*node{n1, newn2}
+ if !reflect.DeepEqual(unwrapNodes(bucket.entries), unwrapNodes(newBcontent)) {
+ t.Fatalf("wrong bucket content after update: %v", bucket.entries)
}
checkIPLimitInvariant(t, tab)
}
func TestTable_addSeenNode(t *testing.T) {
- tab, db := newTestTable(newPingRecorder())
+ tab, db := newTestTable(newPingRecorder(), Config{})
<-tab.initDone
defer db.Close()
@@ -343,8 +335,8 @@ func TestTable_addSeenNode(t *testing.T) {
// Insert two nodes.
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
- tab.addSeenNode(n1)
- tab.addSeenNode(n2)
+ tab.addFoundNode(n1)
+ tab.addFoundNode(n2)
// Verify bucket content:
bcontent := []*node{n1, n2}
@@ -356,7 +348,7 @@ func TestTable_addSeenNode(t *testing.T) {
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
newn2 := wrapNode(enode.SignNull(newrec, n2.ID()))
- tab.addSeenNode(newn2)
+ tab.addFoundNode(newn2)
// Check that bucket content is unchanged.
if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) {
@@ -370,7 +362,10 @@ func TestTable_addSeenNode(t *testing.T) {
// announces a new sequence number, the new record should be pulled.
func TestTable_revalidateSyncRecord(t *testing.T) {
transport := newPingRecorder()
- tab, db := newTestTable(transport)
+ tab, db := newTestTable(transport, Config{
+ Clock: new(mclock.Simulated),
+ Log: testlog.Logger(t, log.LevelTrace),
+ })
<-tab.initDone
defer db.Close()
@@ -383,14 +378,17 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
id := enode.ID{1}
n1 := wrapNode(enode.SignNull(&r, id))
- tab.addSeenNode(n1)
+ tab.addFoundNode(n1)
// Update the node record.
r.Set(enr.WithEntry("foo", "bar"))
n2 := enode.SignNull(&r, id)
transport.updateRecord(n2)
- tab.doRevalidate(make(chan struct{}, 1))
+ // Wait for revalidation. We wait for the node to be revalidated two times
+ // in order to synchronize with the update in the able.
+ waitForRevalidationPing(t, transport, tab, n2.ID())
+ waitForRevalidationPing(t, transport, tab, n2.ID())
intable := tab.getNode(id)
if !reflect.DeepEqual(intable, n2) {
diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go
index be582b6b8b47..0584a74d9b46 100644
--- a/p2p/discover/table_util_test.go
+++ b/p2p/discover/table_util_test.go
@@ -25,6 +25,8 @@ import (
"math/rand"
"net"
"sync"
+ "sync/atomic"
+ "time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
@@ -41,8 +43,7 @@ func init() {
nullNode = enode.SignNull(&r, enode.ID{})
}
-func newTestTable(t transport) (*Table, *enode.DB) {
- cfg := Config{}
+func newTestTable(t transport, cfg Config) (*Table, *enode.DB) {
db, _ := enode.OpenDB("")
tab, _ := newTable(t, db, cfg)
go tab.loop()
@@ -106,12 +107,14 @@ func intIP(i int) net.IP {
}
// fillBucket inserts nodes into the given bucket until it is full.
-func fillBucket(tab *Table, n *node) (last *node) {
- ld := enode.LogDist(tab.self().ID(), n.ID())
- b := tab.bucket(n.ID())
-
+func fillBucket(tab *Table, id enode.ID) (last *node) {
+ ld := enode.LogDist(tab.self().ID(), id)
+ b := tab.bucket(id)
for len(b.entries) < bucketSize {
- b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld)))
+ node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
+ if !tab.addFoundNode(node) {
+ panic("node not added")
+ }
}
return b.entries[bucketSize-1]
@@ -123,16 +126,19 @@ func fillTable(tab *Table, nodes []*node, setLive bool) {
for _, n := range nodes {
if setLive {
n.livenessChecks = 1
+ n.isValidatedLive = true
}
- tab.addSeenNode(n)
+ tab.addFoundNode(n)
}
}
type pingRecorder struct {
- mu sync.Mutex
- dead, pinged map[enode.ID]bool
- records map[enode.ID]*enode.Node
- n *enode.Node
+ mu sync.Mutex
+ cond *sync.Cond
+ dead map[enode.ID]bool
+ records map[enode.ID]*enode.Node
+ pinged []*enode.Node
+ n *enode.Node
}
func newPingRecorder() *pingRecorder {
@@ -141,12 +147,13 @@ func newPingRecorder() *pingRecorder {
r.Set(enr.IP{0, 0, 0, 0})
n := enode.SignNull(&r, enode.ID{})
- return &pingRecorder{
+ t := &pingRecorder{
dead: make(map[enode.ID]bool),
- pinged: make(map[enode.ID]bool),
records: make(map[enode.ID]*enode.Node),
n: n,
}
+ t.cond = sync.NewCond(&t.mu)
+ return t
}
// updateRecord updates a node record. Future calls to ping and
@@ -162,12 +169,40 @@ func (t *pingRecorder) Self() *enode.Node { return nullNode }
func (t *pingRecorder) lookupSelf() []*enode.Node { return nil }
func (t *pingRecorder) lookupRandom() []*enode.Node { return nil }
+func (t *pingRecorder) waitPing(timeout time.Duration) *enode.Node {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ // Wake up the loop on timeout.
+ var timedout atomic.Bool
+ timer := time.AfterFunc(timeout, func() {
+ timedout.Store(true)
+ t.cond.Broadcast()
+ })
+ defer timer.Stop()
+
+ // Wait for a ping.
+ for {
+ if timedout.Load() {
+ return nil
+ }
+ if len(t.pinged) > 0 {
+ n := t.pinged[0]
+ t.pinged = append(t.pinged[:0], t.pinged[1:]...)
+ return n
+ }
+ t.cond.Wait()
+ }
+}
+
// ping simulates a ping request.
func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) {
t.mu.Lock()
defer t.mu.Unlock()
- t.pinged[n.ID()] = true
+ t.pinged = append(t.pinged, n)
+ t.cond.Broadcast()
+
if t.dead[n.ID()] {
return 0, errTimeout
}
@@ -283,3 +318,57 @@ func hexEncPubkey(h string) (ret encPubkey) {
return ret
}
+
+type nodeEventRecorder struct {
+ evc chan recordedNodeEvent
+}
+
+type recordedNodeEvent struct {
+ node *node
+ added bool
+}
+
+func newNodeEventRecorder(buffer int) *nodeEventRecorder {
+ return &nodeEventRecorder{
+ evc: make(chan recordedNodeEvent, buffer),
+ }
+}
+
+func (set *nodeEventRecorder) nodeAdded(b *bucket, n *node) {
+ select {
+ case set.evc <- recordedNodeEvent{n, true}:
+ default:
+ panic("no space in event buffer")
+ }
+}
+
+func (set *nodeEventRecorder) nodeRemoved(b *bucket, n *node) {
+ select {
+ case set.evc <- recordedNodeEvent{n, false}:
+ default:
+ panic("no space in event buffer")
+ }
+}
+
+func (set *nodeEventRecorder) waitNodePresent(id enode.ID, timeout time.Duration) bool {
+ return set.waitNodeEvent(id, timeout, true)
+}
+
+func (set *nodeEventRecorder) waitNodeAbsent(id enode.ID, timeout time.Duration) bool {
+ return set.waitNodeEvent(id, timeout, false)
+}
+
+func (set *nodeEventRecorder) waitNodeEvent(id enode.ID, timeout time.Duration, added bool) bool {
+ timer := time.NewTimer(timeout)
+ defer timer.Stop()
+ for {
+ select {
+ case ev := <-set.evc:
+ if ev.node.ID() == id && ev.added == added {
+ return true
+ }
+ case <-timer.C:
+ return false
+ }
+ }
+}
diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go
index fa4ce65ea7eb..bf694658d5a5 100644
--- a/p2p/discover/v4_udp.go
+++ b/p2p/discover/v4_udp.go
@@ -142,7 +142,7 @@ func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
log: cfg.Log,
}
- tab, err := newMeteredTable(t, ln.Database(), cfg)
+ tab, err := newTable(t, ln.Database(), cfg)
if err != nil {
return nil, err
}
@@ -401,6 +401,10 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
return respN, nil
}
+func (t *UDPv4) TableBuckets() [][]BucketNode {
+ return t.tab.Nodes()
+}
+
// pending adds a reply matcher to the pending reply queue.
// see the documentation of type replyMatcher for a detailed explanation.
func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchFunc) *replyMatcher {
@@ -729,10 +733,10 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
n := wrapNode(enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port))
if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration {
t.sendPing(fromID, from, func() {
- t.tab.addVerifiedNode(n)
+ t.tab.addInboundNode(n)
})
} else {
- t.tab.addVerifiedNode(n)
+ t.tab.addInboundNode(n)
}
// Update node database and endpoint predictor.
diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go
index 5ad2e5233e4c..cbee5f11ced4 100644
--- a/p2p/discover/v4_udp_test.go
+++ b/p2p/discover/v4_udp_test.go
@@ -288,7 +288,7 @@ func TestUDPv4_findnode(t *testing.T) {
n := wrapNode(enode.NewV4(&key.PublicKey, ip, 0, 2000))
// Ensure half of table content isn't verified live yet.
if i > numCandidates/2 {
- n.livenessChecks = 1
+ n.isValidatedLive = true
live[n.ID()] = true
}
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index 2d6547ea8796..b5be16f92015 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -177,7 +177,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) {
cancelCloseCtx: cancelCloseCtx,
}
t.talk = newTalkSystem(t)
- tab, err := newMeteredTable(t, t.db, cfg)
+ tab, err := newTable(t, t.db, cfg)
if err != nil {
return nil, err
}
@@ -756,7 +756,7 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
if fromNode != nil {
// Handshake succeeded, add to table.
- t.tab.addSeenNode(wrapNode(fromNode))
+ t.tab.addInboundNode(wrapNode(fromNode))
}
if packet.Kind() != v5wire.WhoareyouPacket {
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 5b1c493e6635..92e7f0b44821 100644
--- a/p2p/discover/v5_udp_test.go
+++ b/p2p/discover/v5_udp_test.go
@@ -156,7 +156,7 @@ func TestUDPv5_unknownPacket(t *testing.T) {
// Make node known.
n := test.getNode(test.remotekey, test.remoteaddr).Node()
- test.table.addSeenNode(wrapNode(n))
+ test.table.addFoundNode(wrapNode(n))
test.packetIn(&v5wire.Unknown{Nonce: nonce})
test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) {
diff --git a/p2p/server.go b/p2p/server.go
index 53008f3822f3..8e33ff858faf 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -194,8 +194,8 @@ type Server struct {
nodedb *enode.DB
localnode *enode.LocalNode
- ntab *discover.UDPv4
- DiscV5 *discover.UDPv5
+ discv4 *discover.UDPv4
+ discv5 *discover.UDPv5
discmix *enode.FairMix
dialsched *dialScheduler
@@ -456,6 +456,16 @@ func (srv *Server) Self() *enode.Node {
return ln.Node()
}
+// DiscoveryV4 returns the discovery v4 instance, if configured.
+func (srv *Server) DiscoveryV4() *discover.UDPv4 {
+ return srv.discv4
+}
+
+// DiscoveryV4 returns the discovery v5 instance, if configured.
+func (srv *Server) DiscoveryV5() *discover.UDPv5 {
+ return srv.discv5
+}
+
// Stop terminates the server and all active peer connections.
// It blocks until all active connections have been closed.
func (srv *Server) Stop() {
@@ -621,13 +631,13 @@ func (srv *Server) setupDiscovery() error {
)
// If both versions of discovery are running, setup a shared
// connection, so v5 can read unhandled messages from v4.
- if srv.DiscoveryV4 && srv.DiscoveryV5 {
+ if srv.Config.DiscoveryV4 && srv.Config.DiscoveryV5 {
unhandled = make(chan discover.ReadPacket, 100)
sconn = &sharedUDPConn{conn, unhandled}
}
// Start discovery services.
- if srv.DiscoveryV4 {
+ if srv.Config.DiscoveryV4 {
cfg := discover.Config{
PrivateKey: srv.PrivateKey,
NetRestrict: srv.NetRestrict,
@@ -640,18 +650,17 @@ func (srv *Server) setupDiscovery() error {
if err != nil {
return err
}
-
- srv.ntab = ntab
+ srv.discv4 = ntab
srv.discmix.AddSource(ntab.RandomNodes())
}
- if srv.DiscoveryV5 {
+ if srv.Config.DiscoveryV5 {
cfg := discover.Config{
PrivateKey: srv.PrivateKey,
NetRestrict: srv.NetRestrict,
Bootnodes: srv.BootstrapNodesV5,
Log: srv.log,
}
- srv.DiscV5, err = discover.ListenV5(sconn, srv.localnode, cfg)
+ srv.discv5, err = discover.ListenV5(sconn, srv.localnode, cfg)
if err != nil {
return err
}
@@ -678,8 +687,8 @@ func (srv *Server) setupDialScheduler() {
dialer: srv.Dialer,
clock: srv.clock,
}
- if srv.ntab != nil {
- config.resolver = srv.ntab
+ if srv.discv4 != nil {
+ config.resolver = srv.discv4
}
if config.dialer == nil {
@@ -877,12 +886,11 @@ running:
srv.log.Trace("P2P networking is spinning down")
// Terminate discovery. If there is a running lookup it will terminate soon.
- if srv.ntab != nil {
- srv.ntab.Close()
+ if srv.discv4 != nil {
+ srv.discv4.Close()
}
-
- if srv.DiscV5 != nil {
- srv.DiscV5.Close()
+ if srv.discv5 != nil {
+ srv.discv5.Close()
}
// Disconnect all peers.
for _, p := range peers {
From 706cf1c7798016a80b108d2eb13cbc29efc8ed41 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Tue, 28 May 2024 18:13:03 +0200
Subject: [PATCH 085/103] p2p/discover: fix crash when revalidated node is
removed (#29864)
In #29572, I assumed the revalidation list that the node is contained in could only ever
be changed by the outcome of a revalidation request. But turns out that's not true: if the
node gets removed due to FINDNODE failure, it will also be removed from the list it is in.
This causes a crash.
The invariant is: while node is in table, it is always in exactly one of the two lists. So
it seems best to store a pointer to the current list within the node itself.
---
p2p/discover/node.go | 1 +
p2p/discover/table_reval.go | 74 ++++++++++++++++++++------------
p2p/discover/table_reval_test.go | 70 ++++++++++++++++++++++++++++++
p2p/discover/table_util_test.go | 9 +++-
4 files changed, 125 insertions(+), 29 deletions(-)
create mode 100644 p2p/discover/table_reval_test.go
diff --git a/p2p/discover/node.go b/p2p/discover/node.go
index 8f7f9b50ef79..8e8177937254 100644
--- a/p2p/discover/node.go
+++ b/p2p/discover/node.go
@@ -41,6 +41,7 @@ type BucketNode struct {
// The fields of Node may not be modified.
type node struct {
*enode.Node
+ revalList *revalidationList
addedToTable time.Time // first time node was added to bucket or replacement list
addedToBucket time.Time // time it was added in the actual bucket
livenessChecks uint // how often liveness was checked
diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go
index 9a13900ebca4..01a5bdb6fa52 100644
--- a/p2p/discover/table_reval.go
+++ b/p2p/discover/table_reval.go
@@ -39,7 +39,6 @@ type tableRevalidation struct {
type revalidationResponse struct {
n *node
newRecord *enode.Node
- list *revalidationList
didRespond bool
}
@@ -60,9 +59,10 @@ func (tr *tableRevalidation) nodeAdded(tab *Table, n *node) {
// nodeRemoved is called when a node was removed from the table.
func (tr *tableRevalidation) nodeRemoved(n *node) {
- if !tr.fast.remove(n) {
- tr.slow.remove(n)
+ if n.revalList == nil {
+ panic(fmt.Errorf("removed node %v has nil revalList", n.ID()))
}
+ n.revalList.remove(n)
}
// run performs node revalidation.
@@ -70,11 +70,11 @@ func (tr *tableRevalidation) nodeRemoved(n *node) {
// to schedule a timer. However, run can be called at any time.
func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) {
if n := tr.fast.get(now, &tab.rand, tr.activeReq); n != nil {
- tr.startRequest(tab, &tr.fast, n)
+ tr.startRequest(tab, n)
tr.fast.schedule(now, &tab.rand)
}
if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil {
- tr.startRequest(tab, &tr.slow, n)
+ tr.startRequest(tab, n)
tr.slow.schedule(now, &tab.rand)
}
@@ -82,12 +82,12 @@ func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mcloc
}
// startRequest spawns a revalidation request for node n.
-func (tr *tableRevalidation) startRequest(tab *Table, list *revalidationList, n *node) {
+func (tr *tableRevalidation) startRequest(tab *Table, n *node) {
if _, ok := tr.activeReq[n.ID()]; ok {
- panic(fmt.Errorf("duplicate startRequest (list %q, node %v)", list.name, n.ID()))
+ panic(fmt.Errorf("duplicate startRequest (node %v)", n.ID()))
}
tr.activeReq[n.ID()] = struct{}{}
- resp := revalidationResponse{n: n, list: list}
+ resp := revalidationResponse{n: n}
// Fetch the node while holding lock.
tab.mutex.Lock()
@@ -120,11 +120,28 @@ func (tab *Table) doRevalidate(resp revalidationResponse, node *enode.Node) {
// handleResponse processes the result of a revalidation request.
func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationResponse) {
- now := tab.cfg.Clock.Now()
- n := resp.n
- b := tab.bucket(n.ID())
+ var (
+ now = tab.cfg.Clock.Now()
+ n = resp.n
+ b = tab.bucket(n.ID())
+ )
delete(tr.activeReq, n.ID())
+ // If the node was removed from the table while getting checked, we need to stop
+ // processing here to avoid re-adding it.
+ if n.revalList == nil {
+ return
+ }
+
+ // Store potential seeds in database.
+ // This is done via defer to avoid holding Table lock while writing to DB.
+ defer func() {
+ if n.isValidatedLive && n.livenessChecks > 5 {
+ tab.db.UpdateNode(resp.n.Node)
+ }
+ }()
+
+ // Remaining logic needs access to Table internals.
tab.mutex.Lock()
defer tab.mutex.Unlock()
@@ -134,7 +151,7 @@ func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationRespons
if n.livenessChecks <= 0 {
tab.deleteInBucket(b, n.ID())
} else {
- tr.moveToList(&tr.fast, resp.list, n, now, &tab.rand)
+ tr.moveToList(&tr.fast, n, now, &tab.rand)
}
return
}
@@ -151,27 +168,23 @@ func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationRespons
n.isValidatedLive = false
}
}
- tab.log.Debug("Revalidated node", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", resp.list.name)
+ tab.log.Debug("Revalidated node", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList)
// Move node over to slow queue after first validation.
if !endpointChanged {
- tr.moveToList(&tr.slow, resp.list, n, now, &tab.rand)
+ tr.moveToList(&tr.slow, n, now, &tab.rand)
} else {
- tr.moveToList(&tr.fast, resp.list, n, now, &tab.rand)
- }
-
- // Store potential seeds in database.
- if n.isValidatedLive && n.livenessChecks > 5 {
- tab.db.UpdateNode(resp.n.Node)
+ tr.moveToList(&tr.fast, n, now, &tab.rand)
}
}
-func (tr *tableRevalidation) moveToList(dest, source *revalidationList, n *node, now mclock.AbsTime, rand randomSource) {
- if source == dest {
+// moveToList ensures n is in the 'dest' list.
+func (tr *tableRevalidation) moveToList(dest *revalidationList, n *node, now mclock.AbsTime, rand randomSource) {
+ if n.revalList == dest {
return
}
- if !source.remove(n) {
- panic(fmt.Errorf("moveToList(%q -> %q): node %v not in source list", source.name, dest.name, n.ID()))
+ if n.revalList != nil {
+ n.revalList.remove(n)
}
dest.push(n, now, rand)
}
@@ -208,16 +221,23 @@ func (list *revalidationList) push(n *node, now mclock.AbsTime, rand randomSourc
if list.nextTime == never {
list.schedule(now, rand)
}
+ n.revalList = list
}
-func (list *revalidationList) remove(n *node) bool {
+func (list *revalidationList) remove(n *node) {
i := slices.Index(list.nodes, n)
if i == -1 {
- return false
+ panic(fmt.Errorf("node %v not found in list", n.ID()))
}
list.nodes = slices.Delete(list.nodes, i, i+1)
if len(list.nodes) == 0 {
list.nextTime = never
}
- return true
+ n.revalList = nil
+}
+
+func (list *revalidationList) contains(id enode.ID) bool {
+ return slices.ContainsFunc(list.nodes, func(n *node) bool {
+ return n.ID() == id
+ })
}
diff --git a/p2p/discover/table_reval_test.go b/p2p/discover/table_reval_test.go
new file mode 100644
index 000000000000..3adf577ae444
--- /dev/null
+++ b/p2p/discover/table_reval_test.go
@@ -0,0 +1,70 @@
+// 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 .
+
+package discover
+
+import (
+ "net"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common/mclock"
+)
+
+// This test checks that revalidation can handle a node disappearing while
+// a request is active.
+func TestRevalidationNodeRemoved(t *testing.T) {
+ var (
+ clock mclock.Simulated
+ transport = newPingRecorder()
+ tab, db = newInactiveTestTable(transport, Config{Clock: &clock})
+ tr = &tab.revalidation
+ )
+ defer db.Close()
+
+ // Fill a bucket.
+ node := nodeAtDistance(tab.self().ID(), 255, net.IP{77, 88, 99, 1})
+ tab.handleAddNode(addNodeOp{node: node})
+
+ // Start a revalidation request. Schedule once to get the next start time,
+ // then advance the clock to that point and schedule again to start.
+ next := tr.run(tab, clock.Now())
+ clock.Run(time.Duration(next + 1))
+ tr.run(tab, clock.Now())
+ if len(tr.activeReq) != 1 {
+ t.Fatal("revalidation request did not start:", tr.activeReq)
+ }
+
+ // Delete the node.
+ tab.deleteInBucket(tab.bucket(node.ID()), node.ID())
+
+ // Now finish the revalidation request.
+ var resp revalidationResponse
+ select {
+ case resp = <-tab.revalResponseCh:
+ case <-time.After(1 * time.Second):
+ t.Fatal("timed out waiting for revalidation")
+ }
+ tr.handleResponse(tab, resp)
+
+ // Ensure the node was not re-added to the table.
+ if tab.getNode(node.ID()) != nil {
+ t.Fatal("node was re-added to Table")
+ }
+ if tr.fast.contains(node.ID()) || tr.slow.contains(node.ID()) {
+ t.Fatal("removed node contained in revalidation list")
+ }
+}
diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go
index 0584a74d9b46..7ba4271e9653 100644
--- a/p2p/discover/table_util_test.go
+++ b/p2p/discover/table_util_test.go
@@ -44,10 +44,15 @@ func init() {
}
func newTestTable(t transport, cfg Config) (*Table, *enode.DB) {
- db, _ := enode.OpenDB("")
- tab, _ := newTable(t, db, cfg)
+ tab, db := newInactiveTestTable(t, cfg)
go tab.loop()
+ return tab, db
+}
+// newInactiveTestTable creates a Table without running the main loop.
+func newInactiveTestTable(t transport, cfg Config) (*Table, *enode.DB) {
+ db, _ := enode.OpenDB("")
+ tab, _ := newTable(t, db, cfg)
return tab, db
}
From be1e580459b06936cd03a7c53d5061f85cfda3d2 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Thu, 23 May 2024 14:27:03 +0200
Subject: [PATCH 086/103] p2p/enode: fix endpoint determination for IPv6
(#29801)
enode.Node has separate accessor functions for getting the IP, UDP port and TCP port.
These methods performed separate checks for attributes set in the ENR.
With this PR, the accessor methods will now return cached information, and the endpoint is
determined when the node is created. The logic to determine the preferred endpoint is now
more correct, and considers how 'global' each address is when both IPv4 and IPv6 addresses
are present in the ENR.
---
p2p/enode/idscheme.go | 3 +-
p2p/enode/node.go | 138 +++++++++++++++++++++++++----------
p2p/enode/node_test.go | 162 +++++++++++++++++++++++++++++++++++++++++
p2p/enode/nodedb.go | 13 ++--
p2p/enode/urlv4.go | 2 +-
p2p/enr/entries.go | 55 ++++++++++++++
6 files changed, 325 insertions(+), 48 deletions(-)
diff --git a/p2p/enode/idscheme.go b/p2p/enode/idscheme.go
index 66c04e917b09..d07ea23a8cff 100644
--- a/p2p/enode/idscheme.go
+++ b/p2p/enode/idscheme.go
@@ -172,6 +172,5 @@ func SignNull(r *enr.Record, id ID) *Node {
if err := r.SetSig(NullID{}, []byte{}); err != nil {
panic(err)
}
-
- return &Node{r: *r, id: id}
+ return newNodeWithID(r, id)
}
diff --git a/p2p/enode/node.go b/p2p/enode/node.go
index c96c38ade5dd..68ee0fda1719 100644
--- a/p2p/enode/node.go
+++ b/p2p/enode/node.go
@@ -24,6 +24,7 @@ import (
"fmt"
"math/bits"
"net"
+ "net/netip"
"strings"
"github.com/ethereum/go-ethereum/p2p/enr"
@@ -36,6 +37,10 @@ var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded reco
type Node struct {
r enr.Record
id ID
+ // endpoint information
+ ip netip.Addr
+ udp uint16
+ tcp uint16
}
// New wraps a node record. The record must be valid according to the given
@@ -44,13 +49,76 @@ func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) {
if err := r.VerifySignature(validSchemes); err != nil {
return nil, err
}
+ var id ID
+ if n := copy(id[:], validSchemes.NodeAddr(r)); n != len(id) {
+ return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(id))
+ }
+ return newNodeWithID(r, id), nil
+}
- node := &Node{r: *r}
- if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) {
- return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{}))
+func newNodeWithID(r *enr.Record, id ID) *Node {
+ n := &Node{r: *r, id: id}
+ // Set the preferred endpoint.
+ // Here we decide between IPv4 and IPv6, choosing the 'most global' address.
+ var ip4 netip.Addr
+ var ip6 netip.Addr
+ n.Load((*enr.IPv4Addr)(&ip4))
+ n.Load((*enr.IPv6Addr)(&ip6))
+ valid4 := validIP(ip4)
+ valid6 := validIP(ip6)
+ switch {
+ case valid4 && valid6:
+ if localityScore(ip4) >= localityScore(ip6) {
+ n.setIP4(ip4)
+ } else {
+ n.setIP6(ip6)
+ }
+ case valid4:
+ n.setIP4(ip4)
+ case valid6:
+ n.setIP6(ip6)
}
+ return n
+}
- return node, nil
+// validIP reports whether 'ip' is a valid node endpoint IP address.
+func validIP(ip netip.Addr) bool {
+ return ip.IsValid() && !ip.IsMulticast()
+}
+
+func localityScore(ip netip.Addr) int {
+ switch {
+ case ip.IsUnspecified():
+ return 0
+ case ip.IsLoopback():
+ return 1
+ case ip.IsLinkLocalUnicast():
+ return 2
+ case ip.IsPrivate():
+ return 3
+ default:
+ return 4
+ }
+}
+
+func (n *Node) setIP4(ip netip.Addr) {
+ n.ip = ip
+ n.Load((*enr.UDP)(&n.udp))
+ n.Load((*enr.TCP)(&n.tcp))
+}
+
+func (n *Node) setIP6(ip netip.Addr) {
+ if ip.Is4In6() {
+ n.setIP4(ip)
+ return
+ }
+ n.ip = ip
+ if err := n.Load((*enr.UDP6)(&n.udp)); err != nil {
+ n.Load((*enr.UDP)(&n.udp))
+ }
+ if err := n.Load((*enr.TCP6)(&n.tcp)); err != nil {
+ n.Load((*enr.TCP)(&n.tcp))
+ }
}
// MustParse parses a node record or enode:// URL. It panics if the input is invalid.
@@ -96,50 +164,45 @@ func (n *Node) Seq() uint64 {
return n.r.Seq()
}
-// Incomplete returns true for nodes with no IP address.
-func (n *Node) Incomplete() bool {
- return n.IP() == nil
-}
-
// Load retrieves an entry from the underlying record.
func (n *Node) Load(k enr.Entry) error {
return n.r.Load(k)
}
-// IP returns the IP address of the node. This prefers IPv4 addresses.
+// IP returns the IP address of the node.
func (n *Node) IP() net.IP {
- var (
- ip4 enr.IPv4
- ip6 enr.IPv6
- )
-
- if n.Load(&ip4) == nil {
- return net.IP(ip4)
- }
-
- if n.Load(&ip6) == nil {
- return net.IP(ip6)
- }
+ return net.IP(n.ip.AsSlice())
+}
- return nil
+// IPAddr returns the IP address of the node.
+func (n *Node) IPAddr() netip.Addr {
+ return n.ip
}
// UDP returns the UDP port of the node.
func (n *Node) UDP() int {
- var port enr.UDP
-
- n.Load(&port)
-
- return int(port)
+ return int(n.udp)
}
// TCP returns the TCP port of the node.
func (n *Node) TCP() int {
- var port enr.TCP
+ return int(n.tcp)
+}
- n.Load(&port)
+// UDPEndpoint returns the announced TCP endpoint.
+func (n *Node) UDPEndpoint() (netip.AddrPort, bool) {
+ if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 {
+ return netip.AddrPort{}, false
+ }
+ return netip.AddrPortFrom(n.ip, n.udp), true
+}
- return int(port)
+// TCPEndpoint returns the announced TCP endpoint.
+func (n *Node) TCPEndpoint() (netip.AddrPort, bool) {
+ if !n.ip.IsValid() || n.ip.IsUnspecified() || n.tcp == 0 {
+ return netip.AddrPort{}, false
+ }
+ return netip.AddrPortFrom(n.ip, n.udp), true
}
// Pubkey returns the secp256k1 public key of the node, if present.
@@ -162,18 +225,15 @@ func (n *Node) Record() *enr.Record {
// ValidateComplete checks whether n has a valid IP and UDP port.
// Deprecated: don't use this method.
func (n *Node) ValidateComplete() error {
- if n.Incomplete() {
+ if !n.ip.IsValid() {
return errors.New("missing IP address")
}
-
- if n.UDP() == 0 {
- return errors.New("missing UDP port")
- }
-
- ip := n.IP()
- if ip.IsMulticast() || ip.IsUnspecified() {
+ if n.ip.IsMulticast() || n.ip.IsUnspecified() {
return errors.New("invalid IP (multicast/unspecified)")
}
+ if n.udp == 0 {
+ return errors.New("missing UDP port")
+ }
// Validate the node key (on curve, etc.).
var key Secp256k1
diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go
index 92be7c95ab63..98fb29122af6 100644
--- a/p2p/enode/node_test.go
+++ b/p2p/enode/node_test.go
@@ -21,6 +21,7 @@ import (
"encoding/hex"
"fmt"
"math/big"
+ "net/netip"
"testing"
"testing/quick"
@@ -68,6 +69,167 @@ func TestPythonInterop(t *testing.T) {
}
}
+func TestNodeEndpoints(t *testing.T) {
+ id := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
+ type endpointTest struct {
+ name string
+ node *Node
+ wantIP netip.Addr
+ wantUDP int
+ wantTCP int
+ }
+ tests := []endpointTest{
+ {
+ name: "no-addr",
+ node: func() *Node {
+ var r enr.Record
+ return SignNull(&r, id)
+ }(),
+ },
+ {
+ name: "udp-only",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.UDP(9000))
+ return SignNull(&r, id)
+ }(),
+ },
+ {
+ name: "tcp-only",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.TCP(9000))
+ return SignNull(&r, id)
+ }(),
+ },
+ {
+ name: "ipv4-only-loopback",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("127.0.0.1")))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("127.0.0.1"),
+ },
+ {
+ name: "ipv4-only-unspecified",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("0.0.0.0")))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("0.0.0.0"),
+ },
+ {
+ name: "ipv4-only",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("99.22.33.1")))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("99.22.33.1"),
+ },
+ {
+ name: "ipv6-only",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329")))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("2001::ff00:0042:8329"),
+ },
+ {
+ name: "ipv4-loopback-and-ipv6-global",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("127.0.0.1")))
+ r.Set(enr.UDP(30304))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329")))
+ r.Set(enr.UDP6(30306))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("2001::ff00:0042:8329"),
+ wantUDP: 30306,
+ },
+ {
+ name: "ipv4-unspecified-and-ipv6-loopback",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("0.0.0.0")))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("::1")))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("::1"),
+ },
+ {
+ name: "ipv4-private-and-ipv6-global",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2")))
+ r.Set(enr.UDP(30304))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329")))
+ r.Set(enr.UDP6(30306))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("2001::ff00:0042:8329"),
+ wantUDP: 30306,
+ },
+ {
+ name: "ipv4-local-and-ipv6-global",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("169.254.2.6")))
+ r.Set(enr.UDP(30304))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329")))
+ r.Set(enr.UDP6(30306))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("2001::ff00:0042:8329"),
+ wantUDP: 30306,
+ },
+ {
+ name: "ipv4-private-and-ipv6-private",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2")))
+ r.Set(enr.UDP(30304))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("fd00::abcd:1")))
+ r.Set(enr.UDP6(30306))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("192.168.2.2"),
+ wantUDP: 30304,
+ },
+ {
+ name: "ipv4-private-and-ipv6-link-local",
+ node: func() *Node {
+ var r enr.Record
+ r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2")))
+ r.Set(enr.UDP(30304))
+ r.Set(enr.IPv6Addr(netip.MustParseAddr("fe80::1")))
+ r.Set(enr.UDP6(30306))
+ return SignNull(&r, id)
+ }(),
+ wantIP: netip.MustParseAddr("192.168.2.2"),
+ wantUDP: 30304,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ if test.wantIP != test.node.IPAddr() {
+ t.Errorf("node has wrong IP %v, want %v", test.node.IPAddr(), test.wantIP)
+ }
+ if test.wantUDP != test.node.UDP() {
+ t.Errorf("node has wrong UDP port %d, want %d", test.node.UDP(), test.wantUDP)
+ }
+ if test.wantTCP != test.node.TCP() {
+ t.Errorf("node has wrong TCP port %d, want %d", test.node.TCP(), test.wantTCP)
+ }
+ })
+ }
+}
+
func TestHexID(t *testing.T) {
ref := ID{0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
id1 := HexID("0x00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go
index 2f34ff7a55f6..a83c7a673323 100644
--- a/p2p/enode/nodedb.go
+++ b/p2p/enode/nodedb.go
@@ -26,6 +26,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
@@ -264,14 +265,14 @@ func (db *DB) Node(id ID) *Node {
}
func mustDecodeNode(id, data []byte) *Node {
- node := new(Node)
- if err := rlp.DecodeBytes(data, &node.r); err != nil {
+ var r enr.Record
+ if err := rlp.DecodeBytes(data, &r); err != nil {
panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err))
}
- // Restore node id cache.
- copy(node.id[:], id)
-
- return node
+ if len(id) != len(ID{}) {
+ panic(fmt.Errorf("invalid id length %d", len(id)))
+ }
+ return newNodeWithID(&r, ID(id))
}
// UpdateNode inserts - potentially overwriting - a node into the peer database.
diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go
index 5c31e376ec61..1c8d30263406 100644
--- a/p2p/enode/urlv4.go
+++ b/p2p/enode/urlv4.go
@@ -201,7 +201,7 @@ func (n *Node) URLv4() string {
}
u := url.URL{Scheme: "enode"}
- if n.Incomplete() {
+ if !n.ip.IsValid() {
u.Host = nodeid
} else {
addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
diff --git a/p2p/enr/entries.go b/p2p/enr/entries.go
index 896d67835a8f..d82112b9b446 100644
--- a/p2p/enr/entries.go
+++ b/p2p/enr/entries.go
@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"net"
+ "net/netip"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -178,6 +179,60 @@ func (v *IPv6) DecodeRLP(s *rlp.Stream) error {
return nil
}
+// IPv4Addr is the "ip" key, which holds the IP address of the node.
+type IPv4Addr netip.Addr
+
+func (v IPv4Addr) ENRKey() string { return "ip" }
+
+// EncodeRLP implements rlp.Encoder.
+func (v IPv4Addr) EncodeRLP(w io.Writer) error {
+ addr := netip.Addr(v)
+ if !addr.Is4() {
+ return fmt.Errorf("address is not IPv4")
+ }
+ enc := rlp.NewEncoderBuffer(w)
+ bytes := addr.As4()
+ enc.WriteBytes(bytes[:])
+ return enc.Flush()
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (v *IPv4Addr) DecodeRLP(s *rlp.Stream) error {
+ var bytes [4]byte
+ if err := s.ReadBytes(bytes[:]); err != nil {
+ return err
+ }
+ *v = IPv4Addr(netip.AddrFrom4(bytes))
+ return nil
+}
+
+// IPv6Addr is the "ip6" key, which holds the IP address of the node.
+type IPv6Addr netip.Addr
+
+func (v IPv6Addr) ENRKey() string { return "ip6" }
+
+// EncodeRLP implements rlp.Encoder.
+func (v IPv6Addr) EncodeRLP(w io.Writer) error {
+ addr := netip.Addr(v)
+ if !addr.Is6() {
+ return fmt.Errorf("address is not IPv6")
+ }
+ enc := rlp.NewEncoderBuffer(w)
+ bytes := addr.As16()
+ enc.WriteBytes(bytes[:])
+ return enc.Flush()
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (v *IPv6Addr) DecodeRLP(s *rlp.Stream) error {
+ var bytes [16]byte
+ if err := s.ReadBytes(bytes[:]); err != nil {
+ return err
+ }
+ *v = IPv6Addr(netip.AddrFrom16(bytes))
+ return nil
+}
+
// KeyError is an error related to a key.
type KeyError struct {
Key string
From b2c178d935343c97eee808d76e84e16414417fe8 Mon Sep 17 00:00:00 2001
From: Aaron Chen
Date: Fri, 24 May 2024 05:17:51 +0800
Subject: [PATCH 087/103] p2p/enode: fix TCPEndpoint (#29827)
---
p2p/enode/node.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/p2p/enode/node.go b/p2p/enode/node.go
index 68ee0fda1719..0ed3d2906dcd 100644
--- a/p2p/enode/node.go
+++ b/p2p/enode/node.go
@@ -189,7 +189,7 @@ func (n *Node) TCP() int {
return int(n.tcp)
}
-// UDPEndpoint returns the announced TCP endpoint.
+// UDPEndpoint returns the announced UDP endpoint.
func (n *Node) UDPEndpoint() (netip.AddrPort, bool) {
if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 {
return netip.AddrPort{}, false
@@ -202,7 +202,7 @@ func (n *Node) TCPEndpoint() (netip.AddrPort, bool) {
if !n.ip.IsValid() || n.ip.IsUnspecified() || n.tcp == 0 {
return netip.AddrPort{}, false
}
- return netip.AddrPortFrom(n.ip, n.udp), true
+ return netip.AddrPortFrom(n.ip, n.tcp), true
}
// Pubkey returns the secp256k1 public key of the node, if present.
From 7850a9f5f6bee9085577fb9a75cc9d3c11ad7558 Mon Sep 17 00:00:00 2001
From: lightclient <14004106+lightclient@users.noreply.github.com>
Date: Tue, 28 May 2024 13:30:17 -0600
Subject: [PATCH 088/103] p2p/discover: fix update logic in handleAddNode
(#29836)
It seems the semantic differences between addFoundNode and addInboundNode were lost in
(and are unsure if is available) whereas addInboundNode is for adding nodes that have
contacted the local node and we can verify they are active.
handleAddNode seems to be the consolidation of those two methods, yet it bumps the node in
the bucket (updating it's IP addr) even if the node was not an inbound. This PR fixes
this. It wasn't originally caught in tests like TestTable_addSeenNode because the
manipulation of the node object actually modified the node value used by the test.
New logic is added to reject non-inbound updates unless the sequence number of the
(signed) ENR increases. Inbound updates, which are published by the updated node itself,
are always accepted. If an inbound update changes the endpoint, the node will be
revalidated on an expedited schedule.
Co-authored-by: Felix Lange
---
p2p/discover/table.go | 46 ++++++++----
p2p/discover/table_reval.go | 25 ++++---
p2p/discover/table_reval_test.go | 53 +++++++++++++-
p2p/discover/table_test.go | 122 ++++++++++++++++++++++---------
4 files changed, 186 insertions(+), 60 deletions(-)
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index d5ae9c10f902..577da6e8dc73 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -526,8 +526,9 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
}
b := tab.bucket(req.node.ID())
- if tab.bumpInBucket(b, req.node.Node) {
- // Already in bucket, update record.
+ n, _ := tab.bumpInBucket(b, req.node.Node, req.isInbound)
+ if n != nil {
+ // Already in bucket.
return false
}
if len(b.entries) >= bucketSize {
@@ -620,26 +621,45 @@ func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node {
return rep
}
-// bumpInBucket updates the node record of n in the bucket.
-func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node) bool {
+// bumpInBucket updates a node record if it exists in the bucket.
+// The second return value reports whether the node's endpoint (IP/port) was updated.
+func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node, isInbound bool) (n *node, endpointChanged bool) {
i := slices.IndexFunc(b.entries, func(elem *node) bool {
return elem.ID() == newRecord.ID()
})
if i == -1 {
- return false
+ return nil, false // not in bucket
+ }
+ n = b.entries[i]
+
+ // For inbound updates (from the node itself) we accept any change, even if it sets
+ // back the sequence number. For found nodes (!isInbound), seq has to advance. Note
+ // this check also ensures found discv4 nodes (which always have seq=0) can't be
+ // updated.
+ if newRecord.Seq() <= n.Seq() && !isInbound {
+ return n, false
}
- if !newRecord.IP().Equal(b.entries[i].IP()) {
- // Endpoint has changed, ensure that the new IP fits into table limits.
- tab.removeIP(b, b.entries[i].IP())
+ // Check endpoint update against IP limits.
+ ipchanged := newRecord.IPAddr() != n.IPAddr()
+ portchanged := newRecord.UDP() != n.UDP()
+ if ipchanged {
+ tab.removeIP(b, n.IP())
if !tab.addIP(b, newRecord.IP()) {
- // It doesn't, put the previous one back.
- tab.addIP(b, b.entries[i].IP())
- return false
+ // It doesn't fit with the limit, put the previous record back.
+ tab.addIP(b, n.IP())
+ return n, false
}
}
- b.entries[i].Node = newRecord
- return true
+
+ // Apply update.
+ n.Node = newRecord
+ if ipchanged || portchanged {
+ // Ensure node is revalidated quickly for endpoint changes.
+ tab.revalidation.nodeEndpointChanged(tab, n)
+ return n, true
+ }
+ return n, false
}
func (tab *Table) handleTrackRequest(op trackRequestOp) {
diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go
index 01a5bdb6fa52..5d185aa8b428 100644
--- a/p2p/discover/table_reval.go
+++ b/p2p/discover/table_reval.go
@@ -28,6 +28,8 @@ import (
const never = mclock.AbsTime(math.MaxInt64)
+const slowRevalidationFactor = 3
+
// tableRevalidation implements the node revalidation process.
// It tracks all nodes contained in Table, and schedules sending PING to them.
type tableRevalidation struct {
@@ -48,7 +50,7 @@ func (tr *tableRevalidation) init(cfg *Config) {
tr.fast.interval = cfg.PingInterval
tr.fast.name = "fast"
tr.slow.nextTime = never
- tr.slow.interval = cfg.PingInterval * 3
+ tr.slow.interval = cfg.PingInterval * slowRevalidationFactor
tr.slow.name = "slow"
}
@@ -65,6 +67,12 @@ func (tr *tableRevalidation) nodeRemoved(n *node) {
n.revalList.remove(n)
}
+// nodeEndpointChanged is called when a change in IP or port is detected.
+func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *node) {
+ n.isValidatedLive = false
+ tr.moveToList(&tr.fast, n, tab.cfg.Clock.Now(), &tab.rand)
+}
+
// run performs node revalidation.
// It returns the next time it should be invoked, which is used in the Table main loop
// to schedule a timer. However, run can be called at any time.
@@ -146,11 +154,11 @@ func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationRespons
defer tab.mutex.Unlock()
if !resp.didRespond {
- // Revalidation failed.
n.livenessChecks /= 3
if n.livenessChecks <= 0 {
tab.deleteInBucket(b, n.ID())
} else {
+ tab.log.Debug("Node revalidation failed", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList.name)
tr.moveToList(&tr.fast, n, now, &tab.rand)
}
return
@@ -159,22 +167,15 @@ func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationRespons
// The node responded.
n.livenessChecks++
n.isValidatedLive = true
+ tab.log.Debug("Node revalidated", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList.name)
var endpointChanged bool
if resp.newRecord != nil {
- endpointChanged = tab.bumpInBucket(b, resp.newRecord)
- if endpointChanged {
- // If the node changed its advertised endpoint, the updated ENR is not served
- // until it has been revalidated.
- n.isValidatedLive = false
- }
+ _, endpointChanged = tab.bumpInBucket(b, resp.newRecord, false)
}
- tab.log.Debug("Revalidated node", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList)
- // Move node over to slow queue after first validation.
+ // Node moves to slow list if it passed and hasn't changed.
if !endpointChanged {
tr.moveToList(&tr.slow, n, now, &tab.rand)
- } else {
- tr.moveToList(&tr.fast, n, now, &tab.rand)
}
}
diff --git a/p2p/discover/table_reval_test.go b/p2p/discover/table_reval_test.go
index 3adf577ae444..d168767e0dd8 100644
--- a/p2p/discover/table_reval_test.go
+++ b/p2p/discover/table_reval_test.go
@@ -22,11 +22,13 @@ import (
"time"
"github.com/ethereum/go-ethereum/common/mclock"
+ "github.com/ethereum/go-ethereum/p2p/enode"
+ "github.com/ethereum/go-ethereum/p2p/enr"
)
// This test checks that revalidation can handle a node disappearing while
// a request is active.
-func TestRevalidationNodeRemoved(t *testing.T) {
+func TestRevalidation_nodeRemoved(t *testing.T) {
var (
clock mclock.Simulated
transport = newPingRecorder()
@@ -35,7 +37,7 @@ func TestRevalidationNodeRemoved(t *testing.T) {
)
defer db.Close()
- // Fill a bucket.
+ // Add a node to the table.
node := nodeAtDistance(tab.self().ID(), 255, net.IP{77, 88, 99, 1})
tab.handleAddNode(addNodeOp{node: node})
@@ -68,3 +70,50 @@ func TestRevalidationNodeRemoved(t *testing.T) {
t.Fatal("removed node contained in revalidation list")
}
}
+
+// This test checks that nodes with an updated endpoint remain in the fast revalidation list.
+func TestRevalidation_endpointUpdate(t *testing.T) {
+ var (
+ clock mclock.Simulated
+ transport = newPingRecorder()
+ tab, db = newInactiveTestTable(transport, Config{Clock: &clock})
+ tr = &tab.revalidation
+ )
+ defer db.Close()
+
+ // Add node to table.
+ node := nodeAtDistance(tab.self().ID(), 255, net.IP{77, 88, 99, 1})
+ tab.handleAddNode(addNodeOp{node: node})
+
+ // Update the record in transport, including endpoint update.
+ record := node.Record()
+ record.Set(enr.IP{100, 100, 100, 100})
+ record.Set(enr.UDP(9999))
+ nodev2 := enode.SignNull(record, node.ID())
+ transport.updateRecord(nodev2)
+
+ // Start a revalidation request. Schedule once to get the next start time,
+ // then advance the clock to that point and schedule again to start.
+ next := tr.run(tab, clock.Now())
+ clock.Run(time.Duration(next + 1))
+ tr.run(tab, clock.Now())
+ if len(tr.activeReq) != 1 {
+ t.Fatal("revalidation request did not start:", tr.activeReq)
+ }
+
+ // Now finish the revalidation request.
+ var resp revalidationResponse
+ select {
+ case resp = <-tab.revalResponseCh:
+ case <-time.After(1 * time.Second):
+ t.Fatal("timed out waiting for revalidation")
+ }
+ tr.handleResponse(tab, resp)
+
+ if !tr.fast.contains(node.ID()) {
+ t.Fatal("node not contained in fast revalidation list")
+ }
+ if node.isValidatedLive {
+ t.Fatal("node is marked live after endpoint change")
+ }
+}
diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go
index 770aee112c31..14d43a7b3c39 100644
--- a/p2p/discover/table_test.go
+++ b/p2p/discover/table_test.go
@@ -133,7 +133,7 @@ func waitForRevalidationPing(t *testing.T, transport *pingRecorder, tab *Table,
simclock := tab.cfg.Clock.(*mclock.Simulated)
maxAttempts := tab.len() * 8
for i := 0; i < maxAttempts; i++ {
- simclock.Run(tab.cfg.PingInterval)
+ simclock.Run(tab.cfg.PingInterval * slowRevalidationFactor)
p := transport.waitPing(2 * time.Second)
if p == nil {
t.Fatal("Table did not send revalidation ping")
@@ -290,7 +290,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
return reflect.ValueOf(t)
}
-func TestTable_addVerifiedNode(t *testing.T) {
+func TestTable_addInboundNode(t *testing.T) {
tab, db := newTestTable(newPingRecorder(), Config{})
<-tab.initDone
@@ -302,30 +302,26 @@ func TestTable_addVerifiedNode(t *testing.T) {
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
tab.addFoundNode(n1)
tab.addFoundNode(n2)
- bucket := tab.bucket(n1.ID())
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
- // Verify bucket content:
- bcontent := []*node{n1, n2}
- if !reflect.DeepEqual(unwrapNodes(bucket.entries), unwrapNodes(bcontent)) {
- t.Fatalf("wrong bucket content: %v", bucket.entries)
- }
-
- // Add a changed version of n2.
+ // Add a changed version of n2. The bucket should be updated.
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
- newn2 := wrapNode(enode.SignNull(newrec, n2.ID()))
- tab.addInboundNode(newn2)
-
- // Check that bucket is updated correctly.
- newBcontent := []*node{n1, newn2}
- if !reflect.DeepEqual(unwrapNodes(bucket.entries), unwrapNodes(newBcontent)) {
- t.Fatalf("wrong bucket content after update: %v", bucket.entries)
- }
-
- checkIPLimitInvariant(t, tab)
+ n2v2 := enode.SignNull(newrec, n2.ID())
+ tab.addInboundNode(wrapNode(n2v2))
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+
+ // Try updating n2 without sequence number change. The update is accepted
+ // because it's inbound.
+ newrec = n2.Record()
+ newrec.Set(enr.IP{100, 100, 100, 100})
+ newrec.SetSeq(n2.Seq())
+ n2v3 := enode.SignNull(newrec, n2.ID())
+ tab.addInboundNode(wrapNode(n2v3))
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v3})
}
-func TestTable_addSeenNode(t *testing.T) {
+func TestTable_addFoundNode(t *testing.T) {
tab, db := newTestTable(newPingRecorder(), Config{})
<-tab.initDone
@@ -337,24 +333,84 @@ func TestTable_addSeenNode(t *testing.T) {
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
tab.addFoundNode(n1)
tab.addFoundNode(n2)
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
- // Verify bucket content:
- bcontent := []*node{n1, n2}
- if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) {
- t.Fatalf("wrong bucket content: %v", tab.bucket(n1.ID()).entries)
- }
-
- // Add a changed version of n2.
+ // Add a changed version of n2. The bucket should be updated.
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
- newn2 := wrapNode(enode.SignNull(newrec, n2.ID()))
- tab.addFoundNode(newn2)
+ n2v2 := enode.SignNull(newrec, n2.ID())
+ tab.addFoundNode(wrapNode(n2v2))
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+
+ // Try updating n2 without a sequence number change.
+ // The update should not be accepted.
+ newrec = n2.Record()
+ newrec.Set(enr.IP{100, 100, 100, 100})
+ newrec.SetSeq(n2.Seq())
+ n2v3 := enode.SignNull(newrec, n2.ID())
+ tab.addFoundNode(wrapNode(n2v3))
+ checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+}
+
+// This test checks that discv4 nodes can update their own endpoint via PING.
+func TestTable_addInboundNodeUpdateV4Accept(t *testing.T) {
+ tab, db := newTestTable(newPingRecorder(), Config{})
+ <-tab.initDone
+ defer db.Close()
+ defer tab.close()
- // Check that bucket content is unchanged.
- if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) {
- t.Fatalf("wrong bucket content after update: %v", tab.bucket(n1.ID()).entries)
+ // Add a v4 node.
+ key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
+ n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
+ tab.addInboundNode(wrapNode(n1))
+ checkBucketContent(t, tab, []*enode.Node{n1})
+
+ // Add an updated version with changed IP.
+ // The update will be accepted because it is inbound.
+ n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
+ tab.addInboundNode(wrapNode(n1v2))
+ checkBucketContent(t, tab, []*enode.Node{n1v2})
+}
+
+// This test checks that discv4 node entries will NOT be updated when a
+// changed record is found.
+func TestTable_addFoundNodeV4UpdateReject(t *testing.T) {
+ tab, db := newTestTable(newPingRecorder(), Config{})
+ <-tab.initDone
+ defer db.Close()
+ defer tab.close()
+
+ // Add a v4 node.
+ key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
+ n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
+ tab.addFoundNode(wrapNode(n1))
+ checkBucketContent(t, tab, []*enode.Node{n1})
+
+ // Add an updated version with changed IP.
+ // The update won't be accepted because it isn't inbound.
+ n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
+ tab.addFoundNode(wrapNode(n1v2))
+ checkBucketContent(t, tab, []*enode.Node{n1})
+}
+
+func checkBucketContent(t *testing.T, tab *Table, nodes []*enode.Node) {
+ t.Helper()
+
+ b := tab.bucket(nodes[0].ID())
+ if reflect.DeepEqual(unwrapNodes(b.entries), nodes) {
+ return
+ }
+ t.Log("wrong bucket content. have nodes:")
+ for _, n := range b.entries {
+ t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IP())
+ }
+ t.Log("want nodes:")
+ for _, n := range nodes {
+ t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IP())
}
+ t.FailNow()
+ // Also check IP limits.
checkIPLimitInvariant(t, tab)
}
From 0e093b2c9c0c1cd2d76dc02630350071594a8017 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Wed, 29 May 2024 15:02:26 +0200
Subject: [PATCH 089/103] p2p/discover: refactor node and endpoint
representation (#29844)
Here we clean up internal uses of type discover.node, converting most code to use
enode.Node instead. The discover.node type used to be the canonical representation of
network hosts before ENR was introduced. Most code worked with *node to avoid conversions
when interacting with Table methods. Since *node also contains internal state of Table and
is a mutable type, using *node outside of Table code is prone to data races. It's also
cleaner not having to wrap/unwrap *enode.Node all the time.
discover.node has been renamed to tableNode to clarify its purpose.
While here, we also change most uses of net.UDPAddr into netip.AddrPort. While this is
technically a separate refactoring from the *node -> *enode.Node change, it is more
convenient because *enode.Node handles IP addresses as netip.Addr. The switch to package
netip in discovery would've happened very soon anyway.
The change to netip.AddrPort stops at certain interface points. For example, since package
p2p/netutil has not been converted to use netip.Addr yet, we still have to convert to
net.IP/net.UDPAddr in a few places.
---
cmd/devp2p/internal/v4test/framework.go | 2 +-
p2p/discover/common.go | 7 +-
p2p/discover/lookup.go | 19 ++-
p2p/discover/metrics.go | 14 +--
p2p/discover/node.go | 68 ++++++----
p2p/discover/table.go | 130 +++++++------------
p2p/discover/table_reval.go | 22 ++--
p2p/discover/table_reval_test.go | 4 +-
p2p/discover/table_test.go | 84 +++++--------
p2p/discover/table_util_test.go | 34 +++--
p2p/discover/v4_lookup_test.go | 31 +++--
p2p/discover/v4_udp.go | 158 +++++++++++++-----------
p2p/discover/v4_udp_test.go | 117 ++++++++----------
p2p/discover/v4wire/v4wire.go | 17 +--
p2p/discover/v5_talk.go | 6 +-
p2p/discover/v5_udp.go | 81 ++++++------
p2p/discover/v5_udp_test.go | 101 +++++++--------
p2p/server.go | 9 +-
18 files changed, 430 insertions(+), 474 deletions(-)
diff --git a/cmd/devp2p/internal/v4test/framework.go b/cmd/devp2p/internal/v4test/framework.go
index 548e449b69d8..826736d21d7c 100644
--- a/cmd/devp2p/internal/v4test/framework.go
+++ b/cmd/devp2p/internal/v4test/framework.go
@@ -128,7 +128,7 @@ func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint {
}
func (te *testenv) remoteEndpoint() v4wire.Endpoint {
- return v4wire.NewEndpoint(te.remoteAddr, 0)
+ return v4wire.NewEndpoint(te.remoteAddr.AddrPort(), 0)
}
func contains(ns []v4wire.Node, key v4wire.Pubkey) bool {
diff --git a/p2p/discover/common.go b/p2p/discover/common.go
index 298d6436785c..ad28a61835e5 100644
--- a/p2p/discover/common.go
+++ b/p2p/discover/common.go
@@ -22,6 +22,7 @@ import (
"encoding/binary"
"math/rand"
"net"
+ "net/netip"
"sync"
"time"
@@ -34,8 +35,8 @@ import (
// UDPConn is a network connection on which discovery can operate.
type UDPConn interface {
- ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
- WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error)
+ ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error)
+ WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (n int, err error)
Close() error
LocalAddr() net.Addr
}
@@ -97,7 +98,7 @@ func ListenUDP(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
// channel if configured.
type ReadPacket struct {
Data []byte
- Addr *net.UDPAddr
+ Addr netip.AddrPort
}
type randomSource interface {
diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go
index aec3b5086bb9..745fde141c9c 100644
--- a/p2p/discover/lookup.go
+++ b/p2p/discover/lookup.go
@@ -29,16 +29,16 @@ import (
// not need to be an actual node identifier.
type lookup struct {
tab *Table
- queryfunc func(*node) ([]*node, error)
- replyCh chan []*node
+ queryfunc queryFunc
+ replyCh chan []*enode.Node
cancelCh <-chan struct{}
asked, seen map[enode.ID]bool
result nodesByDistance
- replyBuffer []*node
+ replyBuffer []*enode.Node
queries int
}
-type queryFunc func(*node) ([]*node, error)
+type queryFunc func(*enode.Node) ([]*enode.Node, error)
func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup {
it := &lookup{
@@ -47,7 +47,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
asked: make(map[enode.ID]bool),
seen: make(map[enode.ID]bool),
result: nodesByDistance{target: target},
- replyCh: make(chan []*node, alpha),
+ replyCh: make(chan []*enode.Node, alpha),
cancelCh: ctx.Done(),
queries: -1,
}
@@ -62,7 +62,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
func (it *lookup) run() []*enode.Node {
for it.advance() {
}
- return unwrapNodes(it.result.entries)
+ return it.result.entries
}
// advance advances the lookup until any new nodes have been found.
@@ -147,7 +147,7 @@ func (it *lookup) slowdown() {
}
}
-func (it *lookup) query(n *node, reply chan<- []*node) {
+func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) {
r, err := it.queryfunc(n)
if !errors.Is(err, errClosed) { // avoid recording failures on shutdown.
success := len(r) > 0
@@ -162,7 +162,7 @@ func (it *lookup) query(n *node, reply chan<- []*node) {
// lookupIterator performs lookup operations and iterates over all seen nodes.
// When a lookup finishes, a new one is created through nextLookup.
type lookupIterator struct {
- buffer []*node
+ buffer []*enode.Node
nextLookup lookupFunc
ctx context.Context
cancel func()
@@ -181,8 +181,7 @@ func (it *lookupIterator) Node() *enode.Node {
if len(it.buffer) == 0 {
return nil
}
-
- return unwrapNode(it.buffer[0])
+ return it.buffer[0]
}
// Next moves to the next node.
diff --git a/p2p/discover/metrics.go b/p2p/discover/metrics.go
index da8e9cb81726..24d2bb17064d 100644
--- a/p2p/discover/metrics.go
+++ b/p2p/discover/metrics.go
@@ -18,7 +18,7 @@ package discover
import (
"fmt"
- "net"
+ "net/netip"
"github.com/ethereum/go-ethereum/metrics"
)
@@ -58,16 +58,16 @@ func newMeteredConn(conn UDPConn) UDPConn {
return &meteredUdpConn{UDPConn: conn}
}
-// Read delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way.
-func (c *meteredUdpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
- n, addr, err = c.UDPConn.ReadFromUDP(b)
+// ReadFromUDPAddrPort delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way.
+func (c *meteredUdpConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
+ n, addr, err = c.UDPConn.ReadFromUDPAddrPort(b)
ingressTrafficMeter.Mark(int64(n))
return n, addr, err
}
-// Write delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way.
-func (c *meteredUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) {
- n, err = c.UDPConn.WriteToUDP(b, addr)
+// WriteToUDP delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way.
+func (c *meteredUdpConn) WriteToUDP(b []byte, addr netip.AddrPort) (n int, err error) {
+ n, err = c.UDPConn.WriteToUDPAddrPort(b, addr)
egressTrafficMeter.Mark(int64(n))
return n, err
}
diff --git a/p2p/discover/node.go b/p2p/discover/node.go
index 8e8177937254..f5cba89633a6 100644
--- a/p2p/discover/node.go
+++ b/p2p/discover/node.go
@@ -21,7 +21,8 @@ import (
"crypto/elliptic"
"errors"
"math/big"
- "net"
+ "slices"
+ "sort"
"time"
"github.com/ethereum/go-ethereum/common/math"
@@ -37,9 +38,8 @@ type BucketNode struct {
Live bool `json:"live"`
}
-// node represents a host on the network.
-// The fields of Node may not be modified.
-type node struct {
+// tableNode is an entry in Table.
+type tableNode struct {
*enode.Node
revalList *revalidationList
addedToTable time.Time // first time node was added to bucket or replacement list
@@ -80,36 +80,60 @@ func (e encPubkey) id() enode.ID {
return enode.ID(crypto.Keccak256Hash(e[:]))
}
-func wrapNode(n *enode.Node) *node {
- return &node{Node: n}
-}
-
-func wrapNodes(ns []*enode.Node) []*node {
- result := make([]*node, len(ns))
+func unwrapNodes(ns []*tableNode) []*enode.Node {
+ result := make([]*enode.Node, len(ns))
for i, n := range ns {
- result[i] = wrapNode(n)
+ result[i] = n.Node
}
return result
}
-func unwrapNode(n *node) *enode.Node {
- return n.Node
+func (n *tableNode) String() string {
+ return n.Node.String()
}
-func unwrapNodes(ns []*node) []*enode.Node {
- result := make([]*enode.Node, len(ns))
- for i, n := range ns {
- result[i] = unwrapNode(n)
+// nodesByDistance is a list of nodes, ordered by distance to target.
+type nodesByDistance struct {
+ entries []*enode.Node
+ target enode.ID
+}
+
+// push adds the given node to the list, keeping the total size below maxElems.
+func (h *nodesByDistance) push(n *enode.Node, maxElems int) {
+ ix := sort.Search(len(h.entries), func(i int) bool {
+ return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
+ })
+
+ end := len(h.entries)
+ if len(h.entries) < maxElems {
+ h.entries = append(h.entries, n)
}
+ if ix < end {
+ // Slide existing entries down to make room.
+ // This will overwrite the entry we just appended.
+ copy(h.entries[ix+1:], h.entries[ix:])
+ h.entries[ix] = n
+ }
+}
- return result
+type nodeType interface {
+ ID() enode.ID
}
-func (n *node) addr() *net.UDPAddr {
- return &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
+// containsID reports whether ns contains a node with the given ID.
+func containsID[N nodeType](ns []N, id enode.ID) bool {
+ for _, n := range ns {
+ if n.ID() == id {
+ return true
+ }
+ }
+ return false
}
-func (n *node) String() string {
- return n.Node.String()
+// deleteNode removes a node from the list.
+func deleteNode[N nodeType](list []N, id enode.ID) []N {
+ return slices.DeleteFunc(list, func(n N) bool {
+ return n.ID() == id
+ })
}
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index 577da6e8dc73..6a64935cceab 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -27,7 +27,6 @@ import (
"fmt"
"net"
"slices"
- "sort"
"sync"
"time"
@@ -65,7 +64,7 @@ const (
type Table struct {
mutex sync.Mutex // protects buckets, bucket content, nursery, rand
buckets [nBuckets]*bucket // index of known nodes by distance
- nursery []*node // bootstrap nodes
+ nursery []*enode.Node // bootstrap nodes
rand reseedingRandom // source of randomness, periodically reseeded
ips netutil.DistinctNetSet
revalidation tableRevalidation
@@ -85,8 +84,8 @@ type Table struct {
closeReq chan struct{}
closed chan struct{}
- nodeAddedHook func(*bucket, *node)
- nodeRemovedHook func(*bucket, *node)
+ nodeAddedHook func(*bucket, *tableNode)
+ nodeRemovedHook func(*bucket, *tableNode)
}
// transport is implemented by the UDP transports.
@@ -101,20 +100,21 @@ type transport interface {
// bucket contains nodes, ordered by their last activity. the entry
// that was most recently active is the first element in entries.
type bucket struct {
- entries []*node // live entries, sorted by time of last contact
- replacements []*node // recently seen nodes to be used if revalidation fails
+ entries []*tableNode // live entries, sorted by time of last contact
+ replacements []*tableNode // recently seen nodes to be used if revalidation fails
ips netutil.DistinctNetSet
index int
}
type addNodeOp struct {
- node *node
- isInbound bool
+ node *enode.Node
+ isInbound bool
+ forceSetLive bool // for tests
}
type trackRequestOp struct {
- node *node
- foundNodes []*node
+ node *enode.Node
+ foundNodes []*enode.Node
success bool
}
@@ -187,7 +187,7 @@ func (tab *Table) getNode(id enode.ID) *enode.Node {
b := tab.bucket(id)
for _, e := range b.entries {
if e.ID() == id {
- return unwrapNode(e)
+ return e.Node
}
}
@@ -204,7 +204,7 @@ func (tab *Table) close() {
// are used to connect to the network if the table is empty and there
// are no known nodes in the database.
func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
- nursery := make([]*node, 0, len(nodes))
+ nursery := make([]*enode.Node, 0, len(nodes))
for _, n := range nodes {
if err := n.ValidateComplete(); err != nil {
return fmt.Errorf("bad bootstrap node %q: %v", n, err)
@@ -213,7 +213,7 @@ func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IP())
continue
}
- nursery = append(nursery, wrapNode(n))
+ nursery = append(nursery, n)
}
tab.nursery = nursery
return nil
@@ -258,9 +258,9 @@ func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *
liveNodes := &nodesByDistance{target: target}
for _, b := range &tab.buckets {
for _, n := range b.entries {
- nodes.push(n, nresults)
+ nodes.push(n.Node, nresults)
if preferLive && n.isValidatedLive {
- liveNodes.push(n, nresults)
+ liveNodes.push(n.Node, nresults)
}
}
}
@@ -312,8 +312,8 @@ func (tab *Table) len() (n int) {
// list.
//
// The caller must not hold tab.mutex.
-func (tab *Table) addFoundNode(n *node) bool {
- op := addNodeOp{node: n, isInbound: false}
+func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool {
+ op := addNodeOp{node: n, isInbound: false, forceSetLive: forceSetLive}
select {
case tab.addNodeCh <- op:
return <-tab.addNodeHandled
@@ -330,7 +330,7 @@ func (tab *Table) addFoundNode(n *node) bool {
// repeatedly.
//
// The caller must not hold tab.mutex.
-func (tab *Table) addInboundNode(n *node) bool {
+func (tab *Table) addInboundNode(n *enode.Node) bool {
op := addNodeOp{node: n, isInbound: true}
select {
case tab.addNodeCh <- op:
@@ -340,7 +340,7 @@ func (tab *Table) addInboundNode(n *node) bool {
}
}
-func (tab *Table) trackRequest(n *node, success bool, foundNodes []*node) {
+func (tab *Table) trackRequest(n *enode.Node, success bool, foundNodes []*enode.Node) {
op := trackRequestOp{n, foundNodes, success}
select {
case tab.trackRequestCh <- op:
@@ -448,14 +448,15 @@ func (tab *Table) doRefresh(done chan struct{}) {
}
func (tab *Table) loadSeedNodes() {
- seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge))
+ seeds := tab.db.QuerySeeds(seedCount, seedMaxAge)
seeds = append(seeds, tab.nursery...)
for i := range seeds {
seed := seeds[i]
if tab.log.Enabled(context.Background(), log.LevelTrace) {
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
- tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
+ addr, _ := seed.UDPEndpoint()
+ tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age)
}
tab.handleAddNode(addNodeOp{node: seed, isInbound: false})
}
@@ -526,7 +527,7 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
}
b := tab.bucket(req.node.ID())
- n, _ := tab.bumpInBucket(b, req.node.Node, req.isInbound)
+ n, _ := tab.bumpInBucket(b, req.node, req.isInbound)
if n != nil {
// Already in bucket.
return false
@@ -542,15 +543,20 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
}
// Add to bucket.
- b.entries = append(b.entries, req.node)
- b.replacements = deleteNode(b.replacements, req.node)
- tab.nodeAdded(b, req.node)
+ wn := &tableNode{Node: req.node}
+ if req.forceSetLive {
+ wn.livenessChecks = 1
+ wn.isValidatedLive = true
+ }
+ b.entries = append(b.entries, wn)
+ b.replacements = deleteNode(b.replacements, wn.ID())
+ tab.nodeAdded(b, wn)
return true
}
// addReplacement adds n to the replacement cache of bucket b.
-func (tab *Table) addReplacement(b *bucket, n *node) {
- if contains(b.replacements, n.ID()) {
+func (tab *Table) addReplacement(b *bucket, n *enode.Node) {
+ if containsID(b.replacements, n.ID()) {
// TODO: update ENR
return
}
@@ -559,16 +565,15 @@ func (tab *Table) addReplacement(b *bucket, n *node) {
return
}
- n.addedToTable = time.Now()
- var removed *node
-
- b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
+ wn := &tableNode{Node: n, addedToTable: time.Now()}
+ var removed *tableNode
+ b.replacements, removed = pushNode(b.replacements, wn, maxReplacements)
if removed != nil {
tab.removeIP(b, removed.IP())
}
}
-func (tab *Table) nodeAdded(b *bucket, n *node) {
+func (tab *Table) nodeAdded(b *bucket, n *tableNode) {
if n.addedToTable == (time.Time{}) {
n.addedToTable = time.Now()
}
@@ -582,7 +587,7 @@ func (tab *Table) nodeAdded(b *bucket, n *node) {
}
}
-func (tab *Table) nodeRemoved(b *bucket, n *node) {
+func (tab *Table) nodeRemoved(b *bucket, n *tableNode) {
tab.revalidation.nodeRemoved(n)
if tab.nodeRemovedHook != nil {
tab.nodeRemovedHook(b, n)
@@ -594,8 +599,8 @@ func (tab *Table) nodeRemoved(b *bucket, n *node) {
// deleteInBucket removes node n from the table.
// If there are replacement nodes in the bucket, the node is replaced.
-func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node {
- index := slices.IndexFunc(b.entries, func(e *node) bool { return e.ID() == id })
+func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *tableNode {
+ index := slices.IndexFunc(b.entries, func(e *tableNode) bool { return e.ID() == id })
if index == -1 {
// Entry has been removed already.
return nil
@@ -623,8 +628,8 @@ func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node {
// bumpInBucket updates a node record if it exists in the bucket.
// The second return value reports whether the node's endpoint (IP/port) was updated.
-func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node, isInbound bool) (n *node, endpointChanged bool) {
- i := slices.IndexFunc(b.entries, func(elem *node) bool {
+func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node, isInbound bool) (n *tableNode, endpointChanged bool) {
+ i := slices.IndexFunc(b.entries, func(elem *tableNode) bool {
return elem.ID() == newRecord.ID()
})
if i == -1 {
@@ -687,22 +692,12 @@ func (tab *Table) handleTrackRequest(op trackRequestOp) {
// Add found nodes.
for _, n := range op.foundNodes {
- tab.handleAddNode(addNodeOp{n, false})
- }
-}
-
-func contains(ns []*node, id enode.ID) bool {
- for _, n := range ns {
- if n.ID() == id {
- return true
- }
+ tab.handleAddNode(addNodeOp{n, false, false})
}
-
- return false
}
// pushNode adds n to the front of list, keeping at most max items.
-func pushNode(list []*node, n *node, max int) ([]*node, *node) {
+func pushNode(list []*tableNode, n *tableNode, max int) ([]*tableNode, *tableNode) {
if len(list) < max {
list = append(list, nil)
}
@@ -713,40 +708,3 @@ func pushNode(list []*node, n *node, max int) ([]*node, *node) {
return list, removed
}
-
-// deleteNode removes n from list.
-func deleteNode(list []*node, n *node) []*node {
- for i := range list {
- if list[i].ID() == n.ID() {
- return append(list[:i], list[i+1:]...)
- }
- }
-
- return list
-}
-
-// nodesByDistance is a list of nodes, ordered by distance to target.
-type nodesByDistance struct {
- entries []*node
- target enode.ID
-}
-
-// push adds the given node to the list, keeping the total size below maxElems.
-func (h *nodesByDistance) push(n *node, maxElems int) {
- ix := sort.Search(len(h.entries), func(i int) bool {
- return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
- })
-
- end := len(h.entries)
-
- if len(h.entries) < maxElems {
- h.entries = append(h.entries, n)
- }
-
- if ix < end {
- // Slide existing entries down to make room.
- // This will overwrite the entry we just appended.
- copy(h.entries[ix+1:], h.entries[ix:])
- h.entries[ix] = n
- }
-}
diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go
index 5d185aa8b428..f2ea8b34fa3e 100644
--- a/p2p/discover/table_reval.go
+++ b/p2p/discover/table_reval.go
@@ -39,7 +39,7 @@ type tableRevalidation struct {
}
type revalidationResponse struct {
- n *node
+ n *tableNode
newRecord *enode.Node
didRespond bool
}
@@ -55,12 +55,12 @@ func (tr *tableRevalidation) init(cfg *Config) {
}
// nodeAdded is called when the table receives a new node.
-func (tr *tableRevalidation) nodeAdded(tab *Table, n *node) {
+func (tr *tableRevalidation) nodeAdded(tab *Table, n *tableNode) {
tr.fast.push(n, tab.cfg.Clock.Now(), &tab.rand)
}
// nodeRemoved is called when a node was removed from the table.
-func (tr *tableRevalidation) nodeRemoved(n *node) {
+func (tr *tableRevalidation) nodeRemoved(n *tableNode) {
if n.revalList == nil {
panic(fmt.Errorf("removed node %v has nil revalList", n.ID()))
}
@@ -68,7 +68,7 @@ func (tr *tableRevalidation) nodeRemoved(n *node) {
}
// nodeEndpointChanged is called when a change in IP or port is detected.
-func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *node) {
+func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *tableNode) {
n.isValidatedLive = false
tr.moveToList(&tr.fast, n, tab.cfg.Clock.Now(), &tab.rand)
}
@@ -90,7 +90,7 @@ func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mcloc
}
// startRequest spawns a revalidation request for node n.
-func (tr *tableRevalidation) startRequest(tab *Table, n *node) {
+func (tr *tableRevalidation) startRequest(tab *Table, n *tableNode) {
if _, ok := tr.activeReq[n.ID()]; ok {
panic(fmt.Errorf("duplicate startRequest (node %v)", n.ID()))
}
@@ -180,7 +180,7 @@ func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationRespons
}
// moveToList ensures n is in the 'dest' list.
-func (tr *tableRevalidation) moveToList(dest *revalidationList, n *node, now mclock.AbsTime, rand randomSource) {
+func (tr *tableRevalidation) moveToList(dest *revalidationList, n *tableNode, now mclock.AbsTime, rand randomSource) {
if n.revalList == dest {
return
}
@@ -192,14 +192,14 @@ func (tr *tableRevalidation) moveToList(dest *revalidationList, n *node, now mcl
// revalidationList holds a list nodes and the next revalidation time.
type revalidationList struct {
- nodes []*node
+ nodes []*tableNode
nextTime mclock.AbsTime
interval time.Duration
name string
}
// get returns a random node from the queue. Nodes in the 'exclude' map are not returned.
-func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *node {
+func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode {
if now < list.nextTime || len(list.nodes) == 0 {
return nil
}
@@ -217,7 +217,7 @@ func (list *revalidationList) schedule(now mclock.AbsTime, rand randomSource) {
list.nextTime = now.Add(time.Duration(rand.Int63n(int64(list.interval))))
}
-func (list *revalidationList) push(n *node, now mclock.AbsTime, rand randomSource) {
+func (list *revalidationList) push(n *tableNode, now mclock.AbsTime, rand randomSource) {
list.nodes = append(list.nodes, n)
if list.nextTime == never {
list.schedule(now, rand)
@@ -225,7 +225,7 @@ func (list *revalidationList) push(n *node, now mclock.AbsTime, rand randomSourc
n.revalList = list
}
-func (list *revalidationList) remove(n *node) {
+func (list *revalidationList) remove(n *tableNode) {
i := slices.Index(list.nodes, n)
if i == -1 {
panic(fmt.Errorf("node %v not found in list", n.ID()))
@@ -238,7 +238,7 @@ func (list *revalidationList) remove(n *node) {
}
func (list *revalidationList) contains(id enode.ID) bool {
- return slices.ContainsFunc(list.nodes, func(n *node) bool {
+ return slices.ContainsFunc(list.nodes, func(n *tableNode) bool {
return n.ID() == id
})
}
diff --git a/p2p/discover/table_reval_test.go b/p2p/discover/table_reval_test.go
index d168767e0dd8..360544393439 100644
--- a/p2p/discover/table_reval_test.go
+++ b/p2p/discover/table_reval_test.go
@@ -110,10 +110,10 @@ func TestRevalidation_endpointUpdate(t *testing.T) {
}
tr.handleResponse(tab, resp)
- if !tr.fast.contains(node.ID()) {
+ if tr.fast.nodes[0].ID() != node.ID() {
t.Fatal("node not contained in fast revalidation list")
}
- if node.isValidatedLive {
+ if tr.fast.nodes[0].isValidatedLive {
t.Fatal("node is marked live after endpoint change")
}
}
diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go
index 14d43a7b3c39..013af5034d1f 100644
--- a/p2p/discover/table_test.go
+++ b/p2p/discover/table_test.go
@@ -22,6 +22,7 @@ import (
"math/rand"
"net"
"reflect"
+ "slices"
"testing"
"testing/quick"
"time"
@@ -64,7 +65,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
// Fill up the sender's bucket.
replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
- replacementNode := wrapNode(enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99))
+ replacementNode := enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99)
last := fillBucket(tab, replacementNode.ID())
tab.mutex.Lock()
nodeEvents := newNodeEventRecorder(128)
@@ -78,7 +79,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
transport.dead[replacementNode.ID()] = !newNodeIsResponding
// Add replacement node to table.
- tab.addFoundNode(replacementNode)
+ tab.addFoundNode(replacementNode, false)
t.Log("last:", last.ID())
t.Log("replacement:", replacementNode.ID())
@@ -116,12 +117,12 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
if l := len(bucket.entries); l != wantSize {
t.Errorf("wrong bucket size after revalidation: got %d, want %d", l, wantSize)
}
- if ok := contains(bucket.entries, last.ID()); ok != lastInBucketIsResponding {
+ if ok := containsID(bucket.entries, last.ID()); ok != lastInBucketIsResponding {
t.Errorf("revalidated node found: %t, want: %t", ok, lastInBucketIsResponding)
}
wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
- if ok := contains(bucket.entries, replacementNode.ID()); ok != wantNewEntry {
+ if ok := containsID(bucket.entries, replacementNode.ID()); ok != wantNewEntry {
t.Errorf("replacement node found: %t, want: %t", ok, wantNewEntry)
}
}
@@ -155,7 +156,7 @@ func TestTable_IPLimit(t *testing.T) {
for i := 0; i < tableIPLimit+1; i++ {
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
- tab.addFoundNode(n)
+ tab.addFoundNode(n, false)
}
if tab.len() > tableIPLimit {
@@ -175,7 +176,7 @@ func TestTable_BucketIPLimit(t *testing.T) {
d := 3
for i := 0; i < bucketIPLimit+1; i++ {
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
- tab.addFoundNode(n)
+ tab.addFoundNode(n, false)
}
if tab.len() > bucketIPLimit {
@@ -242,7 +243,7 @@ func TestTable_findnodeByID(t *testing.T) {
// check that the result nodes have minimum distance to target.
for _, b := range tab.buckets {
for _, n := range b.entries {
- if contains(result, n.ID()) {
+ if containsID(result, n.ID()) {
continue // don't run the check below for nodes in result
}
@@ -268,7 +269,7 @@ func TestTable_findnodeByID(t *testing.T) {
type closeTest struct {
Self enode.ID
Target enode.ID
- All []*node
+ All []*enode.Node
N int
}
@@ -282,8 +283,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
r := new(enr.Record)
r.Set(enr.IP(genIP(rand)))
- n := wrapNode(enode.SignNull(r, id))
- n.livenessChecks = 1
+ n := enode.SignNull(r, id)
t.All = append(t.All, n)
}
@@ -300,16 +300,16 @@ func TestTable_addInboundNode(t *testing.T) {
// Insert two nodes.
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
- tab.addFoundNode(n1)
- tab.addFoundNode(n2)
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
+ tab.addFoundNode(n1, false)
+ tab.addFoundNode(n2, false)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2})
// Add a changed version of n2. The bucket should be updated.
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
n2v2 := enode.SignNull(newrec, n2.ID())
- tab.addInboundNode(wrapNode(n2v2))
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+ tab.addInboundNode(n2v2)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
// Try updating n2 without sequence number change. The update is accepted
// because it's inbound.
@@ -317,8 +317,8 @@ func TestTable_addInboundNode(t *testing.T) {
newrec.Set(enr.IP{100, 100, 100, 100})
newrec.SetSeq(n2.Seq())
n2v3 := enode.SignNull(newrec, n2.ID())
- tab.addInboundNode(wrapNode(n2v3))
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v3})
+ tab.addInboundNode(n2v3)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2v3})
}
func TestTable_addFoundNode(t *testing.T) {
@@ -331,16 +331,16 @@ func TestTable_addFoundNode(t *testing.T) {
// Insert two nodes.
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
- tab.addFoundNode(n1)
- tab.addFoundNode(n2)
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
+ tab.addFoundNode(n1, false)
+ tab.addFoundNode(n2, false)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2})
// Add a changed version of n2. The bucket should be updated.
newrec := n2.Record()
newrec.Set(enr.IP{99, 99, 99, 99})
n2v2 := enode.SignNull(newrec, n2.ID())
- tab.addFoundNode(wrapNode(n2v2))
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+ tab.addFoundNode(n2v2, false)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
// Try updating n2 without a sequence number change.
// The update should not be accepted.
@@ -348,8 +348,8 @@ func TestTable_addFoundNode(t *testing.T) {
newrec.Set(enr.IP{100, 100, 100, 100})
newrec.SetSeq(n2.Seq())
n2v3 := enode.SignNull(newrec, n2.ID())
- tab.addFoundNode(wrapNode(n2v3))
- checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
+ tab.addFoundNode(n2v3, false)
+ checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
}
// This test checks that discv4 nodes can update their own endpoint via PING.
@@ -362,13 +362,13 @@ func TestTable_addInboundNodeUpdateV4Accept(t *testing.T) {
// Add a v4 node.
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
- tab.addInboundNode(wrapNode(n1))
+ tab.addInboundNode(n1)
checkBucketContent(t, tab, []*enode.Node{n1})
// Add an updated version with changed IP.
// The update will be accepted because it is inbound.
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
- tab.addInboundNode(wrapNode(n1v2))
+ tab.addInboundNode(n1v2)
checkBucketContent(t, tab, []*enode.Node{n1v2})
}
@@ -383,13 +383,13 @@ func TestTable_addFoundNodeV4UpdateReject(t *testing.T) {
// Add a v4 node.
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
- tab.addFoundNode(wrapNode(n1))
+ tab.addFoundNode(n1, false)
checkBucketContent(t, tab, []*enode.Node{n1})
// Add an updated version with changed IP.
// The update won't be accepted because it isn't inbound.
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
- tab.addFoundNode(wrapNode(n1v2))
+ tab.addFoundNode(n1v2, false)
checkBucketContent(t, tab, []*enode.Node{n1})
}
@@ -433,8 +433,8 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
r.Set(enr.IP(net.IP{127, 0, 0, 1}))
id := enode.ID{1}
- n1 := wrapNode(enode.SignNull(&r, id))
- tab.addFoundNode(n1)
+ n1 := enode.SignNull(&r, id)
+ tab.addFoundNode(n1, false)
// Update the node record.
r.Set(enr.WithEntry("foo", "bar"))
@@ -459,7 +459,7 @@ func TestNodesPush(t *testing.T) {
n1 := nodeAtDistance(target, 255, intIP(1))
n2 := nodeAtDistance(target, 254, intIP(2))
n3 := nodeAtDistance(target, 253, intIP(3))
- perm := [][]*node{
+ perm := [][]*enode.Node{
{n3, n2, n1},
{n3, n1, n2},
{n2, n3, n1},
@@ -474,8 +474,7 @@ func TestNodesPush(t *testing.T) {
for _, n := range nodes {
list.push(n, 3)
}
-
- if !slicesEqual(list.entries, perm[0], nodeIDEqual) {
+ if !slices.EqualFunc(list.entries, perm[0], nodeIDEqual) {
t.Fatal("not equal")
}
}
@@ -486,31 +485,16 @@ func TestNodesPush(t *testing.T) {
for _, n := range nodes {
list.push(n, 2)
}
-
- if !slicesEqual(list.entries, perm[0][:2], nodeIDEqual) {
+ if !slices.EqualFunc(list.entries, perm[0][:2], nodeIDEqual) {
t.Fatal("not equal")
}
}
}
-func nodeIDEqual(n1, n2 *node) bool {
+func nodeIDEqual[N nodeType](n1, n2 N) bool {
return n1.ID() == n2.ID()
}
-func slicesEqual[T any](s1, s2 []T, check func(e1, e2 T) bool) bool {
- if len(s1) != len(s2) {
- return false
- }
-
- for i := range s1 {
- if !check(s1[i], s2[i]) {
- return false
- }
- }
-
- return true
-}
-
// gen wraps quick.Value so it's easier to use.
// it generates a random value of the given value's type.
func gen(typ interface{}, rand *rand.Rand) interface{} {
diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go
index 7ba4271e9653..720164d6d0c4 100644
--- a/p2p/discover/table_util_test.go
+++ b/p2p/discover/table_util_test.go
@@ -57,19 +57,19 @@ func newInactiveTestTable(t transport, cfg Config) (*Table, *enode.DB) {
}
// nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
-func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node {
+func nodeAtDistance(base enode.ID, ld int, ip net.IP) *enode.Node {
var r enr.Record
r.Set(enr.IP(ip))
r.Set(enr.UDP(30303))
- return wrapNode(enode.SignNull(&r, idAtDistance(base, ld)))
+ return enode.SignNull(&r, idAtDistance(base, ld))
}
// nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld.
func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node {
results := make([]*enode.Node, n)
for i := range results {
- results[i] = unwrapNode(nodeAtDistance(base, ld, intIP(i)))
+ results[i] = nodeAtDistance(base, ld, intIP(i))
}
return results
@@ -112,12 +112,12 @@ func intIP(i int) net.IP {
}
// fillBucket inserts nodes into the given bucket until it is full.
-func fillBucket(tab *Table, id enode.ID) (last *node) {
+func fillBucket(tab *Table, id enode.ID) (last *tableNode) {
ld := enode.LogDist(tab.self().ID(), id)
b := tab.bucket(id)
for len(b.entries) < bucketSize {
node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
- if !tab.addFoundNode(node) {
+ if !tab.addFoundNode(node, false) {
panic("node not added")
}
}
@@ -127,13 +127,9 @@ func fillBucket(tab *Table, id enode.ID) (last *node) {
// fillTable adds nodes the table to the end of their corresponding bucket
// if the bucket is not full. The caller must not hold tab.mutex.
-func fillTable(tab *Table, nodes []*node, setLive bool) {
+func fillTable(tab *Table, nodes []*enode.Node, setLive bool) {
for _, n := range nodes {
- if setLive {
- n.livenessChecks = 1
- n.isValidatedLive = true
- }
- tab.addFoundNode(n)
+ tab.addFoundNode(n, setLive)
}
}
@@ -231,7 +227,7 @@ func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) {
return t.records[n.ID()], nil
}
-func hasDuplicates(slice []*node) bool {
+func hasDuplicates(slice []*enode.Node) bool {
seen := make(map[enode.ID]bool, len(slice))
for i, e := range slice {
if e == nil {
@@ -281,14 +277,14 @@ func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool {
return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP())
}
-func sortByID(nodes []*enode.Node) {
- slices.SortFunc(nodes, func(a, b *enode.Node) int {
+func sortByID[N nodeType](nodes []N) {
+ slices.SortFunc(nodes, func(a, b N) int {
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
})
}
-func sortedByDistanceTo(distbase enode.ID, slice []*node) bool {
- return slices.IsSortedFunc(slice, func(a, b *node) int {
+func sortedByDistanceTo(distbase enode.ID, slice []*enode.Node) bool {
+ return slices.IsSortedFunc(slice, func(a, b *enode.Node) int {
return enode.DistCmp(distbase, a.ID(), b.ID())
})
}
@@ -329,7 +325,7 @@ type nodeEventRecorder struct {
}
type recordedNodeEvent struct {
- node *node
+ node *tableNode
added bool
}
@@ -339,7 +335,7 @@ func newNodeEventRecorder(buffer int) *nodeEventRecorder {
}
}
-func (set *nodeEventRecorder) nodeAdded(b *bucket, n *node) {
+func (set *nodeEventRecorder) nodeAdded(b *bucket, n *tableNode) {
select {
case set.evc <- recordedNodeEvent{n, true}:
default:
@@ -347,7 +343,7 @@ func (set *nodeEventRecorder) nodeAdded(b *bucket, n *node) {
}
}
-func (set *nodeEventRecorder) nodeRemoved(b *bucket, n *node) {
+func (set *nodeEventRecorder) nodeRemoved(b *bucket, n *tableNode) {
select {
case set.evc <- recordedNodeEvent{n, false}:
default:
diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go
index 53150469eae4..8997031d2707 100644
--- a/p2p/discover/v4_lookup_test.go
+++ b/p2p/discover/v4_lookup_test.go
@@ -19,14 +19,14 @@ package discover
import (
"crypto/ecdsa"
"fmt"
- "net"
+ "net/netip"
+ "slices"
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/discover/v4wire"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
- "golang.org/x/exp/slices"
)
func TestUDPv4_Lookup(t *testing.T) {
@@ -40,7 +40,7 @@ func TestUDPv4_Lookup(t *testing.T) {
}
// Seed table with initial node.
- fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))}, true)
+ fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true)
// Start the lookup.
resultC := make(chan []*enode.Node, 1)
@@ -76,9 +76,9 @@ func TestUDPv4_LookupIterator(t *testing.T) {
defer test.close()
// Seed table with initial nodes.
- bootnodes := make([]*node, len(lookupTestnet.dists[256]))
+ bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256]))
for i := range lookupTestnet.dists[256] {
- bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
+ bootnodes[i] = lookupTestnet.node(256, i)
}
fillTable(test.table, bootnodes, true)
go serveTestnet(test, lookupTestnet)
@@ -115,9 +115,9 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {
defer test.close()
// Seed table with initial nodes.
- bootnodes := make([]*node, len(lookupTestnet.dists[256]))
+ bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256]))
for i := range lookupTestnet.dists[256] {
- bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
+ bootnodes[i] = lookupTestnet.node(256, i)
}
fillTable(test.table, bootnodes, true)
go serveTestnet(test, lookupTestnet)
@@ -148,7 +148,7 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {
func serveTestnet(test *udpTest, testnet *preminedTestnet) {
for done := false; !done; {
- done = test.waitPacketOut(func(p v4wire.Packet, to *net.UDPAddr, hash []byte) {
+ done = test.waitPacketOut(func(p v4wire.Packet, to netip.AddrPort, hash []byte) {
n, key := testnet.nodeByAddr(to)
switch p.(type) {
case *v4wire.Ping:
@@ -171,12 +171,10 @@ func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node
for _, e := range results {
t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes())
}
-
- if hasDuplicates(wrapNodes(results)) {
+ if hasDuplicates(results) {
t.Errorf("result set contains duplicate entries")
}
-
- if !sortedByDistanceTo(tn.target.id(), wrapNodes(results)) {
+ if !sortedByDistanceTo(tn.target.id(), results) {
t.Errorf("result set not sorted by distance to target")
}
@@ -285,9 +283,10 @@ func (tn *preminedTestnet) node(dist, index int) *enode.Node {
return n
}
-func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.PrivateKey) {
- dist := int(addr.IP[1])<<8 + int(addr.IP[2])
- index := int(addr.IP[3])
+func (tn *preminedTestnet) nodeByAddr(addr netip.AddrPort) (*enode.Node, *ecdsa.PrivateKey) {
+ ip := addr.Addr().As4()
+ dist := int(ip[1])<<8 + int(ip[2])
+ index := int(ip[3])
key := tn.dists[dist][index]
return tn.node(dist, index), key
@@ -296,7 +295,7 @@ func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.Pr
func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node {
result := make([]v4wire.Node, len(tn.dists[dist]))
for i := range result {
- result[i] = nodeToRPC(wrapNode(tn.node(dist, i)))
+ result[i] = nodeToRPC(tn.node(dist, i))
}
return result
diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go
index bf694658d5a5..8ddc4b1c2a9a 100644
--- a/p2p/discover/v4_udp.go
+++ b/p2p/discover/v4_udp.go
@@ -26,6 +26,7 @@ import (
"fmt"
"io"
"net"
+ "net/netip"
"sync"
"time"
@@ -45,6 +46,7 @@ var (
errClockWarp = errors.New("reply deadline too far in the future")
errClosed = errors.New("socket closed")
errLowPort = errors.New("low port")
+ errNoUDPEndpoint = errors.New("node has no UDP endpoint")
)
const (
@@ -93,7 +95,7 @@ type UDPv4 struct {
type replyMatcher struct {
// these fields must match in the reply.
from enode.ID
- ip net.IP
+ ip netip.Addr
ptype byte
// time when the request must complete
@@ -119,7 +121,7 @@ type replyMatchFunc func(v4wire.Packet) (matched bool, requestDone bool)
// reply is a reply packet from a certain node.
type reply struct {
from enode.ID
- ip net.IP
+ ip netip.Addr
data v4wire.Packet
// loop indicates whether there was
// a matching request by sending on this channel.
@@ -206,10 +208,12 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node {
}
func (t *UDPv4) ourEndpoint() v4wire.Endpoint {
- n := t.Self()
- a := &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
-
- return v4wire.NewEndpoint(a, uint16(n.TCP()))
+ node := t.Self()
+ addr, ok := node.UDPEndpoint()
+ if !ok {
+ return v4wire.Endpoint{}
+ }
+ return v4wire.NewEndpoint(addr, uint16(node.TCP()))
}
// Ping sends a ping message to the given node.
@@ -220,7 +224,11 @@ func (t *UDPv4) Ping(n *enode.Node) error {
// ping sends a ping message to the given node and waits for a reply.
func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
- rm := t.sendPing(n.ID(), &net.UDPAddr{IP: n.IP(), Port: n.UDP()}, nil)
+ addr, ok := n.UDPEndpoint()
+ if !ok {
+ return 0, errNoUDPEndpoint
+ }
+ rm := t.sendPing(n.ID(), addr, nil)
if err = <-rm.errc; err == nil {
seq = rm.reply.(*v4wire.Pong).ENRSeq
}
@@ -230,7 +238,7 @@ func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
// sendPing sends a ping message to the given node and invokes the callback
// when the reply arrives.
-func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *replyMatcher {
+func (t *UDPv4) sendPing(toid enode.ID, toaddr netip.AddrPort, callback func()) *replyMatcher {
req := t.makePing(toaddr)
packet, hash, err := v4wire.Encode(t.priv, req)
@@ -242,7 +250,7 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r
}
// Add a matcher for the reply to the pending reply queue. Pongs are matched if they
// reference the ping we're about to send.
- rm := t.pending(toid, toaddr.IP, v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) {
+ rm := t.pending(toid, toaddr.Addr(), v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) {
matched = bytes.Equal(p.(*v4wire.Pong).ReplyTok, hash)
if matched && callback != nil {
callback()
@@ -251,13 +259,14 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r
return matched, matched
})
// Send the packet.
- t.localNode.UDPContact(toaddr)
+ toUDPAddr := &net.UDPAddr{IP: toaddr.Addr().AsSlice()}
+ t.localNode.UDPContact(toUDPAddr)
t.write(toaddr, toid, req.Name(), packet)
return rm
}
-func (t *UDPv4) makePing(toaddr *net.UDPAddr) *v4wire.Ping {
+func (t *UDPv4) makePing(toaddr netip.AddrPort) *v4wire.Ping {
return &v4wire.Ping{
Version: 4,
From: t.ourEndpoint(),
@@ -304,8 +313,12 @@ func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
ekey := v4wire.Pubkey(targetKey)
- it := newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) {
- return t.findnode(n.ID(), n.addr(), ekey)
+ it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
+ addr, ok := n.UDPEndpoint()
+ if !ok {
+ return nil, errNoUDPEndpoint
+ }
+ return t.findnode(n.ID(), addr, ekey)
})
return it
@@ -313,21 +326,20 @@ func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
// findnode sends a findnode request to the given node and waits until
// the node has sent up to k neighbors.
-func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubkey) ([]*node, error) {
- t.ensureBond(toid, toaddr)
+func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire.Pubkey) ([]*enode.Node, error) {
+ t.ensureBond(toid, toAddrPort)
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
// active until enough nodes have been received.
- nodes := make([]*node, 0, bucketSize)
+ nodes := make([]*enode.Node, 0, bucketSize)
nreceived := 0
- rm := t.pending(toid, toaddr.IP, v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
+ rm := t.pending(toid, toAddrPort.Addr(), v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
reply := r.(*v4wire.Neighbors)
for _, rn := range reply.Nodes {
nreceived++
-
- n, err := t.nodeFromRPC(toaddr, rn)
+ n, err := t.nodeFromRPC(toAddrPort, rn)
if err != nil {
- t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toaddr, "err", err)
+ t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toAddrPort, "err", err)
continue
}
@@ -336,8 +348,7 @@ func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubke
return true, nreceived >= bucketSize
})
-
- t.send(toaddr, toid, &v4wire.Findnode{
+ t.send(toAddrPort, toid, &v4wire.Findnode{
Target: target,
Expiration: uint64(time.Now().Add(expiration).Unix()),
})
@@ -356,7 +367,7 @@ func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubke
// RequestENR sends ENRRequest to the given node and waits for a response.
func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
- addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
+ addr, _ := n.UDPEndpoint()
t.ensureBond(n.ID(), addr)
req := &v4wire.ENRRequest{
@@ -370,7 +381,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
// Add a matcher for the reply to the pending reply queue. Responses are matched if
// they reference the request we're about to send.
- rm := t.pending(n.ID(), addr.IP, v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
+ rm := t.pending(n.ID(), addr.Addr(), v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
matched = bytes.Equal(r.(*v4wire.ENRResponse).ReplyTok, hash)
return matched, matched
})
@@ -393,8 +404,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) {
if respN.Seq() < n.Seq() {
return n, nil // response record is older
}
-
- if err := netutil.CheckRelayIP(addr.IP, respN.IP()); err != nil {
+ if err := netutil.CheckRelayIP(addr.Addr().AsSlice(), respN.IP()); err != nil {
return nil, fmt.Errorf("invalid IP in response record: %v", err)
}
@@ -407,7 +417,7 @@ func (t *UDPv4) TableBuckets() [][]BucketNode {
// pending adds a reply matcher to the pending reply queue.
// see the documentation of type replyMatcher for a detailed explanation.
-func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchFunc) *replyMatcher {
+func (t *UDPv4) pending(id enode.ID, ip netip.Addr, ptype byte, callback replyMatchFunc) *replyMatcher {
ch := make(chan error, 1)
p := &replyMatcher{from: id, ip: ip, ptype: ptype, callback: callback, errc: ch}
select {
@@ -422,7 +432,7 @@ func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchF
// handleReply dispatches a reply packet, invoking reply matchers. It returns
// whether any matcher considered the packet acceptable.
-func (t *UDPv4) handleReply(from enode.ID, fromIP net.IP, req v4wire.Packet) bool {
+func (t *UDPv4) handleReply(from enode.ID, fromIP netip.Addr, req v4wire.Packet) bool {
matched := make(chan bool, 1)
select {
case t.gotreply <- reply{from, fromIP, req, matched}:
@@ -495,7 +505,7 @@ func (t *UDPv4) loop() {
for el := plist.Front(); el != nil; el = el.Next() {
p := el.Value.(*replyMatcher)
- if p.from == r.from && p.ptype == r.data.Kind() && p.ip.Equal(r.ip) {
+ if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip {
ok, requestDone := p.callback(r.data)
matched = matched || ok
p.reply = r.data
@@ -539,7 +549,7 @@ func (t *UDPv4) loop() {
}
}
-func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]byte, error) {
+func (t *UDPv4) send(toaddr netip.AddrPort, toid enode.ID, req v4wire.Packet) ([]byte, error) {
packet, hash, err := v4wire.Encode(t.priv, req)
if err != nil {
return hash, err
@@ -548,8 +558,8 @@ func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]b
return hash, t.write(toaddr, toid, req.Name(), packet)
}
-func (t *UDPv4) write(toaddr *net.UDPAddr, toid enode.ID, what string, packet []byte) error {
- _, err := t.conn.WriteToUDP(packet, toaddr)
+func (t *UDPv4) write(toaddr netip.AddrPort, toid enode.ID, what string, packet []byte) error {
+ _, err := t.conn.WriteToUDPAddrPort(packet, toaddr)
t.log.Trace(">> "+what, "id", toid, "addr", toaddr, "err", err)
return err
@@ -566,7 +576,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
buf := make([]byte, maxPacketSize)
for {
- nbytes, from, err := t.conn.ReadFromUDP(buf)
+ nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf)
if netutil.IsTemporaryError(err) {
// Ignore temporary read errors.
t.log.Debug("Temporary UDP read error", "err", err)
@@ -589,7 +599,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) {
}
}
-func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error {
+func (t *UDPv4) handlePacket(from netip.AddrPort, buf []byte) error {
rawpacket, fromKey, hash, err := v4wire.Decode(buf)
if err != nil {
t.log.Debug("Bad discv4 packet", "addr", from, "err", err)
@@ -613,15 +623,16 @@ func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error {
}
// checkBond checks if the given node has a recent enough endpoint proof.
-func (t *UDPv4) checkBond(id enode.ID, ip net.IP) bool {
- return time.Since(t.db.LastPongReceived(id, ip)) < bondExpiration
+func (t *UDPv4) checkBond(id enode.ID, ip netip.AddrPort) bool {
+ return time.Since(t.db.LastPongReceived(id, ip.Addr().AsSlice())) < bondExpiration
}
// ensureBond solicits a ping from a node if we haven't seen a ping from it for a while.
// This ensures there is a valid endpoint proof on the remote end.
-func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) {
- tooOld := time.Since(t.db.LastPingReceived(toid, toaddr.IP)) > bondExpiration
- if tooOld || t.db.FindFails(toid, toaddr.IP) > maxFindnodeFailures {
+func (t *UDPv4) ensureBond(toid enode.ID, toaddr netip.AddrPort) {
+ ip := toaddr.Addr().AsSlice()
+ tooOld := time.Since(t.db.LastPingReceived(toid, ip)) > bondExpiration
+ if tooOld || t.db.FindFails(toid, ip) > maxFindnodeFailures {
rm := t.sendPing(toid, toaddr, nil)
<-rm.errc
// Wait for them to ping back and process our pong.
@@ -629,12 +640,11 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) {
}
}
-func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error) {
+func (t *UDPv4) nodeFromRPC(sender netip.AddrPort, rn v4wire.Node) (*enode.Node, error) {
if rn.UDP <= 1024 {
return nil, errLowPort
}
-
- if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
+ if err := netutil.CheckRelayIP(sender.Addr().AsSlice(), rn.IP); err != nil {
return nil, err
}
@@ -646,14 +656,13 @@ func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error)
if err != nil {
return nil, err
}
-
- n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)))
+ n := enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP))
err = n.ValidateComplete()
return n, err
}
-func nodeToRPC(n *node) v4wire.Node {
+func nodeToRPC(n *enode.Node) v4wire.Node {
var key ecdsa.PublicKey
var ekey v4wire.Pubkey
@@ -696,14 +705,14 @@ type packetHandlerV4 struct {
senderKey *ecdsa.PublicKey // used for ping
// preverify checks whether the packet is valid and should be handled at all.
- preverify func(p *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error
+ preverify func(p *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error
// handle handles the packet.
- handle func(req *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte)
+ handle func(req *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte)
}
// PING/v4
-func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
+func (t *UDPv4) verifyPing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Ping)
if v4wire.Expired(req.Expiration) {
@@ -718,7 +727,7 @@ func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
return nil
}
-func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) {
+func (t *UDPv4) handlePing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
req := h.Packet.(*v4wire.Ping)
// Reply.
@@ -730,8 +739,9 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
})
// Ping back if our last pong on file is too far in the past.
- n := wrapNode(enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port))
- if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration {
+ fromIP := from.Addr().AsSlice()
+ n := enode.NewV4(h.senderKey, fromIP, int(req.From.TCP), int(from.Port()))
+ if time.Since(t.db.LastPongReceived(n.ID(), fromIP)) > bondExpiration {
t.sendPing(fromID, from, func() {
t.tab.addInboundNode(n)
})
@@ -740,39 +750,40 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
}
// Update node database and endpoint predictor.
- t.db.UpdateLastPingReceived(n.ID(), from.IP, time.Now())
- t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)})
+ t.db.UpdateLastPingReceived(n.ID(), fromIP, time.Now())
+ fromUDPAddr := &net.UDPAddr{IP: fromIP, Port: int(from.Port())}
+ toUDPAddr := &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}
+ t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
}
// PONG/v4
-func (t *UDPv4) verifyPong(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
+func (t *UDPv4) verifyPong(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Pong)
if v4wire.Expired(req.Expiration) {
return errExpired
}
-
- if !t.handleReply(fromID, from.IP, req) {
+ if !t.handleReply(fromID, from.Addr(), req) {
return errUnsolicitedReply
}
-
- t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)})
- t.db.UpdateLastPongReceived(fromID, from.IP, time.Now())
-
+ fromIP := from.Addr().AsSlice()
+ fromUDPAddr := &net.UDPAddr{IP: fromIP, Port: int(from.Port())}
+ toUDPAddr := &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}
+ t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
+ t.db.UpdateLastPongReceived(fromID, fromIP, time.Now())
return nil
}
// FINDNODE/v4
-func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
+func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Findnode)
if v4wire.Expired(req.Expiration) {
return errExpired
}
-
- if !t.checkBond(fromID, from.IP) {
+ if !t.checkBond(fromID, from) {
// No endpoint proof pong exists, we don't process the packet. This prevents an
// attack vector where the discovery protocol could be used to amplify traffic in a
// DDOS attack. A malicious actor would send a findnode request with the IP address
@@ -785,7 +796,7 @@ func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
return nil
}
-func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) {
+func (t *UDPv4) handleFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
req := h.Packet.(*v4wire.Findnode)
// Determine closest nodes.
@@ -799,7 +810,8 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
var sent bool
for _, n := range closest {
- if netutil.CheckRelayIP(from.IP, n.IP()) == nil {
+ fromIP := from.Addr().AsSlice()
+ if netutil.CheckRelayIP(fromIP, n.IP()) == nil {
p.Nodes = append(p.Nodes, nodeToRPC(n))
}
@@ -817,14 +829,13 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
// NEIGHBORS/v4
-func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
+func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.Neighbors)
if v4wire.Expired(req.Expiration) {
return errExpired
}
-
- if !t.handleReply(fromID, from.IP, h.Packet) {
+ if !t.handleReply(fromID, from.Addr(), h.Packet) {
return errUnsolicitedReply
}
@@ -833,21 +844,20 @@ func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID en
// ENRREQUEST/v4
-func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
+func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
req := h.Packet.(*v4wire.ENRRequest)
if v4wire.Expired(req.Expiration) {
return errExpired
}
-
- if !t.checkBond(fromID, from.IP) {
+ if !t.checkBond(fromID, from) {
return errUnknownNode
}
return nil
}
-func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) {
+func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) {
t.send(from, fromID, &v4wire.ENRResponse{
ReplyTok: mac,
Record: *t.localNode.Node().Record(),
@@ -856,8 +866,8 @@ func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID e
// ENRRESPONSE/v4
-func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error {
- if !t.handleReply(fromID, from.IP, h.Packet) {
+func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error {
+ if !t.handleReply(fromID, from.Addr(), h.Packet) {
return errUnsolicitedReply
}
diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go
index cbee5f11ced4..b88b6a6002f5 100644
--- a/p2p/discover/v4_udp_test.go
+++ b/p2p/discover/v4_udp_test.go
@@ -26,6 +26,7 @@ import (
"io"
"math/rand"
"net"
+ "net/netip"
"reflect"
"sync"
"testing"
@@ -55,7 +56,7 @@ type udpTest struct {
udp *UDPv4
sent [][]byte
localkey, remotekey *ecdsa.PrivateKey
- remoteaddr *net.UDPAddr
+ remoteaddr netip.AddrPort
}
func newUDPTest(t *testing.T) *udpTest {
@@ -64,7 +65,7 @@ func newUDPTest(t *testing.T) *udpTest {
pipe: newpipe(),
localkey: newkey(),
remotekey: newkey(),
- remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
+ remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"),
}
test.db, _ = enode.OpenDB("")
@@ -93,7 +94,7 @@ func (test *udpTest) packetIn(wantError error, data v4wire.Packet) {
}
// handles a packet as if it had been sent to the transport by the key/endpoint.
-func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *net.UDPAddr, data v4wire.Packet) {
+func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr netip.AddrPort, data v4wire.Packet) {
test.t.Helper()
enc, _, err := v4wire.Encode(key, data)
@@ -108,7 +109,7 @@ func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *
}
// waits for a packet to be sent by the transport.
-// validate should have type func(X, *net.UDPAddr, []byte), where X is a packet type.
+// validate should have type func(X, netip.AddrPort, []byte), where X is a packet type.
func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) {
test.t.Helper()
@@ -133,9 +134,7 @@ func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) {
test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
return false
}
-
- fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(hash)})
-
+ fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(hash)})
return false
}
@@ -257,7 +256,7 @@ func TestUDPv4_findnodeTimeout(t *testing.T) {
test := newUDPTest(t)
defer test.close()
- toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
+ toaddr := netip.AddrPortFrom(netip.MustParseAddr("1.2.3.4"), 2222)
toid := enode.ID{1, 2, 3, 4}
target := v4wire.Pubkey{4, 5, 6, 7}
@@ -285,28 +284,25 @@ func TestUDPv4_findnode(t *testing.T) {
for i := 0; i < numCandidates; i++ {
key := newkey()
ip := net.IP{10, 13, 0, byte(i)}
- n := wrapNode(enode.NewV4(&key.PublicKey, ip, 0, 2000))
+ n := enode.NewV4(&key.PublicKey, ip, 0, 2000)
// Ensure half of table content isn't verified live yet.
if i > numCandidates/2 {
- n.isValidatedLive = true
live[n.ID()] = true
}
-
+ test.table.addFoundNode(n, live[n.ID()])
nodes.push(n, numCandidates)
}
- fillTable(test.table, nodes.entries, false)
// ensure there's a bond with the test node,
// findnode won't be accepted otherwise.
remoteID := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID()
- test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now())
+ test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.Addr().AsSlice(), time.Now())
// check that closest neighbors are returned.
expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
-
- waitNeighbors := func(want []*node) {
- test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) {
+ waitNeighbors := func(want []*enode.Node) {
+ test.waitPacketOut(func(p *v4wire.Neighbors, to netip.AddrPort, hash []byte) {
if len(p.Nodes) != len(want) {
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
return
@@ -338,11 +334,10 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
defer test.close()
rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
- test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.IP, time.Now())
+ test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.Addr().AsSlice(), time.Now())
// queue a pending findnode request
- resultc, errc := make(chan []*node, 1), make(chan error, 1)
-
+ resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1)
go func() {
rid := encodePubkey(&test.remotekey.PublicKey).id()
@@ -356,18 +351,18 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
// wait for the findnode to be sent.
// after it is sent, the transport is waiting for a reply
- test.waitPacketOut(func(p *v4wire.Findnode, to *net.UDPAddr, hash []byte) {
+ test.waitPacketOut(func(p *v4wire.Findnode, to netip.AddrPort, hash []byte) {
if p.Target != testTarget {
t.Errorf("wrong target: got %v, want %v", p.Target, testTarget)
}
})
// send the reply as two packets.
- list := []*node{
- wrapNode(enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")),
- wrapNode(enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")),
- wrapNode(enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")),
- wrapNode(enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")),
+ list := []*enode.Node{
+ enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
+ enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
+ enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
+ enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
}
rpclist := make([]v4wire.Node, len(list))
@@ -401,8 +396,8 @@ func TestUDPv4_pingMatch(t *testing.T) {
crand.Read(randToken)
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
- test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {})
- test.waitPacketOut(func(*v4wire.Ping, *net.UDPAddr, []byte) {})
+ test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {})
+ test.waitPacketOut(func(*v4wire.Ping, netip.AddrPort, []byte) {})
test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp})
}
@@ -412,10 +407,10 @@ func TestUDPv4_pingMatchIP(t *testing.T) {
defer test.close()
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
- test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {})
+ test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {})
- test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) {
- wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 1, 2}, Port: 30000}
+ test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) {
+ wrongAddr := netip.MustParseAddrPort("33.44.1.2:30000")
test.packetInFrom(errUnsolicitedReply, test.remotekey, wrongAddr, &v4wire.Pong{
ReplyTok: hash,
To: testLocalAnnounced,
@@ -426,43 +421,36 @@ func TestUDPv4_pingMatchIP(t *testing.T) {
func TestUDPv4_successfulPing(t *testing.T) {
test := newUDPTest(t)
- added := make(chan *node, 1)
- test.table.nodeAddedHook = func(b *bucket, n *node) { added <- n }
+ added := make(chan *tableNode, 1)
+ test.table.nodeAddedHook = func(b *bucket, n *tableNode) { added <- n }
defer test.close()
// The remote side sends a ping packet to initiate the exchange.
go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
// The ping is replied to.
- test.waitPacketOut(func(p *v4wire.Pong, to *net.UDPAddr, hash []byte) {
+ test.waitPacketOut(func(p *v4wire.Pong, to netip.AddrPort, hash []byte) {
pinghash := test.sent[0][:32]
if !bytes.Equal(p.ReplyTok, pinghash) {
t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash)
}
-
- wantTo := v4wire.Endpoint{
- // The mirrored UDP address is the UDP packet sender
- IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
- // The mirrored TCP port is the one from the ping packet
- TCP: testRemote.TCP,
- }
+ // The mirrored UDP address is the UDP packet sender.
+ // The mirrored TCP port is the one from the ping packet.
+ wantTo := v4wire.NewEndpoint(test.remoteaddr, testRemote.TCP)
if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got pong.To %v, want %v", p.To, wantTo)
}
})
// Remote is unknown, the table pings back.
- test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) {
- if !reflect.DeepEqual(p.From, test.udp.ourEndpoint()) {
+ test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) {
+ wantFrom := test.udp.ourEndpoint()
+ wantFrom.IP = net.IP{}
+ if !reflect.DeepEqual(p.From, wantFrom) {
t.Errorf("got ping.From %#v, want %#v", p.From, test.udp.ourEndpoint())
}
-
- wantTo := v4wire.Endpoint{
- // The mirrored UDP address is the UDP packet sender.
- IP: test.remoteaddr.IP,
- UDP: uint16(test.remoteaddr.Port),
- TCP: 0,
- }
+ // The mirrored UDP address is the UDP packet sender.
+ wantTo := v4wire.NewEndpoint(test.remoteaddr, 0)
if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got ping.To %v, want %v", p.To, wantTo)
}
@@ -478,13 +466,11 @@ func TestUDPv4_successfulPing(t *testing.T) {
if n.ID() != rid {
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
}
-
- if !n.IP().Equal(test.remoteaddr.IP) {
- t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP)
+ if !n.IP().Equal(test.remoteaddr.Addr().AsSlice()) {
+ t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.Addr())
}
-
- if n.UDP() != test.remoteaddr.Port {
- t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
+ if n.UDP() != int(test.remoteaddr.Port()) {
+ t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port())
}
if n.TCP() != int(testRemote.TCP) {
@@ -508,12 +494,12 @@ func TestUDPv4_EIP868(t *testing.T) {
// Perform endpoint proof and check for sequence number in packet tail.
test.packetIn(nil, &v4wire.Ping{Expiration: futureExp})
- test.waitPacketOut(func(p *v4wire.Pong, addr *net.UDPAddr, hash []byte) {
+ test.waitPacketOut(func(p *v4wire.Pong, addr netip.AddrPort, hash []byte) {
if p.ENRSeq != wantNode.Seq() {
t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq())
}
})
- test.waitPacketOut(func(p *v4wire.Ping, addr *net.UDPAddr, hash []byte) {
+ test.waitPacketOut(func(p *v4wire.Ping, addr netip.AddrPort, hash []byte) {
if p.ENRSeq != wantNode.Seq() {
t.Errorf("wrong sequence number in ping: %d, want %d", p.ENRSeq, wantNode.Seq())
}
@@ -523,7 +509,7 @@ func TestUDPv4_EIP868(t *testing.T) {
// Request should work now.
test.packetIn(nil, &v4wire.ENRRequest{Expiration: futureExp})
- test.waitPacketOut(func(p *v4wire.ENRResponse, addr *net.UDPAddr, hash []byte) {
+ test.waitPacketOut(func(p *v4wire.ENRResponse, addr netip.AddrPort, hash []byte) {
n, err := enode.New(enode.ValidSchemes, &p.Record)
if err != nil {
t.Fatalf("invalid record: %v", err)
@@ -634,7 +620,7 @@ type dgramPipe struct {
}
type dgram struct {
- to net.UDPAddr
+ to netip.AddrPort
data []byte
}
@@ -648,8 +634,8 @@ func newpipe() *dgramPipe {
}
}
-// WriteToUDP queues a datagram.
-func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
+// WriteToUDPAddrPort queues a datagram.
+func (c *dgramPipe) WriteToUDPAddrPort(b []byte, to netip.AddrPort) (n int, err error) {
msg := make([]byte, len(b))
copy(msg, b)
c.mu.Lock()
@@ -658,17 +644,16 @@ func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
if c.closed {
return 0, errors.New("closed")
}
-
- c.queue = append(c.queue, dgram{*to, b})
+ c.queue = append(c.queue, dgram{to, b})
c.cond.Signal()
return len(b), nil
}
-// ReadFromUDP just hangs until the pipe is closed.
-func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
+// ReadFromUDPAddrPort just hangs until the pipe is closed.
+func (c *dgramPipe) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
<-c.closing
- return 0, nil, io.EOF
+ return 0, netip.AddrPort{}, io.EOF
}
func (c *dgramPipe) Close() error {
diff --git a/p2p/discover/v4wire/v4wire.go b/p2p/discover/v4wire/v4wire.go
index 7408b64bd1c7..81aafbe8b3f0 100644
--- a/p2p/discover/v4wire/v4wire.go
+++ b/p2p/discover/v4wire/v4wire.go
@@ -25,6 +25,7 @@ import (
"fmt"
"math/big"
"net"
+ "net/netip"
"time"
"github.com/ethereum/go-ethereum/common/math"
@@ -150,15 +151,15 @@ type Endpoint struct {
}
// NewEndpoint creates an endpoint.
-func NewEndpoint(addr *net.UDPAddr, tcpPort uint16) Endpoint {
- ip := net.IP{}
- if ip4 := addr.IP.To4(); ip4 != nil {
- ip = ip4
- } else if ip6 := addr.IP.To16(); ip6 != nil {
- ip = ip6
+func NewEndpoint(addr netip.AddrPort, tcpPort uint16) Endpoint {
+ var ip net.IP
+ if addr.Addr().Is4() || addr.Addr().Is4In6() {
+ ip4 := addr.Addr().As4()
+ ip = ip4[:]
+ } else {
+ ip = addr.Addr().AsSlice()
}
-
- return Endpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
+ return Endpoint{IP: ip, UDP: addr.Port(), TCP: tcpPort}
}
type Packet interface {
diff --git a/p2p/discover/v5_talk.go b/p2p/discover/v5_talk.go
index c1f67879402c..2246b47141c0 100644
--- a/p2p/discover/v5_talk.go
+++ b/p2p/discover/v5_talk.go
@@ -18,6 +18,7 @@ package discover
import (
"net"
+ "net/netip"
"sync"
"time"
@@ -70,7 +71,7 @@ func (t *talkSystem) register(protocol string, handler TalkRequestHandler) {
}
// handleRequest handles a talk request.
-func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.TalkRequest) {
+func (t *talkSystem) handleRequest(id enode.ID, addr netip.AddrPort, req *v5wire.TalkRequest) {
t.mutex.Lock()
handler, ok := t.handlers[req.Protocol]
t.mutex.Unlock()
@@ -88,7 +89,8 @@ func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.T
case <-t.slots:
go func() {
defer func() { t.slots <- struct{}{} }()
- respMessage := handler(id, addr, req.Message)
+ udpAddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())}
+ respMessage := handler(id, udpAddr, req.Message)
resp := &v5wire.TalkResponse{ReqID: req.ReqID, Message: respMessage}
t.transport.sendFromAnotherThread(id, addr, resp)
}()
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index b5be16f92015..240647ed5e0d 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -25,6 +25,7 @@ import (
"fmt"
"io"
"net"
+ "net/netip"
"sync"
"time"
@@ -100,14 +101,14 @@ type UDPv5 struct {
type sendRequest struct {
destID enode.ID
- destAddr *net.UDPAddr
+ destAddr netip.AddrPort
msg v5wire.Packet
}
// callV5 represents a remote procedure call against another node.
type callV5 struct {
id enode.ID
- addr *net.UDPAddr
+ addr netip.AddrPort
node *enode.Node // This is required to perform handshakes.
packet v5wire.Packet
@@ -239,7 +240,7 @@ func (t *UDPv5) AllNodes() []*enode.Node {
for _, b := range &t.tab.buckets {
for _, n := range b.entries {
- nodes = append(nodes, unwrapNode(n))
+ nodes = append(nodes, n.Node)
}
}
@@ -273,7 +274,7 @@ func (t *UDPv5) TalkRequest(n *enode.Node, protocol string, request []byte) ([]b
}
// TalkRequestToID sends a talk request to a node and waits for a response.
-func (t *UDPv5) TalkRequestToID(id enode.ID, addr *net.UDPAddr, protocol string, request []byte) ([]byte, error) {
+func (t *UDPv5) TalkRequestToID(id enode.ID, addr netip.AddrPort, protocol string, request []byte) ([]byte, error) {
req := &v5wire.TalkRequest{Protocol: protocol, Message: request}
resp := t.callToID(id, addr, v5wire.TalkResponseMsg, req)
defer t.callDone(resp)
@@ -323,13 +324,13 @@ func (t *UDPv5) newRandomLookup(ctx context.Context) *lookup {
}
func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup {
- return newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) {
+ return newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
return t.lookupWorker(n, target)
})
}
// lookupWorker performs FINDNODE calls against a single node during lookup.
-func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) {
+func (t *UDPv5) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.Node, error) {
var (
dists = lookupDistances(target, destNode.ID())
nodes = nodesByDistance{target: target}
@@ -337,15 +338,14 @@ func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) {
)
var r []*enode.Node
- r, err = t.findnode(unwrapNode(destNode), dists)
-
+ r, err = t.findnode(destNode, dists)
if errors.Is(err, errClosed) {
return nil, err
}
for _, n := range r {
if n.ID() != t.Self().ID() {
- nodes.push(wrapNode(n), findnodeResultLimit)
+ nodes.push(n, findnodeResultLimit)
}
}
@@ -449,7 +449,7 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
if err != nil {
return nil, err
}
- if err := netutil.CheckRelayIP(c.addr.IP, node.IP()); err != nil {
+ if err := netutil.CheckRelayIP(c.addr.Addr().AsSlice(), node.IP()); err != nil {
return nil, err
}
@@ -489,14 +489,14 @@ func containsUint(x uint, xs []uint) bool {
// callToNode sends the given call and sets up a handler for response packets (of message
// type responseType). Responses are dispatched to the call's response channel.
func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 {
- addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
+ addr, _ := n.UDPEndpoint()
c := &callV5{id: n.ID(), addr: addr, node: n}
t.initCall(c, responseType, req)
return c
}
// callToID is like callToNode, but for cases where the node record is not available.
-func (t *UDPv5) callToID(id enode.ID, addr *net.UDPAddr, responseType byte, req v5wire.Packet) *callV5 {
+func (t *UDPv5) callToID(id enode.ID, addr netip.AddrPort, responseType byte, req v5wire.Packet) *callV5 {
c := &callV5{id: id, addr: addr}
t.initCall(c, responseType, req)
return c
@@ -667,12 +667,12 @@ func (t *UDPv5) sendCall(c *callV5) {
// sendResponse sends a response packet to the given node.
// This doesn't trigger a handshake even if no keys are available.
-func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) error {
+func (t *UDPv5) sendResponse(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) error {
_, err := t.send(toID, toAddr, packet, nil)
return err
}
-func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) {
+func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) {
select {
case t.sendCh <- sendRequest{toID, toAddr, packet}:
case <-t.closeCtx.Done():
@@ -680,7 +680,7 @@ func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet
}
// send sends a packet to the given node.
-func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) {
+func (t *UDPv5) send(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) {
addr := toAddr.String()
t.logcontext = append(t.logcontext[:0], "id", toID, "addr", addr)
t.logcontext = packet.AppendLogInfo(t.logcontext)
@@ -693,7 +693,7 @@ func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c
return nonce, err
}
- _, err = t.conn.WriteToUDP(enc, toAddr)
+ _, err = t.conn.WriteToUDPAddrPort(enc, toAddr)
t.log.Trace(">> "+packet.Name(), t.logcontext...)
return nonce, err
@@ -705,7 +705,7 @@ func (t *UDPv5) readLoop() {
buf := make([]byte, maxPacketSize)
for range t.readNextCh {
- nbytes, from, err := t.conn.ReadFromUDP(buf)
+ nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf)
if netutil.IsTemporaryError(err) {
// Ignore temporary read errors.
t.log.Debug("Temporary UDP read error", "err", err)
@@ -724,7 +724,7 @@ func (t *UDPv5) readLoop() {
}
// dispatchReadPacket sends a packet into the dispatch loop.
-func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool {
+func (t *UDPv5) dispatchReadPacket(from netip.AddrPort, content []byte) bool {
select {
case t.packetInCh <- ReadPacket{content, from}:
return true
@@ -734,7 +734,7 @@ func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool {
}
// handlePacket decodes and processes an incoming packet from the network.
-func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
+func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr netip.AddrPort) error {
addr := fromAddr.String()
fromID, fromNode, packet, err := t.codec.Decode(rawpacket, addr)
@@ -756,7 +756,7 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
if fromNode != nil {
// Handshake succeeded, add to table.
- t.tab.addInboundNode(wrapNode(fromNode))
+ t.tab.addInboundNode(fromNode)
}
if packet.Kind() != v5wire.WhoareyouPacket {
@@ -772,13 +772,13 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
}
// handleCallResponse dispatches a response packet to the call waiting for it.
-func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr *net.UDPAddr, p v5wire.Packet) bool {
+func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr netip.AddrPort, p v5wire.Packet) bool {
ac := t.activeCallByNode[fromID]
if ac == nil || !bytes.Equal(p.RequestID(), ac.reqid) {
t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.Name()), "id", fromID, "addr", fromAddr)
return false
}
- if !fromAddr.IP.Equal(ac.addr.IP) || fromAddr.Port != ac.addr.Port {
+ if fromAddr != ac.addr {
t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.Name()), "id", fromID, "addr", fromAddr)
return false
}
@@ -808,7 +808,7 @@ func (t *UDPv5) getNode(id enode.ID) *enode.Node {
}
// handle processes incoming packets according to their message type.
-func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) {
+func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort) {
switch p := p.(type) {
case *v5wire.Unknown:
t.handleUnknown(p, fromID, fromAddr)
@@ -818,7 +818,9 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr)
t.handlePing(p, fromID, fromAddr)
case *v5wire.Pong:
if t.handleCallResponse(fromID, fromAddr, p) {
- t.localNode.UDPEndpointStatement(fromAddr, &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)})
+ fromUDPAddr := &net.UDPAddr{IP: fromAddr.Addr().AsSlice(), Port: int(fromAddr.Port())}
+ toUDPAddr := &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}
+ t.localNode.UDPEndpointStatement(fromUDPAddr, toUDPAddr)
}
case *v5wire.Findnode:
t.handleFindnode(p, fromID, fromAddr)
@@ -832,7 +834,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr)
}
// handleUnknown initiates a handshake by responding with WHOAREYOU.
-func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr *net.UDPAddr) {
+func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) {
challenge := &v5wire.Whoareyou{Nonce: p.Nonce}
crand.Read(challenge.IDNonce[:])
@@ -850,7 +852,7 @@ var (
)
// handleWhoareyou resends the active call as a handshake packet.
-func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr *net.UDPAddr) {
+func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr netip.AddrPort) {
c, err := t.matchWithCall(fromID, p.Nonce)
if err != nil {
t.log.Debug("Invalid "+p.Name(), "addr", fromAddr, "err", err)
@@ -887,33 +889,36 @@ func (t *UDPv5) matchWithCall(fromID enode.ID, nonce v5wire.Nonce) (*callV5, err
}
// handlePing sends a PONG response.
-func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr *net.UDPAddr) {
- remoteIP := fromAddr.IP
- // Handle IPv4 mapped IPv6 addresses in the
- // event the local node is binded to an
- // ipv6 interface.
- if remoteIP.To4() != nil {
- remoteIP = remoteIP.To4()
+func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr netip.AddrPort) {
+ var remoteIP net.IP
+ // Handle IPv4 mapped IPv6 addresses in the event the local node is binded
+ // to an ipv6 interface.
+ if fromAddr.Addr().Is4() || fromAddr.Addr().Is4In6() {
+ ip4 := fromAddr.Addr().As4()
+ remoteIP = ip4[:]
+ } else {
+ remoteIP = fromAddr.Addr().AsSlice()
}
t.sendResponse(fromID, fromAddr, &v5wire.Pong{
ReqID: p.ReqID,
ToIP: remoteIP,
- ToPort: uint16(fromAddr.Port),
+ ToPort: fromAddr.Port(),
ENRSeq: t.localNode.Node().Seq(),
})
}
// handleFindnode returns nodes to the requester.
-func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr *net.UDPAddr) {
- nodes := t.collectTableNodes(fromAddr.IP, p.Distances, findnodeResultLimit)
+func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr netip.AddrPort) {
+ nodes := t.collectTableNodes(fromAddr.Addr(), p.Distances, findnodeResultLimit)
for _, resp := range packNodes(p.ReqID, nodes) {
t.sendResponse(fromID, fromAddr, resp)
}
}
// collectTableNodes creates a FINDNODE result set for the given distances.
-func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*enode.Node {
+func (t *UDPv5) collectTableNodes(rip netip.Addr, distances []uint, limit int) []*enode.Node {
+ ripSlice := rip.AsSlice()
var bn []*enode.Node
var nodes []*enode.Node
@@ -929,7 +934,7 @@ func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*en
for _, n := range t.tab.appendLiveNodes(dist, bn[:0]) {
// Apply some pre-checks to avoid sending invalid nodes.
// Note liveness is checked by appendLiveNodes.
- if netutil.CheckRelayIP(rip, n.IP()) != nil {
+ if netutil.CheckRelayIP(ripSlice, n.IP()) != nil {
continue
}
diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go
index 92e7f0b44821..7a7e1892f7a3 100644
--- a/p2p/discover/v5_udp_test.go
+++ b/p2p/discover/v5_udp_test.go
@@ -23,6 +23,7 @@ import (
"fmt"
"math/rand"
"net"
+ "net/netip"
"reflect"
"testing"
"time"
@@ -113,7 +114,7 @@ func TestUDPv5_pingHandling(t *testing.T) {
defer test.close()
test.packetIn(&v5wire.Ping{ReqID: []byte("foo")})
- test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("foo")) {
t.Error("wrong request ID in response:", p.ReqID)
}
@@ -150,16 +151,16 @@ func TestUDPv5_unknownPacket(t *testing.T) {
// Unknown packet from unknown node.
test.packetIn(&v5wire.Unknown{Nonce: nonce})
- test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) {
check(p, 0)
})
// Make node known.
n := test.getNode(test.remotekey, test.remoteaddr).Node()
- test.table.addFoundNode(wrapNode(n))
+ test.table.addFoundNode(n, false)
test.packetIn(&v5wire.Unknown{Nonce: nonce})
- test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) {
check(p, n.Seq())
})
}
@@ -175,9 +176,9 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16)
nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4)
nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10)
- fillTable(test.table, wrapNodes(nodes253), true)
- fillTable(test.table, wrapNodes(nodes249), true)
- fillTable(test.table, wrapNodes(nodes248), true)
+ fillTable(test.table, nodes253, true)
+ fillTable(test.table, nodes249, true)
+ fillTable(test.table, nodes248, true)
// Requesting with distance zero should return the node's own record.
test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}})
@@ -216,7 +217,7 @@ func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes
}
for {
- test.waitPacketOut(func(p *v5wire.Nodes, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Nodes, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, wantReqID) {
test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID)
}
@@ -262,8 +263,7 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote)
done <- err
}()
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {})
-
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {})
if err := <-done; err != errTimeout {
t.Fatalf("want errTimeout, got %q", err)
}
@@ -273,7 +273,7 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote)
done <- err
}()
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID})
})
@@ -286,8 +286,8 @@ func TestUDPv5_pingCall(t *testing.T) {
_, err := test.udp.ping(remote)
done <- err
}()
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {
- wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 55, 22}, Port: 10101}
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
+ wrongAddr := netip.MustParseAddrPort("33.44.55.22:10101")
test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID})
})
@@ -320,7 +320,7 @@ func TestUDPv5_findnodeCall(t *testing.T) {
}()
// Serve the responses:
- test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) {
if !reflect.DeepEqual(p.Distances, distances) {
t.Fatalf("wrong distances in request: %v", p.Distances)
}
@@ -369,15 +369,15 @@ func TestUDPv5_callResend(t *testing.T) {
}()
// Ping answered by WHOAREYOU.
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
})
// Ping should be re-sent.
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetIn(&v5wire.Pong{ReqID: p.ReqID})
})
// Answer the other ping.
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
test.packetIn(&v5wire.Pong{ReqID: p.ReqID})
})
@@ -406,11 +406,11 @@ func TestUDPv5_multipleHandshakeRounds(t *testing.T) {
}()
// Ping answered by WHOAREYOU.
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
})
// Ping answered by WHOAREYOU again.
- test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) {
test.packetIn(&v5wire.Whoareyou{Nonce: nonce})
})
@@ -440,7 +440,7 @@ func TestUDPv5_callTimeoutReset(t *testing.T) {
}()
// Serve two responses, slowly.
- test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) {
time.Sleep(respTimeout - 50*time.Millisecond)
test.packetIn(&v5wire.Nodes{
ReqID: p.ReqID,
@@ -481,7 +481,7 @@ func TestUDPv5_talkHandling(t *testing.T) {
Protocol: "test",
Message: []byte("test request"),
})
- test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("foo")) {
t.Error("wrong request ID in response:", p.ReqID)
}
@@ -503,7 +503,7 @@ func TestUDPv5_talkHandling(t *testing.T) {
Protocol: "wrong",
Message: []byte("test request"),
})
- test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) {
if !bytes.Equal(p.ReqID, []byte("2")) {
t.Error("wrong request ID in response:", p.ReqID)
}
@@ -533,8 +533,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequest(remote, "test", []byte("test request"))
done <- err
}()
- test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {})
-
+ test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {})
if err := <-done; err != errTimeout {
t.Fatalf("want errTimeout, got %q", err)
}
@@ -544,7 +543,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequest(remote, "test", []byte("test request"))
done <- err
}()
- test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {
if p.Protocol != "test" {
t.Errorf("wrong protocol ID in talk request: %q", p.Protocol)
}
@@ -568,7 +567,7 @@ func TestUDPv5_talkRequest(t *testing.T) {
_, err := test.udp.TalkRequestToID(remote.ID(), test.remoteaddr, "test", []byte("test request 2"))
done <- err
}()
- test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {
if p.Protocol != "test" {
t.Errorf("wrong protocol ID in talk request: %q", p.Protocol)
}
@@ -646,13 +645,14 @@ func TestUDPv5_lookup(t *testing.T) {
for d, nn := range lookupTestnet.dists {
for i, key := range nn {
n := lookupTestnet.node(d, i)
- test.getNode(key, &net.UDPAddr{IP: n.IP(), Port: n.UDP()})
+ addr, _ := n.UDPEndpoint()
+ test.getNode(key, addr)
}
}
// Seed table with initial node.
initialNode := lookupTestnet.node(256, 0)
- fillTable(test.table, []*node{wrapNode(initialNode)}, true)
+ fillTable(test.table, []*enode.Node{initialNode}, true)
// Start the lookup.
resultC := make(chan []*enode.Node, 1)
@@ -666,7 +666,7 @@ func TestUDPv5_lookup(t *testing.T) {
asked := make(map[enode.ID]bool)
for done := false; !done; {
- done = test.waitPacketOut(func(p v5wire.Packet, to *net.UDPAddr, _ v5wire.Nonce) {
+ done = test.waitPacketOut(func(p v5wire.Packet, to netip.AddrPort, _ v5wire.Nonce) {
recipient, key := lookupTestnet.nodeByAddr(to)
switch p := p.(type) {
case *v5wire.Ping:
@@ -723,11 +723,8 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) {
test := newUDPV5Test(t)
defer test.close()
- rawIP := net.IPv4(0xFF, 0x12, 0x33, 0xE5)
- test.remoteaddr = &net.UDPAddr{
- IP: rawIP.To16(),
- Port: 0,
- }
+ rawIP := netip.AddrFrom4([4]byte{0xFF, 0x12, 0x33, 0xE5})
+ test.remoteaddr = netip.AddrPortFrom(netip.AddrFrom16(rawIP.As16()), 0)
remote := test.getNode(test.remotekey, test.remoteaddr).Node()
done := make(chan struct{}, 1)
@@ -736,7 +733,7 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) {
test.udp.handlePing(&v5wire.Ping{ENRSeq: 1}, remote.ID(), test.remoteaddr)
done <- struct{}{}
}()
- test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) {
+ test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) {
if len(p.ToIP) == net.IPv6len {
t.Error("Received untruncated ip address")
}
@@ -744,8 +741,7 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) {
if len(p.ToIP) != net.IPv4len {
t.Errorf("Received ip address with incorrect length: %d", len(p.ToIP))
}
-
- if !p.ToIP.Equal(rawIP) {
+ if !p.ToIP.Equal(rawIP.AsSlice()) {
t.Errorf("Received incorrect ip address: wanted %s but received %s", rawIP.String(), p.ToIP.String())
}
})
@@ -761,9 +757,9 @@ type udpV5Test struct {
db *enode.DB
udp *UDPv5
localkey, remotekey *ecdsa.PrivateKey
- remoteaddr *net.UDPAddr
+ remoteaddr netip.AddrPort
nodesByID map[enode.ID]*enode.LocalNode
- nodesByIP map[string]*enode.LocalNode
+ nodesByIP map[netip.Addr]*enode.LocalNode
}
// testCodec is the packet encoding used by protocol tests. This codec does not perform encryption.
@@ -829,9 +825,9 @@ func newUDPV5Test(t *testing.T) *udpV5Test {
pipe: newpipe(),
localkey: newkey(),
remotekey: newkey(),
- remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
+ remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"),
nodesByID: make(map[enode.ID]*enode.LocalNode),
- nodesByIP: make(map[string]*enode.LocalNode),
+ nodesByIP: make(map[netip.Addr]*enode.LocalNode),
}
test.db, _ = enode.OpenDB("")
ln := enode.NewLocalNode(test.db, test.localkey)
@@ -857,8 +853,8 @@ func (test *udpV5Test) packetIn(packet v5wire.Packet) {
test.packetInFrom(test.remotekey, test.remoteaddr, packet)
}
-// handles a packet as if it had been sent to the transport by the key/endpoint.
-func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, packet v5wire.Packet) {
+// packetInFrom handles a packet as if it had been sent to the transport by the key/endpoint.
+func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr netip.AddrPort, packet v5wire.Packet) {
test.t.Helper()
ln := test.getNode(key, addr)
@@ -875,25 +871,23 @@ func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, pa
}
// getNode ensures the test knows about a node at the given endpoint.
-func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr *net.UDPAddr) *enode.LocalNode {
+func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode {
id := encodePubkey(&key.PublicKey).id()
ln := test.nodesByID[id]
if ln == nil {
db, _ := enode.OpenDB("")
ln = enode.NewLocalNode(db, key)
- ln.SetStaticIP(addr.IP)
- ln.Set(enr.UDP(addr.Port))
+ ln.SetStaticIP(addr.Addr().AsSlice())
+ ln.Set(enr.UDP(addr.Port()))
test.nodesByID[id] = ln
}
-
- test.nodesByIP[string(addr.IP)] = ln
-
+ test.nodesByIP[addr.Addr()] = ln
return ln
}
// waitPacketOut waits for the next output packet and handles it using the given 'validate'
-// function. The function must be of type func (X, *net.UDPAddr, v5wire.Nonce) where X is
+// function. The function must be of type func (X, netip.AddrPort, v5wire.Nonce) where X is
// assignable to packetV5.
func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Helper()
@@ -910,8 +904,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Fatalf("timed out waiting for %v", exptype)
return false
}
-
- ln := test.nodesByIP[string(dgram.to.IP)]
+ ln := test.nodesByIP[dgram.to.Addr()]
if ln == nil {
test.t.Fatalf("attempt to send to non-existing node %v", &dgram.to)
return false
@@ -929,9 +922,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
return false
}
-
- fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(frame.AuthTag)})
-
+ fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(frame.AuthTag)})
return false
}
diff --git a/p2p/server.go b/p2p/server.go
index 8e33ff858faf..d657b059c845 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -24,6 +24,8 @@ import (
"errors"
"fmt"
"net"
+ "net/netip"
+ "slices"
"sync"
"sync/atomic"
"time"
@@ -38,7 +40,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
- "golang.org/x/exp/slices"
)
const (
@@ -493,11 +494,11 @@ type sharedUDPConn struct {
unhandled chan discover.ReadPacket
}
-// ReadFromUDP implements discover.UDPConn
-func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
+// ReadFromUDPAddrPort implements discover.UDPConn
+func (s *sharedUDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
packet, ok := <-s.unhandled
if !ok {
- return 0, nil, errors.New("connection was closed")
+ return 0, netip.AddrPort{}, errors.New("connection was closed")
}
l := len(packet.Data)
From 642f5528f64375cc0795b5d99683cf7d9eed3143 Mon Sep 17 00:00:00 2001
From: Felix Lange
Date: Tue, 12 Mar 2024 19:23:24 +0100
Subject: [PATCH 090/103] p2p: fix race in dialScheduler (#29235)
Co-authored-by: Stefan
---
p2p/dial.go | 51 +++++++++++++++++++++++++++++----------------------
1 file changed, 29 insertions(+), 22 deletions(-)
diff --git a/p2p/dial.go b/p2p/dial.go
index 5658c1b456bc..59cc77c3c046 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -25,6 +25,7 @@ import (
mrand "math/rand"
"net"
"sync"
+ "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common/mclock"
@@ -254,7 +255,7 @@ loop:
}
case task := <-d.doneCh:
- id := task.dest.ID()
+ id := task.dest().ID()
delete(d.dialing, id)
d.updateStaticPool(id)
d.doneSinceLastLog++
@@ -431,7 +432,7 @@ func (d *dialScheduler) startStaticDials(n int) (started int) {
// updateStaticPool attempts to move the given static dial back into staticPool.
func (d *dialScheduler) updateStaticPool(id enode.ID) {
task, ok := d.static[id]
- if ok && task.staticPoolIndex < 0 && d.checkDial(task.dest) == nil {
+ if ok && task.staticPoolIndex < 0 && d.checkDial(task.dest()) == nil {
d.addToStaticPool(task)
}
}
@@ -459,11 +460,11 @@ func (d *dialScheduler) removeFromStaticPool(idx int) {
// startDial runs the given dial task in a separate goroutine.
func (d *dialScheduler) startDial(task *dialTask) {
- d.log.Trace("Starting p2p dial", "id", task.dest.ID(), "ip", task.dest.IP(), "flag", task.flags)
- hkey := string(task.dest.ID().Bytes())
+ node := task.dest()
+ d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IP(), "flag", task.flags)
+ hkey := string(node.ID().Bytes())
d.history.add(hkey, d.clock.Now().Add(dialHistoryExpiration))
- d.dialing[task.dest.ID()] = task
-
+ d.dialing[node.ID()] = task
go func() {
task.run(d)
d.doneCh <- task
@@ -474,39 +475,46 @@ func (d *dialScheduler) startDial(task *dialTask) {
type dialTask struct {
staticPoolIndex int
flags connFlag
+
// These fields are private to the task and should not be
// accessed by dialScheduler while the task is running.
- dest *enode.Node
+ destPtr atomic.Pointer[enode.Node]
lastResolved mclock.AbsTime
resolveDelay time.Duration
}
func newDialTask(dest *enode.Node, flags connFlag) *dialTask {
- return &dialTask{dest: dest, flags: flags, staticPoolIndex: -1}
+ t := &dialTask{flags: flags, staticPoolIndex: -1}
+ t.destPtr.Store(dest)
+ return t
}
type dialError struct {
error
}
+func (t *dialTask) dest() *enode.Node {
+ return t.destPtr.Load()
+}
+
func (t *dialTask) run(d *dialScheduler) {
if t.needResolve() && !t.resolve(d) {
return
}
- err := t.dial(d, t.dest)
+ err := t.dial(d, t.dest())
if err != nil {
// For static nodes, resolve one more time if dialing fails.
if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 {
if t.resolve(d) {
- t.dial(d, t.dest)
+ t.dial(d, t.dest())
}
}
}
}
func (t *dialTask) needResolve() bool {
- return t.flags&staticDialedConn != 0 && t.dest.IP() == nil
+ return t.flags&staticDialedConn != 0 && t.dest().IP() == nil
}
// resolve attempts to find the current endpoint for the destination
@@ -528,7 +536,8 @@ func (t *dialTask) resolve(d *dialScheduler) bool {
return false
}
- resolved := d.resolver.Resolve(t.dest)
+ node := t.dest()
+ resolved := d.resolver.Resolve(node)
t.lastResolved = d.clock.Now()
if resolved == nil {
@@ -536,25 +545,22 @@ func (t *dialTask) resolve(d *dialScheduler) bool {
if t.resolveDelay > maxResolveDelay {
t.resolveDelay = maxResolveDelay
}
-
- d.log.Debug("Resolving node failed", "id", t.dest.ID(), "newdelay", t.resolveDelay)
-
+ d.log.Debug("Resolving node failed", "id", node.ID(), "newdelay", t.resolveDelay)
return false
}
// The node was found.
t.resolveDelay = initialResolveDelay
- t.dest = resolved
- d.log.Debug("Resolved node", "id", t.dest.ID(), "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()})
-
+ t.destPtr.Store(resolved)
+ d.log.Debug("Resolved node", "id", resolved.ID(), "addr", &net.TCPAddr{IP: resolved.IP(), Port: resolved.TCP()})
return true
}
// dial performs the actual connection attempt.
func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error {
dialMeter.Mark(1)
- fd, err := d.dialer.Dial(d.ctx, t.dest)
+ fd, err := d.dialer.Dial(d.ctx, dest)
if err != nil {
- d.log.Trace("Dial error", "id", t.dest.ID(), "addr", nodeAddr(t.dest), "conn", t.flags, "err", cleanupDialErr(err))
+ d.log.Trace("Dial error", "id", dest.ID(), "addr", nodeAddr(dest), "conn", t.flags, "err", cleanupDialErr(err))
dialConnectionError.Mark(1)
return &dialError{err}
}
@@ -562,8 +568,9 @@ func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error {
}
func (t *dialTask) String() string {
- id := t.dest.ID()
- return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], t.dest.IP(), t.dest.TCP())
+ node := t.dest()
+ id := node.ID()
+ return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], node.IP(), node.TCP())
}
func cleanupDialErr(err error) error {
From d67620c8f4bae5c552f5597c62bc03206695ad28 Mon Sep 17 00:00:00 2001
From: Marcello Ardizzone
Date: Wed, 12 Jun 2024 11:11:25 +0200
Subject: [PATCH 091/103] core, eth, internal, miner, params: enforce 30gwei
for gas related configs in bor (#1264)
* add: enforce 30gwei for gas related configs in bor
* chg: fix lint
---
core/txpool/legacypool/legacypool.go | 5 +++--
core/txpool/legacypool/legacypool_test.go | 20 +++++++++++++++++++
docs/cli/example_config.toml | 4 ++--
docs/cli/server.md | 6 +++---
eth/backend.go | 3 ++-
eth/catalyst/api_test.go | 4 ++--
eth/gasprice/gasprice.go | 4 ++--
internal/cli/server/config.go | 6 +++---
internal/cli/server/config_test.go | 13 +++++++++++-
internal/cli/server/testdata/default.toml | 6 +++---
miner/miner.go | 2 +-
miner/worker_test.go | 2 +-
.../testnet-amoy/archive/config.toml | 6 +++---
.../sentry/sentry/bor/config.toml | 6 +++---
.../sentry/validator/bor/config.toml | 6 +++---
.../without-sentry/bor/config.toml | 6 +++---
.../templates/testnet-v4/archive/config.toml | 6 +++---
.../testnet-v4/sentry/sentry/bor/config.toml | 6 +++---
.../sentry/validator/bor/config.toml | 6 +++---
.../testnet-v4/without-sentry/bor/config.toml | 6 +++---
params/protocol_params.go | 9 +++++++++
21 files changed, 87 insertions(+), 45 deletions(-)
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index 9170bd4e14fa..1ffeb54ab182 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -147,7 +147,7 @@ var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
- PriceLimit: 1,
+ PriceLimit: params.BorDefaultTxPoolPriceLimit,
PriceBump: 10,
AccountSlots: 16,
@@ -167,7 +167,8 @@ func (config *Config) sanitize() Config {
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
conf.Rejournal = time.Second
}
- if conf.PriceLimit < 1 {
+ // enforce txpool price limit to 30gwei in bor
+ if conf.PriceLimit != params.BorDefaultTxPoolPriceLimit {
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
conf.PriceLimit = DefaultConfig.PriceLimit
}
diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go
index f4f2048fe4d7..a5fd8de6ab9a 100644
--- a/core/txpool/legacypool/legacypool_test.go
+++ b/core/txpool/legacypool/legacypool_test.go
@@ -61,6 +61,14 @@ const (
func init() {
testTxPoolConfig = DefaultConfig
testTxPoolConfig.Journal = ""
+ /*
+ Given the introduction of `BorDefaultTxPoolPriceLimit=30gwei`,
+ we set `testTxPoolConfig.PriceLimit = 1` to avoid rewriting all `legacypool_test.go` tests,
+ causing code divergence from geth, as this has been widely tested on different networks.
+ Also, `worker_test.go` has been adapted to reflect such changes.
+ Furthermore, config test can be found in `TestTxPoolDefaultPriceLimit`
+ */
+ testTxPoolConfig.PriceLimit = 1
cpy := *params.TestChainConfig
eip1559Config = &cpy
@@ -286,6 +294,18 @@ func (c *testChain) State() (*state.StateDB, error) {
return stdb, nil
}
+// TestTxPoolDefaultPriceLimit ensures the bor default tx pool price limit is set correctly.
+func TestTxPoolDefaultPriceLimit(t *testing.T) {
+ t.Parallel()
+
+ pool, _ := setupPool()
+ defer pool.Close()
+
+ if have, want := pool.config.PriceLimit, uint64(params.BorDefaultTxPoolPriceLimit); have != want {
+ t.Fatalf("txpool price limit incorrect: have %d, want %d", have, want)
+ }
+}
+
// This test simulates a scenario where a new block is imported during a
// state reset and tests whether the pending state is in sync with the
// block head event that initiated the resetState().
diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml
index 889f78f05acb..21705db05571 100644
--- a/docs/cli/example_config.toml
+++ b/docs/cli/example_config.toml
@@ -61,7 +61,7 @@ devfakeauthor = false # Run miner without validator set authorization
nolocals = false # Disables price exemptions for locally submitted transactions
journal = "transactions.rlp" # Disk journal for local transaction to survive node restarts
rejournal = "1h0m0s" # Time interval to regenerate the local transaction journal
- pricelimit = 1 # Minimum gas price limit to enforce for acceptance into the pool (mainnet = 30000000000)
+ pricelimit = 30000000000 # Minimum gas price limit to enforce for acceptance into the pool. Regardless the value set, it will be enforced to 30000000000 in bor.
pricebump = 10 # Price bump percentage to replace an already existing transaction
accountslots = 16 # Minimum number of executable transaction slots guaranteed per account
globalslots = 32768 # Maximum number of executable transaction slots for all accounts
@@ -74,7 +74,7 @@ devfakeauthor = false # Run miner without validator set authorization
etherbase = "" # Public address for block mining rewards
extradata = "" # Block extra data set by the miner (default = client version)
gaslimit = 30000000 # Target gas ceiling for mined blocks
- gasprice = "1000000000" # Minimum gas price for mining a transaction (recommended for mainnet = 30000000000, default suitable for amoy/mumbai/devnet)
+ gasprice = "30000000000" # Minimum gas price for mining a transaction. Regardless the value set, it will be enforced to 30000000000 in bor, default suitable for amoy/mumbai/devnet.
recommit = "2m5s" # The time interval for miner to re-create mining work
commitinterrupt = true # Interrupt the current mining work when time is exceeded and create partial blocks
diff --git a/docs/cli/server.md b/docs/cli/server.md
index fbd4adad3fb2..fc04f12022c1 100644
--- a/docs/cli/server.md
+++ b/docs/cli/server.md
@@ -46,7 +46,7 @@ The ```bor server``` command runs the Bor client.
- ```gpo.blocks```: Number of recent blocks to check for gas prices (default: 20)
-- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 2)
+- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 30000000000). It's set to 30gwei in bor
- ```gpo.maxblockhistory```: Maximum block history of gasprice oracle (default: 1024)
@@ -250,7 +250,7 @@ The ```bor server``` command runs the Bor client.
- ```miner.gaslimit```: Target gas ceiling (gas limit) for mined blocks (default: 30000000)
-- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 1000000000)
+- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 30000000000). It's set to 30gwei in bor
- ```miner.interruptcommit```: Interrupt block commit when block creation time is passed (default: true)
@@ -306,6 +306,6 @@ The ```bor server``` command runs the Bor client.
- ```txpool.pricebump```: Price bump percentage to replace an already existing transaction (default: 10)
-- ```txpool.pricelimit```: Minimum gas price limit to enforce for acceptance into the pool (default: 1)
+- ```txpool.pricelimit```: Minimum gas price limit to enforce the acceptance of txs into the pool (default: 30000000000). It's set to 30gwei in bor
- ```txpool.rejournal```: Time interval to regenerate the local transaction journal (default: 1h0m0s)
\ No newline at end of file
diff --git a/eth/backend.go b/eth/backend.go
index 12170fa238b9..627e39d1f020 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -122,7 +122,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
}
- if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 {
+ // enforce minimum gas price of 30 gwei in bor
+ if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(big.NewInt(params.BorDefaultMinerGasPrice)) != 0 {
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
}
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 9ef0cf7bba55..3e95b57eba46 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -84,7 +84,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
- tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey)
+ tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*32), nil), types.LatestSigner(&config), testKey)
g.AddTx(tx)
testNonce++
}
@@ -604,7 +604,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
Nonce: statedb.GetNonce(testAddr),
Value: new(big.Int),
Gas: 1000000,
- GasPrice: big.NewInt(2 * params.InitialBaseFee),
+ GasPrice: big.NewInt(32 * params.InitialBaseFee),
Data: logCode,
})
ethservice.TxPool().Add([]*types.Transaction{tx}, false, true)
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index b0b58e133b98..41828b019659 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -36,7 +36,7 @@ const sampleNumber = 3 // Number of transactions sampled in a block
var (
DefaultMaxPrice = big.NewInt(500 * params.GWei)
- DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+ DefaultIgnorePrice = big.NewInt(params.BorDefaultGpoIgnorePrice)
)
type Config struct {
@@ -101,7 +101,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
}
ignorePrice := params.IgnorePrice
- if ignorePrice == nil || ignorePrice.Int64() <= 0 {
+ if ignorePrice == nil || ignorePrice.Int64() != DefaultIgnorePrice.Int64() {
ignorePrice = DefaultIgnorePrice
log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
} else if ignorePrice.Int64() > 0 {
diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go
index 43ca8574f486..a496f2358660 100644
--- a/internal/cli/server/config.go
+++ b/internal/cli/server/config.go
@@ -655,7 +655,7 @@ func DefaultConfig() *Config {
NoLocals: false,
Journal: "transactions.rlp",
Rejournal: 1 * time.Hour,
- PriceLimit: 1, // geth's default
+ PriceLimit: params.BorDefaultTxPoolPriceLimit, // bor's default
PriceBump: 10,
AccountSlots: 16,
GlobalSlots: 32768,
@@ -666,8 +666,8 @@ func DefaultConfig() *Config {
Sealer: &SealerConfig{
Enabled: false,
Etherbase: "",
- GasCeil: 30_000_000, // geth's default
- GasPrice: big.NewInt(1 * params.GWei), // geth's default
+ GasCeil: 30_000_000, // geth's default
+ GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // bor's default
ExtraData: "",
Recommit: 125 * time.Second,
CommitInterruptFlag: true,
diff --git a/internal/cli/server/config_test.go b/internal/cli/server/config_test.go
index 3e6bb76b599e..abb8ca39fbbe 100644
--- a/internal/cli/server/config_test.go
+++ b/internal/cli/server/config_test.go
@@ -1,10 +1,14 @@
package server
import (
+ "math/big"
"testing"
"time"
"github.com/stretchr/testify/assert"
+
+ "github.com/ethereum/go-ethereum/eth/ethconfig"
+ "github.com/ethereum/go-ethereum/params"
)
func TestConfigDefault(t *testing.T) {
@@ -15,8 +19,15 @@ func TestConfigDefault(t *testing.T) {
_, err := config.buildNode()
assert.NoError(t, err)
- _, err = config.buildEth(nil, nil)
+ ethConfig, err := config.buildEth(nil, nil)
assert.NoError(t, err)
+ assertBorDefaultGasPrice(t, ethConfig)
+}
+
+// assertBorDefaultGasPrice asserts the bor default gas price is set correctly.
+func assertBorDefaultGasPrice(t *testing.T, ethConfig *ethconfig.Config) {
+ assert.NotNil(t, ethConfig)
+ assert.Equal(t, ethConfig.Miner.GasPrice, big.NewInt(params.BorDefaultMinerGasPrice))
}
func TestConfigMerge(t *testing.T) {
diff --git a/internal/cli/server/testdata/default.toml b/internal/cli/server/testdata/default.toml
index 3a6c2f0cb5c8..72f166a8c24a 100644
--- a/internal/cli/server/testdata/default.toml
+++ b/internal/cli/server/testdata/default.toml
@@ -58,7 +58,7 @@ devfakeauthor = false
nolocals = false
journal = "transactions.rlp"
rejournal = "1h0m0s"
- pricelimit = 1
+ pricelimit = 30000000000
pricebump = 10
accountslots = 16
globalslots = 32768
@@ -71,7 +71,7 @@ devfakeauthor = false
etherbase = ""
extradata = ""
gaslimit = 30000000
- gasprice = "1000000000"
+ gasprice = "30000000000"
recommit = "2m5s"
commitinterrupt = true
@@ -127,7 +127,7 @@ devfakeauthor = false
maxheaderhistory = 1024
maxblockhistory = 1024
maxprice = "500000000000"
- ignoreprice = "2"
+ ignoreprice = "30000000000"
[telemetry]
metrics = false
diff --git a/miner/miner.go b/miner/miner.go
index 03fa21edbfa2..e3f11480b8bd 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -60,7 +60,7 @@ type Config struct {
// DefaultConfig contains default settings for miner.
var DefaultConfig = Config{
GasCeil: 30000000,
- GasPrice: big.NewInt(params.GWei),
+ GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // enforces minimum gas price of 30 gwei in bor
// The default recommit time is chosen as two seconds since
// consensus-layer usually will wait a half slot of time(6s)
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 904dedd19b8c..cac3e936df7f 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -297,7 +297,7 @@ func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *t
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
var tx *types.Transaction
- gasPrice := big.NewInt(10 * params.InitialBaseFee)
+ gasPrice := big.NewInt(30 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(storageContractByteCode)), types.HomesteadSigner{}, testBankKey)
contractAddr := crypto.CreateAddress(TestBankAddress, b.txPool.Nonce(TestBankAddress))
diff --git a/packaging/templates/testnet-amoy/archive/config.toml b/packaging/templates/testnet-amoy/archive/config.toml
index 042cceacc79a..0940fd78198a 100644
--- a/packaging/templates/testnet-amoy/archive/config.toml
+++ b/packaging/templates/testnet-amoy/archive/config.toml
@@ -57,12 +57,12 @@ gcmode = "archive"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -119,7 +119,7 @@ gcmode = "archive"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
index badf20133ad8..94c195a2fec9 100644
--- a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
+++ b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
@@ -57,12 +57,12 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -119,7 +119,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
index cae793b7cd6c..7c12b2c8b7db 100644
--- a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
+++ b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
@@ -59,13 +59,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
index 4eff7f49a3b6..64053f7c3187 100644
--- a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
+++ b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
@@ -59,13 +59,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
-# ignoreprice = "2"
+# ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/archive/config.toml b/packaging/templates/testnet-v4/archive/config.toml
index cee19b700ce8..4bbefafe0dcc 100644
--- a/packaging/templates/testnet-v4/archive/config.toml
+++ b/packaging/templates/testnet-v4/archive/config.toml
@@ -59,12 +59,12 @@ gcmode = "archive"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ gcmode = "archive"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml b/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
index 6be74e41031a..a21a1eb3eb05 100644
--- a/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
+++ b/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
@@ -59,12 +59,12 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/sentry/validator/bor/config.toml b/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
index 7a483d3a1b5f..ed1fac2a51b7 100644
--- a/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
+++ b/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
@@ -61,13 +61,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "2"
+ # ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/without-sentry/bor/config.toml b/packaging/templates/testnet-v4/without-sentry/bor/config.toml
index 82c7c3a5e070..e9e3b0d1cd23 100644
--- a/packaging/templates/testnet-v4/without-sentry/bor/config.toml
+++ b/packaging/templates/testnet-v4/without-sentry/bor/config.toml
@@ -61,13 +61,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 1
+ # pricelimit = 30000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "1000000000"
+ # gasprice = "30000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
-# ignoreprice = "2"
+# ignoreprice = "30000000000"
[telemetry]
metrics = true
diff --git a/params/protocol_params.go b/params/protocol_params.go
index a1f2a17845a9..031e3af0be9b 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -181,6 +181,15 @@ const (
BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block
+
+ // BorDefaultMinerGasPrice defines the minimum gas price for bor validators to mine a transaction.
+ BorDefaultMinerGasPrice = 30 * GWei
+
+ // BorDefaultTxPoolPriceLimit defines the minimum gas price limit for bor to enforce txs acceptance into the pool.
+ BorDefaultTxPoolPriceLimit = 30 * GWei
+
+ // BorDefaultGpoIgnorePrice defines the minimum gas price below which bor gpo will ignore transactions.
+ BorDefaultGpoIgnorePrice = 30 * GWei
)
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
From a9e5c95be84962cf0493d6b182de2c6f52a7372f Mon Sep 17 00:00:00 2001
From: Marcello Ardizzone
Date: Mon, 10 Jun 2024 18:47:04 +0200
Subject: [PATCH 092/103] chg: upgrade deps and solve sec issues (#1261)
* chg: upgrade deps and solve sec issues
* chg: use old kzg-4844 repo
* chg: ignore explicitly Errorf return value
---
ethdb/pebble/pebble.go | 5 +-
go.mod | 235 +++++-----
go.sum | 966 ++++++++++++++++++++++-------------------
log/format.go | 4 +
4 files changed, 638 insertions(+), 572 deletions(-)
diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go
index 3001cb1c6aa0..8df3e52be0fa 100644
--- a/ethdb/pebble/pebble.go
+++ b/ethdb/pebble/pebble.go
@@ -626,7 +626,10 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error {
reader := b.b.Reader()
for {
- kind, k, v, ok := reader.Next()
+ kind, k, v, ok, err := reader.Next()
+ if err != nil {
+ return err
+ }
if !ok {
break
}
diff --git a/go.mod b/go.mod
index 17128decbd96..e627477b0089 100644
--- a/go.mod
+++ b/go.mod
@@ -5,50 +5,50 @@ go 1.22
toolchain go1.22.1
require (
- github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1
- github.com/BurntSushi/toml v1.3.2
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
+ github.com/BurntSushi/toml v1.4.0
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d
github.com/JekaMas/workerpool v1.1.8
- github.com/Microsoft/go-winio v0.6.1
- github.com/VictoriaMetrics/fastcache v1.12.1
- github.com/aws/aws-sdk-go-v2 v1.25.2
- github.com/aws/aws-sdk-go-v2/config v1.18.45
- github.com/aws/aws-sdk-go-v2/credentials v1.13.43
- github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2
- github.com/btcsuite/btcd/btcec/v2 v2.3.2
+ github.com/Microsoft/go-winio v0.6.2
+ github.com/VictoriaMetrics/fastcache v1.12.2
+ github.com/aws/aws-sdk-go-v2 v1.27.2
+ github.com/aws/aws-sdk-go-v2/config v1.27.18
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.18
+ github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10
+ github.com/btcsuite/btcd/btcec/v2 v2.3.3
github.com/cespare/cp v1.1.1
- github.com/cloudflare/cloudflare-go v0.79.0
- github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
+ github.com/cloudflare/cloudflare-go v0.97.0
+ github.com/cockroachdb/pebble v1.1.0
github.com/consensys/gnark-crypto v0.12.1
- github.com/cosmos/cosmos-sdk v0.47.3
+ github.com/cosmos/cosmos-sdk v0.50.6
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/crate-crypto/go-kzg-4844 v0.7.0
- github.com/davecgh/go-spew v1.1.1
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/deckarep/golang-set/v2 v2.6.0
- github.com/dop251/goja v0.0.0-20240220182346-e401ed450204
+ github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2
github.com/emirpasic/gods v1.18.1
github.com/ethereum/c-kzg-4844 v0.4.3
- github.com/fatih/color v1.16.0
+ github.com/fatih/color v1.17.0
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
- github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
- github.com/fsnotify/fsnotify v1.6.0
+ github.com/fjl/memsize v0.0.2
+ github.com/fsnotify/fsnotify v1.7.0
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
github.com/gofrs/flock v0.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/mock v1.6.0
- github.com/golang/protobuf v1.5.3
+ github.com/golang/protobuf v1.5.4
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
- github.com/gorilla/websocket v1.5.0
- github.com/graph-gophers/graphql-go v1.3.0
- github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
- github.com/hashicorp/go-bexpr v0.1.10
- github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
- github.com/hashicorp/hcl/v2 v2.10.1
- github.com/heimdalr/dag v1.2.1
- github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7
+ github.com/gorilla/websocket v1.5.2
+ github.com/graph-gophers/graphql-go v1.5.0
+ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
+ github.com/hashicorp/go-bexpr v0.1.14
+ github.com/hashicorp/golang-lru v1.0.2
+ github.com/hashicorp/hcl/v2 v2.20.1
+ github.com/heimdalr/dag v1.4.0
+ github.com/holiman/billy v0.0.0-20240322075458-72a4e81ec6da
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.4
github.com/huin/goupnp v1.3.0
@@ -62,7 +62,7 @@ require (
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c
github.com/kylelemons/godebug v1.1.0
github.com/maticnetwork/crand v1.0.2
- github.com/maticnetwork/heimdall v1.0.4
+ github.com/maticnetwork/heimdall v1.0.7
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
@@ -73,70 +73,69 @@ require (
github.com/pelletier/go-toml v1.9.5
github.com/peterh/liner v1.2.2
github.com/protolambda/bls12-381-util v0.1.0
- github.com/rs/cors v1.10.1
+ github.com/rs/cors v1.11.0
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/status-im/keycard-go v0.3.2
github.com/stretchr/testify v1.9.0
- github.com/supranational/blst v0.3.11
+ github.com/supranational/blst v0.3.12
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tendermint/tendermint v0.34.24
github.com/tyler-smith/go-bip39 v1.1.0
- github.com/urfave/cli/v2 v2.25.7
+ github.com/urfave/cli/v2 v2.27.2
github.com/xsleonard/go-merkle v1.1.0
- go.opentelemetry.io/otel v1.19.0
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
- go.opentelemetry.io/otel/sdk v1.19.0
- go.uber.org/automaxprocs v1.5.2
- go.uber.org/goleak v1.2.1
- golang.org/x/crypto v0.21.0
- golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
- golang.org/x/sync v0.5.0
- golang.org/x/sys v0.18.0
- golang.org/x/text v0.14.0
- golang.org/x/time v0.3.0
- golang.org/x/tools v0.15.0
- google.golang.org/grpc v1.58.3
- gopkg.in/natefinch/lumberjack.v2 v2.0.0
+ go.opentelemetry.io/otel v1.27.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0
+ go.opentelemetry.io/otel/sdk v1.27.0
+ go.uber.org/automaxprocs v1.5.3
+ go.uber.org/goleak v1.3.0
+ golang.org/x/crypto v0.24.0
+ golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8
+ golang.org/x/sync v0.7.0
+ golang.org/x/sys v0.21.0
+ golang.org/x/text v0.16.0
+ golang.org/x/time v0.5.0
+ golang.org/x/tools v0.22.0
+ google.golang.org/grpc v1.64.0
+ gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
require (
- cloud.google.com/go/compute v1.21.0 // indirect
- cloud.google.com/go/iam v1.1.1 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
+ cloud.google.com/go/iam v1.1.6 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pborman/uuid v1.2.1 // indirect
- github.com/pelletier/go-toml/v2 v2.0.9 // indirect
- github.com/subosito/gotenv v1.4.1 // indirect
+ github.com/pelletier/go-toml/v2 v2.1.0 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
require (
- cloud.google.com/go v0.110.4 // indirect
- cloud.google.com/go/pubsub v1.32.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 // indirect
+ cloud.google.com/go v0.112.1 // indirect
+ cloud.google.com/go/pubsub v1.36.1 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/DataDog/zstd v1.5.2 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/aws/aws-sdk-go v1.40.45 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.28.3 // indirect
- github.com/aws/smithy-go v1.20.1 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
+ github.com/aws/smithy-go v1.20.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
- github.com/cockroachdb/errors v1.9.1 // indirect
+ github.com/cockroachdb/errors v1.11.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
- github.com/cockroachdb/redact v1.1.3 // indirect
+ github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.13 // indirect
- github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
@@ -149,29 +148,28 @@ require (
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kilic/bls12-381 v0.1.0 // indirect
- github.com/klauspost/compress v1.16.7 // indirect
+ github.com/klauspost/compress v1.17.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_golang v1.14.0 // indirect
- github.com/prometheus/client_model v0.4.0 // indirect
- github.com/prometheus/common v0.39.0 // indirect
- github.com/prometheus/procfs v0.9.0 // indirect
- github.com/rogpeppe/go-internal v1.10.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/client_golang v1.19.0 // indirect
+ github.com/prometheus/client_model v0.5.0 // indirect
+ github.com/prometheus/common v0.48.0 // indirect
+ github.com/prometheus/procfs v0.12.0 // indirect
+ github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/tklauser/go-sysconf v0.3.10 // indirect
- github.com/tklauser/numcpus v0.4.0 // indirect
- github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
- golang.org/x/mod v0.14.0 // indirect
- golang.org/x/net v0.23.0 // indirect
- google.golang.org/protobuf v1.31.0
+ github.com/tklauser/go-sysconf v0.3.11 // indirect
+ github.com/tklauser/numcpus v0.6.0 // indirect
+ github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
+ golang.org/x/mod v0.18.0 // indirect
+ golang.org/x/net v0.26.0 // indirect
+ google.golang.org/protobuf v1.34.1
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools v2.2.0+incompatible
pgregory.net/rapid v1.1.0
@@ -179,69 +177,76 @@ require (
)
require (
- cloud.google.com/go/compute/metadata v0.2.3 // indirect
+ cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.1 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
github.com/cbergoon/merkletree v0.2.0 // indirect
- github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cosmos/go-bip39 v1.0.0 // indirect
github.com/cosmos/ledger-cosmos-go v0.13.0 // indirect
+ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/etcd-io/bbolt v1.3.3 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gammazero/deque v0.2.1 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
- github.com/go-logr/logr v1.2.4 // indirect
+ github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/go-redis/redis v6.15.7+incompatible // indirect
- github.com/goccy/go-json v0.10.2 // indirect
+ github.com/go-redis/redis/v8 v8.11.5 // indirect
+ github.com/go-redsync/redsync/v4 v4.0.4 // indirect
+ github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
- github.com/google/go-cmp v0.5.9 // indirect
- github.com/google/s2a-go v0.1.4 // indirect
- github.com/googleapis/gax-go/v2 v2.11.0 // indirect
- github.com/gorilla/mux v1.8.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
- github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/s2a-go v0.1.7 // indirect
+ github.com/googleapis/gax-go/v2 v2.12.2 // indirect
+ github.com/gorilla/mux v1.8.1 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
- github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
- github.com/inconshreveable/mousetrap v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
- github.com/magiconair/properties v1.8.6 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
- github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
+ github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
- github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+ github.com/montanaflynn/stats v0.7.0 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/oapi-codegen/runtime v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/rakyll/statik v0.1.7 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
+ github.com/robfig/cron/v3 v3.0.1 // indirect
+ github.com/sagikazarmark/locafero v0.4.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
- github.com/spf13/afero v1.9.2 // indirect
- github.com/spf13/cast v1.5.0 // indirect
- github.com/spf13/cobra v1.5.0 // indirect
- github.com/spf13/jwalterweatherman v1.1.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.11.0 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- github.com/spf13/viper v1.13.0 // indirect
- github.com/streadway/amqp v1.0.0 // indirect
+ github.com/spf13/viper v1.18.2 // indirect
+ github.com/streadway/amqp v1.1.0 // indirect
github.com/stumble/gorocksdb v0.0.3 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect
@@ -252,30 +257,32 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
- github.com/yusufpapurcu/wmi v1.2.2 // indirect
+ github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/zondax/ledger-go v0.14.1 // indirect
- go.opentelemetry.io/otel/metric v1.19.0 // indirect
- golang.org/x/oauth2 v0.10.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
+ go.opentelemetry.io/otel/metric v1.27.0 // indirect
+ go.uber.org/atomic v1.9.0 // indirect
+ go.uber.org/multierr v1.9.0 // indirect
+ golang.org/x/oauth2 v0.20.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
- github.com/RichardKnop/machinery v1.7.4 // indirect
- github.com/RichardKnop/redsync v1.2.0 // indirect
+ github.com/RichardKnop/machinery v1.10.6 // indirect
github.com/prometheus/tsdb v0.10.0
github.com/zclconf/go-cty v1.13.0 // indirect
github.com/zondax/hid v0.9.1 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
- go.opentelemetry.io/otel/trace v1.19.0
- go.opentelemetry.io/proto/otlp v1.0.0 // indirect
- google.golang.org/api v0.126.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
+ go.opentelemetry.io/otel/trace v1.27.0
+ go.opentelemetry.io/proto/otlp v1.2.0 // indirect
+ google.golang.org/api v0.169.0 // indirect
+ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
)
replace github.com/cosmos/cosmos-sdk => github.com/maticnetwork/cosmos-sdk v0.38.4
@@ -284,7 +291,7 @@ replace github.com/tendermint/tendermint => github.com/maticnetwork/tendermint v
replace github.com/tendermint/tm-db => github.com/tendermint/tm-db v0.2.0
-replace github.com/ethereum/go-ethereum => github.com/maticnetwork/bor v1.2.7
+replace github.com/ethereum/go-ethereum => github.com/maticnetwork/bor v1.3.2
replace github.com/Masterminds/goutils => github.com/Masterminds/goutils v1.1.1
diff --git a/go.sum b/go.sum
index de4c422601e5..8bc6164fa133 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,4 @@
+cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -17,6 +18,7 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
+cloud.google.com/go v0.76.0/go.mod h1:r9EvIAvLrunusnetGdQ50M/gKui1x3zdGW/VELGkdpw=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
@@ -37,8 +39,9 @@ cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFO
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
-cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
+cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
+cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@@ -200,13 +203,13 @@ cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
-cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk=
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
-cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
@@ -375,8 +378,9 @@ cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8=
cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
-cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
+cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
+cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk=
@@ -400,8 +404,9 @@ cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8
cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24=
cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI=
cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM=
-cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U=
cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
+cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM=
+cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=
@@ -515,12 +520,14 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/pubsub v1.10.0/go.mod h1:eNpTrkOy7dCpkNyaSNetMa6udbgecJMd0ZsTJS/cuNo=
cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI=
cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0=
cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8=
cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4=
-cloud.google.com/go/pubsub v1.32.0 h1:JOEkgEYBuUTHSyHS4TcqOFuWr+vD6qO/imsFqShUCp4=
cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
+cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y=
+cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE=
cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg=
cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k=
cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM=
@@ -727,38 +734,65 @@ cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvo
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
-github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1 h1:fXPMAmuh0gDuRDey0atC8cXBuKIlqCzCkL8sm1n9Ov0=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1/go.mod h1:SUZc9YRRHfx2+FAQKNDGrssXehqLpxmwRv2mC/5ntj4=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/testdata/perf v0.0.0-20240208231215-981108a6de20/go.mod h1:KMKhmwqL1TqoNRkQG2KGmDaVwT5Dte9d3PoADB38/UY=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
-github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
-github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
+github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d h1:RO27lgfZF8s9lZ3pWyzc0gCE0RZC+6/PXbRjAa0CNp8=
@@ -767,37 +801,36 @@ github.com/JekaMas/workerpool v1.1.8 h1:IbDbTITrDgt1xAzdzTS9aLk4Q/4dCsjUOopiyFkD
github.com/JekaMas/workerpool v1.1.8/go.mod h1:IoDWPpwMcA27qbuugZKeBslDrgX09lVmksuh9sjzbhc=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
-github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
+github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig/v3 v3.2.1 h1:n6EPaDyLSvCEa3frruQvAiHuNp2dhBlMSmkEr+HuzGc=
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg=
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
-github.com/RichardKnop/machinery v1.7.4 h1:QMHik7BaeN3TsfXcg48xw6tsM9IqzC8rBgoK5i6/IPA=
github.com/RichardKnop/machinery v1.7.4/go.mod h1:W87mnh7t91WdrwGbdnAjvDzqD/bqBV+0+GF276gv/bU=
-github.com/RichardKnop/redsync v1.2.0 h1:gK35hR3zZkQigHKm8wOGb9MpJ9BsrW6MzxezwjTcHP0=
+github.com/RichardKnop/machinery v1.10.6 h1:wviOkVLVM9DaNFAOtXEuZsr9d+Okm4VSw7AILVLIhyc=
+github.com/RichardKnop/machinery v1.10.6/go.mod h1:qT0dXDPzsGqwHoYWO12Gb25MxA/9HfxaqdIaZp9ofWM=
github.com/RichardKnop/redsync v1.2.0/go.mod h1:9b8nBGAX3bE2uCfJGSnsDvF23mKyHTZzmvmj5FH3Tp0=
-github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
-github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
-github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
+github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
+github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
+github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
+github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
@@ -806,6 +839,7 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
+github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -815,12 +849,11 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg=
-github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
@@ -828,6 +861,8 @@ github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:o
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -836,70 +871,55 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
-github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
-github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
-github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
+github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
-github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
-github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
-github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM=
-github.com/aws/aws-sdk-go-v2 v1.25.2 h1:/uiG1avJRgLGiQM9X3qJM8+Qa6KRGK5rRPuXE0HUM+w=
github.com/aws/aws-sdk-go-v2 v1.25.2/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo=
-github.com/aws/aws-sdk-go-v2/config v1.18.43/go.mod h1:NiFev8qlgg8MPzw3fO/EwzMZeZwlJEKGwfpjRPA9Nvw=
-github.com/aws/aws-sdk-go-v2/config v1.18.45 h1:Aka9bI7n8ysuwPeFdm77nfbyHCAKQ3z9ghB3S/38zes=
-github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.41/go.mod h1:cc3Fn7DkKbJalPtQnudHGZZ8ml9+hwtbc1CJONsYYqk=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.43 h1:LU8vo40zBlo3R7bAvBVy/ku4nxGEyZe9N8MqAeFTzF8=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 h1:AK0J8iYBFeUk2Ax7O8YpLtFsfhdOByh2QIkHmigpRYk=
+github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8=
+github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
+github.com/aws/aws-sdk-go-v2/config v1.27.6/go.mod h1:W9RZFF2pL+OhnUSZsQS/eDMWD8v+R+yWgjj3nSlrXVU=
+github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk=
+github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.6/go.mod h1:chJZuJ7TkW4kiMwmldOJOEueBoSkUb4ynZS1d9dhygo=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2/go.mod h1:iRlGzMix0SExQEviAyptRWRGdYNo3+ufW/lCzvKVTUc=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 h1:bNo4LagzUKbjdxE0tIcR9pMzLR2U/Tgie1Hq1HQ3iH8=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2/go.mod h1:wRQv0nN6v9wDXuWThpovGQjqF1HFdcgWjporw14lS8k=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 h1:EtOU5jsPdIQNP+6Q2C5e3d65NKT1PeCiQk+9OdzO12Q=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2/go.mod h1:tyF5sKccmDz0Bv4NrstEr+/9YkSPJHrcO7UsUKf7pWM=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4 h1:jRiWxyuVO8PlkN72wDMVn/haVH4SDCBkUt0Lf/dxd7s=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4/go.mod h1:Ru7vg1iQ7cR4i7SZ/JTLYN9kaXtbL69UdgG0OQWQxW0=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.29.5/go.mod h1:6zl0jh5MUKuJ07eHn3MNeLOVutxwl8m9vQltZjoLakM=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2 h1:/RPQNjh1sDIezpXaFIkZb7MlXnSyAqjVdAwcJuGYTqg=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA=
-github.com/aws/aws-sdk-go-v2/service/sso v1.15.0/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4=
-github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo=
-github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 h1:utEGkfdQ4L6YW/ietH7111ZYglLJvS+sLriHJ1NBJEQ=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.40.1/go.mod h1:7Wa9sIDxey/5b2FK5r1Z6ryVfojt4Nl+VzzpK8q1L+M=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10 h1:J9uHribwEgHmesH5r0enxsZYyiGBWd2AaExSW2SydqE=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10/go.mod h1:tdzmlLwRjsHJjd4XXoSSnubCkVdRa39y4jCp4RACMkY=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.1/go.mod h1:RsYqzYr2F2oPDdpy+PdhephuZxTfjHQe7SOBcZGoAU8=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 h1:9/GylMS45hGGFCcMrUZDVayQE1jYSIN6da9jo7RAYIw=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1/go.mod h1:YjAPFn4kGFqKC54VsHs5fn5B6d+PCY2tziEa3U/GB5Y=
-github.com/aws/aws-sdk-go-v2/service/sts v1.23.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU=
-github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ=
-github.com/aws/aws-sdk-go-v2/service/sts v1.28.3 h1:TkiFkSVX990ryWIMBCT4kPqZEgThQe1xPU/AQXavtvU=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.3/go.mod h1:xYNauIUqSuvzlPVb3VB5no/n48YGhmlInD3Uh0Co8Zc=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
-github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
-github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
-github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
+github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
+github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
-github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8=
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -920,37 +940,36 @@ github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
-github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
+github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
+github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
-github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
-github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
-github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
-github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
+github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0=
+github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
-github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
-github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
-github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
-github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
+github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/cbergoon/merkletree v0.2.0 h1:Bttqr3OuoiZEo4ed1L7fTasHka9II+BF9fhBfbNEEoQ=
github.com/cbergoon/merkletree v0.2.0/go.mod h1:5c15eckUgiucMGDOCanvalj/yJnD+KAZj1qyJtRW5aM=
-github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
@@ -962,6 +981,10 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
+github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -971,11 +994,10 @@ github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMn
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
-github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
-github.com/cloudflare/cloudflare-go v0.79.0 h1:ErwCYDjFCYppDJlDJ/5WhsSmzegAUe2+K9qgFyQDg3M=
-github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc=
+github.com/cloudflare/cloudflare-go v0.89.0/go.mod h1:eyuehb1i6BNRc+ZwaTZAiRHeE+4jbKvHAns19oGeakg=
+github.com/cloudflare/cloudflare-go v0.97.0 h1:feZRGiRF1EbljnNIYdt8014FnOLtC3CCvgkLXu915ks=
+github.com/cloudflare/cloudflare-go v0.97.0/go.mod h1:JXRwuTfHpe5xFg8xytc2w0XC6LcrFsBVMS4WlVaiGg8=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -991,33 +1013,24 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
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/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
-github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
-github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
-github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk=
-github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
-github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8=
+github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw=
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/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM=
-github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A=
-github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo=
-github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
-github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
-github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
-github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
+github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4=
+github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E=
+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/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
-github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU=
+github.com/consensys/gnark-crypto v0.11.2/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -1025,37 +1038,32 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
-github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-cosmos-go v0.13.0 h1:ex0CvCxToSR7j5WjrghPu2Bu9sSXKikjnVvUryNnx4s=
github.com/cosmos/ledger-cosmos-go v0.13.0/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI=
-github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-ipa v0.0.0-20230905211650-63ccabc1a949/go.mod h1:7fZtshzGQ3dxVpDpF51K9mX8oziq8Xd5AoM/UT9fF5o=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
-github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4=
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
+github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
@@ -1063,14 +1071,11 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
-github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
-github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
-github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
-github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
@@ -1080,15 +1085,13 @@ github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnm
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
-github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
-github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
+github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2 h1:OFTHt+yJDo/uaIKMGjEKzc3DGhrpQZoqvMUIloZv6ZY=
+github.com/dop251/goja v0.0.0-20240516125602-ccbae20bcec2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -1098,7 +1101,6 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -1121,7 +1123,6 @@ github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87K
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
-github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/c-kzg-4844 v0.4.3 h1:Mpg+qsE1XyDAc03LyDfJsr8oxrt7mN7HX6wJIlB2880=
github.com/ethereum/c-kzg-4844 v0.4.3/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
@@ -1132,59 +1133,55 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
-github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
+github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY=
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
-github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
+github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA=
+github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gammazero/deque v0.2.0/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
-github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
-github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI=
+github.com/gballet/go-verkle v0.1.0/go.mod h1:7JamHhSTnnHDhcI3G8r4sWaD9XlleriqVlC3FeAQJKM=
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
-github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
-github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
-github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
-github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
-github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
-github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
@@ -1197,7 +1194,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -1212,75 +1208,88 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
-github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
-github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
+github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
+github.com/go-redis/redis/v8 v8.1.1/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw=
+github.com/go-redis/redis/v8 v8.6.0/go.mod h1:DQ9q4Rk2HtwkrwVrdgmphoOQDMfpvcd/nHEwRsicg8s=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
+github.com/go-redsync/redsync/v4 v4.0.4 h1:ru0qG+VCefaZSx3a5ADmlKZXkNdgeeYWIuymDu/tzV8=
+github.com/go-redsync/redsync/v4 v4.0.4/go.mod h1:QBOJAs1k8O6Eyrre4a++pxQgHe5eQ+HF56KuTVv+8Bs=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
-github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
-github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
-github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
+github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
+github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
-github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
-github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
-github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
+github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
-github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
+github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -1316,18 +1325,25 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
-github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
+github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
@@ -1345,9 +1361,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@@ -1380,21 +1396,28 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8q
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
-github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
+github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
+github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
+github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
-github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
+github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@@ -1408,8 +1431,9 @@ github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57Q
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
-github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
+github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA=
+github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -1417,41 +1441,39 @@ github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfre
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0=
-github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
+github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/gorilla/websocket v1.5.2 h1:qoW6V1GT3aZxybsbC6oLnailWnB+qTMVwMreOso9XUw=
+github.com/gorilla/websocket v1.5.2/go.mod h1:0n9H61RBAcf5/38py2MCYbxzPIY9rOkpvvMT24Rqs30=
+github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc=
+github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
+github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
+github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
-github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
-github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-bexpr v0.1.14 h1:uKDeyuOhWhT1r5CiMTjdVY4Aoxdxs6EtwgTGnlosyp4=
+github.com/hashicorp/go-bexpr v0.1.14/go.mod h1:gN7hRKB3s7yT+YvTdnhZVLTENejvhlkZ8UE4YVBS+Q8=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
@@ -1459,8 +1481,9 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -1468,25 +1491,27 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
+github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys=
-github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
+github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
+github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
+github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
@@ -1498,13 +1523,13 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/heimdalr/dag v1.2.1 h1:XJOMaoWqJK1UKdp+4zaO2uwav9GFbHMGCirdViKMRIQ=
-github.com/heimdalr/dag v1.2.1/go.mod h1:Of/wUB7Yoj4dwiOcGOOYIq6MHlPF/8/QMBKFJpwg+yc=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
+github.com/heimdalr/dag v1.4.0 h1:zG3JA4RDVLc55k3AXAgfwa+EgBNZ0TkfOO3C29Ucpmg=
+github.com/heimdalr/dag v1.4.0/go.mod h1:OCh6ghKmU0hPjtwMqWBoNxPmtRioKd1xSu7Zs4sbIqM=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
+github.com/holiman/billy v0.0.0-20240322075458-72a4e81ec6da h1:8qEhdMGSUx67L2s5aGQinJhOwLfIRKLRBHPQq8m6WxE=
+github.com/holiman/billy v0.0.0-20240322075458-72a4e81ec6da/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
-github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
@@ -1513,14 +1538,10 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
-github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
-github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
-github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
-github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
+github.com/hydrogen18/memlistener v1.0.0/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -1528,34 +1549,27 @@ github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM=
github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4=
-github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
-github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0=
-github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
-github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
+github.com/iris-contrib/httpexpect/v2 v2.15.1/go.mod h1:cUwf1Mm5CWs5ahZNHtDq82WuGOitAWBg/eMGevX9ilg=
github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE=
-github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
-github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -1568,8 +1582,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -1578,35 +1590,27 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
-github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c h1:AqsttAyEyIEsNz5WLRwuRwjiT5CMDUfLk6cFJDVPebs=
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kataras/blocks v0.0.6/go.mod h1:UK+Iwk0Oxpc0GdoJja7sEildotAUKK1LYeYcVF0COWc=
github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I=
-github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
-github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA=
-github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
-github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
+github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY=
github.com/kataras/iris/v12 v12.2.0-beta5/go.mod h1:q26aoWJ0Knx/00iPKg5iizDK7oQQSPjbD8np0XDh6dc=
+github.com/kataras/iris/v12 v12.2.5/go.mod h1:bf3oblPF8tQmRgyPCzPZr0mLazvEDFgImdaGZYuN4hw=
github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o=
-github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
-github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
+github.com/kataras/jwt v0.1.9/go.mod h1:Kw6GZv2JQN8K4D3NGaSc/qZzTYfaRdyaC2zWi+RBRIQ=
github.com/kataras/neffos v0.0.20/go.mod h1:srdvC/Uo8mgrApWW0AYtiiLgMbyNPf69qPsd2FhE6MQ=
-github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
-github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
+github.com/kataras/neffos v0.0.22/go.mod h1:IIJZcUDvwBxJGlDj942dqQgyznVKYDti91f8Ez+RRxE=
github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no=
github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI=
-github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
+github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY=
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
@@ -1615,15 +1619,12 @@ github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa
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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
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/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@@ -1633,10 +1634,14 @@ github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHU
github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
-github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
+github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -1655,53 +1660,45 @@ github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LE
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
-github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
-github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
-github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
+github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
+github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
-github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
-github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
-github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
-github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/maticnetwork/bor v1.2.7/go.mod h1:84m4hX8Ru+n3p/WedvrPpmrjdweSMwGJlB1SjCZiP/M=
+github.com/maticnetwork/bor v1.3.2/go.mod h1:AM85jUjwEw0Yk69TTFZ+sQMB506tDMG/4XAwQ4RcRqI=
github.com/maticnetwork/cosmos-sdk v0.38.4 h1:PAfkMXzHDHJoAf4bXQL4UWgwbu/U3yYuXoXxPhXdpBw=
github.com/maticnetwork/cosmos-sdk v0.38.4/go.mod h1:NbuVdUoqlRF6RrFJp27hpbqSoRB8cJJfUxCzUJWtaLA=
github.com/maticnetwork/crand v1.0.2 h1:Af0tAivC8zrxXDpGWNWVT/0s1fOz8w0eRbahZgURS8I=
github.com/maticnetwork/crand v1.0.2/go.mod h1:/NRNL3bj2eYdqpWmoIP5puxndTpi0XRxpj5ZKxfHjyg=
-github.com/maticnetwork/heimdall v0.3.1-0.20230227104835-81bd1055b0bc/go.mod h1:P2DoKhovYP9G9Kj2EH/zHaiRJF1jNU7ZJOyelG4UCa8=
-github.com/maticnetwork/heimdall v1.0.4 h1:xSsLn6JE/KqF3Fs9Wlx1s4dclJf2cduIodx2tL6K/cg=
github.com/maticnetwork/heimdall v1.0.4/go.mod h1:Xh7KFvtbs/SVNjOI8IgYmk6JdzYx89eU/XUwH0AgHLs=
+github.com/maticnetwork/heimdall v1.0.7 h1:QStn+hbZKxfE+PqecaorA/uATDPuQoi+U9Z7IIonb60=
+github.com/maticnetwork/heimdall v1.0.7/go.mod h1:+ANI5+VV28ahwfdl7oMzrcNwaTEs1Fn6z39BqBGcvaA=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53 h1:PjYV+lghs106JKkrYgOnrsfDLoTc11BxZd4rUa4Rus4=
github.com/maticnetwork/polyproto v0.0.3-0.20230216113155-340ea926ca53/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/maticnetwork/tendermint v0.33.0 h1:f+vORM02BoUOlCvnu3Zjw5rv6l6JSNVchWjH03rUuR8=
github.com/maticnetwork/tendermint v0.33.0/go.mod h1:D2fcnxGk6bje+LoPwImuKSSYLiK7/G06IynGNDSEcJk=
-github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@@ -1709,37 +1706,31 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
-github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
-github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
-github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@@ -1750,7 +1741,6 @@ github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLT
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
-github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
@@ -1759,8 +1749,9 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
+github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -1770,7 +1761,6 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
@@ -1786,34 +1776,32 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
-github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
+github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
+github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU=
+github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8=
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
-github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
-github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
-github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g=
github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
-github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
-github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nats-server/v2 v2.9.11/go.mod h1:b0oVuxSlkvS3ZjMkncFeACGyZohbO4XhSqW1Lt7iRRY=
github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
-github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
-github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
-github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nats.go v1.19.0/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
+github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
+github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
@@ -1823,49 +1811,68 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
-github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
-github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
-github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
+github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
+github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
+github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
+github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
+github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
+github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
+github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
+github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
+github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
+github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
+github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
+github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
+github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
+github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
+github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
+github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
+github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
+github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
+github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
+github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
+github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
+github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
+github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
+github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
-github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
-github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
-github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
-github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
-github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -1874,9 +1881,10 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
-github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
-github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
+github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
@@ -1886,12 +1894,14 @@ github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
+github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
+github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -1900,8 +1910,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
@@ -1909,43 +1921,39 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
+github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
+github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
-github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
@@ -1953,12 +1961,12 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
-github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY=
github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk=
github.com/protolambda/bls12-381-util v0.1.0/go.mod h1:cdkysJTRpeFeuUVx/TXGDQNMTiRAalk1vQw3TYTHcE4=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
@@ -1966,11 +1974,13 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -1978,12 +1988,13 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
-github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
-github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
+github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -1991,22 +2002,25 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
-github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
-github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
+github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
+github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
@@ -2026,28 +2040,30 @@ github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
-github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -2055,19 +2071,17 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
-github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
+github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
+github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
-github.com/status-im/keycard-go v0.0.0-20211109104530-b0e0482ba91d/go.mod h1:97vT0Rym0wCnK4B++hNA3nCetr0Mh1KXaVxzSt1arjg=
-github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/status-im/keycard-go v0.3.2 h1:YusIF/bHx6YZis8UTOJrpZFnTs4IkRBdmJXqdiXkpFE=
github.com/status-im/keycard-go v0.3.2/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
+github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
+github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -2075,6 +2089,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -2097,24 +2112,29 @@ github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYt
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
-github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/supranational/blst v0.3.12 h1:Vfas2U2CFHhniv2QkUm2OVa1+pGTdqtpqm9NnhUUbZ8=
+github.com/supranational/blst v0.3.12/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g=
github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk=
+github.com/tdewolff/minify/v2 v2.12.8/go.mod h1:YRgk7CC21LZnbuke2fmYnCTq+zhCgpb0yJACOTUNJ1E=
github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
+github.com/tdewolff/parse/v2 v2.6.7/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI=
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso=
-github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8=
@@ -2122,43 +2142,37 @@ github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJk
github.com/tendermint/tm-db v0.2.0 h1:rJxgdqn6fIiVJZy4zLpY1qVlyD0TU6vhkT4kEf71TQQ=
github.com/tendermint/tm-db v0.2.0/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw=
github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
-github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
-github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
-github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
-github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
+github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
+github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
+github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
-github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
-github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
+github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
-github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
-github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -2174,8 +2188,9 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
+github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
github.com/xsleonard/go-merkle v1.1.0 h1:fHe1fuhJjGH22ZzVTAH0jqHLhTGhOq3wQjJN+8P0jQg=
github.com/xsleonard/go-merkle v1.1.0/go.mod h1:cW4z+UZ/4f2n9IJgIiyDCdYguchoDyDAPmpuOWGxdGg=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
@@ -2192,25 +2207,27 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
+github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
-github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
+github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
-github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo=
github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c=
github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320=
+go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8=
+go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
-go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
+go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
@@ -2219,88 +2236,99 @@ go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsX
go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
+go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
-go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
-go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
+go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
+go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s=
+go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0/go.mod h1:14T5gr+Y6s2AgHPqBMgnGwp04csUjQmYXFWPeiBoq5s=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
+go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
+go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0/go.mod h1:9mLBBnPRf3sf+ASVH2p9xREXVBvwib02FxcKnavtExg=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
-go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
+go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
-go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U=
-go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
+go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
+go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
+go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
-go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0=
-go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
+go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
+go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
+go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg=
+go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
+go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
+go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.opentelemetry.io/proto/otlp v0.10.0/go.mod h1:zG20xCK0szZ1xdokeSOwEcmlXu+x9kkdRe6N1DhKcfU=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
-go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
+go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
-go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
+go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
+go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
+go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -2313,19 +2341,28 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2341,12 +2378,13 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
-golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
-golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
-golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
+golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
+golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2386,6 +2424,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -2393,10 +2432,12 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2406,7 +2447,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -2419,8 +2459,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -2442,10 +2480,10 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
@@ -2453,12 +2491,9 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -2488,9 +2523,16 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2499,6 +2541,8 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -2524,8 +2568,10 @@ golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
-golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
+golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
+golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2545,17 +2591,16 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2564,14 +2609,11 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2579,10 +2621,10 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2604,7 +2646,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2623,9 +2664,7 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2635,6 +2674,7 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2645,7 +2685,7 @@ golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2657,19 +2697,21 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2684,11 +2726,18 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
+golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2702,8 +2751,13 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
+golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2722,36 +2776,33 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -2764,8 +2815,7 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2775,7 +2825,6 @@ golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -2802,6 +2851,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -2814,16 +2864,22 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
+golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
-golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
-golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
+golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -2839,8 +2895,9 @@ gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
+gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ=
gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo=
-google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -2859,6 +2916,8 @@ google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSr
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.38.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
@@ -2905,16 +2964,15 @@ google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjY
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4=
google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
-google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
+google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY=
+google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -2924,7 +2982,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -2958,6 +3015,8 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -3051,6 +3110,7 @@ google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ
google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
+google.golang.org/genproto v0.0.0-20230227214838-9b19f0bdc514/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
@@ -3064,16 +3124,18 @@ google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFl
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
-google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
-google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
@@ -3081,19 +3143,17 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
+google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -3116,7 +3176,6 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
@@ -3135,8 +3194,9 @@ google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
-google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -3156,8 +3216,10 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -3166,24 +3228,18 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
-gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
-gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
-gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -3195,15 +3251,12 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -3264,7 +3317,8 @@ modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
-pgregory.net/rapid v0.4.8/go.mod h1:Z5PbWqjvWR1I3UGjvboUuan4fe4ZYEYNLNQLExzCoUs=
+moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
@@ -3273,6 +3327,4 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/log/format.go b/log/format.go
index 6447f3c1f1e9..bdaa3e00df89 100644
--- a/log/format.go
+++ b/log/format.go
@@ -237,6 +237,10 @@ func appendBigInt(dst []byte, n *big.Int) []byte {
return appendInt64(dst, n.Int64())
}
+ if len(n.String()) > 64*1024*1024 {
+ _ = fmt.Errorf("%s value in appendBigInt is too large to format", n.String())
+ }
+
var (
text = n.String()
buf = make([]byte, len(text)+len(text)/3)
From 9c2d462e21a1e94acdfb6165b547fc6819ec1fa3 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Tue, 18 Jun 2024 14:43:29 +0530
Subject: [PATCH 093/103] internal/cli: check for delete permissions before
starting to prune (#1269)
---
internal/cli/snapshot.go | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/internal/cli/snapshot.go b/internal/cli/snapshot.go
index a575210bb5de..9463c49da034 100644
--- a/internal/cli/snapshot.go
+++ b/internal/cli/snapshot.go
@@ -445,6 +445,28 @@ func (c *PruneBlockCommand) validateAgainstSnapshot(stack *node.Node, dbHandles
return nil
}
+// checkDeletePermissions checks if the user has the permission to
+// delete the given `path`.
+func checkDeletePermissions(path string) (bool, error) {
+ dirInfo, err := os.Stat(path)
+ if err != nil {
+ return false, err
+ }
+ // Check if the user has write and execute permissions on the directory
+ if dirInfo.Mode().Perm()&(0200|0100) == (0200 | 0100) {
+ // Also check if the parent directory has write permissions because delete needs them
+ parentDir := filepath.Dir(path)
+ parentDirInfo, err := os.Stat(parentDir)
+ if err != nil {
+ return false, err
+ }
+ if parentDirInfo.Mode().Perm()&0200 != 0 {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
// pruneBlock is the entry point for the ancient pruning process. Based on the user specified
// params, it will prune the ancient data. It also handles the case where the pruning process
// was interrupted earlier.
@@ -466,6 +488,17 @@ func (c *PruneBlockCommand) pruneBlock(stack *node.Node, fdHandles int) error {
}
newAncientPath := filepath.Join(path, "ancient_back")
+
+ // Check if we have delete permissions on the ancient datadir path beforehand
+ allow, err := checkDeletePermissions(oldAncientPath)
+ if err != nil {
+ log.Error("Failed to check delete permissions for ancient datadir", "path", oldAncientPath, "err", err)
+ return err
+ }
+ if !allow {
+ return fmt.Errorf("user doesn't have delete permissions on ancient datadir: %s", oldAncientPath)
+ }
+
blockpruner := pruner.NewBlockPruner(stack, oldAncientPath, newAncientPath, c.blockAmountReserved)
lock, exist, err := fileutil.Flock(filepath.Join(oldAncientPath, "PRUNEFLOCK"))
From 2fd85b37170ed08c4f1aa30c1e008d2e01abf1b9 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Tue, 18 Jun 2024 14:43:51 +0530
Subject: [PATCH 094/103] eth, consensus: refactor whitelisting related logs
and improve error handling (#1268)
* eth, consensus: refactor whitelisting related logs and error handling
* core: fix lint
* eth: fix tests, check against root error
* eth: use ctx correctly while logging
* eth: refactor comments and error message
---
consensus/bor/heimdall.go | 6 +-
consensus/bor/heimdallapp/milestone.go | 23 ++++---
consensus/bor/heimdallgrpc/milestone.go | 17 ++---
core/blockchain_test.go | 2 +
eth/backend.go | 18 ++---
eth/bor_api_backend.go | 10 +--
eth/bor_checkpoint_verifier.go | 39 ++++++-----
eth/downloader/whitelist/finality.go | 23 +++++--
eth/downloader/whitelist/service.go | 2 +
eth/handler_bor.go | 90 ++++++++++++-------------
eth/handler_bor_test.go | 4 +-
11 files changed, 123 insertions(+), 111 deletions(-)
diff --git a/consensus/bor/heimdall.go b/consensus/bor/heimdall.go
index a093cc95da73..5335e99bee18 100644
--- a/consensus/bor/heimdall.go
+++ b/consensus/bor/heimdall.go
@@ -17,8 +17,8 @@ type IHeimdallClient interface {
FetchCheckpointCount(ctx context.Context) (int64, error)
FetchMilestone(ctx context.Context) (*milestone.Milestone, error)
FetchMilestoneCount(ctx context.Context) (int64, error)
- FetchNoAckMilestone(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id failed in the Heimdall
- FetchLastNoAckMilestone(ctx context.Context) (string, error) //Fetch latest failed milestone id
- FetchMilestoneID(ctx context.Context, milestoneID string) error //Fetch the bool value whether milestone corresponding to the given id is in process in Heimdall
+ FetchNoAckMilestone(ctx context.Context, milestoneID string) error // Fetch the bool value whether milestone corresponding to the given id failed in the Heimdall
+ FetchLastNoAckMilestone(ctx context.Context) (string, error) // Fetch latest failed milestone id
+ FetchMilestoneID(ctx context.Context, milestoneID string) error // Fetch the bool value whether milestone corresponding to the given id is in process in Heimdall
Close()
}
diff --git a/consensus/bor/heimdallapp/milestone.go b/consensus/bor/heimdallapp/milestone.go
index b7183b649867..8d9d7a094363 100644
--- a/consensus/bor/heimdallapp/milestone.go
+++ b/consensus/bor/heimdallapp/milestone.go
@@ -14,30 +14,31 @@ import (
)
func (h *HeimdallAppClient) FetchMilestoneCount(_ context.Context) (int64, error) {
- log.Info("Fetching milestone count")
+ log.Debug("Fetching milestone count")
res := h.hApp.CheckpointKeeper.GetMilestoneCount(h.NewContext())
- log.Info("Fetched Milestone Count")
+ log.Debug("Fetched Milestone Count", "res", int64(res))
return int64(res), nil
}
func (h *HeimdallAppClient) FetchMilestone(_ context.Context) (*milestone.Milestone, error) {
- log.Info("Fetching Latest Milestone")
+ log.Debug("Fetching Latest Milestone")
res, err := h.hApp.CheckpointKeeper.GetLastMilestone(h.NewContext())
if err != nil {
return nil, err
}
- log.Info("Fetched Latest Milestone")
+ milestone := toBorMilestone(res)
+ log.Debug("Fetched Latest Milestone", "milestone", milestone)
- return toBorMilestone(res), nil
+ return milestone, nil
}
func (h *HeimdallAppClient) FetchNoAckMilestone(_ context.Context, milestoneID string) error {
- log.Info("Fetching No Ack Milestone By MilestoneID", "MilestoneID", milestoneID)
+ log.Debug("Fetching No Ack Milestone By MilestoneID", "MilestoneID", milestoneID)
res := h.hApp.CheckpointKeeper.GetNoAckMilestone(h.NewContext(), milestoneID)
if res {
@@ -45,21 +46,21 @@ func (h *HeimdallAppClient) FetchNoAckMilestone(_ context.Context, milestoneID s
return nil
}
- return fmt.Errorf("Still No Ack Milestone exist corresponding to MilestoneId:%v", milestoneID)
+ return fmt.Errorf("still no-ack milestone exist corresponding to milestoneID: %v", milestoneID)
}
func (h *HeimdallAppClient) FetchLastNoAckMilestone(_ context.Context) (string, error) {
- log.Info("Fetching Latest No Ack Milestone ID")
+ log.Debug("Fetching Latest No Ack Milestone ID")
res := h.hApp.CheckpointKeeper.GetLastNoAckMilestone(h.NewContext())
- log.Info("Fetched Latest No Ack Milestone ID")
+ log.Debug("Fetched Latest No Ack Milestone ID", "res", res)
return res, nil
}
func (h *HeimdallAppClient) FetchMilestoneID(_ context.Context, milestoneID string) error {
- log.Info("Fetching Milestone ID ", "MilestoneID", milestoneID)
+ log.Debug("Fetching Milestone ID ", "MilestoneID", milestoneID)
res := chTypes.GetMilestoneID()
@@ -67,7 +68,7 @@ func (h *HeimdallAppClient) FetchMilestoneID(_ context.Context, milestoneID stri
return nil
}
- return fmt.Errorf("Milestone corresponding to Milestone ID:%v doesn't exist in Heimdall", milestoneID)
+ return fmt.Errorf("milestone corresponding to milestoneID: %v doesn't exist in heimdall", milestoneID)
}
func toBorMilestone(hdMilestone *hmTypes.Milestone) *milestone.Milestone {
diff --git a/consensus/bor/heimdallgrpc/milestone.go b/consensus/bor/heimdallgrpc/milestone.go
index 917e33771af8..a156838548b8 100644
--- a/consensus/bor/heimdallgrpc/milestone.go
+++ b/consensus/bor/heimdallgrpc/milestone.go
@@ -5,6 +5,7 @@ import (
"fmt"
"math/big"
+ "github.com/ethereum/go-ethereum/consensus/bor/heimdall"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/milestone"
"github.com/ethereum/go-ethereum/log"
@@ -48,14 +49,14 @@ func (h *HeimdallGRPCClient) FetchMilestone(ctx context.Context) (*milestone.Mil
}
func (h *HeimdallGRPCClient) FetchLastNoAckMilestone(ctx context.Context) (string, error) {
- log.Info("Fetching latest no ack milestone Id")
+ log.Debug("Fetching latest no ack milestone Id")
res, err := h.client.FetchLastNoAckMilestone(ctx, nil)
if err != nil {
return "", err
}
- log.Info("Fetched last no-ack milestone")
+ log.Debug("Fetched last no-ack milestone", "res", res.Result.Result)
return res.Result.Result, nil
}
@@ -65,7 +66,7 @@ func (h *HeimdallGRPCClient) FetchNoAckMilestone(ctx context.Context, milestoneI
MilestoneID: milestoneID,
}
- log.Info("Fetching no ack milestone", "milestoneaID", milestoneID)
+ log.Debug("Fetching no ack milestone", "milestoneID", milestoneID)
res, err := h.client.FetchNoAckMilestone(ctx, req)
if err != nil {
@@ -73,10 +74,10 @@ func (h *HeimdallGRPCClient) FetchNoAckMilestone(ctx context.Context, milestoneI
}
if !res.Result.Result {
- return fmt.Errorf("Not in rejected list: milestoneID %q", milestoneID)
+ return fmt.Errorf("%w: milestoneID %q", heimdall.ErrNotInRejectedList, milestoneID)
}
- log.Info("Fetched no ack milestone", "milestoneaID", milestoneID)
+ log.Debug("Fetched no ack milestone", "milestoneID", milestoneID)
return nil
}
@@ -86,7 +87,7 @@ func (h *HeimdallGRPCClient) FetchMilestoneID(ctx context.Context, milestoneID s
MilestoneID: milestoneID,
}
- log.Info("Fetching milestone id", "milestoneID", milestoneID)
+ log.Debug("Fetching milestone id", "milestoneID", milestoneID)
res, err := h.client.FetchMilestoneID(ctx, req)
if err != nil {
@@ -94,10 +95,10 @@ func (h *HeimdallGRPCClient) FetchMilestoneID(ctx context.Context, milestoneID s
}
if !res.Result.Result {
- return fmt.Errorf("This milestoneID %q does not exist", milestoneID)
+ return fmt.Errorf("%w: milestoneID %q", heimdall.ErrNotInMilestoneList, milestoneID)
}
- log.Info("Fetched milestone id", "milestoneID", milestoneID)
+ log.Debug("Fetched milestone id", "milestoneID", milestoneID)
return nil
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 519c31fd288e..dd80594ab120 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -4500,10 +4500,12 @@ func TestTxIndexer(t *testing.T) {
}
verify := func(db ethdb.Database, expTail uint64) {
tail := rawdb.ReadTxIndexTail(db)
+ //nolint:staticcheck
if tail == nil {
t.Fatal("Failed to write tx index tail")
}
+ //nolint:staticcheck
if *tail != expTail {
t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail)
}
diff --git a/eth/backend.go b/eth/backend.go
index 627e39d1f020..8a6a705e7bb1 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -695,16 +695,12 @@ func retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, time
return
}
- // first run for fetching milestones
+ // first run
firstCtx, cancel := context.WithTimeout(context.Background(), timeout)
- err = fn(firstCtx, ethHandler, bor)
+ _ = fn(firstCtx, ethHandler, bor)
cancel()
- if err != nil {
- log.Warn(fmt.Sprintf("unable to start the %s service - first run", fnName), "err", err)
- }
-
ticker := time.NewTicker(tickerDuration)
defer ticker.Stop()
@@ -712,13 +708,11 @@ func retryHeimdallHandler(fn heimdallHandler, tickerDuration time.Duration, time
select {
case <-ticker.C:
ctx, cancel := context.WithTimeout(context.Background(), timeout)
- err := fn(ctx, ethHandler, bor)
- cancel()
+ // Skip any error reporting here as it's handled in respective functions
+ _ = fn(ctx, ethHandler, bor)
- if err != nil {
- log.Warn(fmt.Sprintf("unable to handle %s", fnName), "err", err)
- }
+ cancel()
case <-closeCh:
return
}
@@ -754,7 +748,7 @@ func (s *Ethereum) handleMilestone(ctx context.Context, ethHandler *ethHandler,
// If the current chain head is behind the received milestone, add it to the future milestone
// list. Also, the hash mismatch (end block hash) error will lead to rewind so also
// add that milestone to the future milestone list.
- if errors.Is(err, errMissingBlocks) || errors.Is(err, errHashMismatch) {
+ if errors.Is(err, errChainOutOfSync) || errors.Is(err, errHashMismatch) {
ethHandler.downloader.ProcessFutureMilestone(num, hash)
}
diff --git a/eth/bor_api_backend.go b/eth/bor_api_backend.go
index 9f909caa8eb7..93e93bb1371d 100644
--- a/eth/bor_api_backend.go
+++ b/eth/bor_api_backend.go
@@ -53,16 +53,16 @@ func (b *EthAPIBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, e
return false, errBorEngineNotAvailable
}
- //Confirmation of 16 blocks on the endblock
+ // Confirmation of 16 blocks on the endblock
tipConfirmationBlockNr := endBlockNr + uint64(16)
- //Check if tipConfirmation block exit
+ // Check if tipConfirmation block exit
_, err := b.BlockByNumber(ctx, rpc.BlockNumber(tipConfirmationBlockNr))
if err != nil {
return false, errTipConfirmationBlock
}
- //Check if end block exist
+ // Check if end block exist
localEndBlock, err := b.BlockByNumber(ctx, rpc.BlockNumber(endBlockNr))
if err != nil {
return false, errEndBlock
@@ -75,12 +75,12 @@ func (b *EthAPIBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, e
if !isLocked {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
- return false, errors.New("Whitelisted number or locked sprint number is more than the received end block number")
+ return false, errors.New("whitelisted number or locked sprint number is more than the received end block number")
}
if localEndBlockHash != hash {
downloader.UnlockMutex(false, "", endBlockNr, common.Hash{})
- return false, fmt.Errorf("Hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
+ return false, fmt.Errorf("hash mismatch: localChainHash %s, milestoneHash %s", localEndBlockHash, hash)
}
downloader.UnlockMutex(true, milestoneId, endBlockNr, localEndBlock.Hash())
diff --git a/eth/bor_checkpoint_verifier.go b/eth/bor_checkpoint_verifier.go
index b2fd2c2d2193..4d7a054374ab 100644
--- a/eth/bor_checkpoint_verifier.go
+++ b/eth/bor_checkpoint_verifier.go
@@ -12,12 +12,16 @@ import (
)
var (
- // errMissingBlocks is returned when we don't have the blocks locally, yet.
- errMissingBlocks = errors.New("missing blocks")
+ // errMissingCurrentBlock is returned when we don't have the current block
+ // present locally.
+ errMissingCurrentBlock = errors.New("current block missing")
- // errRootHash is returned when we aren't able to calculate the root hash
- // locally for a range of blocks.
- errRootHash = errors.New("failed to get local root hash")
+ // errChainOutOfSync is returned when we're trying to process a future
+ // checkpoint/milestone and we haven't reached at that number yet.
+ errChainOutOfSync = errors.New("chain out of sync")
+
+ // errRootHash is returned when the root hash calculation for a range of blocks fails.
+ errRootHash = errors.New("root hash calculation failed")
// errHashMismatch is returned when the local hash doesn't match
// with the hash of checkpoint/milestone. It is the root hash of blocks
@@ -27,13 +31,10 @@ var (
// errEndBlock is returned when we're unable to fetch a block locally.
errEndBlock = errors.New("failed to get end block")
- // errEndBlock is returned when we're unable to fetch a block locally.
+ // errEndBlock is returned when we're unable to fetch the tip confirmation block locally.
errTipConfirmationBlock = errors.New("failed to get tip confirmation block")
- // errBlockNumberConversion is returned when we get err in parsing hexautil block number
- errBlockNumberConversion = errors.New("failed to parse the block number")
-
- //Metrics for collecting the rewindLength
+ // rewindLengthMeter for collecting info about the length of chain rewinded
rewindLengthMeter = metrics.NewRegisteredMeter("chain/autorewind/length", nil)
)
@@ -57,14 +58,14 @@ func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start ui
currentBlock := eth.BlockChain().CurrentBlock()
if currentBlock == nil {
log.Debug(fmt.Sprintf("Failed to fetch current block from blockchain while verifying incoming %s", str))
- return hash, errMissingBlocks
+ return hash, errMissingCurrentBlock
}
head := currentBlock.Number.Uint64()
if head < end {
log.Debug(fmt.Sprintf("Current head block behind incoming %s block", str), "head", head, "end block", end)
- return hash, errMissingBlocks
+ return hash, errChainOutOfSync
}
var localHash string
@@ -77,15 +78,15 @@ func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start ui
localHash, err = handler.ethAPI.GetRootHash(ctx, start, end)
if err != nil {
- log.Debug("Failed to get root hash of given block range while whitelisting checkpoint", "start", start, "end", end, "err", err)
- return hash, errRootHash
+ log.Debug("Failed to calculate root hash of given block range while whitelisting checkpoint", "start", start, "end", end, "err", err)
+ return hash, fmt.Errorf("%w: %v", errRootHash, err)
}
} else {
// in case of milestone(isCheckpoint==false) get the hash of endBlock
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting milestone", "number", end, "err", err)
- return hash, errEndBlock
+ return hash, fmt.Errorf("%w: %v", errEndBlock, err)
}
localHash = fmt.Sprintf("%v", block["hash"])[2:]
@@ -93,7 +94,6 @@ func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start ui
//nolint
if localHash != hash {
-
if isCheckpoint {
log.Warn("Root hash mismatch while whitelisting checkpoint", "expected", localHash, "got", hash)
} else {
@@ -124,9 +124,9 @@ func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start ui
}
if isCheckpoint {
- log.Warn("Rewinding chain due to checkpoint root hash mismatch", "number", rewindTo)
+ log.Info("Rewinding chain due to checkpoint root hash mismatch", "number", rewindTo)
} else {
- log.Warn("Rewinding chain due to milestone endblock hash mismatch", "number", rewindTo)
+ log.Info("Rewinding chain due to milestone endblock hash mismatch", "number", rewindTo)
}
rewindBack(eth, head, rewindTo)
@@ -138,7 +138,7 @@ func borVerify(ctx context.Context, eth *Ethereum, handler *ethHandler, start ui
block, err := handler.ethAPI.GetBlockByNumber(ctx, rpc.BlockNumber(end), false)
if err != nil {
log.Debug("Failed to get end block hash while whitelisting", "err", err)
- return hash, errEndBlock
+ return hash, fmt.Errorf("%w: %v", errEndBlock, err)
}
hash = fmt.Sprintf("%v", block["hash"])
@@ -170,5 +170,4 @@ func rewind(eth *Ethereum, head uint64, rewindTo uint64) {
} else {
rewindLengthMeter.Mark(int64(head - rewindTo))
}
-
}
diff --git a/eth/downloader/whitelist/finality.go b/eth/downloader/whitelist/finality.go
index 7ab684d88a2b..6d8f98132183 100644
--- a/eth/downloader/whitelist/finality.go
+++ b/eth/downloader/whitelist/finality.go
@@ -18,6 +18,7 @@ type finality[T rawdb.BlockFinality[T]] struct {
Number uint64 // Number , populated by reaching out to heimdall
interval uint64 // Interval, until which we can allow importing
doExist bool
+ name string // Name of the service (checkpoint or milestone)
}
type finalityService interface {
@@ -43,21 +44,33 @@ func (f *finality[T]) IsValidPeer(fetchHeadersByNumber func(number uint64, amoun
return isValidPeer(fetchHeadersByNumber, doExist, number, hash)
}
-// IsValidChain checks the validity of chain by comparing it
-// against the local checkpoint entry
-// todo: need changes
+// IsValidChain checks the validity of chain by comparing it against the local
+// whitelisted entry of checkpoint/milestone.
func (f *finality[T]) IsValidChain(currentHeader *types.Header, chain []*types.Header) (bool, error) {
// Return if we've received empty chain
if len(chain) == 0 {
return false, nil
}
- res, err := isValidChain(currentHeader, chain, f.doExist, f.Number, f.Hash)
+ return isValidChain(currentHeader, chain, f.doExist, f.Number, f.Hash)
+}
- return res, err
+// reportWhitelist logs the block number and hash if a new and unique entry is being inserted
+// and it doesn't log for duplicate/redundant entries.
+func (f *finality[T]) reportWhitelist(block uint64, hash common.Hash) {
+ msg := fmt.Sprintf("Whitelisting new %s from heimdall", f.name)
+ if !f.doExist {
+ log.Info(msg, "block", block, "hash", hash)
+ } else {
+ if f.Number != block && f.Hash != hash {
+ log.Info(msg, "block", block, "hash", hash)
+ }
+ }
}
func (f *finality[T]) Process(block uint64, hash common.Hash) {
+ f.reportWhitelist(block, hash)
+
f.doExist = true
f.Hash = hash
f.Number = block
diff --git a/eth/downloader/whitelist/service.go b/eth/downloader/whitelist/service.go
index 3c23e4a2683a..7e7e1985da05 100644
--- a/eth/downloader/whitelist/service.go
+++ b/eth/downloader/whitelist/service.go
@@ -60,6 +60,7 @@ func NewService(db ethdb.Database) *Service {
Hash: checkpointHash,
interval: 256,
db: db,
+ name: "checkpoint",
},
},
@@ -70,6 +71,7 @@ func NewService(db ethdb.Database) *Service {
Hash: milestoneHash,
interval: 256,
db: db,
+ name: "milestone",
},
Locked: locked,
diff --git a/eth/handler_bor.go b/eth/handler_bor.go
index 9fbc096d3d1f..be06165b1118 100644
--- a/eth/handler_bor.go
+++ b/eth/handler_bor.go
@@ -3,6 +3,8 @@ package eth
import (
"context"
"errors"
+ "fmt"
+ "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor"
@@ -18,8 +20,6 @@ var (
// errMilestone is returned when we are unable to fetch the
// latest milestone from the local heimdall.
errMilestone = errors.New("failed to fetch latest milestone")
-
- ErrNotInRejectedList = errors.New("MilestoneID not in rejected list")
)
// fetchWhitelistCheckpoint fetches the latest checkpoint from it's local heimdall
@@ -32,19 +32,23 @@ func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor,
// fetch the latest checkpoint from Heimdall
checkpoint, err := bor.HeimdallClient.FetchCheckpoint(ctx, -1)
+ err = reportCommonErrors("latest checkpoint", err, errCheckpoint)
if err != nil {
- log.Debug("Failed to fetch latest checkpoint for whitelisting", "err", err)
- return blockNum, blockHash, errCheckpoint
+ return blockNum, blockHash, err
}
- log.Info("Got new checkpoint from heimdall", "start", checkpoint.StartBlock.Uint64(), "end", checkpoint.EndBlock.Uint64(), "rootHash", checkpoint.RootHash.String())
+ log.Debug("Got new checkpoint from heimdall", "start", checkpoint.StartBlock.Uint64(), "end", checkpoint.EndBlock.Uint64(), "rootHash", checkpoint.RootHash.String())
// Verify if the checkpoint fetched can be added to the local whitelist entry or not
// If verified, it returns the hash of the end block of the checkpoint. If not,
// it will return appropriate error.
hash, err := verifier.verify(ctx, eth, h, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64(), checkpoint.RootHash.String()[2:], true)
if err != nil {
- log.Warn("Failed to whitelist checkpoint", "err", err)
+ if errors.Is(err, errChainOutOfSync) {
+ log.Info("Whitelisting checkpoint deferred", "err", err)
+ } else {
+ log.Warn("Failed to whitelist checkpoint", "err", err)
+ }
return blockNum, blockHash, err
}
@@ -64,72 +68,68 @@ func (h *ethHandler) fetchWhitelistMilestone(ctx context.Context, bor *bor.Bor,
// fetch latest milestone
milestone, err := bor.HeimdallClient.FetchMilestone(ctx)
- if errors.Is(err, heimdall.ErrServiceUnavailable) {
- log.Debug("Failed to fetch latest milestone for whitelisting", "err", err)
- return num, hash, err
- }
-
+ err = reportCommonErrors("latest milestone", err, errMilestone)
if err != nil {
- log.Error("Failed to fetch latest milestone for whitelisting", "err", err)
- return num, hash, errMilestone
+ return num, hash, err
}
num = milestone.EndBlock.Uint64()
hash = milestone.Hash
- log.Info("Got new milestone from heimdall", "start", milestone.StartBlock.Uint64(), "end", milestone.EndBlock.Uint64(), "hash", milestone.Hash.String())
+ log.Debug("Got new milestone from heimdall", "start", milestone.StartBlock.Uint64(), "end", milestone.EndBlock.Uint64(), "hash", milestone.Hash.String())
- // Verify if the milestone fetched can be added to the local whitelist entry or not
- // If verified, it returns the hash of the end block of the milestone. If not,
- // it will return appropriate error.
+ // Verify if the milestone fetched can be added to the local whitelist entry or not. If verified,
+ // the hash of the end block of the milestone is returned else appropriate error is returned.
_, err = verifier.verify(ctx, eth, h, milestone.StartBlock.Uint64(), milestone.EndBlock.Uint64(), milestone.Hash.String()[2:], false)
if err != nil {
+ if errors.Is(err, errChainOutOfSync) {
+ log.Info("Whitelisting milestone deferred", "err", err)
+ } else {
+ log.Warn("Failed to whitelist milestone", "err", err)
+ }
h.downloader.UnlockSprint(milestone.EndBlock.Uint64())
- return num, hash, err
}
- return num, hash, nil
+ return num, hash, err
}
func (h *ethHandler) fetchNoAckMilestone(ctx context.Context, bor *bor.Bor) (string, error) {
- var (
- milestoneID string
- )
-
- // fetch latest milestone
milestoneID, err := bor.HeimdallClient.FetchLastNoAckMilestone(ctx)
- if errors.Is(err, heimdall.ErrServiceUnavailable) {
- log.Debug("Failed to fetch latest no-ack milestone", "err", err)
- return milestoneID, err
- }
-
- if err != nil {
- log.Error("Failed to fetch latest no-ack milestone", "err", err)
- return milestoneID, errMilestone
- }
+ err = reportCommonErrors("latest no-ack milestone", err, nil)
- return milestoneID, nil
+ return milestoneID, err
}
func (h *ethHandler) fetchNoAckMilestoneByID(ctx context.Context, bor *bor.Bor, milestoneID string) error {
- // fetch latest milestone
err := bor.HeimdallClient.FetchNoAckMilestone(ctx, milestoneID)
- if errors.Is(err, heimdall.ErrServiceUnavailable) {
- log.Debug("Failed to fetch no-ack milestone by ID", "milestoneID", milestoneID, "err", err)
- return err
+ if errors.Is(err, heimdall.ErrNotInRejectedList) {
+ log.Debug("MilestoneID not in rejected list", "milestoneID", milestoneID)
}
+ err = reportCommonErrors("no-ack milestone by ID", err, nil, "milestoneID", milestoneID)
+ return err
+}
- // fixme: handle different types of errors
- if errors.Is(err, ErrNotInRejectedList) {
- log.Warn("MilestoneID not in rejected list", "milestoneID", milestoneID, "err", err)
+// reportCommonErrors reports common errors which can occur while fetching data from heimdall. It also
+// returns back the wrapped erorr if required to the caller.
+func reportCommonErrors(msg string, err error, wrapError error, ctx ...interface{}) error {
+ if err == nil {
return err
}
- if err != nil {
- log.Error("Failed to fetch no-ack milestone by ID ", "milestoneID", milestoneID, "err", err)
+ // We're skipping extra check to the `heimdall.ErrServiceUnavailable` error as it should not
+ // occur post HF (in heimdall). If it does, we'll anyways warn below as a normal error.
+
+ ctx = append(ctx, "err", err)
+
+ if strings.Contains(err.Error(), "context deadline exceeded") {
+ log.Warn(fmt.Sprintf("Failed to fetch %s, please check the heimdall endpoint and status of your heimdall node", msg), ctx...)
+ } else {
+ log.Warn(fmt.Sprintf("Failed to fetch %s", msg), ctx...)
+ }
- return errMilestone
+ if wrapError != nil {
+ return fmt.Errorf("%w: %v", wrapError, err)
}
- return nil
+ return err
}
diff --git a/eth/handler_bor_test.go b/eth/handler_bor_test.go
index 7ceab1fc648f..b062c9cdc76e 100644
--- a/eth/handler_bor_test.go
+++ b/eth/handler_bor_test.go
@@ -103,7 +103,7 @@ func fetchCheckpointTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, han
ctx := context.Background()
_, _, err := handler.fetchWhitelistCheckpoint(ctx, bor, nil, verifier)
- require.Equal(t, err, errCheckpoint)
+ require.ErrorIs(t, err, errCheckpoint)
// create 4 mock checkpoints
checkpoints = createMockCheckpoints(4)
@@ -133,7 +133,7 @@ func fetchMilestoneTest(t *testing.T, heimdall *mockHeimdall, bor *bor.Bor, hand
ctx := context.Background()
_, _, err := handler.fetchWhitelistMilestone(ctx, bor, nil, verifier)
- require.Equal(t, err, errMilestone)
+ require.ErrorIs(t, err, errMilestone)
// create 4 mock checkpoints
milestones = createMockMilestones(4)
From c551b4198e742a267e700b2e25788644fc505e67 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Wed, 19 Jun 2024 10:58:22 +0530
Subject: [PATCH 095/103] bump version to v1.3.4-beta
---
packaging/templates/package_scripts/control | 2 +-
packaging/templates/package_scripts/control.arm64 | 2 +-
packaging/templates/package_scripts/control.profile.amd64 | 2 +-
packaging/templates/package_scripts/control.profile.arm64 | 2 +-
packaging/templates/package_scripts/control.validator | 2 +-
.../templates/package_scripts/control.validator.arm64 | 2 +-
params/version.go | 8 ++++----
7 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/packaging/templates/package_scripts/control b/packaging/templates/package_scripts/control
index b95ef8aa6d09..d68714b66e24 100644
--- a/packaging/templates/package_scripts/control
+++ b/packaging/templates/package_scripts/control
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.arm64 b/packaging/templates/package_scripts/control.arm64
index f8a37a1c29d9..517c54068f62 100644
--- a/packaging/templates/package_scripts/control.arm64
+++ b/packaging/templates/package_scripts/control.arm64
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.amd64 b/packaging/templates/package_scripts/control.profile.amd64
index d9e7edfd9043..e0942c5ec33c 100644
--- a/packaging/templates/package_scripts/control.profile.amd64
+++ b/packaging/templates/package_scripts/control.profile.amd64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.arm64 b/packaging/templates/package_scripts/control.profile.arm64
index cf6823c4e254..d918e1659c84 100644
--- a/packaging/templates/package_scripts/control.profile.arm64
+++ b/packaging/templates/package_scripts/control.profile.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator b/packaging/templates/package_scripts/control.validator
index 6da01046a388..efe92eafc077 100644
--- a/packaging/templates/package_scripts/control.validator
+++ b/packaging/templates/package_scripts/control.validator
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator.arm64 b/packaging/templates/package_scripts/control.validator.arm64
index e7679e5e46b5..be481029fba5 100644
--- a/packaging/templates/package_scripts/control.validator.arm64
+++ b/packaging/templates/package_scripts/control.validator.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.3
+Version: 1.3.4-beta
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/params/version.go b/params/version.go
index 7b836091d3de..f0412885af62 100644
--- a/params/version.go
+++ b/params/version.go
@@ -21,10 +21,10 @@ import (
)
const (
- VersionMajor = 1 // Major version component of the current release
- VersionMinor = 3 // Minor version component of the current release
- VersionPatch = 3 // Patch version component of the current release
- VersionMeta = "" // Version metadata to append to the version string
+ VersionMajor = 1 // Major version component of the current release
+ VersionMinor = 3 // Minor version component of the current release
+ VersionPatch = 4 // Patch version component of the current release
+ VersionMeta = "beta" // Version metadata to append to the version string
)
var GitCommit string
From 0bc2dcbd7180d13795f3d207a3aa2033f34a5792 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Thu, 20 Jun 2024 17:19:12 +0530
Subject: [PATCH 096/103] cmd/utils: use defined filter api instance instead of
a new one (#1270)
---
cmd/utils/flags.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index f65940f29709..1fad54804f20 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -2019,7 +2019,7 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
filterAPI := filters.NewFilterAPI(filterSystem, false, ethcfg.BorLogs)
stack.RegisterAPIs([]rpc.API{{
Namespace: "eth",
- Service: filters.NewFilterAPI(filterSystem, false, false),
+ Service: filterAPI,
}})
// avoiding constructor changed by introducing new method to set genesis
From b228a474919c498c150afb06f1e9af899a29f3c3 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Fri, 21 Jun 2024 21:01:59 +0530
Subject: [PATCH 097/103] bump version to v1.3.4-beta2
---
packaging/templates/package_scripts/control | 2 +-
packaging/templates/package_scripts/control.arm64 | 2 +-
packaging/templates/package_scripts/control.profile.amd64 | 2 +-
packaging/templates/package_scripts/control.profile.arm64 | 2 +-
packaging/templates/package_scripts/control.validator | 2 +-
.../templates/package_scripts/control.validator.arm64 | 2 +-
params/version.go | 8 ++++----
7 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/packaging/templates/package_scripts/control b/packaging/templates/package_scripts/control
index d68714b66e24..ae34f09c733e 100644
--- a/packaging/templates/package_scripts/control
+++ b/packaging/templates/package_scripts/control
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.arm64 b/packaging/templates/package_scripts/control.arm64
index 517c54068f62..f7b46f0de060 100644
--- a/packaging/templates/package_scripts/control.arm64
+++ b/packaging/templates/package_scripts/control.arm64
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.amd64 b/packaging/templates/package_scripts/control.profile.amd64
index e0942c5ec33c..6db054dfabbf 100644
--- a/packaging/templates/package_scripts/control.profile.amd64
+++ b/packaging/templates/package_scripts/control.profile.amd64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.arm64 b/packaging/templates/package_scripts/control.profile.arm64
index d918e1659c84..b005edd293aa 100644
--- a/packaging/templates/package_scripts/control.profile.arm64
+++ b/packaging/templates/package_scripts/control.profile.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator b/packaging/templates/package_scripts/control.validator
index efe92eafc077..01ca94d29300 100644
--- a/packaging/templates/package_scripts/control.validator
+++ b/packaging/templates/package_scripts/control.validator
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator.arm64 b/packaging/templates/package_scripts/control.validator.arm64
index be481029fba5..181811732ea0 100644
--- a/packaging/templates/package_scripts/control.validator.arm64
+++ b/packaging/templates/package_scripts/control.validator.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta
+Version: 1.3.4-beta2
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/params/version.go b/params/version.go
index f0412885af62..4e455c7a26b0 100644
--- a/params/version.go
+++ b/params/version.go
@@ -21,10 +21,10 @@ import (
)
const (
- VersionMajor = 1 // Major version component of the current release
- VersionMinor = 3 // Minor version component of the current release
- VersionPatch = 4 // Patch version component of the current release
- VersionMeta = "beta" // Version metadata to append to the version string
+ VersionMajor = 1 // Major version component of the current release
+ VersionMinor = 3 // Minor version component of the current release
+ VersionPatch = 4 // Patch version component of the current release
+ VersionMeta = "beta2" // Version metadata to append to the version string
)
var GitCommit string
From 32c9405afac3cef120b2404f585777f043c66a11 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Mon, 24 Jun 2024 17:33:17 +0530
Subject: [PATCH 098/103] core/rawdb: log ancient pruner offset
---
core/rawdb/database.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
index 3762a4366f45..fd03c6b35782 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -273,6 +273,7 @@ func resolveOffset(db ethdb.KeyValueStore, isLastOffset bool) uint64 {
//nolint:gocognit
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset bool) (ethdb.Database, error) {
offset := resolveOffset(db, isLastOffset)
+ log.Info("Resolving ancient pruner offset", "isLastOffset", isLastOffset, "offset", offset)
// Create the idle freezer instance
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, offset)
From 1a05223076cf11f95e4f65dd4a5da8c342acb410 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Thu, 11 Jul 2024 11:05:12 +0530
Subject: [PATCH 099/103] PIP-35 for amoy: enforce gas related configs (#1285)
* enforce gas related configs for amoy
* internal/cli/server: revert changes to default config.toml for test
* eth: check nil genesis for tests
---
builder/files/config.toml | 6 ++---
core/txpool/legacypool/legacypool.go | 23 +++++++++++-----
core/txpool/legacypool/legacypool_test.go | 5 +++-
docs/cli/example_config.toml | 6 ++---
docs/cli/server.md | 6 ++---
eth/backend.go | 27 ++++++++++++++++---
eth/catalyst/api_test.go | 4 +--
eth/gasprice/gasprice.go | 23 +++++++++++-----
internal/cli/server/config.go | 6 ++---
internal/cli/server/config_test.go | 3 +++
internal/cli/server/testdata/default.toml | 6 ++---
miner/miner.go | 7 +++--
miner/worker_test.go | 2 +-
.../testnet-amoy/archive/config.toml | 6 ++---
.../sentry/sentry/bor/config.toml | 6 ++---
.../sentry/validator/bor/config.toml | 6 ++---
.../without-sentry/bor/config.toml | 6 ++---
params/protocol_params.go | 6 ++---
18 files changed, 100 insertions(+), 54 deletions(-)
diff --git a/builder/files/config.toml b/builder/files/config.toml
index 41281bc7e066..7fa047572c5a 100644
--- a/builder/files/config.toml
+++ b/builder/files/config.toml
@@ -56,7 +56,7 @@ syncmode = "full"
[txpool]
nolocals = true
- pricelimit = 30000000000
+ pricelimit = 30000000000 # 25000000000 for amoy
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -69,7 +69,7 @@ syncmode = "full"
[miner]
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "30000000000" # 25000000000 for amoy
# mine = true
# etherbase = "VALIDATOR ADDRESS"
# extradata = ""
@@ -127,7 +127,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "30000000000" # 25000000000 for amoy
[telemetry]
metrics = true
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index 1ffeb54ab182..d6f39f8f914b 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -147,7 +147,9 @@ var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
- PriceLimit: params.BorDefaultTxPoolPriceLimit,
+ // (PIP-35): Update price limit to `params.BorDefaultTxPoolPriceLimit` once
+ // the change is applied over all networks.
+ PriceLimit: 1,
PriceBump: 10,
AccountSlots: 16,
@@ -161,16 +163,23 @@ var DefaultConfig = Config{
// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
-func (config *Config) sanitize() Config {
+func (config *Config) sanitize(chainId *big.Int) Config {
conf := *config
if conf.Rejournal < time.Second {
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
conf.Rejournal = time.Second
}
- // enforce txpool price limit to 30gwei in bor
- if conf.PriceLimit != params.BorDefaultTxPoolPriceLimit {
- log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
- conf.PriceLimit = DefaultConfig.PriceLimit
+ // (PIP-35): Only enforce the 25gwei txpool price limit for amoy
+ if chainId.Cmp(params.AmoyChainConfig.ChainID) == 0 {
+ if conf.PriceLimit != params.BorDefaultTxPoolPriceLimit {
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", params.BorDefaultTxPoolPriceLimit)
+ conf.PriceLimit = params.BorDefaultTxPoolPriceLimit
+ }
+ } else {
+ if conf.PriceLimit < 1 {
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
+ conf.PriceLimit = DefaultConfig.PriceLimit
+ }
}
if conf.PriceBump < 1 {
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
@@ -250,7 +259,7 @@ type txpoolResetRequest struct {
// transactions from the network.
func New(config Config, chain BlockChain, options ...func(pool *LegacyPool)) *LegacyPool {
// Sanitize the input to ensure no vulnerable gas prices are set
- config = (&config).sanitize()
+ config = (&config).sanitize(chain.Config().ChainID)
// Create the transaction pool with its initial settings
pool := &LegacyPool{
diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go
index a5fd8de6ab9a..d61d1f8f078f 100644
--- a/core/txpool/legacypool/legacypool_test.go
+++ b/core/txpool/legacypool/legacypool_test.go
@@ -62,7 +62,7 @@ func init() {
testTxPoolConfig = DefaultConfig
testTxPoolConfig.Journal = ""
/*
- Given the introduction of `BorDefaultTxPoolPriceLimit=30gwei`,
+ Given the introduction of `BorDefaultTxPoolPriceLimit=25gwei`,
we set `testTxPoolConfig.PriceLimit = 1` to avoid rewriting all `legacypool_test.go` tests,
causing code divergence from geth, as this has been widely tested on different networks.
Also, `worker_test.go` has been adapted to reflect such changes.
@@ -296,6 +296,9 @@ func (c *testChain) State() (*state.StateDB, error) {
// TestTxPoolDefaultPriceLimit ensures the bor default tx pool price limit is set correctly.
func TestTxPoolDefaultPriceLimit(t *testing.T) {
+ // (PIP-35): Only applicable to amoy
+ t.Skip("Skipped because the price enforcement is only applied to amoy")
+
t.Parallel()
pool, _ := setupPool()
diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml
index 21705db05571..4137015ee97c 100644
--- a/docs/cli/example_config.toml
+++ b/docs/cli/example_config.toml
@@ -61,7 +61,7 @@ devfakeauthor = false # Run miner without validator set authorization
nolocals = false # Disables price exemptions for locally submitted transactions
journal = "transactions.rlp" # Disk journal for local transaction to survive node restarts
rejournal = "1h0m0s" # Time interval to regenerate the local transaction journal
- pricelimit = 30000000000 # Minimum gas price limit to enforce for acceptance into the pool. Regardless the value set, it will be enforced to 30000000000 in bor.
+ pricelimit = 30000000000 # Minimum gas price limit to enforce for acceptance into the pool. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
pricebump = 10 # Price bump percentage to replace an already existing transaction
accountslots = 16 # Minimum number of executable transaction slots guaranteed per account
globalslots = 32768 # Maximum number of executable transaction slots for all accounts
@@ -74,7 +74,7 @@ devfakeauthor = false # Run miner without validator set authorization
etherbase = "" # Public address for block mining rewards
extradata = "" # Block extra data set by the miner (default = client version)
gaslimit = 30000000 # Target gas ceiling for mined blocks
- gasprice = "30000000000" # Minimum gas price for mining a transaction. Regardless the value set, it will be enforced to 30000000000 in bor, default suitable for amoy/mumbai/devnet.
+ gasprice = "30000000000" # Minimum gas price for mining a transaction. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
recommit = "2m5s" # The time interval for miner to re-create mining work
commitinterrupt = true # Interrupt the current mining work when time is exceeded and create partial blocks
@@ -128,7 +128,7 @@ devfakeauthor = false # Run miner without validator set authorization
maxheaderhistory = 1024 # Maximum header history of gasprice oracle
maxblockhistory = 1024 # Maximum block history of gasprice oracle
maxprice = "5000000000000" # Maximum gas price will be recommended by gpo
- ignoreprice = "2" # Gas price below which gpo will ignore transactions (recommended for mainnet = 30000000000, default suitable for amoy/mumbai/devnet)
+ ignoreprice = "2" # Gas price below which gpo will ignore transactions. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
[telemetry]
metrics = false # Enable metrics collection and reporting
diff --git a/docs/cli/server.md b/docs/cli/server.md
index fc04f12022c1..fbd4adad3fb2 100644
--- a/docs/cli/server.md
+++ b/docs/cli/server.md
@@ -46,7 +46,7 @@ The ```bor server``` command runs the Bor client.
- ```gpo.blocks```: Number of recent blocks to check for gas prices (default: 20)
-- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 30000000000). It's set to 30gwei in bor
+- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 2)
- ```gpo.maxblockhistory```: Maximum block history of gasprice oracle (default: 1024)
@@ -250,7 +250,7 @@ The ```bor server``` command runs the Bor client.
- ```miner.gaslimit```: Target gas ceiling (gas limit) for mined blocks (default: 30000000)
-- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 30000000000). It's set to 30gwei in bor
+- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 1000000000)
- ```miner.interruptcommit```: Interrupt block commit when block creation time is passed (default: true)
@@ -306,6 +306,6 @@ The ```bor server``` command runs the Bor client.
- ```txpool.pricebump```: Price bump percentage to replace an already existing transaction (default: 10)
-- ```txpool.pricelimit```: Minimum gas price limit to enforce the acceptance of txs into the pool (default: 30000000000). It's set to 30gwei in bor
+- ```txpool.pricelimit```: Minimum gas price limit to enforce for acceptance into the pool (default: 1)
- ```txpool.rejournal```: Time interval to regenerate the local transaction journal (default: 1h0m0s)
\ No newline at end of file
diff --git a/eth/backend.go b/eth/backend.go
index 8a6a705e7bb1..9492ab2a077b 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -122,10 +122,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
}
- // enforce minimum gas price of 30 gwei in bor
- if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(big.NewInt(params.BorDefaultMinerGasPrice)) != 0 {
- log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
- config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
+ // (PIP-35): Only enforce the minimum gas price of 25 gwei for amoy
+ if config.Genesis != nil && config.Genesis.Config.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
+ if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(big.NewInt(params.BorDefaultMinerGasPrice)) != 0 {
+ log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", params.BorDefaultMinerGasPrice)
+ config.Miner.GasPrice = new(big.Int).SetUint64(params.BorDefaultMinerGasPrice)
+ }
+ } else {
+ if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 {
+ log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
+ config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
+ }
}
if config.NoPruning && config.TrieDirtyCache > 0 {
if config.SnapshotCache > 0 {
@@ -251,6 +258,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
}
+ // (PIP-35): Update the default ignore price for amoy
+ if chainConfig.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
+ gasprice.DefaultIgnorePriceAmoy = new(big.Int).SetUint64(params.BorDefaultGpoIgnorePrice)
+ }
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
if err != nil {
return nil, err
@@ -277,6 +288,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil {
return nil, err
}
+
+ // The `config.TxPool.PriceLimit` used above doesn't reflect the sanitized/enforced changes
+ // made in the txpool. Update the `gasTip` explicitly to reflect the enforced value.
+ // (PIP-35): This change is only applied for Amoy network. Remove the checks once it's applied for all networks.
+ if chainConfig.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
+ eth.txPool.SetGasTip(new(big.Int).SetUint64(params.BorDefaultTxPoolPriceLimit))
+ }
+
// Permit the downloader to use the trie cache allowance during fast sync
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
if eth.handler, err = newHandler(&handlerConfig{
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 3e95b57eba46..9ef0cf7bba55 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -84,7 +84,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
- tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*32), nil), types.LatestSigner(&config), testKey)
+ tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey)
g.AddTx(tx)
testNonce++
}
@@ -604,7 +604,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
Nonce: statedb.GetNonce(testAddr),
Value: new(big.Int),
Gas: 1000000,
- GasPrice: big.NewInt(32 * params.InitialBaseFee),
+ GasPrice: big.NewInt(2 * params.InitialBaseFee),
Data: logCode,
})
ethservice.TxPool().Add([]*types.Transaction{tx}, false, true)
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 41828b019659..364afd686376 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -35,8 +35,9 @@ import (
const sampleNumber = 3 // Number of transactions sampled in a block
var (
- DefaultMaxPrice = big.NewInt(500 * params.GWei)
- DefaultIgnorePrice = big.NewInt(params.BorDefaultGpoIgnorePrice)
+ DefaultMaxPrice = big.NewInt(500 * params.GWei)
+ DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+ DefaultIgnorePriceAmoy *big.Int // (PIP-35): Default ignore price for amoy (to be removed later)
)
type Config struct {
@@ -100,13 +101,21 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
log.Warn("Sanitizing invalid gasprice oracle price cap", "provided", params.MaxPrice, "updated", maxPrice)
}
+ // (PIP-35): Enforce the ignore price for amoy. The default value for amoy (which is
+ // an indicator of being on amoy) should be set by the caller.
ignorePrice := params.IgnorePrice
- if ignorePrice == nil || ignorePrice.Int64() != DefaultIgnorePrice.Int64() {
- ignorePrice = DefaultIgnorePrice
- log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
- } else if ignorePrice.Int64() > 0 {
- log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
+ if DefaultIgnorePriceAmoy != nil {
+ if ignorePrice == nil || ignorePrice.Int64() != DefaultIgnorePriceAmoy.Int64() {
+ ignorePrice = DefaultIgnorePriceAmoy
+ log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
+ }
+ } else {
+ if ignorePrice == nil || ignorePrice.Int64() <= 0 {
+ ignorePrice = DefaultIgnorePrice
+ log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
+ }
}
+ log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
maxHeaderHistory := params.MaxHeaderHistory
if maxHeaderHistory < 1 {
diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go
index a496f2358660..54e04ef8e221 100644
--- a/internal/cli/server/config.go
+++ b/internal/cli/server/config.go
@@ -655,7 +655,7 @@ func DefaultConfig() *Config {
NoLocals: false,
Journal: "transactions.rlp",
Rejournal: 1 * time.Hour,
- PriceLimit: params.BorDefaultTxPoolPriceLimit, // bor's default
+ PriceLimit: 1, // Default for every network except Amoy
PriceBump: 10,
AccountSlots: 16,
GlobalSlots: 32768,
@@ -666,8 +666,8 @@ func DefaultConfig() *Config {
Sealer: &SealerConfig{
Enabled: false,
Etherbase: "",
- GasCeil: 30_000_000, // geth's default
- GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // bor's default
+ GasCeil: 30_000_000, // geth's default
+ GasPrice: big.NewInt(1 * params.GWei), // Default for every network except Amoy
ExtraData: "",
Recommit: 125 * time.Second,
CommitInterruptFlag: true,
diff --git a/internal/cli/server/config_test.go b/internal/cli/server/config_test.go
index abb8ca39fbbe..3b9739518b25 100644
--- a/internal/cli/server/config_test.go
+++ b/internal/cli/server/config_test.go
@@ -26,6 +26,9 @@ func TestConfigDefault(t *testing.T) {
// assertBorDefaultGasPrice asserts the bor default gas price is set correctly.
func assertBorDefaultGasPrice(t *testing.T, ethConfig *ethconfig.Config) {
+ // (PIP-35): Only applicable to amoy
+ t.Skip("Skipped because the price enforcement is only applied to amoy")
+
assert.NotNil(t, ethConfig)
assert.Equal(t, ethConfig.Miner.GasPrice, big.NewInt(params.BorDefaultMinerGasPrice))
}
diff --git a/internal/cli/server/testdata/default.toml b/internal/cli/server/testdata/default.toml
index 72f166a8c24a..3a6c2f0cb5c8 100644
--- a/internal/cli/server/testdata/default.toml
+++ b/internal/cli/server/testdata/default.toml
@@ -58,7 +58,7 @@ devfakeauthor = false
nolocals = false
journal = "transactions.rlp"
rejournal = "1h0m0s"
- pricelimit = 30000000000
+ pricelimit = 1
pricebump = 10
accountslots = 16
globalslots = 32768
@@ -71,7 +71,7 @@ devfakeauthor = false
etherbase = ""
extradata = ""
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "1000000000"
recommit = "2m5s"
commitinterrupt = true
@@ -127,7 +127,7 @@ devfakeauthor = false
maxheaderhistory = 1024
maxblockhistory = 1024
maxprice = "500000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "2"
[telemetry]
metrics = false
diff --git a/miner/miner.go b/miner/miner.go
index e3f11480b8bd..26bf5e62e8a7 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -59,8 +59,11 @@ type Config struct {
// DefaultConfig contains default settings for miner.
var DefaultConfig = Config{
- GasCeil: 30000000,
- GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // enforces minimum gas price of 30 gwei in bor
+ GasCeil: 30000000,
+
+ // (PIP-35): Update the gas price to `params.BorDefaultMinerGasPrice` once
+ // the change is applied over all networks.
+ GasPrice: big.NewInt(params.GWei),
// The default recommit time is chosen as two seconds since
// consensus-layer usually will wait a half slot of time(6s)
diff --git a/miner/worker_test.go b/miner/worker_test.go
index cac3e936df7f..904dedd19b8c 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -297,7 +297,7 @@ func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *t
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
var tx *types.Transaction
- gasPrice := big.NewInt(30 * params.InitialBaseFee)
+ gasPrice := big.NewInt(10 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(storageContractByteCode)), types.HomesteadSigner{}, testBankKey)
contractAddr := crypto.CreateAddress(TestBankAddress, b.txPool.Nonce(TestBankAddress))
diff --git a/packaging/templates/testnet-amoy/archive/config.toml b/packaging/templates/testnet-amoy/archive/config.toml
index 0940fd78198a..8233b6e4d2a8 100644
--- a/packaging/templates/testnet-amoy/archive/config.toml
+++ b/packaging/templates/testnet-amoy/archive/config.toml
@@ -57,12 +57,12 @@ gcmode = "archive"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -119,7 +119,7 @@ gcmode = "archive"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
index 94c195a2fec9..e020e535975c 100644
--- a/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
+++ b/packaging/templates/testnet-amoy/sentry/sentry/bor/config.toml
@@ -57,12 +57,12 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -119,7 +119,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
index 7c12b2c8b7db..9df5c9ac3bee 100644
--- a/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
+++ b/packaging/templates/testnet-amoy/sentry/validator/bor/config.toml
@@ -59,13 +59,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
index 64053f7c3187..6fb1b809cbb2 100644
--- a/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
+++ b/packaging/templates/testnet-amoy/without-sentry/bor/config.toml
@@ -59,13 +59,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
-# ignoreprice = "30000000000"
+# ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 031e3af0be9b..204439c21c50 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -183,13 +183,13 @@ const (
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block
// BorDefaultMinerGasPrice defines the minimum gas price for bor validators to mine a transaction.
- BorDefaultMinerGasPrice = 30 * GWei
+ BorDefaultMinerGasPrice = 25 * GWei
// BorDefaultTxPoolPriceLimit defines the minimum gas price limit for bor to enforce txs acceptance into the pool.
- BorDefaultTxPoolPriceLimit = 30 * GWei
+ BorDefaultTxPoolPriceLimit = 25 * GWei
// BorDefaultGpoIgnorePrice defines the minimum gas price below which bor gpo will ignore transactions.
- BorDefaultGpoIgnorePrice = 30 * GWei
+ BorDefaultGpoIgnorePrice = 25 * GWei
)
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
From 67159969e0343357003afbf2340fd9b32b5911c4 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Thu, 11 Jul 2024 14:08:07 +0530
Subject: [PATCH 100/103] miner: log last tx hash before commit interrupt
(#1289)
---
miner/test_backend.go | 5 ++++-
miner/worker.go | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/miner/test_backend.go b/miner/test_backend.go
index 035acce03754..2611a266738d 100644
--- a/miner/test_backend.go
+++ b/miner/test_backend.go
@@ -540,6 +540,8 @@ func (w *worker) commitTransactionsWithDelay(env *environment, txs *transactions
}(chDeps)
}
+ var lastTxHash common.Hash
+
mainloop:
for {
if interruptCtx != nil {
@@ -551,7 +553,7 @@ mainloop:
select {
case <-interruptCtx.Done():
txCommitInterruptCounter.Inc(1)
- log.Warn("Tx Level Interrupt")
+ log.Warn("Tx Level Interrupt", "hash", lastTxHash)
break mainloop
default:
}
@@ -573,6 +575,7 @@ mainloop:
if ltx == nil {
break
}
+ lastTxHash = ltx.Hash
// If we don't have enough space for the next transaction, skip the account.
if env.gasPool.Gas() < ltx.Gas {
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
diff --git a/miner/worker.go b/miner/worker.go
index 536ff801561d..31f41d9a44e7 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -956,6 +956,8 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
}(chDeps)
}
+ var lastTxHash common.Hash
+
mainloop:
for {
// Check interruption signal and abort building if it's fired.
@@ -974,7 +976,7 @@ mainloop:
select {
case <-interruptCtx.Done():
txCommitInterruptCounter.Inc(1)
- log.Warn("Tx Level Interrupt")
+ log.Warn("Tx Level Interrupt", "hash", lastTxHash)
break mainloop
default:
}
@@ -990,6 +992,7 @@ mainloop:
if ltx == nil {
break
}
+ lastTxHash = ltx.Hash
// If we don't have enough space for the next transaction, skip the account.
if env.gasPool.Gas() < ltx.Gas {
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
From 31545e536525bd221c38b392dff32dc8cc463f9f Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Thu, 11 Jul 2024 14:08:46 +0530
Subject: [PATCH 101/103] bump version to v1.3.4-beta3
---
packaging/templates/package_scripts/control | 2 +-
packaging/templates/package_scripts/control.arm64 | 2 +-
packaging/templates/package_scripts/control.profile.amd64 | 2 +-
packaging/templates/package_scripts/control.profile.arm64 | 2 +-
packaging/templates/package_scripts/control.validator | 2 +-
packaging/templates/package_scripts/control.validator.arm64 | 2 +-
params/version.go | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/packaging/templates/package_scripts/control b/packaging/templates/package_scripts/control
index ae34f09c733e..e9a3ae5e4c66 100644
--- a/packaging/templates/package_scripts/control
+++ b/packaging/templates/package_scripts/control
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.arm64 b/packaging/templates/package_scripts/control.arm64
index f7b46f0de060..2b24c94ce1b0 100644
--- a/packaging/templates/package_scripts/control.arm64
+++ b/packaging/templates/package_scripts/control.arm64
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.amd64 b/packaging/templates/package_scripts/control.profile.amd64
index 6db054dfabbf..9b7092d0a882 100644
--- a/packaging/templates/package_scripts/control.profile.amd64
+++ b/packaging/templates/package_scripts/control.profile.amd64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.arm64 b/packaging/templates/package_scripts/control.profile.arm64
index b005edd293aa..e0d4fa553eb4 100644
--- a/packaging/templates/package_scripts/control.profile.arm64
+++ b/packaging/templates/package_scripts/control.profile.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator b/packaging/templates/package_scripts/control.validator
index 01ca94d29300..56a631ada36f 100644
--- a/packaging/templates/package_scripts/control.validator
+++ b/packaging/templates/package_scripts/control.validator
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator.arm64 b/packaging/templates/package_scripts/control.validator.arm64
index 181811732ea0..7776298b1272 100644
--- a/packaging/templates/package_scripts/control.validator.arm64
+++ b/packaging/templates/package_scripts/control.validator.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta2
+Version: 1.3.4-beta3
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/params/version.go b/params/version.go
index 4e455c7a26b0..7b1838cf065f 100644
--- a/params/version.go
+++ b/params/version.go
@@ -24,7 +24,7 @@ const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release
VersionPatch = 4 // Patch version component of the current release
- VersionMeta = "beta2" // Version metadata to append to the version string
+ VersionMeta = "beta3" // Version metadata to append to the version string
)
var GitCommit string
From dce4253605113930e6afafa8220519f0418143c9 Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Tue, 23 Jul 2024 19:32:48 +0530
Subject: [PATCH 102/103] PIP-35: enforce gas related configs for mainnet
(#1290)
* enforce gas configs for all networks
* eth/gasprice: remove else if for log
* docs/cli: update example config
* fix tests
* internal/cli/server: fix tests
* miner: fix tests
---
builder/files/config.toml | 6 ++---
core/txpool/legacypool/legacypool.go | 23 +++++-----------
core/txpool/legacypool/legacypool_test.go | 3 ---
docs/cli/example_config.toml | 6 ++---
docs/cli/server.md | 6 ++---
eth/backend.go | 26 +++++--------------
eth/catalyst/api_test.go | 4 +--
eth/gasprice/gasprice.go | 22 +++++-----------
internal/cli/server/command_test.go | 2 +-
internal/cli/server/config.go | 8 +++---
internal/cli/server/config_legacy_test.go | 2 +-
internal/cli/server/config_test.go | 3 ---
internal/cli/server/testdata/default.toml | 6 ++---
internal/cli/server/testdata/test.toml | 2 +-
miner/miner.go | 7 ++---
miner/worker_test.go | 6 ++---
.../templates/mainnet-v1/archive/config.toml | 6 ++---
.../mainnet-v1/sentry/sentry/bor/config.toml | 6 ++---
.../sentry/validator/bor/config.toml | 6 ++---
.../mainnet-v1/without-sentry/bor/config.toml | 6 ++---
.../templates/testnet-v4/archive/config.toml | 6 ++---
.../testnet-v4/sentry/sentry/bor/config.toml | 6 ++---
.../sentry/validator/bor/config.toml | 6 ++---
.../testnet-v4/without-sentry/bor/config.toml | 6 ++---
24 files changed, 71 insertions(+), 109 deletions(-)
diff --git a/builder/files/config.toml b/builder/files/config.toml
index 7fa047572c5a..df3403197932 100644
--- a/builder/files/config.toml
+++ b/builder/files/config.toml
@@ -56,7 +56,7 @@ syncmode = "full"
[txpool]
nolocals = true
- pricelimit = 30000000000 # 25000000000 for amoy
+ pricelimit = 25000000000
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -69,7 +69,7 @@ syncmode = "full"
[miner]
gaslimit = 30000000
- gasprice = "30000000000" # 25000000000 for amoy
+ gasprice = "25000000000"
# mine = true
# etherbase = "VALIDATOR ADDRESS"
# extradata = ""
@@ -127,7 +127,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000" # 25000000000 for amoy
+ ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index d6f39f8f914b..adbb80a42a0f 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -147,9 +147,7 @@ var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
- // (PIP-35): Update price limit to `params.BorDefaultTxPoolPriceLimit` once
- // the change is applied over all networks.
- PriceLimit: 1,
+ PriceLimit: params.BorDefaultTxPoolPriceLimit,
PriceBump: 10,
AccountSlots: 16,
@@ -163,23 +161,16 @@ var DefaultConfig = Config{
// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
-func (config *Config) sanitize(chainId *big.Int) Config {
+func (config *Config) sanitize() Config {
conf := *config
if conf.Rejournal < time.Second {
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
conf.Rejournal = time.Second
}
- // (PIP-35): Only enforce the 25gwei txpool price limit for amoy
- if chainId.Cmp(params.AmoyChainConfig.ChainID) == 0 {
- if conf.PriceLimit != params.BorDefaultTxPoolPriceLimit {
- log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", params.BorDefaultTxPoolPriceLimit)
- conf.PriceLimit = params.BorDefaultTxPoolPriceLimit
- }
- } else {
- if conf.PriceLimit < 1 {
- log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
- conf.PriceLimit = DefaultConfig.PriceLimit
- }
+ // PIP-35: Enforce min price limit to 25 gwei
+ if conf.PriceLimit != params.BorDefaultTxPoolPriceLimit {
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
+ conf.PriceLimit = DefaultConfig.PriceLimit
}
if conf.PriceBump < 1 {
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
@@ -259,7 +250,7 @@ type txpoolResetRequest struct {
// transactions from the network.
func New(config Config, chain BlockChain, options ...func(pool *LegacyPool)) *LegacyPool {
// Sanitize the input to ensure no vulnerable gas prices are set
- config = (&config).sanitize(chain.Config().ChainID)
+ config = (&config).sanitize()
// Create the transaction pool with its initial settings
pool := &LegacyPool{
diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go
index d61d1f8f078f..923f9213f547 100644
--- a/core/txpool/legacypool/legacypool_test.go
+++ b/core/txpool/legacypool/legacypool_test.go
@@ -296,9 +296,6 @@ func (c *testChain) State() (*state.StateDB, error) {
// TestTxPoolDefaultPriceLimit ensures the bor default tx pool price limit is set correctly.
func TestTxPoolDefaultPriceLimit(t *testing.T) {
- // (PIP-35): Only applicable to amoy
- t.Skip("Skipped because the price enforcement is only applied to amoy")
-
t.Parallel()
pool, _ := setupPool()
diff --git a/docs/cli/example_config.toml b/docs/cli/example_config.toml
index 4137015ee97c..ea869925c400 100644
--- a/docs/cli/example_config.toml
+++ b/docs/cli/example_config.toml
@@ -61,7 +61,7 @@ devfakeauthor = false # Run miner without validator set authorization
nolocals = false # Disables price exemptions for locally submitted transactions
journal = "transactions.rlp" # Disk journal for local transaction to survive node restarts
rejournal = "1h0m0s" # Time interval to regenerate the local transaction journal
- pricelimit = 30000000000 # Minimum gas price limit to enforce for acceptance into the pool. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
+ pricelimit = 25000000000 # Minimum gas price limit to enforce for acceptance into the pool. Regardless the value set, it will be enforced to 25000000000 for all networks
pricebump = 10 # Price bump percentage to replace an already existing transaction
accountslots = 16 # Minimum number of executable transaction slots guaranteed per account
globalslots = 32768 # Maximum number of executable transaction slots for all accounts
@@ -74,7 +74,7 @@ devfakeauthor = false # Run miner without validator set authorization
etherbase = "" # Public address for block mining rewards
extradata = "" # Block extra data set by the miner (default = client version)
gaslimit = 30000000 # Target gas ceiling for mined blocks
- gasprice = "30000000000" # Minimum gas price for mining a transaction. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
+ gasprice = "25000000000" # Minimum gas price for mining a transaction. Regardless the value set, it will be enforced to 25000000000 for all networks
recommit = "2m5s" # The time interval for miner to re-create mining work
commitinterrupt = true # Interrupt the current mining work when time is exceeded and create partial blocks
@@ -128,7 +128,7 @@ devfakeauthor = false # Run miner without validator set authorization
maxheaderhistory = 1024 # Maximum header history of gasprice oracle
maxblockhistory = 1024 # Maximum block history of gasprice oracle
maxprice = "5000000000000" # Maximum gas price will be recommended by gpo
- ignoreprice = "2" # Gas price below which gpo will ignore transactions. Recommended for mainnet = 30000000000 (not enforced). For Amoy network, regardless the value set, it will be enforced to 25000000000 in bor.
+ ignoreprice = "25000000000" # Gas price below which gpo will ignore transactions. Regardless the value set, it will be enforced to 25000000000 for all networks
[telemetry]
metrics = false # Enable metrics collection and reporting
diff --git a/docs/cli/server.md b/docs/cli/server.md
index fbd4adad3fb2..fb0f01e77793 100644
--- a/docs/cli/server.md
+++ b/docs/cli/server.md
@@ -46,7 +46,7 @@ The ```bor server``` command runs the Bor client.
- ```gpo.blocks```: Number of recent blocks to check for gas prices (default: 20)
-- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 2)
+- ```gpo.ignoreprice```: Gas price below which gpo will ignore transactions (default: 25000000000)
- ```gpo.maxblockhistory```: Maximum block history of gasprice oracle (default: 1024)
@@ -250,7 +250,7 @@ The ```bor server``` command runs the Bor client.
- ```miner.gaslimit```: Target gas ceiling (gas limit) for mined blocks (default: 30000000)
-- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 1000000000)
+- ```miner.gasprice```: Minimum gas price for mining a transaction (default: 25000000000)
- ```miner.interruptcommit```: Interrupt block commit when block creation time is passed (default: true)
@@ -306,6 +306,6 @@ The ```bor server``` command runs the Bor client.
- ```txpool.pricebump```: Price bump percentage to replace an already existing transaction (default: 10)
-- ```txpool.pricelimit```: Minimum gas price limit to enforce for acceptance into the pool (default: 1)
+- ```txpool.pricelimit```: Minimum gas price limit to enforce for acceptance into the pool (default: 25000000000)
- ```txpool.rejournal```: Time interval to regenerate the local transaction journal (default: 1h0m0s)
\ No newline at end of file
diff --git a/eth/backend.go b/eth/backend.go
index 9492ab2a077b..1bd7d941f8bc 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -122,18 +122,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
}
- // (PIP-35): Only enforce the minimum gas price of 25 gwei for amoy
- if config.Genesis != nil && config.Genesis.Config.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
- if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(big.NewInt(params.BorDefaultMinerGasPrice)) != 0 {
- log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", params.BorDefaultMinerGasPrice)
- config.Miner.GasPrice = new(big.Int).SetUint64(params.BorDefaultMinerGasPrice)
- }
- } else {
- if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 {
- log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
- config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
- }
+
+ // PIP-35: Enforce min gas price to 25 gwei
+ if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(big.NewInt(params.BorDefaultMinerGasPrice)) != 0 {
+ log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
+ config.Miner.GasPrice = ethconfig.Defaults.Miner.GasPrice
}
+
if config.NoPruning && config.TrieDirtyCache > 0 {
if config.SnapshotCache > 0 {
config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
@@ -258,10 +253,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker)
}
- // (PIP-35): Update the default ignore price for amoy
- if chainConfig.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
- gasprice.DefaultIgnorePriceAmoy = new(big.Int).SetUint64(params.BorDefaultGpoIgnorePrice)
- }
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
if err != nil {
return nil, err
@@ -291,10 +282,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// The `config.TxPool.PriceLimit` used above doesn't reflect the sanitized/enforced changes
// made in the txpool. Update the `gasTip` explicitly to reflect the enforced value.
- // (PIP-35): This change is only applied for Amoy network. Remove the checks once it's applied for all networks.
- if chainConfig.ChainID.Cmp(params.AmoyChainConfig.ChainID) == 0 {
- eth.txPool.SetGasTip(new(big.Int).SetUint64(params.BorDefaultTxPoolPriceLimit))
- }
+ eth.txPool.SetGasTip(new(big.Int).SetUint64(params.BorDefaultTxPoolPriceLimit))
// Permit the downloader to use the trie cache allowance during fast sync
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 9ef0cf7bba55..8b7bcfb5c682 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -84,7 +84,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
- tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey)
+ tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*26), nil), types.LatestSigner(&config), testKey)
g.AddTx(tx)
testNonce++
}
@@ -604,7 +604,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
Nonce: statedb.GetNonce(testAddr),
Value: new(big.Int),
Gas: 1000000,
- GasPrice: big.NewInt(2 * params.InitialBaseFee),
+ GasPrice: big.NewInt(26 * params.InitialBaseFee),
Data: logCode,
})
ethservice.TxPool().Add([]*types.Transaction{tx}, false, true)
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 364afd686376..4e95a1c6cdd6 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -35,9 +35,8 @@ import (
const sampleNumber = 3 // Number of transactions sampled in a block
var (
- DefaultMaxPrice = big.NewInt(500 * params.GWei)
- DefaultIgnorePrice = big.NewInt(2 * params.Wei)
- DefaultIgnorePriceAmoy *big.Int // (PIP-35): Default ignore price for amoy (to be removed later)
+ DefaultMaxPrice = big.NewInt(500 * params.GWei)
+ DefaultIgnorePrice = big.NewInt(params.BorDefaultGpoIgnorePrice) // bor's default
)
type Config struct {
@@ -101,21 +100,14 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
log.Warn("Sanitizing invalid gasprice oracle price cap", "provided", params.MaxPrice, "updated", maxPrice)
}
- // (PIP-35): Enforce the ignore price for amoy. The default value for amoy (which is
- // an indicator of being on amoy) should be set by the caller.
+ // PIP-35: Enforce the ignore price to 25 gwei
ignorePrice := params.IgnorePrice
- if DefaultIgnorePriceAmoy != nil {
- if ignorePrice == nil || ignorePrice.Int64() != DefaultIgnorePriceAmoy.Int64() {
- ignorePrice = DefaultIgnorePriceAmoy
- log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
- }
+ if ignorePrice == nil || ignorePrice.Int64() != DefaultIgnorePrice.Int64() {
+ ignorePrice = DefaultIgnorePrice
+ log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
} else {
- if ignorePrice == nil || ignorePrice.Int64() <= 0 {
- ignorePrice = DefaultIgnorePrice
- log.Warn("Sanitizing invalid gasprice oracle ignore price", "provided", params.IgnorePrice, "updated", ignorePrice)
- }
+ log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
}
- log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
maxHeaderHistory := params.MaxHeaderHistory
if maxHeaderHistory < 1 {
diff --git a/internal/cli/server/command_test.go b/internal/cli/server/command_test.go
index de6a2d5efab2..3181dd640281 100644
--- a/internal/cli/server/command_test.go
+++ b/internal/cli/server/command_test.go
@@ -86,7 +86,7 @@ func TestFlagsWithConfig(t *testing.T) {
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
},
)
- require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(30000000000))
+ require.Equal(t, c.config.Sealer.GasPrice, big.NewInt(25000000000))
require.Equal(t, c.config.Sealer.Recommit, recommit)
require.Equal(t, c.config.JsonRPC.RPCEVMTimeout, evmTimeout)
require.Equal(t, c.config.JsonRPC.Http.API, []string{"eth", "bor"})
diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go
index 54e04ef8e221..5960fdeab0a1 100644
--- a/internal/cli/server/config.go
+++ b/internal/cli/server/config.go
@@ -655,7 +655,7 @@ func DefaultConfig() *Config {
NoLocals: false,
Journal: "transactions.rlp",
Rejournal: 1 * time.Hour,
- PriceLimit: 1, // Default for every network except Amoy
+ PriceLimit: params.BorDefaultTxPoolPriceLimit, // bor's default
PriceBump: 10,
AccountSlots: 16,
GlobalSlots: 32768,
@@ -666,8 +666,8 @@ func DefaultConfig() *Config {
Sealer: &SealerConfig{
Enabled: false,
Etherbase: "",
- GasCeil: 30_000_000, // geth's default
- GasPrice: big.NewInt(1 * params.GWei), // Default for every network except Amoy
+ GasCeil: 30_000_000, // geth's default
+ GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // bor's default
ExtraData: "",
Recommit: 125 * time.Second,
CommitInterruptFlag: true,
@@ -678,7 +678,7 @@ func DefaultConfig() *Config {
MaxHeaderHistory: 1024,
MaxBlockHistory: 1024,
MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
+ IgnorePrice: gasprice.DefaultIgnorePrice, // bor's default
},
JsonRPC: &JsonRPCConfig{
IPCDisable: false,
diff --git a/internal/cli/server/config_legacy_test.go b/internal/cli/server/config_legacy_test.go
index e9957c3353b6..2d2d7ec397c3 100644
--- a/internal/cli/server/config_legacy_test.go
+++ b/internal/cli/server/config_legacy_test.go
@@ -26,7 +26,7 @@ func TestConfigLegacy(t *testing.T) {
"31000000": "0x2087b9e2b353209c2c21e370c82daa12278efd0fe5f0febe6c29035352cf050e",
"32000000": "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68",
}
- testConfig.Sealer.GasPrice = big.NewInt(30000000000)
+ testConfig.Sealer.GasPrice = big.NewInt(25000000000)
testConfig.Sealer.Recommit = 20 * time.Second
testConfig.JsonRPC.RPCEVMTimeout = 5 * time.Second
testConfig.JsonRPC.TxFeeCap = 6.0
diff --git a/internal/cli/server/config_test.go b/internal/cli/server/config_test.go
index 3b9739518b25..abb8ca39fbbe 100644
--- a/internal/cli/server/config_test.go
+++ b/internal/cli/server/config_test.go
@@ -26,9 +26,6 @@ func TestConfigDefault(t *testing.T) {
// assertBorDefaultGasPrice asserts the bor default gas price is set correctly.
func assertBorDefaultGasPrice(t *testing.T, ethConfig *ethconfig.Config) {
- // (PIP-35): Only applicable to amoy
- t.Skip("Skipped because the price enforcement is only applied to amoy")
-
assert.NotNil(t, ethConfig)
assert.Equal(t, ethConfig.Miner.GasPrice, big.NewInt(params.BorDefaultMinerGasPrice))
}
diff --git a/internal/cli/server/testdata/default.toml b/internal/cli/server/testdata/default.toml
index 3a6c2f0cb5c8..0df37d610c76 100644
--- a/internal/cli/server/testdata/default.toml
+++ b/internal/cli/server/testdata/default.toml
@@ -58,7 +58,7 @@ devfakeauthor = false
nolocals = false
journal = "transactions.rlp"
rejournal = "1h0m0s"
- pricelimit = 1
+ pricelimit = 25000000000
pricebump = 10
accountslots = 16
globalslots = 32768
@@ -71,7 +71,7 @@ devfakeauthor = false
etherbase = ""
extradata = ""
gaslimit = 30000000
- gasprice = "1000000000"
+ gasprice = "25000000000"
recommit = "2m5s"
commitinterrupt = true
@@ -127,7 +127,7 @@ devfakeauthor = false
maxheaderhistory = 1024
maxblockhistory = 1024
maxprice = "500000000000"
- ignoreprice = "2"
+ ignoreprice = "25000000000"
[telemetry]
metrics = false
diff --git a/internal/cli/server/testdata/test.toml b/internal/cli/server/testdata/test.toml
index 04b2dbed357f..e1ff535956cd 100644
--- a/internal/cli/server/testdata/test.toml
+++ b/internal/cli/server/testdata/test.toml
@@ -11,7 +11,7 @@ snapshot = true
"32000000" = "0x875500011e5eecc0c554f95d07b31cf59df4ca2505f4dbbfffa7d4e4da917c68"
[miner]
- gasprice = "30000000000"
+ gasprice = "25000000000"
recommit = "20s"
[jsonrpc]
diff --git a/miner/miner.go b/miner/miner.go
index 26bf5e62e8a7..e0cb47bcc0ae 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -59,11 +59,8 @@ type Config struct {
// DefaultConfig contains default settings for miner.
var DefaultConfig = Config{
- GasCeil: 30000000,
-
- // (PIP-35): Update the gas price to `params.BorDefaultMinerGasPrice` once
- // the change is applied over all networks.
- GasPrice: big.NewInt(params.GWei),
+ GasCeil: 30000000,
+ GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // enforce min gas price to 25 gwei for bor
// The default recommit time is chosen as two seconds since
// consensus-layer usually will wait a half slot of time(6s)
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 904dedd19b8c..cab23340497d 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -269,7 +269,7 @@ func (b *testWorkerBackend) PeerCount() int {
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
var tx *types.Transaction
- gasPrice := big.NewInt(10 * params.InitialBaseFee)
+ gasPrice := big.NewInt(26 * params.InitialBaseFee)
if creation {
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
} else {
@@ -297,7 +297,7 @@ func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *t
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
var tx *types.Transaction
- gasPrice := big.NewInt(10 * params.InitialBaseFee)
+ gasPrice := big.NewInt(26 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(storageContractByteCode)), types.HomesteadSigner{}, testBankKey)
contractAddr := crypto.CreateAddress(TestBankAddress, b.txPool.Nonce(TestBankAddress))
@@ -309,7 +309,7 @@ func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, co
func (b *testWorkerBackend) newStorageContractCallTx(to common.Address, nonce uint64) *types.Transaction {
var tx *types.Transaction
- gasPrice := big.NewInt(10 * params.InitialBaseFee)
+ gasPrice := big.NewInt(26 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewTransaction(nonce, to, nil, storageCallTxGas, gasPrice, common.FromHex(storageContractTxCallData)), types.HomesteadSigner{}, testBankKey)
diff --git a/packaging/templates/mainnet-v1/archive/config.toml b/packaging/templates/mainnet-v1/archive/config.toml
index 9be166bac817..1fb929b358e9 100644
--- a/packaging/templates/mainnet-v1/archive/config.toml
+++ b/packaging/templates/mainnet-v1/archive/config.toml
@@ -51,7 +51,7 @@ gcmode = "archive"
[txpool]
nolocals = true
- pricelimit = 30000000000
+ pricelimit = 25000000000
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -64,7 +64,7 @@ gcmode = "archive"
[miner]
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ gcmode = "archive"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
index 5dec42f6b6dc..9d606059fd7e 100644
--- a/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
+++ b/packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml
@@ -51,7 +51,7 @@ syncmode = "full"
[txpool]
nolocals = true
- pricelimit = 30000000000
+ pricelimit = 25000000000
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -64,7 +64,7 @@ syncmode = "full"
[miner]
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
index b9ad57f5c232..9b581ac71de5 100644
--- a/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
+++ b/packaging/templates/mainnet-v1/sentry/validator/bor/config.toml
@@ -53,7 +53,7 @@ syncmode = "full"
[txpool]
nolocals = true
- pricelimit = 30000000000
+ pricelimit = 25000000000
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -67,7 +67,7 @@ syncmode = "full"
[miner]
mine = true
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/mainnet-v1/without-sentry/bor/config.toml b/packaging/templates/mainnet-v1/without-sentry/bor/config.toml
index c7a7a3ddb67a..5f4c86815f6a 100644
--- a/packaging/templates/mainnet-v1/without-sentry/bor/config.toml
+++ b/packaging/templates/mainnet-v1/without-sentry/bor/config.toml
@@ -53,7 +53,7 @@ syncmode = "full"
[txpool]
nolocals = true
- pricelimit = 30000000000
+ pricelimit = 25000000000
accountslots = 16
globalslots = 32768
accountqueue = 16
@@ -67,7 +67,7 @@ syncmode = "full"
[miner]
mine = true
gaslimit = 30000000
- gasprice = "30000000000"
+ gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- ignoreprice = "30000000000"
+ ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/archive/config.toml b/packaging/templates/testnet-v4/archive/config.toml
index 4bbefafe0dcc..16fe8e96d227 100644
--- a/packaging/templates/testnet-v4/archive/config.toml
+++ b/packaging/templates/testnet-v4/archive/config.toml
@@ -59,12 +59,12 @@ gcmode = "archive"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ gcmode = "archive"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml b/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
index a21a1eb3eb05..2af9f206f1da 100644
--- a/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
+++ b/packaging/templates/testnet-v4/sentry/sentry/bor/config.toml
@@ -59,12 +59,12 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# mine = false
# etherbase = ""
# extradata = ""
@@ -121,7 +121,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/sentry/validator/bor/config.toml b/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
index ed1fac2a51b7..ff397370b554 100644
--- a/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
+++ b/packaging/templates/testnet-v4/sentry/validator/bor/config.toml
@@ -61,13 +61,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
- # ignoreprice = "30000000000"
+ # ignoreprice = "25000000000"
[telemetry]
metrics = true
diff --git a/packaging/templates/testnet-v4/without-sentry/bor/config.toml b/packaging/templates/testnet-v4/without-sentry/bor/config.toml
index e9e3b0d1cd23..3690048645f6 100644
--- a/packaging/templates/testnet-v4/without-sentry/bor/config.toml
+++ b/packaging/templates/testnet-v4/without-sentry/bor/config.toml
@@ -61,13 +61,13 @@ syncmode = "full"
# locals = []
# journal = ""
# rejournal = "1h0m0s"
- # pricelimit = 30000000000
+ # pricelimit = 25000000000
# pricebump = 10
[miner]
mine = true
gaslimit = 30000000
- # gasprice = "30000000000"
+ # gasprice = "25000000000"
# etherbase = ""
# extradata = ""
# recommit = "2m5s"
@@ -123,7 +123,7 @@ syncmode = "full"
# maxheaderhistory = 1024
# maxblockhistory = 1024
# maxprice = "5000000000000"
-# ignoreprice = "30000000000"
+# ignoreprice = "25000000000"
[telemetry]
metrics = true
From 29840e2016c2f3c2091db69b0441d1bd443d03ad Mon Sep 17 00:00:00 2001
From: Manav Darji
Date: Thu, 25 Jul 2024 16:48:09 +0530
Subject: [PATCH 103/103] bump version to v1.3.4
---
packaging/templates/package_scripts/control | 2 +-
packaging/templates/package_scripts/control.arm64 | 2 +-
packaging/templates/package_scripts/control.profile.amd64 | 2 +-
packaging/templates/package_scripts/control.profile.arm64 | 2 +-
packaging/templates/package_scripts/control.validator | 2 +-
.../templates/package_scripts/control.validator.arm64 | 2 +-
params/version.go | 8 ++++----
7 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/packaging/templates/package_scripts/control b/packaging/templates/package_scripts/control
index e9a3ae5e4c66..da6b7bbfad76 100644
--- a/packaging/templates/package_scripts/control
+++ b/packaging/templates/package_scripts/control
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.arm64 b/packaging/templates/package_scripts/control.arm64
index 2b24c94ce1b0..d818fd23b61f 100644
--- a/packaging/templates/package_scripts/control.arm64
+++ b/packaging/templates/package_scripts/control.arm64
@@ -1,5 +1,5 @@
Source: bor
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.amd64 b/packaging/templates/package_scripts/control.profile.amd64
index 9b7092d0a882..accd2ff65c2b 100644
--- a/packaging/templates/package_scripts/control.profile.amd64
+++ b/packaging/templates/package_scripts/control.profile.amd64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.profile.arm64 b/packaging/templates/package_scripts/control.profile.arm64
index e0d4fa553eb4..1974de5ee092 100644
--- a/packaging/templates/package_scripts/control.profile.arm64
+++ b/packaging/templates/package_scripts/control.profile.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator b/packaging/templates/package_scripts/control.validator
index 56a631ada36f..3b71d548e0a0 100644
--- a/packaging/templates/package_scripts/control.validator
+++ b/packaging/templates/package_scripts/control.validator
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/packaging/templates/package_scripts/control.validator.arm64 b/packaging/templates/package_scripts/control.validator.arm64
index 7776298b1272..3bb238d801a5 100644
--- a/packaging/templates/package_scripts/control.validator.arm64
+++ b/packaging/templates/package_scripts/control.validator.arm64
@@ -1,5 +1,5 @@
Source: bor-profile
-Version: 1.3.4-beta3
+Version: 1.3.4
Section: develop
Priority: standard
Maintainer: Polygon
diff --git a/params/version.go b/params/version.go
index 7b1838cf065f..6dbc3b6cf2f8 100644
--- a/params/version.go
+++ b/params/version.go
@@ -21,10 +21,10 @@ import (
)
const (
- VersionMajor = 1 // Major version component of the current release
- VersionMinor = 3 // Minor version component of the current release
- VersionPatch = 4 // Patch version component of the current release
- VersionMeta = "beta3" // Version metadata to append to the version string
+ VersionMajor = 1 // Major version component of the current release
+ VersionMinor = 3 // Minor version component of the current release
+ VersionPatch = 4 // Patch version component of the current release
+ VersionMeta = "" // Version metadata to append to the version string
)
var GitCommit string