From e7ba29707a0863cc2bfe6251e6387fc01f252416 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 22:41:05 +0900 Subject: [PATCH 01/14] feat: calculate gas price by transactions --- serve/methods/gas.go | 62 +++++++++++++++++++++++++++++++++++++ serve/methods/mocks_test.go | 22 +++++++++++++ serve/methods/types.go | 8 +++++ 3 files changed, 92 insertions(+) create mode 100644 serve/methods/gas.go create mode 100644 serve/methods/mocks_test.go create mode 100644 serve/methods/types.go diff --git a/serve/methods/gas.go b/serve/methods/gas.go new file mode 100644 index 0000000..335163a --- /dev/null +++ b/serve/methods/gas.go @@ -0,0 +1,62 @@ +package methods + +import ( + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type gasFeeTotalInfo struct { + Low int64 + High int64 + TotalAmount int64 + TotalCount int64 +} + +func GetGasPricesByTxResults(txs []*types.TxResult) ([]*GasPrice, error) { + gasFeeInfoMap := make(map[string]*gasFeeTotalInfo) + + for _, t := range txs { + var stdTx std.Tx + if err := amino.Unmarshal(t.Tx, &stdTx); err != nil { + continue + } + + gasFeeDenom := stdTx.Fee.GasFee.Denom + gasFeeAmount := stdTx.Fee.GasFee.Amount + + if _, exists := gasFeeInfoMap[gasFeeDenom]; !exists { + gasFeeInfoMap[gasFeeDenom] = &gasFeeTotalInfo{} + } + + if gasFeeInfoMap[gasFeeDenom].Low == 0 || gasFeeInfoMap[gasFeeDenom].Low > gasFeeAmount { + gasFeeInfoMap[gasFeeDenom].Low = gasFeeAmount + } + + if gasFeeInfoMap[gasFeeDenom].High == 0 || gasFeeInfoMap[gasFeeDenom].High < gasFeeAmount { + gasFeeInfoMap[gasFeeDenom].High = gasFeeAmount + } + + gasFeeInfoMap[gasFeeDenom].TotalAmount += gasFeeAmount + gasFeeInfoMap[gasFeeDenom].TotalCount++ + } + + gasPrices := make([]*GasPrice, 0) + + for denom, gasFeeInfo := range gasFeeInfoMap { + if gasFeeInfo.TotalCount == 0 { + continue + } + + average := gasFeeInfo.TotalAmount / gasFeeInfo.TotalCount + + gasPrices = append(gasPrices, &GasPrice{ + High: gasFeeInfo.High, + Low: gasFeeInfo.Low, + Average: average, + Denom: denom, + }) + } + + return gasPrices, nil +} diff --git a/serve/methods/mocks_test.go b/serve/methods/mocks_test.go new file mode 100644 index 0000000..ea127c5 --- /dev/null +++ b/serve/methods/mocks_test.go @@ -0,0 +1,22 @@ +package methods + +import ( + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/std" +) + +func makeTxResultHasGasFee(gasFeeAmount int64, gasFeeDenom string) *types.TxResult { + tx := std.Tx{ + Fee: std.Fee{ + GasFee: std.Coin{ + Denom: gasFeeDenom, + Amount: gasFeeAmount, + }, + }, + } + + return &types.TxResult{ + Tx: amino.MustMarshal(tx), + } +} diff --git a/serve/methods/types.go b/serve/methods/types.go new file mode 100644 index 0000000..52fb654 --- /dev/null +++ b/serve/methods/types.go @@ -0,0 +1,8 @@ +package methods + +type GasPrice struct { + Denom string `json:"denom"` + Low int64 `json:"low"` + Average int64 `json:"average"` + High int64 `json:"high"` +} From 5862826993b93c7bfefe2209b2c2968a4c70539f Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 22:41:37 +0900 Subject: [PATCH 02/14] feat: add gas price rpc endpoints --- serve/handlers/gas/gas.go | 124 +++++++++++++++++++++++++++++++ serve/handlers/gas/gas_test.go | 42 +++++++++++ serve/handlers/gas/mocks_test.go | 36 +++++++++ serve/handlers/gas/types.go | 19 +++++ serve/methods/gas_test.go | 124 +++++++++++++++++++++++++++++++ 5 files changed, 345 insertions(+) create mode 100644 serve/handlers/gas/gas.go create mode 100644 serve/handlers/gas/gas_test.go create mode 100644 serve/handlers/gas/mocks_test.go create mode 100644 serve/handlers/gas/types.go create mode 100644 serve/methods/gas_test.go diff --git a/serve/handlers/gas/gas.go b/serve/handlers/gas/gas.go new file mode 100644 index 0000000..056db70 --- /dev/null +++ b/serve/handlers/gas/gas.go @@ -0,0 +1,124 @@ +package gas + +import ( + "fmt" + "math" + "strconv" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/gnolang/tx-indexer/serve/metadata" + "github.com/gnolang/tx-indexer/serve/methods" + "github.com/gnolang/tx-indexer/serve/spec" +) + +const DefaultBlockRangeSize = 1_000 + +type Handler struct { + storage Storage +} + +func NewHandler(storage Storage) *Handler { + return &Handler{ + storage: storage, + } +} + +func (h *Handler) GetGasPriceHandler( + _ *metadata.Metadata, + params []any, +) (any, *spec.BaseJSONError) { + // Check the params + if len(params) != 0 && len(params) != 2 { + return nil, spec.GenerateInvalidParamCountError() + } + + var toBlockNum, fromBlockNum uint64 + + if len(params) == 0 { + latestHeight, err := h.storage.GetLatestHeight() + if err != nil { + return nil, spec.GenerateResponseError(err) + } + + fromBlockNum, toBlockNum = initializeDefaultBlockRangeByHeight(latestHeight) + } else { + fromBlockNum, toBlockNum = parseBlockRangeByParams(params) + } + + response, err := h.getGasPriceBy(fromBlockNum, toBlockNum) + if err != nil { + return nil, spec.GenerateResponseError(err) + } + + return response, nil +} + +func (h *Handler) getGasPriceBy(fromBlockNum, toBlockNum uint64) ([]*methods.GasPrice, error) { + it, err := h. + storage. + TxIterator( + fromBlockNum, + toBlockNum, + 0, + math.MaxUint32, + ) + if err != nil { + return nil, gqlerror.Wrap(err) + } + + defer it.Close() + + txs := make([]*types.TxResult, 0) + + for { + if !it.Next() { + break + } + + tx, itErr := it.Value() + if itErr != nil { + return nil, err + } + + txs = append(txs, tx) + } + + gasPrices, err := methods.GetGasPricesByTxResults(txs) + if err != nil { + return nil, err + } + + return gasPrices, nil +} + +func toUint64(data any) (uint64, error) { + return strconv.ParseUint(fmt.Sprintf("%v", data), 10, 64) +} + +func initializeDefaultBlockRangeByHeight(latestHeight uint64) (uint64, uint64) { + toBlockNum := latestHeight + + var fromBlockNum uint64 + + if latestHeight > DefaultBlockRangeSize { + fromBlockNum = latestHeight - DefaultBlockRangeSize + } + + return fromBlockNum, toBlockNum +} + +func parseBlockRangeByParams(params []any) (uint64, uint64) { + fromBlockNum, err := toUint64(params[0]) + if err != nil { + fromBlockNum = 0 + } + + toBlockNum, err := toUint64(params[1]) + if err != nil { + toBlockNum = 0 + } + + return fromBlockNum, toBlockNum +} diff --git a/serve/handlers/gas/gas_test.go b/serve/handlers/gas/gas_test.go new file mode 100644 index 0000000..56633b4 --- /dev/null +++ b/serve/handlers/gas/gas_test.go @@ -0,0 +1,42 @@ +package gas + +import ( + "testing" + + "github.com/gnolang/tx-indexer/serve/spec" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetTx_InvalidParams(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + params []any + }{ + { + "invalid param length", + []any{1}, + }, + { + "invalid param type", + []any{"totally invalid param type"}, + }, + } + + for _, testCase := range testTable { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + h := NewHandler(&mockStorage{}) + + response, err := h.GetGasPriceHandler(nil, testCase.params) + assert.Nil(t, response) + + require.NotNil(t, err) + + assert.Equal(t, spec.InvalidParamsErrorCode, err.Code) + }) + } +} diff --git a/serve/handlers/gas/mocks_test.go b/serve/handlers/gas/mocks_test.go new file mode 100644 index 0000000..a742c0c --- /dev/null +++ b/serve/handlers/gas/mocks_test.go @@ -0,0 +1,36 @@ +package gas + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/tx-indexer/storage" +) + +type getLatestHeight func() (uint64, error) + +type txIterator func(uint64, uint64, uint32, uint32) (storage.Iterator[*types.TxResult], error) + +type mockStorage struct { + getLatestHeightFn getLatestHeight + txIteratorFn txIterator +} + +func (m *mockStorage) GetLatestHeight() (uint64, error) { + if m.getLatestHeightFn != nil { + return m.getLatestHeightFn() + } + + return 0, nil +} + +func (m *mockStorage) TxIterator( + fromBlockNum, + toBlockNum uint64, + fromTxIndex, + toTxIndex uint32, +) (storage.Iterator[*types.TxResult], error) { + if m.txIteratorFn != nil { + return m.txIteratorFn(fromBlockNum, toBlockNum, fromTxIndex, toTxIndex) + } + + return nil, nil +} diff --git a/serve/handlers/gas/types.go b/serve/handlers/gas/types.go new file mode 100644 index 0000000..9b71a44 --- /dev/null +++ b/serve/handlers/gas/types.go @@ -0,0 +1,19 @@ +package gas + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/tx-indexer/storage" +) + +type Storage interface { + // GetTx returns specified tx from permanent storage + GetLatestHeight() (uint64, error) + + // GetTxByHash fetches the tx using the transaction hash + TxIterator( + fromBlockNum, + toBlockNum uint64, + fromTxIndex, + toTxIndex uint32, + ) (storage.Iterator[*types.TxResult], error) +} diff --git a/serve/methods/gas_test.go b/serve/methods/gas_test.go new file mode 100644 index 0000000..805a50b --- /dev/null +++ b/serve/methods/gas_test.go @@ -0,0 +1,124 @@ +package methods + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetGasPricesByTxResults_EmptyTransactions(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + txs []*types.TxResult + }{ + { + "txs is nil", + nil, + }, + { + "tx is empty", + []*types.TxResult{}, + }, + } + + for _, testCase := range testTable { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + response, err := GetGasPricesByTxResults(testCase.txs) + + assert.Nil(t, err) + + require.NotNil(t, response) + + assert.Equal(t, len(response), 0) + }) + } +} + +func TestGetGasPricesByTxResults_Transactions(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + txs []*types.TxResult + results []GasPrice + }{ + { + "single transaction", + []*types.TxResult{ + makeTxResultHasGasFee(1, "ugnot"), + }, + []GasPrice{ + { + Denom: "ugnot", + High: 1, + Average: 1, + Low: 1, + }, + }, + }, + { + "variable amount", + []*types.TxResult{ + makeTxResultHasGasFee(1, "ugnot"), + makeTxResultHasGasFee(2, "ugnot"), + makeTxResultHasGasFee(3, "ugnot"), + makeTxResultHasGasFee(4, "ugnot"), + }, + []GasPrice{ + { + Denom: "ugnot", + High: 4, + Average: 2, + Low: 1, + }, + }, + }, + { + "variable amounts and coins", + []*types.TxResult{ + makeTxResultHasGasFee(1, "ugnot"), + makeTxResultHasGasFee(2, "ugnot"), + makeTxResultHasGasFee(3, "uatom"), + makeTxResultHasGasFee(4, "uatom"), + }, + []GasPrice{ + { + Denom: "ugnot", + High: 2, + Average: 1, + Low: 1, + }, + { + Denom: "uatom", + High: 4, + Average: 3, + Low: 3, + }, + }, + }, + } + + for _, testCase := range testTable { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + response, err := GetGasPricesByTxResults(testCase.txs) + + assert.Nil(t, err) + require.NotNil(t, response) + + for index, responseItem := range response { + assert.Equal(t, responseItem.Denom, testCase.results[index].Denom) + assert.Equal(t, responseItem.High, testCase.results[index].High) + assert.Equal(t, responseItem.Average, testCase.results[index].Average) + assert.Equal(t, responseItem.Low, testCase.results[index].Low) + } + }) + } +} From f1f3e45bd4aaf4e91a79cb4ac1e9c7bc241a4986 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 22:41:55 +0900 Subject: [PATCH 03/14] feat: add gas price rpc subscription methods --- serve/filters/subscription/gas.go | 45 ++++++++++++++++++++++++++ serve/filters/subscription/gas_test.go | 45 ++++++++++++++++++++++++++ serve/handlers/subs/subs.go | 2 ++ 3 files changed, 92 insertions(+) create mode 100644 serve/filters/subscription/gas.go create mode 100644 serve/filters/subscription/gas_test.go diff --git a/serve/filters/subscription/gas.go b/serve/filters/subscription/gas.go new file mode 100644 index 0000000..fffc2f6 --- /dev/null +++ b/serve/filters/subscription/gas.go @@ -0,0 +1,45 @@ +package subscription + +import ( + "fmt" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/tx-indexer/events" + "github.com/gnolang/tx-indexer/serve/conns" + "github.com/gnolang/tx-indexer/serve/methods" + "github.com/gnolang/tx-indexer/serve/spec" +) + +const ( + NewGasPriceEvent = "newGasPrice" +) + +// GasPriceSubscription is the new-transactions type +// subscription +type GasPriceSubscription struct { + *baseSubscription +} + +func NewGasPriceSubscription(conn conns.WSConnection) *GasPriceSubscription { + return &GasPriceSubscription{ + baseSubscription: newBaseSubscription(conn), + } +} + +func (b *GasPriceSubscription) GetType() events.Type { + return NewGasPriceEvent +} + +func (b *GasPriceSubscription) WriteResponse(id string, data any) error { + txResults, ok := data.([]*types.TxResult) + if !ok { + return fmt.Errorf("unable to cast txResult, %s", data) + } + + gasPrices, err := methods.GetGasPricesByTxResults(txResults) + if err != nil { + return err + } + + return b.conn.WriteData(spec.NewJSONSubscribeResponse(id, gasPrices)) +} diff --git a/serve/filters/subscription/gas_test.go b/serve/filters/subscription/gas_test.go new file mode 100644 index 0000000..55b0746 --- /dev/null +++ b/serve/filters/subscription/gas_test.go @@ -0,0 +1,45 @@ +package subscription + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gnolang/tx-indexer/internal/mock" + "github.com/gnolang/tx-indexer/serve/methods" + "github.com/gnolang/tx-indexer/serve/spec" +) + +func TestGasPriceSubscription_WriteResponse(t *testing.T) { + t.Parallel() + + var ( + capturedWrite any + + mockTxResults = []*types.TxResult{} + mockGasPrices = []*methods.GasPrice{} + ) + + expectedGasPricesResponse := spec.NewJSONSubscribeResponse("", mockGasPrices) + + mockConn := &mock.Conn{ + WriteDataFn: func(data any) error { + capturedWrite = data + + return nil + }, + } + + // Create the block subscription + gasPriceSubscription := NewGasPriceSubscription(mockConn) + + // Write the response + require.NoError(t, gasPriceSubscription.WriteResponse("", mockTxResults)) + + // Make sure the captured data matches + require.NotNil(t, capturedWrite) + + assert.Equal(t, expectedGasPricesResponse, capturedWrite) +} diff --git a/serve/handlers/subs/subs.go b/serve/handlers/subs/subs.go index 3f5acbe..69a969a 100644 --- a/serve/handlers/subs/subs.go +++ b/serve/handlers/subs/subs.go @@ -136,6 +136,8 @@ func (h *Handler) subscribe(connID, eventType string) (string, error) { return h.filterManager.NewBlockSubscription(conn), nil case subscription.NewTransactionsEvent: return h.filterManager.NewTransactionSubscription(conn), nil + case subscription.NewGasPriceEvent: + return h.filterManager.NewGasPriceSubscription(conn), nil default: return "", fmt.Errorf("invalid event type: %s", eventType) } From 77553a036240c903ab09e88809e2bb7838fa8c0a Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 22:42:16 +0900 Subject: [PATCH 04/14] feat: register gas price handler --- cmd/start.go | 3 +++ serve/filters/manager.go | 10 ++++++++++ serve/jsonrpc.go | 11 +++++++++++ 3 files changed, 24 insertions(+) diff --git a/cmd/start.go b/cmd/start.go index 501e8fe..2eaea9e 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -225,6 +225,9 @@ func setupJSONRPC( // Transaction handlers j.RegisterTxEndpoints(db) + // Gas handlers + j.RegisterGasEndpoints(db) + // Block handlers j.RegisterBlockEndpoints(db) diff --git a/serve/filters/manager.go b/serve/filters/manager.go index 7335aa7..351aab5 100644 --- a/serve/filters/manager.go +++ b/serve/filters/manager.go @@ -85,6 +85,11 @@ func (f *Manager) NewTransactionSubscription(conn conns.WSConnection) string { return f.newSubscription(filterSubscription.NewTransactionSubscription(conn)) } +// NewGasPriceSubscription creates gas fee subscriptions for blocks with transactions (over WS) +func (f *Manager) NewGasPriceSubscription(conn conns.WSConnection) string { + return f.newSubscription(filterSubscription.NewGasPriceSubscription(conn)) +} + // newSubscription adds new subscription to the subscription map func (f *Manager) newSubscription(subscription subscription) string { return f.subscriptions.addSubscription(subscription) @@ -123,6 +128,11 @@ func (f *Manager) subscribeToEvents() { // Send events to all `newHeads` subscriptions f.subscriptions.sendEvent(filterSubscription.NewHeadsEvent, newBlock.Block) + // Send an event to the `newGasPrice` subscription when creating a block with transactions + if len(newBlock.Results) > 0 { + f.subscriptions.sendEvent(filterSubscription.NewGasPriceEvent, newBlock.Results) + } + for _, txResult := range newBlock.Results { // Apply transaction to filters f.updateFiltersWithTxResult(txResult) diff --git a/serve/jsonrpc.go b/serve/jsonrpc.go index df6b3fe..f6802bf 100644 --- a/serve/jsonrpc.go +++ b/serve/jsonrpc.go @@ -16,6 +16,7 @@ import ( "github.com/gnolang/tx-indexer/serve/conns/wsconn" "github.com/gnolang/tx-indexer/serve/filters" "github.com/gnolang/tx-indexer/serve/handlers/block" + "github.com/gnolang/tx-indexer/serve/handlers/gas" "github.com/gnolang/tx-indexer/serve/handlers/subs" "github.com/gnolang/tx-indexer/serve/handlers/tx" "github.com/gnolang/tx-indexer/serve/metadata" @@ -127,6 +128,16 @@ func (j *JSONRPC) RegisterTxEndpoints(db tx.Storage) { ) } +// RegisterGasPriceEndpoints registers the gas price endpoints +func (j *JSONRPC) RegisterGasEndpoints(db gas.Storage) { + gasPriceHandler := gas.NewHandler(db) + + j.RegisterHandler( + "getGasPrice", + gasPriceHandler.GetGasPriceHandler, + ) +} + // RegisterBlockEndpoints registers the block endpoints func (j *JSONRPC) RegisterBlockEndpoints(db block.Storage) { blockHandler := block.NewHandler(db) From 4b84fd2377ada5970dc79849f82f2ed993a2dd20 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 23:00:31 +0900 Subject: [PATCH 05/14] fix: rename function name --- serve/methods/gas_test.go | 18 +++++++++--------- serve/methods/mocks_test.go | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/serve/methods/gas_test.go b/serve/methods/gas_test.go index 805a50b..051a599 100644 --- a/serve/methods/gas_test.go +++ b/serve/methods/gas_test.go @@ -51,7 +51,7 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { { "single transaction", []*types.TxResult{ - makeTxResultHasGasFee(1, "ugnot"), + makeTxResultWithGasFee(1, "ugnot"), }, []GasPrice{ { @@ -65,10 +65,10 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { { "variable amount", []*types.TxResult{ - makeTxResultHasGasFee(1, "ugnot"), - makeTxResultHasGasFee(2, "ugnot"), - makeTxResultHasGasFee(3, "ugnot"), - makeTxResultHasGasFee(4, "ugnot"), + makeTxResultWithGasFee(1, "ugnot"), + makeTxResultWithGasFee(2, "ugnot"), + makeTxResultWithGasFee(3, "ugnot"), + makeTxResultWithGasFee(4, "ugnot"), }, []GasPrice{ { @@ -82,10 +82,10 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { { "variable amounts and coins", []*types.TxResult{ - makeTxResultHasGasFee(1, "ugnot"), - makeTxResultHasGasFee(2, "ugnot"), - makeTxResultHasGasFee(3, "uatom"), - makeTxResultHasGasFee(4, "uatom"), + makeTxResultWithGasFee(1, "ugnot"), + makeTxResultWithGasFee(2, "ugnot"), + makeTxResultWithGasFee(3, "uatom"), + makeTxResultWithGasFee(4, "uatom"), }, []GasPrice{ { diff --git a/serve/methods/mocks_test.go b/serve/methods/mocks_test.go index ea127c5..0e9c4a6 100644 --- a/serve/methods/mocks_test.go +++ b/serve/methods/mocks_test.go @@ -6,7 +6,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -func makeTxResultHasGasFee(gasFeeAmount int64, gasFeeDenom string) *types.TxResult { +func makeTxResultWithGasFee(gasFeeAmount int64, gasFeeDenom string) *types.TxResult { tx := std.Tx{ Fee: std.Fee{ GasFee: std.Coin{ From 71816b197b4692dfb25f216eb563d498c322ca2c Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 23:15:16 +0900 Subject: [PATCH 06/14] fix: testcase --- serve/methods/gas_test.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/serve/methods/gas_test.go b/serve/methods/gas_test.go index 051a599..88e0899 100644 --- a/serve/methods/gas_test.go +++ b/serve/methods/gas_test.go @@ -113,12 +113,22 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { assert.Nil(t, err) require.NotNil(t, response) - for index, responseItem := range response { - assert.Equal(t, responseItem.Denom, testCase.results[index].Denom) - assert.Equal(t, responseItem.High, testCase.results[index].High) - assert.Equal(t, responseItem.Average, testCase.results[index].Average) - assert.Equal(t, responseItem.Low, testCase.results[index].Low) + count := 0 + + for _, responseItem := range response { + for _, testCaseResult := range testCase.results { + if responseItem.Denom == testCaseResult.Denom { + assert.Equal(t, responseItem.Denom, testCaseResult.Denom) + assert.Equal(t, responseItem.High, testCaseResult.High) + assert.Equal(t, responseItem.Average, testCaseResult.Average) + assert.Equal(t, responseItem.Low, testCaseResult.Low) + + count++ + } + } } + + assert.Equal(t, count, len(testCase.results)) }) } } From ba730189f480617d1785bfda316e4b6a8d662665 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Tue, 26 Nov 2024 23:18:33 +0900 Subject: [PATCH 07/14] fix: lint --- serve/methods/gas_test.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/serve/methods/gas_test.go b/serve/methods/gas_test.go index 88e0899..330e480 100644 --- a/serve/methods/gas_test.go +++ b/serve/methods/gas_test.go @@ -117,14 +117,16 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { for _, responseItem := range response { for _, testCaseResult := range testCase.results { - if responseItem.Denom == testCaseResult.Denom { - assert.Equal(t, responseItem.Denom, testCaseResult.Denom) - assert.Equal(t, responseItem.High, testCaseResult.High) - assert.Equal(t, responseItem.Average, testCaseResult.Average) - assert.Equal(t, responseItem.Low, testCaseResult.Low) - - count++ + if responseItem.Denom != testCaseResult.Denom { + continue } + + assert.Equal(t, responseItem.Denom, testCaseResult.Denom) + assert.Equal(t, responseItem.High, testCaseResult.High) + assert.Equal(t, responseItem.Average, testCaseResult.Average) + assert.Equal(t, responseItem.Low, testCaseResult.Low) + + count++ } } From e512a29c91e29e27ee85f1a578d9e40362c58658 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 27 Nov 2024 12:45:22 +0900 Subject: [PATCH 08/14] feat: calculate gas per block --- serve/filters/subscription/gas.go | 4 +- serve/handlers/gas/gas.go | 13 ++-- serve/handlers/gas/gas_test.go | 3 +- serve/handlers/gas/mocks_test.go | 14 ++-- serve/handlers/gas/types.go | 11 +--- serve/methods/gas.go | 105 +++++++++++++++++++++++------- serve/methods/gas_test.go | 77 ++++++++++++++++------ serve/methods/mocks_test.go | 17 +++-- 8 files changed, 168 insertions(+), 76 deletions(-) diff --git a/serve/filters/subscription/gas.go b/serve/filters/subscription/gas.go index fffc2f6..b98a632 100644 --- a/serve/filters/subscription/gas.go +++ b/serve/filters/subscription/gas.go @@ -31,12 +31,12 @@ func (b *GasPriceSubscription) GetType() events.Type { } func (b *GasPriceSubscription) WriteResponse(id string, data any) error { - txResults, ok := data.([]*types.TxResult) + block, ok := data.(*types.Block) if !ok { return fmt.Errorf("unable to cast txResult, %s", data) } - gasPrices, err := methods.GetGasPricesByTxResults(txResults) + gasPrices, err := methods.GetGasPricesByBlock(block) if err != nil { return err } diff --git a/serve/handlers/gas/gas.go b/serve/handlers/gas/gas.go index 056db70..6a31aee 100644 --- a/serve/handlers/gas/gas.go +++ b/serve/handlers/gas/gas.go @@ -2,7 +2,6 @@ package gas import ( "fmt" - "math" "strconv" "github.com/gnolang/gno/tm2/pkg/bft/types" @@ -58,11 +57,9 @@ func (h *Handler) GetGasPriceHandler( func (h *Handler) getGasPriceBy(fromBlockNum, toBlockNum uint64) ([]*methods.GasPrice, error) { it, err := h. storage. - TxIterator( + BlockIterator( fromBlockNum, toBlockNum, - 0, - math.MaxUint32, ) if err != nil { return nil, gqlerror.Wrap(err) @@ -70,22 +67,22 @@ func (h *Handler) getGasPriceBy(fromBlockNum, toBlockNum uint64) ([]*methods.Gas defer it.Close() - txs := make([]*types.TxResult, 0) + blocks := make([]*types.Block, 0) for { if !it.Next() { break } - tx, itErr := it.Value() + block, itErr := it.Value() if itErr != nil { return nil, err } - txs = append(txs, tx) + blocks = append(blocks, block) } - gasPrices, err := methods.GetGasPricesByTxResults(txs) + gasPrices, err := methods.GetGasPricesByBlocks(blocks) if err != nil { return nil, err } diff --git a/serve/handlers/gas/gas_test.go b/serve/handlers/gas/gas_test.go index 56633b4..9f18fce 100644 --- a/serve/handlers/gas/gas_test.go +++ b/serve/handlers/gas/gas_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestGetTx_InvalidParams(t *testing.T) { +func TestGetGasPriceHandler_InvalidParams(t *testing.T) { t.Parallel() testTable := []struct { @@ -35,7 +35,6 @@ func TestGetTx_InvalidParams(t *testing.T) { assert.Nil(t, response) require.NotNil(t, err) - assert.Equal(t, spec.InvalidParamsErrorCode, err.Code) }) } diff --git a/serve/handlers/gas/mocks_test.go b/serve/handlers/gas/mocks_test.go index a742c0c..6feadac 100644 --- a/serve/handlers/gas/mocks_test.go +++ b/serve/handlers/gas/mocks_test.go @@ -7,11 +7,11 @@ import ( type getLatestHeight func() (uint64, error) -type txIterator func(uint64, uint64, uint32, uint32) (storage.Iterator[*types.TxResult], error) +type blockIterator func(uint64, uint64) (storage.Iterator[*types.Block], error) type mockStorage struct { getLatestHeightFn getLatestHeight - txIteratorFn txIterator + blockIteratorFn blockIterator } func (m *mockStorage) GetLatestHeight() (uint64, error) { @@ -22,14 +22,12 @@ func (m *mockStorage) GetLatestHeight() (uint64, error) { return 0, nil } -func (m *mockStorage) TxIterator( +func (m *mockStorage) BlockIterator( fromBlockNum, toBlockNum uint64, - fromTxIndex, - toTxIndex uint32, -) (storage.Iterator[*types.TxResult], error) { - if m.txIteratorFn != nil { - return m.txIteratorFn(fromBlockNum, toBlockNum, fromTxIndex, toTxIndex) +) (storage.Iterator[*types.Block], error) { + if m.blockIteratorFn != nil { + return m.blockIteratorFn(fromBlockNum, toBlockNum) } return nil, nil diff --git a/serve/handlers/gas/types.go b/serve/handlers/gas/types.go index 9b71a44..7208497 100644 --- a/serve/handlers/gas/types.go +++ b/serve/handlers/gas/types.go @@ -6,14 +6,9 @@ import ( ) type Storage interface { - // GetTx returns specified tx from permanent storage + // GetLatestHeight returns the latest block height from the storage GetLatestHeight() (uint64, error) - // GetTxByHash fetches the tx using the transaction hash - TxIterator( - fromBlockNum, - toBlockNum uint64, - fromTxIndex, - toTxIndex uint32, - ) (storage.Iterator[*types.TxResult], error) + // BlockIterator iterates over Blocks, limiting the results to be between the provided block numbers + BlockIterator(fromBlockNum, toBlockNum uint64) (storage.Iterator[*types.Block], error) } diff --git a/serve/methods/gas.go b/serve/methods/gas.go index 335163a..ef1f8be 100644 --- a/serve/methods/gas.go +++ b/serve/methods/gas.go @@ -13,50 +13,107 @@ type gasFeeTotalInfo struct { TotalCount int64 } -func GetGasPricesByTxResults(txs []*types.TxResult) ([]*GasPrice, error) { +// GetGasPricesByBlock calculates the gas price statistics (low, high, average) +// for a single block. +func GetGasPricesByBlock(block *types.Block) ([]*GasPrice, error) { + blocks := []*types.Block{block} + + return GetGasPricesByBlocks(blocks) +} + +// GetGasPricesByBlocks calculates the gas price statistics (low, high, average) +// for multiple blocks. +func GetGasPricesByBlocks(blocks []*types.Block) ([]*GasPrice, error) { gasFeeInfoMap := make(map[string]*gasFeeTotalInfo) - for _, t := range txs { + for _, block := range blocks { + blockGasFeeInfo := calculateGasFeePerBlock(block) + + for denom, gasFeeInfo := range blockGasFeeInfo { + currentGasFeeInfo := gasFeeInfoMap[denom] + gasFeeInfoMap[denom] = calculateGasFee(currentGasFeeInfo, gasFeeInfo) + } + } + + return calculateGasPrices(gasFeeInfoMap), nil +} + +// calculateGasFeePerBlock processes all transactions in a single block to compute +// gas fee statistics (low, high, total amount, total count) for each gas fee denomination. +func calculateGasFeePerBlock(block *types.Block) map[string]*gasFeeTotalInfo { + gasFeeInfo := make(map[string]*gasFeeTotalInfo) + + for _, t := range block.Txs { var stdTx std.Tx - if err := amino.Unmarshal(t.Tx, &stdTx); err != nil { + if err := amino.Unmarshal(t, &stdTx); err != nil { continue } - gasFeeDenom := stdTx.Fee.GasFee.Denom - gasFeeAmount := stdTx.Fee.GasFee.Amount + denom := stdTx.Fee.GasFee.Denom + amount := stdTx.Fee.GasFee.Amount - if _, exists := gasFeeInfoMap[gasFeeDenom]; !exists { - gasFeeInfoMap[gasFeeDenom] = &gasFeeTotalInfo{} + info := gasFeeInfo[denom] + if info == nil { + info = &gasFeeTotalInfo{} + gasFeeInfo[denom] = info } - if gasFeeInfoMap[gasFeeDenom].Low == 0 || gasFeeInfoMap[gasFeeDenom].Low > gasFeeAmount { - gasFeeInfoMap[gasFeeDenom].Low = gasFeeAmount - } + info.Low = min(info.Low, amount) + info.High = max(info.High, amount) + info.TotalAmount += amount + info.TotalCount++ + } - if gasFeeInfoMap[gasFeeDenom].High == 0 || gasFeeInfoMap[gasFeeDenom].High < gasFeeAmount { - gasFeeInfoMap[gasFeeDenom].High = gasFeeAmount - } + return gasFeeInfo +} - gasFeeInfoMap[gasFeeDenom].TotalAmount += gasFeeAmount - gasFeeInfoMap[gasFeeDenom].TotalCount++ +// calculateGasFee merges the gas fee statistics from a block into the global statistics. +func calculateGasFee(currentInfo *gasFeeTotalInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { + if currentInfo == nil { + currentInfo = &gasFeeTotalInfo{} } - gasPrices := make([]*GasPrice, 0) + currentInfo.Low = min(currentInfo.Low, blockInfo.Low) + currentInfo.High = max(currentInfo.High, blockInfo.High) + currentInfo.TotalAmount += blockInfo.TotalAmount / blockInfo.TotalCount + currentInfo.TotalCount++ + + return currentInfo +} + +// calculateGasPrices generates the final gas price statistics (low, high, average) +func calculateGasPrices(gasFeeInfoMap map[string]*gasFeeTotalInfo) []*GasPrice { + gasPrices := make([]*GasPrice, 0, len(gasFeeInfoMap)) - for denom, gasFeeInfo := range gasFeeInfoMap { - if gasFeeInfo.TotalCount == 0 { + for denom, info := range gasFeeInfoMap { + if info.TotalCount == 0 { continue } - average := gasFeeInfo.TotalAmount / gasFeeInfo.TotalCount - gasPrices = append(gasPrices, &GasPrice{ - High: gasFeeInfo.High, - Low: gasFeeInfo.Low, - Average: average, + High: info.High, + Low: info.Low, + Average: info.TotalAmount / info.TotalCount, Denom: denom, }) } - return gasPrices, nil + return gasPrices +} + +// min calculates the smaller of two values, or returns the new value +// if the current value is uninitialized (0). +func min(current, newValue int64) int64 { + if current == 0 || newValue < current { + return newValue + } + return current +} + +// max calculates the larger of two values. +func max(current, newValue int64) int64 { + if newValue > current { + return newValue + } + return current } diff --git a/serve/methods/gas_test.go b/serve/methods/gas_test.go index 330e480..245adcc 100644 --- a/serve/methods/gas_test.go +++ b/serve/methods/gas_test.go @@ -8,12 +8,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestGetGasPricesByTxResults_EmptyTransactions(t *testing.T) { +func TestGetGasPricesByBlocks_EmptyTransactions(t *testing.T) { t.Parallel() testTable := []struct { - name string - txs []*types.TxResult + name string + blocks []*types.Block }{ { "txs is nil", @@ -21,7 +21,7 @@ func TestGetGasPricesByTxResults_EmptyTransactions(t *testing.T) { }, { "tx is empty", - []*types.TxResult{}, + make([]*types.Block, 0), }, } @@ -29,7 +29,7 @@ func TestGetGasPricesByTxResults_EmptyTransactions(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { t.Parallel() - response, err := GetGasPricesByTxResults(testCase.txs) + response, err := GetGasPricesByBlocks(testCase.blocks) assert.Nil(t, err) @@ -40,18 +40,22 @@ func TestGetGasPricesByTxResults_EmptyTransactions(t *testing.T) { } } -func TestGetGasPricesByTxResults_Transactions(t *testing.T) { +func TestGetGasPricesByBlocks_Transactions(t *testing.T) { t.Parallel() testTable := []struct { name string - txs []*types.TxResult + blocks []*types.Block results []GasPrice }{ { "single transaction", - []*types.TxResult{ - makeTxResultWithGasFee(1, "ugnot"), + []*types.Block{ + makeBlockWithTxs(1, + []types.Tx{ + makeTxResultWithGasFee(1, "ugnot"), + }, + ), }, []GasPrice{ { @@ -64,11 +68,15 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { }, { "variable amount", - []*types.TxResult{ - makeTxResultWithGasFee(1, "ugnot"), - makeTxResultWithGasFee(2, "ugnot"), - makeTxResultWithGasFee(3, "ugnot"), - makeTxResultWithGasFee(4, "ugnot"), + []*types.Block{ + makeBlockWithTxs(1, + []types.Tx{ + makeTxResultWithGasFee(1, "ugnot"), + makeTxResultWithGasFee(2, "ugnot"), + makeTxResultWithGasFee(3, "ugnot"), + makeTxResultWithGasFee(4, "ugnot"), + }, + ), }, []GasPrice{ { @@ -81,11 +89,15 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { }, { "variable amounts and coins", - []*types.TxResult{ - makeTxResultWithGasFee(1, "ugnot"), - makeTxResultWithGasFee(2, "ugnot"), - makeTxResultWithGasFee(3, "uatom"), - makeTxResultWithGasFee(4, "uatom"), + []*types.Block{ + makeBlockWithTxs(1, + []types.Tx{ + makeTxResultWithGasFee(1, "ugnot"), + makeTxResultWithGasFee(2, "ugnot"), + makeTxResultWithGasFee(3, "uatom"), + makeTxResultWithGasFee(4, "uatom"), + }, + ), }, []GasPrice{ { @@ -102,13 +114,38 @@ func TestGetGasPricesByTxResults_Transactions(t *testing.T) { }, }, }, + { + "calculate the average value per block", + []*types.Block{ + makeBlockWithTxs(1, + []types.Tx{ + makeTxResultWithGasFee(1, "ugnot"), + }, + ), + makeBlockWithTxs(2, + []types.Tx{ + makeTxResultWithGasFee(10, "ugnot"), + makeTxResultWithGasFee(10, "ugnot"), + makeTxResultWithGasFee(10, "ugnot"), + }, + ), + }, + []GasPrice{ + { + Denom: "ugnot", + High: 10, + Average: 5, + Low: 1, + }, + }, + }, } for _, testCase := range testTable { t.Run(testCase.name, func(t *testing.T) { t.Parallel() - response, err := GetGasPricesByTxResults(testCase.txs) + response, err := GetGasPricesByBlocks(testCase.blocks) assert.Nil(t, err) require.NotNil(t, response) diff --git a/serve/methods/mocks_test.go b/serve/methods/mocks_test.go index 0e9c4a6..76a1e5b 100644 --- a/serve/methods/mocks_test.go +++ b/serve/methods/mocks_test.go @@ -6,7 +6,18 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -func makeTxResultWithGasFee(gasFeeAmount int64, gasFeeDenom string) *types.TxResult { +func makeBlockWithTxs(height int64, txs []types.Tx) *types.Block { + return &types.Block{ + Header: types.Header{ + Height: height, + }, + Data: types.Data{ + Txs: txs, + }, + } +} + +func makeTxResultWithGasFee(gasFeeAmount int64, gasFeeDenom string) types.Tx { tx := std.Tx{ Fee: std.Fee{ GasFee: std.Coin{ @@ -16,7 +27,5 @@ func makeTxResultWithGasFee(gasFeeAmount int64, gasFeeDenom string) *types.TxRes }, } - return &types.TxResult{ - Tx: amino.MustMarshal(tx), - } + return amino.MustMarshal(tx) } From 3ff010dfada17b1e8a1a043bdaa2e7281a927e47 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 27 Nov 2024 12:49:48 +0900 Subject: [PATCH 09/14] fix: fix a lint --- serve/methods/gas.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/serve/methods/gas.go b/serve/methods/gas.go index ef1f8be..96dca39 100644 --- a/serve/methods/gas.go +++ b/serve/methods/gas.go @@ -68,7 +68,7 @@ func calculateGasFeePerBlock(block *types.Block) map[string]*gasFeeTotalInfo { } // calculateGasFee merges the gas fee statistics from a block into the global statistics. -func calculateGasFee(currentInfo *gasFeeTotalInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { +func calculateGasFee(currentInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { if currentInfo == nil { currentInfo = &gasFeeTotalInfo{} } @@ -107,6 +107,7 @@ func min(current, newValue int64) int64 { if current == 0 || newValue < current { return newValue } + return current } @@ -115,5 +116,6 @@ func max(current, newValue int64) int64 { if newValue > current { return newValue } + return current } From ef59041fccec4de65785d40c305063287d5eac04 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 27 Nov 2024 14:29:30 +0900 Subject: [PATCH 10/14] fix: fix a test --- serve/filters/subscription/gas_test.go | 4 ++-- serve/methods/gas.go | 15 +++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/serve/filters/subscription/gas_test.go b/serve/filters/subscription/gas_test.go index 55b0746..47253f8 100644 --- a/serve/filters/subscription/gas_test.go +++ b/serve/filters/subscription/gas_test.go @@ -18,7 +18,7 @@ func TestGasPriceSubscription_WriteResponse(t *testing.T) { var ( capturedWrite any - mockTxResults = []*types.TxResult{} + mockBlock = &types.Block{} mockGasPrices = []*methods.GasPrice{} ) @@ -36,7 +36,7 @@ func TestGasPriceSubscription_WriteResponse(t *testing.T) { gasPriceSubscription := NewGasPriceSubscription(mockConn) // Write the response - require.NoError(t, gasPriceSubscription.WriteResponse("", mockTxResults)) + require.NoError(t, gasPriceSubscription.WriteResponse("", mockBlock)) // Make sure the captured data matches require.NotNil(t, capturedWrite) diff --git a/serve/methods/gas.go b/serve/methods/gas.go index 96dca39..3e79835 100644 --- a/serve/methods/gas.go +++ b/serve/methods/gas.go @@ -58,7 +58,7 @@ func calculateGasFeePerBlock(block *types.Block) map[string]*gasFeeTotalInfo { gasFeeInfo[denom] = info } - info.Low = min(info.Low, amount) + info.Low = minInt64WithDefault(info.Low, amount) info.High = max(info.High, amount) info.TotalAmount += amount info.TotalCount++ @@ -73,7 +73,7 @@ func calculateGasFee(currentInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { currentInfo = &gasFeeTotalInfo{} } - currentInfo.Low = min(currentInfo.Low, blockInfo.Low) + currentInfo.Low = minInt64WithDefault(currentInfo.Low, blockInfo.Low) currentInfo.High = max(currentInfo.High, blockInfo.High) currentInfo.TotalAmount += blockInfo.TotalAmount / blockInfo.TotalCount currentInfo.TotalCount++ @@ -103,19 +103,10 @@ func calculateGasPrices(gasFeeInfoMap map[string]*gasFeeTotalInfo) []*GasPrice { // min calculates the smaller of two values, or returns the new value // if the current value is uninitialized (0). -func min(current, newValue int64) int64 { +func minInt64WithDefault(current, newValue int64) int64 { if current == 0 || newValue < current { return newValue } return current } - -// max calculates the larger of two values. -func max(current, newValue int64) int64 { - if newValue > current { - return newValue - } - - return current -} From 1aad6ed0f3cd500e61cfa445b63da780facff036 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Thu, 28 Nov 2024 01:45:25 +0900 Subject: [PATCH 11/14] fix: send data to subscription --- serve/filters/manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serve/filters/manager.go b/serve/filters/manager.go index 351aab5..13c3a96 100644 --- a/serve/filters/manager.go +++ b/serve/filters/manager.go @@ -129,8 +129,8 @@ func (f *Manager) subscribeToEvents() { f.subscriptions.sendEvent(filterSubscription.NewHeadsEvent, newBlock.Block) // Send an event to the `newGasPrice` subscription when creating a block with transactions - if len(newBlock.Results) > 0 { - f.subscriptions.sendEvent(filterSubscription.NewGasPriceEvent, newBlock.Results) + if len(newBlock.Block.Txs) > 0 { + f.subscriptions.sendEvent(filterSubscription.NewGasPriceEvent, newBlock.Block) } for _, txResult := range newBlock.Results { From 267bfaf500120483e97b21589dee58a933a8e483 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Thu, 28 Nov 2024 14:03:56 +0900 Subject: [PATCH 12/14] fix: remove toUint64 function --- serve/handlers/gas/gas.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/serve/handlers/gas/gas.go b/serve/handlers/gas/gas.go index 6a31aee..e8773cd 100644 --- a/serve/handlers/gas/gas.go +++ b/serve/handlers/gas/gas.go @@ -90,10 +90,6 @@ func (h *Handler) getGasPriceBy(fromBlockNum, toBlockNum uint64) ([]*methods.Gas return gasPrices, nil } -func toUint64(data any) (uint64, error) { - return strconv.ParseUint(fmt.Sprintf("%v", data), 10, 64) -} - func initializeDefaultBlockRangeByHeight(latestHeight uint64) (uint64, uint64) { toBlockNum := latestHeight @@ -107,12 +103,12 @@ func initializeDefaultBlockRangeByHeight(latestHeight uint64) (uint64, uint64) { } func parseBlockRangeByParams(params []any) (uint64, uint64) { - fromBlockNum, err := toUint64(params[0]) + fromBlockNum, err := strconv.ParseUint(fmt.Sprintf("%v", params[0]), 10, 64) if err != nil { fromBlockNum = 0 } - toBlockNum, err := toUint64(params[1]) + toBlockNum, err := strconv.ParseUint(fmt.Sprintf("%v", params[1]), 10, 64) if err != nil { toBlockNum = 0 } From d302eacac3035d97fa338e50d87a13432c4a04aa Mon Sep 17 00:00:00 2001 From: jinoosss Date: Thu, 28 Nov 2024 15:20:44 +0900 Subject: [PATCH 13/14] fix: change a calculateGasFee function --- serve/methods/gas.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/serve/methods/gas.go b/serve/methods/gas.go index 3e79835..32faf59 100644 --- a/serve/methods/gas.go +++ b/serve/methods/gas.go @@ -1,6 +1,8 @@ package methods import ( + "fmt" + "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/std" @@ -30,8 +32,15 @@ func GetGasPricesByBlocks(blocks []*types.Block) ([]*GasPrice, error) { blockGasFeeInfo := calculateGasFeePerBlock(block) for denom, gasFeeInfo := range blockGasFeeInfo { - currentGasFeeInfo := gasFeeInfoMap[denom] - gasFeeInfoMap[denom] = calculateGasFee(currentGasFeeInfo, gasFeeInfo) + _, exists := gasFeeInfoMap[denom] + if !exists { + gasFeeInfoMap[denom] = &gasFeeTotalInfo{} + } + + err := modifyAggregatedInfo(gasFeeInfoMap[denom], gasFeeInfo) + if err != nil { + return nil, err + } } } @@ -67,10 +76,10 @@ func calculateGasFeePerBlock(block *types.Block) map[string]*gasFeeTotalInfo { return gasFeeInfo } -// calculateGasFee merges the gas fee statistics from a block into the global statistics. -func calculateGasFee(currentInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { +// modifyAggregatedInfo updates the aggregated gas fee statistics by merging the block's statistics. +func modifyAggregatedInfo(currentInfo, blockInfo *gasFeeTotalInfo) error { if currentInfo == nil { - currentInfo = &gasFeeTotalInfo{} + return fmt.Errorf("not initialized aggregated data") } currentInfo.Low = minInt64WithDefault(currentInfo.Low, blockInfo.Low) @@ -78,7 +87,7 @@ func calculateGasFee(currentInfo, blockInfo *gasFeeTotalInfo) *gasFeeTotalInfo { currentInfo.TotalAmount += blockInfo.TotalAmount / blockInfo.TotalCount currentInfo.TotalCount++ - return currentInfo + return nil } // calculateGasPrices generates the final gas price statistics (low, high, average) From 245fc48ee15311d9665fdce655860a44bc0b2463 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Thu, 28 Nov 2024 15:44:06 +0900 Subject: [PATCH 14/14] fix: use a standard min --- serve/methods/gas.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/serve/methods/gas.go b/serve/methods/gas.go index 32faf59..c2be404 100644 --- a/serve/methods/gas.go +++ b/serve/methods/gas.go @@ -67,7 +67,7 @@ func calculateGasFeePerBlock(block *types.Block) map[string]*gasFeeTotalInfo { gasFeeInfo[denom] = info } - info.Low = minInt64WithDefault(info.Low, amount) + info.Low = minWithDefault(info.Low, amount) info.High = max(info.High, amount) info.TotalAmount += amount info.TotalCount++ @@ -82,7 +82,7 @@ func modifyAggregatedInfo(currentInfo, blockInfo *gasFeeTotalInfo) error { return fmt.Errorf("not initialized aggregated data") } - currentInfo.Low = minInt64WithDefault(currentInfo.Low, blockInfo.Low) + currentInfo.Low = minWithDefault(currentInfo.Low, blockInfo.Low) currentInfo.High = max(currentInfo.High, blockInfo.High) currentInfo.TotalAmount += blockInfo.TotalAmount / blockInfo.TotalCount currentInfo.TotalCount++ @@ -112,10 +112,10 @@ func calculateGasPrices(gasFeeInfoMap map[string]*gasFeeTotalInfo) []*GasPrice { // min calculates the smaller of two values, or returns the new value // if the current value is uninitialized (0). -func minInt64WithDefault(current, newValue int64) int64 { - if current == 0 || newValue < current { +func minWithDefault(current, newValue int64) int64 { + if current <= 0 { return newValue } - return current + return min(current, newValue) }