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:
Henry de Valence 2019-12-05 12:56:58 -08:00 committed by Deirdre Connolly
parent 82e246d87b
commit c013895cd7
12 changed files with 525 additions and 151 deletions

View File

@ -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
zebra-chain/src/proofs.rs Normal file
View File

@ -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 {}
}

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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>>,
},
}

View File

@ -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\")"
);
}
}

View File

@ -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],
}

View File

@ -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!();
}
}

View File

@ -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],
}

View File

@ -0,0 +1 @@

View File

@ -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,
}

View File

@ -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::*;