diff --git a/anchor/database/src/state.rs b/anchor/database/src/state.rs index da7c79f..d5cff8e 100644 --- a/anchor/database/src/state.rs +++ b/anchor/database/src/state.rs @@ -1,11 +1,13 @@ use crate::{DatabaseError, NetworkDatabase, NetworkState, Pool, PoolConn, SqlStatement, SQL}; use ssv_types::{ - Cluster, ClusterId, ClusterMember, Operator, OperatorId, Share, ValidatorMetadata, + Cluster, ClusterId, ClusterMember, Operator, OperatorId, Share, ValidatorIndex, + ValidatorMetadata, }; use std::collections::{HashMap, HashSet}; +use types::Address; impl NetworkState { - // Main constructor that builds the network state from the database data + /// Build the network state from the database data pub(crate) fn new_with_state(conn_pool: &Pool, id: OperatorId) -> Result { // Get database connection from the pool let conn = conn_pool.get()?; @@ -125,8 +127,36 @@ impl NetworkDatabase { self.state.clusters.contains(id) } + /// Get own share of key for a Cluster we are a member in + pub fn get_share(&self, id: &ClusterId) -> Option<&Share> { + self.state.shares.get(id) + } + /// Set the id of our own operator pub fn set_own_id(&mut self, id: OperatorId) { self.state.id = Some(id); } + + /// Get the metatdata for the cluster + pub fn get_validator_metadata(&self, id: &ClusterId) -> Option<&ValidatorMetadata> { + self.state.validator_metadata.get(id) + } + + /// Get the Fee Recipient address + pub fn get_fee_recipient(&self, id: &ClusterId) -> Option
{ + if let Some(metadata) = self.state.validator_metadata.get(id) { + Some(metadata.fee_recipient) + } else { + None + } + } + + /// Get the Validator Index + pub fn get_validator_index(&self, id: &ClusterId) -> Option { + if let Some(metadata) = self.state.validator_metadata.get(id) { + Some(metadata.validator_index) + } else { + None + } + } } diff --git a/anchor/database/src/tests/mod.rs b/anchor/database/src/tests/mod.rs index eeb5979..22a2c88 100644 --- a/anchor/database/src/tests/mod.rs +++ b/anchor/database/src/tests/mod.rs @@ -2,6 +2,7 @@ mod cluster_tests; mod operator_tests; mod state_tests; mod utils; +mod validator_tests; pub mod test_prelude { pub use super::utils::*; diff --git a/anchor/database/src/tests/operator_tests.rs b/anchor/database/src/tests/operator_tests.rs index 04767b1..101c69b 100644 --- a/anchor/database/src/tests/operator_tests.rs +++ b/anchor/database/src/tests/operator_tests.rs @@ -89,4 +89,14 @@ mod operator_database_tests { assertions::assert_operator_not_exists_fully(&fixture.db, operator.id); } } + + #[test] + /// Try to delete an operator that does not exist + fn test_delete_dne_operator() { + let mut fixture = TestFixture::new_empty(); + fixture + .db + .delete_operator(OperatorId(1)) + .expect_err("Deletion should fail. Operator DNE"); + } } diff --git a/anchor/database/src/tests/utils.rs b/anchor/database/src/tests/utils.rs index 8134365..5dcb6f6 100644 --- a/anchor/database/src/tests/utils.rs +++ b/anchor/database/src/tests/utils.rs @@ -236,11 +236,11 @@ pub mod queries { pub fn get_validator( db: &NetworkDatabase, validator_pubkey: &str, - ) -> Option<(String, i64, String)> { + ) -> Option<(String, i64, String, String, i64)> { let conn = db.connection().unwrap(); - let validator = conn.prepare("SELECT validator_pubkey, cluster_id, owner FROM validators WHERE validator_pubkey = ?1") + let validator = conn.prepare("SELECT validator_pubkey, cluster_id, owner, fee_recipient, validator_index FROM validators WHERE validator_pubkey = ?1") .unwrap() - .query_row(params![validator_pubkey], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?))) + .query_row(params![validator_pubkey], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?))) .optional() .unwrap(); validator diff --git a/anchor/database/src/tests/validator_tests.rs b/anchor/database/src/tests/validator_tests.rs new file mode 100644 index 0000000..d4d2d89 --- /dev/null +++ b/anchor/database/src/tests/validator_tests.rs @@ -0,0 +1,69 @@ +use super::test_prelude::*; + +#[cfg(test)] +mod validator_database_tests { + use super::*; + use types::Address; + + #[test] + /// Test updating the fee recipient address + fn test_update_fee_recipient() { + let mut fixture = TestFixture::new(Some(1)); + + let validator_pubkey = fixture.cluster.validator_metadata.validator_pubkey; + let updated_fee_recipient = Address::random(); + let cluster_id = fixture.cluster.cluster_id; + fixture + .db + .update_fee_recipient(cluster_id, validator_pubkey.clone(), updated_fee_recipient) + .expect("Failed to update fee recipient"); + + // make sure the state store has changed, then check the db + assert_eq!( + updated_fee_recipient, + fixture + .db + .get_fee_recipient(&cluster_id) + .expect("Failed to get fee recipient") + ); + assert_eq!( + updated_fee_recipient.to_string(), + queries::get_validator(&fixture.db, &(validator_pubkey.to_string())) + .expect("Failed to fetch Validator") + .3 + ); + } + + #[test] + /// Test setting the validator index + fn test_set_validator_index() { + let mut fixture = TestFixture::new(Some(1)); + + let validator_pubkey = fixture.cluster.validator_metadata.validator_pubkey; + let updated_validator_index = ValidatorIndex(10); + let cluster_id = fixture.cluster.cluster_id; + fixture + .db + .set_validator_index( + cluster_id, + validator_pubkey.clone(), + updated_validator_index, + ) + .expect("Failed to update validator index"); + + // make sure the state store has changed, then check the db + assert_eq!( + updated_validator_index, + fixture + .db + .get_validator_index(&cluster_id) + .expect("Failed to get validator index") + ); + assert_eq!( + *updated_validator_index as i64, + queries::get_validator(&fixture.db, &(validator_pubkey.to_string())) + .expect("Failed to fetch Validator") + .4 + ); + } +} diff --git a/anchor/database/src/validator_operations.rs b/anchor/database/src/validator_operations.rs index 4015411..33206eb 100644 --- a/anchor/database/src/validator_operations.rs +++ b/anchor/database/src/validator_operations.rs @@ -1,39 +1,64 @@ use super::{DatabaseError, NetworkDatabase, SqlStatement, SQL}; use rusqlite::params; -use ssv_types::{ClusterId, ValidatorIndex, ValidatorMetadata}; +use ssv_types::{ClusterId, ValidatorIndex}; use types::{Address, PublicKey}; /// Implements all validator related db functionality impl NetworkDatabase { - /// Populates or updates the fee recipient for the validator + /// Update the fee recipient address for a validator pub fn update_fee_recipient( &mut self, + cluster_id: ClusterId, validator_pubkey: PublicKey, fee_recipient: Address, ) -> Result<(), DatabaseError> { + // Make sure we are part of the cluster for this Validator + if !self.state.clusters.contains(&cluster_id) { + return Err(DatabaseError::NotFound(format!( + "Validator for Cluster {} not in database", + *cluster_id + ))); + } + let conn = self.connection()?; conn.prepare_cached(SQL[&SqlStatement::UpdateFeeRecipient])? .execute(params![ - validator_pubkey.to_string(), - fee_recipient.to_string() + fee_recipient.to_string(), + validator_pubkey.to_string() ])?; + let metadata = self + .state + .validator_metadata + .get_mut(&cluster_id) + .expect("Cluster should exist"); + metadata.fee_recipient = fee_recipient; Ok(()) } /// Set the index of the validator pub fn set_validator_index( &mut self, + cluster_id: ClusterId, validator_pubkey: PublicKey, index: ValidatorIndex, ) -> Result<(), DatabaseError> { + // Make sure we are part of the cluster for this validaor + if !self.state.clusters.contains(&cluster_id) { + return Err(DatabaseError::NotFound(format!( + "Validator for Cluster {} not in database", + *cluster_id + ))); + } + let conn = self.connection()?; conn.prepare_cached(SQL[&SqlStatement::SetValidatorIndex])? - .execute(params![validator_pubkey.to_string(), *index])?; + .execute(params![*index, validator_pubkey.to_string()])?; + let metadata = self + .state + .validator_metadata + .get_mut(&cluster_id) + .expect("Cluster should exist"); + metadata.validator_index = index; Ok(()) } - - /// Get the metatdata for the cluster - pub fn get_validator_metadata(&self, id: &ClusterId) -> Option<&ValidatorMetadata> { - self.state.validator_metadata.get(id) - } }