Skip to content

Commit

Permalink
Merge pull request #167 from confio/verify_validators-2
Browse files Browse the repository at this point in the history
Verify validators revisited
  • Loading branch information
maurolacy committed Aug 25, 2022
2 parents 9b122d2 + e409500 commit fa6305d
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 96 deletions.
20 changes: 10 additions & 10 deletions contracts/tg4-engagement/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,27 @@ pub enum QueryMsg {
Hooks {},
/// Return the current number of preauths. Returns PreauthResponse.
Preauths {},
/// Return how much rewards are assigned for withdrawal to given address. Returns
/// Return how many rewards are assigned for withdrawal from the given address. Returns
/// `RewardsResponse`.
WithdrawableRewards { owner: String },
/// Return how much rewards were distributed in total by this contract. Returns
/// Return how many rewards were distributed in total by this contract. Returns
/// `RewardsResponse`.
DistributedRewards {},
/// Return how much funds were send to this contract since last `ExecuteMsg::DistribtueFunds`,
/// and wait for distribution. Returns `RewardsResponse`.
/// Return how many funds were sent to this contract since last `ExecuteMsg::DistributeFunds`,
/// and await for distribution. Returns `RewardsResponse`.
UndistributedRewards {},
/// Returns address allowed for withdrawal funds assigned to owner. Returns `DelegateResponse`
/// Return address allowed for withdrawal of the funds assigned to owner. Returns `DelegateResponse`
Delegated { owner: String },
/// Returns information about the halflife, including the duration in seconds, the last
/// and the next occurence.
/// Returns information about the half-life, including the duration in seconds, the last
/// and the next occurrence.
Halflife {},
/// Returns information (bool) whether given address is an active slasher
/// Returns information (bool) about whether the given address is an active slasher
IsSlasher { addr: String },
/// Returns all active slashers as vector of addresses
/// Returns all active slashers as a vector of addresses
ListSlashers {},
/// Returns rewards distribution data
DistributionData {},
/// Returns withdraw adjustment
/// Returns withdraw adjustment data
WithdrawAdjustmentData { addr: String },
}

Expand Down
128 changes: 63 additions & 65 deletions contracts/tgrade-valset/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::cmp::{max, min};
use std::collections::BTreeSet;
use std::convert::TryInto;
use std::convert::{TryFrom, TryInto};

#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
Expand Down Expand Up @@ -33,7 +33,7 @@ use crate::msg::{
use crate::rewards::pay_block_rewards;
use crate::state::{
export, import, operators, Config, EpochInfo, OperatorInfo, ValidatorInfo, ValidatorSlashing,
ValsetState, CONFIG, EPOCH, JAIL, PENDING_VALIDATORS, VALIDATORS, VALIDATOR_SLASHING,
ValsetState, BLOCK_SIGNERS, CONFIG, EPOCH, JAIL, VALIDATORS, VALIDATOR_SLASHING,
VALIDATOR_START_HEIGHT,
};

Expand All @@ -43,6 +43,9 @@ pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const REWARDS_INIT_REPLY_ID: u64 = 1;

/// Missed blocks interval a validator can be jailed for.
pub const MISSED_BLOCKS: u64 = 1000;

/// We use this custom message everywhere
pub type Response = cosmwasm_std::Response<TgradeMsg>;
pub type SubMsg = cosmwasm_std::SubMsg<TgradeMsg>;
Expand Down Expand Up @@ -652,6 +655,18 @@ fn is_genesis_block(block: &BlockInfo) -> bool {
fn end_block(deps: DepsMut<TgradeQuery>, env: Env) -> Result<Response, ContractError> {
let cfg = CONFIG.load(deps.storage)?;

if cfg.verify_validators {
// Update the block signers height at each block
deps.querier
.query::<ValidatorVoteResponse>(&QueryRequest::Custom(TgradeQuery::ValidatorVotes {}))?
.votes
.iter()
.filter(|&v| v.voted)
.try_for_each(|v| {
BLOCK_SIGNERS.save(deps.storage, v.address.as_slice(), &env.block.height)
})?;
}

// check if needed and quit early if we didn't hit epoch boundary
let mut epoch = EPOCH.load(deps.storage)?;
let cur_epoch = env.block.time.nanos() / (1_000_000_000 * epoch.epoch_length);
Expand All @@ -671,32 +686,38 @@ fn end_block(deps: DepsMut<TgradeQuery>, env: Env) -> Result<Response, ContractE
EPOCH.save(deps.storage, &epoch)?;

if cfg.verify_validators {
if let Some(pending) = PENDING_VALIDATORS.may_load(deps.storage)? {
let votes = deps
.querier
.query::<ValidatorVoteResponse>(&QueryRequest::Custom(
TgradeQuery::ValidatorVotes {},
))?
.votes;

let expiration = JailingPeriod::from_duration(
JailingDuration::Duration(cfg.offline_jail_duration),
&env.block,
);

for val in pending {
let vote = votes
.iter()
.find(|vote| vote.address.as_slice() == val.1.to_address());
if vote.is_none() || !vote.unwrap().voted {
JAIL.save(deps.storage, &val.0, &expiration)?;
let expiration = JailingPeriod::from_duration(
JailingDuration::Duration(cfg.offline_jail_duration),
&env.block,
);

VALIDATORS
.load(deps.storage)?
.iter()
.flat_map(|v| match Ed25519Pubkey::try_from(&v.validator_pubkey) {
Ok(pubkey) => Some((v, pubkey)),
_ => None, // Silently ignore wrong / different type pubkeys
})
.try_for_each(|(v, ed25519_pubkey)| {
let operator_addr = &v.operator;
let validator_addr = ed25519_pubkey.to_address();
let mut height = BLOCK_SIGNERS.may_load(deps.storage, &validator_addr)?;
if height.is_none() {
// Not a block signer yet, check their validator start height instead
height = VALIDATOR_START_HEIGHT.may_load(deps.storage, operator_addr)?;
}
}
}
match height {
Some(h) if h > env.block.height.saturating_sub(MISSED_BLOCKS) => Ok(()),
_ => {
// validator is inactive for at least MISSED_BLOCKS, jail!
JAIL.save(deps.storage, operator_addr, &expiration)
}
}
})?;
}

// calculate and store new validator set
let (mut validators, auto_unjail) = calculate_validators(deps.as_ref(), &env)?;
let (validators, auto_unjail) = calculate_validators(deps.as_ref(), &env)?;

// auto unjailing
for addr in &auto_unjail {
Expand All @@ -712,32 +733,6 @@ fn end_block(deps: DepsMut<TgradeQuery>, env: Env) -> Result<Response, ContractE
remove: remove.clone(),
};

if cfg.verify_validators {
// pending validators are validators who have just been added and have yet to verify they're online
// and signing blocks (or at least signing the first one)
let pending: Vec<_> = add
.iter()
.filter_map(|m| {
let addr = Addr::unchecked(&m.addr);
if let Ok(op) = operators().load(deps.storage, &addr) {
if !op.active_validator {
Some((addr, op.pubkey))
} else {
None
}
} else {
None
}
})
.collect();
PENDING_VALIDATORS.save(deps.storage, &pending)?;
for v in &mut validators {
if pending.iter().any(|(addr, _)| *addr == v.operator) {
v.power = cfg.min_points;
}
}
}

VALIDATORS.save(deps.storage, &validators)?;

// update operators list with info about whether or not they're active validators
Expand Down Expand Up @@ -948,6 +943,9 @@ pub fn migrate(
if let Some(max_validators) = msg.max_validators {
cfg.max_validators = max_validators;
}
if let Some(verify_validators) = msg.verify_validators {
cfg.verify_validators = verify_validators;
}
Ok(cfg)
})?;

Expand Down Expand Up @@ -1157,12 +1155,12 @@ mod test {
vec![
ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey1".into()),
power: 1
power: 1,
},
ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey2".into()),
power: 2
}
power: 2,
},
],
diff.diffs
);
Expand All @@ -1175,12 +1173,12 @@ mod test {
vec![
ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey1".into()),
power: 0
power: 0,
},
ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey2".into()),
power: 0
}
power: 0,
},
],
diff.diffs
);
Expand All @@ -1200,7 +1198,7 @@ mod test {
assert_eq!(
vec![ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey3".into()),
power: 3
power: 3,
},],
diff.diffs
);
Expand All @@ -1215,7 +1213,7 @@ mod test {
assert_eq!(
vec![ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey1".into()),
power: 1
power: 1,
},],
diff.diffs
);
Expand All @@ -1229,7 +1227,7 @@ mod test {
assert_eq!(
vec![ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey2".into()),
power: 0
power: 0,
},],
diff.diffs
);
Expand All @@ -1243,7 +1241,7 @@ mod test {
assert_eq!(
vec![ValidatorUpdate {
pubkey: Pubkey::Ed25519(b"pubkey1".into()),
power: 0
power: 0,
},],
diff.diffs
);
Expand Down Expand Up @@ -1271,7 +1269,7 @@ mod test {
.iter()
.map(|vi| ValidatorUpdate {
pubkey: vi.validator_pubkey.clone(),
power: vi.power
power: vi.power,
})
.collect()
},
Expand All @@ -1295,7 +1293,7 @@ mod test {
.iter()
.map(|vi| ValidatorUpdate {
pubkey: vi.validator_pubkey.clone(),
power: 0
power: 0,
})
.collect()
},
Expand All @@ -1318,7 +1316,7 @@ mod test {
ValidatorDiff {
diffs: vec![ValidatorUpdate {
pubkey: cur.last().as_ref().unwrap().validator_pubkey.clone(),
power: (VALIDATORS + 1) as u64
power: (VALIDATORS + 1) as u64,
}]
},
diff
Expand All @@ -1344,7 +1342,7 @@ mod test {
.take(VALIDATORS - 1)
.map(|vi| ValidatorUpdate {
pubkey: vi.validator_pubkey.clone(),
power: vi.power
power: vi.power,
})
.collect()
},
Expand Down Expand Up @@ -1388,7 +1386,7 @@ mod test {
.take(VALIDATORS - 1)
.map(|vi| ValidatorUpdate {
pubkey: vi.validator_pubkey.clone(),
power: 0
power: 0,
})
.collect()
},
Expand Down
1 change: 1 addition & 0 deletions contracts/tgrade-valset/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ pub struct InstantiateResponse {
pub struct MigrateMsg {
pub min_points: Option<u64>,
pub max_validators: Option<u32>,
pub verify_validators: Option<bool>,
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions contracts/tgrade-valset/src/multitest/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fn migration_can_alter_cfg() {
&MigrateMsg {
min_points: Some(5),
max_validators: Some(10),
verify_validators: Some(true),
},
)
.unwrap();
Expand Down
7 changes: 7 additions & 0 deletions contracts/tgrade-valset/src/multitest/suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,13 @@ impl Suite {
Ok(diff)
}

pub fn advance_blocks(&mut self, blocks: u64) -> AnyResult<Option<ValidatorDiff>> {
self.app.advance_blocks(blocks);
let (_, diff) = self.app.end_block()?;
self.app.begin_block(vec![])?;
Ok(diff)
}

/// Timestamp of current block
pub fn timestamp(&self) -> Timestamp {
self.app.block_info().time
Expand Down
Loading

0 comments on commit fa6305d

Please sign in to comment.