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.
This commit is contained in:
Henry de Valence 2020-02-05 14:32:10 -08:00 committed by Deirdre Connolly
parent b0f61c4dd2
commit 972d16518f
13 changed files with 32 additions and 27 deletions

View File

@ -48,13 +48,13 @@ impl From<BlockHeader> for BlockHeaderHash {
let mut hash_writer = Sha256dWriter::default(); let mut hash_writer = Sha256dWriter::default();
block_header block_header
.zcash_serialize(&mut hash_writer) .zcash_serialize(&mut hash_writer)
.expect("Block headers must serialize."); .expect("Sha256dWriter is infallible");
Self(hash_writer.finish()) Self(hash_writer.finish())
} }
} }
impl ZcashSerialize for BlockHeaderHash { impl ZcashSerialize for BlockHeaderHash {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0)?; writer.write_all(&self.0)?;
Ok(()) Ok(())
} }
@ -117,7 +117,7 @@ pub struct BlockHeader {
} }
impl ZcashSerialize for BlockHeader { impl ZcashSerialize for BlockHeader {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
// "The current and only defined block version number for Zcash is 4." // "The current and only defined block version number for Zcash is 4."
writer.write_u32::<LittleEndian>(4)?; writer.write_u32::<LittleEndian>(4)?;
self.previous_block_hash.zcash_serialize(&mut writer)?; self.previous_block_hash.zcash_serialize(&mut writer)?;
@ -170,7 +170,7 @@ pub struct Block {
} }
impl ZcashSerialize for Block { impl ZcashSerialize for Block {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.header.zcash_serialize(&mut writer)?; self.header.zcash_serialize(&mut writer)?;
self.transactions.zcash_serialize(&mut writer)?; self.transactions.zcash_serialize(&mut writer)?;
Ok(()) Ok(())

View File

@ -51,7 +51,7 @@ impl Clone for EquihashSolution {
impl Eq for EquihashSolution {} impl Eq for EquihashSolution {}
impl ZcashSerialize for EquihashSolution { impl ZcashSerialize for EquihashSolution {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_compactsize(EQUIHASH_SOLUTION_SIZE as u64)?; writer.write_compactsize(EQUIHASH_SOLUTION_SIZE as u64)?;
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())

View File

@ -18,7 +18,7 @@ pub struct MerkleTree<T> {
} }
impl<Transaction> ZcashSerialize for MerkleTree<Transaction> { impl<Transaction> ZcashSerialize for MerkleTree<Transaction> {
fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), io::Error> {
unimplemented!(); unimplemented!();
} }
} }
@ -40,7 +40,7 @@ impl From<MerkleTree<Transaction>> for MerkleTreeRootHash {
let mut hash_writer = Sha256dWriter::default(); let mut hash_writer = Sha256dWriter::default();
merkle_tree merkle_tree
.zcash_serialize(&mut hash_writer) .zcash_serialize(&mut hash_writer)
.expect("The merkle tree of transactions must serialize."); .expect("Sha256dWriter is infallible");
Self(hash_writer.finish()) Self(hash_writer.finish())
} }
} }

View File

@ -59,7 +59,7 @@ impl SaplingNoteCommitmentTree {
} }
impl ZcashSerialize for SaplingNoteCommitmentTree { impl ZcashSerialize for SaplingNoteCommitmentTree {
fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), io::Error> {
unimplemented!(); unimplemented!();
} }
} }

View File

@ -34,7 +34,7 @@ impl PartialEq for Bctv14Proof {
impl Eq for Bctv14Proof {} impl Eq for Bctv14Proof {}
impl ZcashSerialize for Bctv14Proof { impl ZcashSerialize for Bctv14Proof {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())
} }

View File

@ -34,7 +34,7 @@ impl PartialEq for Groth16Proof {
impl Eq for Groth16Proof {} impl Eq for Groth16Proof {}
impl ZcashSerialize for Groth16Proof { impl ZcashSerialize for Groth16Proof {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())
} }

View File

@ -37,7 +37,12 @@ pub trait ZcashSerialize: Sized {
/// This function has a `zcash_` prefix to alert the reader that the /// This function has a `zcash_` prefix to alert the reader that the
/// serialization in use is consensus-critical serialization, rather than /// serialization in use is consensus-critical serialization, rather than
/// some other kind of serialization. /// some other kind of serialization.
fn zcash_serialize<W: io::Write>(&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<W: io::Write>(&self, writer: W) -> Result<(), io::Error>;
} }
/// Consensus-critical serialization for Zcash. /// Consensus-critical serialization for Zcash.
@ -56,7 +61,7 @@ pub trait ZcashDeserialize: Sized {
} }
impl<T: ZcashSerialize> ZcashSerialize for Vec<T> { impl<T: ZcashSerialize> ZcashSerialize for Vec<T> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_compactsize(self.len() as u64)?; writer.write_compactsize(self.len() as u64)?;
for x in self { for x in self {
x.zcash_serialize(&mut writer)?; x.zcash_serialize(&mut writer)?;

View File

@ -154,7 +154,7 @@ impl PartialEq for EncryptedCiphertext {
impl Eq for EncryptedCiphertext {} impl Eq for EncryptedCiphertext {}
impl ZcashSerialize for EncryptedCiphertext { impl ZcashSerialize for EncryptedCiphertext {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())
} }

View File

@ -16,7 +16,7 @@ const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085; const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;
impl ZcashSerialize for OutPoint { impl ZcashSerialize for OutPoint {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.hash.0[..])?; writer.write_all(&self.hash.0[..])?;
writer.write_u32::<LittleEndian>(self.index)?; writer.write_u32::<LittleEndian>(self.index)?;
Ok(()) Ok(())
@ -33,7 +33,7 @@ impl ZcashDeserialize for OutPoint {
} }
impl ZcashSerialize for TransparentInput { impl ZcashSerialize for TransparentInput {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.previous_output.zcash_serialize(&mut writer)?; self.previous_output.zcash_serialize(&mut writer)?;
self.signature_script.zcash_serialize(&mut writer)?; self.signature_script.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(self.sequence)?; writer.write_u32::<LittleEndian>(self.sequence)?;
@ -52,7 +52,7 @@ impl ZcashDeserialize for TransparentInput {
} }
impl ZcashSerialize for TransparentOutput { impl ZcashSerialize for TransparentOutput {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u64::<LittleEndian>(self.value)?; writer.write_u64::<LittleEndian>(self.value)?;
self.pk_script.zcash_serialize(&mut writer)?; self.pk_script.zcash_serialize(&mut writer)?;
Ok(()) Ok(())
@ -69,7 +69,7 @@ impl ZcashDeserialize for TransparentOutput {
} }
impl<P: ZkSnarkProof> ZcashSerialize for JoinSplit<P> { impl<P: ZkSnarkProof> ZcashSerialize for JoinSplit<P> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u64::<LittleEndian>(self.vpub_old)?; writer.write_u64::<LittleEndian>(self.vpub_old)?;
writer.write_u64::<LittleEndian>(self.vpub_new)?; writer.write_u64::<LittleEndian>(self.vpub_new)?;
writer.write_all(&self.anchor[..])?; writer.write_all(&self.anchor[..])?;
@ -109,7 +109,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<P> {
} }
impl<P: ZkSnarkProof> ZcashSerialize for JoinSplitData<P> { impl<P: ZkSnarkProof> ZcashSerialize for JoinSplitData<P> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_compactsize(self.joinsplits().count() as u64)?; writer.write_compactsize(self.joinsplits().count() as u64)?;
for joinsplit in self.joinsplits() { for joinsplit in self.joinsplits() {
joinsplit.zcash_serialize(&mut writer)?; joinsplit.zcash_serialize(&mut writer)?;
@ -145,7 +145,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> {
} }
impl ZcashSerialize for SpendDescription { impl ZcashSerialize for SpendDescription {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv[..])?; writer.write_all(&self.cv[..])?;
writer.write_all(&self.anchor.0[..])?; writer.write_all(&self.anchor.0[..])?;
writer.write_all(&self.nullifier[..])?; writer.write_all(&self.nullifier[..])?;
@ -171,7 +171,7 @@ impl ZcashDeserialize for SpendDescription {
} }
impl ZcashSerialize for OutputDescription { impl ZcashSerialize for OutputDescription {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv[..])?; writer.write_all(&self.cv[..])?;
writer.write_all(&self.cmu[..])?; writer.write_all(&self.cmu[..])?;
writer.write_all(&self.ephemeral_key[..])?; writer.write_all(&self.ephemeral_key[..])?;
@ -196,7 +196,7 @@ impl ZcashDeserialize for OutputDescription {
} }
impl ZcashSerialize for Transaction { impl ZcashSerialize for Transaction {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
match self { match self {
Transaction::V1 { Transaction::V1 {
inputs, inputs,

View File

@ -237,7 +237,7 @@ impl PartialEq for EncryptedCiphertext {
impl Eq for EncryptedCiphertext {} impl Eq for EncryptedCiphertext {}
impl ZcashSerialize for EncryptedCiphertext { impl ZcashSerialize for EncryptedCiphertext {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())
} }
@ -300,7 +300,7 @@ impl PartialEq for OutCiphertext {
impl Eq for OutCiphertext {} impl Eq for OutCiphertext {}
impl ZcashSerialize for OutCiphertext { impl ZcashSerialize for OutCiphertext {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())
} }

View File

@ -60,7 +60,7 @@ pub enum LockTime {
} }
impl ZcashSerialize for LockTime { impl ZcashSerialize for LockTime {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
// This implementation does not check the invariants on `LockTime` so that the // This implementation does not check the invariants on `LockTime` so that the
// serialization is fallible only if the underlying writer is. This ensures that // serialization is fallible only if the underlying writer is. This ensures that
// we can always compute a hash of a transaction object. // we can always compute a hash of a transaction object.
@ -106,7 +106,7 @@ impl Arbitrary for LockTime {
pub struct Script(pub Vec<u8>); pub struct Script(pub Vec<u8>);
impl ZcashSerialize for Script { impl ZcashSerialize for Script {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_compactsize(self.0.len() as u64)?; writer.write_compactsize(self.0.len() as u64)?;
writer.write_all(&self.0[..])?; writer.write_all(&self.0[..])?;
Ok(()) Ok(())

View File

@ -67,7 +67,7 @@ impl PartialOrd for MetaAddr {
} }
impl ZcashSerialize for MetaAddr { impl ZcashSerialize for MetaAddr {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
writer.write_u32::<LittleEndian>(self.last_seen.timestamp() as u32)?; writer.write_u32::<LittleEndian>(self.last_seen.timestamp() as u32)?;
writer.write_u64::<LittleEndian>(self.services.bits())?; writer.write_u64::<LittleEndian>(self.services.bits())?;
writer.write_socket_addr(self.addr)?; writer.write_socket_addr(self.addr)?;

View File

@ -55,7 +55,7 @@ impl From<BlockHeaderHash> for InventoryHash {
} }
impl ZcashSerialize for InventoryHash { impl ZcashSerialize for InventoryHash {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> { fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
let (code, bytes) = match *self { let (code, bytes) = match *self {
InventoryHash::Error => (0, [0; 32]), InventoryHash::Error => (0, [0; 32]),
InventoryHash::Tx(hash) => (1, hash.0), InventoryHash::Tx(hash) => (1, hash.0),