Skip to content

Commit

Permalink
Add PCZT commands
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Dec 10, 2024
1 parent 4723623 commit ff072fb
Show file tree
Hide file tree
Showing 11 changed files with 859 additions and 27 deletions.
169 changes: 154 additions & 15 deletions Cargo.lock

Large diffs are not rendered by default.

26 changes: 16 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ publish = false
[dependencies]
anyhow = "1"
bip0039 = { version = "0.12", features = ["std", "all-languages"] }
bip32 = "0.5"
futures-util = "0.3"
gumdrop = "0.8"
hex = "0.4"
jubjub = "0.10"
prost = "0.13"
rayon = "1.7"
rusqlite = { version = "0.32", features = ["time"] }
Expand All @@ -27,10 +29,11 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
uuid = "1"

orchard = { version = "0.10", default-features = false }
pczt = "0.0"
sapling = { package = "sapling-crypto", version = "0.3" }
zcash_address = "0.6"
zcash_client_backend = { version = "0.15", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "tor"] }
zcash_client_sqlite = { version = "0.13", features = ["unstable", "orchard"] }
zcash_client_backend = { version = "0.15", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "pczt", "tor"] }
zcash_client_sqlite = { version = "0.13", features = ["unstable", "orchard", "serde"] }
zcash_keys = { version = "0.5", features = ["unstable", "orchard"] }
zcash_primitives = "0.20"
zcash_proofs = "0.20"
Expand Down Expand Up @@ -66,11 +69,14 @@ tui = [
]

[patch.crates-io]
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "e0f04e6c7749751e7f590b2c25275f1fa3421d50" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "bcd08e1d23e70c42a338f3e3f79d6f4c0c219805" }
pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
sapling-crypto = { git = "https://github.com/zcash/sapling-crypto.git", rev = "29cff9683cdf2f0c522ff3224081dfb4fbc80248" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "1e274c892a11cd15f643f08ffa579166d60180bb" }
1 change: 1 addition & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(crate) mod list_accounts;
pub(crate) mod list_addresses;
pub(crate) mod list_tx;
pub(crate) mod list_unspent;
pub(crate) mod pczt;
pub(crate) mod propose;
pub(crate) mod reset;
pub(crate) mod send;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/list_addresses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Command {
println!("Account {}", self.account_id);
let (ua, _) = account
.uivk()
.default_address(UnifiedAddressRequest::all().unwrap())?;
.default_address(UnifiedAddressRequest::all())?;
println!(" Default Address: {}", ua.encode(&params));
Ok(())
}
Expand Down
21 changes: 21 additions & 0 deletions src/commands/pczt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use gumdrop::Options;

pub(crate) mod combine;
pub(crate) mod create;
pub(crate) mod prove;
pub(crate) mod send;
pub(crate) mod sign;

#[derive(Debug, Options)]
pub(crate) enum Command {
#[options(help = "create a PCZT")]
Create(create::Command),
#[options(help = "create proofs for a PCZT")]
Prove(prove::Command),
#[options(help = "apply signatures to a PCZT")]
Sign(sign::Command),
#[options(help = "combine two PCZTs")]
Combine(combine::Command),
#[options(help = "extract a finished transaction and send it")]
Send(send::Command),
}
40 changes: 40 additions & 0 deletions src/commands/pczt/combine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::path::PathBuf;

use anyhow::anyhow;
use gumdrop::Options;
use pczt::{roles::combiner::Combiner, Pczt};
use tokio::{
fs::File,
io::{stdout, AsyncReadExt, AsyncWriteExt},
};

// Options accepted for the `pczt combine` command
#[derive(Debug, Options)]
pub(crate) struct Command {
#[options(help = "a list of PCZT files to combine")]
input: Vec<PathBuf>,
}

impl Command {
pub(crate) async fn run(self) -> Result<(), anyhow::Error> {
let mut pczts = vec![];
for f in self.input {
let mut f = File::open(f).await?;

let mut buf = vec![];
f.read_to_end(&mut buf).await?;

let pczt = Pczt::parse(&buf).map_err(|e| anyhow!("Failed to read PCZT: {:?}", e))?;

pczts.push(pczt);
}

let pczt = Combiner::new(pczts)
.combine()
.map_err(|e| anyhow!("Failed to combine PCZTs: {:?}", e))?;

stdout().write_all(&pczt.serialize()).await?;

Ok(())
}
}
121 changes: 121 additions & 0 deletions src/commands/pczt/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#![allow(deprecated)]
use std::{num::NonZeroUsize, str::FromStr};

use anyhow::anyhow;
use gumdrop::Options;

use tokio::io::{stdout, AsyncWriteExt};
use zcash_address::ZcashAddress;
use zcash_client_backend::{
data_api::{
wallet::{
create_pczt_from_proposal, input_selection::GreedyInputSelector, propose_transfer,
},
WalletRead,
},
fees::{standard::MultiOutputChangeStrategy, DustOutputPolicy, SplitPolicy, StandardFeeRule},
wallet::OvkPolicy,
ShieldedProtocol,
};
use zcash_client_sqlite::WalletDb;
use zcash_protocol::{
memo::{Memo, MemoBytes},
value::Zatoshis,
};
use zip321::{Payment, TransactionRequest};

use crate::{config::WalletConfig, data::get_db_paths, error, MIN_CONFIRMATIONS};

// Options accepted for the `pczt create` command
#[derive(Debug, Options)]
pub(crate) struct Command {
#[options(
required,
help = "the recipient's Unified, Sapling or transparent address"
)]
address: String,

#[options(required, help = "the amount in zatoshis")]
value: u64,

#[options(help = "a memo to send to the recipient")]
memo: Option<String>,

#[options(
help = "note management: the number of notes to maintain in the wallet",
default = "4"
)]
target_note_count: usize,

#[options(
help = "note management: the minimum allowed value for split change amounts",
default = "10000000"
)]
min_split_output_value: u64,
}

impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> Result<(), anyhow::Error> {
let config = WalletConfig::read(wallet_dir.as_ref())?;
let params = config.network();

let (_, db_data) = get_db_paths(wallet_dir.as_ref());
let mut db_data = WalletDb::for_path(db_data, params)?;
let account_id = *db_data
.get_account_ids()?
.first()
.ok_or(anyhow!("Wallet has no accounts"))?;

// Create the PCZT.
let change_strategy = MultiOutputChangeStrategy::new(
StandardFeeRule::Zip317,
None,
ShieldedProtocol::Orchard,
DustOutputPolicy::default(),
SplitPolicy::with_min_output_value(
NonZeroUsize::new(self.target_note_count)
.ok_or(anyhow!("target note count must be nonzero"))?,
Zatoshis::from_u64(self.min_split_output_value)?,
),
);
let input_selector = GreedyInputSelector::new();

let request = TransactionRequest::new(vec![Payment::new(
ZcashAddress::from_str(&self.address).map_err(|_| error::Error::InvalidRecipient)?,
Zatoshis::from_u64(self.value).map_err(|_| error::Error::InvalidAmount)?,
self.memo
.map(|memo| Memo::from_str(&memo))
.transpose()?
.map(MemoBytes::from),
None,
None,
vec![],
)
.ok_or_else(|| anyhow!("Invalid memo"))?])
.map_err(error::Error::from)?;

let proposal = propose_transfer(
&mut db_data,
&params,
account_id,
&input_selector,
&change_strategy,
request,
MIN_CONFIRMATIONS,
)
.map_err(error::Error::from)?;

let pczt = create_pczt_from_proposal(
&mut db_data,
&params,
account_id,
OvkPolicy::Sender,
&proposal,
)
.map_err(error::Error::from)?;

stdout().write_all(&pczt.serialize()).await?;

Ok(())
}
}
Loading

0 comments on commit ff072fb

Please sign in to comment.