diff --git a/zingolib/src/lightclient/send.rs b/zingolib/src/lightclient/send.rs index 1d7c049e0..065b415a2 100644 --- a/zingolib/src/lightclient/send.rs +++ b/zingolib/src/lightclient/send.rs @@ -1,7 +1,10 @@ //! TODO: Add Mod Description Here! use log::debug; -use zcash_client_backend::address::Address; +use zcash_client_backend::{ + address::Address, + zip321::{Payment, TransactionRequest}, +}; use zcash_primitives::{ consensus::BlockHeight, memo::MemoBytes, @@ -13,7 +16,40 @@ use super::{LightClient, LightWalletSendProgress}; use crate::{utils::zatoshis_from_u64, wallet::Pool}; #[cfg(feature = "zip317")] -use zcash_primitives::transaction::TxId; +use { + crate::{error::ZingoLibError, wallet::tx_map_and_maybe_trees::TxMapAndMaybeTrees}, + std::{num::NonZeroU32, ops::DerefMut}, + zcash_client_backend::{ + data_api::wallet::input_selection::GreedyInputSelector, ShieldedProtocol, + }, + zcash_primitives::transaction::TxId, + zingoconfig::ChainType, +}; + +#[cfg(feature = "zip317")] +type GISKit = GreedyInputSelector< + TxMapAndMaybeTrees, + zcash_client_backend::fees::zip317::SingleOutputChangeStrategy, +>; + +/// converts from raw receivers to TransactionRequest +pub fn receivers_becomes_transaction_request( + receivers: Vec<(Address, NonNegativeAmount, Option)>, +) -> Result { + let mut payments = vec![]; + for out in receivers.clone() { + payments.push(Payment { + recipient_address: out.0, + amount: out.1, + memo: out.2, + label: None, + message: None, + other_params: vec![], + }); + } + + TransactionRequest::new(payments) +} impl LightClient { async fn get_submission_height(&self) -> Result { @@ -30,11 +66,44 @@ impl LightClient { #[cfg(feature = "zip317")] pub async fn do_propose_spend( &self, - _receivers: Vec<(Address, NonNegativeAmount, Option)>, + receivers: Vec<(Address, NonNegativeAmount, Option)>, ) -> Result { - use crate::test_framework::mocks::ProposalBuilder; + let request = + receivers_becomes_transaction_request(receivers).map_err(|e| e.to_string())?; + + let change_strategy = zcash_client_backend::fees::zip317::SingleOutputChangeStrategy::new( + zcash_primitives::transaction::fees::zip317::FeeRule::standard(), + None, + ShieldedProtocol::Orchard, + ); // review consider change strategy! + + let input_selector = GISKit::new( + change_strategy, + zcash_client_backend::fees::DustOutputPolicy::default(), + ); + + let mut tmamt = self + .wallet + .transaction_context + .transaction_metadata_set + .write() + .await; + + let proposal = zcash_client_backend::data_api::wallet::propose_transfer::< + TxMapAndMaybeTrees, + ChainType, + GISKit, + ZingoLibError, + >( + tmamt.deref_mut(), + &self.wallet.transaction_context.config.chain, + zcash_primitives::zip32::AccountId::ZERO, + &input_selector, + request, + NonZeroU32::MIN, //review! use custom constant? + ) + .map_err(|e| ZingoLibError::Error(format!("error this function todo error {:?}", e)))?; - let proposal = ProposalBuilder::default().build(); let mut latest_proposal_lock = self.latest_proposal.write().await; *latest_proposal_lock = Some(crate::data::proposal::ZingoProposal::Transfer( proposal.clone(), diff --git a/zingolib/src/wallet/transaction_records_by_id.rs b/zingolib/src/wallet/transaction_records_by_id.rs index 3836fe2af..b8728a286 100644 --- a/zingolib/src/wallet/transaction_records_by_id.rs +++ b/zingolib/src/wallet/transaction_records_by_id.rs @@ -17,7 +17,7 @@ use zcash_primitives::consensus::BlockHeight; use zcash_primitives::transaction::TxId; -mod trait_inputsource; +pub mod trait_inputsource; use super::notes::query::OutputSpendStatusQuery; diff --git a/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs b/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs index 4fbaa8e99..d78cfaabf 100644 --- a/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs +++ b/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs @@ -132,7 +132,7 @@ impl InputSource for TransactionRecordsById { sources: &[zcash_client_backend::ShieldedProtocol], anchor_height: zcash_primitives::consensus::BlockHeight, exclude: &[Self::NoteRef], - ) -> Result, InputSourceError> { + ) -> Result, Self::Error> { let mut sapling_note_noteref_pairs: Vec<(sapling_crypto::Note, NoteId)> = Vec::new(); let mut orchard_note_noteref_pairs: Vec<(orchard::Note, NoteId)> = Vec::new(); for transaction_record in self.values().filter(|transaction_record| { diff --git a/zingolib/src/wallet/tx_map_and_maybe_trees.rs b/zingolib/src/wallet/tx_map_and_maybe_trees.rs index c043f1fcf..98dc9e54e 100644 --- a/zingolib/src/wallet/tx_map_and_maybe_trees.rs +++ b/zingolib/src/wallet/tx_map_and_maybe_trees.rs @@ -47,9 +47,12 @@ impl TxMapAndMaybeTrees { pub mod error { use std::fmt::{Debug, Display, Formatter, Result}; + use crate::wallet::transaction_records_by_id::trait_inputsource::error::InputSourceError; + #[derive(Debug, PartialEq)] pub enum TxMapAndMaybeTreesError { NoSpendCapability, + InputSource(InputSourceError), } impl From<&TxMapAndMaybeTreesError> for String { @@ -59,6 +62,7 @@ pub mod error { NoSpendCapability => { "No witness trees. This is viewkey watch, not a spendkey wallet.".to_string() } + InputSource(e) => e.to_string(), }; format!("{:#?} - {}", value, explanation) } @@ -70,4 +74,5 @@ pub mod error { } } +pub mod trait_stub_inputsource; pub mod trait_walletread; diff --git a/zingolib/src/wallet/tx_map_and_maybe_trees/trait_stub_inputsource.rs b/zingolib/src/wallet/tx_map_and_maybe_trees/trait_stub_inputsource.rs new file mode 100644 index 000000000..810d0c8b9 --- /dev/null +++ b/zingolib/src/wallet/tx_map_and_maybe_trees/trait_stub_inputsource.rs @@ -0,0 +1,69 @@ +//! this mod brings input source functionality from transaction_records_by_id + +use zcash_client_backend::{ + data_api::{InputSource, SpendableNotes}, + wallet::NoteId, +}; + +use super::{error::TxMapAndMaybeTreesError, TxMapAndMaybeTrees}; + +/// A trait representing the capability to query a data store for unspent transaction outputs belonging to a wallet. +/// combining this with WalletRead unlocks propose_transaction +/// all implementations in this file redirect to transaction_records_by_id +impl InputSource for TxMapAndMaybeTrees { + type Error = TxMapAndMaybeTreesError; + type AccountId = zcash_primitives::zip32::AccountId; + type NoteRef = NoteId; + + fn get_spendable_note( + &self, + txid: &zcash_primitives::transaction::TxId, + protocol: zcash_client_backend::ShieldedProtocol, + index: u32, + ) -> Result< + Option< + zcash_client_backend::wallet::ReceivedNote< + Self::NoteRef, + zcash_client_backend::wallet::Note, + >, + >, + Self::Error, + > { + self.transaction_records_by_id + .get_spendable_note(txid, protocol, index) + .map_err(TxMapAndMaybeTreesError::InputSource) + } + + fn select_spendable_notes( + &self, + account: Self::AccountId, + target_value: zcash_primitives::transaction::components::amount::NonNegativeAmount, + sources: &[zcash_client_backend::ShieldedProtocol], + anchor_height: zcash_primitives::consensus::BlockHeight, + exclude: &[Self::NoteRef], + ) -> Result, Self::Error> { + self.transaction_records_by_id + .select_spendable_notes(account, target_value, sources, anchor_height, exclude) + .map_err(TxMapAndMaybeTreesError::InputSource) + } + + fn get_unspent_transparent_output( + &self, + outpoint: &zcash_primitives::transaction::components::OutPoint, + ) -> Result, Self::Error> { + self.transaction_records_by_id + .get_unspent_transparent_output(outpoint) + .map_err(TxMapAndMaybeTreesError::InputSource) + } + + fn get_unspent_transparent_outputs( + &self, + address: &zcash_primitives::legacy::TransparentAddress, + max_height: zcash_primitives::consensus::BlockHeight, + exclude: &[zcash_primitives::transaction::components::OutPoint], + ) -> Result, Self::Error> { + self.transaction_records_by_id + .get_unspent_transparent_outputs(address, max_height, exclude) + .map_err(TxMapAndMaybeTreesError::InputSource) + } +}