Merge pull request #68 from Electric-Coin-Company/pczt-improvements
PCZT improvements
This commit is contained in:
commit
3b9de014c2
|
@ -1747,8 +1747,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "equihash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab579d7cf78477773b03e80bc2f89702ef02d7112c711d54ca93dcdce68533d5"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
|
@ -1799,8 +1798,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "f4jumble"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d42773cb15447644d170be20231a3268600e0c4cea8987d013b93ac973d3cf7"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
]
|
||||
|
@ -3400,9 +3398,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.7.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
|
||||
checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d"
|
||||
|
||||
[[package]]
|
||||
name = "noop_proc_macro"
|
||||
|
@ -3611,8 +3609,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
|||
[[package]]
|
||||
name = "orchard"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02f7152474406422f572de163e0bc63b2126cdbfe17bc849efbbde36fcfe647e"
|
||||
source = "git+https://github.com/zcash/orchard.git?rev=4fa6d3b549f8803016a309281404eab095d04de8#4fa6d3b549f8803016a309281404eab095d04de8"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bitvec",
|
||||
|
@ -3780,8 +3777,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pczt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "545f95dc3d5295b9ed0360aa3b63e610bd4a73f2f382c616b2fda66c3288e989"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"bls12_381",
|
||||
|
@ -4857,8 +4853,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sapling-crypto"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85c2acdbbab83d554fc2dceea5f7d6d3da71e57adb18a6c80b8901bd0eee54b0"
|
||||
source = "git+https://github.com/zcash/sapling-crypto.git?rev=3c2235747553da642fb142d1eeb9b1afa8391987#3c2235747553da642fb142d1eeb9b1afa8391987"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bellman",
|
||||
|
@ -7420,8 +7415,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
|||
[[package]]
|
||||
name = "zcash_address"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b955fe87f2d9052e3729bdbeb0e94975355f4fe39f7d26aea9457bec6a0bb55"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"bech32 0.11.0",
|
||||
"bs58",
|
||||
|
@ -7434,8 +7428,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_client_backend"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a187ad05cdfe13707c07e6aedca8026b34921d081decfd0b43aac1efd438a7"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"arti-client",
|
||||
"base64 0.22.1",
|
||||
|
@ -7497,8 +7490,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_client_sqlite"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b8046e94c3d746cc00e0ceb4ec4263c4fb93271a8681b425365ad600148ab15"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"bip32",
|
||||
"bs58",
|
||||
|
@ -7536,8 +7528,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_encoding"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3654116ae23ab67dd1f849b01f8821a8a156f884807ff665eac109bf28306c4d"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"core2",
|
||||
"nonempty",
|
||||
|
@ -7546,8 +7537,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_keys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ad3cf576c6e6094cd03f446fcb83ad241ec315a088593cd50940f135cb03ce1"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"bech32 0.11.0",
|
||||
"bip32",
|
||||
|
@ -7589,8 +7579,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_primitives"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b45f3ca3a9df34fcdbf036c2c814417bb417bde742812abc09d744bb3d7ed72"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bip32",
|
||||
|
@ -7630,8 +7619,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_proofs"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5826910c516675eca1f34b3557e159f4e35a4a1711b39fa4f01fb0adb9a9c24"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"bellman",
|
||||
"blake2b_simd",
|
||||
|
@ -7653,8 +7641,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_protocol"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82cb36b15b5a1be70b30c32ce40372dead6561df8a467e297f96b892873a63a2"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"core2",
|
||||
"document-features",
|
||||
|
@ -7677,8 +7664,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zcash_transparent"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0512e8e02af804e852fbbc4bd5db35a9037bc253d2ce396506293a6e7dd745"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"bip32",
|
||||
"blake2b_simd",
|
||||
|
@ -7809,8 +7795,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "zip321"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3e613defb0940acef1f54774b51c7f48f2fa705613dd800870dc69f35cd2ea"
|
||||
source = "git+https://github.com/zcash/librustzcash.git?rev=9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b#9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"nom",
|
||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -78,3 +78,17 @@ tui = [
|
|||
"dep:tokio-util",
|
||||
"dep:tui-logger",
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
orchard = { git = "https://github.com/zcash/orchard.git", rev = "4fa6d3b549f8803016a309281404eab095d04de8" }
|
||||
pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
sapling = { package = "sapling-crypto", git = "https://github.com/zcash/sapling-crypto.git", rev = "3c2235747553da642fb142d1eeb9b1afa8391987" }
|
||||
transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" }
|
||||
|
|
|
@ -4,6 +4,7 @@ pub(crate) mod combine;
|
|||
pub(crate) mod create;
|
||||
pub(crate) mod inspect;
|
||||
pub(crate) mod prove;
|
||||
pub(crate) mod redact;
|
||||
pub(crate) mod send;
|
||||
pub(crate) mod shield;
|
||||
pub(crate) mod sign;
|
||||
|
@ -19,6 +20,8 @@ pub(crate) enum Command {
|
|||
Shield(shield::Command),
|
||||
#[options(help = "inspect a PCZT")]
|
||||
Inspect(inspect::Command),
|
||||
#[options(help = "redact a PCZT")]
|
||||
Redact(redact::Command),
|
||||
#[options(help = "create proofs for a PCZT")]
|
||||
Prove(prove::Command),
|
||||
#[options(help = "apply signatures to a PCZT")]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::anyhow;
|
||||
use gumdrop::Options;
|
||||
use pczt::{roles::verifier::Verifier, Pczt};
|
||||
use secrecy::ExposeSecret;
|
||||
use tokio::io::{stdin, AsyncReadExt};
|
||||
use zcash_primitives::transaction::{
|
||||
sighash::{SighashType, SignableInput},
|
||||
|
@ -8,18 +9,62 @@ use zcash_primitives::transaction::{
|
|||
txid::{to_txid, TxIdDigester},
|
||||
TxVersion,
|
||||
};
|
||||
use zcash_protocol::consensus::{NetworkConstants, Parameters};
|
||||
use zip32::fingerprint::SeedFingerprint;
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
|
||||
// Options accepted for the `pczt inspect` command
|
||||
#[derive(Debug, Options)]
|
||||
pub(crate) struct Command {}
|
||||
pub(crate) struct Command {
|
||||
#[options(
|
||||
help = "age identity file to decrypt the mnemonic phrase with (if a wallet is provided)"
|
||||
)]
|
||||
identity: Option<String>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub(crate) async fn run(self) -> Result<(), anyhow::Error> {
|
||||
pub(crate) async fn run(self, wallet_dir: Option<String>) -> Result<(), anyhow::Error> {
|
||||
// Allow the user to optionally provide the wallet dir, to inspect the PCZT in the
|
||||
// context of a wallet.
|
||||
let mut config = match (WalletConfig::read(wallet_dir.as_ref()), wallet_dir) {
|
||||
(Err(_), None) => Ok(None),
|
||||
(res, _) => res.map(Some),
|
||||
}?;
|
||||
|
||||
let mut buf = vec![];
|
||||
stdin().read_to_end(&mut buf).await?;
|
||||
|
||||
let pczt = Pczt::parse(&buf).map_err(|e| anyhow!("Failed to read PCZT: {:?}", e))?;
|
||||
|
||||
let seed_fp = config
|
||||
.as_mut()
|
||||
.zip(self.identity)
|
||||
.map(|(config, identity)| {
|
||||
// Decrypt the mnemonic to access the seed.
|
||||
let identities = age::IdentityFile::from_file(identity)?.into_identities()?;
|
||||
config.decrypt(identities.iter().map(|i| i.as_ref() as _))?;
|
||||
|
||||
let seed = config
|
||||
.seed()
|
||||
.ok_or(anyhow!(
|
||||
"Identity provided for a wallet that doesn't have a seed"
|
||||
))?
|
||||
.expose_secret();
|
||||
|
||||
SeedFingerprint::from_seed(seed)
|
||||
.ok_or_else(|| anyhow!("Invalid seed length"))
|
||||
.map(|seed_fp| {
|
||||
(
|
||||
seed_fp,
|
||||
zip32::ChildIndex::hardened(
|
||||
config.network().network_type().coin_type(),
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let mut transparent_inputs = vec![];
|
||||
let mut transparent_outputs = vec![];
|
||||
let mut sapling_spends = vec![];
|
||||
|
@ -53,7 +98,19 @@ impl Command {
|
|||
sapling_outputs = bundle
|
||||
.outputs()
|
||||
.iter()
|
||||
.map(|output| (output.user_address().clone(), *output.value()))
|
||||
.map(|output| {
|
||||
(
|
||||
output.user_address().clone(),
|
||||
*output.value(),
|
||||
output
|
||||
.zip32_derivation()
|
||||
.as_ref()
|
||||
.zip(seed_fp.as_ref())
|
||||
.and_then(|(derivation, (seed_fp, coin_type))| {
|
||||
derivation.extract_account_index(seed_fp, *coin_type)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Ok::<_, pczt::roles::verifier::SaplingError<()>>(())
|
||||
})
|
||||
|
@ -67,6 +124,14 @@ impl Command {
|
|||
*action.spend().value(),
|
||||
action.output().user_address().clone(),
|
||||
*action.output().value(),
|
||||
action
|
||||
.output()
|
||||
.zip32_derivation()
|
||||
.as_ref()
|
||||
.zip(seed_fp.as_ref())
|
||||
.and_then(|(derivation, (seed_fp, coin_type))| {
|
||||
derivation.extract_account_index(seed_fp, *coin_type)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
@ -129,29 +194,40 @@ impl Command {
|
|||
|
||||
if !pczt.sapling().outputs().is_empty() {
|
||||
println!("{} Sapling outputs", pczt.sapling().outputs().len());
|
||||
for (index, (user_address, value)) in sapling_outputs.iter().enumerate() {
|
||||
for (index, (user_address, value, account_index)) in sapling_outputs.iter().enumerate()
|
||||
{
|
||||
if let Some(value) = value {
|
||||
if value.inner() == 0 {
|
||||
println!("- {index}: Zero value (likely a dummy)");
|
||||
} else {
|
||||
println!(
|
||||
"- {index}: {} zatoshis{}",
|
||||
"- {index}: {} zatoshis{}{}",
|
||||
value.inner(),
|
||||
match user_address {
|
||||
Some(addr) => format!(" to {addr}"),
|
||||
None => "".into(),
|
||||
},
|
||||
match account_index {
|
||||
Some(idx) =>
|
||||
format!(" (change to ZIP 32 account index {})", u32::from(*idx)),
|
||||
None => "".into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if let Some(addr) = user_address {
|
||||
println!("- {index}: {addr}");
|
||||
} else if let Some(idx) = account_index {
|
||||
println!(
|
||||
"- {index}: change to ZIP 32 account index {}",
|
||||
u32::from(*idx)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !pczt.orchard().actions().is_empty() {
|
||||
println!("{} Orchard actions:", pczt.orchard().actions().len());
|
||||
for (index, (spend_value, output_user_address, output_value)) in
|
||||
for (index, (spend_value, output_user_address, output_value, output_account_index)) in
|
||||
orchard_actions.iter().enumerate()
|
||||
{
|
||||
println!("- {index}:");
|
||||
|
@ -167,16 +243,26 @@ impl Command {
|
|||
println!(" - Output: Zero value (likely a dummy)");
|
||||
} else {
|
||||
println!(
|
||||
" - Output: {} zatoshis{}",
|
||||
" - Output: {} zatoshis{}{}",
|
||||
value.inner(),
|
||||
match output_user_address {
|
||||
Some(addr) => format!(" to {addr}"),
|
||||
None => "".into(),
|
||||
},
|
||||
match output_account_index {
|
||||
Some(idx) =>
|
||||
format!(" (change to ZIP 32 account index {})", u32::from(*idx)),
|
||||
None => "".into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if let Some(addr) = output_user_address {
|
||||
println!(" - Output: {addr}");
|
||||
} else if let Some(idx) = output_account_index {
|
||||
println!(
|
||||
"- {index}: change to ZIP 32 account index {}",
|
||||
u32::from(*idx)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,472 @@
|
|||
use anyhow::anyhow;
|
||||
use gumdrop::Options;
|
||||
use pczt::{
|
||||
roles::redactor::{
|
||||
orchard::ActionRedactor,
|
||||
sapling::{OutputRedactor as SaplingOutputRedactor, SpendRedactor},
|
||||
transparent::{InputRedactor, OutputRedactor as TransparentOutputRedactor},
|
||||
Redactor,
|
||||
},
|
||||
Pczt,
|
||||
};
|
||||
use tokio::io::{stdin, stdout, AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
// Options accepted for the `pczt redact` command
|
||||
#[derive(Debug, Options)]
|
||||
pub(crate) struct Command {
|
||||
#[options(help = "a list of PCZT keys to redact, in foo.bar.baz notation")]
|
||||
key: Vec<String>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub(crate) async fn run(self) -> Result<(), anyhow::Error> {
|
||||
let mut buf = vec![];
|
||||
stdin().read_to_end(&mut buf).await?;
|
||||
|
||||
let pczt = Pczt::parse(&buf).map_err(|e| anyhow!("Failed to read PCZT: {:?}", e))?;
|
||||
|
||||
let pczt = self
|
||||
.key
|
||||
.into_iter()
|
||||
.try_fold(Redactor::new(pczt), |r, key| {
|
||||
redact_pczt(r, key.split('.')).map_err(|e| anyhow!("Failed to redact '{key}': {e}"))
|
||||
})?
|
||||
.finish();
|
||||
|
||||
stdout().write_all(&pczt.serialize()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_pczt<'a>(
|
||||
r: Redactor,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
match key.next() {
|
||||
Some("global") => redact_global(r, key),
|
||||
Some("transparent") => redact_transparent(r, key),
|
||||
Some("sapling") => redact_sapling(r, key),
|
||||
Some("orchard") => redact_orchard(r, key),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_global<'a>(
|
||||
r: Redactor,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
match key.next() {
|
||||
Some(
|
||||
field @ ("tx_version"
|
||||
| "version_group_id"
|
||||
| "consensus_branch_id"
|
||||
| "fallback_lock_time"
|
||||
| "expiry_height"
|
||||
| "coin_type"
|
||||
| "tx_modifiable"),
|
||||
) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("proprietary") => Ok(r.redact_global_with(|mut global| match key.next() {
|
||||
Some(key) => global.redact_proprietary(key),
|
||||
None => global.clear_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_transparent<'a>(
|
||||
r: Redactor,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
match key.next() {
|
||||
Some("inputs") => redact_transparent_input(r, None, key),
|
||||
Some("outputs") => redact_transparent_output(r, None, key),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_transparent_input<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(InputRedactor),
|
||||
{
|
||||
r.redact_transparent_with(|mut transparent| match index {
|
||||
Some(index) => transparent.redact_input(index, f),
|
||||
None => transparent.redact_inputs(f),
|
||||
})
|
||||
}
|
||||
|
||||
fn redact_map<F, const N: usize>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
key: &str,
|
||||
mut f: F,
|
||||
) -> Result<Redactor, anyhow::Error>
|
||||
where
|
||||
F: FnMut(InputRedactor, [u8; N]),
|
||||
{
|
||||
match hex::decode(key) {
|
||||
Ok(key) => match key[..].try_into() {
|
||||
Ok(key) => Ok(redact(r, index, |input| f(input, key))),
|
||||
_ => Err(anyhow!("Invalid map key length")),
|
||||
},
|
||||
_ => Err(anyhow!("Invalid hex '{key}'")),
|
||||
}
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(
|
||||
field @ ("prevout_txid"
|
||||
| "prevout_index"
|
||||
| "sequence"
|
||||
| "required_time_lock_time"
|
||||
| "required_height_lock_time"
|
||||
| "value"
|
||||
| "script_pubkey"
|
||||
| "sighash_type"),
|
||||
) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("script_sig") => Ok(redact(r, index, |mut output| output.clear_script_sig())),
|
||||
Some("redeem_script") => Ok(redact(r, index, |mut output| output.clear_redeem_script())),
|
||||
Some("partial_signatures") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_partial_signature(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| {
|
||||
input.clear_partial_signatures()
|
||||
})),
|
||||
},
|
||||
Some("bip32_derivation") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_bip32_derivation(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| input.clear_bip32_derivation())),
|
||||
},
|
||||
Some("ripemd160_preimages") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_ripemd160_preimage(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| {
|
||||
input.clear_ripemd160_preimages()
|
||||
})),
|
||||
},
|
||||
Some("sha256_preimages") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_sha256_preimage(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| input.clear_sha256_preimages())),
|
||||
},
|
||||
Some("hash160_preimages") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_hash160_preimage(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| {
|
||||
input.clear_hash160_preimages()
|
||||
})),
|
||||
},
|
||||
Some("hash256_preimages") => match key.next() {
|
||||
Some(pubkey) => redact_map(r, index, pubkey, |mut input, pubkey| {
|
||||
input.redact_hash256_preimage(pubkey)
|
||||
}),
|
||||
None => Ok(redact(r, index, |mut input| {
|
||||
input.clear_hash256_preimages()
|
||||
})),
|
||||
},
|
||||
Some("proprietary") => Ok(redact(r, index, |mut input| match key.next() {
|
||||
Some(key) => input.redact_proprietary(key),
|
||||
None => input.clear_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_transparent_output<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(TransparentOutputRedactor),
|
||||
{
|
||||
r.redact_transparent_with(|mut transparent| match index {
|
||||
Some(index) => transparent.redact_output(index, f),
|
||||
None => transparent.redact_outputs(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ ("value" | "script_pubkey")) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("redeem_script") => Ok(redact(r, index, |mut output| output.clear_redeem_script())),
|
||||
Some("bip32_derivation") => match key.next() {
|
||||
Some(data) => match hex::decode(data) {
|
||||
Ok(pubkey) => match pubkey[..].try_into() {
|
||||
Ok(pubkey) => Ok(redact(r, index, |mut output| {
|
||||
output.redact_bip32_derivation(pubkey)
|
||||
})),
|
||||
_ => Err(anyhow!("Invalid pubkey length")),
|
||||
},
|
||||
_ => Err(anyhow!("Invalid hex pubkey '{data}'")),
|
||||
},
|
||||
None => Ok(redact(r, index, |mut output| {
|
||||
output.clear_bip32_derivation()
|
||||
})),
|
||||
},
|
||||
Some("user_address") => Ok(redact(r, index, |mut output| output.clear_user_address())),
|
||||
Some("proprietary") => Ok(redact(r, index, |mut output| match key.next() {
|
||||
Some(key) => output.redact_proprietary(key),
|
||||
None => output.clear_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_sapling<'a>(
|
||||
r: Redactor,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
match key.next() {
|
||||
Some("spends") => redact_sapling_spend(r, None, key),
|
||||
Some("spend") => match key.next() {
|
||||
Some(index) => match index.parse() {
|
||||
Ok(index) => redact_sapling_spend(r, Some(index), key),
|
||||
Err(_) => Err(anyhow!("Invalid index '{index}'")),
|
||||
},
|
||||
None => Err(anyhow!("Missing index")),
|
||||
},
|
||||
Some("outputs") => redact_sapling_output(r, None, key),
|
||||
Some("output") => match key.next() {
|
||||
Some(index) => match index.parse() {
|
||||
Ok(index) => redact_sapling_output(r, Some(index), key),
|
||||
Err(_) => Err(anyhow!("Invalid index '{index}'")),
|
||||
},
|
||||
None => Err(anyhow!("Missing index")),
|
||||
},
|
||||
Some(field @ ("value_sum" | "anchor")) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("bsk") => Ok(r.redact_sapling_with(|mut sapling| sapling.clear_bsk())),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_sapling_spend<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(SpendRedactor),
|
||||
{
|
||||
r.redact_sapling_with(|mut sapling| match index {
|
||||
Some(index) => sapling.redact_spend(index, f),
|
||||
None => sapling.redact_spends(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ ("cv" | "nullifier" | "rk")) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("zkproof") => Ok(redact(r, index, |mut spend| spend.clear_zkproof())),
|
||||
Some("spend_auth_sig") => Ok(redact(r, index, |mut spend| spend.clear_spend_auth_sig())),
|
||||
Some("recipient") => Ok(redact(r, index, |mut spend| spend.clear_recipient())),
|
||||
Some("value") => Ok(redact(r, index, |mut spend| spend.clear_value())),
|
||||
Some("rcm") => Ok(redact(r, index, |mut spend| spend.clear_rcm())),
|
||||
Some("rseed") => Ok(redact(r, index, |mut spend| spend.clear_rseed())),
|
||||
Some("rcv") => Ok(redact(r, index, |mut spend| spend.clear_rcv())),
|
||||
Some("proof_generation_key") => Ok(redact(r, index, |mut spend| {
|
||||
spend.clear_proof_generation_key()
|
||||
})),
|
||||
Some("witness") => Ok(redact(r, index, |mut spend| spend.clear_witness())),
|
||||
Some("alpha") => Ok(redact(r, index, |mut spend| spend.clear_alpha())),
|
||||
Some("zip32_derivation") => {
|
||||
Ok(redact(r, index, |mut spend| spend.clear_zip32_derivation()))
|
||||
}
|
||||
Some("dummy_ask") => Ok(redact(r, index, |mut spend| spend.clear_dummy_ask())),
|
||||
Some("proprietary") => Ok(redact(r, index, |mut spend| match key.next() {
|
||||
Some(key) => spend.redact_proprietary(key),
|
||||
None => spend.clear_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_sapling_output<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(SaplingOutputRedactor),
|
||||
{
|
||||
r.redact_sapling_with(|mut sapling| match index {
|
||||
Some(index) => sapling.redact_output(index, f),
|
||||
None => sapling.redact_outputs(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ ("cv" | "cmu" | "ephemeral_key" | "enc_ciphertext" | "out_ciphertext")) => {
|
||||
Err(anyhow!("Cannot redact '{}'", field))
|
||||
}
|
||||
Some("zkproof") => Ok(redact(r, index, |mut output| output.clear_zkproof())),
|
||||
Some("recipient") => Ok(redact(r, index, |mut output| output.clear_recipient())),
|
||||
Some("value") => Ok(redact(r, index, |mut output| output.clear_value())),
|
||||
Some("rseed") => Ok(redact(r, index, |mut output| output.clear_rseed())),
|
||||
Some("rcv") => Ok(redact(r, index, |mut output| output.clear_rcv())),
|
||||
Some("ock") => Ok(redact(r, index, |mut output| output.clear_ock())),
|
||||
Some("zip32_derivation") => Ok(redact(r, index, |mut output| {
|
||||
output.clear_zip32_derivation()
|
||||
})),
|
||||
Some("user_address") => Ok(redact(r, index, |mut output| output.clear_user_address())),
|
||||
Some("proprietary") => Ok(redact(r, index, |mut output| match key.next() {
|
||||
Some(key) => output.redact_proprietary(key),
|
||||
None => output.clear_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_orchard<'a>(
|
||||
r: Redactor,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
match key.next() {
|
||||
Some("actions") => redact_orchard_action(r, None, key),
|
||||
Some("action") => match key.next() {
|
||||
Some(index) => match index.parse() {
|
||||
Ok(index) => redact_orchard_action(r, Some(index), key),
|
||||
Err(_) => Err(anyhow!("Invalid index '{index}'")),
|
||||
},
|
||||
None => Err(anyhow!("Missing index")),
|
||||
},
|
||||
Some(field @ ("flags" | "value_sum" | "anchor")) => {
|
||||
Err(anyhow!("Cannot redact '{}'", field))
|
||||
}
|
||||
Some("zkproof") => Ok(r.redact_orchard_with(|mut orchard| orchard.clear_zkproof())),
|
||||
Some("bsk") => Ok(r.redact_orchard_with(|mut orchard| orchard.clear_bsk())),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_orchard_action<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(ActionRedactor),
|
||||
{
|
||||
r.redact_orchard_with(|mut orchard| match index {
|
||||
Some(index) => orchard.redact_action(index, f),
|
||||
None => orchard.redact_actions(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ "cv_net") => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("spend") => redact_orchard_spend(r, index, key),
|
||||
Some("output") => redact_orchard_output(r, index, key),
|
||||
Some("rcv") => Ok(redact(r, index, |mut action| action.clear_rcv())),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_orchard_spend<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(ActionRedactor),
|
||||
{
|
||||
r.redact_orchard_with(|mut orchard| match index {
|
||||
Some(index) => orchard.redact_action(index, f),
|
||||
None => orchard.redact_actions(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ ("nullifier" | "rk")) => Err(anyhow!("Cannot redact '{}'", field)),
|
||||
Some("spend_auth_sig") => Ok(redact(r, index, |mut action| action.clear_spend_auth_sig())),
|
||||
Some("recipient") => Ok(redact(r, index, |mut action| {
|
||||
action.clear_spend_recipient()
|
||||
})),
|
||||
Some("value") => Ok(redact(r, index, |mut action| action.clear_spend_value())),
|
||||
Some("rho") => Ok(redact(r, index, |mut action| action.clear_spend_rho())),
|
||||
Some("rseed") => Ok(redact(r, index, |mut action| action.clear_spend_rseed())),
|
||||
Some("fvk") => Ok(redact(r, index, |mut action| action.clear_spend_fvk())),
|
||||
Some("witness") => Ok(redact(r, index, |mut action| action.clear_spend_witness())),
|
||||
Some("alpha") => Ok(redact(r, index, |mut action| action.clear_spend_alpha())),
|
||||
Some("zip32_derivation") => Ok(redact(r, index, |mut action| {
|
||||
action.clear_spend_zip32_derivation()
|
||||
})),
|
||||
Some("dummy_sk") => Ok(redact(r, index, |mut action| action.clear_spend_dummy_sk())),
|
||||
Some("proprietary") => Ok(r.redact_orchard_with(|mut orchard| match index {
|
||||
Some(index) => orchard.redact_action(index, |mut action| match key.next() {
|
||||
Some(key) => action.redact_spend_proprietary(key),
|
||||
None => action.clear_spend_proprietary(),
|
||||
}),
|
||||
None => orchard.redact_actions(|mut actions| match key.next() {
|
||||
Some(key) => actions.redact_spend_proprietary(key),
|
||||
None => actions.clear_spend_proprietary(),
|
||||
}),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_orchard_output<'a>(
|
||||
r: Redactor,
|
||||
index: Option<usize>,
|
||||
mut key: impl Iterator<Item = &'a str>,
|
||||
) -> Result<Redactor, anyhow::Error> {
|
||||
fn redact<F>(r: Redactor, index: Option<usize>, f: F) -> Redactor
|
||||
where
|
||||
F: FnMut(ActionRedactor),
|
||||
{
|
||||
r.redact_orchard_with(|mut orchard| match index {
|
||||
Some(index) => orchard.redact_action(index, f),
|
||||
None => orchard.redact_actions(f),
|
||||
})
|
||||
}
|
||||
|
||||
match key.next() {
|
||||
Some(field @ ("cmx" | "ephemeral_key" | "enc_ciphertext" | "out_ciphertext")) => {
|
||||
Err(anyhow!("Cannot redact '{}'", field))
|
||||
}
|
||||
Some("recipient") => Ok(redact(r, index, |mut action| {
|
||||
action.clear_output_recipient()
|
||||
})),
|
||||
Some("value") => Ok(redact(r, index, |mut action| action.clear_output_value())),
|
||||
Some("rseed") => Ok(redact(r, index, |mut action| action.clear_output_rseed())),
|
||||
Some("ock") => Ok(redact(r, index, |mut action| action.clear_output_ock())),
|
||||
Some("zip32_derivation") => Ok(redact(r, index, |mut action| {
|
||||
action.clear_output_zip32_derivation()
|
||||
})),
|
||||
Some("user_address") => Ok(redact(r, index, |mut action| {
|
||||
action.clear_output_user_address()
|
||||
})),
|
||||
Some("proprietary") => Ok(redact(r, index, |mut action| match key.next() {
|
||||
Some(key) => action.redact_output_proprietary(key),
|
||||
None => action.clear_output_proprietary(),
|
||||
})),
|
||||
Some(field) => Err(anyhow!("Unknown field '{}'", field)),
|
||||
None => Err(anyhow!("Empty field")),
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, convert::Infallible};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use gumdrop::Options;
|
||||
use pczt::{
|
||||
roles::{signer::Signer, updater::Updater},
|
||||
roles::{signer::Signer, verifier::Verifier},
|
||||
Pczt,
|
||||
};
|
||||
use secrecy::ExposeSecret;
|
||||
|
@ -46,8 +46,7 @@ impl Command {
|
|||
let seed_fp =
|
||||
SeedFingerprint::from_seed(seed).ok_or_else(|| anyhow!("Invalid seed length"))?;
|
||||
|
||||
// Find all the spends matching our seed. For now as a hack, we use the Updater
|
||||
// role to access the bundle data we need.
|
||||
// Find all the spends matching our seed.
|
||||
enum KeyRef {
|
||||
Orchard {
|
||||
index: usize,
|
||||
|
@ -62,80 +61,58 @@ impl Command {
|
|||
},
|
||||
}
|
||||
let mut keys = BTreeMap::<zip32::AccountId, Vec<KeyRef>>::new();
|
||||
let pczt = Updater::new(pczt)
|
||||
.update_orchard_with(|updater| {
|
||||
for (index, action) in updater.bundle().actions().iter().enumerate() {
|
||||
if let Some(derivation) = action.spend().zip32_derivation() {
|
||||
if derivation.seed_fingerprint() == &seed_fp.to_bytes()
|
||||
&& derivation.derivation_path().len() == 3
|
||||
&& derivation.derivation_path()[0] == zip32::ChildIndex::hardened(32)
|
||||
&& derivation.derivation_path()[1]
|
||||
== zip32::ChildIndex::hardened(params.network_type().coin_type())
|
||||
{
|
||||
let account_index = zip32::AccountId::try_from(
|
||||
derivation.derivation_path()[2].index() - (1 << 31),
|
||||
let pczt = Verifier::new(pczt)
|
||||
.with_orchard::<Infallible, _>(|bundle| {
|
||||
for (index, action) in bundle.actions().iter().enumerate() {
|
||||
if let Some(account_index) = action
|
||||
.spend()
|
||||
.zip32_derivation()
|
||||
.as_ref()
|
||||
.and_then(|derivation| {
|
||||
derivation.extract_account_index(
|
||||
&seed_fp,
|
||||
zip32::ChildIndex::hardened(params.network_type().coin_type()),
|
||||
)
|
||||
.expect("valid");
|
||||
|
||||
keys.entry(account_index)
|
||||
.or_default()
|
||||
.push(KeyRef::Orchard { index });
|
||||
}
|
||||
})
|
||||
{
|
||||
keys.entry(account_index)
|
||||
.or_default()
|
||||
.push(KeyRef::Orchard { index });
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("no errors")
|
||||
.update_sapling_with(|updater| {
|
||||
for (index, spend) in updater.bundle().spends().iter().enumerate() {
|
||||
if let Some(derivation) = spend.zip32_derivation() {
|
||||
if derivation.seed_fingerprint() == &seed_fp.to_bytes()
|
||||
&& derivation.derivation_path().len() == 3
|
||||
&& derivation.derivation_path()[0] == zip32::ChildIndex::hardened(32)
|
||||
&& derivation.derivation_path()[1]
|
||||
== zip32::ChildIndex::hardened(params.network_type().coin_type())
|
||||
{
|
||||
let account_index = zip32::AccountId::try_from(
|
||||
derivation.derivation_path()[2].index() - (1 << 31),
|
||||
.map_err(|e| anyhow!("Invalid PCZT: {:?}", e))?
|
||||
.with_sapling::<Infallible, _>(|bundle| {
|
||||
for (index, spend) in bundle.spends().iter().enumerate() {
|
||||
if let Some(account_index) =
|
||||
spend.zip32_derivation().as_ref().and_then(|derivation| {
|
||||
derivation.extract_account_index(
|
||||
&seed_fp,
|
||||
zip32::ChildIndex::hardened(params.network_type().coin_type()),
|
||||
)
|
||||
.expect("valid");
|
||||
|
||||
keys.entry(account_index)
|
||||
.or_default()
|
||||
.push(KeyRef::Sapling { index });
|
||||
}
|
||||
})
|
||||
{
|
||||
keys.entry(account_index)
|
||||
.or_default()
|
||||
.push(KeyRef::Sapling { index });
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("no errors")
|
||||
.update_transparent_with(|updater| {
|
||||
for (index, input) in updater.bundle().inputs().iter().enumerate() {
|
||||
.map_err(|e| anyhow!("Invalid PCZT: {:?}", e))?
|
||||
.with_transparent::<Infallible, _>(|bundle| {
|
||||
for (index, input) in bundle.inputs().iter().enumerate() {
|
||||
for derivation in input.bip32_derivation().values() {
|
||||
if derivation.seed_fingerprint() == &seed_fp.to_bytes()
|
||||
&& derivation.derivation_path().len() == 5
|
||||
&& derivation.derivation_path()[0]
|
||||
== bip32::ChildNumber::new(44, true).expect("valid")
|
||||
&& derivation.derivation_path()[1]
|
||||
== bip32::ChildNumber::new(params.network_type().coin_type(), true)
|
||||
.expect("valid")
|
||||
&& derivation.derivation_path()[2].is_hardened()
|
||||
&& !derivation.derivation_path()[3].is_hardened()
|
||||
&& !derivation.derivation_path()[4].is_hardened()
|
||||
if let Some((account_index, scope, address_index)) = derivation
|
||||
.extract_bip_44_fields(
|
||||
&seed_fp,
|
||||
bip32::ChildNumber(
|
||||
params.network_type().coin_type()
|
||||
| bip32::ChildNumber::HARDENED_FLAG,
|
||||
),
|
||||
)
|
||||
{
|
||||
let account_index =
|
||||
zip32::AccountId::try_from(derivation.derivation_path()[2].index())
|
||||
.expect("valid");
|
||||
|
||||
let scope = TransparentKeyScope::custom(
|
||||
derivation.derivation_path()[3].index(),
|
||||
)
|
||||
.expect("valid");
|
||||
let address_index = NonHardenedChildIndex::from_index(
|
||||
derivation.derivation_path()[4].index(),
|
||||
)
|
||||
.expect("valid");
|
||||
|
||||
keys.entry(account_index)
|
||||
.or_default()
|
||||
.push(KeyRef::Transparent {
|
||||
|
@ -148,7 +125,7 @@ impl Command {
|
|||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("no errors")
|
||||
.map_err(|e| anyhow!("Invalid PCZT: {:?}", e))?
|
||||
.finish();
|
||||
|
||||
let mut signer =
|
||||
|
|
|
@ -174,7 +174,8 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
Some(Command::Pczt(command)) => match command {
|
||||
commands::pczt::Command::Create(command) => command.run(opts.wallet_dir).await,
|
||||
commands::pczt::Command::Shield(command) => command.run(opts.wallet_dir).await,
|
||||
commands::pczt::Command::Inspect(command) => command.run().await,
|
||||
commands::pczt::Command::Inspect(command) => command.run(opts.wallet_dir).await,
|
||||
commands::pczt::Command::Redact(command) => command.run().await,
|
||||
commands::pczt::Command::Prove(command) => command.run(opts.wallet_dir).await,
|
||||
commands::pczt::Command::Sign(command) => command.run(opts.wallet_dir).await,
|
||||
commands::pczt::Command::Combine(command) => command.run().await,
|
||||
|
|
Loading…
Reference in New Issue