Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add secondary indexes + querying tx by hash #16

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions internal/mock/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Storage struct {
GetWriteBatchFn func() storage.Batch
GetBlockFn func(uint64) (*types.Block, error)
GetTxFn func(uint64, uint32) (*types.TxResult, error)
GetTxByHashFn func(string) (*types.TxResult, error)
}

func (m *Storage) GetLatestHeight() (uint64, error) {
Expand Down Expand Up @@ -41,6 +42,14 @@ func (m *Storage) GetTx(blockNum uint64, index uint32) (*types.TxResult, error)
panic("not implemented")
}

func (m *Storage) GetTxByHash(txHash string) (*types.TxResult, error) {
if m.GetTxByHashFn != nil {
return m.GetTxByHashFn(txHash)
}

panic("not implemented")
}

// BlockIterator iterates over Blocks, limiting the results to be between the provided block numbers
func (m *Storage) BlockIterator(_, _ uint64) (storage.Iterator[*types.Block], error) {
panic("not implemented") // TODO: Implement
Expand Down
70 changes: 69 additions & 1 deletion serve/graph/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions serve/graph/model/models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions serve/graph/model/transaction.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"encoding/base64"
"fmt"

"github.com/gnolang/gno/tm2/pkg/bft/types"
Expand All @@ -22,6 +23,10 @@ func (t *Transaction) Index() int {
return int(t.t.Index)
}

func (t *Transaction) Hash() string {
return base64.StdEncoding.EncodeToString(t.t.Tx.Hash())
}

func (t *Transaction) BlockHeight() int {
return int(t.t.Height)
}
Expand Down
11 changes: 11 additions & 0 deletions serve/graph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ type Transaction {
"""
index: Int!

"""
Hash from Transaction content in base64 encoding.
"""
hash: String!

"""
The height of the Block in which this Transaction is included. Links the Transaction to its containing Block.
"""
Expand Down Expand Up @@ -134,6 +139,12 @@ input TransactionFilter {
Maximum `gas_used` value for filtering Transactions, exclusive. Refines selection based on the computational effort actually consumed.
"""
to_gas_used: Int

"""
Hash from Transaction content in base64 encoding. If this filter is used, any other filter will be ignored.
"""
hash: String

}

"""
Expand Down
12 changes: 10 additions & 2 deletions serve/graph/schema.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion serve/handlers/tx/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import "github.com/gnolang/gno/tm2/pkg/bft/types"

type getTxDelegate func(uint64, uint32) (*types.TxResult, error)

type getTxHashDelegate func(string) (*types.TxResult, error)

type mockStorage struct {
getTxFn getTxDelegate
getTxFn getTxDelegate
getTxHashFn getTxHashDelegate
}

func (m *mockStorage) GetTx(bn uint64, ti uint32) (*types.TxResult, error) {
Expand All @@ -15,3 +18,11 @@ func (m *mockStorage) GetTx(bn uint64, ti uint32) (*types.TxResult, error) {

return nil, nil
}

func (m *mockStorage) GetTxByHash(h string) (*types.TxResult, error) {
if m.getTxHashFn != nil {
return m.getTxHashFn(h)
}

return nil, nil
}
49 changes: 49 additions & 0 deletions serve/handlers/tx/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,39 @@ func (h *Handler) GetTxHandler(
return encodedResponse, nil
}

func (h *Handler) GetTxByHashHandler(
_ *metadata.Metadata,
params []any,
) (any, *spec.BaseJSONError) {
// Check the params
if len(params) < 1 {
return nil, spec.GenerateInvalidParamCountError()
}

// Extract the params
txHash, ok := params[0].(string)
if !ok {
return nil, spec.GenerateInvalidParamError(1)
}

// Run the handler
response, err := h.getTxByHash(txHash)
if err != nil {
return nil, spec.GenerateResponseError(err)
}

if response == nil {
return nil, nil
}

encodedResponse, err := encode.PrepareValue(response)
if err != nil {
return nil, spec.GenerateResponseError(err)
}

return encodedResponse, nil
}

// getTx fetches the tx from storage, if any
func (h *Handler) getTx(blockNum uint64, txIndex uint32) (*types.TxResult, error) {
tx, err := h.storage.GetTx(blockNum, txIndex)
Expand All @@ -74,3 +107,19 @@ func (h *Handler) getTx(blockNum uint64, txIndex uint32) (*types.TxResult, error

return tx, nil
}

// getTx fetches the tx from storage, if any
func (h *Handler) getTxByHash(hash string) (*types.TxResult, error) {
tx, err := h.storage.GetTxByHash(hash)
if errors.Is(err, storageErrors.ErrNotFound) {
// Wrap the error
//nolint:nilnil // This is a special case
return nil, nil
}

if err != nil {
return nil, err
}

return tx, nil
}
42 changes: 42 additions & 0 deletions serve/handlers/tx/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,46 @@ func TestGetBlock_Handler(t *testing.T) {

assert.Equal(t, txResult, &decodedTxResult)
})

t.Run("block found in storage by hash", func(t *testing.T) {
t.Parallel()

var (
hash = "hash"

txResult = &types.TxResult{
Height: 10,
}

mockStorage = &mockStorage{
getTxHashFn: func(s string) (*types.TxResult, error) {
require.Equal(t, hash, s)

return txResult, nil
},
}
)

h := NewHandler(mockStorage)

responseRaw, err := h.GetTxByHashHandler(nil, []any{hash})
require.Nil(t, err)

require.NotNil(t, responseRaw)

// Make sure the response is valid (base64 + amino)
response, ok := responseRaw.(string)
require.True(t, ok)

// Decode from base64
encodedTxResult, decodeErr := base64.StdEncoding.DecodeString(response)
require.Nil(t, decodeErr)

// Decode from amino binary
var decodedTxResult types.TxResult

require.NoError(t, amino.Unmarshal(encodedTxResult, &decodedTxResult))

assert.Equal(t, txResult, &decodedTxResult)
})
}
3 changes: 3 additions & 0 deletions serve/handlers/tx/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ import "github.com/gnolang/gno/tm2/pkg/bft/types"
type Storage interface {
// GetTx returns specified tx from permanent storage
GetTx(uint64, uint32) (*types.TxResult, error)

// GetTxByHash fetches the tx using the transaction hash
GetTxByHash(txHash string) (*types.TxResult, error)
}
5 changes: 5 additions & 0 deletions serve/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ func (j *JSONRPC) RegisterTxEndpoints(db tx.Storage) {
"getTxResult",
txHandler.GetTxHandler,
)

j.RegisterHandler(
"getTxResultByHash",
txHandler.GetTxByHashHandler,
)
}

// RegisterBlockEndpoints registers the block endpoints
Expand Down
Loading
Loading