mirror of
https://github.com/luxfi/indexer.git
synced 2026-09-24 00:25:37 +00:00
Co-authored-by: dhrubabasu <[email protected]> Co-authored-by: Patrick O'Grady <[email protected]> Co-authored-by: Stephen <[email protected]>
209 lines
4.4 KiB
Go
209 lines
4.4 KiB
Go
package rewards
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ava-labs/avalanchego/api"
|
|
"github.com/ava-labs/avalanchego/ids"
|
|
"github.com/ava-labs/avalanchego/utils/formatting"
|
|
avalancheGoAvax "github.com/ava-labs/avalanchego/vms/components/avax"
|
|
"github.com/ava-labs/avalanchego/vms/platformvm"
|
|
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
|
|
"github.com/ava-labs/ortelius/db"
|
|
"github.com/ava-labs/ortelius/models"
|
|
"github.com/ava-labs/ortelius/services"
|
|
"github.com/ava-labs/ortelius/services/indexes/avax"
|
|
"github.com/ava-labs/ortelius/servicesctrl"
|
|
"github.com/ava-labs/ortelius/utils"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type Handler struct {
|
|
client platformvm.Client
|
|
conns *utils.Connections
|
|
perist db.Persist
|
|
avaxAssetID ids.ID
|
|
writer *avax.Writer
|
|
cid ids.ID
|
|
doneCh chan struct{}
|
|
}
|
|
|
|
func (r *Handler) Start(sc *servicesctrl.Control) error {
|
|
conns, err := sc.Database()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go r.runTicker(sc, conns)
|
|
return nil
|
|
}
|
|
|
|
func (r *Handler) Close() {
|
|
close(r.doneCh)
|
|
}
|
|
|
|
func (r *Handler) runTicker(sc *servicesctrl.Control, conns *utils.Connections) {
|
|
sc.Log.Info("start")
|
|
defer func() {
|
|
sc.Log.Info("stop")
|
|
}()
|
|
|
|
ticker := time.NewTicker(5 * time.Second)
|
|
|
|
r.doneCh = make(chan struct{}, 1)
|
|
|
|
r.conns = conns
|
|
r.client = platformvm.NewClient(sc.ServicesCfg.AvalancheGO)
|
|
r.perist = db.NewPersist()
|
|
|
|
r.avaxAssetID = sc.GenesisContainer.AvaxAssetID
|
|
|
|
r.cid = ids.Empty
|
|
r.writer = avax.NewWriter(r.cid.String(), r.avaxAssetID)
|
|
|
|
defer func() {
|
|
ticker.Stop()
|
|
_ = conns.Close()
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
err := r.processRewards()
|
|
if err != nil {
|
|
sc.Log.Error("failed processing rewards",
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
case <-r.doneCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Handler) processRewards() error {
|
|
job := r.conns.Stream().NewJob("rewards-handler")
|
|
sess := r.conns.DB().NewSessionForEventReceiver(job)
|
|
|
|
ctx := context.Background()
|
|
|
|
var err error
|
|
|
|
type RewardTx struct {
|
|
ID string
|
|
Txid string
|
|
Type models.BlockType
|
|
CreatedAt time.Time
|
|
}
|
|
var reardsTxs []RewardTx
|
|
_, err = sess.Select(
|
|
db.TableRewards+".id",
|
|
db.TableRewards+".txid",
|
|
db.TablePvmBlocks+".type",
|
|
db.TableRewards+".created_at",
|
|
).
|
|
From(db.TableRewards).
|
|
Join(db.TablePvmBlocks, db.TableRewards+".block_id = "+db.TablePvmBlocks+".parent_id").
|
|
Where(db.TableRewards+".processed = ? and "+db.TableRewards+".created_at < ?", 0, time.Now().Add(-3*time.Second)).
|
|
LoadContext(ctx, &reardsTxs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(reardsTxs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, rewardTx := range reardsTxs {
|
|
if rewardTx.Type == models.BlockTypeAbort {
|
|
err = r.markRewardProcessed(rewardTx.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
id, err := ids.FromString(rewardTx.Txid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var rewardsUtxos [][]byte
|
|
arg := &api.GetTxArgs{TxID: id, Encoding: formatting.Hex}
|
|
rewardsUtxos, err = r.client.GetRewardUTXOs(ctx, arg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(rewardsUtxos) == 0 {
|
|
return fmt.Errorf("no rewards %s", rewardTx.Txid)
|
|
}
|
|
|
|
err = r.processRewardUtxos(rewardsUtxos, rewardTx.CreatedAt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = r.markRewardProcessed(rewardTx.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *Handler) processRewardUtxos(rewardsUtxos [][]byte, createdAt time.Time) error {
|
|
job := r.conns.Stream().NewJob("rewards-handler-persist")
|
|
sess := r.conns.DB().NewSessionForEventReceiver(job)
|
|
|
|
dbTx, err := sess.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dbTx.RollbackUnlessCommitted()
|
|
|
|
ctx := context.Background()
|
|
|
|
for _, reawrdUtxo := range rewardsUtxos {
|
|
var utxo *avalancheGoAvax.UTXO
|
|
_, err = txs.Codec.Unmarshal(reawrdUtxo, &utxo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cCtx := services.NewConsumerContext(ctx, sess, createdAt.Unix(), int64(createdAt.Nanosecond()), r.perist)
|
|
|
|
_, _, err = r.writer.ProcessStateOut(
|
|
cCtx,
|
|
utxo.Out,
|
|
utxo.TxID,
|
|
utxo.OutputIndex,
|
|
utxo.AssetID(),
|
|
0,
|
|
0,
|
|
r.cid.String(),
|
|
false,
|
|
false,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return dbTx.Commit()
|
|
}
|
|
|
|
func (r *Handler) markRewardProcessed(id string) error {
|
|
job := r.conns.Stream().NewJob("rewards-handler")
|
|
sess := r.conns.DB().NewSessionForEventReceiver(job)
|
|
|
|
ctx := context.Background()
|
|
|
|
reward := &db.Rewards{
|
|
ID: id,
|
|
Processed: 1,
|
|
}
|
|
|
|
return r.perist.UpdateRewardsProcessed(ctx, sess, reward)
|
|
}
|