diff --git a/chain/src/main.rs b/chain/src/main.rs index c27ccf36..63e5dddc 100644 --- a/chain/src/main.rs +++ b/chain/src/main.rs @@ -255,10 +255,7 @@ async fn crawling_fn( transaction_conn, redelegations, )?; - repository::pos::delete_old_redelegations( - transaction_conn, - epoch, - )?; + repository::pos::clear_redelegations(transaction_conn, epoch)?; repository::pos::remove_withdraws( transaction_conn, epoch, diff --git a/chain/src/repository/pos.rs b/chain/src/repository/pos.rs index 27cb6738..b5b8e7d7 100644 --- a/chain/src/repository/pos.rs +++ b/chain/src/repository/pos.rs @@ -165,7 +165,7 @@ pub fn insert_redelegations( anyhow::Ok(()) } -pub fn delete_old_redelegations( +pub fn clear_redelegations( transaction_conn: &mut PgConnection, current_epoch: Epoch, ) -> anyhow::Result<()> { @@ -254,11 +254,11 @@ pub fn update_validator_metadata( #[cfg(test)] mod tests { use orm::bond::BondDb; + use orm::redelegation::RedelegationDb; use orm::unbond::UnbondDb; use orm::validators::ValidatorInsertDb; use shared::balance::Amount; - use shared::bond::Bond; - use shared::unbond::Unbond; + use shared::pos::{Bond, Redelegation, Unbond}; use shared::validator::Validator; use test_helpers::db::TestDb; @@ -612,6 +612,200 @@ mod tests { .expect("Failed to run test"); } + /// Test that the insert_redelegations function panics if validator is not in db. + #[tokio::test] + #[should_panic] + async fn test_insert_redelegations_with_missing_validator() { + let db = TestDb::new(); + + db.run_test(|conn| { + let fake_validator = Validator::fake(); + let fake_redelegations: Vec = (0..10) + .map(|_| Redelegation::fake(fake_validator.clone().address)) + .collect(); + + insert_redelegations(conn, fake_redelegations)?; + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the insert_redelegations function correctly inserts redelegations into the empty db. + #[tokio::test] + async fn test_insert_redelegations_with_empty_db() { + let db = TestDb::new(); + + db.run_test(|conn| { + let fake_validator = Validator::fake(); + let fake_redelegations_len = 10; + let fake_redelegations: Vec = (0 + ..fake_redelegations_len) + .map(|_| Redelegation::fake(fake_validator.clone().address)) + .collect(); + + seed_validator(conn, fake_validator)?; + + insert_redelegations(conn, fake_redelegations)?; + + let queried_redelegations = query_redelegations(conn); + + assert_eq!(queried_redelegations.len(), fake_redelegations_len); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the insert_redelegations function updates the raw_amount on conflict + #[tokio::test] + async fn test_insert_redelegations_with_conflict() { + let db = TestDb::new(); + + db.run_test(|conn| { + let fake_validator = Validator::fake(); + let fake_redelegations_len = 10; + let fake_redelegations: Vec = (0 + ..fake_redelegations_len) + .map(|_| Redelegation::fake(fake_validator.clone().address)) + .collect(); + + seed_redelegations( + conn, + fake_validator.clone(), + fake_redelegations.clone(), + )?; + + let new_epoch = 123 as Epoch; + let mut updated_redelegations = fake_redelegations.clone(); + updated_redelegations + .iter_mut() + .for_each(|unbond| unbond.epoch = new_epoch); + + insert_redelegations(conn, updated_redelegations)?; + + let queried_redelegations = query_redelegations(conn); + let queried_redelegations_len = queried_redelegations.len(); + + assert_eq!(queried_redelegations_len, fake_redelegations_len); + assert_eq!( + queried_redelegations + .into_iter() + .map(|b| b.epoch as Epoch) + .collect::>(), + vec![new_epoch; queried_redelegations_len] + ); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the insert_redelegations function correctly handles empty redelegations input. + #[tokio::test] + async fn test_insert_redelegations_with_empty_redelegations() { + let db = TestDb::new(); + + db.run_test(|conn| { + let fake_redelegations_len = 10; + let fake_validator = Validator::fake(); + let fake_redelegations: Vec = (0 + ..fake_redelegations_len) + .map(|_| Redelegation::fake(fake_validator.clone().address)) + .collect(); + seed_redelegations(conn, fake_validator, fake_redelegations)?; + + insert_redelegations(conn, vec![])?; + + let queried_bonds = query_redelegations(conn); + + assert_eq!(queried_bonds.len(), fake_redelegations_len); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the function correctly handles epoch 0 input. + #[tokio::test] + async fn test_clear_redelegations_with_empty_addresses() { + let db = TestDb::new(); + + db.run_test(|conn| { + let validator = Validator::fake(); + let redelegations = (0..10) + .map(|_| Redelegation::fake(validator.clone().address)) + .collect(); + + seed_redelegations(conn, validator, redelegations)?; + clear_redelegations(conn, 0)?; + + let queried_redelegations = query_redelegations(conn); + + assert_eq!(queried_redelegations.len(), 10); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the clear_redelegations function does nothing when there are not redelegations + /// in the db. + #[tokio::test] + async fn test_clear_redelegations_with_no_redelegations() { + let db = TestDb::new(); + + db.run_test(|conn| { + clear_redelegations(conn, 99999)?; + + let queried_redelegations = query_redelegations(conn); + + assert_eq!(queried_redelegations.len(), 0); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + + /// Test that the clear_redelegations function removes the correct redelegations from the + /// db. + #[tokio::test] + async fn test_clear_redelegations() { + let db = TestDb::new(); + + db.run_test(|conn| { + let validator = Validator::fake(); + let redelegations: Vec = (0..10) + .map(|i| { + let red = Redelegation::fake(validator.clone().address); + Redelegation { + epoch: i as Epoch, + ..red + } + }) + .collect(); + + seed_redelegations(conn, validator.clone(), redelegations.clone())?; + + clear_redelegations(conn, 5)?; + + let queried_redelegations = query_redelegations(conn); + + // We removed all redelegations with epoch <= 5, so we have 6,7,8,9 left + assert_eq!(queried_redelegations.len(), 4); + + anyhow::Ok(()) + }) + .await + .expect("Failed to run test"); + } + fn seed_bonds( conn: &mut PgConnection, validator: Validator, @@ -660,6 +854,34 @@ mod tests { anyhow::Ok(()) } + fn seed_redelegations( + conn: &mut PgConnection, + validator: Validator, + redelegations: Redelegations, + ) -> anyhow::Result<()> { + let validator: ValidatorDb = diesel::insert_into(validators::table) + .values(ValidatorInsertDb::from_validator(validator)) + .get_result(conn) + .context("Failed to insert validator")?; + + diesel::insert_into(redelegation::table) + .values::<&Vec>( + &redelegations + .into_iter() + .map(|unbond| { + RedelegationInsertDb::from_redelegation( + unbond, + validator.id, + ) + }) + .collect::>(), + ) + .execute(conn) + .context("Failed to update balances in db")?; + + anyhow::Ok(()) + } + fn seed_validator( conn: &mut PgConnection, validator: Validator, @@ -685,4 +907,11 @@ mod tests { .load::(conn) .expect("Failed to query bonds") } + + fn query_redelegations(conn: &mut PgConnection) -> Vec { + redelegation::table + .select(RedelegationDb::as_select()) + .load::(conn) + .expect("Failed to query bonds") + } } diff --git a/shared/src/bond.rs b/shared/src/bond.rs deleted file mode 100644 index 6d773aa4..00000000 --- a/shared/src/bond.rs +++ /dev/null @@ -1,35 +0,0 @@ -use fake::Fake; - -use crate::balance::Amount; -use crate::block::Epoch; -use crate::id::Id; - -#[derive(Hash, Debug, Clone, PartialEq, Eq)] -pub struct BondAddresses { - pub source: Id, - pub target: Id, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Bond { - pub source: Id, - pub target: Id, - pub amount: Amount, - pub start: Epoch, -} - -impl Bond { - pub fn fake(validator_address: Id) -> Self { - let source_address = - namada_core::address::gen_established_address("namada-indexer"); - - Self { - source: Id::Account(source_address.to_string()), - target: validator_address, - amount: Amount::fake(), - start: (1..1000).fake::(), - } - } -} - -pub type Bonds = Vec; diff --git a/shared/src/pos.rs b/shared/src/pos.rs index 09198a38..3f934150 100644 --- a/shared/src/pos.rs +++ b/shared/src/pos.rs @@ -72,15 +72,13 @@ pub struct Redelegation { } impl Redelegation { - pub fn fake() -> Self { + pub fn fake(validator_address: Id) -> Self { let delegator = namada_core::address::gen_established_address("delegator"); - let validator = - namada_core::address::gen_established_address("validator"); Self { delegator: Id::from(delegator), - validator: Id::from(validator), + validator: validator_address, epoch: (1..1000).fake::(), } } diff --git a/shared/src/unbond.rs b/shared/src/unbond.rs deleted file mode 100644 index 677035c2..00000000 --- a/shared/src/unbond.rs +++ /dev/null @@ -1,35 +0,0 @@ -use fake::Fake; - -use crate::balance::Amount; -use crate::block::Epoch; -use crate::id::Id; - -#[derive(Hash, Debug, Clone, PartialEq, Eq)] -pub struct UnbondAddresses { - pub source: Id, - pub validator: Id, -} - -#[derive(Debug, Clone)] -pub struct Unbond { - pub source: Id, - pub target: Id, - pub amount: Amount, - pub withdraw_at: Epoch, -} - -impl Unbond { - pub fn fake(validator_address: Id) -> Self { - let source_address = - namada_core::address::gen_established_address("namada-indexer"); - - Self { - source: Id::Account(source_address.to_string()), - target: validator_address, - amount: Amount::fake(), - withdraw_at: (3..10).fake::(), - } - } -} - -pub type Unbonds = Vec;