diff --git a/.github/workflows/lint-go.yaml b/.github/workflows/lint-go.yaml
index ea4d2195a..0f49ceb39 100644
--- a/.github/workflows/lint-go.yaml
+++ b/.github/workflows/lint-go.yaml
@@ -17,6 +17,13 @@ jobs:
with:
go-version: '1.22'
cache: false
+
+ - name: Check `builtins` directory
+ # if it has any changes in the 'builtins' dir after running `go generate`, echo an error and fail the workflow
+ run: |
+ go generate ./builtin/gen
+ git diff --exit-code builtin/gen || (echo "\n\n\nbuiltin/gen directory is not up to date, run 'go generate ./...' to update it" && exit 1)
+
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
diff --git a/README.md b/README.md
index 50ee4e85c..c41d0923a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
@@ -44,9 +44,9 @@ ___
## Documentation
-- [Build](./docs/build.md) - How to build the `thor` binary.
-- [Usage](./docs/usage.md) - How to run thor with different configurations.
-- [Hosting a Node](./docs/hosting-a-node.md) - Considerations and requirements for hosting a node.
+- [Build](https://github.com/vechain/thor/blob/master/docs/build.md) - How to build the `thor` binary.
+- [Usage](https://github.com/vechain/thor/blob/master/docs/usage.md) - How to run thor with different configurations.
+- [Hosting a Node](https://github.com/vechain/thor/blob/master/docs/hosting-a-node.md) - Considerations and requirements for hosting a node.
- [Core Concepts](https://docs.vechain.org/core-concepts) - Core concepts of the VeChainThor blockchain.
- [API Reference](https://mainnet.vechain.org) - The API reference for the VeChainThor blockchain.
@@ -67,7 +67,7 @@ To chat with other community members you can join:
-Do note that our [Code of Conduct](./docs/CODE_OF_CONDUCT.md) applies to all VeChain community channels. Users are
+Do note that our [Code of Conduct](https://github.com/vechain/thor/blob/master/docs/CODE_OF_CONDUCT.md) applies to all VeChain community channels. Users are
**highly encouraged** to read and adhere to them to avoid repercussions.
---
@@ -75,7 +75,7 @@ Do note that our [Code of Conduct](./docs/CODE_OF_CONDUCT.md) applies to all VeC
## Contributing
Contributions to VeChainThor are welcome and highly appreciated. However, before you jump right into it, we would like
-you to review our [Contribution Guidelines](./docs/CONTRIBUTING.md) to make sure you have a smooth experience
+you to review our [Contribution Guidelines](https://github.com/vechain/thor/blob/master/docs/CONTRIBUTING.md) to make sure you have a smooth experience
contributing to VeChainThor.
---
diff --git a/api/accounts/accounts.go b/api/accounts/accounts.go
index 4bd940709..54058a160 100644
--- a/api/accounts/accounts.go
+++ b/api/accounts/accounts.go
@@ -358,27 +358,27 @@ func (a *Accounts) Mount(root *mux.Router, pathPrefix string) {
sub.Path("/*").
Methods(http.MethodPost).
- Name("accounts_call_batch_code").
+ Name("POST /accounts/*").
HandlerFunc(utils.WrapHandlerFunc(a.handleCallBatchCode))
sub.Path("/{address}").
Methods(http.MethodGet).
- Name("accounts_get_account").
+ Name("GET /accounts/{address}").
HandlerFunc(utils.WrapHandlerFunc(a.handleGetAccount))
sub.Path("/{address}/code").
Methods(http.MethodGet).
- Name("accounts_get_code").
+ Name("GET /accounts/{address}/code").
HandlerFunc(utils.WrapHandlerFunc(a.handleGetCode))
sub.Path("/{address}/storage/{key}").
Methods("GET").
- Name("accounts_get_storage").
+ Name("GET /accounts/{address}/storage").
HandlerFunc(utils.WrapHandlerFunc(a.handleGetStorage))
// These two methods are currently deprecated
sub.Path("").
Methods(http.MethodPost).
- Name("accounts_call_contract").
+ Name("POST /accounts").
HandlerFunc(utils.WrapHandlerFunc(a.handleCallContract))
sub.Path("/{address}").
Methods(http.MethodPost).
- Name("accounts_call_contract_address").
+ Name("POST /accounts/{address}").
HandlerFunc(utils.WrapHandlerFunc(a.handleCallContract))
}
diff --git a/api/blocks/blocks.go b/api/blocks/blocks.go
index bddb3ac12..ff86e02e6 100644
--- a/api/blocks/blocks.go
+++ b/api/blocks/blocks.go
@@ -95,6 +95,6 @@ func (b *Blocks) Mount(root *mux.Router, pathPrefix string) {
sub := root.PathPrefix(pathPrefix).Subrouter()
sub.Path("/{revision}").
Methods(http.MethodGet).
- Name("blocks_get_block").
+ Name("GET /blocks/{revision}").
HandlerFunc(utils.WrapHandlerFunc(b.handleGetBlock))
}
diff --git a/api/debug/debug.go b/api/debug/debug.go
index e84a88d57..5ff54f1dc 100644
--- a/api/debug/debug.go
+++ b/api/debug/debug.go
@@ -466,14 +466,14 @@ func (d *Debug) Mount(root *mux.Router, pathPrefix string) {
sub.Path("/tracers").
Methods(http.MethodPost).
- Name("debug_trace_clause").
+ Name("POST /debug/tracers").
HandlerFunc(utils.WrapHandlerFunc(d.handleTraceClause))
sub.Path("/tracers/call").
Methods(http.MethodPost).
- Name("debug_trace_call").
+ Name("POST /debug/tracers/call").
HandlerFunc(utils.WrapHandlerFunc(d.handleTraceCall))
sub.Path("/storage-range").
Methods(http.MethodPost).
- Name("debug_trace_storage").
+ Name("POST /debug/storage-range").
HandlerFunc(utils.WrapHandlerFunc(d.handleDebugStorage))
}
diff --git a/api/doc/README.md b/api/doc/README.md
index 2f2c0ee62..644641053 100644
--- a/api/doc/README.md
+++ b/api/doc/README.md
@@ -1,7 +1,7 @@
## Swagger
swagger-ui from https://github.com/swagger-api/swagger-ui @v5.11.2
-- Created [window-observer.js](swagger-ui/window-observer.js) to remove `Try it out` functionality for subscription endpoints
+- Created [window-observer.js](./swagger-ui/window-observer.js) to remove `Try it out` functionality for subscription endpoints
```bash
curl https://unpkg.com/swagger-ui-dist@5.11.2/swagger-ui.css > swagger-ui/swagger-ui.css
@@ -11,7 +11,7 @@ curl https://unpkg.com/swagger-ui-dist@5.11.2/swagger-ui-standalone-preset.js >
## Stoplight
Spotlight UI from https://github.com/stoplightio/elements @v8.0.3
-- Created [window-observer.js](stoplight-ui/window-observer.js) to remove `Send API Request` functionality for subscription endpoints
+- Created [window-observer.js](./stoplight-ui/window-observer.js) to remove `Send API Request` functionality for subscription endpoints
```bash
curl https://unpkg.com/@stoplight/elements@8.0.3/styles.min.css > stoplight-ui/styles.min.css
diff --git a/api/events/events.go b/api/events/events.go
index b4c93fadc..d203212db 100644
--- a/api/events/events.go
+++ b/api/events/events.go
@@ -85,6 +85,6 @@ func (e *Events) Mount(root *mux.Router, pathPrefix string) {
sub.Path("").
Methods(http.MethodPost).
- Name("logs_filter_event").
+ Name("POST /logs/event").
HandlerFunc(utils.WrapHandlerFunc(e.handleFilter))
}
diff --git a/api/metrics.go b/api/metrics.go
index 9fd5c3d94..57631be71 100644
--- a/api/metrics.go
+++ b/api/metrics.go
@@ -19,9 +19,15 @@ import (
)
var (
+ websocketDurations = []int64{
+ 0, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000, 25_000,
+ 50_000, 100_000, 250_000, 500_000, 1000_000, 2_500_000, 5_000_000, 10_000_000,
+ }
metricHTTPReqCounter = metrics.LazyLoadCounterVec("api_request_count", []string{"name", "code", "method"})
metricHTTPReqDuration = metrics.LazyLoadHistogramVec("api_duration_ms", []string{"name", "code", "method"}, metrics.BucketHTTPReqs)
- metricActiveWebsocketCount = metrics.LazyLoadGaugeVec("api_active_websocket_count", []string{"subject"})
+ metricWebsocketDuration = metrics.LazyLoadHistogramVec("api_websocket_duration", []string{"name", "code"}, websocketDurations)
+ metricActiveWebsocketGauge = metrics.LazyLoadGaugeVec("api_active_websocket_gauge", []string{"name"})
+ metricWebsocketCounter = metrics.LazyLoadCounterVec("api_websocket_counter", []string{"name"})
)
// metricsResponseWriter is a wrapper around http.ResponseWriter that captures the status code.
@@ -62,7 +68,7 @@ func metricsMiddleware(next http.Handler) http.Handler {
var (
enabled = false
name = ""
- subscription = ""
+ subscription = false
)
// all named route will be recorded
@@ -70,24 +76,24 @@ func metricsMiddleware(next http.Handler) http.Handler {
enabled = true
name = rt.GetName()
if strings.HasPrefix(name, "subscriptions") {
- // example path: /subscriptions/txpool -> subject = txpool
- paths := strings.Split(r.URL.Path, "/")
- if len(paths) > 2 {
- subscription = paths[2]
- }
+ subscription = true
+ name = "WS " + r.URL.Path
}
}
now := time.Now()
mrw := newMetricsResponseWriter(w)
- if subscription != "" {
- metricActiveWebsocketCount().AddWithLabel(1, map[string]string{"subject": subscription})
+ if subscription {
+ metricActiveWebsocketGauge().AddWithLabel(1, map[string]string{"name": name})
+ metricWebsocketCounter().AddWithLabel(1, map[string]string{"name": name})
}
next.ServeHTTP(mrw, r)
- if subscription != "" {
- metricActiveWebsocketCount().AddWithLabel(-1, map[string]string{"subject": subscription})
+ if subscription {
+ metricActiveWebsocketGauge().AddWithLabel(-1, map[string]string{"name": name})
+ // record websocket duration in seconds, not MS
+ metricWebsocketDuration().ObserveWithLabels(time.Since(now).Milliseconds()/1000, map[string]string{"name": name, "code": strconv.Itoa(mrw.statusCode)})
} else if enabled {
metricHTTPReqCounter().AddWithLabel(1, map[string]string{"name": name, "code": strconv.Itoa(mrw.statusCode), "method": r.Method})
metricHTTPReqDuration().ObserveWithLabels(time.Since(now).Milliseconds(), map[string]string{"name": name, "code": strconv.Itoa(mrw.statusCode), "method": r.Method})
diff --git a/api/metrics_test.go b/api/metrics_test.go
index 1d486188a..b7b1b7e0d 100644
--- a/api/metrics_test.go
+++ b/api/metrics_test.go
@@ -77,7 +77,7 @@ func TestMetricsMiddleware(t *testing.T) {
assert.Equal(t, "method", labels[1].GetName())
assert.Equal(t, "GET", labels[1].GetValue())
assert.Equal(t, "name", labels[2].GetName())
- assert.Equal(t, "accounts_get_account", labels[2].GetValue())
+ assert.Equal(t, "GET /accounts/{address}", labels[2].GetValue())
labels = m[1].GetLabel()
assert.Equal(t, 3, len(labels))
@@ -86,7 +86,7 @@ func TestMetricsMiddleware(t *testing.T) {
assert.Equal(t, "method", labels[1].GetName())
assert.Equal(t, "GET", labels[1].GetValue())
assert.Equal(t, "name", labels[2].GetName())
- assert.Equal(t, "accounts_get_account", labels[2].GetValue())
+ assert.Equal(t, "GET /accounts/{address}", labels[2].GetValue())
labels = m[2].GetLabel()
assert.Equal(t, 3, len(labels))
@@ -95,7 +95,7 @@ func TestMetricsMiddleware(t *testing.T) {
assert.Equal(t, "method", labels[1].GetName())
assert.Equal(t, "GET", labels[1].GetValue())
assert.Equal(t, "name", labels[2].GetName())
- assert.Equal(t, "accounts_get_account", labels[2].GetValue())
+ assert.Equal(t, "GET /accounts/{address}", labels[2].GetValue())
}
func TestWebsocketMetrics(t *testing.T) {
@@ -120,13 +120,13 @@ func TestWebsocketMetrics(t *testing.T) {
metrics, err := parser.TextToMetricFamilies(bytes.NewReader(body))
assert.Nil(t, err)
- m := metrics["thor_metrics_api_active_websocket_count"].GetMetric()
+ m := metrics["thor_metrics_api_active_websocket_gauge"].GetMetric()
assert.Equal(t, 1, len(m), "should be 1 metric entries")
assert.Equal(t, float64(1), m[0].GetGauge().GetValue())
labels := m[0].GetLabel()
- assert.Equal(t, "subject", labels[0].GetName())
- assert.Equal(t, "beat", labels[0].GetValue())
+ assert.Equal(t, "name", labels[0].GetName())
+ assert.Equal(t, "WS /subscriptions/beat", labels[0].GetValue())
// initiate 1 beat subscription, active websocket should be 2
conn2, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
@@ -137,7 +137,7 @@ func TestWebsocketMetrics(t *testing.T) {
metrics, err = parser.TextToMetricFamilies(bytes.NewReader(body))
assert.Nil(t, err)
- m = metrics["thor_metrics_api_active_websocket_count"].GetMetric()
+ m = metrics["thor_metrics_api_active_websocket_gauge"].GetMetric()
assert.Equal(t, 1, len(m), "should be 1 metric entries")
assert.Equal(t, float64(2), m[0].GetGauge().GetValue())
@@ -151,16 +151,16 @@ func TestWebsocketMetrics(t *testing.T) {
metrics, err = parser.TextToMetricFamilies(bytes.NewReader(body))
assert.Nil(t, err)
- m = metrics["thor_metrics_api_active_websocket_count"].GetMetric()
+ m = metrics["thor_metrics_api_active_websocket_gauge"].GetMetric()
assert.Equal(t, 2, len(m), "should be 2 metric entries")
// both m[0] and m[1] should have the value of 1
assert.Equal(t, float64(2), m[0].GetGauge().GetValue())
assert.Equal(t, float64(1), m[1].GetGauge().GetValue())
- // m[1] should have the subject of block
+ // m[1] should have the name of block
labels = m[1].GetLabel()
- assert.Equal(t, "subject", labels[0].GetName())
- assert.Equal(t, "block", labels[0].GetValue())
+ assert.Equal(t, "name", labels[0].GetName())
+ assert.Equal(t, "WS /subscriptions/block", labels[0].GetValue())
}
func httpGet(t *testing.T, url string) ([]byte, int) {
diff --git a/api/node/node.go b/api/node/node.go
index 11c1ce7e1..21c69dcdf 100644
--- a/api/node/node.go
+++ b/api/node/node.go
@@ -35,6 +35,6 @@ func (n *Node) Mount(root *mux.Router, pathPrefix string) {
sub.Path("/network/peers").
Methods(http.MethodGet).
- Name("node_get_peers").
+ Name("GET /node/network/peers").
HandlerFunc(utils.WrapHandlerFunc(n.handleNetwork))
}
diff --git a/api/transactions/transactions.go b/api/transactions/transactions.go
index 7616a5d61..e4846bbde 100644
--- a/api/transactions/transactions.go
+++ b/api/transactions/transactions.go
@@ -218,14 +218,14 @@ func (t *Transactions) Mount(root *mux.Router, pathPrefix string) {
sub.Path("").
Methods(http.MethodPost).
- Name("transactions_send_tx").
+ Name("POST /transactions").
HandlerFunc(utils.WrapHandlerFunc(t.handleSendTransaction))
sub.Path("/{id}").
Methods(http.MethodGet).
- Name("transactions_get_tx").
+ Name("GET /transactions/{id}").
HandlerFunc(utils.WrapHandlerFunc(t.handleGetTransactionByID))
sub.Path("/{id}/receipt").
Methods(http.MethodGet).
- Name("transactions_get_receipt").
+ Name("GET /transactions/{id}/receipt").
HandlerFunc(utils.WrapHandlerFunc(t.handleGetTransactionReceiptByID))
}
diff --git a/api/transactions/transactions_benchmark_test.go b/api/transactions/transactions_benchmark_test.go
new file mode 100644
index 000000000..be8080455
--- /dev/null
+++ b/api/transactions/transactions_benchmark_test.go
@@ -0,0 +1,530 @@
+// Copyright (c) 2024 The VeChainThor developers
+
+// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
+// file LICENSE or
+
+package transactions
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+ "fmt"
+ "math"
+ "math/big"
+ "path/filepath"
+ "runtime/debug"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/elastic/gosigar"
+ "github.com/ethereum/go-ethereum/common/fdlimit"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/require"
+ "github.com/vechain/thor/v2/block"
+ "github.com/vechain/thor/v2/chain"
+ "github.com/vechain/thor/v2/cmd/thor/solo"
+ "github.com/vechain/thor/v2/genesis"
+ "github.com/vechain/thor/v2/logdb"
+ "github.com/vechain/thor/v2/muxdb"
+ "github.com/vechain/thor/v2/packer"
+ "github.com/vechain/thor/v2/state"
+ "github.com/vechain/thor/v2/test/datagen"
+ "github.com/vechain/thor/v2/test/testchain"
+ "github.com/vechain/thor/v2/thor"
+ "github.com/vechain/thor/v2/tx"
+ "github.com/vechain/thor/v2/txpool"
+)
+
+var (
+ cachedAccounts []genesis.DevAccount
+ once sync.Once
+ blockCount = 1_000
+)
+
+func getCachedAccounts(b *testing.B) []genesis.DevAccount {
+ once.Do(func() {
+ now := time.Now()
+ cachedAccounts = createAccounts(b, 10_000)
+ b.Logf("Created accounts in: %f secs", time.Since(now).Seconds())
+ })
+ return cachedAccounts
+}
+
+func BenchmarkFetchTx_RealDB_RandomSigners_ManyClausesPerTx(b *testing.B) {
+ // create state accounts
+ accounts := getCachedAccounts(b)
+
+ // randomly pick a signer for signing the transactions
+ randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx)
+
+ // create test db - will be automagically removed when the benchmark ends
+ db, err := openTempMainDB(b.TempDir())
+ require.NoError(b, err)
+
+ // create blocks
+ newChain, transactions := createPackedChain(b, db, blockCount, accounts, randomSignerFunc)
+
+ // shuffle the transaction into a randomized order
+ randomizedTransactions := shuffleSlice(transactions)
+ b.Logf("About to process %d txs", len(randomizedTransactions))
+
+ // run the benchmarks
+ b.Run("getTransaction", func(b *testing.B) {
+ benchmarkGetTransaction(b, newChain, randomizedTransactions)
+ })
+
+ b.Run("getReceipt", func(b *testing.B) {
+ benchmarkGetReceipt(b, newChain, randomizedTransactions)
+ })
+}
+
+func BenchmarkFetchTx_RealDB_RandomSigners_OneClausePerTx(b *testing.B) {
+ // create state accounts
+ accounts := getCachedAccounts(b)
+
+ // randomly pick a signer for signing the transactions
+ randomSignerFunc := randomPickSignerFunc(accounts, createOneClausePerTx)
+
+ // create test db - will be automagically removed when the benchmark ends
+ db, err := openTempMainDB(b.TempDir())
+ require.NoError(b, err)
+
+ // create blocks
+ newChain, transactions := createPackedChain(b, db, blockCount, accounts, randomSignerFunc)
+
+ // shuffle the transaction into a randomized order
+ randomizedTransactions := shuffleSlice(transactions)
+ b.Logf("About to process %d txs", len(randomizedTransactions))
+
+ // run the benchmarks
+ b.Run("getTransaction", func(b *testing.B) {
+ benchmarkGetTransaction(b, newChain, randomizedTransactions)
+ })
+
+ b.Run("getReceipt", func(b *testing.B) {
+ benchmarkGetReceipt(b, newChain, randomizedTransactions)
+ })
+}
+
+func BenchmarkFetchTx_RandomSigners_ManyClausesPerTx(b *testing.B) {
+ // create state accounts
+ accounts := getCachedAccounts(b)
+
+ // randomly pick a signer for signing the transactions
+ randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx)
+
+ // create blocks
+ newChain, transactions := createPackedChain(b, muxdb.NewMem(), blockCount, accounts, randomSignerFunc)
+
+ // shuffle the transaction into a randomized order
+ randomizedTransactions := shuffleSlice(transactions)
+ b.Logf("About to process %d txs", len(randomizedTransactions))
+
+ // run the benchmarks
+ b.Run("getTransaction", func(b *testing.B) {
+ benchmarkGetTransaction(b, newChain, randomizedTransactions)
+ })
+
+ b.Run("getReceipt", func(b *testing.B) {
+ benchmarkGetReceipt(b, newChain, randomizedTransactions)
+ })
+}
+
+func BenchmarkFetchTx_RandomSigners_OneClausePerTx(b *testing.B) {
+ // Setup phase: Not part of the benchmark timing
+ b.StopTimer()
+
+ // create state accounts
+ accounts := getCachedAccounts(b)
+
+ // randomly pick a signer for signing the transactions
+ randomSignerFunc := randomPickSignerFunc(accounts, createOneClausePerTx)
+
+ // create blocks
+ newChain, transactions := createPackedChain(b, muxdb.NewMem(), blockCount, accounts, randomSignerFunc)
+
+ // shuffle the transaction into a randomized order
+ randomizedTransactions := shuffleSlice(transactions)
+ b.Logf("About to process %d txs", len(randomizedTransactions))
+
+ // run the benchmarks
+ b.Run("getTransaction", func(b *testing.B) {
+ benchmarkGetTransaction(b, newChain, randomizedTransactions)
+ })
+
+ b.Run("getReceipt", func(b *testing.B) {
+ benchmarkGetReceipt(b, newChain, randomizedTransactions)
+ })
+}
+
+func benchmarkGetTransaction(b *testing.B, thorChain *testchain.Chain, randTxs tx.Transactions) {
+ mempool := txpool.New(thorChain.Repo(), thorChain.Stater(), txpool.Options{Limit: 10, LimitPerAccount: 16, MaxLifetime: 10 * time.Minute})
+ transactionAPI := New(thorChain.Repo(), mempool)
+ head := thorChain.Repo().BestBlockSummary().Header.ID()
+ var err error
+
+ // Measure memory usage
+ b.ReportAllocs()
+
+ // Benchmark execution
+ b.ResetTimer()
+
+ for _, randTx := range randTxs {
+ _, err = transactionAPI.getRawTransaction(randTx.ID(), head, false)
+ if err != nil {
+ b.Fatalf("getRawTransaction failed: %v", err)
+ }
+ }
+}
+
+func benchmarkGetReceipt(b *testing.B, thorChain *testchain.Chain, randTxs tx.Transactions) {
+ mempool := txpool.New(thorChain.Repo(), thorChain.Stater(), txpool.Options{Limit: 10, LimitPerAccount: 16, MaxLifetime: 10 * time.Minute})
+ transactionAPI := New(thorChain.Repo(), mempool)
+ head := thorChain.Repo().BestBlockSummary().Header.ID()
+ var err error
+
+ // Measure memory usage
+ b.ReportAllocs()
+
+ // Benchmark execution
+ b.ResetTimer()
+
+ for _, randTx := range randTxs {
+ _, err = transactionAPI.getTransactionReceiptByID(randTx.ID(), head)
+ if err != nil {
+ b.Fatalf("getTransactionReceiptByID failed: %v", err)
+ }
+ }
+}
+
+func createPackedChain(b *testing.B, db *muxdb.MuxDB, noBlocks int, accounts []genesis.DevAccount, createTxFunc func(chain *testchain.Chain) (tx.Transactions, error)) (*testchain.Chain, tx.Transactions) {
+ proposer := &accounts[0]
+
+ // mock a fake chain for block production
+ fakeChain, err := createChain(db, accounts)
+ require.NoError(b, err)
+
+ // pre-alloc blocks
+ var transactions tx.Transactions
+
+ // Start from the Genesis block
+ previousBlock := fakeChain.GenesisBlock()
+ for i := 0; i < noBlocks; i++ {
+ newTxs, err := createTxFunc(fakeChain)
+ require.NoError(b, err)
+ previousBlock, err = packTxsIntoBlock(
+ fakeChain,
+ proposer,
+ previousBlock,
+ newTxs,
+ )
+ require.NoError(b, err)
+ transactions = append(transactions, newTxs...)
+ }
+
+ return fakeChain, transactions
+}
+
+func createOneClausePerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error) {
+ var transactions tx.Transactions
+ gasUsed := uint64(0)
+ for gasUsed < 9_500_000 {
+ toAddr := datagen.RandAddress()
+ cla := tx.NewClause(&toAddr).WithValue(big.NewInt(10000))
+ transaction := new(tx.Builder).
+ ChainTag(thorChain.Repo().ChainTag()).
+ GasPriceCoef(1).
+ Expiration(math.MaxUint32 - 1).
+ Gas(21_000).
+ Nonce(uint64(datagen.RandInt())).
+ Clause(cla).
+ BlockRef(tx.NewBlockRef(0)).
+ Build()
+
+ sig, err := crypto.Sign(transaction.SigningHash().Bytes(), signerPK)
+ if err != nil {
+ return nil, err
+ }
+ transaction = transaction.WithSignature(sig)
+
+ gasUsed += 21_000 // Gas per transaction
+ transactions = append(transactions, transaction)
+ }
+ return transactions, nil
+}
+
+func createManyClausesPerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error) {
+ var transactions tx.Transactions
+ gasUsed := uint64(0)
+ txGas := uint64(42_000)
+
+ transactionBuilder := new(tx.Builder).
+ ChainTag(thorChain.Repo().ChainTag()).
+ GasPriceCoef(1).
+ Expiration(math.MaxUint32 - 1).
+ Nonce(uint64(datagen.RandInt())).
+ BlockRef(tx.NewBlockRef(0))
+
+ for ; gasUsed < 9_500_000; gasUsed += txGas {
+ toAddr := datagen.RandAddress()
+ transactionBuilder.Clause(tx.NewClause(&toAddr).WithValue(big.NewInt(10000)))
+ }
+
+ transaction := transactionBuilder.Gas(gasUsed).Build()
+
+ sig, err := crypto.Sign(transaction.SigningHash().Bytes(), signerPK)
+ if err != nil {
+ return nil, err
+ }
+ transaction = transaction.WithSignature(sig)
+
+ transactions = append(transactions, transaction)
+
+ return transactions, nil
+}
+
+func packTxsIntoBlock(thorChain *testchain.Chain, proposerAccount *genesis.DevAccount, parentBlk *block.Block, transactions tx.Transactions) (*block.Block, error) {
+ p := packer.New(thorChain.Repo(), thorChain.Stater(), proposerAccount.Address, &proposerAccount.Address, thorChain.GetForkConfig())
+
+ parentSum, err := thorChain.Repo().GetBlockSummary(parentBlk.Header().ID())
+ if err != nil {
+ return nil, err
+ }
+
+ flow, err := p.Schedule(parentSum, parentBlk.Header().Timestamp()+1)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, transaction := range transactions {
+ err = flow.Adopt(transaction)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ b1, stage, receipts, err := flow.Pack(proposerAccount.PrivateKey, 0, false)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, err := stage.Commit(); err != nil {
+ return nil, err
+ }
+
+ if err := thorChain.Repo().AddBlock(b1, receipts, 0, true); err != nil {
+ return nil, err
+ }
+
+ return b1, nil
+}
+
+func createChain(db *muxdb.MuxDB, accounts []genesis.DevAccount) (*testchain.Chain, error) {
+ forkConfig := thor.NoFork
+ forkConfig.VIP191 = 1
+ forkConfig.BLOCKLIST = 0
+ forkConfig.VIP214 = 2
+
+ // Create the state manager (Stater) with the initialized database.
+ stater := state.NewStater(db)
+
+ authAccs := make([]genesis.Authority, 0, len(accounts))
+ stateAccs := make([]genesis.Account, 0, len(accounts))
+
+ for _, acc := range accounts {
+ authAccs = append(authAccs, genesis.Authority{
+ MasterAddress: acc.Address,
+ EndorsorAddress: acc.Address,
+ Identity: thor.BytesToBytes32([]byte("master")),
+ })
+ bal, _ := new(big.Int).SetString("1000000000000000000000000000", 10)
+ stateAccs = append(stateAccs, genesis.Account{
+ Address: acc.Address,
+ Balance: (*genesis.HexOrDecimal256)(bal),
+ Energy: (*genesis.HexOrDecimal256)(bal),
+ Code: "",
+ Storage: nil,
+ })
+ }
+ mbp := uint64(1_000)
+ genConfig := genesis.CustomGenesis{
+ LaunchTime: 1526400000,
+ GasLimit: thor.InitialGasLimit,
+ ExtraData: "",
+ ForkConfig: &forkConfig,
+ Authority: authAccs,
+ Accounts: stateAccs,
+ Params: genesis.Params{
+ MaxBlockProposers: &mbp,
+ },
+ }
+
+ builder, err := genesis.NewCustomNet(&genConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize the genesis and retrieve the genesis block
+ //gene := genesis.NewDevnet()
+ geneBlk, _, _, err := builder.Build(stater)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the repository which manages chain data, using the database and genesis block.
+ repo, err := chain.NewRepository(db, geneBlk)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create an inMemory logdb
+ logDb, err := logdb.NewMem()
+ if err != nil {
+ return nil, err
+ }
+
+ return testchain.New(
+ db,
+ builder,
+ solo.NewBFTEngine(repo),
+ repo,
+ stater,
+ geneBlk,
+ logDb,
+ thor.NoFork,
+ ), nil
+}
+
+func randomPickSignerFunc(
+ accounts []genesis.DevAccount,
+ createTxFun func(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error),
+) func(chain *testchain.Chain) (tx.Transactions, error) {
+ return func(chain *testchain.Chain) (tx.Transactions, error) {
+ // Ensure there are accounts available
+ if len(accounts) == 0 {
+ return nil, fmt.Errorf("no accounts available to pick a random sender")
+ }
+
+ // Securely pick a random index
+ maxLen := big.NewInt(int64(len(accounts)))
+ randomIndex, err := rand.Int(rand.Reader, maxLen)
+ if err != nil {
+ return nil, fmt.Errorf("failed to generate random index: %v", err)
+ }
+
+ // Use the selected account to create transactions
+ sender := accounts[randomIndex.Int64()]
+ return createTxFun(sender.PrivateKey, chain)
+ }
+}
+
+func createAccounts(b *testing.B, accountNo int) []genesis.DevAccount {
+ var accs []genesis.DevAccount
+
+ for i := 0; i < accountNo; i++ {
+ pk, err := crypto.GenerateKey()
+ require.NoError(b, err)
+ addr := crypto.PubkeyToAddress(pk.PublicKey)
+ accs = append(accs, genesis.DevAccount{Address: thor.Address(addr), PrivateKey: pk})
+ }
+
+ return accs
+}
+
+func openTempMainDB(dir string) (*muxdb.MuxDB, error) {
+ cacheMB := normalizeCacheSize(4096)
+
+ fdCache := suggestFDCache()
+
+ opts := muxdb.Options{
+ TrieNodeCacheSizeMB: cacheMB,
+ TrieCachedNodeTTL: 30, // 5min
+ TrieDedupedPartitionFactor: math.MaxUint32,
+ TrieWillCleanHistory: true,
+ OpenFilesCacheCapacity: fdCache,
+ ReadCacheMB: 256, // rely on os page cache other than huge db read cache.
+ WriteBufferMB: 128,
+ }
+
+ // go-ethereum stuff
+ // Ensure Go's GC ignores the database cache for trigger percentage
+ totalCacheMB := cacheMB + opts.ReadCacheMB + opts.WriteBufferMB*2
+ gogc := math.Max(10, math.Min(100, 50/(float64(totalCacheMB)/1024)))
+
+ debug.SetGCPercent(int(gogc))
+
+ if opts.TrieWillCleanHistory {
+ opts.TrieHistPartitionFactor = 256
+ } else {
+ opts.TrieHistPartitionFactor = 524288
+ }
+
+ db, err := muxdb.Open(filepath.Join(dir, "maindb"), &opts)
+ if err != nil {
+ return nil, errors.Wrapf(err, "open main database [%v]", dir)
+ }
+ return db, nil
+}
+
+func normalizeCacheSize(sizeMB int) int {
+ if sizeMB < 128 {
+ sizeMB = 128
+ }
+
+ var mem gosigar.Mem
+ if err := mem.Get(); err != nil {
+ fmt.Println("failed to get total mem:", "err", err)
+ } else {
+ total := int(mem.Total / 1024 / 1024)
+ half := total / 2
+
+ // limit to not less than total/2 and up to total-2GB
+ limitMB := total - 2048
+ if limitMB < half {
+ limitMB = half
+ }
+
+ if sizeMB > limitMB {
+ sizeMB = limitMB
+ fmt.Println("cache size(MB) limited", "limit", limitMB)
+ }
+ }
+ return sizeMB
+}
+
+func suggestFDCache() int {
+ limit, err := fdlimit.Current()
+ if err != nil {
+ fmt.Println("unable to get fdlimit", "error", err)
+ return 500
+ }
+ if limit <= 1024 {
+ fmt.Println("low fd limit, increase it if possible", "limit", limit)
+ }
+
+ n := limit / 2
+ if n > 5120 {
+ return 5120
+ }
+ return n
+}
+
+func shuffleSlice(slice tx.Transactions) tx.Transactions {
+ shuffled := make(tx.Transactions, len(slice))
+ copy(shuffled, slice)
+
+ for i := len(shuffled) - 1; i > 0; i-- {
+ n, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
+ if err != nil {
+ panic(err) // Handle errors appropriately in real code
+ }
+
+ // Swap the current element with the random index
+ j := int(n.Int64())
+ shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
+ }
+
+ return shuffled
+}
diff --git a/api/transactions/types.go b/api/transactions/types.go
index 7c3a892ac..93cae1660 100644
--- a/api/transactions/types.go
+++ b/api/transactions/types.go
@@ -89,8 +89,9 @@ func convertTransaction(tx *tx.Transaction, header *block.Header) *Transaction {
origin, _ := tx.Origin()
delegator, _ := tx.Delegator()
- cls := make(Clauses, len(tx.Clauses()))
- for i, c := range tx.Clauses() {
+ txClauses := tx.Clauses()
+ cls := make(Clauses, len(txClauses))
+ for i, c := range txClauses {
cls[i] = convertClause(c)
}
br := tx.BlockRef()
@@ -187,9 +188,10 @@ func convertReceipt(txReceipt *tx.Receipt, header *block.Header, tx *tx.Transact
origin,
},
}
+ txClauses := tx.Clauses()
receipt.Outputs = make([]*Output, len(txReceipt.Outputs))
for i, output := range txReceipt.Outputs {
- clause := tx.Clauses()[i]
+ clause := txClauses[i]
var contractAddr *thor.Address
if clause.To() == nil {
cAddr := thor.CreateContractAddress(tx.ID(), uint32(i), 0)
diff --git a/api/transfers/transfers.go b/api/transfers/transfers.go
index 2a6cbfb9e..a036f30bd 100644
--- a/api/transfers/transfers.go
+++ b/api/transfers/transfers.go
@@ -94,6 +94,6 @@ func (t *Transfers) Mount(root *mux.Router, pathPrefix string) {
sub.Path("").
Methods(http.MethodPost).
- Name("logs_filter_transfer").
+ Name("POST /logs/transfer").
HandlerFunc(utils.WrapHandlerFunc(t.handleFilterTransferLogs))
}
diff --git a/builtin/gen/bindata.go b/builtin/gen/bindata.go
index 8f1ffbc5d..c0724a53d 100644
--- a/builtin/gen/bindata.go
+++ b/builtin/gen/bindata.go
@@ -1,4 +1,4 @@
-// Package gen Code generated by go-bindata. (@generated) DO NOT EDIT.
+// Code generated by go-bindata. DO NOT EDIT.
// sources:
// compiled/Authority.abi
// compiled/Authority.bin-runtime
@@ -76,32 +76,21 @@ type bindataFileInfo struct {
modTime time.Time
}
-// Name return file name
func (fi bindataFileInfo) Name() string {
return fi.name
}
-
-// Size return file size
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
-
-// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
-
-// Mode return file modify time
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
-
-// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool {
- return fi.mode&os.ModeDir != 0
+ return false
}
-
-// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
@@ -794,11 +783,13 @@ var _bindata = map[string]func() (*asset, error){
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
-// data/
-// foo.txt
-// img/
-// a.png
-// b.png
+//
+// data/
+// foo.txt
+// img/
+// a.png
+// b.png
+//
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
diff --git a/builtin/gen/gen.go b/builtin/gen/gen.go
index d08c2f179..ce5fed8e1 100644
--- a/builtin/gen/gen.go
+++ b/builtin/gen/gen.go
@@ -6,5 +6,6 @@
package gen
//go:generate rm -rf ./compiled/
-//go:generate solc --optimize-runs 200 --overwrite --bin-runtime --abi -o ./compiled authority.sol energy.sol executor.sol extension.sol extension-v2.sol measure.sol params.sol prototype.sol
-//go:generate go-bindata -nometadata -ignore=_ -pkg gen -o bindata.go compiled/
+//go:generate docker run -v ./:/solidity ethereum/solc:0.4.24 --optimize-runs 200 --overwrite --bin-runtime --abi -o /solidity/compiled authority.sol energy.sol executor.sol extension.sol extension-v2.sol measure.sol params.sol prototype.sol
+//go:generate go run github.com/go-bindata/go-bindata/go-bindata@v1.0.0 -nometadata -ignore=_ -pkg gen -o bindata.go compiled/
+//go:generate go fmt
diff --git a/chain/repository.go b/chain/repository.go
index 5fe131444..135e28cf2 100644
--- a/chain/repository.go
+++ b/chain/repository.go
@@ -56,6 +56,8 @@ type Repository struct {
caches struct {
summaries *cache
+ txs *cache
+ receipts *cache
}
}
@@ -81,6 +83,9 @@ func NewRepository(db *muxdb.MuxDB, genesis *block.Block) (*Repository, error) {
}
repo.caches.summaries = newCache(512)
+ repo.caches.txs = newCache(2048)
+ repo.caches.receipts = newCache(2048)
+
if val, err := repo.propStore.Get(bestBlockIDKey); err != nil {
if !repo.propStore.IsNotFound(err) {
return nil, err
@@ -170,6 +175,7 @@ func (r *Repository) saveBlock(block *block.Block, receipts tx.Receipts, conflic
if err := saveRLP(bodyPutter, keyBuf[:], tx); err != nil {
return nil, err
}
+ r.caches.txs.Add(string(keyBuf), tx)
}
// save receipts
@@ -178,6 +184,7 @@ func (r *Repository) saveBlock(block *block.Block, receipts tx.Receipts, conflic
if err := saveRLP(bodyPutter, keyBuf, receipt); err != nil {
return nil, err
}
+ r.caches.receipts.Add(string(keyBuf), receipt)
}
}
if err := indexChainHead(headPutter, header); err != nil {
@@ -287,8 +294,18 @@ func (r *Repository) GetBlockSummary(id thor.Bytes32) (summary *BlockSummary, er
}
func (r *Repository) getTransaction(key []byte) (*tx.Transaction, error) {
+ trx, err := r.caches.txs.GetOrLoad(string(key), func() (interface{}, error) {
+ return loadTransaction(r.bodyStore, key)
+ })
+ if err != nil {
+ return nil, err
+ }
+ return trx.(*tx.Transaction), nil
+}
+
+func loadTransaction(r kv.Getter, key []byte) (*tx.Transaction, error) {
var tx tx.Transaction
- if err := loadRLP(r.bodyStore, key, &tx); err != nil {
+ if err := loadRLP(r, key[:], &tx); err != nil {
return nil, err
}
return &tx, nil
@@ -330,8 +347,18 @@ func (r *Repository) GetBlock(id thor.Bytes32) (*block.Block, error) {
}
func (r *Repository) getReceipt(key []byte) (*tx.Receipt, error) {
+ cached, err := r.caches.receipts.GetOrLoad(string(key), func() (interface{}, error) {
+ return loadReceipt(r.bodyStore, key)
+ })
+ if err != nil {
+ return nil, err
+ }
+ return cached.(*tx.Receipt), nil
+}
+
+func loadReceipt(r kv.Getter, key []byte) (*tx.Receipt, error) {
var receipt tx.Receipt
- if err := loadRLP(r.bodyStore, key, &receipt); err != nil {
+ if err := loadRLP(r, key[:], &receipt); err != nil {
return nil, err
}
return &receipt, nil
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 407dc0b2a..e5a45b559 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -1,7 +1,7 @@
# Contributing to VechainThor
Welcome to VechainThor! We appreciate your interest in contributing. By participating in this project, you agree to
-abide by our [Code of Conduct](https://github.com/vechain/thor/blob/master/CODE_OF_CONDUCT.md).
+abide by our [Code of Conduct](https://github.com/vechain/thor/blob/master/docs/CODE_OF_CONDUCT.md).
## VeChain Improvement Proposals (VIPs)
diff --git a/docs/hosting-a-node.md b/docs/hosting-a-node.md
index 1dc1e12ce..6212d7360 100644
--- a/docs/hosting-a-node.md
+++ b/docs/hosting-a-node.md
@@ -21,7 +21,7 @@ state, including the disk space required for various node types.
### Command Line Options
-Please refer to [Command Line Options](./usage.md#command-line-options) in the usage documentation to see a list of all
+Please refer to [Command Line Options](https://github.com/vechain/thor/blob/master/docs/usage.md#command-line-options) in the usage documentation to see a list of all
available options.
---
diff --git a/docs/usage.md b/docs/usage.md
index 7358e3f0e..3a3b7694a 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -20,7 +20,7 @@ ___
### Running from source
-- To install the `thor` binary, follow the instructions in the [build](build) guide.
+- To install the `thor` binary, follow the instructions in the [build](https://github.com/vechain/thor/blob/master/docs/build.md) guide.
Connect to vechain's mainnet:
@@ -47,7 +47,7 @@ ___
### Running a discovery node
-- To install the `disco` binary, follow the instructions in the [build](build) guide.
+- To install the `disco` binary, follow the instructions in the [build](https://github.com/vechain/thor/blob/master/docs/build.md) guide.
Start a discovery node:
diff --git a/go.mod b/go.mod
index 2ae96305e..52ca3fc14 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.22
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c
- github.com/pkg/errors v0.8.0
+ github.com/pkg/errors v0.8.1-0.20171216070316-e881fd58d78e
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
@@ -28,7 +28,7 @@ require (
github.com/stretchr/testify v1.8.4
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a
github.com/vechain/go-ecvrf v0.0.0-20220525125849-96fa0442e765
- golang.org/x/crypto v0.21.0
+ golang.org/x/crypto v0.22.0
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v3 v3.0.1
@@ -38,6 +38,7 @@ require (
github.com/aristanetworks/goarista v0.0.0-20180222005525-c41ed3986faa // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 // indirect
+ github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/cespare/cp v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
@@ -47,17 +48,16 @@ require (
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-stack/stack v1.7.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/huin/goupnp v0.0.0-20171109214107-dceda08e705b // indirect
- github.com/jackpal/go-nat-pmp v1.0.1 // indirect
+ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 // indirect
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
- golang.org/x/net v0.23.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/net v0.24.0 // indirect
+ golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951 // indirect
@@ -65,4 +65,4 @@ require (
replace github.com/syndtr/goleveldb => github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655
-replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20240528020007-2994c2a24b9c
+replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2
diff --git a/go.sum b/go.sum
index 148c5c433..b0085450f 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,8 @@ 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/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -23,6 +25,7 @@ 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.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
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=
@@ -84,8 +87,8 @@ github.com/huin/goupnp v0.0.0-20171109214107-dceda08e705b h1:mvnS3LbcRgdM4nBLksE
github.com/huin/goupnp v0.0.0-20171109214107-dceda08e705b/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA=
-github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
+github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@@ -119,12 +122,13 @@ github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3
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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c h1:MUyE44mTvnI5A0xrxIxaMqoWFzPfQvtE2IWUollMDMs=
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
-github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1-0.20171216070316-e881fd58d78e h1:osn9cOzd93npXpRuTFR/MPjiTvTSNHA7pqbXkPyLqQ4=
+github.com/pkg/errors v0.8.1-0.20171216070316-e881fd58d78e/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
@@ -152,8 +156,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vechain/go-ecvrf v0.0.0-20220525125849-96fa0442e765 h1:jvr+TSivjObZmOKVdqlgeLtRhaDG27gE39PMuE2IJ24=
github.com/vechain/go-ecvrf v0.0.0-20220525125849-96fa0442e765/go.mod h1:cwnTMgAVzMb30xMKnGI1LdU1NjMiPllYb7i3ibj/fzE=
-github.com/vechain/go-ethereum v1.8.15-0.20240528020007-2994c2a24b9c h1:YfGsGXMNKI64gR76KumYgGnYSdAFtMA8igtmpFiBt74=
-github.com/vechain/go-ethereum v1.8.15-0.20240528020007-2994c2a24b9c/go.mod h1:EhX+lSkpNdEIxu1zOXtiFZu5nv1i8MX1mQA/qhUE+gw=
+github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2 h1:ch3DqXvl1ApfJut768bf5Vlhqtw+bxAWTyPDYXQkQZk=
+github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2/go.mod h1:yPUCNmntAh1PritrMfSi7noK+9vVPStZX3wgh3ieaY0=
github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655 h1:CbHcWpCi7wOYfpoErRABh3Slyq9vO0Ay/EHN5GuJSXQ=
github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -162,8 +166,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -176,8 +180,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -201,8 +205,8 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/metrics/noop.go b/metrics/noop.go
index d07f38f2e..b804486b6 100644
--- a/metrics/noop.go
+++ b/metrics/noop.go
@@ -52,4 +52,4 @@ func (n noopMeters) Observe(int64) {}
func (n *noopMetrics) ObserveWithLabels(int64, map[string]string) {}
-func (n *noopMetrics) collectDiskIO(_ time.Duration) {}
+func (n *noopMetrics) collectDiskIO(time.Duration) {}
diff --git a/metrics/telemetry.go b/metrics/telemetry.go
index 9ab3bd633..1d1ee96f2 100644
--- a/metrics/telemetry.go
+++ b/metrics/telemetry.go
@@ -5,7 +5,10 @@
package metrics
-import "net/http"
+import (
+ "net/http"
+ "sync"
+)
// metrics is a singleton service that provides global access to a set of meters
// it wraps multiple implementations and defaults to a no-op implementation
@@ -30,7 +33,11 @@ func HTTPHandler() http.Handler {
// Define standard buckets for histograms
var (
Bucket10s = []int64{0, 500, 1000, 2000, 3000, 4000, 5000, 7500, 10_000}
- BucketHTTPReqs = []int64{0, 150, 300, 450, 600, 900, 1200, 1500, 3000}
+ BucketHTTPReqs = []int64{
+ 0, 1, 2, 5, 10, 20, 30, 50, 75, 100,
+ 150, 200, 300, 400, 500, 750, 1000,
+ 1500, 2000, 3000, 4000, 5000, 10000,
+ }
)
// HistogramMeter represents the type of metric that is calculated by aggregating
@@ -96,12 +103,11 @@ func GaugeVec(name string, labels []string) GaugeVecMeter {
// - it avoid metrics definition to determine the singleton to use (noop vs prometheus)
func LazyLoad[T any](f func() T) func() T {
var result T
- var loaded bool
+ var once sync.Once
return func() T {
- if !loaded {
+ once.Do(func() {
result = f()
- loaded = true
- }
+ })
return result
}
}