Skip to content

Commit

Permalink
add task that tracks stake delegation (#157)
Browse files Browse the repository at this point in the history
* introduce new relation that tracks stake delegation certs

* add 'delegation/address' route

* regen docs/bin/openapi.json

* remove copy-pasted comment in migration

* cleanup unused branches/variants of certificates

* add previous pool column

this helps getting un-delegation events by pool, since otherwise
re-delegation doesn't directly address the pool that no longer has the
delegation.

* add endpoint get pool's delegation history by pool

* add slot and pool fields to the delegation for pool query

* delegationForAddress: extract staking credential if arg is full address

* rename migration file and add stake credential column index

* add limit for slot range and pools

* eslint fixes
  • Loading branch information
ecioppettini authored Dec 1, 2023
1 parent d8fc630 commit b3deea0
Show file tree
Hide file tree
Showing 22 changed files with 826 additions and 1 deletion.
232 changes: 232 additions & 0 deletions docs/bin/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,120 @@
}
]
},
"DelegationForAddressResponse": {
"properties": {
"txId": {
"type": "string",
"nullable": true
},
"pool": {
"type": "string",
"nullable": true
}
},
"required": [
"txId",
"pool"
],
"type": "object"
},
"DelegationForAddressRequest": {
"properties": {
"until": {
"properties": {
"absoluteSlot": {
"type": "number",
"format": "double"
}
},
"required": [
"absoluteSlot"
],
"type": "object"
},
"address": {
"$ref": "#/components/schemas/Address"
}
},
"required": [
"until",
"address"
],
"type": "object"
},
"PoolHex": {
"type": "string",
"example": "8200581c8baf48931c5187cd59fde553f4e7da2e1a2aa9202ec6e67815cb3f8a",
"pattern": "[0-9a-fA-F]{56}"
},
"DelegationForPoolResponse": {
"items": {
"properties": {
"slot": {
"type": "number",
"format": "double"
},
"txId": {
"type": "string",
"nullable": true
},
"pool": {
"allOf": [
{
"$ref": "#/components/schemas/PoolHex"
}
],
"nullable": true
},
"credential": {
"$ref": "#/components/schemas/Address"
}
},
"required": [
"slot",
"txId",
"pool",
"credential"
],
"type": "object"
},
"type": "array"
},
"Pool": {
"$ref": "#/components/schemas/PoolHex"
},
"DelegationForPoolRequest": {
"properties": {
"range": {
"properties": {
"maxSlot": {
"type": "number",
"format": "double"
},
"minSlot": {
"type": "number",
"format": "double"
}
},
"required": [
"maxSlot",
"minSlot"
],
"type": "object"
},
"pools": {
"items": {
"$ref": "#/components/schemas/Pool"
},
"type": "array"
}
},
"required": [
"range",
"pools"
],
"type": "object"
},
"PolicyId": {
"type": "string",
"example": "b863bc7369f46136ac1048adb2fa7dae3af944c3bbb2be2f216a8d4f",
Expand Down Expand Up @@ -1008,6 +1122,124 @@
}
}
},
"/delegation/address": {
"post": {
"operationId": "DelegationForAddress",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DelegationForAddressResponse"
}
}
}
},
"400": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"409": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"422": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
}
},
"security": [],
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DelegationForAddressRequest"
}
}
}
}
}
},
"/delegation/pool": {
"post": {
"operationId": "DelegationForPool",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DelegationForPoolResponse"
}
}
}
},
"400": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"409": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
},
"422": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorShape"
}
}
}
}
},
"security": [],
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DelegationForPoolRequest"
}
}
}
}
}
},
"/dex/last-price": {
"post": {
"operationId": "DexLastPrice",
Expand Down
1 change: 1 addition & 0 deletions indexer/entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
38 changes: 38 additions & 0 deletions indexer/entity/src/stake_delegation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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<Vec<u8>>,
pub tx_id: i64,
pub previous_pool: Option<Vec<u8>>,
}

#[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<super::stake_credential::Entity> for Entity {
fn to() -> RelationDef {
Relation::StakeCredential.def()
}
}

impl ActiveModelBehavior for ActiveModel {}
2 changes: 2 additions & 0 deletions indexer/execution_plans/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ readonly=false
readonly=false

[MultieraCip25EntryTask]

[MultieraAddressDelegationTask]
2 changes: 2 additions & 0 deletions indexer/migration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_000016_create_stake_delegation_table;

pub struct Migrator;

Expand All @@ -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_000016_create_stake_delegation_table::Migration),
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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_000016_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())
.col(ColumnDef::new(Column::PreviousPool).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-stake_delegation_credential-stake_credential")
.col(Column::StakeCredential)
.to_owned(),
)
.await
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Entity).to_owned())
.await
}
}
1 change: 1 addition & 0 deletions indexer/tasks/src/multiera/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading

0 comments on commit b3deea0

Please sign in to comment.