mirror of
https://github.com/alexgo-io/gaze-brc20-indexer.git
synced 2026-01-12 14:34:54 +08:00
Merge remote-tracking branch 'origin/feat/brc20-module' into feature/brc20-module-api
This commit is contained in:
@@ -54,7 +54,7 @@ var (
|
|||||||
ErrInvalidDec = errors.New("invalid dec")
|
ErrInvalidDec = errors.New("invalid dec")
|
||||||
ErrInvalidSelfMint = errors.New("invalid self_mint")
|
ErrInvalidSelfMint = errors.New("invalid self_mint")
|
||||||
ErrInvalidAmt = errors.New("invalid amt")
|
ErrInvalidAmt = errors.New("invalid amt")
|
||||||
ErrNumberOverflow = errors.New("number overflow: max value is (2^64-1) * 10^18")
|
ErrNumberOverflow = errors.New("number overflow: max value is (2^64-1)")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParsePayload(transfer *entity.InscriptionTransfer) (*Payload, error) {
|
func ParsePayload(transfer *entity.InscriptionTransfer) (*Payload, error) {
|
||||||
@@ -64,7 +64,7 @@ func ParsePayload(transfer *entity.InscriptionTransfer) (*Payload, error) {
|
|||||||
return nil, errors.Wrap(err, "failed to unmarshal payload as json")
|
return nil, errors.Wrap(err, "failed to unmarshal payload as json")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.P != "brc20" {
|
if p.P != "brc-20" {
|
||||||
return nil, errors.WithStack(ErrInvalidProtocol)
|
return nil, errors.WithStack(ErrInvalidProtocol)
|
||||||
}
|
}
|
||||||
if !Operation(p.Op).IsValid() {
|
if !Operation(p.Op).IsValid() {
|
||||||
@@ -94,6 +94,9 @@ func ParsePayload(transfer *entity.InscriptionTransfer) (*Payload, error) {
|
|||||||
if p.Dec != nil {
|
if p.Dec != nil {
|
||||||
rawDec = *p.Dec
|
rawDec = *p.Dec
|
||||||
}
|
}
|
||||||
|
if rawDec == "" {
|
||||||
|
rawDec = "18"
|
||||||
|
}
|
||||||
dec, ok := strconv.ParseUint(rawDec, 10, 16)
|
dec, ok := strconv.ParseUint(rawDec, 10, 16)
|
||||||
if ok != nil {
|
if ok != nil {
|
||||||
return nil, errors.Wrap(ok, "failed to parse dec")
|
return nil, errors.Wrap(ok, "failed to parse dec")
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ func (p *Processor) processBRC20States(ctx context.Context, transfers []*entity.
|
|||||||
}
|
}
|
||||||
payload, err := brc20.ParsePayload(transfer)
|
payload, err := brc20.ParsePayload(transfer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to parse payload")
|
// skip invalid payloads
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
payloads = append(payloads, payload)
|
payloads = append(payloads, payload)
|
||||||
ticks[payload.Tick] = struct{}{}
|
ticks[payload.Tick] = struct{}{}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transaction, blockHeader types.BlockHeader) error {
|
func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transaction, blockHeader types.BlockHeader, transfersInOutPoints map[wire.OutPoint]map[ordinals.SatPoint][]*entity.InscriptionTransfer, outpointValues map[wire.OutPoint]uint64) error {
|
||||||
ctx = logger.WithContext(ctx, slogx.String("tx_hash", tx.TxHash.String()))
|
ctx = logger.WithContext(ctx, slogx.String("tx_hash", tx.TxHash.String()))
|
||||||
envelopes := ordinals.ParseEnvelopesFromTx(tx)
|
envelopes := ordinals.ParseEnvelopesFromTx(tx)
|
||||||
inputOutPoints := lo.Map(tx.TxIn, func(txIn *types.TxIn, _ int) wire.OutPoint {
|
inputOutPoints := lo.Map(tx.TxIn, func(txIn *types.TxIn, _ int) wire.OutPoint {
|
||||||
@@ -29,18 +29,19 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
Index: txIn.PreviousOutIndex,
|
Index: txIn.PreviousOutIndex,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
transfersInOutPoints, err := p.getInscriptionTransfersInOutPoints(ctx, inputOutPoints)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to get inscriptions in outpoints")
|
|
||||||
}
|
|
||||||
// cache outpoint values for future blocks
|
// cache outpoint values for future blocks
|
||||||
for outIndex, txOut := range tx.TxOut {
|
for outIndex, txOut := range tx.TxOut {
|
||||||
p.outPointValueCache.Add(wire.OutPoint{
|
outPoint := wire.OutPoint{
|
||||||
Hash: tx.TxHash,
|
Hash: tx.TxHash,
|
||||||
Index: uint32(outIndex),
|
Index: uint32(outIndex),
|
||||||
}, uint64(txOut.Value))
|
|
||||||
}
|
}
|
||||||
if len(envelopes) == 0 && len(transfersInOutPoints) == 0 {
|
p.outPointValueCache.Add(outPoint, uint64(txOut.Value))
|
||||||
|
outpointValues[outPoint] = uint64(txOut.Value)
|
||||||
|
}
|
||||||
|
outPointsWithTransfers := lo.Keys(transfersInOutPoints)
|
||||||
|
txContainsTransfers := len(lo.Intersect(inputOutPoints, outPointsWithTransfers)) > 0
|
||||||
|
isCoinbase := tx.TxIn[0].PreviousOutTxHash.IsEqual(&chainhash.Hash{})
|
||||||
|
if len(envelopes) == 0 && !txContainsTransfers && !isCoinbase {
|
||||||
// no inscription activity, skip
|
// no inscription activity, skip
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -53,21 +54,17 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
count int
|
count int
|
||||||
})
|
})
|
||||||
idCounter := uint32(0)
|
idCounter := uint32(0)
|
||||||
inputValues, err := p.getOutPointValues(ctx, inputOutPoints)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to get outpoint values")
|
|
||||||
}
|
|
||||||
for i, input := range tx.TxIn {
|
for i, input := range tx.TxIn {
|
||||||
inputOutPoint := wire.OutPoint{
|
|
||||||
Hash: input.PreviousOutTxHash,
|
|
||||||
Index: input.PreviousOutIndex,
|
|
||||||
}
|
|
||||||
inputValue := inputValues[inputOutPoint]
|
|
||||||
// skip coinbase inputs since there can't be an inscription in coinbase
|
// skip coinbase inputs since there can't be an inscription in coinbase
|
||||||
if input.PreviousOutTxHash.IsEqual(&chainhash.Hash{}) {
|
if input.PreviousOutTxHash.IsEqual(&chainhash.Hash{}) {
|
||||||
totalInputValue += p.getBlockSubsidy(uint64(tx.BlockHeight))
|
totalInputValue += p.getBlockSubsidy(uint64(tx.BlockHeight))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
inputOutPoint := wire.OutPoint{
|
||||||
|
Hash: input.PreviousOutTxHash,
|
||||||
|
Index: input.PreviousOutIndex,
|
||||||
|
}
|
||||||
|
inputValue := outpointValues[inputOutPoint]
|
||||||
|
|
||||||
transfersInOutPoint := transfersInOutPoints[inputOutPoint]
|
transfersInOutPoint := transfersInOutPoints[inputOutPoint]
|
||||||
for satPoint, transfers := range transfersInOutPoint {
|
for satPoint, transfers := range transfersInOutPoint {
|
||||||
@@ -134,7 +131,7 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// inscriptions are no longer cursed after jubilee, but BRC20 still considers them as cursed
|
// inscriptions are no longer cursed after jubilee, but BRC20 still considers them as cursed
|
||||||
if cursed && uint64(tx.BlockHeight) > ordinals.GetJubileeHeight(p.network) {
|
if cursed && uint64(tx.BlockHeight) >= ordinals.GetJubileeHeight(p.network) {
|
||||||
cursed = false
|
cursed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +191,6 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
|
|
||||||
// if tx is coinbase, add inscriptions sent as fee to outputs of this tx
|
// if tx is coinbase, add inscriptions sent as fee to outputs of this tx
|
||||||
ownInscriptionCount := len(floatingInscriptions)
|
ownInscriptionCount := len(floatingInscriptions)
|
||||||
isCoinbase := tx.TxIn[0].PreviousOutTxHash.IsEqual(&chainhash.Hash{})
|
|
||||||
if isCoinbase {
|
if isCoinbase {
|
||||||
floatingInscriptions = append(floatingInscriptions, p.flotsamsSentAsFee...)
|
floatingInscriptions = append(floatingInscriptions, p.flotsamsSentAsFee...)
|
||||||
}
|
}
|
||||||
@@ -257,7 +253,7 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := p.updateInscriptionLocation(ctx, satPoint, flotsam, sentAsFee, tx, blockHeader); err != nil {
|
if err := p.updateInscriptionLocation(ctx, satPoint, flotsam, sentAsFee, tx, blockHeader, transfersInOutPoints); err != nil {
|
||||||
return errors.Wrap(err, "failed to update inscription location")
|
return errors.Wrap(err, "failed to update inscription location")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,7 +266,7 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
OutPoint: wire.OutPoint{},
|
OutPoint: wire.OutPoint{},
|
||||||
Offset: p.lostSats + flotsam.Offset - totalOutputValue,
|
Offset: p.lostSats + flotsam.Offset - totalOutputValue,
|
||||||
}
|
}
|
||||||
if err := p.updateInscriptionLocation(ctx, newSatPoint, flotsam, false, tx, blockHeader); err != nil {
|
if err := p.updateInscriptionLocation(ctx, newSatPoint, flotsam, false, tx, blockHeader, transfersInOutPoints); err != nil {
|
||||||
return errors.Wrap(err, "failed to update inscription location")
|
return errors.Wrap(err, "failed to update inscription location")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +283,7 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint ordinals.SatPoint, flotsam *entity.Flotsam, sentAsFee bool, tx *types.Transaction, blockHeader types.BlockHeader) error {
|
func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint ordinals.SatPoint, flotsam *entity.Flotsam, sentAsFee bool, tx *types.Transaction, blockHeader types.BlockHeader, transfersInOutPoints map[wire.OutPoint]map[ordinals.SatPoint][]*entity.InscriptionTransfer) error {
|
||||||
txOut := tx.TxOut[newSatPoint.OutPoint.Index]
|
txOut := tx.TxOut[newSatPoint.OutPoint.Index]
|
||||||
if flotsam.OriginOld != nil {
|
if flotsam.OriginOld != nil {
|
||||||
entry, err := p.getInscriptionEntryById(ctx, flotsam.InscriptionId)
|
entry, err := p.getInscriptionEntryById(ctx, flotsam.InscriptionId)
|
||||||
@@ -313,6 +309,12 @@ func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint o
|
|||||||
// track transfers even if transfer count exceeds 2 (because we need to check for reinscriptions)
|
// track transfers even if transfer count exceeds 2 (because we need to check for reinscriptions)
|
||||||
p.newInscriptionTransfers = append(p.newInscriptionTransfers, transfer)
|
p.newInscriptionTransfers = append(p.newInscriptionTransfers, transfer)
|
||||||
p.newInscriptionEntryStates[entry.Id] = entry
|
p.newInscriptionEntryStates[entry.Id] = entry
|
||||||
|
|
||||||
|
// add new transfer to transfersInOutPoints cache
|
||||||
|
if _, ok := transfersInOutPoints[newSatPoint.OutPoint]; !ok {
|
||||||
|
transfersInOutPoints[newSatPoint.OutPoint] = make(map[ordinals.SatPoint][]*entity.InscriptionTransfer)
|
||||||
|
}
|
||||||
|
transfersInOutPoints[newSatPoint.OutPoint][newSatPoint] = append(transfersInOutPoints[newSatPoint.OutPoint][newSatPoint], transfer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,6 +364,11 @@ func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint o
|
|||||||
p.newInscriptionEntries[entry.Id] = entry
|
p.newInscriptionEntries[entry.Id] = entry
|
||||||
p.newInscriptionEntryStates[entry.Id] = entry
|
p.newInscriptionEntryStates[entry.Id] = entry
|
||||||
|
|
||||||
|
// add new transfer to transfersInOutPoints cache
|
||||||
|
if _, ok := transfersInOutPoints[newSatPoint.OutPoint]; !ok {
|
||||||
|
transfersInOutPoints[newSatPoint.OutPoint] = make(map[ordinals.SatPoint][]*entity.InscriptionTransfer)
|
||||||
|
}
|
||||||
|
transfersInOutPoints[newSatPoint.OutPoint][newSatPoint] = append(transfersInOutPoints[newSatPoint.OutPoint][newSatPoint], transfer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@@ -397,6 +404,10 @@ func (p *Processor) getOutPointValues(ctx context.Context, outPoints []wire.OutP
|
|||||||
|
|
||||||
outPointsToFetch := make([]wire.OutPoint, 0)
|
outPointsToFetch := make([]wire.OutPoint, 0)
|
||||||
for i, outPoint := range outPoints {
|
for i, outPoint := range outPoints {
|
||||||
|
if outPoint.Hash == (chainhash.Hash{}) {
|
||||||
|
// skip coinbase input
|
||||||
|
continue
|
||||||
|
}
|
||||||
if cacheValues[i] != 0 {
|
if cacheValues[i] != 0 {
|
||||||
result[outPoint] = cacheValues[i]
|
result[outPoint] = cacheValues[i]
|
||||||
} else {
|
} else {
|
||||||
@@ -415,7 +426,7 @@ func (p *Processor) getOutPointValues(ctx context.Context, outPoints []wire.OutP
|
|||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
txOuts, err := p.btcClient.GetTransactionOutputs(ectx, txHash)
|
txOuts, err := p.btcClient.GetTransactionOutputs(ectx, txHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to get transaction outputs")
|
return errors.Wrapf(err, "failed to get transaction outputs for hash %s", txHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update cache
|
// update cache
|
||||||
@@ -433,6 +444,10 @@ func (p *Processor) getOutPointValues(ctx context.Context, outPoints []wire.OutP
|
|||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
for i := range outPoints {
|
for i := range outPoints {
|
||||||
|
if outPoints[i].Hash == (chainhash.Hash{}) {
|
||||||
|
// skip coinbase input
|
||||||
|
continue
|
||||||
|
}
|
||||||
if result[outPoints[i]] == 0 {
|
if result[outPoints[i]] == 0 {
|
||||||
result[outPoints[i]] = uint64(txOutsByHash[outPoints[i].Hash][outPoints[i].Index].Value)
|
result[outPoints[i]] = uint64(txOutsByHash[outPoints[i].Hash][outPoints[i].Index].Value)
|
||||||
}
|
}
|
||||||
@@ -454,7 +469,6 @@ func (p *Processor) getInscriptionTransfersInOutPoints(ctx context.Context, outP
|
|||||||
result[outPoint] = make(map[ordinals.SatPoint][]*entity.InscriptionTransfer)
|
result[outPoint] = make(map[ordinals.SatPoint][]*entity.InscriptionTransfer)
|
||||||
}
|
}
|
||||||
result[outPoint][transfer.NewSatPoint] = append(result[outPoint][transfer.NewSatPoint], transfer)
|
result[outPoint][transfer.NewSatPoint] = append(result[outPoint][transfer.NewSatPoint], transfer)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gaze-network/indexer-network/common/errs"
|
"github.com/gaze-network/indexer-network/common/errs"
|
||||||
"github.com/gaze-network/indexer-network/core/types"
|
"github.com/gaze-network/indexer-network/core/types"
|
||||||
@@ -20,14 +22,61 @@ import (
|
|||||||
func (p *Processor) Process(ctx context.Context, blocks []*types.Block) error {
|
func (p *Processor) Process(ctx context.Context, blocks []*types.Block) error {
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
ctx = logger.WithContext(ctx, slogx.Uint64("height", uint64(block.Header.Height)))
|
ctx = logger.WithContext(ctx, slogx.Uint64("height", uint64(block.Header.Height)))
|
||||||
logger.DebugContext(ctx, "Processing new block")
|
|
||||||
p.blockReward = p.getBlockSubsidy(uint64(block.Header.Height))
|
p.blockReward = p.getBlockSubsidy(uint64(block.Header.Height))
|
||||||
p.flotsamsSentAsFee = make([]*entity.Flotsam, 0)
|
p.flotsamsSentAsFee = make([]*entity.Flotsam, 0)
|
||||||
|
|
||||||
// put coinbase tx (first tx) at the end of block
|
// put coinbase tx (first tx) at the end of block
|
||||||
transactions := append(block.Transactions[1:], block.Transactions[0])
|
transactions := append(block.Transactions[1:], block.Transactions[0])
|
||||||
|
|
||||||
|
var inputOutPoints []wire.OutPoint
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
if err := p.processInscriptionTx(ctx, tx, block.Header); err != nil {
|
for _, txIn := range tx.TxIn {
|
||||||
|
if txIn.PreviousOutTxHash == (chainhash.Hash{}) {
|
||||||
|
// skip coinbase input
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inputOutPoints = append(inputOutPoints, wire.OutPoint{
|
||||||
|
Hash: txIn.PreviousOutTxHash,
|
||||||
|
Index: txIn.PreviousOutIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pre-fetch inscriptions in outpoints
|
||||||
|
transfersInOutPoints, err := p.getInscriptionTransfersInOutPoints(ctx, inputOutPoints)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get inscriptions in outpoints")
|
||||||
|
}
|
||||||
|
// pre-fetch outpoint values for transactions with inscriptions/envelopes
|
||||||
|
outPointsToFetchValues := make([]wire.OutPoint, 0)
|
||||||
|
for _, tx := range transactions {
|
||||||
|
txInputOutPoints := lo.Map(tx.TxIn, func(txIn *types.TxIn, _ int) wire.OutPoint {
|
||||||
|
return wire.OutPoint{
|
||||||
|
Hash: txIn.PreviousOutTxHash,
|
||||||
|
Index: txIn.PreviousOutIndex,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
envelopes := ordinals.ParseEnvelopesFromTx(tx)
|
||||||
|
outPointsWithTransfers := lo.Keys(transfersInOutPoints)
|
||||||
|
txContainsTransfers := len(lo.Intersect(txInputOutPoints, outPointsWithTransfers)) > 0
|
||||||
|
isCoinbase := tx.TxIn[0].PreviousOutTxHash.IsEqual(&chainhash.Hash{})
|
||||||
|
if len(envelopes) == 0 && !txContainsTransfers && !isCoinbase {
|
||||||
|
// no inscription activity, skip tx
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outPointsToFetchValues = append(outPointsToFetchValues, lo.Map(tx.TxIn, func(txIn *types.TxIn, _ int) wire.OutPoint {
|
||||||
|
return wire.OutPoint{
|
||||||
|
Hash: txIn.PreviousOutTxHash,
|
||||||
|
Index: txIn.PreviousOutIndex,
|
||||||
|
}
|
||||||
|
})...)
|
||||||
|
}
|
||||||
|
outPointValues, err := p.getOutPointValues(ctx, outPointsToFetchValues)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get input values")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range transactions {
|
||||||
|
if err := p.processInscriptionTx(ctx, tx, block.Header, transfersInOutPoints, outPointValues); err != nil {
|
||||||
return errors.Wrap(err, "failed to process tx")
|
return errors.Wrap(err, "failed to process tx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user