From 972d16518fefafeb0b8ecfb0b22e7ac0873d68a8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 5 Feb 2020 14:32:10 -0800 Subject: [PATCH] Make ZcashSerialize infallible mod its Writer. Closes #158. As discussed on the issue, this makes it possible to safely serialize data into hashes, and encourages serializable data to make illegal states unrepresentable. --- zebra-chain/src/block.rs | 8 ++++---- zebra-chain/src/equihash_solution.rs | 2 +- zebra-chain/src/merkle_tree.rs | 4 ++-- zebra-chain/src/note_commitment_tree.rs | 2 +- zebra-chain/src/proofs/bctv14.rs | 2 +- zebra-chain/src/proofs/groth16.rs | 2 +- zebra-chain/src/serialization.rs | 9 +++++++-- zebra-chain/src/transaction/joinsplit.rs | 2 +- zebra-chain/src/transaction/serialize.rs | 16 ++++++++-------- zebra-chain/src/transaction/shielded_data.rs | 4 ++-- zebra-chain/src/types.rs | 4 ++-- zebra-network/src/meta_addr.rs | 2 +- zebra-network/src/protocol/external/inv.rs | 2 +- 13 files changed, 32 insertions(+), 27 deletions(-) diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index a614bb37..7c6ffff7 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -48,13 +48,13 @@ impl From for BlockHeaderHash { let mut hash_writer = Sha256dWriter::default(); block_header .zcash_serialize(&mut hash_writer) - .expect("Block headers must serialize."); + .expect("Sha256dWriter is infallible"); Self(hash_writer.finish()) } } impl ZcashSerialize for BlockHeaderHash { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0)?; Ok(()) } @@ -117,7 +117,7 @@ pub struct BlockHeader { } impl ZcashSerialize for BlockHeader { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { // "The current and only defined block version number for Zcash is 4." writer.write_u32::(4)?; self.previous_block_hash.zcash_serialize(&mut writer)?; @@ -170,7 +170,7 @@ pub struct Block { } impl ZcashSerialize for Block { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { self.header.zcash_serialize(&mut writer)?; self.transactions.zcash_serialize(&mut writer)?; Ok(()) diff --git a/zebra-chain/src/equihash_solution.rs b/zebra-chain/src/equihash_solution.rs index 55ca8116..bf3a825e 100644 --- a/zebra-chain/src/equihash_solution.rs +++ b/zebra-chain/src/equihash_solution.rs @@ -51,7 +51,7 @@ impl Clone for EquihashSolution { impl Eq for EquihashSolution {} impl ZcashSerialize for EquihashSolution { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_compactsize(EQUIHASH_SOLUTION_SIZE as u64)?; writer.write_all(&self.0[..])?; Ok(()) diff --git a/zebra-chain/src/merkle_tree.rs b/zebra-chain/src/merkle_tree.rs index 1ed9b2f5..b9e2292a 100644 --- a/zebra-chain/src/merkle_tree.rs +++ b/zebra-chain/src/merkle_tree.rs @@ -18,7 +18,7 @@ pub struct MerkleTree { } impl ZcashSerialize for MerkleTree { - fn zcash_serialize(&self, _writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, _writer: W) -> Result<(), io::Error> { unimplemented!(); } } @@ -40,7 +40,7 @@ impl From> for MerkleTreeRootHash { let mut hash_writer = Sha256dWriter::default(); merkle_tree .zcash_serialize(&mut hash_writer) - .expect("The merkle tree of transactions must serialize."); + .expect("Sha256dWriter is infallible"); Self(hash_writer.finish()) } } diff --git a/zebra-chain/src/note_commitment_tree.rs b/zebra-chain/src/note_commitment_tree.rs index 32eb0bd2..fdecee26 100644 --- a/zebra-chain/src/note_commitment_tree.rs +++ b/zebra-chain/src/note_commitment_tree.rs @@ -59,7 +59,7 @@ impl SaplingNoteCommitmentTree { } impl ZcashSerialize for SaplingNoteCommitmentTree { - fn zcash_serialize(&self, _writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, _writer: W) -> Result<(), io::Error> { unimplemented!(); } } diff --git a/zebra-chain/src/proofs/bctv14.rs b/zebra-chain/src/proofs/bctv14.rs index dd5c99a7..52743454 100644 --- a/zebra-chain/src/proofs/bctv14.rs +++ b/zebra-chain/src/proofs/bctv14.rs @@ -34,7 +34,7 @@ impl PartialEq for Bctv14Proof { impl Eq for Bctv14Proof {} impl ZcashSerialize for Bctv14Proof { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } diff --git a/zebra-chain/src/proofs/groth16.rs b/zebra-chain/src/proofs/groth16.rs index 6fd9a4b9..7200f677 100644 --- a/zebra-chain/src/proofs/groth16.rs +++ b/zebra-chain/src/proofs/groth16.rs @@ -34,7 +34,7 @@ impl PartialEq for Groth16Proof { impl Eq for Groth16Proof {} impl ZcashSerialize for Groth16Proof { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } diff --git a/zebra-chain/src/serialization.rs b/zebra-chain/src/serialization.rs index 6d12767b..6caeb4d4 100644 --- a/zebra-chain/src/serialization.rs +++ b/zebra-chain/src/serialization.rs @@ -37,7 +37,12 @@ pub trait ZcashSerialize: Sized { /// This function has a `zcash_` prefix to alert the reader that the /// serialization in use is consensus-critical serialization, rather than /// some other kind of serialization. - fn zcash_serialize(&self, writer: W) -> Result<(), SerializationError>; + /// + /// Notice that the error type is [`std::io::Error`]; this indicates that + /// serialization MUST be infallible up to errors in the underlying writer. + /// In other words, any type implementing `ZcashSerialize` must make illegal + /// states unrepresentable. + fn zcash_serialize(&self, writer: W) -> Result<(), io::Error>; } /// Consensus-critical serialization for Zcash. @@ -56,7 +61,7 @@ pub trait ZcashDeserialize: Sized { } impl ZcashSerialize for Vec { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_compactsize(self.len() as u64)?; for x in self { x.zcash_serialize(&mut writer)?; diff --git a/zebra-chain/src/transaction/joinsplit.rs b/zebra-chain/src/transaction/joinsplit.rs index 9c3f741b..f399d02d 100644 --- a/zebra-chain/src/transaction/joinsplit.rs +++ b/zebra-chain/src/transaction/joinsplit.rs @@ -154,7 +154,7 @@ impl PartialEq for EncryptedCiphertext { impl Eq for EncryptedCiphertext {} impl ZcashSerialize for EncryptedCiphertext { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 12e1d5b1..89df7738 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -16,7 +16,7 @@ const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270; const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085; impl ZcashSerialize for OutPoint { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.hash.0[..])?; writer.write_u32::(self.index)?; Ok(()) @@ -33,7 +33,7 @@ impl ZcashDeserialize for OutPoint { } impl ZcashSerialize for TransparentInput { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { self.previous_output.zcash_serialize(&mut writer)?; self.signature_script.zcash_serialize(&mut writer)?; writer.write_u32::(self.sequence)?; @@ -52,7 +52,7 @@ impl ZcashDeserialize for TransparentInput { } impl ZcashSerialize for TransparentOutput { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_u64::(self.value)?; self.pk_script.zcash_serialize(&mut writer)?; Ok(()) @@ -69,7 +69,7 @@ impl ZcashDeserialize for TransparentOutput { } impl ZcashSerialize for JoinSplit

{ - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_u64::(self.vpub_old)?; writer.write_u64::(self.vpub_new)?; writer.write_all(&self.anchor[..])?; @@ -109,7 +109,7 @@ impl ZcashDeserialize for JoinSplit

{ } impl ZcashSerialize for JoinSplitData

{ - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_compactsize(self.joinsplits().count() as u64)?; for joinsplit in self.joinsplits() { joinsplit.zcash_serialize(&mut writer)?; @@ -145,7 +145,7 @@ impl ZcashDeserialize for Option> { } impl ZcashSerialize for SpendDescription { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.cv[..])?; writer.write_all(&self.anchor.0[..])?; writer.write_all(&self.nullifier[..])?; @@ -171,7 +171,7 @@ impl ZcashDeserialize for SpendDescription { } impl ZcashSerialize for OutputDescription { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.cv[..])?; writer.write_all(&self.cmu[..])?; writer.write_all(&self.ephemeral_key[..])?; @@ -196,7 +196,7 @@ impl ZcashDeserialize for OutputDescription { } impl ZcashSerialize for Transaction { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { Transaction::V1 { inputs, diff --git a/zebra-chain/src/transaction/shielded_data.rs b/zebra-chain/src/transaction/shielded_data.rs index 54f9350c..b3397cfe 100644 --- a/zebra-chain/src/transaction/shielded_data.rs +++ b/zebra-chain/src/transaction/shielded_data.rs @@ -237,7 +237,7 @@ impl PartialEq for EncryptedCiphertext { impl Eq for EncryptedCiphertext {} impl ZcashSerialize for EncryptedCiphertext { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } @@ -300,7 +300,7 @@ impl PartialEq for OutCiphertext { impl Eq for OutCiphertext {} impl ZcashSerialize for OutCiphertext { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } diff --git a/zebra-chain/src/types.rs b/zebra-chain/src/types.rs index 185e54e9..f746c188 100644 --- a/zebra-chain/src/types.rs +++ b/zebra-chain/src/types.rs @@ -60,7 +60,7 @@ pub enum LockTime { } impl ZcashSerialize for LockTime { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { // 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. @@ -106,7 +106,7 @@ impl Arbitrary for LockTime { pub struct Script(pub Vec); impl ZcashSerialize for Script { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_compactsize(self.0.len() as u64)?; writer.write_all(&self.0[..])?; Ok(()) diff --git a/zebra-network/src/meta_addr.rs b/zebra-network/src/meta_addr.rs index bc8784b9..867d20a1 100644 --- a/zebra-network/src/meta_addr.rs +++ b/zebra-network/src/meta_addr.rs @@ -67,7 +67,7 @@ impl PartialOrd for MetaAddr { } impl ZcashSerialize for MetaAddr { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), std::io::Error> { writer.write_u32::(self.last_seen.timestamp() as u32)?; writer.write_u64::(self.services.bits())?; writer.write_socket_addr(self.addr)?; diff --git a/zebra-network/src/protocol/external/inv.rs b/zebra-network/src/protocol/external/inv.rs index f22389ba..3bf7105d 100644 --- a/zebra-network/src/protocol/external/inv.rs +++ b/zebra-network/src/protocol/external/inv.rs @@ -55,7 +55,7 @@ impl From for InventoryHash { } impl ZcashSerialize for InventoryHash { - fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + fn zcash_serialize(&self, mut writer: W) -> Result<(), std::io::Error> { let (code, bytes) = match *self { InventoryHash::Error => (0, [0; 32]), InventoryHash::Tx(hash) => (1, hash.0),