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