Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a WtxId struct, and use it in Zebra's external network protocol #2618

Merged
merged 9 commits into from
Aug 16, 2021
16 changes: 15 additions & 1 deletion zebra-chain/src/serialization/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io, num::TryFromIntError};
use std::{array::TryFromSliceError, io, num::TryFromIntError, str::Utf8Error};

use thiserror::Error;

Expand All @@ -9,20 +9,34 @@ pub enum SerializationError {
/// An io error that prevented deserialization
#[error("io error: {0}")]
Io(#[from] io::Error),

/// The data to be deserialized was malformed.
// XXX refine errors
#[error("parse error: {0}")]
Parse(&'static str),

/// A string was not UTF-8.
///
/// Note: Rust `String` and `str` are always UTF-8.
#[error("string was not UTF-8: {0}")]
Utf8Error(#[from] Utf8Error),

/// A slice was an unexpected length during deserialization.
#[error("slice was the wrong length: {0}")]
TryFromSliceError(#[from] TryFromSliceError),

/// The length of a vec is too large to convert to a usize (and thus, too large to allocate on this platform)
#[error("compactsize too large: {0}")]
TryFromIntError(#[from] TryFromIntError),

/// An error caused when validating a zatoshi `Amount`
#[error("input couldn't be parsed as a zatoshi `Amount`: {source}")]
Amount {
/// The source error indicating how the num failed to validate
#[from]
source: crate::amount::Error,
},

/// Invalid transaction with a non-zero balance and no Sapling shielded spends or outputs.
///
/// Transaction does not conform to the Sapling [consensus
Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub mod arbitrary;
mod tests;

pub use auth_digest::AuthDigest;
pub use hash::Hash;
pub use hash::{Hash, WtxId};
pub use joinsplit::JoinSplitData;
pub use lock_time::LockTime;
pub use memo::Memo;
Expand Down
98 changes: 93 additions & 5 deletions zebra-chain/src/transaction/auth_digest.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,108 @@
use crate::primitives::zcash_primitives::auth_digest;
use std::fmt;

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;

use crate::{
primitives::zcash_primitives::auth_digest,
serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
},
};

use super::Transaction;

/// An authorizing data commitment hash as specified in [ZIP-244].
///
/// [ZIP-244]: https://zips.z.cash/zip-0244..
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
/// Note: Zebra displays transaction and block hashes in big-endian byte-order,
/// following the u256 convention set by Bitcoin and zcashd.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

///
/// [ZIP-244]: https://zips.z.cash/zip-0244
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct AuthDigest(pub(crate) [u8; 32]);

impl<'a> From<&'a Transaction> for AuthDigest {
impl From<Transaction> for AuthDigest {
/// Computes the authorizing data commitment for a transaction.
///
/// # Panics
///
/// If passed a pre-v5 transaction.
fn from(transaction: Transaction) -> Self {
// use the ref implementation, to avoid cloning the transaction
AuthDigest::from(&transaction)
}
}

impl From<&Transaction> for AuthDigest {
/// Computes the authorizing data commitment for a transaction.
///
/// # Panics
///
/// If passed a pre-v5 transaction.
fn from(transaction: &'a Transaction) -> Self {
fn from(transaction: &Transaction) -> Self {
auth_digest(transaction)
}
}

impl From<[u8; 32]> for AuthDigest {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}

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

impl From<&AuthDigest> for [u8; 32] {
fn from(auth_digest: &AuthDigest) -> Self {
(*auth_digest).into()
}
}

impl fmt::Display for AuthDigest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut reversed_bytes = self.0;
reversed_bytes.reverse();
f.write_str(&hex::encode(&reversed_bytes))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
}

impl fmt::Debug for AuthDigest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut reversed_bytes = self.0;
reversed_bytes.reverse();
f.debug_tuple("AuthDigest")
.field(&hex::encode(reversed_bytes))
.finish()
}
}

impl std::str::FromStr for AuthDigest {
type Err = SerializationError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut bytes = [0; 32];
if hex::decode_to_slice(s, &mut bytes[..]).is_err() {
Err(SerializationError::Parse("hex decoding error"))
} else {
bytes.reverse();
Ok(AuthDigest(bytes))
}
}
}

impl ZcashSerialize for AuthDigest {
fn zcash_serialize<W: std::io::Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
writer.write_32_bytes(&self.into())
}
}

impl ZcashDeserialize for AuthDigest {
fn zcash_deserialize<R: std::io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(reader.read_32_bytes()?.into())
}
}
Loading