feat(btc): implement repo for get blocks

Co-authored-by: Gaze <dev@gaze.network>
This commit is contained in:
Gaze
2024-04-17 06:35:54 +07:00
parent 60e755a5b3
commit b22a026314

View File

@@ -1,10 +1,16 @@
package postgres
import (
"cmp"
"context"
"encoding/hex"
"slices"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/modules/bitcoin/repository/postgres/gen"
"github.com/samber/lo"
)
func (r *Repository) GetLatestBlockHeader(ctx context.Context) (types.BlockHeader, error) {
@@ -74,3 +80,121 @@ func (r *Repository) RevertBlocks(ctx context.Context, from int64) error {
}
return nil
}
func (r *Repository) GetBlocksByHeightRange(ctx context.Context, from int64, to int64) ([]*types.Block, error) {
blocks, err := r.queries.GetBlocksByHeightRange(ctx, gen.GetBlocksByHeightRangeParams{
FromHeight: int32(from),
ToHeight: int32(to),
})
if err != nil {
return nil, errors.Wrap(err, "failed to get blocks by height range")
}
txs, err := r.queries.GetTransactionsByHeightRange(ctx, gen.GetTransactionsByHeightRangeParams{
FromHeight: int32(from),
ToHeight: int32(to),
})
if err != nil {
return nil, errors.Wrap(err, "failed to get transactions by height range")
}
txHashes := lo.Map(txs, func(tx gen.BitcoinTransaction, _ int) string { return tx.TxHash })
txOuts, err := r.queries.GetTransactionTxOutsByTxHashes(ctx, txHashes)
if err != nil {
return nil, errors.Wrap(err, "failed to get transaction txouts by tx hashes")
}
txIns, err := r.queries.GetTransactionTxInsByTxHashes(ctx, txHashes)
if err != nil {
return nil, errors.Wrap(err, "failed to get transaction txins by tx hashes")
}
// Grouping result by block height and tx hash
groupedTxs := lo.GroupBy(txs, func(tx gen.BitcoinTransaction) int32 { return tx.BlockHeight })
groupedTxOuts := lo.GroupBy(txOuts, func(txOut gen.BitcoinTransactionTxout) string { return txOut.TxHash })
groupedTxIns := lo.GroupBy(txIns, func(txIn gen.BitcoinTransactionTxin) string { return txIn.TxHash })
var errs []error
result := lo.Map(blocks, func(blockModel gen.BitcoinBlock, _ int) *types.Block {
header, err := mapBlockHeaderModelToType(blockModel)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to map block header model to type"))
return nil
}
txsModel := groupedTxs[blockModel.BlockHeight]
return &types.Block{
Header: header,
Transactions: lo.Map(txsModel, func(txModel gen.BitcoinTransaction, _ int) *types.Transaction {
blockHash, err := chainhash.NewHashFromStr(txModel.BlockHash)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to parse block hash"))
return nil
}
txHash, err := chainhash.NewHashFromStr(txModel.TxHash)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to parse tx hash"))
return nil
}
txOutsModel := groupedTxOuts[txModel.TxHash]
txInsModel := groupedTxIns[txModel.TxHash]
// Sort txins and txouts by index (Asc)
slices.SortFunc(txOutsModel, func(i, j gen.BitcoinTransactionTxout) int {
return cmp.Compare(i.TxIdx, j.TxIdx)
})
slices.SortFunc(txInsModel, func(i, j gen.BitcoinTransactionTxin) int {
return cmp.Compare(i.TxIdx, j.TxIdx)
})
return &types.Transaction{
BlockHeight: int64(txModel.BlockHeight),
BlockHash: *blockHash,
Index: uint32(txModel.Idx),
TxHash: *txHash,
Version: txModel.Version,
LockTime: uint32(txModel.Locktime),
TxIn: lo.Map(txInsModel, func(src gen.BitcoinTransactionTxin, _ int) *types.TxIn {
scriptsig, err := hex.DecodeString(src.Scriptsig)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to decode scriptsig"))
return nil
}
prevoutTxHash, err := chainhash.NewHashFromStr(src.PrevoutTxHash)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to parse prevout tx hash"))
return nil
}
return &types.TxIn{
SignatureScript: scriptsig,
Witness: [][]byte{}, // TODO: implement witness
Sequence: uint32(src.Sequence),
PreviousOutIndex: uint32(src.PrevoutTxIdx),
PreviousOutTxHash: *prevoutTxHash,
}
}),
TxOut: lo.Map(txOutsModel, func(src gen.BitcoinTransactionTxout, _ int) *types.TxOut {
pkscript, err := hex.DecodeString(src.Pkscript)
if err != nil {
errs = append(errs, errors.Wrap(err, "failed to decode pkscript"))
return nil
}
return &types.TxOut{
PkScript: pkscript,
Value: src.Value,
}
}),
}
}),
}
})
if len(errs) > 0 {
return nil, errors.Wrap(errors.Join(errs...), "failed while mapping result")
}
return result, nil
}