Skip to content

Commit

Permalink
Merge branch 'tomas/rpc-sub-vp-pos' (#570)
Browse files Browse the repository at this point in the history
* tomas/rpc-sub-vp-pos:
  wasm: update checksums
  queries/shell: refactor to single def
  queries/router: rm dbg prints
  queries: add more PoS queries and use them for gov
  RPC: add PoS is_validator and bond_amount queries
  shared: implement PosReadOnly for Storage
  • Loading branch information
tzemanovic committed Nov 2, 2022
2 parents 6cbce4a + bea1e36 commit 62bb69c
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 350 deletions.
106 changes: 39 additions & 67 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,10 +1072,7 @@ pub async fn is_validator(
ledger_address: TendermintAddress,
) -> bool {
let client = HttpClient::new(ledger_address).unwrap();
let key = pos::validator_state_key(address);
let state: Option<pos::ValidatorStates> =
query_storage_value(&client, &key).await;
state.is_some()
unwrap_client_response(RPC.vp().pos().is_validator(&client, address).await)
}

/// Check if a given address is a known delegator
Expand Down Expand Up @@ -1519,8 +1516,10 @@ pub async fn get_proposal_votes(
.expect("Vote key should contains the voting address.")
.clone();
if vote.is_yay() && validators.contains(&voter_address) {
let amount =
get_validator_stake(client, epoch, &voter_address).await;
let amount: VotePower =
get_validator_stake(client, epoch, &voter_address)
.await
.into();
yay_validators.insert(voter_address, amount);
} else if !validators.contains(&voter_address) {
let validator_address =
Expand Down Expand Up @@ -1594,12 +1593,13 @@ pub async fn get_proposal_offline_votes(
if proposal_vote.vote.is_yay()
&& validators.contains(&proposal_vote.address)
{
let amount = get_validator_stake(
let amount: VotePower = get_validator_stake(
client,
proposal.tally_epoch,
&proposal_vote.address,
)
.await;
.await
.into();
yay_validators.insert(proposal_vote.address, amount);
} else if is_delegator_at(
client,
Expand Down Expand Up @@ -1697,26 +1697,25 @@ pub async fn compute_tally(
epoch: Epoch,
votes: Votes,
) -> ProposalResult {
let validators = get_all_validators(client, epoch).await;
let total_stacked_tokens =
get_total_staked_tokes(client, epoch, &validators).await;
let total_staked_tokens: VotePower =
get_total_staked_tokens(client, epoch).await.into();

let Votes {
yay_validators,
yay_delegators,
nay_delegators,
} = votes;

let mut total_yay_stacked_tokens = VotePower::from(0_u64);
let mut total_yay_staked_tokens = VotePower::from(0_u64);
for (_, amount) in yay_validators.clone().into_iter() {
total_yay_stacked_tokens += amount;
total_yay_staked_tokens += amount;
}

// YAY: Add delegator amount whose validator didn't vote / voted nay
for (_, vote_map) in yay_delegators.iter() {
for (validator_address, vote_power) in vote_map.iter() {
if !yay_validators.contains_key(validator_address) {
total_yay_stacked_tokens += vote_power;
total_yay_staked_tokens += vote_power;
}
}
}
Expand All @@ -1725,23 +1724,23 @@ pub async fn compute_tally(
for (_, vote_map) in nay_delegators.iter() {
for (validator_address, vote_power) in vote_map.iter() {
if yay_validators.contains_key(validator_address) {
total_yay_stacked_tokens -= vote_power;
total_yay_staked_tokens -= vote_power;
}
}
}

if total_yay_stacked_tokens >= (total_stacked_tokens / 3) * 2 {
if total_yay_staked_tokens >= (total_staked_tokens / 3) * 2 {
ProposalResult {
result: TallyResult::Passed,
total_voting_power: total_stacked_tokens,
total_yay_power: total_yay_stacked_tokens,
total_voting_power: total_staked_tokens,
total_yay_power: total_yay_staked_tokens,
total_nay_power: 0,
}
} else {
ProposalResult {
result: TallyResult::Rejected,
total_voting_power: total_stacked_tokens,
total_yay_power: total_yay_stacked_tokens,
total_voting_power: total_staked_tokens,
total_yay_power: total_yay_staked_tokens,
total_nay_power: 0,
}
}
Expand Down Expand Up @@ -1803,69 +1802,42 @@ pub async fn get_bond_amount_at(
pub async fn get_all_validators(
client: &HttpClient,
epoch: Epoch,
) -> Vec<Address> {
let validator_set_key = pos::validator_set_key();
let validator_sets =
query_storage_value::<pos::ValidatorSets>(client, &validator_set_key)
.await
.expect("Validator set should always be set");
let validator_set = validator_sets
.get(epoch)
.expect("Validator set should be always set in the current epoch");
let all_validators = validator_set.active.union(&validator_set.inactive);
all_validators
.map(|validator| validator.address.clone())
.collect()
) -> HashSet<Address> {
unwrap_client_response(
RPC.vp()
.pos()
.validator_addresses(client, &Some(epoch))
.await,
)
}

pub async fn get_total_staked_tokes(
pub async fn get_total_staked_tokens(
client: &HttpClient,
epoch: Epoch,
validators: &[Address],
) -> VotePower {
let mut total = VotePower::from(0_u64);

for validator in validators {
total += get_validator_stake(client, epoch, validator).await;
}
total
) -> token::Amount {
unwrap_client_response(
RPC.vp().pos().total_stake(client, &Some(epoch)).await,
)
}

async fn get_validator_stake(
client: &HttpClient,
epoch: Epoch,
validator: &Address,
) -> VotePower {
let total_voting_power_key = pos::validator_total_deltas_key(validator);
let total_voting_power = query_storage_value::<pos::ValidatorTotalDeltas>(
client,
&total_voting_power_key,
) -> token::Amount {
unwrap_client_response(
RPC.vp()
.pos()
.validator_stake(client, validator, &Some(epoch))
.await,
)
.await
.expect("Total deltas should be defined");
let epoched_total_voting_power = total_voting_power.get(epoch);

VotePower::try_from(epoched_total_voting_power.unwrap_or_default())
.unwrap_or_default()
}

pub async fn get_delegators_delegation(
client: &HttpClient,
address: &Address,
_epoch: Epoch,
) -> Vec<Address> {
let key = pos::bonds_for_source_prefix(address);
let bonds_iter = query_storage_prefix::<pos::Bonds>(client, &key).await;

let mut delegation_addresses: Vec<Address> = Vec::new();
if let Some(bonds) = bonds_iter {
for (key, _epoched_amount) in bonds {
let validator_address = pos::get_validator_address_from_bond(&key)
.expect("Delegation key should contain validator address.");
delegation_addresses.push(validator_address);
}
}
delegation_addresses
) -> HashSet<Address> {
unwrap_client_response(RPC.vp().pos().delegations(client, address).await)
}

pub async fn get_governance_parameters(client: &HttpClient) -> GovParams {
Expand Down
70 changes: 36 additions & 34 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::HashSet;
use std::convert::TryFrom;
use std::env;
use std::fs::File;
Expand Down Expand Up @@ -682,12 +683,9 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) {
safe_exit(1)
}
}
let mut delegation_addresses = rpc::get_delegators_delegation(
&client,
&voter_address,
epoch,
)
.await;
let mut delegations =
rpc::get_delegators_delegation(&client, &voter_address)
.await;

// Optimize by quering if a vote from a validator
// is equal to ours. If so, we can avoid voting, but ONLY if we
Expand All @@ -704,22 +702,22 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) {
)
.await
{
delegation_addresses = filter_delegations(
delegations = filter_delegations(
&client,
delegation_addresses,
delegations,
proposal_id,
&args.vote,
)
.await;
}

println!("{:?}", delegation_addresses);
println!("{:?}", delegations);

let tx_data = VoteProposalData {
id: proposal_id,
vote: args.vote,
voter: voter_address,
delegations: delegation_addresses,
delegations: delegations.into_iter().collect(),
};

let data = tx_data
Expand Down Expand Up @@ -779,33 +777,37 @@ async fn is_safe_voting_window(
/// vote)
async fn filter_delegations(
client: &HttpClient,
mut delegation_addresses: Vec<Address>,
delegations: HashSet<Address>,
proposal_id: u64,
delegator_vote: &ProposalVote,
) -> Vec<Address> {
let mut remove_indexes: Vec<usize> = vec![];

for (index, validator_address) in delegation_addresses.iter().enumerate() {
let vote_key = gov_storage::get_vote_proposal_key(
proposal_id,
validator_address.to_owned(),
validator_address.to_owned(),
);

if let Some(validator_vote) =
rpc::query_storage_value::<ProposalVote>(client, &vote_key).await
{
if &validator_vote == delegator_vote {
remove_indexes.push(index);
}
}
}

for index in remove_indexes {
delegation_addresses.swap_remove(index);
}
) -> HashSet<Address> {
// Filter delegations by their validator's vote concurrently
let delegations = futures::future::join_all(
delegations
.into_iter()
// we cannot use `filter/filter_map` directly because we want to
// return a future
.map(|validator_address| async {
let vote_key = gov_storage::get_vote_proposal_key(
proposal_id,
validator_address.to_owned(),
validator_address.to_owned(),
);

delegation_addresses
if let Some(validator_vote) =
rpc::query_storage_value::<ProposalVote>(client, &vote_key)
.await
{
if &validator_vote == delegator_vote {
return None;
}
}
Some(validator_address)
}),
)
.await;
// Take out the `None`s
delegations.into_iter().flatten().collect()
}

pub async fn submit_bond(ctx: Context, args: args::Bond) {
Expand Down
28 changes: 23 additions & 5 deletions apps/src/lib/node/ledger/shell/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ where
})?;

let votes = get_proposal_votes(&shell.storage, proposal_end_epoch, id);
let tally_result =
compute_tally(&shell.storage, proposal_end_epoch, votes);
let is_accepted = votes.and_then(|votes| {
compute_tally(&shell.storage, proposal_end_epoch, votes)
});

let transfer_address = match tally_result {
TallyResult::Passed => {
let transfer_address = match is_accepted {
Ok(true) => {
let proposal_author_key = gov_storage::get_author_key(id);
let proposal_author = shell
.read_storage_key::<Address>(&proposal_author_key)
Expand Down Expand Up @@ -161,7 +162,7 @@ where
}
}
}
TallyResult::Rejected | TallyResult::Unknown => {
Ok(false) => {
let proposal_event: Event = ProposalEvent::new(
EventType::Proposal.to_string(),
TallyResult::Rejected,
Expand All @@ -173,6 +174,23 @@ where
response.events.push(proposal_event);
proposals_result.rejected.push(id);

slash_fund_address
}
Err(err) => {
tracing::error!(
"Unexpectedly failed to tally proposal ID {id} with error \
{err}"
);
let proposal_event: Event = ProposalEvent::new(
EventType::Proposal.to_string(),
TallyResult::Failed,
id,
false,
false,
)
.into();
response.events.push(proposal_event);

slash_fund_address
}
};
Expand Down
Loading

0 comments on commit 62bb69c

Please sign in to comment.