From e27623109634cd1444dc8d54631220f2597c402c Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Tue, 3 Oct 2023 15:45:30 -0300 Subject: [PATCH] introduce new relation that tracks stake delegation certs --- indexer/entity/src/lib.rs | 1 + indexer/entity/src/stake_delegation.rs | 37 ++++++ indexer/execution_plans/default.toml | 2 + indexer/migration/src/lib.rs | 2 + ...27_231206_create_stake_delegation_table.rs | 74 ++++++++++++ indexer/tasks/src/multiera/mod.rs | 1 + .../multiera/multiera_address_delegation.rs | 113 ++++++++++++++++++ 7 files changed, 230 insertions(+) create mode 100644 indexer/entity/src/stake_delegation.rs create mode 100644 indexer/migration/src/m20230927_231206_create_stake_delegation_table.rs create mode 100644 indexer/tasks/src/multiera/multiera_address_delegation.rs diff --git a/indexer/entity/src/lib.rs b/indexer/entity/src/lib.rs index 3cb250a0..423266f4 100644 --- a/indexer/entity/src/lib.rs +++ b/indexer/entity/src/lib.rs @@ -15,4 +15,5 @@ pub mod dex_swap; pub mod native_asset; pub mod plutus_data; pub mod plutus_data_hash; +pub mod stake_delegation; pub mod transaction_metadata; diff --git a/indexer/entity/src/stake_delegation.rs b/indexer/entity/src/stake_delegation.rs new file mode 100644 index 00000000..3f069a5f --- /dev/null +++ b/indexer/entity/src/stake_delegation.rs @@ -0,0 +1,37 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "StakeDelegationCredentialRelation")] +pub struct Model { + #[sea_orm(primary_key, column_type = "BigInteger")] + pub id: i64, + pub stake_credential: i64, + // pool registrations are not tracked in StakeCredentials, + pub pool_credential: Option>, + pub tx_id: i64, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::stake_credential::Entity", + from = "Column::StakeCredential", + to = "super::stake_credential::Column::Id" + )] + StakeCredential, + #[sea_orm( + belongs_to = "super::transaction::Entity", + from = "Column::TxId", + to = "super::transaction::Column::Id" + )] + Transaction, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::StakeCredential.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/indexer/execution_plans/default.toml b/indexer/execution_plans/default.toml index 70e98cbd..c9cbdbf0 100644 --- a/indexer/execution_plans/default.toml +++ b/indexer/execution_plans/default.toml @@ -63,3 +63,5 @@ readonly=false readonly=false [MultieraCip25EntryTask] + +[MultieraAddressDelegationTask] diff --git a/indexer/migration/src/lib.rs b/indexer/migration/src/lib.rs index 884a1c7d..15d1f8f7 100644 --- a/indexer/migration/src/lib.rs +++ b/indexer/migration/src/lib.rs @@ -17,6 +17,7 @@ mod m20220528_000012_create_plutus_data_table; mod m20220808_000013_create_transaction_reference_input_table; mod m20221031_000014_create_dex_table; mod m20230223_000015_modify_block_table; +mod m20230927_231206_create_stake_delegation_table; pub struct Migrator; @@ -41,6 +42,7 @@ impl MigratorTrait for Migrator { Box::new(m20220808_000013_create_transaction_reference_input_table::Migration), Box::new(m20221031_000014_create_dex_table::Migration), Box::new(m20230223_000015_modify_block_table::Migration), + Box::new(m20230927_231206_create_stake_delegation_table::Migration), ] } } diff --git a/indexer/migration/src/m20230927_231206_create_stake_delegation_table.rs b/indexer/migration/src/m20230927_231206_create_stake_delegation_table.rs new file mode 100644 index 00000000..59301301 --- /dev/null +++ b/indexer/migration/src/m20230927_231206_create_stake_delegation_table.rs @@ -0,0 +1,74 @@ +use entity::prelude::{StakeCredential, StakeCredentialColumn, Transaction, TransactionColumn}; +use entity::stake_delegation::*; +use sea_schema::migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20230927_231206_create_stake_delegation_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Entity) + .if_not_exists() + .col( + ColumnDef::new(Column::Id) + .big_integer() + .not_null() + .auto_increment(), + ) + .col( + ColumnDef::new(Column::StakeCredential) + .big_integer() + .not_null(), + ) + .col(ColumnDef::new(Column::TxId).big_integer().not_null()) + .col(ColumnDef::new(Column::PoolCredential).binary()) + .foreign_key( + ForeignKey::create() + .name("fk-stake_delegation-credential_id") + .from(Entity, Column::StakeCredential) + .to(StakeCredential, StakeCredentialColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-stake_delegation-tx_id") + .from(Entity, Column::TxId) + .to(Transaction, TransactionColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .primary_key( + Index::create() + .table(Entity) + .name("stake_delegation_credential-pk") + .col(Column::Id), + ) + .to_owned(), + ) + .await + + // manager + // .create_index( + // Index::create() + // .table(Entity) + // .name("index-address_credential-credential") + // .col(Column::CredentialId) + // .to_owned(), + // ) + // .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Entity).to_owned()) + .await + } +} diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index 0ccf716b..f6fc1a41 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -1,6 +1,7 @@ pub mod dex; pub mod multiera_address; pub mod multiera_address_credential_relations; +pub mod multiera_address_delegation; pub mod multiera_asset_mint; pub mod multiera_block; pub mod multiera_cip25entry; diff --git a/indexer/tasks/src/multiera/multiera_address_delegation.rs b/indexer/tasks/src/multiera/multiera_address_delegation.rs new file mode 100644 index 00000000..5744813c --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_address_delegation.rs @@ -0,0 +1,113 @@ +use crate::{ + multiera::multiera_stake_credentials::MultieraStakeCredentialTask, + types::{AddressCredentialRelationValue, TxCredentialRelationValue}, +}; +use cardano_multiplatform_lib::{ + address::{BaseAddress, EnterpriseAddress, PointerAddress, RewardAddress}, + byron::ByronAddress, +}; +use entity::{ + prelude::*, + sea_orm::{prelude::*, DatabaseTransaction}, +}; +use pallas::ledger::{ + primitives::{alonzo::Certificate, Fragment}, + traverse::{MultiEraBlock, MultiEraCert, MultiEraOutput, MultiEraTx}, +}; +use sea_orm::{Order, QueryOrder, Set}; +use std::collections::{BTreeMap, BTreeSet}; +use std::ops::Deref; + +use super::{ + multiera_address_credential_relations::QueuedAddressCredentialRelation, + multiera_txs::MultieraTransactionTask, relation_map::RelationMap, +}; +use crate::config::EmptyConfig::EmptyConfig; +use crate::dsl::database_task::BlockGlobalInfo; +use crate::dsl::task_macro::*; + +carp_task! { + name MultieraAddressDelegationTask; + configuration EmptyConfig; + doc "Tracks stake delegation actions to pools."; + era multiera; + dependencies [MultieraStakeCredentialTask]; + read [multiera_txs, multiera_stake_credential]; + write []; + should_add_task |block, _properties| { + // recall: txs may have no outputs if they just burn all inputs as fee + // TODO: this runs slightly more than it should + !block.1.is_empty() + }; + execute |previous_data, task| handle( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_stake_credential, + ); + merge_result |_previous_data, _result| {}; +} + +async fn handle( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>, BlockGlobalInfo>, + multiera_txs: &[TransactionModel], + multiera_stake_credential: &BTreeMap, StakeCredentialModel>, +) -> Result<(), DbErr> { + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + for cert in tx_body.certs() { + { + let tx_id = cardano_transaction.id; + let cert = &cert; + match cert.as_alonzo().unwrap() { + Certificate::StakeDelegation(credential, pool) => { + let credential = credential.encode_fragment().unwrap(); + + let stake_credential_id = multiera_stake_credential + .get(&credential.to_vec()) + .unwrap() + .id; + + entity::stake_delegation::ActiveModel { + stake_credential: Set(stake_credential_id), + pool_credential: Set(Some(pool.to_vec())), + tx_id: Set(tx_id), + ..Default::default() + } + .save(db_tx) + .await?; + } + Certificate::StakeRegistration(credential) => {} + Certificate::StakeDeregistration(credential) => { + let credential = credential.encode_fragment().unwrap(); + + let stake_credential_id = multiera_stake_credential + .get(&credential.to_vec()) + .unwrap() + .id; + + entity::stake_delegation::ActiveModel { + stake_credential: Set(stake_credential_id), + pool_credential: Set(None), + tx_id: Set(tx_id), + ..Default::default() + } + .save(db_tx) + .await?; + } + Certificate::PoolRegistration { + operator, + pool_owners, + reward_account, + .. + } => {} + Certificate::PoolRetirement(key_hash, _) => {} + Certificate::GenesisKeyDelegation(_, _, _) => {} + Certificate::MoveInstantaneousRewardsCert(mir) => {} + }; + }; + } + } + + Ok(()) +}