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
This commit is contained in:
parent
82e246d87b
commit
c013895cd7
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
|
|
@ -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 {}
|
||||
|
|
@ -1,158 +1,88 @@
|
|||
//! Transaction types.
|
||||
|
||||
use std::{fmt, io};
|
||||
|
||||
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,
|
||||
|
||||
/// 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>,
|
||||
|
||||
/// Transaction version as defined by the sender. Intended for
|
||||
/// "replacement" of transactions when information is updated
|
||||
/// before inclusion into a block.
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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).
|
||||
// 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!();
|
||||
}
|
||||
}
|
||||
mod hash;
|
||||
mod joinsplit;
|
||||
mod serialize;
|
||||
mod shielded_data;
|
||||
mod transparent;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod tests;
|
||||
|
||||
use std::io::Write;
|
||||
pub use hash::TransactionHash;
|
||||
pub use joinsplit::{JoinSplit, JoinSplitData, SproutInputNoteData, SproutOutputNoteData};
|
||||
pub use shielded_data::{OutputDescription, ShieldedData, SpendDescription};
|
||||
pub use transparent::{OutPoint, TransparentInput, TransparentOutput};
|
||||
|
||||
use super::TransactionHash;
|
||||
use crate::proofs::{Bctv14Proof, Groth16Proof};
|
||||
use crate::types::{BlockHeight, LockTime};
|
||||
|
||||
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\")"
|
||||
);
|
||||
}
|
||||
/// A Zcash 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).
|
||||
///
|
||||
/// 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>>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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\")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
use crate::proofs::ZkSnarkProof;
|
||||
|
||||
/// Describes input notes to a Sprout transaction.
|
||||
///
|
||||
/// The [protocol specification §7.2][ps] describes these fields as being encoded
|
||||
/// separately into two arrays of the same length. Instead, by bundling them
|
||||
/// together into one structure, we can ensure that it's not possible to create a
|
||||
/// JoinSplit description with mismatched array lengths. This means we do not
|
||||
/// need to maintain any invariants about equal array lengths.
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SproutInputNoteData {
|
||||
/// A nullifier for the input note.
|
||||
///
|
||||
/// XXX refine type
|
||||
pub nullifier: [u8; 32],
|
||||
/// A message authentication tag.
|
||||
///
|
||||
/// XXX refine type
|
||||
pub vmac: [u8; 32],
|
||||
}
|
||||
|
||||
/// Describes output notes from a Sprout transaction.
|
||||
///
|
||||
/// The [protocol specification §7.2][ps] describes these fields as being encoded
|
||||
/// separately into two arrays of the same length. Instead, by bundling them
|
||||
/// together into one structure, we can ensure that it's not possible to create a
|
||||
/// JoinSplit description with mismatched array lengths. This means we do not
|
||||
/// need to maintain any invariants about equal array lengths.
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SproutOutputNoteData {
|
||||
/// A note commitment for this output note.
|
||||
///
|
||||
/// XXX refine type
|
||||
pub commitment: [u8; 32],
|
||||
/// A ciphertext component for this output note.
|
||||
///
|
||||
/// XXX refine type
|
||||
/// XXX this should be a [u8; 601] but we need trait impls.
|
||||
pub enc_ciphertext: Vec<u8>,
|
||||
}
|
||||
|
||||
/// A _JoinSplit Description_, as described in [protocol specification §7.2][ps].
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct JoinSplit<P: ZkSnarkProof> {
|
||||
/// A value that the JoinSplit transfer removes from the transparent value
|
||||
/// pool.
|
||||
///
|
||||
/// XXX refine to an Amount
|
||||
vpub_old: u64,
|
||||
/// A value that the JoinSplit transfer inserts into the transparent value
|
||||
/// pool.
|
||||
///
|
||||
/// XXX refine to an Amount
|
||||
vpub_new: u64,
|
||||
/// A root of the Sprout note commitment tree at some block height in the
|
||||
/// past, or the root produced by a previous JoinSplit transfer in this
|
||||
/// transaction.
|
||||
///
|
||||
/// XXX refine type
|
||||
anchor: [u8; 32],
|
||||
/// An X25519 public key.
|
||||
///
|
||||
/// XXX refine to an x25519-dalek type?
|
||||
ephemeral_key: [u8; 32],
|
||||
/// A 256-bit seed that must be chosen independently at random for each
|
||||
/// JoinSplit description.
|
||||
random_seed: [u8; 32],
|
||||
/// A sequence of input notes for this transaction.
|
||||
input_notes: Vec<SproutInputNoteData>,
|
||||
/// A sequence of output notes for this transaction.
|
||||
output_notes: Vec<SproutOutputNoteData>,
|
||||
/// A ZK JoinSplit proof, either a [`Groth16Proof`] or a [`Bctv14Proof`].
|
||||
zkproof: P,
|
||||
}
|
||||
|
||||
/// A bundle of JoinSplit descriptions and signature data.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct JoinSplitData<P: ZkSnarkProof> {
|
||||
/// A sequence of JoinSplit descriptions using proofs of type `P`.
|
||||
pub joinsplits: Vec<JoinSplit<P>>,
|
||||
/// The public key for the JoinSplit signature.
|
||||
// XXX refine to a Zcash-flavored Ed25519 pubkey.
|
||||
pub pub_key: [u8; 32],
|
||||
/// The JoinSplit signature.
|
||||
// XXX refine to a Zcash-flavored Ed25519 signature.
|
||||
// for now it's [u64; 8] rather than [u8; 64] to get trait impls
|
||||
pub sig: [u64; 8],
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//! Contains impls of `ZcashSerialize`, `ZcashDeserialize` for all of the
|
||||
//! transaction types, so that all of the serialization logic is in one place.
|
||||
|
||||
use std::io;
|
||||
|
||||
use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
|
||||
|
||||
use super::Transaction;
|
||||
|
||||
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!();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// XXX this name seems too long?
|
||||
use crate::note_commitment_tree::SaplingNoteTreeRootHash;
|
||||
|
||||
/// A _Spend Description_, as described in [protocol specification §7.3][ps].
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SpendDescription {
|
||||
/// A value commitment to the value of the input note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub cv: [u8; 32],
|
||||
/// A root of the Sapling note commitment tree at some block height in the past.
|
||||
pub anchor: SaplingNoteTreeRootHash,
|
||||
/// The nullifier of the input note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub nullifier: [u8; 32],
|
||||
/// The randomized public key for `spend_auth_sig`.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub rk: [u8; 32],
|
||||
/// The ZK spend proof.
|
||||
///
|
||||
/// XXX add proof types.
|
||||
/// XXX for now it's [u64; 24] instead of [u8; 192] to get trait impls
|
||||
pub zkproof: [u64; 24],
|
||||
/// A signature authorizing this spend.
|
||||
///
|
||||
/// XXX refine to a specific type: redjubjub signature?
|
||||
/// XXX for now it's [u64; 8] instead of [u8; 64] to get trait impls
|
||||
pub spend_auth_sig: [u64; 8],
|
||||
}
|
||||
|
||||
/// A _Output Description_, as described in [protocol specification §7.4][ps].
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#outputencoding
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct OutputDescription {
|
||||
/// A value commitment to the value of the input note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub cv: [u8; 32],
|
||||
/// The u-coordinate of the note commitment for the output note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub cmu: [u8; 32],
|
||||
/// An encoding of an ephemeral Jubjub public key.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
pub ephemeral_key: [u8; 32],
|
||||
/// A ciphertext component for the encrypted output note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
/// XXX this is a Vec<u8> rather than a [u8; 580] to get trait impls
|
||||
pub enc_ciphertext: Vec<u8>,
|
||||
/// A ciphertext component for the encrypted output note.
|
||||
///
|
||||
/// XXX refine to a specific type.
|
||||
/// XXX this is a [u64; 10] rather than a [u8; 80] to get trait impls
|
||||
pub out_ciphertext: [u64; 10],
|
||||
/// The ZK output proof.
|
||||
///
|
||||
/// XXX add proof types.
|
||||
/// XXX for now it's [u64; 24] instead of [u8; 192] to get trait impls
|
||||
pub zkproof: [u64; 24],
|
||||
}
|
||||
|
||||
/// Sapling-on-Groth16 spend and output descriptions.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ShieldedData {
|
||||
/// A sequence of [`SpendDescription`]s for this transaction.
|
||||
pub shielded_spends: Vec<SpendDescription>,
|
||||
/// A sequence of shielded outputs for this transaction.
|
||||
pub shielded_outputs: Vec<OutputDescription>,
|
||||
/// A signature on the transaction hash.
|
||||
// XXX refine this type to a RedJubjub signature.
|
||||
// for now it's [u64; 8] rather than [u8; 64] to get trait impls
|
||||
pub binding_sig: [u64; 8],
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//! Transaction types.
|
||||
|
||||
use crate::types::Script;
|
||||
|
||||
use super::TransactionHash;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// A transparent input to a transaction.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TransparentInput {
|
||||
/// The previous output transaction reference.
|
||||
pub previous_output: OutPoint,
|
||||
|
||||
/// Computational Script for confirming transaction authorization.
|
||||
pub signature_script: Script,
|
||||
|
||||
/// Transaction version as defined by the sender. Intended for
|
||||
/// "replacement" of transactions when information is updated
|
||||
/// before inclusion into a block.
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
/// A transparent output from a transaction.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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).
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct TransparentOutput {
|
||||
/// Transaction value.
|
||||
// At https://en.bitcoin.it/wiki/Protocol_documentation#tx, this is an i64.
|
||||
// XXX refine to Amount ?
|
||||
pub value: u64,
|
||||
|
||||
/// Usually contains the public key as a Bitcoin script setting up
|
||||
/// conditions to claim this output.
|
||||
pub pk_script: Script,
|
||||
}
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
//! Newtype wrappers for primitive data types with semantic meaning.
|
||||
|
||||
use std::{fmt, io};
|
||||
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use hex;
|
||||
use std::fmt;
|
||||
|
||||
use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
|
||||
|
||||
/// A 4-byte checksum using truncated double-SHA256 (two rounds of SHA256).
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
|
|
@ -30,6 +35,51 @@ impl fmt::Debug for Sha256dChecksum {
|
|||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct BlockHeight(pub u32);
|
||||
|
||||
/// A Bitcoin-style `locktime`, representing either a block height or an epoch
|
||||
/// time.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// Users should not construct a `LockTime` with `BlockHeight` greater than or
|
||||
/// equal to `500_000_000` or a timestamp before 4 November 1985 (Unix timestamp
|
||||
/// less than `500_000_000`).
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum LockTime {
|
||||
/// Unlock at a particular block height.
|
||||
Height(BlockHeight),
|
||||
/// Unlock at a particular time.
|
||||
Time(DateTime<Utc>),
|
||||
}
|
||||
|
||||
impl ZcashSerialize for LockTime {
|
||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
|
||||
// This implementation does not check the invariants on `LockTime` so that the
|
||||
// serialization is fallible only if the underlying writer is. This ensures that
|
||||
// we can always compute a hash of a transaction object.
|
||||
use LockTime::*;
|
||||
match self {
|
||||
Height(BlockHeight(n)) => writer.write_u32::<LittleEndian>(*n)?,
|
||||
Time(t) => writer.write_u32::<LittleEndian>(t.timestamp() as u32)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ZcashDeserialize for LockTime {
|
||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||
let n = reader.read_u32::<LittleEndian>()?;
|
||||
if n < 500_000_000 {
|
||||
Ok(LockTime::Height(BlockHeight(n)))
|
||||
} else {
|
||||
Ok(LockTime::Time(Utc.timestamp(n as i64, 0)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An encoding of a Bitcoin script.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Script(pub Vec<u8>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in New Issue