-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update Transaction definition. (#105)
* Added a few more top-level fields for the Transaction struct * Add a placeholder Script type. This could alternately use bytes::Bytes to save some allocations but I don't think this is important to get perfectly now. In the future, we will want to have all of the script handling code in the zebra-script crate, but we will need to have a container type for an encoded script in zebra-chain, because otherwise zebra-chain would depend on zebra-script and not the other way around. * Rename Transaction{Input,Output} -> Transparent{Input,Output}. These are only *transparent* inputs and outputs, so by putting Transparent in the name (instead of Transaction) it's more clear that a transaction's inputs or outputs can also be shielded. * Add a LockTime enum. * First attempt at a Transaction enum. This attempts to map the versioning and field presence rules into an ADT, so that structurally invalid transactions (e.g., a BCTV14 proof in a Sapling transaction) are unrepresentable. * Update zebra-chain/src/transaction.rs Co-Authored-By: Daira Hopwood <daira@jacaranda.org> * Add fixme note on type refinement. * Rename Transaction variants according to version. * Split transaction.rs into submodules. * Start filling in spend, output descriptions. * Progress on JoinSplit data structures. This has a lot of duplication and should really use generics to abstract over Sprout-on-BCTV14 or Sprout-on-Groth16. * Add data types for Bctv14 and Groth16 proofs. This also adds a trait to abstract over them. * Make JoinSplit descriptions generic over the proof system. * Update zebra-chain/src/transaction/joinsplit.rs
- Loading branch information
1 parent
82e246d
commit c013895
Showing
12 changed files
with
524 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>>, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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\")" | ||
); | ||
} | ||
} |
Oops, something went wrong.