mirror of
https://github.com/alexgo-io/gaze-brc20-indexer.git
synced 2026-01-12 14:34:54 +08:00
218 lines
7.9 KiB
Go
218 lines
7.9 KiB
Go
package brc20
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
"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/core/indexer"
|
|
"github.com/gaze-network/indexer-network/core/types"
|
|
"github.com/gaze-network/indexer-network/modules/brc20/internal/datagateway"
|
|
"github.com/gaze-network/indexer-network/modules/brc20/internal/entity"
|
|
"github.com/gaze-network/indexer-network/modules/brc20/internal/ordinals"
|
|
"github.com/gaze-network/indexer-network/pkg/btcclient"
|
|
"github.com/gaze-network/indexer-network/pkg/logger"
|
|
"github.com/gaze-network/indexer-network/pkg/logger/slogx"
|
|
"github.com/gaze-network/indexer-network/pkg/lru"
|
|
)
|
|
|
|
// Make sure to implement the Bitcoin Processor interface
|
|
var _ indexer.Processor[*types.Block] = (*Processor)(nil)
|
|
|
|
type Processor struct {
|
|
brc20Dg datagateway.BRC20DataGateway
|
|
indexerInfoDg datagateway.IndexerInfoDataGateway
|
|
btcClient btcclient.Contract
|
|
network common.Network
|
|
cleanupFuncs []func(context.Context) error
|
|
|
|
// block states
|
|
flotsamsSentAsFee []*entity.Flotsam
|
|
blockReward uint64
|
|
|
|
// processor stats
|
|
cursedInscriptionCount uint64
|
|
blessedInscriptionCount uint64
|
|
lostSats uint64
|
|
|
|
// cache
|
|
outPointValueCache *lru.Cache[wire.OutPoint, uint64]
|
|
|
|
// flush buffers
|
|
newInscriptionTransfers []*entity.InscriptionTransfer
|
|
newInscriptionEntries map[ordinals.InscriptionId]*ordinals.InscriptionEntry
|
|
newInscriptionEntryStates map[ordinals.InscriptionId]*ordinals.InscriptionEntry
|
|
}
|
|
|
|
// TODO: move this to config
|
|
const outPointValueCacheSize = 100000
|
|
|
|
func NewProcessor(brc20Dg datagateway.BRC20DataGateway, indexerInfoDg datagateway.IndexerInfoDataGateway, btcClient btcclient.Contract, network common.Network, cleanupFuncs []func(context.Context) error) (*Processor, error) {
|
|
outPointValueCache, err := lru.New[wire.OutPoint, uint64](outPointValueCacheSize)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create outPointValueCache")
|
|
}
|
|
|
|
return &Processor{
|
|
brc20Dg: brc20Dg,
|
|
indexerInfoDg: indexerInfoDg,
|
|
btcClient: btcClient,
|
|
network: network,
|
|
cleanupFuncs: cleanupFuncs,
|
|
|
|
flotsamsSentAsFee: make([]*entity.Flotsam, 0),
|
|
blockReward: 0,
|
|
|
|
cursedInscriptionCount: 0, // to be initialized by p.VerifyStates()
|
|
blessedInscriptionCount: 0, // to be initialized by p.VerifyStates()
|
|
lostSats: 0, // to be initialized by p.VerifyStates()
|
|
outPointValueCache: outPointValueCache,
|
|
|
|
newInscriptionTransfers: make([]*entity.InscriptionTransfer, 0),
|
|
newInscriptionEntries: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry),
|
|
newInscriptionEntryStates: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry),
|
|
}, nil
|
|
}
|
|
|
|
// VerifyStates implements indexer.Processor.
|
|
func (p *Processor) VerifyStates(ctx context.Context) error {
|
|
indexerState, err := p.indexerInfoDg.GetLatestIndexerState(ctx)
|
|
if err != nil && !errors.Is(err, errs.NotFound) {
|
|
return errors.Wrap(err, "failed to get latest indexer state")
|
|
}
|
|
// if not found, create indexer state
|
|
if errors.Is(err, errs.NotFound) {
|
|
if err := p.indexerInfoDg.CreateIndexerState(ctx, entity.IndexerState{
|
|
ClientVersion: ClientVersion,
|
|
DBVersion: DBVersion,
|
|
EventHashVersion: EventHashVersion,
|
|
Network: p.network,
|
|
}); err != nil {
|
|
return errors.Wrap(err, "failed to set indexer state")
|
|
}
|
|
} else {
|
|
if indexerState.DBVersion != DBVersion {
|
|
return errors.Wrapf(errs.ConflictSetting, "db version mismatch: current version is %d. Please upgrade to version %d", indexerState.DBVersion, DBVersion)
|
|
}
|
|
if indexerState.EventHashVersion != EventHashVersion {
|
|
return errors.Wrapf(errs.ConflictSetting, "event version mismatch: current version is %d. Please reset rune's db first.", indexerState.EventHashVersion, EventHashVersion)
|
|
}
|
|
if indexerState.Network != p.network {
|
|
return errors.Wrapf(errs.ConflictSetting, "network mismatch: latest indexed network is %d, configured network is %d. If you want to change the network, please reset the database", indexerState.Network, p.network)
|
|
}
|
|
}
|
|
|
|
stats, err := p.brc20Dg.GetProcessorStats(ctx)
|
|
if err != nil {
|
|
if !errors.Is(err, errs.NotFound) {
|
|
return errors.Wrap(err, "failed to count cursed inscriptions")
|
|
}
|
|
stats = &entity.ProcessorStats{
|
|
BlockHeight: uint64(startingBlockHeader[p.network].Height),
|
|
CursedInscriptionCount: 0,
|
|
BlessedInscriptionCount: 0,
|
|
LostSats: 0,
|
|
}
|
|
}
|
|
p.cursedInscriptionCount = stats.CursedInscriptionCount
|
|
p.blessedInscriptionCount = stats.BlessedInscriptionCount
|
|
p.lostSats = stats.LostSats
|
|
return nil
|
|
}
|
|
|
|
// CurrentBlock implements indexer.Processor.
|
|
func (p *Processor) CurrentBlock(ctx context.Context) (types.BlockHeader, error) {
|
|
blockHeader, err := p.brc20Dg.GetLatestBlock(ctx)
|
|
if err != nil {
|
|
if errors.Is(err, errs.NotFound) {
|
|
return startingBlockHeader[p.network], nil
|
|
}
|
|
return types.BlockHeader{}, errors.Wrap(err, "failed to get latest block")
|
|
}
|
|
return blockHeader, nil
|
|
}
|
|
|
|
// GetIndexedBlock implements indexer.Processor.
|
|
func (p *Processor) GetIndexedBlock(ctx context.Context, height int64) (types.BlockHeader, error) {
|
|
block, err := p.brc20Dg.GetIndexedBlockByHeight(ctx, height)
|
|
if err != nil {
|
|
return types.BlockHeader{}, errors.Wrap(err, "failed to get indexed block")
|
|
}
|
|
return types.BlockHeader{
|
|
Height: int64(block.Height),
|
|
Hash: block.Hash,
|
|
}, nil
|
|
}
|
|
|
|
// Name implements indexer.Processor.
|
|
func (p *Processor) Name() string {
|
|
return "brc20"
|
|
}
|
|
|
|
// RevertData implements indexer.Processor.
|
|
func (p *Processor) RevertData(ctx context.Context, from int64) error {
|
|
brc20DgTx, err := p.brc20Dg.BeginBRC20Tx(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to begin transaction")
|
|
}
|
|
defer func() {
|
|
if err := brc20DgTx.Rollback(ctx); err != nil {
|
|
logger.WarnContext(ctx, "failed to rollback transaction",
|
|
slogx.Error(err),
|
|
slogx.String("event", "rollback_brc20_insertion"),
|
|
)
|
|
}
|
|
}()
|
|
|
|
if err := brc20DgTx.DeleteIndexedBlocksSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete indexed blocks")
|
|
}
|
|
if err := brc20DgTx.DeleteProcessorStatsSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete processor stats")
|
|
}
|
|
if err := brc20DgTx.DeleteTickEntriesSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete ticks")
|
|
}
|
|
if err := brc20DgTx.DeleteTickEntryStatesSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete tick states")
|
|
}
|
|
if err := brc20DgTx.DeleteDeployEventsSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete deploy events")
|
|
}
|
|
if err := brc20DgTx.DeleteMintEventsSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete mint events")
|
|
}
|
|
if err := brc20DgTx.DeleteTransferEventsSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete transfer events")
|
|
}
|
|
if err := brc20DgTx.DeleteBalancesSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete balances")
|
|
}
|
|
if err := brc20DgTx.DeleteInscriptionEntriesSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete inscription entries")
|
|
}
|
|
if err := brc20DgTx.DeleteInscriptionEntryStatesSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete inscription entry states")
|
|
}
|
|
if err := brc20DgTx.DeleteInscriptionTransfersSinceHeight(ctx, uint64(from)); err != nil {
|
|
return errors.Wrap(err, "failed to delete inscription transfers")
|
|
}
|
|
|
|
if err := brc20DgTx.Commit(ctx); err != nil {
|
|
return errors.Wrap(err, "failed to commit transaction")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Processor) Shutdown(ctx context.Context) error {
|
|
var errs []error
|
|
for _, cleanup := range p.cleanupFuncs {
|
|
if err := cleanup(ctx); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
return errors.WithStack(errors.Join(errs...))
|
|
}
|