mirror of
https://github.com/alexgo-io/gaze-indexer.git
synced 2026-04-29 04:05:12 +08:00
Merge branch 'feature/bitcoin-indexer' into feat/runes-module
This commit is contained in:
@@ -6,7 +6,11 @@ type ErrorKind string
|
||||
|
||||
const (
|
||||
// NotFound is returned when a requested item is not found.
|
||||
NotFound = ErrorKind("Not Found")
|
||||
NotFound = ErrorKind("Not Found")
|
||||
|
||||
// InternalError is returned when internal logic got error
|
||||
InternalError = ErrorKind("Internal Error")
|
||||
|
||||
OverflowUint64 = ErrorKind("overflow uint64")
|
||||
OverflowUint128 = ErrorKind("overflow uint128")
|
||||
)
|
||||
|
||||
12
modules/bitcoin/btcclient/contract.go
Normal file
12
modules/bitcoin/btcclient/contract.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package btcclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
)
|
||||
|
||||
type Contract interface {
|
||||
GetTransaction(ctx context.Context, txHash chainhash.Hash) (*types.Transaction, error)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ INSERT INTO bitcoin_transaction_txouts ("tx_hash","tx_idx","pkscript","value") V
|
||||
WITH update_txout AS (
|
||||
UPDATE "bitcoin_transaction_txouts"
|
||||
SET "is_spent" = true
|
||||
WHERE "tx_hash" = $3 AND "tx_idx" = $4
|
||||
WHERE "tx_hash" = $3 AND "tx_idx" = $4 AND "is_spent" = false -- TODO: should throw an error if already spent
|
||||
RETURNING "pkscript"
|
||||
)
|
||||
INSERT INTO bitcoin_transaction_txins ("tx_hash","tx_idx","prevout_tx_hash","prevout_tx_idx","prevout_pkscript","scriptsig","witness","sequence")
|
||||
|
||||
69
modules/bitcoin/internal/repository/postgres/block.go
Normal file
69
modules/bitcoin/internal/repository/postgres/block.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
)
|
||||
|
||||
func (r *Repository) GetLatestBlockHeader(ctx context.Context) (types.BlockHeader, error) {
|
||||
model, err := r.queries.GetLatestBlockHeader(ctx)
|
||||
if err != nil {
|
||||
return types.BlockHeader{}, errors.Wrap(err, "failed to get latest block header")
|
||||
}
|
||||
|
||||
data, err := mapBlockHeaderModelToType(model)
|
||||
if err != nil {
|
||||
return types.BlockHeader{}, errors.Wrap(err, "failed to map block header model to type")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r *Repository) InsertBlock(ctx context.Context, block *types.Block) error {
|
||||
blockParams, txParams, txoutParams, txinParams := mapBlockTypeToParams(block)
|
||||
|
||||
tx, err := r.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
panic(r) // re-throw panic after rollback
|
||||
}
|
||||
}()
|
||||
|
||||
queries := r.queries.WithTx(tx)
|
||||
|
||||
if err := queries.InsertBlock(ctx, blockParams); err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
return errors.Wrapf(err, "failed to insert block, height: %d, hash: %s", blockParams.BlockHeight, blockParams.BlockHash)
|
||||
}
|
||||
|
||||
for _, params := range txParams {
|
||||
if err := queries.InsertTransaction(ctx, params); err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
return errors.Wrapf(err, "failed to insert transaction, hash: %s", params.TxHash)
|
||||
}
|
||||
}
|
||||
|
||||
// Should insert txout first, then txin
|
||||
// Because txin references txout
|
||||
for _, params := range txoutParams {
|
||||
if err := queries.InsertTransactionTxOut(ctx, params); err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
return errors.Wrapf(err, "failed to insert transaction txout, %v:%v", params.TxHash, params.TxIdx)
|
||||
}
|
||||
}
|
||||
|
||||
for _, params := range txinParams {
|
||||
if err := queries.InsertTransactionTxIn(ctx, params); err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
return errors.Wrapf(err, "failed to insert transaction txin, %v:%v", params.TxHash, params.TxIdx)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -93,7 +93,7 @@ const insertTransactionTxIn = `-- name: InsertTransactionTxIn :exec
|
||||
WITH update_txout AS (
|
||||
UPDATE "bitcoin_transaction_txouts"
|
||||
SET "is_spent" = true
|
||||
WHERE "tx_hash" = $3 AND "tx_idx" = $4
|
||||
WHERE "tx_hash" = $3 AND "tx_idx" = $4 AND "is_spent" = false -- TODO: should throw an error if already spent
|
||||
RETURNING "pkscript"
|
||||
)
|
||||
INSERT INTO bitcoin_transaction_txins ("tx_hash","tx_idx","prevout_tx_hash","prevout_tx_idx","prevout_pkscript","scriptsig","witness","sequence")
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"github.com/gaze-network/indexer-network/core/types"
|
||||
"github.com/gaze-network/indexer-network/modules/bitcoin/internal/repository/postgres/gen"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func mapBlockHeaderTypeToModel(src *types.BlockHeader) gen.BitcoinBlock {
|
||||
func mapBlockHeaderTypeToModel(src types.BlockHeader) gen.BitcoinBlock {
|
||||
return gen.BitcoinBlock{
|
||||
BlockHeight: int32(src.Height),
|
||||
BlockHash: src.Hash.String(),
|
||||
@@ -26,20 +27,20 @@ func mapBlockHeaderTypeToModel(src *types.BlockHeader) gen.BitcoinBlock {
|
||||
}
|
||||
}
|
||||
|
||||
func mapBlockHeaderModelToType(src gen.BitcoinBlock) (*types.BlockHeader, error) {
|
||||
func mapBlockHeaderModelToType(src gen.BitcoinBlock) (types.BlockHeader, error) {
|
||||
hash, err := chainhash.NewHashFromStr(src.BlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse block hash")
|
||||
return types.BlockHeader{}, errors.Join(errors.Wrap(err, "failed to parse block hash"), errs.InternalError)
|
||||
}
|
||||
prevHash, err := chainhash.NewHashFromStr(src.PrevBlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse prev block hash")
|
||||
return types.BlockHeader{}, errors.Join(errors.Wrap(err, "failed to parse prev block hash"), errs.InternalError)
|
||||
}
|
||||
merkleRoot, err := chainhash.NewHashFromStr(src.MerkleRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse merkle root")
|
||||
return types.BlockHeader{}, errors.Join(errors.Wrap(err, "failed to parse merkle root"), errs.InternalError)
|
||||
}
|
||||
return &types.BlockHeader{
|
||||
return types.BlockHeader{
|
||||
Hash: *hash,
|
||||
Height: int64(src.BlockHeight),
|
||||
Version: src.Version,
|
||||
@@ -51,13 +52,25 @@ func mapBlockHeaderModelToType(src gen.BitcoinBlock) (*types.BlockHeader, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapBlockTypeToModel(src *types.Block) (gen.BitcoinBlock, []gen.BitcoinTransaction, []gen.BitcoinTransactionTxin, []gen.BitcoinTransactionTxout) {
|
||||
block := mapBlockHeaderTypeToModel(&src.Header)
|
||||
txs := make([]gen.BitcoinTransaction, 0, len(src.Transactions))
|
||||
txins := make([]gen.BitcoinTransactionTxin, 0)
|
||||
txouts := make([]gen.BitcoinTransactionTxout, 0)
|
||||
func mapBlockTypeToParams(src *types.Block) (gen.InsertBlockParams, []gen.InsertTransactionParams, []gen.InsertTransactionTxOutParams, []gen.InsertTransactionTxInParams) {
|
||||
txs := make([]gen.InsertTransactionParams, 0, len(src.Transactions))
|
||||
txouts := make([]gen.InsertTransactionTxOutParams, 0)
|
||||
txins := make([]gen.InsertTransactionTxInParams, 0)
|
||||
block := gen.InsertBlockParams{
|
||||
BlockHeight: int32(src.Header.Height),
|
||||
BlockHash: src.Header.Hash.String(),
|
||||
Version: src.Header.Version,
|
||||
MerkleRoot: src.Header.MerkleRoot.String(),
|
||||
PrevBlockHash: src.Header.PrevBlock.String(),
|
||||
Timestamp: pgtype.Timestamptz{
|
||||
Time: src.Header.Timestamp,
|
||||
Valid: true,
|
||||
},
|
||||
Bits: int32(src.Header.Bits),
|
||||
Nonce: int32(src.Header.Nonce),
|
||||
}
|
||||
for txIdx, srcTx := range src.Transactions {
|
||||
tx := gen.BitcoinTransaction{
|
||||
tx := gen.InsertTransactionParams{
|
||||
TxHash: srcTx.TxHash.String(),
|
||||
Version: srcTx.Version,
|
||||
Locktime: int32(srcTx.LockTime),
|
||||
@@ -68,13 +81,12 @@ func mapBlockTypeToModel(src *types.Block) (gen.BitcoinBlock, []gen.BitcoinTrans
|
||||
txs = append(txs, tx)
|
||||
|
||||
for idx, txin := range srcTx.TxIn {
|
||||
txins = append(txins, gen.BitcoinTransactionTxin{
|
||||
TxHash: tx.TxHash,
|
||||
TxIdx: int16(idx),
|
||||
PrevoutTxHash: txin.PreviousOutTxHash.String(),
|
||||
PrevoutTxIdx: int16(txin.PreviousOutIndex),
|
||||
PrevoutPkscript: "", // Note: this will be updated while inserting to the database
|
||||
Scriptsig: hex.EncodeToString(txin.SignatureScript),
|
||||
txins = append(txins, gen.InsertTransactionTxInParams{
|
||||
TxHash: tx.TxHash,
|
||||
TxIdx: int16(idx),
|
||||
PrevoutTxHash: txin.PreviousOutTxHash.String(),
|
||||
PrevoutTxIdx: int16(txin.PreviousOutIndex),
|
||||
Scriptsig: hex.EncodeToString(txin.SignatureScript),
|
||||
// TODO: should figure out how to store this
|
||||
// Witness: pgtype.Text{
|
||||
// String: string(txin.Witness),
|
||||
@@ -85,14 +97,13 @@ func mapBlockTypeToModel(src *types.Block) (gen.BitcoinBlock, []gen.BitcoinTrans
|
||||
}
|
||||
|
||||
for idx, txout := range srcTx.TxOut {
|
||||
txouts = append(txouts, gen.BitcoinTransactionTxout{
|
||||
txouts = append(txouts, gen.InsertTransactionTxOutParams{
|
||||
TxHash: tx.TxHash,
|
||||
TxIdx: int16(idx),
|
||||
Pkscript: hex.EncodeToString(txout.PkScript),
|
||||
Value: txout.Value,
|
||||
IsSpent: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
return block, txs, txins, txouts
|
||||
return block, txs, txouts, txins
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ package postgres
|
||||
|
||||
import (
|
||||
"github.com/gaze-network/indexer-network/internal/postgres"
|
||||
"github.com/gaze-network/indexer-network/modules/bitcoin/internal/datagateway"
|
||||
"github.com/gaze-network/indexer-network/modules/bitcoin/internal/repository/postgres/gen"
|
||||
)
|
||||
|
||||
// Make sure Repository implements the BitcoinDataGateway interface
|
||||
var _ datagateway.BitcoinDataGateway = (*Repository)(nil)
|
||||
|
||||
type Repository struct {
|
||||
db postgres.DB
|
||||
queries *gen.Queries
|
||||
|
||||
Reference in New Issue
Block a user