From 542294d66fff38d47c65a0894661eaa5ce48af26 Mon Sep 17 00:00:00 2001 From: Erwan Or Date: Wed, 1 May 2024 20:41:24 -0400 Subject: [PATCH] planner: begin restoring `VoteIntent` code --- crates/view/src/planner.rs | 145 ++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 66 deletions(-) diff --git a/crates/view/src/planner.rs b/crates/view/src/planner.rs index dcf57f50ad..97f45e9490 100644 --- a/crates/view/src/planner.rs +++ b/crates/view/src/planner.rs @@ -69,9 +69,9 @@ pub struct Planner { #[derive(Debug, Clone)] struct VoteIntent { start_block_height: u64, - // start_position: tct::Position, - // rate_data: BTreeMap, - // vote: Vote, + start_position: tct::Position, + rate_data: BTreeMap, + vote: Vote, } impl Debug for Planner { @@ -110,10 +110,79 @@ impl Planner { balance } - fn _push(&mut self, action: ActionPlan) { + fn push(&mut self, action: ActionPlan) { self.actions.push(action); } + pub fn add_votes( + &mut self, + voting_notes: Vec>, + ) -> Result<()> { + for ( + records, + ( + proposal, + VoteIntent { + start_position, + vote, + rate_data, + .. + }, + ), + ) in voting_notes + .into_iter() + .chain(std::iter::repeat(vec![])) // Chain with infinite repeating no notes, so the zip doesn't stop early + .zip(mem::take(&mut self.vote_intents).into_iter()) + { + // Keep track of whether we successfully could vote on this proposal + let mut voted = false; + + for (record, identity_key) in records { + // Vote with precisely this note on the proposal, computing the correct exchange + // rate for self-minted vote receipt tokens using the exchange rate of the validator + // at voting start time. If the validator was not active at the start of the + // proposal, the vote will be rejected by stateful verification, so skip the note + // and continue to the next one. + let Some(rate_data) = rate_data.get(&identity_key) else { + continue; + }; + let unbonded_amount = rate_data.unbonded_amount(record.note.amount()).into(); + + // If the delegation token is unspent, "roll it over" by spending it (this will + // result in change sent back to us). This unlinks nullifiers used for voting on + // multiple non-overlapping proposals, increasing privacy. + if record.height_spent.is_none() { + self.push( + SpendPlan::new(&mut OsRng, record.note.clone(), record.position).into(), + ); + } + + self.delegator_vote_precise( + proposal, + start_position, + vote, + record.note, + record.position, + unbonded_amount, + ); + + voted = true; + } + + if !voted { + // If there are no notes to vote with, return an error, because otherwise the user + // would compose a transaction that would not satisfy their intention, and would + // silently eat the fee. + anyhow::bail!( + "can't vote on proposal {} because no delegation notes were staked to an active validator when voting started", + proposal + ); + } + } + + Ok(()) + } + fn gas_estimate(&self) -> Gas { // TODO: this won't include the gas cost for the bytes of the tx itself // so this gas estimate will be an underestimate, but since the tx-bytes contribution @@ -528,22 +597,22 @@ impl Planner { /// Vote with all possible vote weight on a given proposal. /// /// Voting twice on the same proposal in the same planner will overwrite the previous vote. - #[instrument(skip(self, _start_position, _start_rate_data))] + #[instrument(skip(self, start_position, start_rate_data))] pub fn delegator_vote( &mut self, proposal: u64, start_block_height: u64, - _start_position: tct::Position, - _start_rate_data: BTreeMap, + start_position: tct::Position, + start_rate_data: BTreeMap, vote: Vote, ) -> &mut Self { self.vote_intents.insert( proposal, VoteIntent { - // start_position, + start_position, start_block_height, - // vote, - // rate_data: start_rate_data, + vote, + rate_data: start_rate_data, }, ); self @@ -578,62 +647,6 @@ impl Planner { self } - // /// Initiates a Dutch auction using protocol-controlled liquidity. - // #[instrument(skip(self))] - // pub fn dutch_auction_schedule( - // &mut self, - // input: Value, - // output_id: asset::Id, - // max_output: Amount, - // min_output: Amount, - // start_height: u64, - // end_height: u64, - // step_count: u64, - // nonce: [u8; 32], - // ) -> &mut Self { - // self.action(ActionPlan::ActionDutchAuctionSchedule( - // ActionDutchAuctionSchedule { - // description: DutchAuctionDescription { - // input, - // output_id, - // max_output, - // min_output, - // start_height, - // end_height, - // step_count, - // nonce, - // }, - // }, - // )) - // } - - // /// Ends a Dutch auction using protocol-controlled liquidity. - // #[instrument(skip(self))] - // pub fn dutch_auction_end(&mut self, auction_id: AuctionId) -> &mut Self { - // self.action(ActionPlan::ActionDutchAuctionEnd(ActionDutchAuctionEnd { - // auction_id, - // })) - // } - - // /// Withdraws the reserves of the Dutch auction. - // #[instrument(skip(self))] - // pub fn dutch_auction_withdraw( - // &mut self, - // auction_id: AuctionId, - // seq: u64, - // reserves_input: Value, - // reserves_output: Value, - // ) -> &mut Self { - // self.action(ActionPlan::ActionDutchAuctionWithdraw( - // ActionDutchAuctionWithdrawPlan { - // auction_id, - // seq, - // reserves_input, - // reserves_output, - // }, - // )) - // } - fn action(&mut self, action: ActionPlan) -> &mut Self { // Track the contribution of the action to the transaction's balance self.balance += action.balance();