Skip to content

Commit

Permalink
indexer: index more details about blocks and transactions
Browse files Browse the repository at this point in the history
* indexerdb: add block details chain_id, proposer_address and last_block_hash
* indexerdb: rename method GetBlock -> GetBlockByHeight
* indexer: new method BlockByHeight (and indexerdb.GetBlockByHash)
* indexer: new method SearchBlocks (and indexerdb.SearchBlocks)

transactions:
* indexerdb: add raw_tx, subtype, signature and signer in transactions table
* indexer: new methods CountTransactionsByHeight and SearchTransactions
* indexer: rename GetTransaction* methods -> GetTxMetadata*

indexer migrations:
 * 0014_alter_columns_table_blocks.sql
 * 0015_alter_columns_table_transactions.sql

* vochaintx: add tx.TxSubtype method
  • Loading branch information
altergui committed Sep 2, 2024
1 parent 07143c2 commit 823c841
Show file tree
Hide file tree
Showing 18 changed files with 592 additions and 163 deletions.
4 changes: 2 additions & 2 deletions api/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ type TransactionReference struct {

// TransactionsList is used to return a paginated list to the client
type TransactionsList struct {
Transactions []*indexertypes.Transaction `json:"transactions"`
Pagination *Pagination `json:"pagination"`
Transactions []*indexertypes.TransactionMetadata `json:"transactions"`
Pagination *Pagination `json:"pagination"`
}

// FeesList is used to return a paginated list to the client
Expand Down
4 changes: 2 additions & 2 deletions api/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ func (a *API) chainTxRefByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo
if err != nil {
return err
}
ref, err := a.indexer.GetTxHashReference(hash)
ref, err := a.indexer.GetTxMetadataByHash(hash)
if err != nil {
if errors.Is(err, indexer.ErrTransactionNotFound) {
return ErrTransactionNotFound
Expand Down Expand Up @@ -690,7 +690,7 @@ func (a *API) chainTxHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) er
return ErrVochainGetTxFailed.WithErr(err)
}

ref, err := a.indexer.GetTxReferenceByBlockHeightAndBlockIndex(height, index)
ref, err := a.indexer.GetTransactionByHeightAndIndex(height, index)
if err != nil {
if errors.Is(err, indexer.ErrTransactionNotFound) {
return ErrTransactionNotFound
Expand Down
13 changes: 9 additions & 4 deletions vochain/indexer/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func BenchmarkIndexer(b *testing.B) {
tx := &vochaintx.Tx{
TxID: rnd.Random32(),
TxModelType: "vote",
Tx: &models.Tx{Payload: &models.Tx_Vote{}},
}
idx.OnNewTx(tx, height, txBlockIndex)
curTxs = append(curTxs, tx)
Expand Down Expand Up @@ -112,7 +113,7 @@ func BenchmarkIndexer(b *testing.B) {
qt.Check(b, bytes.Equal(voteRef.Meta.TxHash, tx.TxID[:]), qt.IsTrue)
}

txRef, err := idx.GetTxHashReference(tx.TxID[:])
txRef, err := idx.GetTxMetadataByHash(tx.TxID[:])
qt.Check(b, err, qt.IsNil)
if err == nil {
qt.Check(b, txRef.BlockHeight, qt.Equals, vote.Height)
Expand All @@ -138,7 +139,11 @@ func BenchmarkFetchTx(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < numTxs; j++ {
idx.OnNewTx(&vochaintx.Tx{TxID: util.Random32()}, uint32(i), int32(j))
idx.OnNewTx(&vochaintx.Tx{
TxID: util.Random32(),
TxModelType: "vote",
Tx: &models.Tx{Payload: &models.Tx_Vote{}},
}, uint32(i), int32(j))
}
err := idx.Commit(uint32(i))
qt.Assert(b, err, qt.IsNil)
Expand All @@ -147,14 +152,14 @@ func BenchmarkFetchTx(b *testing.B) {

startTime := time.Now()
for j := 0; j < numTxs; j++ {
_, err = idx.GetTxReferenceByBlockHeightAndBlockIndex(int64(i), int64(j))
_, err = idx.GetTransactionByHeightAndIndex(int64(i), int64(j))
qt.Assert(b, err, qt.IsNil)
}
log.Infof("fetched %d transactions (out of %d total) by height+index, took %s",
numTxs, (i+1)*numTxs, time.Since(startTime))
startTime = time.Now()
for j := 0; j < numTxs; j++ {
_, err = idx.GetTxHashReference([]byte(fmt.Sprintf("hash%d%d", i, j)))
_, err = idx.GetTxMetadataByHash([]byte(fmt.Sprintf("hash%d%d", i, j)))
qt.Assert(b, err, qt.IsNil)
}
log.Infof("fetched %d transactions (out of %d total) by hash, took %s",
Expand Down
62 changes: 58 additions & 4 deletions vochain/indexer/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,73 @@ import (
"errors"
"fmt"
"time"

indexerdb "go.vocdoni.io/dvote/vochain/indexer/db"
"go.vocdoni.io/dvote/vochain/indexer/indexertypes"
)

// ErrBlockNotFound is returned if the block is not found in the indexer database.
var ErrBlockNotFound = fmt.Errorf("block not found")

// BlockTimestamp returns the timestamp of the block at the given height
func (idx *Indexer) BlockTimestamp(height int64) (time.Time, error) {
block, err := idx.readOnlyQuery.GetBlock(context.TODO(), height)
block, err := idx.BlockByHeight(height)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return time.Time{}, ErrBlockNotFound
}
return time.Time{}, err
}
return block.Time, nil
}

// BlockByHeight returns the available information of the block at the given height
func (idx *Indexer) BlockByHeight(height int64) (*indexertypes.Block, error) {
block, err := idx.readOnlyQuery.GetBlockByHeight(context.TODO(), height)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrBlockNotFound
}
return nil, err
}
return indexertypes.BlockFromDB(&block), nil
}

// BlockByHash returns the available information of the block with the given hash
func (idx *Indexer) BlockByHash(hash []byte) (*indexertypes.Block, error) {
block, err := idx.readOnlyQuery.GetBlockByHash(context.TODO(), hash)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrBlockNotFound
}
return nil, err
}
return indexertypes.BlockFromDB(&block), nil
}

// BlockList returns the list of blocks indexed.
// chainID, hash, proposerAddress are optional, if declared as zero-value will be ignored.
// The first one returned is the newest, so they are in descending order.
func (idx *Indexer) BlockList(limit, offset int, chainID, hash, proposerAddress string) ([]*indexertypes.Block, uint64, error) {
if offset < 0 {
return nil, 0, fmt.Errorf("invalid value: offset cannot be %d", offset)
}
if limit <= 0 {
return nil, 0, fmt.Errorf("invalid value: limit cannot be %d", limit)
}
results, err := idx.readOnlyQuery.SearchBlocks(context.TODO(), indexerdb.SearchBlocksParams{
Limit: int64(limit),
Offset: int64(offset),
ChainID: chainID,
HashSubstr: hash,
ProposerAddress: proposerAddress,
})
if err != nil {
return nil, 0, err
}
list := []*indexertypes.Block{}
for _, row := range results {
list = append(list, indexertypes.BlockFromDBRow(&row))
}
if len(results) == 0 {
return list, 0, nil
}
return list, uint64(results[0].TotalCount), nil
}
141 changes: 130 additions & 11 deletions vochain/indexer/db/blocks.sql.go

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

Loading

0 comments on commit 823c841

Please sign in to comment.