Skip to content

Commit

Permalink
api,explorer,sqlite: add additional endpoint for getting chain indices
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Sep 6, 2024
1 parent dd1d288 commit 4ee9e49
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 32 deletions.
5 changes: 5 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ func (c *Client) Transactions(ids []types.TransactionID) (resp []explorer.Transa
return
}

func (c *Client) TransactionIndices(id types.TransactionID, offset, limit uint64) (resp []types.ChainIndex, err error) {

Check failure on line 127 in api/client.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

exported: exported method Client.TransactionIndices should have comment or be unexported (revive)
err = c.c.GET(fmt.Sprintf("/transactions/%s/indices?offset=%d&limit=%d", id, offset, limit), &resp)
return
}

// AddressSiacoinUTXOs returns the specified address' unspent outputs.
func (c *Client) AddressSiacoinUTXOs(address types.Address, offset, limit uint64) (resp []explorer.SiacoinOutput, err error) {
err = c.c.GET(fmt.Sprintf("/addresses/%s/utxos/siacoin?offset=%d&limit=%d", address, offset, limit), &resp)
Expand Down
19 changes: 17 additions & 2 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type (
BestTip(height uint64) (types.ChainIndex, error)
Metrics(id types.BlockID) (explorer.Metrics, error)
Transactions(ids []types.TransactionID) ([]explorer.Transaction, error)
TransactionIndices(id types.TransactionID, offset, limit uint64) ([]types.ChainIndex, error)
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
SiacoinElements(ids []types.SiacoinOutputID) (result []explorer.SiacoinOutput, err error)
SiafundElements(ids []types.SiafundOutputID) (result []explorer.SiafundOutput, err error)
Expand Down Expand Up @@ -251,6 +252,19 @@ func (s *server) transactionsIDHandler(jc jape.Context) {
jc.Encode(txns[0])
}

func (s *server) transactionsIDIndicesHandler(jc jape.Context) {
var id types.TransactionID
if jc.DecodeParam("id", &id) != nil {
return
}

indices, err := s.e.TransactionIndices(id, 0, 100)
if jc.Check("failed to get transaction indices", err) != nil {
return
}
jc.Encode(indices)
}

func (s *server) transactionsBatchHandler(jc jape.Context) {
var ids []types.TransactionID
if jc.Decode(&ids) != nil {
Expand Down Expand Up @@ -479,8 +493,9 @@ func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {

"GET /blocks/:id": srv.blocksIDHandler,

"GET /transactions/:id": srv.transactionsIDHandler,
"POST /transactions": srv.transactionsBatchHandler,
"GET /transactions/:id": srv.transactionsIDHandler,
"POST /transactions": srv.transactionsBatchHandler,
"GET /transactions/:id/indices": srv.transactionsIDIndicesHandler,

"GET /addresses/:address/utxos/siacoin": srv.addressessAddressUtxosSiacoinHandler,
"GET /addresses/:address/utxos/siafund": srv.addressessAddressUtxosSiafundHandler,
Expand Down
5 changes: 5 additions & 0 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Store interface {
MerkleProof(leafIndex uint64) ([]types.Hash256, error)
Metrics(id types.BlockID) (Metrics, error)
Transactions(ids []types.TransactionID) ([]Transaction, error)
TransactionIndices(txid types.TransactionID, offset, limit uint64) ([]types.ChainIndex, error)
UnspentSiacoinOutputs(address types.Address, offset, limit uint64) ([]SiacoinOutput, error)
UnspentSiafundOutputs(address types.Address, offset, limit uint64) ([]SiafundOutput, error)
AddressEvents(address types.Address, offset, limit uint64) (events []Event, err error)
Expand Down Expand Up @@ -146,6 +147,10 @@ func (e *Explorer) Transactions(ids []types.TransactionID) ([]Transaction, error
return e.s.Transactions(ids)
}

func (e *Explorer) TransactionIndices(id types.TransactionID, offset, limit uint64) ([]types.ChainIndex, error) {

Check failure on line 150 in explorer/explorer.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

exported: exported method Explorer.TransactionIndices should have comment or be unexported (revive)
return e.s.TransactionIndices(id, offset, limit)
}

// UnspentSiacoinOutputs returns the unspent siacoin outputs owned by the
// specified address.
func (e *Explorer) UnspentSiacoinOutputs(address types.Address, offset, limit uint64) ([]SiacoinOutput, error) {
Expand Down
30 changes: 28 additions & 2 deletions persist/sqlite/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,19 @@ func TestSendTransactions(t *testing.T) {
check(t, "siafunds", expectSF, sf)
}

checkChainIndices := func(t *testing.T, txnID types.TransactionID, expected []types.ChainIndex) {
indices, err := db.TransactionIndices(txnID, 0, 100)
switch {
case err != nil:
t.Fatal(err)
case len(indices) != len(expected):
t.Fatalf("expected %d indices, got %d", len(expected), len(indices))
}
for i := range indices {
check(t, "index", expected[i], indices[i])
}
}

checkTransaction := func(expectTxn types.Transaction, gotTxn explorer.Transaction) {
check(t, "siacoin inputs", len(expectTxn.SiacoinInputs), len(gotTxn.SiacoinInputs))
check(t, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs))
Expand Down Expand Up @@ -577,7 +590,7 @@ func TestSendTransactions(t *testing.T) {
// with the actual transactions
for i := range b.Transactions {
checkTransaction(b.Transactions[i], block.Transactions[i])
check(t, "chain indices", []types.ChainIndex{cm.Tip()}, block.Transactions[i].ChainIndices)
checkChainIndices(t, b.Transactions[i].ID(), []types.ChainIndex{cm.Tip()})

txns, err := db.Transactions([]types.TransactionID{b.Transactions[i].ID()})
if err != nil {
Expand Down Expand Up @@ -1624,6 +1637,19 @@ func TestRevertSendTransactions(t *testing.T) {
check(t, "siafunds", expectSF, sf)
}

checkChainIndices := func(t *testing.T, txnID types.TransactionID, expected []types.ChainIndex) {
indices, err := db.TransactionIndices(txnID, 0, 100)
switch {
case err != nil:
t.Fatal(err)
case len(indices) != len(expected):
t.Fatalf("expected %d indices, got %d", len(expected), len(indices))
}
for i := range indices {
check(t, "index", expected[i], indices[i])
}
}

checkTransaction := func(expectTxn types.Transaction, gotTxn explorer.Transaction) {
check(t, "siacoin inputs", len(expectTxn.SiacoinInputs), len(gotTxn.SiacoinInputs))
check(t, "siacoin outputs", len(expectTxn.SiacoinOutputs), len(gotTxn.SiacoinOutputs))
Expand Down Expand Up @@ -1781,7 +1807,7 @@ func TestRevertSendTransactions(t *testing.T) {
// with the actual transactions
for i := range b.Transactions {
checkTransaction(b.Transactions[i], block.Transactions[i])
check(t, "chain indices", []types.ChainIndex{cm.Tip()}, block.Transactions[i].ChainIndices)
checkChainIndices(t, b.Transactions[i].ID(), []types.ChainIndex{cm.Tip()})

txns, err := db.Transactions([]types.TransactionID{b.Transactions[i].ID()})
if err != nil {
Expand Down
52 changes: 24 additions & 28 deletions persist/sqlite/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,31 @@ import (
)

// transactionChainIndices returns the chain indices of the blocks the transaction

Check failure on line 10 in persist/sqlite/transactions.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

exported: comment on exported method Store.TransactionIndices should be of the form "TransactionIndices ..." (revive)
// was in.
func transactionChainIndices(tx *txn, txnIDs []int64) (map[int64][]types.ChainIndex, error) {
query := `SELECT bt.transaction_id, bt.block_id, b.height
FROM block_transactions bt
JOIN blocks b ON bt.block_id = b.id
WHERE bt.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY bt.block_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
if err != nil {
return nil, err
}
defer rows.Close()

result := make(map[int64][]types.ChainIndex)
for rows.Next() {
var txnID int64
var index types.ChainIndex
if err := rows.Scan(&txnID, decode(&index.ID), decode(&index.Height)); err != nil {
return nil, fmt.Errorf("failed to scan chain index: %w", err)
// was included in. If the transaction has not been included in any blocks, the
// result will be nil,nil.
func (s *Store) TransactionIndices(txnID types.TransactionID, offset, limit uint64) (indices []types.ChainIndex, err error) {
err = s.transaction(func(tx *txn) error {
rows, err := tx.Query(`SELECT DISTINCT b.id, b.height FROM blocks b
INNER JOIN block_transactions bt ON (bt.block_id = b.id)
INNER JOIN transactions t ON (t.id = bt.transaction_id)
WHERE t.transaction_id = ?
ORDER BY b.height DESC
LIMIT ? OFFSET ?`, encode(txnID), limit, offset)
if err != nil {
return err
}
result[txnID] = append(result[txnID], index)
}
return result, nil
defer rows.Close()

for rows.Next() {
var index types.ChainIndex
if err := rows.Scan(decode(&index.ID), decode(&index.Height)); err != nil {
return fmt.Errorf("failed to scan chain index: %w", err)
}
indices = append(indices, index)
}
return rows.Err()
})
return
}

// transactionMinerFee returns the miner fees for each transaction.
Expand Down Expand Up @@ -484,11 +486,6 @@ func getTransactions(tx *txn, idMap map[int64]transactionID) ([]explorer.Transac
dbIDs[id.order] = dbID
}

txnChainIndices, err := transactionChainIndices(tx, dbIDs)
if err != nil {
return nil, fmt.Errorf("getTransactions: failed to get chain indices: %w", err)
}

txnArbitraryData, err := transactionArbitraryData(tx, dbIDs)
if err != nil {
return nil, fmt.Errorf("getTransactions: failed to get arbitrary data: %w", err)
Expand Down Expand Up @@ -543,7 +540,6 @@ func getTransactions(tx *txn, idMap map[int64]transactionID) ([]explorer.Transac
for _, dbID := range dbIDs {
txn := explorer.Transaction{
ID: idMap[dbID].id,
ChainIndices: txnChainIndices[dbID],
SiacoinInputs: txnSiacoinInputs[dbID],
SiacoinOutputs: txnSiacoinOutputs[dbID],
SiafundInputs: txnSiafundInputs[dbID],
Expand Down

0 comments on commit 4ee9e49

Please sign in to comment.