Skip to content

Commit

Permalink
Merge remote-tracking branch 'namada/tomas/rpc-sub-vp-pos' (#570) int…
Browse files Browse the repository at this point in the history
…o main

* namada/tomas/rpc-sub-vp-pos:
  changelog: add #570
  [ci] wasm checksums update
  Update apps/src/lib/client/tx.rs
  shared/queries: refactor prefix iter using storage_api
  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
juped committed Nov 29, 2022
2 parents bc540f9 + 7df04fc commit 75f2e1b
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 363 deletions.
1 change: 1 addition & 0 deletions .changelog/unreleased/improvements/570-rpc-sub-vp-pos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added PoS specific queries ([#570](https://github.com/anoma/namada/pull/570))
106 changes: 39 additions & 67 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,10 +1835,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 @@ -2380,8 +2377,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 @@ -2455,12 +2454,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 @@ -2558,26 +2558,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 @@ -2586,23 +2585,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 @@ -2664,69 +2663,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
69 changes: 34 additions & 35 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2014,12 +2014,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 @@ -2036,22 +2033,20 @@ 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);

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 @@ -2117,33 +2112,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 @@ -162,7 +163,7 @@ where
}
}
}
TallyResult::Rejected | TallyResult::Unknown => {
Ok(false) => {
let proposal_event: Event = ProposalEvent::new(
EventType::Proposal.to_string(),
TallyResult::Rejected,
Expand All @@ -174,6 +175,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 75f2e1b

Please sign in to comment.