Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/originflr/Banff_block_support'
Browse files Browse the repository at this point in the history
  • Loading branch information
mboben committed Nov 11, 2024
2 parents cd582e0 + fe0dd15 commit 666b49e
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 39 deletions.
1 change: 1 addition & 0 deletions database/pchain_entities.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type PChainTx struct {
Bytes []byte `gorm:"type:mediumblob"`
FeePercentage uint32 // Fee percentage (in case of add validator transaction)
ChainTime *time.Time // Chain time, time of the previous advance time transaction
BlockTime *time.Time `gorm:"index"` // Block time, non-null from Banff block activation on (Avalanche 1.9.0)
}

type PChainTxInput struct {
Expand Down
24 changes: 14 additions & 10 deletions database/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ const (
type PChainTxType string

const (
PChainRewardValidatorTx PChainTxType = "REWARD_TX"
PChainAddDelegatorTx PChainTxType = "ADD_DELEGATOR_TX"
PChainAddValidatorTx PChainTxType = "ADD_VALIDATOR_TX"
PChainImportTx PChainTxType = "IMPORT_TX"
PChainExportTx PChainTxType = "EXPORT_TX"
PChainAdvanceTimeTx PChainTxType = "ADVANCE_TIME_TX"
PChainCreateChainTx PChainTxType = "CREATE_CHAIN_TX"
PChainCreateSubnetTx PChainTxType = "CREATE_SUBNET_TX"
PChainAddSubnetValidatorTx PChainTxType = "ADD_SUBNET_VALIDATOR_TX"
PChainUnknownTx PChainTxType = "UNKNOWN_TX"
PChainRewardValidatorTx PChainTxType = "REWARD_TX"
PChainAddDelegatorTx PChainTxType = "ADD_DELEGATOR_TX"
PChainAddValidatorTx PChainTxType = "ADD_VALIDATOR_TX"
PChainImportTx PChainTxType = "IMPORT_TX"
PChainExportTx PChainTxType = "EXPORT_TX"
PChainAdvanceTimeTx PChainTxType = "ADVANCE_TIME_TX"
PChainCreateChainTx PChainTxType = "CREATE_CHAIN_TX"
PChainCreateSubnetTx PChainTxType = "CREATE_SUBNET_TX"
PChainAddSubnetValidatorTx PChainTxType = "ADD_SUBNET_VALIDATOR_TX"
PChainRemoveSubnetValidatorTx PChainTxType = "REMOVE_SUBNET_VALIDATOR_TX"
PChainTransformSubnetTx PChainTxType = "TRANSFORM_SUBNET_TX"
PChainAddPermissionlessValidatorTx PChainTxType = "ADD_PERMISSIONLESS_VALIDATOR_TX"
PChainAddPermissionlessDelegatorTx PChainTxType = "ADD_PERMISSIONLESS_DELEGATOR_TX"
PChainUnknownTx PChainTxType = "UNKNOWN_TX"
)

type PChainBlockType string
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module flare-indexer

go 1.22
go 1.21

require (
github.com/BurntSushi/toml v1.2.1
Expand Down
9 changes: 6 additions & 3 deletions indexer/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type IndexerContext interface {
}

type IndexerFlags struct {
Version bool

ConfigFileName string

// Set start epoch for voting cronjob to this value, overrides config and database value,
Expand All @@ -33,8 +35,7 @@ type indexerContext struct {
flags *IndexerFlags
}

func BuildContext() (IndexerContext, error) {
flags := parseIndexerFlags()
func BuildContext(flags *IndexerFlags) (IndexerContext, error) {
cfg, err := config.BuildConfig(flags.ConfigFileName)
if err != nil {
return nil, err
Expand All @@ -59,13 +60,15 @@ func (c *indexerContext) DB() *gorm.DB { return c.db }

func (c *indexerContext) Flags() *IndexerFlags { return c.flags }

func parseIndexerFlags() *IndexerFlags {
func ParseIndexerFlags() *IndexerFlags {
cfgFlag := flag.String("config", globalConfig.CONFIG_FILE, "Configuration file (toml format)")
versionFlag := flag.Bool("version", false, "Print version information and exit")
resetVotingFlag := flag.Int64("reset-voting", 0, "Set start epoch for voting cronjob to this value, overrides config and database value, valid values are > 0")
resetMirrorFlag := flag.Int64("reset-mirroring", 0, "Set start epoch for mirroring cronjob to this value, overrides config and database value, valid values are > 0")
flag.Parse()

return &IndexerFlags{
Version: *versionFlag,
ConfigFileName: *cfgFlag,
ResetVotingCronjob: *resetVotingFlag,
ResetMirrorCronjob: *resetMirrorFlag,
Expand Down
9 changes: 8 additions & 1 deletion indexer/main/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ import (
)

func main() {
ctx, err := context.BuildContext()
flags := context.ParseIndexerFlags()

if flags.Version {
fmt.Printf("Flare P-chain indexer version %s\n", shared.ApplicationVersion)
return
}

ctx, err := context.BuildContext(flags)
if err != nil {
fmt.Printf("%v\n", err)
return
Expand Down
49 changes: 37 additions & 12 deletions indexer/pchain/batch_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/ava-labs/avalanchego/vms/platformvm/blocks"
"github.com/ava-labs/avalanchego/vms/platformvm/fx"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/proposervm/block"
mapset "github.com/deckarep/golang-set/v2"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -91,26 +90,37 @@ func (xi *txBatchIndexer) Reset(containerLen int) (err error) {
}

func (xi *txBatchIndexer) AddContainer(index uint64, container indexer.Container) error {
blk, err := block.Parse(container.Bytes)
if err != nil {
return err
}
innerBlk, err := blocks.Parse(blocks.GenesisCodec, blk.Block())
innerBlk, err := chain.ParsePChainBlock(container.Bytes)
if err != nil {
return err
}

switch innerBlkType := innerBlk.(type) {
case *blocks.ApricotProposalBlock:
tx := innerBlkType.Tx
err = xi.addTx(&container, database.PChainProposalBlock, innerBlk.Height(), tx)
err = xi.addTx(&container, database.PChainProposalBlock, innerBlk.Height(), 0, tx)
case *blocks.ApricotCommitBlock:
xi.addEmptyTx(&container, database.PChainCommitBlock, innerBlk.Height())
xi.addEmptyTx(&container, database.PChainCommitBlock, innerBlk.Height(), 0)
case *blocks.ApricotAbortBlock:
xi.addEmptyTx(&container, database.PChainAbortBlock, innerBlk.Height())
xi.addEmptyTx(&container, database.PChainAbortBlock, innerBlk.Height(), 0)
case *blocks.ApricotStandardBlock:
for _, tx := range innerBlkType.Txs() {
err = xi.addTx(&container, database.PChainStandardBlock, innerBlk.Height(), tx)
err = xi.addTx(&container, database.PChainStandardBlock, innerBlk.Height(), 0, tx)
if err != nil {
break
}
}
// Banff blocks were introduced in Avalanche 1.9.0
case *blocks.BanffProposalBlock:
tx := innerBlkType.Tx
err = xi.addTx(&container, database.PChainProposalBlock, innerBlk.Height(), innerBlkType.Time, tx)
case *blocks.BanffCommitBlock:
xi.addEmptyTx(&container, database.PChainCommitBlock, innerBlk.Height(), innerBlkType.Time)
case *blocks.BanffAbortBlock:
xi.addEmptyTx(&container, database.PChainAbortBlock, innerBlk.Height(), innerBlkType.Time)
case *blocks.BanffStandardBlock:
for _, tx := range innerBlkType.Txs() {
err = xi.addTx(&container, database.PChainStandardBlock, innerBlk.Height(), innerBlkType.Time, tx)
if err != nil {
break
}
Expand All @@ -125,7 +135,7 @@ func (xi *txBatchIndexer) ProcessBatch() error {
return xi.inOutIndexer.ProcessBatch()
}

func (xi *txBatchIndexer) addTx(container *indexer.Container, blockType database.PChainBlockType, height uint64, tx *txs.Tx) error {
func (xi *txBatchIndexer) addTx(container *indexer.Container, blockType database.PChainBlockType, height uint64, blockTime uint64, tx *txs.Tx) error {
txID := tx.ID().String()
dbTx := &database.PChainTx{}
dbTx.TxID = &txID
Expand All @@ -135,6 +145,10 @@ func (xi *txBatchIndexer) addTx(container *indexer.Container, blockType database
dbTx.Timestamp = chain.TimestampToTime(container.Timestamp)
dbTx.Bytes = container.Bytes
dbTx.ChainTime = xi.chainTime
if blockTime != 0 {
time := time.Unix(int64(blockTime), 0)
dbTx.BlockTime = &time
}

var err error = nil
switch unsignedTx := tx.Unsigned.(type) {
Expand All @@ -156,6 +170,13 @@ func (xi *txBatchIndexer) addTx(container *indexer.Container, blockType database
err = xi.updateGeneralBaseTx(dbTx, database.PChainCreateChainTx, &unsignedTx.BaseTx)
case *txs.CreateSubnetTx:
err = xi.updateGeneralBaseTx(dbTx, database.PChainCreateSubnetTx, &unsignedTx.BaseTx)
case *txs.RemoveSubnetValidatorTx:
err = xi.updateGeneralBaseTx(dbTx, database.PChainRemoveSubnetValidatorTx, &unsignedTx.BaseTx)
case *txs.TransformSubnetTx:
err = xi.updateGeneralBaseTx(dbTx, database.PChainTransformSubnetTx, &unsignedTx.BaseTx)
// We leave out the following transaction types as they are rejected by Flare nodes
// - AddPermissionlessValidatorTx
// - AddPermissionlessDelegatorTx
default:
err = fmt.Errorf("p-chain transaction %v with type %T in block %d is not indexed", dbTx.TxID, unsignedTx, height)
}
Expand All @@ -165,14 +186,18 @@ func (xi *txBatchIndexer) addTx(container *indexer.Container, blockType database
return xi.addAddresses(tx)
}

func (xi *txBatchIndexer) addEmptyTx(container *indexer.Container, blockType database.PChainBlockType, height uint64) {
func (xi *txBatchIndexer) addEmptyTx(container *indexer.Container, blockType database.PChainBlockType, height uint64, blockTime uint64) {
dbTx := &database.PChainTx{}
dbTx.BlockID = container.ID.String()
dbTx.BlockType = blockType
dbTx.BlockHeight = height
dbTx.Timestamp = chain.TimestampToTime(container.Timestamp)
dbTx.Bytes = container.Bytes
dbTx.TxID = nil
if blockTime != 0 {
time := time.Unix(int64(blockTime), 0)
dbTx.BlockTime = &time
}

xi.newTxs = append(xi.newTxs, dbTx)
}
Expand Down
5 changes: 5 additions & 0 deletions indexer/pchain/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
func init() {
migrations.Container.Add("2023-02-10-00-00", "Create initial state for P-Chain transactions", createPChainTxState)
migrations.Container.Add("2024-01-24-00-00", "Update transaction input type", updateTxInputType)
migrations.Container.Add("2024-11-07-00-00", "Alter type column size in p_chain_txes table", alterPChainTxType)
}

func createPChainTxState(db *gorm.DB) error {
Expand All @@ -29,3 +30,7 @@ func updateTxInputType(db *gorm.DB) error {
}
return db.Model(&database.PChainTxInput{}).Where("type IS NULL").Update("type", database.DefaultInput).Error
}

func alterPChainTxType(db *gorm.DB) error {
return db.Exec("ALTER TABLE p_chain_txes CHANGE COLUMN type type VARCHAR(40)").Error
}
5 changes: 5 additions & 0 deletions indexer/shared/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package shared

const (
ApplicationVersion = "2.0.0"
)
9 changes: 9 additions & 0 deletions indexer/shared/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func InitMetricsServer(cfg *config.MetricsConfig) {

r.Path("/metrics").Handler(promhttp.Handler())
r.Path("/health").HandlerFunc(healthHandler)
r.Path("/version").HandlerFunc(versionHandler)

srv := &http.Server{
Addr: cfg.PrometheusAddress,
Expand All @@ -70,6 +71,14 @@ func healthHandler(w http.ResponseWriter, r *http.Request) {
}
}

func versionHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
_, err := w.Write([]byte(ApplicationVersion))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}

func writeHealthResponse(w http.ResponseWriter) (err error) {
ok, err := getHealthStatus()
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions services/api/pchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ApiPChainTxBase struct {
Weight uint64 `json:"weight"`
FeePercentage uint32 `json:"feePercentage"`
ChainTime *time.Time `json:"chainTime"`
BlockTime *time.Time `json:"blockTime"`
}

type ApiPChainTx struct {
Expand Down Expand Up @@ -55,6 +56,7 @@ func newApiPChainTxBase(tx *database.PChainTx) ApiPChainTxBase {
Weight: tx.Weight,
ChainTime: tx.ChainTime,
FeePercentage: tx.FeePercentage,
BlockTime: tx.BlockTime,
}
}

Expand Down
2 changes: 1 addition & 1 deletion services/main/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func main() {
}

muxRouter := mux.NewRouter()
router := utils.NewSwaggerRouter(muxRouter, "Flare P-Chain Indexer", "0.1.0")
router := utils.NewSwaggerRouter(muxRouter, "Flare P-Chain Indexer", "2.0.0")
routes.AddTransferRoutes(router, ctx)
routes.AddStakerRoutes(router, ctx)
routes.AddUTXORoutes(router, ctx)
Expand Down
15 changes: 10 additions & 5 deletions utils/chain/p_chain_rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

const (
RequestTimeout = 10 * time.Second
ClientRequestTimeout = 10 * time.Second
)

// Copy-paste from
Expand Down Expand Up @@ -49,13 +49,15 @@ func (c *AvalancheRPCClient) GetRewardUTXOs(id ids.ID) (*GetRewardUTXOsReply, er
TxID: id,
Encoding: formatting.Hex,
}
reply := &GetRewardUTXOsReply{}
ctx, cancelCtx := context.WithTimeout(context.Background(), RequestTimeout)
ctx, cancelCtx := context.WithTimeout(context.Background(), ClientRequestTimeout)
defer cancelCtx()

response, err := c.client.Call(ctx, "platform.getRewardUTXOs", params)
if err != nil {
return nil, err
}

reply := &GetRewardUTXOsReply{}
err = response.GetObject(reply)
if err != nil {
return nil, err
Expand All @@ -68,12 +70,15 @@ func (c *AvalancheRPCClient) GetTx(id ids.ID) (*api.GetTxReply, error) {
TxID: id,
Encoding: formatting.Hex,
}
reply := &api.GetTxReply{}
ctx := context.Background()
ctx, cancelCtx := context.WithTimeout(context.Background(), ClientRequestTimeout)
defer cancelCtx()

response, err := c.client.Call(ctx, "platform.getTx", params)
if err != nil {
return nil, err
}

reply := &api.GetTxReply{}
err = response.GetObject(reply)
if err != nil {
return nil, err
Expand Down
28 changes: 22 additions & 6 deletions utils/chain/p_chain_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,34 @@ var (
ErrInvalidCredentialType = errors.New("invalid credential type")
)

// If block.Parse fails, try to parse as a "pre-fork" block
func ParsePChainBlock(blockBytes []byte) (blocks.Block, error) {
blk, err := block.Parse(blockBytes)
var innerBlk blocks.Block
if err == nil {
innerBlk, err = blocks.Parse(blocks.GenesisCodec, blk.Block())
if err != nil {
return nil, errors.Wrap(err, "failed to parse inner block")
}
} else {
// try to parse as as a "pre-fork" block
innerBlk, err = blocks.Parse(blocks.Codec, blockBytes)
if err != nil {
return nil, errors.Wrap(err, "failed to parse block")
}
}
return innerBlk, nil
}

// For a given block (byte array) return a list of public keys for
// signatures of inputs of the transaction in this block
// Block must be of type "ApricotProposalBlock"
func PublicKeysFromPChainBlock(blockBytes []byte) ([][]crypto.PublicKey, error) {
blk, err := block.Parse(blockBytes)
innerBlk, err := ParsePChainBlock(blockBytes)
if err != nil {
return nil, errors.Wrap(err, "failed to parse block")
}
innerBlk, err := blocks.Parse(blocks.GenesisCodec, blk.Block())
if err != nil {
return nil, errors.Wrap(err, "failed to parse inner block")
return nil, err
}

if propBlk, ok := innerBlk.(*blocks.ApricotProposalBlock); ok {
return PublicKeysFromPChainTx(propBlk.Tx)
} else {
Expand Down

0 comments on commit 666b49e

Please sign in to comment.