From a6979f1e7249b277ec080dcb2070eed094eb50ca Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 27 Jan 2022 17:15:02 +0100 Subject: [PATCH] Merge pull request #51 from confio/33-valset-limit-active-valset-query-2 Validator set query pagination --- contracts/tgrade-valset/src/contract.rs | 31 ++++++++++-- contracts/tgrade-valset/src/msg.rs | 6 ++- .../tgrade-valset/src/multitest/contract.rs | 50 ++++++++++++++++++- .../tgrade-valset/src/multitest/jailing.rs | 2 +- .../tgrade-valset/src/multitest/stake.rs | 4 +- .../tgrade-valset/src/multitest/suite.rs | 17 +++++-- 6 files changed, 94 insertions(+), 16 deletions(-) diff --git a/contracts/tgrade-valset/src/contract.rs b/contracts/tgrade-valset/src/contract.rs index 4e02efac..9b107f38 100644 --- a/contracts/tgrade-valset/src/contract.rs +++ b/contracts/tgrade-valset/src/contract.rs @@ -1,4 +1,4 @@ -use std::cmp::max; +use std::cmp::{max, min}; use std::collections::BTreeSet; use std::convert::TryInto; @@ -380,7 +380,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(to_binary(&list_active_validators(deps, env)?)?), + ListActiveValidators { start_after, limit } => Ok(to_binary(&list_active_validators( + deps, + start_after, + limit, + )?)?), SimulateActiveValidators {} => Ok(to_binary(&simulate_active_validators(deps, env)?)?), ListValidatorSlashing { operator } => { Ok(to_binary(&list_validator_slashing(deps, env, operator)?)?) @@ -468,10 +472,28 @@ fn list_validator_keys( fn list_active_validators( deps: Deps, - _env: Env, + start_after: Option, + limit: Option, ) -> Result { + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start_after = maybe_addr(deps.api, start_after)?; + let validators = VALIDATORS.load(deps.storage)?; - Ok(ListActiveValidatorsResponse { validators }) + // Simulate a range query + let mut i = 0; + if let Some(start_after) = start_after { + for v in &validators { + if v.operator == start_after { + i += 1; + break; + } + i += 1; + } + } + let validators = &validators[i..min(i + limit, validators.len())]; + Ok(ListActiveValidatorsResponse { + validators: Vec::from(validators), + }) } fn simulate_active_validators( @@ -574,6 +596,7 @@ fn end_block(deps: DepsMut, env: Env) -> Result { } let old_validators = VALIDATORS.load(deps.storage)?; + VALIDATORS.save(deps.storage, &validators)?; // determine the diff to send back to tendermint let (diff, add, remove) = calculate_diff(validators, old_validators); diff --git a/contracts/tgrade-valset/src/msg.rs b/contracts/tgrade-valset/src/msg.rs index 6f64d359..2393ac74 100644 --- a/contracts/tgrade-valset/src/msg.rs +++ b/contracts/tgrade-valset/src/msg.rs @@ -281,8 +281,10 @@ pub enum QueryMsg { }, /// List the current validator set, sorted by power descending - /// (no pagination - reasonable limit from max_validators) - ListActiveValidators {}, + ListActiveValidators { + start_after: Option, + limit: Option, + }, /// This will calculate who the new validators would be if /// we recalculated end block right now. diff --git a/contracts/tgrade-valset/src/multitest/contract.rs b/contracts/tgrade-valset/src/multitest/contract.rs index 4fc13e4a..19958da4 100644 --- a/contracts/tgrade-valset/src/multitest/contract.rs +++ b/contracts/tgrade-valset/src/multitest/contract.rs @@ -59,7 +59,7 @@ fn initialization() { // Validators should be set on genesis processing block assert_active_validators( - &suite.list_active_validators().unwrap(), + &suite.list_active_validators(None, None).unwrap(), &[(members[2], 5), (members[3], 8)], ); @@ -71,6 +71,52 @@ fn initialization() { } } +#[test] +fn validators_query_pagination() { + let members = vec!["member1", "member2", "member3", "member4", "member5"]; + + let suite = SuiteBuilder::new() + .with_engagement(&members_init(&members, &[2, 3, 5, 8, 4])) + .with_operators(&members) + .with_epoch_reward(coin(100, "eth")) + .with_max_validators(10) + .with_min_weight(2) + .with_epoch_length(3600) + .build(); + + // Query without pagination + assert_active_validators( + &suite.list_active_validators(None, None).unwrap(), + &[ + (members[0], 2), + (members[1], 3), + (members[2], 5), + (members[3], 8), + (members[4], 4), + ], + ); + + // List only first 3 + let page = suite.list_active_validators(None, 3).unwrap(); + assert_active_validators(&page, &[(members[2], 5), (members[3], 8), (members[4], 4)]); + + // List 2 entries after 2rd validator + assert_active_validators( + &suite + .list_active_validators(page.last().unwrap().operator.to_string(), 2) + .unwrap(), + &[(members[0], 2), (members[1], 3)], + ); + + // Starting at unknown validator will return empty query result + assert_active_validators( + &suite + .list_active_validators("unknown_member".to_owned(), None) + .unwrap(), + &[], + ); +} + #[test] fn simulate_validators() { let members = vec![ @@ -90,7 +136,7 @@ fn simulate_validators() { ); assert_active_validators( - &suite.list_active_validators().unwrap(), + &suite.list_active_validators(None, None).unwrap(), &[(members[4], 13), (members[5], 21)], ); } diff --git a/contracts/tgrade-valset/src/multitest/jailing.rs b/contracts/tgrade-valset/src/multitest/jailing.rs index 299c09fb..1d541086 100644 --- a/contracts/tgrade-valset/src/multitest/jailing.rs +++ b/contracts/tgrade-valset/src/multitest/jailing.rs @@ -308,7 +308,7 @@ fn enb_block_ignores_jailed_validators() { suite.advance_epoch().unwrap(); assert_active_validators( - &suite.list_active_validators().unwrap(), + &suite.list_active_validators(None, None).unwrap(), &[(members[2], 5), (members[3], 8)], ); } diff --git a/contracts/tgrade-valset/src/multitest/stake.rs b/contracts/tgrade-valset/src/multitest/stake.rs index 4207e993..23b56cb4 100644 --- a/contracts/tgrade-valset/src/multitest/stake.rs +++ b/contracts/tgrade-valset/src/multitest/stake.rs @@ -39,7 +39,7 @@ fn init_and_query_state() { ); // no initial active set - let active = suite.list_active_validators().unwrap(); + let active = suite.list_active_validators(None, None).unwrap(); assert_eq!(active, vec![]); // check a validator is set @@ -82,7 +82,7 @@ fn simulate_validators() { // what do we expect? // 1..24 have pubkeys registered, we take the top 10, but none have stake yet, so zero - let active = suite.list_active_validators().unwrap(); + let active = suite.list_active_validators(None, None).unwrap(); assert_eq!(0, active.len()); // One member bonds needed tokens to have enough weight diff --git a/contracts/tgrade-valset/src/multitest/suite.rs b/contracts/tgrade-valset/src/multitest/suite.rs index c683945e..c51e5e6a 100644 --- a/contracts/tgrade-valset/src/multitest/suite.rs +++ b/contracts/tgrade-valset/src/multitest/suite.rs @@ -671,11 +671,18 @@ impl Suite { Ok(resp.validators) } - pub fn list_active_validators(&self) -> StdResult> { - let resp: ListActiveValidatorsResponse = self - .app - .wrap() - .query_wasm_smart(self.valset.clone(), &QueryMsg::ListActiveValidators {})?; + pub fn list_active_validators( + &self, + start_after: impl Into>, + limit: impl Into>, + ) -> StdResult> { + let resp: ListActiveValidatorsResponse = self.app.wrap().query_wasm_smart( + self.valset.clone(), + &QueryMsg::ListActiveValidators { + start_after: start_after.into(), + limit: limit.into(), + }, + )?; Ok(resp.validators) }