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

Update Transaction definition. #105

Merged
merged 16 commits into from
Dec 5, 2019
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
1 change: 1 addition & 0 deletions zebra-chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod sha256d_writer;
pub mod block;
pub mod equihash_solution;
pub mod note_commitment_tree;
pub mod proofs;
pub mod serialization;
pub mod transaction;
pub mod types;
22 changes: 22 additions & 0 deletions zebra-chain/src/proofs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! ZK proofs used in Zcash.

use std::fmt::Debug;

mod bctv14;
mod groth16;

pub use bctv14::Bctv14Proof;
pub use groth16::Groth16Proof;

/// A marker trait used to abstract over BCTV14 or Groth16 proofs.
pub trait ZkSnarkProof: Copy + Clone + Debug + PartialEq + Eq + private::Sealed {}
impl ZkSnarkProof for Bctv14Proof {}
impl ZkSnarkProof for Groth16Proof {}

mod private {
use super::*;

pub trait Sealed {}
impl Sealed for Bctv14Proof {}
impl Sealed for Groth16Proof {}
}
32 changes: 32 additions & 0 deletions zebra-chain/src/proofs/bctv14.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::fmt;

/// An encoding of a BCTV14 proof, as used in Zcash.
pub struct Bctv14Proof(pub [u8; 296]);

impl fmt::Debug for Bctv14Proof {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Bctv14Proof")
.field(&hex::encode(&self.0[..]))
.finish()
}
}

// These impls all only exist because of array length restrictions.

impl Copy for Bctv14Proof {}

impl Clone for Bctv14Proof {
fn clone(&self) -> Self {
let mut bytes = [0; 296];
bytes[..].copy_from_slice(&self.0[..]);
Self(bytes)
}
}

impl PartialEq for Bctv14Proof {
fn eq(&self, other: &Self) -> bool {
self.0[..] == other.0[..]
}
}

impl Eq for Bctv14Proof {}
32 changes: 32 additions & 0 deletions zebra-chain/src/proofs/groth16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::fmt;

/// An encoding of a Groth16 proof, as used in Zcash.
pub struct Groth16Proof(pub [u8; 192]);

impl fmt::Debug for Groth16Proof {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Groth16Proof")
.field(&hex::encode(&self.0[..]))
.finish()
}
}

// These impls all only exist because of array length restrictions.

impl Copy for Groth16Proof {}

impl Clone for Groth16Proof {
fn clone(&self) -> Self {
let mut bytes = [0; 192];
bytes[..].copy_from_slice(&self.0[..]);
Self(bytes)
}
}

impl PartialEq for Groth16Proof {
fn eq(&self, other: &Self) -> bool {
self.0[..] == other.0[..]
}
}

impl Eq for Groth16Proof {}
228 changes: 79 additions & 149 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,158 +1,88 @@
//! Transaction types.

use std::{fmt, io};
mod hash;
mod joinsplit;
mod serialize;
mod shielded_data;
mod transparent;

use hex;

use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
use crate::sha256d_writer::Sha256dWriter;

/// A hash of a `Transaction`
///
/// TODO: I'm pretty sure this is also a SHA256d hash but I haven't
/// confirmed it yet.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct TransactionHash(pub [u8; 32]);

impl fmt::Debug for TransactionHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("TransactionHash")
.field(&hex::encode(&self.0))
.finish()
}
}

impl From<Transaction> for TransactionHash {
fn from(transaction: Transaction) -> Self {
let mut hash_writer = Sha256dWriter::default();
transaction
.zcash_serialize(&mut hash_writer)
.expect("Transactions must serialize into the hash.");
Self(hash_writer.finish())
}
}

/// OutPoint
///
/// A particular transaction output reference.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct OutPoint {
/// References the transaction that contains the UTXO being spent.
pub hash: TransactionHash,

/// Identifies which UTXO from that transaction is referenced; the
/// first output is 0, etc.
pub index: u32,
}

/// Transaction Input
// `Copy` cannot be implemented for `Vec<u8>`
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TransactionInput {
/// The previous output transaction reference.
pub previous_output: OutPoint,
#[cfg(test)]
mod tests;

/// Computational Script for confirming transaction authorization.
// XXX pzec uses their own `Bytes` type that wraps a `Vec<u8>`
// with some extra methods.
pub signature_script: Vec<u8>,
pub use hash::TransactionHash;
pub use joinsplit::{JoinSplit, JoinSplitData, SproutInputNoteData, SproutOutputNoteData};
pub use shielded_data::{OutputDescription, ShieldedData, SpendDescription};
pub use transparent::{OutPoint, TransparentInput, TransparentOutput};

/// Transaction version as defined by the sender. Intended for
/// "replacement" of transactions when information is updated
/// before inclusion into a block.
pub sequence: u32,
}
use crate::proofs::{Bctv14Proof, Groth16Proof};
use crate::types::{BlockHeight, LockTime};

/// Transaction Output
///
/// The most fundamental building block of a transaction is a
/// transaction output -- the ZEC you own in your "wallet" is in
/// fact a subset of unspent transaction outputs (or "UTXO"s) of the
/// global UTXO set.
/// A Zcash transaction.
///
/// UTXOs are indivisible, discrete units of value which can only be
/// consumed in their entirety. Thus, if I want to send you 1 ZEC and
/// I only own one UTXO worth 2 ZEC, I would construct a transaction
/// that spends my UTXO and sends 1 ZEC to you and 1 ZEC back to me
/// (just like receiving change).
// `Copy` cannot be implemented for `Vec<u8>`
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TransactionOutput {
/// Transaction value.
// At https://en.bitcoin.it/wiki/Protocol_documentation#tx, this is an i64.
pub value: u64,

/// Usually contains the public key as a Bitcoin script setting up
/// conditions to claim this output.
pub pk_script: Vec<u8>,
}

/// Transaction
/// A transaction is an encoded data structure that facilitates the transfer of
/// value between two public key addresses on the Zcash ecosystem. Everything is
/// designed to ensure that transactions can created, propagated on the network,
/// validated, and finally added to the global ledger of transactions (the
/// blockchain).
///
/// A transaction is an encoded data structure that facilitates the
/// transfer of value between two public key addresses on the Zcash
/// ecosystem. Everything is designed to ensure that transactions can
/// created, propagated on the network, validated, and finally added
/// to the global ledger of transactions (the blockchain).
// This is not up to date with the data included in the Zcash
// transaction format: https://zips.z.cash/protocol/protocol.pdf
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Transaction {
/// Transaction data format version (note, this is signed).
pub version: i32,

/// A list of 1 or more transaction inputs or sources for coins.
pub tx_in: Vec<TransactionInput>,

/// A list of 1 or more transaction outputs or destinations for coins.
pub tx_out: Vec<TransactionOutput>,

/// The block number or timestamp at which this transaction is unlocked:
///
/// |Value |Description |
/// |------------|----------------------------------------------------|
/// |0 |Not locked (default) |
/// |< 500000000 |Block number at which this transaction is unlocked |
/// |>= 500000000|UNIX timestamp at which this transaction is unlocked|
///
/// If all `TransactionInput`s have final (0xffffffff) sequence
/// numbers, then lock_time is irrelevant. Otherwise, the
/// transaction may not be added to a block until after `lock_time`.
pub lock_time: u32,
}

impl ZcashSerialize for Transaction {
fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), SerializationError> {
unimplemented!();
}
}

impl ZcashDeserialize for Transaction {
fn zcash_deserialize<R: io::Read>(_reader: R) -> Result<Self, SerializationError> {
unimplemented!();
}
}

#[cfg(test)]
mod tests {

use std::io::Write;

use super::TransactionHash;

use crate::sha256d_writer::Sha256dWriter;

#[test]
fn transactionhash_debug() {
let preimage = b"foo bar baz";
let mut sha_writer = Sha256dWriter::default();
let _ = sha_writer.write_all(preimage);

let hash = TransactionHash(sha_writer.finish());

assert_eq!(
format!("{:?}", hash),
"TransactionHash(\"bf46b4b5030752fedac6f884976162bbfb29a9398f104a280b3e34d51b416631\")"
);
}
/// Zcash has a number of different transaction formats. They are represented
/// internally by different enum variants. Because we checkpoint on Sapling
/// activation, we do not parse any pre-Sapling transaction types.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Transaction {
/// A fully transparent transaction (`version = 1`).
V1 {
/// The transparent inputs to the transaction.
inputs: Vec<TransparentInput>,
/// The transparent outputs from the transaction.
outputs: Vec<TransparentOutput>,
/// The earliest time or block height that this transaction can be added to the
/// chain.
lock_time: LockTime,
},
/// A Sprout transaction (`version = 2`).
V2 {
/// The transparent inputs to the transaction.
inputs: Vec<TransparentInput>,
/// The transparent outputs from the transaction.
outputs: Vec<TransparentOutput>,
/// The earliest time or block height that this transaction can be added to the
/// chain.
lock_time: LockTime,
/// The JoinSplit data for this transaction, if any.
joinsplit_data: Option<JoinSplitData<Bctv14Proof>>,
},
/// An Overwinter transaction (`version = 3`).
V3 {
/// The transparent inputs to the transaction.
inputs: Vec<TransparentInput>,
/// The transparent outputs from the transaction.
outputs: Vec<TransparentOutput>,
/// The earliest time or block height that this transaction can be added to the
/// chain.
lock_time: LockTime,
/// The latest block height that this transaction can be added to the chain.
expiry_height: BlockHeight,
/// The JoinSplit data for this transaction, if any.
joinsplit_data: Option<JoinSplitData<Bctv14Proof>>,
},
/// A Sapling transaction (`version = 4`).
V4 {
/// The transparent inputs to the transaction.
inputs: Vec<TransparentInput>,
/// The transparent outputs from the transaction.
outputs: Vec<TransparentOutput>,
/// The earliest time or block height that this transaction can be added to the
/// chain.
lock_time: LockTime,
/// The latest block height that this transaction can be added to the chain.
expiry_height: BlockHeight,
/// The net value of Sapling spend transfers minus output transfers.
// XXX refine this to an Amount type.
value_balance: i64,
/// The shielded data for this transaction, if any.
shielded_data: Option<ShieldedData>,
/// The JoinSplit data for this transaction, if any.
joinsplit_data: Option<JoinSplitData<Groth16Proof>>,
},
}
55 changes: 55 additions & 0 deletions zebra-chain/src/transaction/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::fmt;

use hex;

use crate::{serialization::ZcashSerialize, sha256d_writer::Sha256dWriter};

use super::Transaction;

/// A hash of a `Transaction`
///
/// TODO: I'm pretty sure this is also a SHA256d hash but I haven't
/// confirmed it yet.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct TransactionHash(pub [u8; 32]);

impl From<Transaction> for TransactionHash {
fn from(transaction: Transaction) -> Self {
let mut hash_writer = Sha256dWriter::default();
transaction
.zcash_serialize(&mut hash_writer)
.expect("Transactions must serialize into the hash.");
Self(hash_writer.finish())
}
}

impl fmt::Debug for TransactionHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("TransactionHash")
.field(&hex::encode(&self.0))
.finish()
}
}

#[cfg(test)]
mod tests {
use std::io::Write;

use crate::sha256d_writer::Sha256dWriter;

use super::*;

#[test]
fn transactionhash_debug() {
let preimage = b"foo bar baz";
let mut sha_writer = Sha256dWriter::default();
let _ = sha_writer.write_all(preimage);

let hash = TransactionHash(sha_writer.finish());

assert_eq!(
format!("{:?}", hash),
"TransactionHash(\"bf46b4b5030752fedac6f884976162bbfb29a9398f104a280b3e34d51b416631\")"
);
}
}
Loading