Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,16 @@ tui = [
]

[patch.crates-io]
equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
f4jumble = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "2448b6d730c8e07d829c85429b756b5f84073624" }
equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
f4jumble = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
4 changes: 4 additions & 0 deletions src/commands/pczt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub(crate) mod combine;
pub(crate) mod create;
pub(crate) mod create_manual;
pub(crate) mod inspect;
pub(crate) mod pay_manual;
pub(crate) mod prove;
pub(crate) mod redact;
pub(crate) mod send;
Expand All @@ -23,6 +24,9 @@ pub(crate) enum Command {
Shield(shield::Command),
/// Create a PCZT from manually-provided transparent inputs
CreateManual(create_manual::Command),
/// Create a PCZT to satisfy a payment request by spending manually-provided transparent
/// inputs.
PayManual(pay_manual::Command),
/// Inspect a PCZT
Inspect(inspect::Command),
/// Adds BIP 44 or ZIP 32 derivations to a PCZT
Expand Down
160 changes: 7 additions & 153 deletions src/commands/pczt/create_manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ use anyhow::anyhow;
use clap::Args;
use pczt::roles::{creator::Creator, io_finalizer::IoFinalizer, updater::Updater};
use rand::rngs::OsRng;
use serde::Deserialize;
use tokio::io::{stdout, AsyncWriteExt};
use transparent::{
address::{Script, TransparentAddress},
bundle::{OutPoint, TxOut},
};
use transparent::builder::TransparentInputInfo;

use zcash_address::ZcashAddress;
use zcash_client_backend::proto::service::{ChainSpec, TxFilter};
use zcash_keys::address::{Address, Receiver};
use zcash_keys::address::Address;
use zcash_primitives::transaction::{
builder::{Builder, PcztResult},
fees::zip317,
Expand All @@ -25,12 +21,12 @@ use zcash_protocol::{
memo::{Memo, MemoBytes},
value::Zatoshis,
};
use zcash_script::script;

use crate::{
config::WalletConfig,
data::Network,
error,
helpers::pczt::create_manual::{add_inputs, add_recipient, handle_recipient, parse_coins},
remote::{tor_client, Servers},
};

Expand Down Expand Up @@ -78,69 +74,6 @@ pub(crate) struct Command {
disable_tor: bool,
}

fn parse_coins(s: &str) -> anyhow::Result<Vec<Coin>> {
Ok(serde_json::from_str(s)?)
}

#[derive(Clone, Debug, Deserialize)]
struct Coin {
txid: String,
out_index: u32,
value: Option<u64>,
script_pubkey: Option<String>,
pubkey: Option<secp256k1::PublicKey>,
redeem_script: Option<String>,
}

impl Coin {
/// Returns a pointer to this coin in the Zcash chain.
fn outpoint(&self) -> anyhow::Result<OutPoint> {
let hash: [u8; 32] = {
let mut bytes = hex::decode(&self.txid)?;
bytes.reverse();
bytes
.as_slice()
.try_into()
.map_err(|e| anyhow!("Invalid coin outpoint hash: {e}"))?
};

Ok(OutPoint::new(hash, self.out_index))
}

/// Returns the coin itself, if provided.
fn coin(&self) -> anyhow::Result<Option<TxOut>> {
self.value
.zip(self.script_pubkey.as_ref())
.map(|(value, script_pubkey)| {
let value = Zatoshis::from_u64(value).map_err(|_| error::Error::InvalidAmount)?;
let script_pubkey = Script(script::Code(hex::decode(script_pubkey)?));
Ok(TxOut::new(value, script_pubkey))
})
.transpose()
}

/// Returns the information needed to spend this coin.
fn spend_info(&self) -> anyhow::Result<SpendInfo> {
match (&self.pubkey, &self.redeem_script) {
(None, None) => Err(anyhow!("Missing either `pubkey` or `redeem_script")),
(Some(_), Some(_)) => Err(anyhow!("Cannot provide both `pubkey` and `redeem_script`")),
(Some(pubkey), None) => Ok(SpendInfo::P2pkh { pubkey: *pubkey }),
(None, Some(script_hex)) => {
let script_bytes = hex::decode(script_hex)?;
let redeem_script = script::FromChain::parse(&script::Code(script_bytes))
.map_err(|e| anyhow!("{e:?}"))?;
Ok(SpendInfo::P2sh { redeem_script })
}
}
}
}

#[derive(Clone)]
enum SpendInfo {
P2pkh { pubkey: secp256k1::PublicKey },
P2sh { redeem_script: script::FromChain },
}

impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> anyhow::Result<()> {
let params = if let Some(network) = self.network {
Expand Down Expand Up @@ -218,91 +151,12 @@ impl Command {
};

value_in = (value_in + coin.value).ok_or_else(|| anyhow!("Balance overflow"))?;
transparent_inputs.push((utxo, coin, spend_info));
}

fn handle_recipient<C, T>(
recipient: Address,
ctx: C,
on_transparent: impl FnOnce(TransparentAddress, C) -> anyhow::Result<T>,
on_sapling: impl FnOnce(sapling::PaymentAddress, C) -> anyhow::Result<T>,
on_orchard: impl FnOnce(orchard::Address, C) -> anyhow::Result<T>,
) -> anyhow::Result<T> {
match recipient {
Address::Sapling(payment_address) => on_sapling(payment_address, ctx),
Address::Transparent(transparent_address) => {
on_transparent(transparent_address, ctx)
}
Address::Unified(unified_address) => match unified_address
.as_understood_receivers()
.into_iter()
.next()
.ok_or_else(|| anyhow!("Recipient is UA with no understood receivers"))?
{
Receiver::Orchard(address) => on_orchard(address, ctx),
Receiver::Sapling(payment_address) => on_sapling(payment_address, ctx),
Receiver::Transparent(transparent_address) => {
on_transparent(transparent_address, ctx)
}
},
// Only supported inputs are transparent, so it's fine to send directly to
// a TEX address.
Address::Tex(p2pkh_hash) => {
on_transparent(TransparentAddress::PublicKeyHash(p2pkh_hash), ctx)
}
}
let input = TransparentInputInfo::from_parts(utxo, coin, spend_info)
.map_err(|e| anyhow!("Invalid transparent input data: {}", e))?;
transparent_inputs.push(input);
}

let add_inputs = |builder: &mut Builder<'_, _, _>,
transparent_inputs: Vec<(OutPoint, TxOut, SpendInfo)>|
-> anyhow::Result<()> {
for (utxo, coin, spend_info) in transparent_inputs {
match spend_info {
SpendInfo::P2pkh { pubkey } => builder
.add_transparent_input(pubkey, utxo, coin)
.map_err(|e| anyhow!("{e}"))?,
SpendInfo::P2sh { redeem_script } => builder
.add_transparent_p2sh_input(redeem_script, utxo, coin)
.map_err(|e| anyhow!("{e}"))?,
}
}
Ok(())
};

let add_recipient = |builder: &mut Builder<'_, _, _>,
recipient: Address,
value: Zatoshis,
memo: Option<MemoBytes>|
-> anyhow::Result<()> {
handle_recipient(
recipient,
(builder, memo),
|to, (builder, _)| {
builder
.add_transparent_output(&to, value)
.map_err(|e| anyhow!("{e}"))
},
|to, (builder, memo)| {
Ok(builder.add_sapling_output::<zip317::FeeError>(
None,
to,
value,
memo.unwrap_or(MemoBytes::empty()),
)?)
},
|recipient, (builder, memo)| {
Ok(builder.add_orchard_output::<zip317::FeeError>(
None,
recipient,
value,
memo.unwrap_or(MemoBytes::empty()),
)?)
},
)?;
Ok(())
};

let prepare_builder = |transparent_inputs: Vec<(OutPoint, TxOut, SpendInfo)>,
let prepare_builder = |transparent_inputs: Vec<TransparentInputInfo>,
recipient: Address,
value: Zatoshis,
memo: Option<MemoBytes>|
Expand Down
Loading
Loading