Compare commits

..

3 Commits

Author SHA1 Message Date
Gaze
2225fa64ae refactor: remove unused value 2024-10-04 13:20:11 +07:00
Gaze
1bd1a362ba fix: use slogx.Stringer 2024-10-04 13:15:47 +07:00
Gaze
3c5907c94d fix: add different genesis runes config for each network 2024-10-04 13:02:30 +07:00
41 changed files with 385 additions and 1543 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Setup Sqlc
uses: sqlc-dev/setup-sqlc@v4
with:
sqlc-version: "1.27.0"
sqlc-version: "1.26.0"
- name: Check Diff
run: sqlc diff

View File

@@ -101,6 +101,3 @@ linters-settings:
attr-only: true
key-naming-case: snake
args-on-sep-lines: true
gosec:
excludes:
- G115

View File

@@ -24,9 +24,6 @@ var (
// Skippable is returned when got an error but it can be skipped or ignored and continue
Skippable = errors.NewWithDepth(depth, "skippable")
// Retryable is returned when got an error but it can be retried
Retryable = errors.NewWithDepth(depth, "retryable")
// Unsupported is returned when a feature or result is not supported
Unsupported = errors.NewWithDepth(depth, "unsupported")

View File

@@ -6,7 +6,6 @@ import (
"sync"
"time"
"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/datasources"
@@ -143,7 +142,7 @@ func (i *Indexer[T]) process(ctx context.Context) (err error) {
// validate reorg from first input
{
remoteBlockHeader := firstInputHeader
if i.currentBlock.Hash != (chainhash.Hash{}) && !remoteBlockHeader.PrevBlock.IsEqual(&i.currentBlock.Hash) {
if !remoteBlockHeader.PrevBlock.IsEqual(&i.currentBlock.Hash) {
logger.WarnContext(ctx, "Detected chain reorganization. Searching for fork point...",
slogx.String("event", "reorg_detected"),
slogx.Stringer("current_hash", i.currentBlock.Hash),
@@ -216,7 +215,7 @@ func (i *Indexer[T]) process(ctx context.Context) (err error) {
return errors.Wrapf(errs.InternalError, "input is not continuous, input[%d] height: %d, input[%d] height: %d", i-1, prevHeader.Height, i, header.Height)
}
if prevHeader.Hash != (chainhash.Hash{}) && !header.PrevBlock.IsEqual(&prevHeader.Hash) {
if !header.PrevBlock.IsEqual(&prevHeader.Hash) {
logger.WarnContext(ctx, "Chain Reorganization occurred in the middle of batch fetching inputs, need to try to fetch again")
// end current round

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: blocks.sql
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: events.sql
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: nodes.sql
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: nodesales.sql
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: test.sql
package gen

View File

@@ -1,8 +1,6 @@
package httphandler
import (
"net/url"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common/errs"
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
@@ -13,10 +11,11 @@ import (
)
type getBalancesRequest struct {
paginationRequest
Wallet string `params:"wallet"`
Id string `query:"id"`
BlockHeight uint64 `query:"blockHeight"`
Limit int32 `query:"limit"`
Offset int32 `query:"offset"`
}
const (
@@ -24,20 +23,13 @@ const (
getBalancesDefaultLimit = 100
)
func (r *getBalancesRequest) Validate() error {
func (r getBalancesRequest) Validate() error {
var errList []error
if r.Wallet == "" {
errList = append(errList, errors.New("'wallet' is required"))
}
if r.Id != "" {
id, err := url.QueryUnescape(r.Id)
if err != nil {
return errors.WithStack(err)
}
r.Id = id
if !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
}
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
}
if r.Limit < 0 {
errList = append(errList, errors.New("'limit' must be non-negative"))
@@ -74,8 +66,8 @@ func (h *HttpHandler) GetBalances(ctx *fiber.Ctx) (err error) {
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
if err := req.ParseDefault(); err != nil {
return errors.WithStack(err)
if req.Limit == 0 {
req.Limit = getBalancesDefaultLimit
}
pkScript, ok := resolvePkScript(h.network, req.Wallet)

View File

@@ -40,7 +40,7 @@ func (r getBalancesBatchRequest) Validate() error {
errList = append(errList, errors.Errorf("queries[%d]: 'wallet' is required", i))
}
if query.Id != "" && !isRuneIdOrRuneName(query.Id) {
errList = append(errList, errors.Errorf("queries[%d]: id '%s' is not valid rune id or rune name", i, query.Id))
errList = append(errList, errors.Errorf("queries[%d]: 'id' is not valid rune id or rune name", i))
}
if query.Limit < 0 {
errList = append(errList, errors.Errorf("queries[%d]: 'limit' must be non-negative", i))
@@ -89,7 +89,7 @@ func (h *HttpHandler) GetBalancesBatch(ctx *fiber.Ctx) (err error) {
}
if query.Limit == 0 {
query.Limit = getBalancesDefaultLimit
query.Limit = getBalancesMaxLimit
}
balances, err := h.usecase.GetBalancesByPkScript(ctx, pkScript, blockHeight, query.Limit, query.Offset)

View File

@@ -3,7 +3,6 @@ package httphandler
import (
"bytes"
"encoding/hex"
"net/url"
"slices"
"github.com/cockroachdb/errors"
@@ -16,24 +15,21 @@ import (
)
type getHoldersRequest struct {
paginationRequest
Id string `params:"id"`
BlockHeight uint64 `query:"blockHeight"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
const (
getHoldersMaxLimit = 1000
getHoldersMaxLimit = 1000
getHoldersDefaultLimit = 100
)
func (r *getHoldersRequest) Validate() error {
func (r getHoldersRequest) Validate() error {
var errList []error
id, err := url.QueryUnescape(r.Id)
if err != nil {
return errors.WithStack(err)
}
r.Id = id
if !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
}
if r.Limit < 0 {
errList = append(errList, errors.New("'limit' must be non-negative"))
@@ -72,9 +68,6 @@ func (h *HttpHandler) GetHolders(ctx *fiber.Ctx) (err error) {
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
if err := req.ParseDefault(); err != nil {
return errors.WithStack(err)
}
blockHeight := req.BlockHeight
if blockHeight == 0 {
@@ -85,6 +78,10 @@ func (h *HttpHandler) GetHolders(ctx *fiber.Ctx) (err error) {
blockHeight = uint64(blockHeader.Height)
}
if req.Limit == 0 {
req.Limit = getHoldersDefaultLimit
}
var runeId runes.RuneId
if req.Id != "" {
var ok bool

View File

@@ -1,7 +1,6 @@
package httphandler
import (
"net/url"
"slices"
"github.com/cockroachdb/errors"
@@ -18,15 +17,10 @@ type getTokenInfoRequest struct {
BlockHeight uint64 `query:"blockHeight"`
}
func (r *getTokenInfoRequest) Validate() error {
func (r getTokenInfoRequest) Validate() error {
var errList []error
id, err := url.QueryUnescape(r.Id)
if err != nil {
return errors.WithStack(err)
}
r.Id = id
if !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
}
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
}
@@ -63,9 +57,9 @@ type getTokenInfoResult struct {
MintedAmount uint128.Uint128 `json:"mintedAmount"`
BurnedAmount uint128.Uint128 `json:"burnedAmount"`
Decimals uint8 `json:"decimals"`
DeployedAt int64 `json:"deployedAt"` // unix timestamp
DeployedAt uint64 `json:"deployedAt"` // unix timestamp
DeployedAtHeight uint64 `json:"deployedAtHeight"`
CompletedAt *int64 `json:"completedAt"` // unix timestamp
CompletedAt *uint64 `json:"completedAt"` // unix timestamp
CompletedAtHeight *uint64 `json:"completedAtHeight"`
HoldersCount int `json:"holdersCount"`
Extend tokenInfoExtend `json:"extend"`
@@ -150,9 +144,9 @@ func (h *HttpHandler) GetTokenInfo(ctx *fiber.Ctx) (err error) {
MintedAmount: mintedAmount,
BurnedAmount: runeEntry.BurnedAmount,
Decimals: runeEntry.Divisibility,
DeployedAt: runeEntry.EtchedAt.Unix(),
DeployedAt: uint64(runeEntry.EtchedAt.Unix()),
DeployedAtHeight: runeEntry.EtchingBlock,
CompletedAt: lo.Ternary(runeEntry.CompletedAt.IsZero(), nil, lo.ToPtr(runeEntry.CompletedAt.Unix())),
CompletedAt: lo.Ternary(runeEntry.CompletedAt.IsZero(), nil, lo.ToPtr(uint64(runeEntry.CompletedAt.Unix()))),
CompletedAtHeight: runeEntry.CompletedAtHeight,
HoldersCount: len(holdingBalances),
Extend: tokenInfoExtend{

View File

@@ -1,172 +0,0 @@
package httphandler
import (
"fmt"
"strings"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common/errs"
"github.com/gaze-network/indexer-network/modules/runes/runes"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
const (
getTokensMaxLimit = 1000
)
type GetTokensScope string
const (
GetTokensScopeAll GetTokensScope = "all"
GetTokensScopeOngoing GetTokensScope = "ongoing"
)
func (s GetTokensScope) IsValid() bool {
switch s {
case GetTokensScopeAll, GetTokensScopeOngoing:
return true
}
return false
}
type getTokensRequest struct {
paginationRequest
Search string `query:"search"`
BlockHeight uint64 `query:"blockHeight"`
Scope GetTokensScope `query:"scope"`
}
func (req getTokensRequest) Validate() error {
var errList []error
if err := req.paginationRequest.Validate(); err != nil {
errList = append(errList, err)
}
if req.Limit > getTokensMaxLimit {
errList = append(errList, errors.Errorf("limit must be less than or equal to 1000"))
}
if req.Scope != "" && !req.Scope.IsValid() {
errList = append(errList, errors.Errorf("invalid scope: %s", req.Scope))
}
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
}
func (req *getTokensRequest) ParseDefault() error {
if err := req.paginationRequest.ParseDefault(); err != nil {
return errors.WithStack(err)
}
if req.Scope == "" {
req.Scope = GetTokensScopeAll
}
return nil
}
type getTokensResult struct {
List []getTokenInfoResult `json:"list"`
}
type getTokensResponse = HttpResponse[getTokensResult]
func (h *HttpHandler) GetTokens(ctx *fiber.Ctx) (err error) {
var req getTokensRequest
if err := ctx.QueryParser(&req); err != nil {
return errors.WithStack(err)
}
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
if err := req.ParseDefault(); err != nil {
return errors.WithStack(err)
}
blockHeight := req.BlockHeight
if blockHeight == 0 {
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
if err != nil {
if errors.Is(err, errs.NotFound) {
return errs.NewPublicError("latest block not found")
}
return errors.Wrap(err, "error during GetLatestBlock")
}
blockHeight = uint64(blockHeader.Height)
}
// remove spacers
search := strings.Replace(strings.Replace(req.Search, "•", "", -1), ".", "", -1)
var entries []*runes.RuneEntry
switch req.Scope {
case GetTokensScopeAll:
entries, err = h.usecase.GetRuneEntries(ctx.UserContext(), search, blockHeight, req.Limit, req.Offset)
if err != nil {
return errors.Wrap(err, "error during GetRuneEntryList")
}
case GetTokensScopeOngoing:
entries, err = h.usecase.GetOngoingRuneEntries(ctx.UserContext(), search, blockHeight, req.Limit, req.Offset)
if err != nil {
return errors.Wrap(err, "error during GetRuneEntryList")
}
default:
return errs.NewPublicError(fmt.Sprintf("invalid scope: %s", req.Scope))
}
runeIds := lo.Map(entries, func(item *runes.RuneEntry, _ int) runes.RuneId { return item.RuneId })
totalHolders, err := h.usecase.GetTotalHoldersByRuneIds(ctx.UserContext(), runeIds, blockHeight)
if err != nil {
return errors.Wrap(err, "error during GetTotalHoldersByRuneIds")
}
result := make([]getTokenInfoResult, 0, len(entries))
for _, ent := range entries {
totalSupply, err := ent.Supply()
if err != nil {
return errors.Wrap(err, "cannot get total supply of rune")
}
mintedAmount, err := ent.MintedAmount()
if err != nil {
return errors.Wrap(err, "cannot get minted amount of rune")
}
circulatingSupply := mintedAmount.Sub(ent.BurnedAmount)
terms := lo.FromPtr(ent.Terms)
result = append(result, getTokenInfoResult{
Id: ent.RuneId,
Name: ent.SpacedRune,
Symbol: string(ent.Symbol),
TotalSupply: totalSupply,
CirculatingSupply: circulatingSupply,
MintedAmount: mintedAmount,
BurnedAmount: ent.BurnedAmount,
Decimals: ent.Divisibility,
DeployedAt: ent.EtchedAt.Unix(),
DeployedAtHeight: ent.EtchingBlock,
CompletedAt: lo.Ternary(ent.CompletedAt.IsZero(), nil, lo.ToPtr(ent.CompletedAt.Unix())),
CompletedAtHeight: ent.CompletedAtHeight,
HoldersCount: int(totalHolders[ent.RuneId]),
Extend: tokenInfoExtend{
Entry: entry{
Divisibility: ent.Divisibility,
Premine: ent.Premine,
Rune: ent.SpacedRune.Rune,
Spacers: ent.SpacedRune.Spacers,
Symbol: string(ent.Symbol),
Terms: entryTerms{
Amount: lo.FromPtr(terms.Amount),
Cap: lo.FromPtr(terms.Cap),
HeightStart: terms.HeightStart,
HeightEnd: terms.HeightEnd,
OffsetStart: terms.OffsetStart,
OffsetEnd: terms.OffsetEnd,
},
Turbo: ent.Turbo,
},
},
})
}
return errors.WithStack(ctx.JSON(getTokensResponse{
Result: &getTokensResult{
List: result,
},
}))
}

View File

@@ -1,171 +0,0 @@
package httphandler
import (
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common/errs"
"github.com/gaze-network/indexer-network/modules/runes/runes"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
type getTransactionByHashRequest struct {
Hash string `params:"hash"`
}
func (r getTransactionByHashRequest) Validate() error {
var errList []error
if len(r.Hash) == 0 {
errList = append(errList, errs.NewPublicError("hash is required"))
}
if len(r.Hash) > chainhash.MaxHashStringSize {
errList = append(errList, errs.NewPublicError(fmt.Sprintf("hash length must be less than or equal to %d bytes", chainhash.MaxHashStringSize)))
}
if len(errList) == 0 {
return nil
}
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
}
type getTransactionByHashResponse = HttpResponse[transaction]
func (h *HttpHandler) GetTransactionByHash(ctx *fiber.Ctx) (err error) {
var req getTransactionByHashRequest
if err := ctx.ParamsParser(&req); err != nil {
return errors.WithStack(err)
}
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
hash, err := chainhash.NewHashFromStr(req.Hash)
if err != nil {
return errs.NewPublicError("invalid transaction hash")
}
tx, err := h.usecase.GetRuneTransaction(ctx.UserContext(), *hash)
if err != nil {
if errors.Is(err, errs.NotFound) {
return fiber.NewError(fiber.StatusNotFound, "transaction not found")
}
return errors.Wrap(err, "error during GetRuneTransaction")
}
allRuneIds := make(map[runes.RuneId]struct{})
for id := range tx.Mints {
allRuneIds[id] = struct{}{}
}
for id := range tx.Burns {
allRuneIds[id] = struct{}{}
}
for _, input := range tx.Inputs {
allRuneIds[input.RuneId] = struct{}{}
}
for _, output := range tx.Outputs {
allRuneIds[output.RuneId] = struct{}{}
}
runeEntries, err := h.usecase.GetRuneEntryByRuneIdBatch(ctx.UserContext(), lo.Keys(allRuneIds))
if err != nil {
return errors.Wrap(err, "error during GetRuneEntryByRuneIdBatch")
}
respTx := &transaction{
TxHash: tx.Hash,
BlockHeight: tx.BlockHeight,
Index: tx.Index,
Timestamp: tx.Timestamp.Unix(),
Inputs: make([]txInputOutput, 0, len(tx.Inputs)),
Outputs: make([]txInputOutput, 0, len(tx.Outputs)),
Mints: make(map[string]amountWithDecimal, len(tx.Mints)),
Burns: make(map[string]amountWithDecimal, len(tx.Burns)),
Extend: runeTransactionExtend{
RuneEtched: tx.RuneEtched,
Runestone: nil,
},
}
for _, input := range tx.Inputs {
address := addressFromPkScript(input.PkScript, h.network)
respTx.Inputs = append(respTx.Inputs, txInputOutput{
PkScript: hex.EncodeToString(input.PkScript),
Address: address,
Id: input.RuneId,
Amount: input.Amount,
Decimals: runeEntries[input.RuneId].Divisibility,
Index: input.Index,
})
}
for _, output := range tx.Outputs {
address := addressFromPkScript(output.PkScript, h.network)
respTx.Outputs = append(respTx.Outputs, txInputOutput{
PkScript: hex.EncodeToString(output.PkScript),
Address: address,
Id: output.RuneId,
Amount: output.Amount,
Decimals: runeEntries[output.RuneId].Divisibility,
Index: output.Index,
})
}
for id, amount := range tx.Mints {
respTx.Mints[id.String()] = amountWithDecimal{
Amount: amount,
Decimals: runeEntries[id].Divisibility,
}
}
for id, amount := range tx.Burns {
respTx.Burns[id.String()] = amountWithDecimal{
Amount: amount,
Decimals: runeEntries[id].Divisibility,
}
}
if tx.Runestone != nil {
var e *etching
if tx.Runestone.Etching != nil {
var symbol *string
if tx.Runestone.Etching.Symbol != nil {
symbol = lo.ToPtr(string(*tx.Runestone.Etching.Symbol))
}
var t *terms
if tx.Runestone.Etching.Terms != nil {
t = &terms{
Amount: tx.Runestone.Etching.Terms.Amount,
Cap: tx.Runestone.Etching.Terms.Cap,
HeightStart: tx.Runestone.Etching.Terms.HeightStart,
HeightEnd: tx.Runestone.Etching.Terms.HeightEnd,
OffsetStart: tx.Runestone.Etching.Terms.OffsetStart,
OffsetEnd: tx.Runestone.Etching.Terms.OffsetEnd,
}
}
e = &etching{
Divisibility: tx.Runestone.Etching.Divisibility,
Premine: tx.Runestone.Etching.Premine,
Rune: tx.Runestone.Etching.Rune,
Spacers: tx.Runestone.Etching.Spacers,
Symbol: symbol,
Terms: t,
Turbo: tx.Runestone.Etching.Turbo,
}
}
respTx.Extend.Runestone = &runestone{
Cenotaph: tx.Runestone.Cenotaph,
Flaws: lo.Ternary(tx.Runestone.Cenotaph, tx.Runestone.Flaws.CollectAsString(), nil),
Etching: e,
Edicts: lo.Map(tx.Runestone.Edicts, func(ed runes.Edict, _ int) edict {
return edict{
Id: ed.Id,
Amount: ed.Amount,
Output: ed.Output,
}
}),
Mint: tx.Runestone.Mint,
Pointer: tx.Runestone.Pointer,
}
}
return errors.WithStack(ctx.JSON(getTransactionByHashResponse{
Result: respTx,
}))
}

View File

@@ -4,7 +4,6 @@ import (
"cmp"
"encoding/hex"
"fmt"
"net/url"
"slices"
"github.com/btcsuite/btcd/chaincfg/chainhash"
@@ -17,28 +16,23 @@ import (
)
type getTransactionsRequest struct {
paginationRequest
Wallet string `query:"wallet"`
Id string `query:"id"`
FromBlock int64 `query:"fromBlock"`
ToBlock int64 `query:"toBlock"`
Limit int32 `query:"limit"`
Offset int32 `query:"offset"`
}
const (
getTransactionsMaxLimit = 3000
getTransactionsMaxLimit = 3000
getTransactionsDefaultLimit = 100
)
func (r *getTransactionsRequest) Validate() error {
func (r getTransactionsRequest) Validate() error {
var errList []error
if r.Id != "" {
id, err := url.QueryUnescape(r.Id)
if err != nil {
return errors.WithStack(err)
}
r.Id = id
if !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
}
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
}
if r.FromBlock < -1 {
errList = append(errList, errors.Errorf("invalid fromBlock range"))
@@ -134,9 +128,6 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
if err := req.ParseDefault(); err != nil {
return errors.WithStack(err)
}
var pkScript []byte
if req.Wallet != "" {
@@ -155,6 +146,9 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
return errs.NewPublicError("unable to resolve rune id from \"id\"")
}
}
if req.Limit == 0 {
req.Limit = getTransactionsDefaultLimit
}
// default to latest block
if req.ToBlock == 0 {
@@ -191,22 +185,23 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
return errors.Wrap(err, "error during GetRuneTransactions")
}
allRuneIds := make(map[runes.RuneId]struct{})
var allRuneIds []runes.RuneId
for _, tx := range txs {
for id := range tx.Mints {
allRuneIds[id] = struct{}{}
allRuneIds = append(allRuneIds, id)
}
for id := range tx.Burns {
allRuneIds[id] = struct{}{}
allRuneIds = append(allRuneIds, id)
}
for _, input := range tx.Inputs {
allRuneIds[input.RuneId] = struct{}{}
allRuneIds = append(allRuneIds, input.RuneId)
}
for _, output := range tx.Outputs {
allRuneIds[output.RuneId] = struct{}{}
allRuneIds = append(allRuneIds, output.RuneId)
}
}
runeEntries, err := h.usecase.GetRuneEntryByRuneIdBatch(ctx.UserContext(), lo.Keys(allRuneIds))
allRuneIds = lo.Uniq(allRuneIds)
runeEntries, err := h.usecase.GetRuneEntryByRuneIdBatch(ctx.UserContext(), allRuneIds)
if err != nil {
if errors.Is(err, errs.NotFound) {
return errs.NewPublicError("rune entries not found")

View File

@@ -1,8 +1,6 @@
package httphandler
import (
"net/url"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common/errs"
@@ -14,30 +12,25 @@ import (
)
type getUTXOsRequest struct {
paginationRequest
Wallet string `params:"wallet"`
Id string `query:"id"`
BlockHeight uint64 `query:"blockHeight"`
Limit int32 `query:"limit"`
Offset int32 `query:"offset"`
}
const (
getUTXOsMaxLimit = 3000
getUTXOsMaxLimit = 3000
getUTXOsDefaultLimit = 100
)
func (r *getUTXOsRequest) Validate() error {
func (r getUTXOsRequest) Validate() error {
var errList []error
if r.Wallet == "" {
errList = append(errList, errors.New("'wallet' is required"))
}
if r.Id != "" {
id, err := url.QueryUnescape(r.Id)
if err != nil {
return errors.WithStack(err)
}
r.Id = id
if !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
}
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
}
if r.Limit < 0 {
errList = append(errList, errors.New("'limit' must be non-negative"))
@@ -85,15 +78,16 @@ func (h *HttpHandler) GetUTXOs(ctx *fiber.Ctx) (err error) {
if err := req.Validate(); err != nil {
return errors.WithStack(err)
}
if err := req.ParseDefault(); err != nil {
return errors.WithStack(err)
}
pkScript, ok := resolvePkScript(h.network, req.Wallet)
if !ok {
return errs.NewPublicError("unable to resolve pkscript from \"wallet\"")
}
if req.Limit == 0 {
req.Limit = getUTXOsDefaultLimit
}
blockHeight := req.BlockHeight
if blockHeight == 0 {
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())

View File

@@ -7,9 +7,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common"
"github.com/gaze-network/indexer-network/common/errs"
"github.com/gaze-network/indexer-network/modules/runes/runes"
"github.com/gaze-network/indexer-network/modules/runes/usecase"
"github.com/gaze-network/indexer-network/pkg/logger"
@@ -33,53 +31,6 @@ type HttpResponse[T any] struct {
Result *T `json:"result,omitempty"`
}
type paginationRequest struct {
Offset int32 `query:"offset"`
Limit int32 `query:"limit"`
// OrderBy string `query:"orderBy"` // ASC or DESC
// SortBy string `query:"sortBy"` // column name
}
func (req paginationRequest) Validate() error {
var errList []error
// this just safeguard for limit,
// each path should have own validation.
if req.Limit > 10000 {
errList = append(errList, errors.Errorf("too large limit"))
}
if req.Limit < 0 {
errList = append(errList, errors.Errorf("limit must be greater than or equal to 0"))
}
if req.Offset < 0 {
errList = append(errList, errors.Errorf("offset must be greater than or equal to 0"))
}
// TODO:
// if req.OrderBy != "" && req.OrderBy != "ASC" && req.OrderBy != "DESC" {
// errList = append(errList, errors.Errorf("invalid orderBy value, must be `ASC` or `DESC`"))
// }
return errs.WithPublicMessage(errors.Join(errList...), "pagination validation error")
}
func (req *paginationRequest) ParseDefault() error {
if req == nil {
return nil
}
if req.Limit == 0 {
req.Limit = 100
}
// TODO:
// if req.OrderBy == "" {
// req.OrderBy = "ASC"
// }
return nil
}
func resolvePkScript(network common.Network, wallet string) ([]byte, bool) {
if wallet == "" {
return nil, false

View File

@@ -10,13 +10,11 @@ func (h *HttpHandler) Mount(router fiber.Router) error {
r.Post("/balances/wallet/batch", h.GetBalancesBatch)
r.Get("/balances/wallet/:wallet", h.GetBalances)
r.Get("/transactions", h.GetTransactions)
r.Get("/transactions/hash/:hash", h.GetTransactionByHash)
r.Get("/holders/:id", h.GetHolders)
r.Get("/info/:id", h.GetTokenInfo)
r.Get("/utxos/wallet/:wallet", h.GetUTXOs)
r.Post("/utxos/output/batch", h.GetUTXOsOutputByLocationBatch)
r.Get("/utxos/output/:txHash", h.GetUTXOsOutputByLocation)
r.Get("/block", h.GetCurrentBlock)
r.Get("/tokens", h.GetTokens)
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"time"
"github.com/Cleverse/go-utilities/utils"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/gaze-network/indexer-network/common"
"github.com/gaze-network/indexer-network/core/types"
@@ -19,19 +20,22 @@ const (
EventHashVersion = 1
)
// starting block heights and hashes should be 1 block before activation block, as indexer will start from the block after this value
var StartingBlockHeader = map[common.Network]types.BlockHeader{
common.NetworkMainnet: {
Height: 839999,
Hash: *utils.Must(chainhash.NewHashFromStr("0000000000000000000172014ba58d66455762add0512355ad651207918494ab")),
},
common.NetworkTestnet: {
Height: 2519999,
Hash: *utils.Must(chainhash.NewHashFromStr("000000000006f45c16402f05d9075db49d3571cf5273cf4cbeaa2aa295f7c833")),
},
common.NetworkFractalMainnet: {
Height: 83999,
Hash: *utils.Must(chainhash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000000")), // TODO: Update this to match real hash
},
common.NetworkFractalTestnet: {
Height: 83999,
Hash: *utils.Must(chainhash.NewHashFromStr("00000000000000613ddfbdd1778b17cea3818febcbbf82762eafaa9461038343")),
},
}
@@ -67,7 +71,7 @@ var GenesisRuneConfigMap = map[common.Network]GenesisRuneConfig{
},
Turbo: true,
EtchingTxHash: chainhash.Hash{},
EtchedAt: time.Unix(0, 0),
EtchedAt: time.Time{},
},
common.NetworkFractalMainnet: {
RuneId: runes.RuneId{BlockHeight: 1, TxIndex: 0},
@@ -86,7 +90,7 @@ var GenesisRuneConfigMap = map[common.Network]GenesisRuneConfig{
},
Turbo: true,
EtchingTxHash: chainhash.Hash{},
EtchedAt: time.Unix(0, 0),
EtchedAt: time.Time{},
},
common.NetworkFractalTestnet: {
RuneId: runes.RuneId{BlockHeight: 1, TxIndex: 0},
@@ -105,7 +109,7 @@ var GenesisRuneConfigMap = map[common.Network]GenesisRuneConfig{
},
Turbo: true,
EtchingTxHash: chainhash.Hash{},
EtchedAt: time.Unix(0, 0),
EtchedAt: time.Time{},
},
}

View File

@@ -1,6 +1,5 @@
BEGIN;
CREATE EXTENSION pg_trgm;
-- Indexer Client Information
CREATE TABLE IF NOT EXISTS "runes_indexer_stats" (
@@ -49,7 +48,6 @@ CREATE TABLE IF NOT EXISTS "runes_entries" (
"etched_at" TIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_rune_idx ON "runes_entries" USING BTREE ("rune");
CREATE INDEX IF NOT EXISTS runes_entries_rune_gin_idx ON "runes_entries" USING GIN ("rune" gin_trgm_ops); -- to speed up queries with LIKE operator
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_number_idx ON "runes_entries" USING BTREE ("number");
CREATE TABLE IF NOT EXISTS "runes_entry_states" (

View File

@@ -13,12 +13,6 @@ SELECT * FROM balances WHERE amount > 0 ORDER BY amount DESC, pkscript LIMIT $3
-- name: GetBalanceByPkScriptAndRuneId :one
SELECT * FROM runes_balances WHERE pkscript = $1 AND rune_id = $2 AND block_height <= $3 ORDER BY block_height DESC LIMIT 1;
-- name: GetTotalHoldersByRuneIds :many
WITH balances AS (
SELECT DISTINCT ON (rune_id, pkscript) * FROM runes_balances WHERE rune_id = ANY(@rune_ids::TEXT[]) AND block_height <= @block_height ORDER BY rune_id, pkscript, block_height DESC
)
SELECT rune_id, COUNT(DISTINCT pkscript) FROM balances WHERE amount > 0 GROUP BY rune_id;
-- name: GetOutPointBalancesAtOutPoint :many
SELECT * FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2;
@@ -63,49 +57,6 @@ SELECT * FROM runes_entries
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
WHERE runes_entries.rune_id = ANY(@rune_ids::text[]) AND etching_block <= @height;
-- name: GetRuneEntries :many
WITH states AS (
-- select latest state
SELECT DISTINCT ON (rune_id) * FROM runes_entry_states WHERE block_height <= @height ORDER BY rune_id, block_height DESC
)
SELECT * FROM runes_entries
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
WHERE (
@search = '' OR
runes_entries.rune ILIKE @search || '%'
)
ORDER BY runes_entries.number
LIMIT @_limit OFFSET @_offset;
-- name: GetOngoingRuneEntries :many
WITH states AS (
-- select latest state
SELECT DISTINCT ON (rune_id) * FROM runes_entry_states WHERE block_height <= @height::integer ORDER BY rune_id, block_height DESC
)
SELECT * FROM runes_entries
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
WHERE (
runes_entries.terms = TRUE AND
COALESCE(runes_entries.terms_amount, 0) != 0 AND
COALESCE(runes_entries.terms_cap, 0) != 0 AND
states.mints < runes_entries.terms_cap AND
(
runes_entries.terms_height_start IS NULL OR runes_entries.terms_height_start <= @height::integer
) AND (
runes_entries.terms_height_end IS NULL OR @height::integer <= runes_entries.terms_height_end
) AND (
runes_entries.terms_offset_start IS NULL OR runes_entries.terms_offset_start + runes_entries.etching_block <= @height::integer
) AND (
runes_entries.terms_offset_end IS NULL OR @height::integer <= runes_entries.terms_offset_start + runes_entries.etching_block
)
) AND (
@search::text = '' OR
runes_entries.rune ILIKE '%' || @search::text || '%'
)
ORDER BY states.mints DESC
LIMIT @_limit OFFSET @_offset;
-- name: GetRuneIdFromRune :one
SELECT rune_id FROM runes_entries WHERE rune = $1;
@@ -136,24 +87,24 @@ SELECT * FROM runes_transactions
-- name: CountRuneEntries :one
SELECT COUNT(*) FROM runes_entries;
-- name: CreateRuneEntries :batchexec
-- name: CreateRuneEntry :exec
INSERT INTO runes_entries (rune_id, rune, number, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18);
-- name: CreateRuneEntryStates :batchexec
-- name: CreateRuneEntryState :exec
INSERT INTO runes_entry_states (rune_id, block_height, mints, burned_amount, completed_at, completed_at_height) VALUES ($1, $2, $3, $4, $5, $6);
-- name: CreateRuneTransactions :batchexec
-- name: CreateRuneTransaction :exec
INSERT INTO runes_transactions (hash, block_height, index, timestamp, inputs, outputs, mints, burns, rune_etched) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
-- name: CreateRunestones :batchexec
-- name: CreateRunestone :exec
INSERT INTO runes_runestones (tx_hash, block_height, etching, etching_divisibility, etching_premine, etching_rune, etching_spacers, etching_symbol, etching_terms, etching_terms_amount, etching_terms_cap, etching_terms_height_start, etching_terms_height_end, etching_terms_offset_start, etching_terms_offset_end, etching_turbo, edicts, mint, pointer, cenotaph, flaws)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21);
-- name: CreateOutPointBalances :batchexec
INSERT INTO runes_outpoint_balances (rune_id, pkscript, tx_hash, tx_idx, amount, block_height, spent_height) VALUES ($1, $2, $3, $4, $5, $6, $7);
-- name: SpendOutPointBalancesBatch :batchexec
-- name: SpendOutPointBalances :exec
UPDATE runes_outpoint_balances SET spent_height = $1 WHERE tx_hash = $2 AND tx_idx = $3;
-- name: CreateRuneBalanceAtBlock :batchexec

View File

@@ -44,10 +44,6 @@ type RunesReaderDataGateway interface {
GetRuneEntryByRuneIdAndHeight(ctx context.Context, runeId runes.RuneId, blockHeight uint64) (*runes.RuneEntry, error)
// GetRuneEntryByRuneIdAndHeightBatch returns the RuneEntries for the given runeIds and block height.
GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]*runes.RuneEntry, error)
// GetRuneEntries returns a list of rune entries, sorted by etching order. If search is not empty, it will filter the results by rune name (prefix).
GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error)
// GetOngoingRuneEntries returns a list of ongoing rune entries (can still mint), sorted by mint progress percent. If search is not empty, it will filter the results by rune name (prefix).
GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error)
// CountRuneEntries returns the number of existing rune entries.
CountRuneEntries(ctx context.Context) (uint64, error)
@@ -60,17 +56,15 @@ type RunesReaderDataGateway interface {
GetBalancesByRuneId(ctx context.Context, runeId runes.RuneId, blockHeight uint64, limit int32, offset int32) ([]*entity.Balance, error)
// GetBalancesByPkScriptAndRuneId returns the balance for the given pkScript and runeId at the given blockHeight.
GetBalanceByPkScriptAndRuneId(ctx context.Context, pkScript []byte, runeId runes.RuneId, blockHeight uint64) (*entity.Balance, error)
// GetTotalHoldersByRuneIds returns the total holders of each the given runeIds.
GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error)
}
type RunesWriterDataGateway interface {
CreateRuneEntries(ctx context.Context, entries []*runes.RuneEntry, blockHeight uint64) error
CreateRuneEntryStates(ctx context.Context, entries []*runes.RuneEntry, blockHeight uint64) error
CreateRuneEntry(ctx context.Context, entry *runes.RuneEntry, blockHeight uint64) error
CreateRuneEntryState(ctx context.Context, entry *runes.RuneEntry, blockHeight uint64) error
CreateOutPointBalances(ctx context.Context, outPointBalances []*entity.OutPointBalance) error
SpendOutPointBalancesBatch(ctx context.Context, outPoints []wire.OutPoint, blockHeight uint64) error
SpendOutPointBalances(ctx context.Context, outPoint wire.OutPoint, blockHeight uint64) error
CreateRuneBalances(ctx context.Context, params []CreateRuneBalancesParams) error
CreateRuneTransactions(ctx context.Context, txs []*entity.RuneTransaction) error
CreateRuneTransaction(ctx context.Context, tx *entity.RuneTransaction) error
CreateIndexedBlock(ctx context.Context, block *entity.IndexedBlock) error
// TODO: collapse these into a single function (ResetStateToHeight)?

View File

@@ -145,10 +145,7 @@ func (p *Processor) ensureGenesisRune(ctx context.Context, network common.Networ
EtchingTxHash: genesisRuneConfig.EtchingTxHash,
EtchedAt: genesisRuneConfig.EtchedAt,
}
if err := p.runesDg.CreateRuneEntries(ctx, []*runes.RuneEntry{runeEntry}, genesisRuneConfig.RuneId.BlockHeight); err != nil {
return errors.Wrap(err, "failed to create genesis rune entry")
}
if err := p.runesDg.CreateRuneEntryStates(ctx, []*runes.RuneEntry{runeEntry}, genesisRuneConfig.RuneId.BlockHeight); err != nil {
if err := p.runesDg.CreateRuneEntry(ctx, runeEntry, genesisRuneConfig.RuneId.BlockHeight); err != nil {
return errors.Wrap(err, "failed to create genesis rune entry")
}
}

View File

@@ -27,26 +27,19 @@ import (
func (p *Processor) Process(ctx context.Context, blocks []*types.Block) error {
for _, block := range blocks {
ctx := logger.WithContext(ctx, slog.Int64("height", block.Header.Height))
logger.InfoContext(ctx, "Processing new block",
slogx.String("event", "runes_processor_processing_block"),
slog.Int("txs", len(block.Transactions)),
)
logger.DebugContext(ctx, "Processing new block", slog.Int("txs", len(block.Transactions)))
start := time.Now()
for _, tx := range block.Transactions {
if err := p.processTx(ctx, tx, block.Header); err != nil {
return errors.Wrap(err, "failed to process tx")
}
}
timeTakenToProcess := time.Since(start)
logger.InfoContext(ctx, "Processed block",
slogx.String("event", "runes_processor_processed_block"),
slog.Duration("time_taken", timeTakenToProcess),
)
if err := p.flushBlock(ctx, block.Header); err != nil {
return errors.Wrap(err, "failed to flush block")
}
logger.DebugContext(ctx, "Inserted new block")
}
return nil
}
@@ -676,7 +669,6 @@ func (p *Processor) getRunesBalancesAtOutPoint(ctx context.Context, outPoint wir
}
func (p *Processor) flushBlock(ctx context.Context, blockHeader types.BlockHeader) error {
start := time.Now()
runesDgTx, err := p.runesDg.BeginRunesTx(ctx)
if err != nil {
return errors.Wrap(err, "failed to begin runes tx")
@@ -699,7 +691,7 @@ func (p *Processor) flushBlock(ctx context.Context, blockHeader types.BlockHeade
if err != nil && errors.Is(err, errs.NotFound) && blockHeader.Height-1 == constants.StartingBlockHeader[p.network].Height {
prevIndexedBlock = &entity.IndexedBlock{
Height: constants.StartingBlockHeader[p.network].Height,
Hash: chainhash.Hash{},
Hash: constants.StartingBlockHeader[p.network].Hash,
EventHash: chainhash.Hash{},
CumulativeEventHash: chainhash.Hash{},
}
@@ -723,82 +715,78 @@ func (p *Processor) flushBlock(ctx context.Context, blockHeader types.BlockHeade
return errors.Wrap(err, "failed to create indexed block")
}
// flush new rune entries
newRuneEntries := lo.Values(p.newRuneEntries)
if err := runesDgTx.CreateRuneEntries(ctx, newRuneEntries, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create rune entry")
{
for _, runeEntry := range p.newRuneEntries {
if err := runesDgTx.CreateRuneEntry(ctx, runeEntry, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create rune entry")
}
}
p.newRuneEntries = make(map[runes.RuneId]*runes.RuneEntry)
}
p.newRuneEntries = make(map[runes.RuneId]*runes.RuneEntry)
// flush new rune entry states
newRuneEntryStates := lo.Values(p.newRuneEntryStates)
if err := runesDgTx.CreateRuneEntryStates(ctx, newRuneEntryStates, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create rune entry state")
{
for _, runeEntry := range p.newRuneEntryStates {
if err := runesDgTx.CreateRuneEntryState(ctx, runeEntry, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create rune entry state")
}
}
p.newRuneEntryStates = make(map[runes.RuneId]*runes.RuneEntry)
}
p.newRuneEntryStates = make(map[runes.RuneId]*runes.RuneEntry)
// flush new outpoint balances
newBalances := make([]*entity.OutPointBalance, 0)
for _, balances := range p.newOutPointBalances {
newBalances = append(newBalances, balances...)
{
newBalances := make([]*entity.OutPointBalance, 0)
for _, balances := range p.newOutPointBalances {
newBalances = append(newBalances, balances...)
}
if err := runesDgTx.CreateOutPointBalances(ctx, newBalances); err != nil {
return errors.Wrap(err, "failed to create outpoint balances")
}
p.newOutPointBalances = make(map[wire.OutPoint][]*entity.OutPointBalance)
}
if err := runesDgTx.CreateOutPointBalances(ctx, newBalances); err != nil {
return errors.Wrap(err, "failed to create outpoint balances")
}
p.newOutPointBalances = make(map[wire.OutPoint][]*entity.OutPointBalance)
// flush new spend outpoints
newSpendOutPoints := p.newSpendOutPoints
if err := runesDgTx.SpendOutPointBalancesBatch(ctx, newSpendOutPoints, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create spend outpoint")
{
for _, outPoint := range p.newSpendOutPoints {
if err := runesDgTx.SpendOutPointBalances(ctx, outPoint, uint64(blockHeader.Height)); err != nil {
return errors.Wrap(err, "failed to create spend outpoint")
}
}
p.newSpendOutPoints = make([]wire.OutPoint, 0)
}
p.newSpendOutPoints = make([]wire.OutPoint, 0)
// flush new balances
params := make([]datagateway.CreateRuneBalancesParams, 0)
for pkScriptStr, balances := range p.newBalances {
pkScript, err := hex.DecodeString(pkScriptStr)
if err != nil {
return errors.Wrap(err, "failed to decode pk script")
{
params := make([]datagateway.CreateRuneBalancesParams, 0)
for pkScriptStr, balances := range p.newBalances {
pkScript, err := hex.DecodeString(pkScriptStr)
if err != nil {
return errors.Wrap(err, "failed to decode pk script")
}
for runeId, balance := range balances {
params = append(params, datagateway.CreateRuneBalancesParams{
PkScript: pkScript,
RuneId: runeId,
Balance: balance,
BlockHeight: uint64(blockHeader.Height),
})
}
}
for runeId, balance := range balances {
params = append(params, datagateway.CreateRuneBalancesParams{
PkScript: pkScript,
RuneId: runeId,
Balance: balance,
BlockHeight: uint64(blockHeader.Height),
})
if err := runesDgTx.CreateRuneBalances(ctx, params); err != nil {
return errors.Wrap(err, "failed to create balances at block")
}
p.newBalances = make(map[string]map[runes.RuneId]uint128.Uint128)
}
if err := runesDgTx.CreateRuneBalances(ctx, params); err != nil {
return errors.Wrap(err, "failed to create balances at block")
}
p.newBalances = make(map[string]map[runes.RuneId]uint128.Uint128)
// flush new rune transactions
newRuneTxs := p.newRuneTxs
if err := runesDgTx.CreateRuneTransactions(ctx, newRuneTxs); err != nil {
return errors.Wrap(err, "failed to create rune transaction")
{
for _, runeTx := range p.newRuneTxs {
if err := runesDgTx.CreateRuneTransaction(ctx, runeTx); err != nil {
return errors.Wrap(err, "failed to create rune transaction")
}
}
p.newRuneTxs = make([]*entity.RuneTransaction, 0)
}
p.newRuneTxs = make([]*entity.RuneTransaction, 0)
if err := runesDgTx.Commit(ctx); err != nil {
return errors.Wrap(err, "failed to commit runes tx")
}
timeTaken := time.Since(start)
logger.InfoContext(ctx, "Flushed block",
slogx.String("event", "runes_processor_flushed_block"),
slog.Int64("height", blockHeader.Height),
slog.String("hash", blockHeader.Hash.String()),
slog.String("event_hash", hex.EncodeToString(eventHash[:])),
slog.String("cumulative_event_hash", hex.EncodeToString(cumulativeEventHash[:])),
slog.Int("new_rune_entries", len(newRuneEntries)),
slog.Int("new_rune_entry_states", len(newRuneEntryStates)),
slog.Int("new_outpoint_balances", len(newBalances)),
slog.Int("new_spend_outpoints", len(newSpendOutPoints)),
slog.Int("new_balances", len(params)),
slog.Int("new_rune_txs", len(newRuneTxs)),
slogx.Duration("time_taken", timeTaken),
)
// submit event to reporting system
if p.reportingClient != nil {

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: batch.go
package gen
@@ -128,344 +128,3 @@ func (b *CreateRuneBalanceAtBlockBatchResults) Close() error {
b.closed = true
return b.br.Close()
}
const createRuneEntries = `-- name: CreateRuneEntries :batchexec
INSERT INTO runes_entries (rune_id, rune, number, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
`
type CreateRuneEntriesBatchResults struct {
br pgx.BatchResults
tot int
closed bool
}
type CreateRuneEntriesParams struct {
RuneID string
Rune string
Number int64
Spacers int32
Premine pgtype.Numeric
Symbol int32
Divisibility int16
Terms bool
TermsAmount pgtype.Numeric
TermsCap pgtype.Numeric
TermsHeightStart pgtype.Int4
TermsHeightEnd pgtype.Int4
TermsOffsetStart pgtype.Int4
TermsOffsetEnd pgtype.Int4
Turbo bool
EtchingBlock int32
EtchingTxHash string
EtchedAt pgtype.Timestamp
}
func (q *Queries) CreateRuneEntries(ctx context.Context, arg []CreateRuneEntriesParams) *CreateRuneEntriesBatchResults {
batch := &pgx.Batch{}
for _, a := range arg {
vals := []interface{}{
a.RuneID,
a.Rune,
a.Number,
a.Spacers,
a.Premine,
a.Symbol,
a.Divisibility,
a.Terms,
a.TermsAmount,
a.TermsCap,
a.TermsHeightStart,
a.TermsHeightEnd,
a.TermsOffsetStart,
a.TermsOffsetEnd,
a.Turbo,
a.EtchingBlock,
a.EtchingTxHash,
a.EtchedAt,
}
batch.Queue(createRuneEntries, vals...)
}
br := q.db.SendBatch(ctx, batch)
return &CreateRuneEntriesBatchResults{br, len(arg), false}
}
func (b *CreateRuneEntriesBatchResults) Exec(f func(int, error)) {
defer b.br.Close()
for t := 0; t < b.tot; t++ {
if b.closed {
if f != nil {
f(t, ErrBatchAlreadyClosed)
}
continue
}
_, err := b.br.Exec()
if f != nil {
f(t, err)
}
}
}
func (b *CreateRuneEntriesBatchResults) Close() error {
b.closed = true
return b.br.Close()
}
const createRuneEntryStates = `-- name: CreateRuneEntryStates :batchexec
INSERT INTO runes_entry_states (rune_id, block_height, mints, burned_amount, completed_at, completed_at_height) VALUES ($1, $2, $3, $4, $5, $6)
`
type CreateRuneEntryStatesBatchResults struct {
br pgx.BatchResults
tot int
closed bool
}
type CreateRuneEntryStatesParams struct {
RuneID string
BlockHeight int32
Mints pgtype.Numeric
BurnedAmount pgtype.Numeric
CompletedAt pgtype.Timestamp
CompletedAtHeight pgtype.Int4
}
func (q *Queries) CreateRuneEntryStates(ctx context.Context, arg []CreateRuneEntryStatesParams) *CreateRuneEntryStatesBatchResults {
batch := &pgx.Batch{}
for _, a := range arg {
vals := []interface{}{
a.RuneID,
a.BlockHeight,
a.Mints,
a.BurnedAmount,
a.CompletedAt,
a.CompletedAtHeight,
}
batch.Queue(createRuneEntryStates, vals...)
}
br := q.db.SendBatch(ctx, batch)
return &CreateRuneEntryStatesBatchResults{br, len(arg), false}
}
func (b *CreateRuneEntryStatesBatchResults) Exec(f func(int, error)) {
defer b.br.Close()
for t := 0; t < b.tot; t++ {
if b.closed {
if f != nil {
f(t, ErrBatchAlreadyClosed)
}
continue
}
_, err := b.br.Exec()
if f != nil {
f(t, err)
}
}
}
func (b *CreateRuneEntryStatesBatchResults) Close() error {
b.closed = true
return b.br.Close()
}
const createRuneTransactions = `-- name: CreateRuneTransactions :batchexec
INSERT INTO runes_transactions (hash, block_height, index, timestamp, inputs, outputs, mints, burns, rune_etched) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`
type CreateRuneTransactionsBatchResults struct {
br pgx.BatchResults
tot int
closed bool
}
type CreateRuneTransactionsParams struct {
Hash string
BlockHeight int32
Index int32
Timestamp pgtype.Timestamp
Inputs []byte
Outputs []byte
Mints []byte
Burns []byte
RuneEtched bool
}
func (q *Queries) CreateRuneTransactions(ctx context.Context, arg []CreateRuneTransactionsParams) *CreateRuneTransactionsBatchResults {
batch := &pgx.Batch{}
for _, a := range arg {
vals := []interface{}{
a.Hash,
a.BlockHeight,
a.Index,
a.Timestamp,
a.Inputs,
a.Outputs,
a.Mints,
a.Burns,
a.RuneEtched,
}
batch.Queue(createRuneTransactions, vals...)
}
br := q.db.SendBatch(ctx, batch)
return &CreateRuneTransactionsBatchResults{br, len(arg), false}
}
func (b *CreateRuneTransactionsBatchResults) Exec(f func(int, error)) {
defer b.br.Close()
for t := 0; t < b.tot; t++ {
if b.closed {
if f != nil {
f(t, ErrBatchAlreadyClosed)
}
continue
}
_, err := b.br.Exec()
if f != nil {
f(t, err)
}
}
}
func (b *CreateRuneTransactionsBatchResults) Close() error {
b.closed = true
return b.br.Close()
}
const createRunestones = `-- name: CreateRunestones :batchexec
INSERT INTO runes_runestones (tx_hash, block_height, etching, etching_divisibility, etching_premine, etching_rune, etching_spacers, etching_symbol, etching_terms, etching_terms_amount, etching_terms_cap, etching_terms_height_start, etching_terms_height_end, etching_terms_offset_start, etching_terms_offset_end, etching_turbo, edicts, mint, pointer, cenotaph, flaws)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
`
type CreateRunestonesBatchResults struct {
br pgx.BatchResults
tot int
closed bool
}
type CreateRunestonesParams struct {
TxHash string
BlockHeight int32
Etching bool
EtchingDivisibility pgtype.Int2
EtchingPremine pgtype.Numeric
EtchingRune pgtype.Text
EtchingSpacers pgtype.Int4
EtchingSymbol pgtype.Int4
EtchingTerms pgtype.Bool
EtchingTermsAmount pgtype.Numeric
EtchingTermsCap pgtype.Numeric
EtchingTermsHeightStart pgtype.Int4
EtchingTermsHeightEnd pgtype.Int4
EtchingTermsOffsetStart pgtype.Int4
EtchingTermsOffsetEnd pgtype.Int4
EtchingTurbo pgtype.Bool
Edicts []byte
Mint pgtype.Text
Pointer pgtype.Int4
Cenotaph bool
Flaws int32
}
func (q *Queries) CreateRunestones(ctx context.Context, arg []CreateRunestonesParams) *CreateRunestonesBatchResults {
batch := &pgx.Batch{}
for _, a := range arg {
vals := []interface{}{
a.TxHash,
a.BlockHeight,
a.Etching,
a.EtchingDivisibility,
a.EtchingPremine,
a.EtchingRune,
a.EtchingSpacers,
a.EtchingSymbol,
a.EtchingTerms,
a.EtchingTermsAmount,
a.EtchingTermsCap,
a.EtchingTermsHeightStart,
a.EtchingTermsHeightEnd,
a.EtchingTermsOffsetStart,
a.EtchingTermsOffsetEnd,
a.EtchingTurbo,
a.Edicts,
a.Mint,
a.Pointer,
a.Cenotaph,
a.Flaws,
}
batch.Queue(createRunestones, vals...)
}
br := q.db.SendBatch(ctx, batch)
return &CreateRunestonesBatchResults{br, len(arg), false}
}
func (b *CreateRunestonesBatchResults) Exec(f func(int, error)) {
defer b.br.Close()
for t := 0; t < b.tot; t++ {
if b.closed {
if f != nil {
f(t, ErrBatchAlreadyClosed)
}
continue
}
_, err := b.br.Exec()
if f != nil {
f(t, err)
}
}
}
func (b *CreateRunestonesBatchResults) Close() error {
b.closed = true
return b.br.Close()
}
const spendOutPointBalancesBatch = `-- name: SpendOutPointBalancesBatch :batchexec
UPDATE runes_outpoint_balances SET spent_height = $1 WHERE tx_hash = $2 AND tx_idx = $3
`
type SpendOutPointBalancesBatchBatchResults struct {
br pgx.BatchResults
tot int
closed bool
}
type SpendOutPointBalancesBatchParams struct {
SpentHeight pgtype.Int4
TxHash string
TxIdx int32
}
func (q *Queries) SpendOutPointBalancesBatch(ctx context.Context, arg []SpendOutPointBalancesBatchParams) *SpendOutPointBalancesBatchBatchResults {
batch := &pgx.Batch{}
for _, a := range arg {
vals := []interface{}{
a.SpentHeight,
a.TxHash,
a.TxIdx,
}
batch.Queue(spendOutPointBalancesBatch, vals...)
}
br := q.db.SendBatch(ctx, batch)
return &SpendOutPointBalancesBatchBatchResults{br, len(arg), false}
}
func (b *SpendOutPointBalancesBatchBatchResults) Exec(f func(int, error)) {
defer b.br.Close()
for t := 0; t < b.tot; t++ {
if b.closed {
if f != nil {
f(t, ErrBatchAlreadyClosed)
}
continue
}
_, err := b.br.Exec()
if f != nil {
f(t, err)
}
}
}
func (b *SpendOutPointBalancesBatchBatchResults) Close() error {
b.closed = true
return b.br.Close()
}

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: data.sql
package gen
@@ -45,6 +45,168 @@ func (q *Queries) CreateIndexedBlock(ctx context.Context, arg CreateIndexedBlock
return err
}
const createRuneEntry = `-- name: CreateRuneEntry :exec
INSERT INTO runes_entries (rune_id, rune, number, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
`
type CreateRuneEntryParams struct {
RuneID string
Rune string
Number int64
Spacers int32
Premine pgtype.Numeric
Symbol int32
Divisibility int16
Terms bool
TermsAmount pgtype.Numeric
TermsCap pgtype.Numeric
TermsHeightStart pgtype.Int4
TermsHeightEnd pgtype.Int4
TermsOffsetStart pgtype.Int4
TermsOffsetEnd pgtype.Int4
Turbo bool
EtchingBlock int32
EtchingTxHash string
EtchedAt pgtype.Timestamp
}
func (q *Queries) CreateRuneEntry(ctx context.Context, arg CreateRuneEntryParams) error {
_, err := q.db.Exec(ctx, createRuneEntry,
arg.RuneID,
arg.Rune,
arg.Number,
arg.Spacers,
arg.Premine,
arg.Symbol,
arg.Divisibility,
arg.Terms,
arg.TermsAmount,
arg.TermsCap,
arg.TermsHeightStart,
arg.TermsHeightEnd,
arg.TermsOffsetStart,
arg.TermsOffsetEnd,
arg.Turbo,
arg.EtchingBlock,
arg.EtchingTxHash,
arg.EtchedAt,
)
return err
}
const createRuneEntryState = `-- name: CreateRuneEntryState :exec
INSERT INTO runes_entry_states (rune_id, block_height, mints, burned_amount, completed_at, completed_at_height) VALUES ($1, $2, $3, $4, $5, $6)
`
type CreateRuneEntryStateParams struct {
RuneID string
BlockHeight int32
Mints pgtype.Numeric
BurnedAmount pgtype.Numeric
CompletedAt pgtype.Timestamp
CompletedAtHeight pgtype.Int4
}
func (q *Queries) CreateRuneEntryState(ctx context.Context, arg CreateRuneEntryStateParams) error {
_, err := q.db.Exec(ctx, createRuneEntryState,
arg.RuneID,
arg.BlockHeight,
arg.Mints,
arg.BurnedAmount,
arg.CompletedAt,
arg.CompletedAtHeight,
)
return err
}
const createRuneTransaction = `-- name: CreateRuneTransaction :exec
INSERT INTO runes_transactions (hash, block_height, index, timestamp, inputs, outputs, mints, burns, rune_etched) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`
type CreateRuneTransactionParams struct {
Hash string
BlockHeight int32
Index int32
Timestamp pgtype.Timestamp
Inputs []byte
Outputs []byte
Mints []byte
Burns []byte
RuneEtched bool
}
func (q *Queries) CreateRuneTransaction(ctx context.Context, arg CreateRuneTransactionParams) error {
_, err := q.db.Exec(ctx, createRuneTransaction,
arg.Hash,
arg.BlockHeight,
arg.Index,
arg.Timestamp,
arg.Inputs,
arg.Outputs,
arg.Mints,
arg.Burns,
arg.RuneEtched,
)
return err
}
const createRunestone = `-- name: CreateRunestone :exec
INSERT INTO runes_runestones (tx_hash, block_height, etching, etching_divisibility, etching_premine, etching_rune, etching_spacers, etching_symbol, etching_terms, etching_terms_amount, etching_terms_cap, etching_terms_height_start, etching_terms_height_end, etching_terms_offset_start, etching_terms_offset_end, etching_turbo, edicts, mint, pointer, cenotaph, flaws)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
`
type CreateRunestoneParams struct {
TxHash string
BlockHeight int32
Etching bool
EtchingDivisibility pgtype.Int2
EtchingPremine pgtype.Numeric
EtchingRune pgtype.Text
EtchingSpacers pgtype.Int4
EtchingSymbol pgtype.Int4
EtchingTerms pgtype.Bool
EtchingTermsAmount pgtype.Numeric
EtchingTermsCap pgtype.Numeric
EtchingTermsHeightStart pgtype.Int4
EtchingTermsHeightEnd pgtype.Int4
EtchingTermsOffsetStart pgtype.Int4
EtchingTermsOffsetEnd pgtype.Int4
EtchingTurbo pgtype.Bool
Edicts []byte
Mint pgtype.Text
Pointer pgtype.Int4
Cenotaph bool
Flaws int32
}
func (q *Queries) CreateRunestone(ctx context.Context, arg CreateRunestoneParams) error {
_, err := q.db.Exec(ctx, createRunestone,
arg.TxHash,
arg.BlockHeight,
arg.Etching,
arg.EtchingDivisibility,
arg.EtchingPremine,
arg.EtchingRune,
arg.EtchingSpacers,
arg.EtchingSymbol,
arg.EtchingTerms,
arg.EtchingTermsAmount,
arg.EtchingTermsCap,
arg.EtchingTermsHeightStart,
arg.EtchingTermsHeightEnd,
arg.EtchingTermsOffsetStart,
arg.EtchingTermsOffsetEnd,
arg.EtchingTurbo,
arg.Edicts,
arg.Mint,
arg.Pointer,
arg.Cenotaph,
arg.Flaws,
)
return err
}
const deleteIndexedBlockSinceHeight = `-- name: DeleteIndexedBlockSinceHeight :exec
DELETE FROM runes_indexed_blocks WHERE height >= $1
`
@@ -266,120 +428,6 @@ func (q *Queries) GetLatestIndexedBlock(ctx context.Context) (RunesIndexedBlock,
return i, err
}
const getOngoingRuneEntries = `-- name: GetOngoingRuneEntries :many
WITH states AS (
-- select latest state
SELECT DISTINCT ON (rune_id) rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entry_states WHERE block_height <= $1::integer ORDER BY rune_id, block_height DESC
)
SELECT runes_entries.rune_id, number, rune, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at, states.rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entries
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
WHERE (
runes_entries.terms = TRUE AND
COALESCE(runes_entries.terms_amount, 0) != 0 AND
COALESCE(runes_entries.terms_cap, 0) != 0 AND
states.mints < runes_entries.terms_cap AND
(
runes_entries.terms_height_start IS NULL OR runes_entries.terms_height_start <= $1::integer
) AND (
runes_entries.terms_height_end IS NULL OR $1::integer <= runes_entries.terms_height_end
) AND (
runes_entries.terms_offset_start IS NULL OR runes_entries.terms_offset_start + runes_entries.etching_block <= $1::integer
) AND (
runes_entries.terms_offset_end IS NULL OR $1::integer <= runes_entries.terms_offset_start + runes_entries.etching_block
)
) AND (
$2::text = '' OR
runes_entries.rune ILIKE '%' || $2::text || '%'
)
ORDER BY states.mints DESC
LIMIT $4 OFFSET $3
`
type GetOngoingRuneEntriesParams struct {
Height int32
Search string
Offset int32
Limit int32
}
type GetOngoingRuneEntriesRow struct {
RuneID string
Number int64
Rune string
Spacers int32
Premine pgtype.Numeric
Symbol int32
Divisibility int16
Terms bool
TermsAmount pgtype.Numeric
TermsCap pgtype.Numeric
TermsHeightStart pgtype.Int4
TermsHeightEnd pgtype.Int4
TermsOffsetStart pgtype.Int4
TermsOffsetEnd pgtype.Int4
Turbo bool
EtchingBlock int32
EtchingTxHash string
EtchedAt pgtype.Timestamp
RuneID_2 pgtype.Text
BlockHeight pgtype.Int4
Mints pgtype.Numeric
BurnedAmount pgtype.Numeric
CompletedAt pgtype.Timestamp
CompletedAtHeight pgtype.Int4
}
func (q *Queries) GetOngoingRuneEntries(ctx context.Context, arg GetOngoingRuneEntriesParams) ([]GetOngoingRuneEntriesRow, error) {
rows, err := q.db.Query(ctx, getOngoingRuneEntries,
arg.Height,
arg.Search,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetOngoingRuneEntriesRow
for rows.Next() {
var i GetOngoingRuneEntriesRow
if err := rows.Scan(
&i.RuneID,
&i.Number,
&i.Rune,
&i.Spacers,
&i.Premine,
&i.Symbol,
&i.Divisibility,
&i.Terms,
&i.TermsAmount,
&i.TermsCap,
&i.TermsHeightStart,
&i.TermsHeightEnd,
&i.TermsOffsetStart,
&i.TermsOffsetEnd,
&i.Turbo,
&i.EtchingBlock,
&i.EtchingTxHash,
&i.EtchedAt,
&i.RuneID_2,
&i.BlockHeight,
&i.Mints,
&i.BurnedAmount,
&i.CompletedAt,
&i.CompletedAtHeight,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOutPointBalancesAtOutPoint = `-- name: GetOutPointBalancesAtOutPoint :many
SELECT rune_id, pkscript, tx_hash, tx_idx, amount, block_height, spent_height FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2
`
@@ -417,105 +465,6 @@ func (q *Queries) GetOutPointBalancesAtOutPoint(ctx context.Context, arg GetOutP
return items, nil
}
const getRuneEntries = `-- name: GetRuneEntries :many
WITH states AS (
-- select latest state
SELECT DISTINCT ON (rune_id) rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entry_states WHERE block_height <= $4 ORDER BY rune_id, block_height DESC
)
SELECT runes_entries.rune_id, number, rune, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at, states.rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entries
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
WHERE (
$1 = '' OR
runes_entries.rune ILIKE $1 || '%'
)
ORDER BY runes_entries.number
LIMIT $3 OFFSET $2
`
type GetRuneEntriesParams struct {
Search interface{}
Offset int32
Limit int32
Height int32
}
type GetRuneEntriesRow struct {
RuneID string
Number int64
Rune string
Spacers int32
Premine pgtype.Numeric
Symbol int32
Divisibility int16
Terms bool
TermsAmount pgtype.Numeric
TermsCap pgtype.Numeric
TermsHeightStart pgtype.Int4
TermsHeightEnd pgtype.Int4
TermsOffsetStart pgtype.Int4
TermsOffsetEnd pgtype.Int4
Turbo bool
EtchingBlock int32
EtchingTxHash string
EtchedAt pgtype.Timestamp
RuneID_2 pgtype.Text
BlockHeight pgtype.Int4
Mints pgtype.Numeric
BurnedAmount pgtype.Numeric
CompletedAt pgtype.Timestamp
CompletedAtHeight pgtype.Int4
}
func (q *Queries) GetRuneEntries(ctx context.Context, arg GetRuneEntriesParams) ([]GetRuneEntriesRow, error) {
rows, err := q.db.Query(ctx, getRuneEntries,
arg.Search,
arg.Offset,
arg.Limit,
arg.Height,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetRuneEntriesRow
for rows.Next() {
var i GetRuneEntriesRow
if err := rows.Scan(
&i.RuneID,
&i.Number,
&i.Rune,
&i.Spacers,
&i.Premine,
&i.Symbol,
&i.Divisibility,
&i.Terms,
&i.TermsAmount,
&i.TermsCap,
&i.TermsHeightStart,
&i.TermsHeightEnd,
&i.TermsOffsetStart,
&i.TermsOffsetEnd,
&i.Turbo,
&i.EtchingBlock,
&i.EtchingTxHash,
&i.EtchedAt,
&i.RuneID_2,
&i.BlockHeight,
&i.Mints,
&i.BurnedAmount,
&i.CompletedAt,
&i.CompletedAtHeight,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getRuneEntriesByRuneIds = `-- name: GetRuneEntriesByRuneIds :many
WITH states AS (
-- select latest state
@@ -1022,41 +971,19 @@ func (q *Queries) GetRunesUTXOsByRuneIdAndPkScript(ctx context.Context, arg GetR
return items, nil
}
const getTotalHoldersByRuneIds = `-- name: GetTotalHoldersByRuneIds :many
WITH balances AS (
SELECT DISTINCT ON (rune_id, pkscript) pkscript, block_height, rune_id, amount FROM runes_balances WHERE rune_id = ANY($1::TEXT[]) AND block_height <= $2 ORDER BY rune_id, pkscript, block_height DESC
)
SELECT rune_id, COUNT(DISTINCT pkscript) FROM balances WHERE amount > 0 GROUP BY rune_id
const spendOutPointBalances = `-- name: SpendOutPointBalances :exec
UPDATE runes_outpoint_balances SET spent_height = $1 WHERE tx_hash = $2 AND tx_idx = $3
`
type GetTotalHoldersByRuneIdsParams struct {
RuneIds []string
BlockHeight int32
type SpendOutPointBalancesParams struct {
SpentHeight pgtype.Int4
TxHash string
TxIdx int32
}
type GetTotalHoldersByRuneIdsRow struct {
RuneID string
Count int64
}
func (q *Queries) GetTotalHoldersByRuneIds(ctx context.Context, arg GetTotalHoldersByRuneIdsParams) ([]GetTotalHoldersByRuneIdsRow, error) {
rows, err := q.db.Query(ctx, getTotalHoldersByRuneIds, arg.RuneIds, arg.BlockHeight)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTotalHoldersByRuneIdsRow
for rows.Next() {
var i GetTotalHoldersByRuneIdsRow
if err := rows.Scan(&i.RuneID, &i.Count); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
func (q *Queries) SpendOutPointBalances(ctx context.Context, arg SpendOutPointBalancesParams) error {
_, err := q.db.Exec(ctx, spendOutPointBalances, arg.SpentHeight, arg.TxHash, arg.TxIdx)
return err
}
const unspendOutPointBalancesSinceHeight = `-- name: UnspendOutPointBalancesSinceHeight :exec

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
// source: info.sql
package gen

View File

@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.26.0
package gen

View File

@@ -63,7 +63,7 @@ func mapIndexerStateTypeToParams(src entity.IndexerState) gen.SetIndexerStatePar
}
}
func mapRuneEntryModelToType(src gen.GetRuneEntriesRow) (runes.RuneEntry, error) {
func mapRuneEntryModelToType(src gen.GetRuneEntriesByRuneIdsRow) (runes.RuneEntry, error) {
runeId, err := runes.NewRuneIdFromString(src.RuneID)
if err != nil {
return runes.RuneEntry{}, errors.Wrap(err, "failed to parse rune id")
@@ -153,21 +153,21 @@ func mapRuneEntryModelToType(src gen.GetRuneEntriesRow) (runes.RuneEntry, error)
}, nil
}
func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.CreateRuneEntriesParams, gen.CreateRuneEntryStatesParams, error) {
func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.CreateRuneEntryParams, gen.CreateRuneEntryStateParams, error) {
runeId := src.RuneId.String()
rune := src.SpacedRune.Rune.String()
spacers := int32(src.SpacedRune.Spacers)
mints, err := numericFromUint128(&src.Mints)
if err != nil {
return gen.CreateRuneEntriesParams{}, gen.CreateRuneEntryStatesParams{}, errors.Wrap(err, "failed to parse mints")
return gen.CreateRuneEntryParams{}, gen.CreateRuneEntryStateParams{}, errors.Wrap(err, "failed to parse mints")
}
burnedAmount, err := numericFromUint128(&src.BurnedAmount)
if err != nil {
return gen.CreateRuneEntriesParams{}, gen.CreateRuneEntryStatesParams{}, errors.Wrap(err, "failed to parse burned amount")
return gen.CreateRuneEntryParams{}, gen.CreateRuneEntryStateParams{}, errors.Wrap(err, "failed to parse burned amount")
}
premine, err := numericFromUint128(&src.Premine)
if err != nil {
return gen.CreateRuneEntriesParams{}, gen.CreateRuneEntryStatesParams{}, errors.Wrap(err, "failed to parse premine")
return gen.CreateRuneEntryParams{}, gen.CreateRuneEntryStateParams{}, errors.Wrap(err, "failed to parse premine")
}
var completedAt pgtype.Timestamp
if !src.CompletedAt.IsZero() {
@@ -187,13 +187,13 @@ func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.Crea
if src.Terms.Amount != nil {
termsAmount, err = numericFromUint128(src.Terms.Amount)
if err != nil {
return gen.CreateRuneEntriesParams{}, gen.CreateRuneEntryStatesParams{}, errors.Wrap(err, "failed to parse terms amount")
return gen.CreateRuneEntryParams{}, gen.CreateRuneEntryStateParams{}, errors.Wrap(err, "failed to parse terms amount")
}
}
if src.Terms.Cap != nil {
termsCap, err = numericFromUint128(src.Terms.Cap)
if err != nil {
return gen.CreateRuneEntriesParams{}, gen.CreateRuneEntryStatesParams{}, errors.Wrap(err, "failed to parse terms cap")
return gen.CreateRuneEntryParams{}, gen.CreateRuneEntryStateParams{}, errors.Wrap(err, "failed to parse terms cap")
}
}
if src.Terms.HeightStart != nil {
@@ -221,9 +221,9 @@ func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.Crea
}
}
}
etchedAt := pgtype.Timestamp{Time: src.EtchedAt, Valid: true}
etchedAt := pgtype.Timestamp{Time: time.Time{}, Valid: true}
return gen.CreateRuneEntriesParams{
return gen.CreateRuneEntryParams{
RuneID: runeId,
Rune: rune,
Number: int64(src.Number),
@@ -242,7 +242,7 @@ func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.Crea
EtchingBlock: int32(src.EtchingBlock),
EtchingTxHash: src.EtchingTxHash.String(),
EtchedAt: etchedAt,
}, gen.CreateRuneEntryStatesParams{
}, gen.CreateRuneEntryStateParams{
BlockHeight: int32(blockHeight),
RuneID: runeId,
Mints: mints,
@@ -253,7 +253,7 @@ func mapRuneEntryTypeToParams(src runes.RuneEntry, blockHeight uint64) (gen.Crea
}
// mapRuneTransactionModelToType returns params for creating a new rune transaction and (optionally) a runestone.
func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneTransactionsParams, *gen.CreateRunestonesParams, error) {
func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneTransactionParams, *gen.CreateRunestoneParams, error) {
var timestamp pgtype.Timestamp
if !src.Timestamp.IsZero() {
timestamp.Time = src.Timestamp
@@ -261,11 +261,11 @@ func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneT
}
inputsBytes, err := json.Marshal(src.Inputs)
if err != nil {
return gen.CreateRuneTransactionsParams{}, nil, errors.Wrap(err, "failed to marshal inputs")
return gen.CreateRuneTransactionParams{}, nil, errors.Wrap(err, "failed to marshal inputs")
}
outputsBytes, err := json.Marshal(src.Outputs)
if err != nil {
return gen.CreateRuneTransactionsParams{}, nil, errors.Wrap(err, "failed to marshal outputs")
return gen.CreateRuneTransactionParams{}, nil, errors.Wrap(err, "failed to marshal outputs")
}
mints := make(map[string]uint128.Uint128)
for key, value := range src.Mints {
@@ -273,7 +273,7 @@ func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneT
}
mintsBytes, err := json.Marshal(mints)
if err != nil {
return gen.CreateRuneTransactionsParams{}, nil, errors.Wrap(err, "failed to marshal mints")
return gen.CreateRuneTransactionParams{}, nil, errors.Wrap(err, "failed to marshal mints")
}
burns := make(map[string]uint128.Uint128)
for key, value := range src.Burns {
@@ -281,19 +281,19 @@ func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneT
}
burnsBytes, err := json.Marshal(burns)
if err != nil {
return gen.CreateRuneTransactionsParams{}, nil, errors.Wrap(err, "failed to marshal burns")
return gen.CreateRuneTransactionParams{}, nil, errors.Wrap(err, "failed to marshal burns")
}
var runestoneParams *gen.CreateRunestonesParams
var runestoneParams *gen.CreateRunestoneParams
if src.Runestone != nil {
params, err := mapRunestoneTypeToParams(*src.Runestone, src.Hash, src.BlockHeight)
if err != nil {
return gen.CreateRuneTransactionsParams{}, nil, errors.Wrap(err, "failed to map runestone to params")
return gen.CreateRuneTransactionParams{}, nil, errors.Wrap(err, "failed to map runestone to params")
}
runestoneParams = &params
}
return gen.CreateRuneTransactionsParams{
return gen.CreateRuneTransactionParams{
Hash: src.Hash.String(),
BlockHeight: int32(src.BlockHeight),
Index: int32(src.Index),
@@ -409,15 +409,15 @@ func mapRuneTransactionModelToType(src gen.RunesTransaction) (entity.RuneTransac
}, nil
}
func mapRunestoneTypeToParams(src runes.Runestone, txHash chainhash.Hash, blockHeight uint64) (gen.CreateRunestonesParams, error) {
var runestoneParams gen.CreateRunestonesParams
func mapRunestoneTypeToParams(src runes.Runestone, txHash chainhash.Hash, blockHeight uint64) (gen.CreateRunestoneParams, error) {
var runestoneParams gen.CreateRunestoneParams
// TODO: optimize serialized edicts
edictsBytes, err := json.Marshal(src.Edicts)
if err != nil {
return gen.CreateRunestonesParams{}, errors.Wrap(err, "failed to marshal runestone edicts")
return gen.CreateRunestoneParams{}, errors.Wrap(err, "failed to marshal runestone edicts")
}
runestoneParams = gen.CreateRunestonesParams{
runestoneParams = gen.CreateRunestoneParams{
TxHash: txHash.String(),
BlockHeight: int32(blockHeight),
Edicts: edictsBytes,
@@ -433,7 +433,7 @@ func mapRunestoneTypeToParams(src runes.Runestone, txHash chainhash.Hash, blockH
if etching.Premine != nil {
premine, err := numericFromUint128(etching.Premine)
if err != nil {
return gen.CreateRunestonesParams{}, errors.Wrap(err, "failed to parse etching premine")
return gen.CreateRunestoneParams{}, errors.Wrap(err, "failed to parse etching premine")
}
runestoneParams.EtchingPremine = premine
}
@@ -452,14 +452,14 @@ func mapRunestoneTypeToParams(src runes.Runestone, txHash chainhash.Hash, blockH
if terms.Amount != nil {
amount, err := numericFromUint128(terms.Amount)
if err != nil {
return gen.CreateRunestonesParams{}, errors.Wrap(err, "failed to parse etching terms amount")
return gen.CreateRunestoneParams{}, errors.Wrap(err, "failed to parse etching terms amount")
}
runestoneParams.EtchingTermsAmount = amount
}
if terms.Cap != nil {
cap, err := numericFromUint128(terms.Cap)
if err != nil {
return gen.CreateRunestonesParams{}, errors.Wrap(err, "failed to parse etching terms cap")
return gen.CreateRunestoneParams{}, errors.Wrap(err, "failed to parse etching terms cap")
}
runestoneParams.EtchingTermsCap = cap
}

View File

@@ -122,11 +122,8 @@ func (r *Repository) GetRuneTransactions(ctx context.Context, pkScript []byte, r
func (r *Repository) GetRuneTransaction(ctx context.Context, txHash chainhash.Hash) (*entity.RuneTransaction, error) {
row, err := r.queries.GetRuneTransaction(ctx, txHash.String())
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, errors.WithStack(errs.NotFound)
}
return nil, errors.Wrap(err, "error during query")
if errors.Is(err, pgx.ErrNoRows) {
return nil, errors.WithStack(errs.NotFound)
}
runeTxModel, runestoneModel, err := extractModelRuneTxAndRunestone(gen.GetRuneTransactionsRow(row))
@@ -265,7 +262,7 @@ func (r *Repository) GetRuneEntryByRuneIdBatch(ctx context.Context, runeIds []ru
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
var errs []error
for i, runeEntryModel := range rows {
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(runeEntryModel))
runeEntry, err := mapRuneEntryModelToType(runeEntryModel)
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
continue
@@ -305,7 +302,7 @@ func (r *Repository) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, run
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
var errs []error
for i, runeEntryModel := range rows {
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(runeEntryModel))
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesByRuneIdsRow(runeEntryModel))
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
continue
@@ -319,62 +316,6 @@ func (r *Repository) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, run
return runeEntries, nil
}
func (r *Repository) GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error) {
rows, err := r.queries.GetRuneEntries(ctx, gen.GetRuneEntriesParams{
Search: search,
Height: int32(blockHeight),
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, errors.Wrap(err, "error during query")
}
runeEntries := make([]*runes.RuneEntry, 0, len(rows))
var errs []error
for i, model := range rows {
runeEntry, err := mapRuneEntryModelToType(model)
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
continue
}
runeEntries = append(runeEntries, &runeEntry)
}
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
return runeEntries, nil
}
func (r *Repository) GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error) {
rows, err := r.queries.GetOngoingRuneEntries(ctx, gen.GetOngoingRuneEntriesParams{
Search: search,
Height: int32(blockHeight),
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, errors.Wrap(err, "error during query")
}
runeEntries := make([]*runes.RuneEntry, 0, len(rows))
var errs []error
for i, model := range rows {
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(model))
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
continue
}
runeEntries = append(runeEntries, &runeEntry)
}
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
return runeEntries, nil
}
func (r *Repository) CountRuneEntries(ctx context.Context) (uint64, error) {
count, err := r.queries.CountRuneEntries(ctx)
if err != nil {
@@ -459,113 +400,49 @@ func (r *Repository) GetBalanceByPkScriptAndRuneId(ctx context.Context, pkScript
return result, nil
}
func (r *Repository) GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error) {
rows, err := r.queries.GetTotalHoldersByRuneIds(ctx, gen.GetTotalHoldersByRuneIdsParams{
RuneIds: lo.Map(runeIds, func(runeId runes.RuneId, _ int) string { return runeId.String() }),
BlockHeight: int32(blockHeight),
})
func (r *Repository) CreateRuneTransaction(ctx context.Context, tx *entity.RuneTransaction) error {
if tx == nil {
return nil
}
txParams, runestoneParams, err := mapRuneTransactionTypeToParams(*tx)
if err != nil {
return nil, errors.Wrap(err, "error during query")
return errors.Wrap(err, "failed to map rune transaction to params")
}
holders := make(map[runes.RuneId]int64, len(rows))
for _, row := range rows {
runeId, err := runes.NewRuneIdFromString(row.RuneID)
if err != nil {
return nil, errors.Wrap(err, "failed to parse RuneId")
}
holders[runeId] = row.Count
if err = r.queries.CreateRuneTransaction(ctx, txParams); err != nil {
return errors.Wrap(err, "error during exec CreateRuneTransaction")
}
return holders, nil
}
func (r *Repository) CreateRuneTransactions(ctx context.Context, txs []*entity.RuneTransaction) error {
if len(txs) == 0 {
return nil
}
txParams := make([]gen.CreateRuneTransactionsParams, 0, len(txs))
runestoneParams := make([]gen.CreateRunestonesParams, 0, len(txs))
for _, tx := range txs {
txParam, runestoneParam, err := mapRuneTransactionTypeToParams(*tx)
if err != nil {
return errors.Wrap(err, "failed to map rune transaction to params")
if runestoneParams != nil {
if err = r.queries.CreateRunestone(ctx, *runestoneParams); err != nil {
return errors.Wrap(err, "error during exec CreateRunestone")
}
txParams = append(txParams, txParam)
if runestoneParam != nil {
runestoneParams = append(runestoneParams, *runestoneParam)
}
}
createTxResults := r.queries.CreateRuneTransactions(ctx, txParams)
var execErrors []error
createTxResults.Exec(func(i int, err error) {
if err != nil {
execErrors = append(execErrors, err)
}
})
if len(execErrors) > 0 {
return errors.Wrap(errors.Join(execErrors...), "error during exec CreateRuneTransactions")
}
createRunestoneResults := r.queries.CreateRunestones(ctx, runestoneParams)
execErrors = make([]error, 0)
createRunestoneResults.Exec(func(i int, err error) {
if err != nil {
execErrors = append(execErrors, err)
}
})
if len(execErrors) > 0 {
return errors.Wrap(errors.Join(execErrors...), "error during exec CreateRunestones")
}
return nil
}
func (r *Repository) CreateRuneEntries(ctx context.Context, entries []*runes.RuneEntry, blockHeight uint64) error {
if len(entries) == 0 {
func (r *Repository) CreateRuneEntry(ctx context.Context, entry *runes.RuneEntry, blockHeight uint64) error {
if entry == nil {
return nil
}
createParams := make([]gen.CreateRuneEntriesParams, 0, len(entries))
for _, entry := range entries {
param, _, err := mapRuneEntryTypeToParams(*entry, blockHeight)
if err != nil {
return errors.Wrap(err, "failed to map rune entry to params")
}
createParams = append(createParams, param)
createParams, _, err := mapRuneEntryTypeToParams(*entry, blockHeight)
if err != nil {
return errors.Wrap(err, "failed to map rune entry to params")
}
results := r.queries.CreateRuneEntries(ctx, createParams)
var execErrors []error
results.Exec(func(i int, err error) {
if err != nil {
execErrors = append(execErrors, err)
}
})
if len(execErrors) > 0 {
return errors.Wrap(errors.Join(execErrors...), "error during exec CreateRuneEntries")
if err = r.queries.CreateRuneEntry(ctx, createParams); err != nil {
return errors.Wrap(err, "error during exec CreateRuneEntry")
}
return nil
}
func (r *Repository) CreateRuneEntryStates(ctx context.Context, entries []*runes.RuneEntry, blockHeight uint64) error {
if len(entries) == 0 {
func (r *Repository) CreateRuneEntryState(ctx context.Context, entry *runes.RuneEntry, blockHeight uint64) error {
if entry == nil {
return nil
}
createParams := make([]gen.CreateRuneEntryStatesParams, 0, len(entries))
for _, entry := range entries {
_, param, err := mapRuneEntryTypeToParams(*entry, blockHeight)
if err != nil {
return errors.Wrap(err, "failed to map rune entry to params")
}
createParams = append(createParams, param)
_, createStateParams, err := mapRuneEntryTypeToParams(*entry, blockHeight)
if err != nil {
return errors.Wrap(err, "failed to map rune entry to params")
}
results := r.queries.CreateRuneEntryStates(ctx, createParams)
var execErrors []error
results.Exec(func(i int, err error) {
if err != nil {
execErrors = append(execErrors, err)
}
})
if len(execErrors) > 0 {
return errors.Wrap(errors.Join(execErrors...), "error during exec CreateRuneEntryStates")
if err = r.queries.CreateRuneEntryState(ctx, createStateParams); err != nil {
return errors.Wrap(err, "error during exec CreateRuneEntryState")
}
return nil
}
@@ -592,25 +469,13 @@ func (r *Repository) CreateOutPointBalances(ctx context.Context, outPointBalance
return nil
}
func (r *Repository) SpendOutPointBalancesBatch(ctx context.Context, outPoints []wire.OutPoint, blockHeight uint64) error {
params := make([]gen.SpendOutPointBalancesBatchParams, 0, len(outPoints))
for _, outPoint := range outPoints {
params = append(params, gen.SpendOutPointBalancesBatchParams{
TxHash: outPoint.Hash.String(),
TxIdx: int32(outPoint.Index),
SpentHeight: pgtype.Int4{Int32: int32(blockHeight), Valid: true},
})
}
results := r.queries.SpendOutPointBalancesBatch(ctx, params)
var execErrors []error
results.Exec(func(i int, err error) {
if err != nil {
execErrors = append(execErrors, err)
}
})
if len(execErrors) > 0 {
return errors.Wrap(errors.Join(execErrors...), "error during exec")
func (r *Repository) SpendOutPointBalances(ctx context.Context, outPoint wire.OutPoint, blockHeight uint64) error {
if err := r.queries.SpendOutPointBalances(ctx, gen.SpendOutPointBalancesParams{
TxHash: outPoint.Hash.String(),
TxIdx: int32(outPoint.Index),
SpentHeight: pgtype.Int4{Int32: int32(blockHeight), Valid: true},
}); err != nil {
return errors.Wrap(err, "error during exec")
}
return nil
}

View File

@@ -67,11 +67,10 @@ func New(injector do.Injector) (indexer.IndexerWorker, error) {
}
processor := NewProcessor(runesDg, indexerInfoDg, bitcoinClient, conf.Network, reportingClient, cleanupFuncs)
if !conf.APIOnly {
if err := processor.VerifyStates(ctx); err != nil {
return nil, errors.WithStack(err)
}
if err := processor.VerifyStates(ctx); err != nil {
return nil, errors.WithStack(err)
}
// Mount API
apiHandlers := lo.Uniq(conf.Modules.Runes.APIHandlers)
for _, handler := range apiHandlers {

View File

@@ -25,11 +25,3 @@ func (u *Usecase) GetBalancesByRuneId(ctx context.Context, runeId runes.RuneId,
}
return balances, nil
}
func (u *Usecase) GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error) {
holders, err := u.runesDg.GetTotalHoldersByRuneIds(ctx, runeIds, blockHeight)
if err != nil {
return nil, errors.Wrap(err, "failed to get total holders by rune ids")
}
return holders, nil
}

View File

@@ -46,19 +46,3 @@ func (u *Usecase) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, runeId
}
return runeEntry, nil
}
func (u *Usecase) GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit, offset int32) ([]*runes.RuneEntry, error) {
entries, err := u.runesDg.GetRuneEntries(ctx, search, blockHeight, limit, offset)
if err != nil {
return nil, errors.Wrap(err, "failed to listing rune entries")
}
return entries, nil
}
func (u *Usecase) GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit, offset int32) ([]*runes.RuneEntry, error) {
entries, err := u.runesDg.GetOngoingRuneEntries(ctx, search, blockHeight, limit, offset)
if err != nil {
return nil, errors.Wrap(err, "failed to listing rune entries")
}
return entries, nil
}

View File

@@ -3,7 +3,6 @@ package usecase
import (
"context"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
"github.com/gaze-network/indexer-network/modules/runes/runes"
@@ -17,11 +16,3 @@ func (u *Usecase) GetRuneTransactions(ctx context.Context, pkScript []byte, rune
}
return txs, nil
}
func (u *Usecase) GetRuneTransaction(ctx context.Context, hash chainhash.Hash) (*entity.RuneTransaction, error) {
tx, err := u.runesDg.GetRuneTransaction(ctx, hash)
if err != nil {
return nil, errors.Wrap(err, "error during GetRuneTransaction")
}
return tx, nil
}

View File

@@ -24,11 +24,7 @@ func VerifySignature(address string, message string, sigBase64 string, defaultNe
return nil
}
type SignTxInputOptions struct {
SigHashType txscript.SigHashType
}
func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.TxOut, inputIndex int, options ...SignTxInputOptions) (*wire.MsgTx, error) {
func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.TxOut, inputIndex int) (*wire.MsgTx, error) {
if privateKey == nil {
return nil, errors.Wrap(errs.InvalidArgument, "PrivateKey is required")
}
@@ -39,14 +35,6 @@ func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.T
return nil, errors.Wrap(errs.InvalidArgument, "PrevTxOut is required")
}
// defaults sigHashType to SigHashAll | SigHashAnyOneCanPay
if len(options) == 0 {
options = append(options, SignTxInputOptions{
SigHashType: txscript.SigHashAll | txscript.SigHashAnyOneCanPay,
})
}
sigHashType := options[0].SigHashType
prevOutFetcher := txscript.NewCannedPrevOutputFetcher(prevTxOut.PkScript, prevTxOut.Value)
sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher)
if len(tx.TxIn) <= inputIndex {
@@ -65,7 +53,7 @@ func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.T
inputIndex,
prevTxOut.Value,
prevTxOut.PkScript,
sigHashType,
txscript.SigHashAll|txscript.SigHashAnyOneCanPay,
privateKey)
if err != nil {
return nil, errors.Wrap(err, "failed to sign")
@@ -78,7 +66,7 @@ func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.T
inputIndex,
prevTxOut.Value,
prevTxOut.PkScript,
sigHashType,
txscript.SigHashAll|txscript.SigHashAnyOneCanPay,
privateKey,
true,
)
@@ -91,7 +79,7 @@ func SignTxInput(tx *wire.MsgTx, privateKey *btcec.PrivateKey, prevTxOut *wire.T
tx,
inputIndex,
prevTxOut.PkScript,
sigHashType,
txscript.SigHashAll|txscript.SigHashAnyOneCanPay,
privateKey,
true,
)

View File

@@ -163,7 +163,6 @@ func TestSignTxInput(t *testing.T) {
AddOp(txscript.OP_0).
AddData(pubKeyHash).
Script()
require.NoError(t, err)
tx, prevTxOut := generateTxAndPrevTxOutFromPkScript(pkScript)
signedTx, err := SignTxInput(
@@ -176,9 +175,7 @@ func TestSignTxInput(t *testing.T) {
pubKey := privKey.PubKey()
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
address, err := btcutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams)
require.NoError(t, err)
pkScript, err := txscript.PayToAddrScript(address)
require.NoError(t, err)
tx, prevTxOut := generateTxAndPrevTxOutFromPkScript(pkScript)
signedTx, err := SignTxInput(
@@ -187,61 +184,4 @@ func TestSignTxInput(t *testing.T) {
require.NoError(t, err)
verifySignedTx(t, signedTx, prevTxOut)
})
t.Run("custom sighash type", func(t *testing.T) {
pubKey := privKey.PubKey()
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
pkScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_0).
AddData(pubKeyHash).
Script()
require.NoError(t, err)
tx, prevTxOut := generateTxAndPrevTxOutFromPkScript(pkScript)
sigHashTypes := []txscript.SigHashType{
txscript.SigHashAll,
txscript.SigHashNone,
txscript.SigHashSingle,
txscript.SigHashAll | txscript.SigHashAnyOneCanPay,
txscript.SigHashNone | txscript.SigHashAnyOneCanPay,
txscript.SigHashSingle | txscript.SigHashAnyOneCanPay,
}
for _, sigHashType := range sigHashTypes {
signedTx, err := SignTxInput(
tx, privKey, prevTxOut, 0, SignTxInputOptions{
SigHashType: sigHashType,
},
)
require.NoError(t, err)
verifySignedTx(t, signedTx, prevTxOut)
// check last byte of signature equals to sigHashType
signature := signedTx.TxIn[0].Witness[0]
assert.Equal(t, sigHashType, txscript.SigHashType(signature[len(signature)-1]))
}
})
t.Run("default sighash type", func(t *testing.T) {
pubKey := privKey.PubKey()
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
pkScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_0).
AddData(pubKeyHash).
Script()
require.NoError(t, err)
tx, prevTxOut := generateTxAndPrevTxOutFromPkScript(pkScript)
signedTx, err := SignTxInput(
tx, privKey, prevTxOut, 0,
)
require.NoError(t, err)
verifySignedTx(t, signedTx, prevTxOut)
// check last byte of signature equals to sigHashType
signature := signedTx.TxIn[0].Witness[0]
expected := txscript.SigHashAll | txscript.SigHashAnyOneCanPay
assert.Equal(t, expected, txscript.SigHashType(signature[len(signature)-1]))
})
}

View File

@@ -14,12 +14,6 @@ import (
"github.com/valyala/fasthttp"
)
var DefaultClient = fasthttp.Client{
MaxConnsPerHost: 10240, // default is 512
ReadBufferSize: 4 * 1024,
WriteBufferSize: 4 * 1024,
}
type Config struct {
// Enable debug mode
Debug bool
@@ -149,7 +143,7 @@ func (h *Client) request(ctx context.Context, reqOptions RequestOptions) (*HttpR
fasthttp.ReleaseRequest(req)
}()
if err := DefaultClient.Do(req, resp); err != nil {
if err := fasthttp.Do(req, resp); err != nil {
return nil, errors.Wrapf(err, "url: %s", url)
}