Skip to content

Commit

Permalink
zcash_client_backend: Add serialization & parsing for protobuf Propos…
Browse files Browse the repository at this point in the history
…al representation.
  • Loading branch information
nuttycom committed Nov 9, 2023
1 parent f310637 commit ffec359
Show file tree
Hide file tree
Showing 14 changed files with 513 additions and 50 deletions.
27 changes: 22 additions & 5 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this library adheres to Rust's notion of
- `TransparentInputSource`
- `SaplingInputSource`
- `wallet::propose_standard_transfer_to_address`
- `wallet::input_selection::Proposal::from_parts`
- `wallet::input_selection::SaplingInputs`
- `wallet::input_selection::ShieldingSelector` has been
factored out from the `InputSelector` trait to separate out transparent
Expand All @@ -20,6 +21,22 @@ and this library adheres to Rust's notion of
- `zcash_client_backend::wallet`:
- `ReceivedSaplingNote::from_parts`
- `ReceivedSaplingNote::{txid, output_index, diversifier, rseed, note_commitment_tree_position}`
- `zcash_client_backend::zip321::TransactionRequest::total`
- `zcash_client_backend::proto::`
- `PROPOSAL_SER_V1`
- `ProposalError`
- `proposal::Proposal::{from_standard_proposal, try_into_standard_proposal}`
- `proposal::ProposedInput::parse_txid`
- `impl Clone for zcash_client_backend::{
zip321::{Payment, TransactionRequest, Zip321Error, parse::Param, parse::IndexedParam},
wallet::{ReceivedSaplingNote, WalletTransparentOutput},
wallet::input_selection::{Proposal, SaplingInputs},
}`
- `impl {PartialEq, Eq} for zcash_client_backend::{
zip321::{Zip321Error, parse::Param, parse::IndexedParam},
wallet::{ReceivedSaplingNote, WalletTransparentOutput},
wallet::input_selection::{Proposal, SaplingInputs},
}`

### Changed
- `zcash_client_backend::data_api`:
Expand All @@ -33,11 +50,11 @@ and this library adheres to Rust's notion of
backend-specific note identifier. The related `NoteRef` type parameter has
been removed from `error::Error`.
- A new variant `UnsupportedPoolType` has been added.
- `wallet::shield_transparent_funds` no longer
takes a `memo` argument; instead, memos to be associated with the shielded
outputs should be specified in the construction of the value of the
`input_selector` argument, which is used to construct the proposed shielded
values as internal "change" outputs.
- `wallet::shield_transparent_funds` no longer takes a `memo` argument;
instead, memos to be associated with the shielded outputs should be
specified in the construction of the value of the `input_selector`
argument, which is used to construct the proposed shielded values as
internal "change" outputs.
- `wallet::create_proposed_transaction` no longer takes a
`change_memo` argument; instead, change memos are represented in the
individual values of the `proposed_change` field of the `Proposal`'s
Expand Down
30 changes: 30 additions & 0 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ pub trait SaplingInputSource {
/// or a UUID.
type NoteRef: Copy + Debug + Eq + Ord;

/// Returns a received Sapling note, or Ok(None) if the note is not known to belong to the
/// wallet or if the note is not spendable.
fn get_spendable_sapling_note(
&self,
txid: &TxId,
index: u32,
) -> Result<Option<ReceivedSaplingNote<Self::NoteRef>>, Self::Error>;

/// Returns a list of spendable Sapling notes sufficient to cover the specified target value,
/// if possible.
fn select_spendable_sapling_notes(
Expand All @@ -240,6 +248,13 @@ pub trait TransparentInputSource {
/// The type of errors produced by a wallet backend.
type Error;

/// Returns a received transparent UTXO, or Ok(None) if the UTXO is not known to belong to the
/// wallet or is not spendable.
fn get_unspent_transparent_output(
&self,
outpoint: &OutPoint,
) -> Result<Option<WalletTransparentOutput>, Self::Error>;

/// Returns a list of unspent transparent UTXOs that appear in the chain at heights up to and
/// including `max_height`.
fn get_unspent_transparent_outputs(
Expand Down Expand Up @@ -979,6 +994,14 @@ pub mod testing {
type Error = ();
type NoteRef = u32;

fn get_spendable_sapling_note(
&self,
_txid: &TxId,
_index: u32,
) -> Result<Option<ReceivedSaplingNote<Self::NoteRef>>, Self::Error> {
Ok(None)
}

fn select_spendable_sapling_notes(
&self,
_account: AccountId,
Expand All @@ -994,6 +1017,13 @@ pub mod testing {
impl TransparentInputSource for MockWalletDb {
type Error = ();

fn get_unspent_transparent_output(
&self,
_outpoint: &OutPoint,
) -> Result<Option<WalletTransparentOutput>, Self::Error> {
Ok(None)
}

fn get_unspent_transparent_outputs(
&self,
_address: &TransparentAddress,
Expand Down
52 changes: 52 additions & 0 deletions zcash_client_backend/src/data_api/wallet/input_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl<DE: fmt::Display, SE: fmt::Display> fmt::Display for InputSelectorError<DE,
}

/// The inputs to be consumed and outputs to be produced in a proposed transaction.
#[derive(Clone, PartialEq, Eq)]
pub struct Proposal<FeeRuleT, NoteRef> {
transaction_request: TransactionRequest,
transparent_inputs: Vec<WalletTransparentOutput>,
Expand All @@ -90,6 +91,45 @@ pub struct Proposal<FeeRuleT, NoteRef> {
}

impl<FeeRuleT, NoteRef> Proposal<FeeRuleT, NoteRef> {
/// Constructs a [`Proposal`] from its constituent parts.
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_parts(
transaction_request: TransactionRequest,
transparent_inputs: Vec<WalletTransparentOutput>,
sapling_inputs: Option<SaplingInputs<NoteRef>>,
balance: TransactionBalance,
fee_rule: FeeRuleT,
min_target_height: BlockHeight,
is_shielding: bool,
) -> Result<Self, ()> {
let transparent_total = transparent_inputs
.iter()
.map(|out| out.txout().value)
.fold(Ok(NonNegativeAmount::ZERO), |acc, a| (acc? + a).ok_or(()))?;
let sapling_total = sapling_inputs
.iter()
.flat_map(|s_in| s_in.notes().iter())
.map(|out| out.value())
.fold(Ok(NonNegativeAmount::ZERO), |acc, a| (acc? + a).ok_or(()))?;
let input_total = (transparent_total + sapling_total).ok_or(())?;

let output_total = (transaction_request.total()? + balance.total()).ok_or(())?;

if input_total == output_total {
Ok(Self {
transaction_request,
transparent_inputs,
sapling_inputs,
balance,
fee_rule,
min_target_height,
is_shielding,
})
} else {
Err(())
}
}

/// Returns the transaction request that describes the payments to be made.
pub fn transaction_request(&self) -> &TransactionRequest {
&self.transaction_request
Expand Down Expand Up @@ -147,12 +187,24 @@ impl<FeeRuleT, NoteRef> Debug for Proposal<FeeRuleT, NoteRef> {
}

/// The Sapling inputs to a proposed transaction.
#[derive(Clone, PartialEq, Eq)]
pub struct SaplingInputs<NoteRef> {
anchor_height: BlockHeight,
notes: NonEmpty<ReceivedSaplingNote<NoteRef>>,
}

impl<NoteRef> SaplingInputs<NoteRef> {
/// Constructs a [`SaplingInputs`] from its constituent parts.
pub fn from_parts(
anchor_height: BlockHeight,
notes: NonEmpty<ReceivedSaplingNote<NoteRef>>,
) -> Self {
Self {
anchor_height,
notes,
}
}

/// Returns the anchor height for Sapling inputs that should be used when constructing the
/// proposed transaction.
pub fn anchor_height(&self) -> BlockHeight {
Expand Down
3 changes: 3 additions & 0 deletions zcash_client_backend/src/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ impl ChangeValue {
pub struct TransactionBalance {
proposed_change: Vec<ChangeValue>,
fee_required: NonNegativeAmount,

// A cache for the sum of proposed change and fee; we compute it on construction anyway, so we
// cache the resulting value.
total: NonNegativeAmount,
}

Expand Down
Loading

0 comments on commit ffec359

Please sign in to comment.