Skip to content

Commit

Permalink
zcash_transparent: Refactor code so it compiles in its new crate
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d authored and nuttycom committed Dec 15, 2024
1 parent 1a3eeab commit e4915f9
Show file tree
Hide file tree
Showing 28 changed files with 303 additions and 273 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions components/zcash_protocol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `zcash_protocol::TxId` (moved from `zcash_primitives::transaction`).

## [0.4.2] - 2024-12-13
### Added
- `no-std` compatibility (`alloc` is required). A default-enabled `std` feature
Expand Down
4 changes: 4 additions & 0 deletions components/zcash_protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ memuse = { workspace = true, optional = true }
# - Documentation
document-features = { workspace = true, optional = true }

# - Encodings
core2.workspace = true
hex.workspace = true

# - Test dependencies
proptest = { workspace = true, optional = true }
incrementalmerkletree = { workspace = true, optional = true }
Expand Down
3 changes: 3 additions & 0 deletions components/zcash_protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub mod local_consensus;
pub mod memo;
pub mod value;

mod txid;
pub use txid::TxId;

/// A Zcash shielded transfer protocol.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ShieldedProtocol {
Expand Down
65 changes: 65 additions & 0 deletions components/zcash_protocol/src/txid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use alloc::string::ToString;
use core::fmt;
use core2::io::{self, Read, Write};

#[cfg(feature = "std")]
use memuse::DynamicUsage;

/// The identifier for a Zcash transaction.
///
/// - For v1-4 transactions, this is a double-SHA-256 hash of the encoded transaction.
/// This means that it is malleable, and only a reliable identifier for transactions
/// that have been mined.
/// - For v5 transactions onwards, this identifier is derived only from "effecting" data,
/// and is non-malleable in all contexts.
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct TxId([u8; 32]);

#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(TxId);

impl fmt::Debug for TxId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// The (byte-flipped) hex string is more useful than the raw bytes, because we can
// look that up in RPC methods and block explorers.
let txid_str = self.to_string();
f.debug_tuple("TxId").field(&txid_str).finish()
}
}

impl fmt::Display for TxId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0;
data.reverse();
formatter.write_str(&hex::encode(data))
}
}

impl AsRef<[u8; 32]> for TxId {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}

impl From<TxId> for [u8; 32] {
fn from(value: TxId) -> Self {
value.0
}
}

impl TxId {
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
TxId(bytes)
}

pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut hash = [0u8; 32];
reader.read_exact(&mut hash)?;
Ok(TxId::from_bytes(hash))
}

pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.0)?;
Ok(())
}
}
1 change: 1 addition & 0 deletions devtools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ zcash_protocol = {workspace = true, features = ["local-consensus"] }

# Transparent
secp256k1.workspace = true
transparent.workspace = true

# Sprout
ed25519-zebra = "4"
Expand Down
28 changes: 18 additions & 10 deletions devtools/src/bin/inspect/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{
convert::{TryFrom, TryInto},
};

use ::transparent::sighash::SighashType;
use bellman::groth16;
use group::GroupEncoding;
use orchard::note_encryption::OrchardDomain;
Expand Down Expand Up @@ -283,7 +284,7 @@ pub(crate) fn inspect(
let sig = secp256k1::ecdsa::Signature::from_der(
&txin.script_sig.0[1..1 + sig_len],
);
let hash_type = txin.script_sig.0[1 + sig_len];
let hash_type = SighashType::parse(txin.script_sig.0[1 + sig_len]);
let pubkey_bytes = &txin.script_sig.0[1 + sig_len + 2..];
let pubkey = secp256k1::PublicKey::from_slice(pubkey_bytes);

Expand All @@ -293,28 +294,35 @@ pub(crate) fn inspect(
i, e
);
}
if hash_type.is_none() {
eprintln!(" ⚠️ Txin {} has invalid sighash type", i);
}
if let Err(e) = pubkey {
eprintln!(
" ⚠️ Txin {} has invalid pubkey encoding: {}",
i, e
);
}
if let (Ok(sig), Ok(pubkey)) = (sig, pubkey) {
if let (Ok(sig), Some(hash_type), Ok(pubkey)) =
(sig, hash_type, pubkey)
{
#[allow(deprecated)]
if pubkey_to_address(&pubkey) != addr {
eprintln!(" ⚠️ Txin {} pubkey does not match coin's script_pubkey", i);
}

let sighash = signature_hash(
tx,
&SignableInput::Transparent {
hash_type,
index: i,
// For P2PKH these are the same.
script_code: &coin.script_pubkey,
script_pubkey: &coin.script_pubkey,
value: coin.value,
},
&SignableInput::Transparent(
::transparent::sighash::SignableInput::from_parts(
hash_type,
i,
// For P2PKH these are the same.
&coin.script_pubkey,
&coin.script_pubkey,
coin.value,
),
),
txid_parts,
);
let msg = secp256k1::Message::from_slice(sighash.as_ref())
Expand Down
16 changes: 15 additions & 1 deletion pczt/src/roles/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,21 @@ impl Signer {
// TODO

input
.sign(index, &self.tx_data, &self.txid_parts, sk, &self.secp)
.sign(
index,
|input| {
v5_signature_hash(
&self.tx_data,
&SignableInput::Transparent(input),
&self.txid_parts,
)
.as_ref()
.try_into()
.unwrap()
},
sk,
&self.secp,
)
.map_err(Error::TransparentSign)?;

// Update transaction modifiability:
Expand Down
7 changes: 5 additions & 2 deletions zcash_primitives/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ and this library adheres to Rust's notion of
- `zcash_primitives::transaction::components::transparent`:
- `builder::TransparentBuilder::add_input` now takes `secp256k1::PublicKey`
instead of `secp256k1::SecretKey`.
- `Bundle<Unauthorized>::apply_signatures` now takes an additional argument
`&TransparentSigningSet`.
- `Bundle<Unauthorized>::apply_signatures` has had its arguments replaced with
a function providing the sighash calculation, and `&TransparentSigningSet`.
- `builder::Error` has a new variant `MissingSigningKey`.
- `zcash_primitives::transaction::builder`:
- `Builder::add_orchard_spend` now takes `orchard::keys::FullViewingKey`
Expand All @@ -38,6 +38,9 @@ and this library adheres to Rust's notion of
- `&TransparentSigningSet`
- `&[sapling::zip32::ExtendedSpendingKey]`
- `&[orchard::keys::SpendAuthorizingKey]`
- `zcash_primitives::transaction::sighash`:
- `SignableInput::Transparent` is now a wrapper around
`zcash_transparent::sighash::SignableInput`.

## [0.20.0] - 2024-11-14

Expand Down
12 changes: 8 additions & 4 deletions zcash_primitives/src/transaction/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,14 @@ impl<'a, P: consensus::Parameters, U: sapling::builder::ProverProgress> Builder<
.clone()
.map(|b| {
b.apply_signatures(
#[cfg(feature = "transparent-inputs")]
&unauthed_tx,
#[cfg(feature = "transparent-inputs")]
&txid_parts,
|input| {
*signature_hash(
&unauthed_tx,
&SignableInput::Transparent(input),
&txid_parts,
)
.as_ref()
},
transparent_signing_set,
)
})
Expand Down
66 changes: 4 additions & 62 deletions zcash_primitives/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ mod tests;

use blake2b_simd::Hash as Blake2bHash;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use memuse::DynamicUsage;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::Debug;
use std::io::{self, Read, Write};
use std::ops::Deref;
Expand Down Expand Up @@ -60,63 +58,7 @@ const ZFUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF;
#[cfg(zcash_unstable = "zfuture")]
const ZFUTURE_TX_VERSION: u32 = 0x0000FFFF;

/// The identifier for a Zcash transaction.
///
/// - For v1-4 transactions, this is a double-SHA-256 hash of the encoded transaction.
/// This means that it is malleable, and only a reliable identifier for transactions
/// that have been mined.
/// - For v5 transactions onwards, this identifier is derived only from "effecting" data,
/// and is non-malleable in all contexts.
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct TxId([u8; 32]);

memuse::impl_no_dynamic_usage!(TxId);

impl fmt::Debug for TxId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// The (byte-flipped) hex string is more useful than the raw bytes, because we can
// look that up in RPC methods and block explorers.
let txid_str = self.to_string();
f.debug_tuple("TxId").field(&txid_str).finish()
}
}

impl fmt::Display for TxId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0;
data.reverse();
formatter.write_str(&hex::encode(data))
}
}

impl AsRef<[u8; 32]> for TxId {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}

impl From<TxId> for [u8; 32] {
fn from(value: TxId) -> Self {
value.0
}
}

impl TxId {
pub fn from_bytes(bytes: [u8; 32]) -> Self {
TxId(bytes)
}

pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut hash = [0u8; 32];
reader.read_exact(&mut hash)?;
Ok(TxId::from_bytes(hash))
}

pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.0)?;
Ok(())
}
}
pub use zcash_protocol::TxId;

/// The set of defined transaction format versions.
///
Expand Down Expand Up @@ -611,12 +553,12 @@ impl Transaction {

fn from_data_v4(data: TransactionData<Authorized>) -> io::Result<Self> {
let mut tx = Transaction {
txid: TxId([0; 32]),
txid: TxId::from_bytes([0; 32]),
data,
};
let mut writer = HashWriter::default();
tx.write(&mut writer)?;
tx.txid.0.copy_from_slice(&writer.into_hash());
tx.txid = TxId::from_bytes(writer.into_hash().into());
Ok(tx)
}

Expand Down Expand Up @@ -706,7 +648,7 @@ impl Transaction {
txid.copy_from_slice(&hash_bytes);

Ok(Transaction {
txid: TxId(txid),
txid: TxId::from_bytes(txid),
data: TransactionData {
version,
consensus_branch_id,
Expand Down
19 changes: 5 additions & 14 deletions zcash_primitives/src/transaction/sighash.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use blake2b_simd::Hash as Blake2bHash;

use super::{
components::amount::NonNegativeAmount, sighash_v4::v4_signature_hash,
sighash_v5::v5_signature_hash, Authorization, TransactionData, TxDigests, TxVersion,
};
use crate::{
legacy::Script,
sapling::{self, bundle::GrothProofBytes},
sighash_v4::v4_signature_hash, sighash_v5::v5_signature_hash, Authorization, TransactionData,
TxDigests, TxVersion,
};
use crate::sapling::{self, bundle::GrothProofBytes};

#[cfg(zcash_unstable = "zfuture")]
use {super::components::Amount, crate::extensions::transparent::Precondition};
Expand All @@ -16,13 +13,7 @@ pub use transparent::sighash::*;

pub enum SignableInput<'a> {
Shielded,
Transparent {
hash_type: u8,
index: usize,
script_code: &'a Script,
script_pubkey: &'a Script,
value: NonNegativeAmount,
},
Transparent(transparent::sighash::SignableInput<'a>),
#[cfg(zcash_unstable = "zfuture")]
Tze {
index: usize,
Expand All @@ -35,7 +26,7 @@ impl<'a> SignableInput<'a> {
pub fn hash_type(&self) -> u8 {
match self {
SignableInput::Shielded => SIGHASH_ALL,
SignableInput::Transparent { hash_type, .. } => *hash_type,
SignableInput::Transparent(input) => input.hash_type().encode(),
#[cfg(zcash_unstable = "zfuture")]
SignableInput::Tze { .. } => SIGHASH_ALL,
}
Expand Down
Loading

0 comments on commit e4915f9

Please sign in to comment.