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();
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<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)?;
Ok(())
}
@ -117,7 +117,7 @@ pub struct 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."
writer.write_u32::<LittleEndian>(4)?;
self.previous_block_hash.zcash_serialize(&mut writer)?;
@ -170,7 +170,7 @@ pub struct 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.transactions.zcash_serialize(&mut writer)?;
Ok(())

View File

@ -51,7 +51,7 @@ impl Clone for EquihashSolution {
impl Eq 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_all(&self.0[..])?;
Ok(())

View File

@ -18,7 +18,7 @@ pub struct MerkleTree<T> {
}
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!();
}
}
@ -40,7 +40,7 @@ impl From<MerkleTree<Transaction>> 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())
}
}

View File

@ -59,7 +59,7 @@ impl 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!();
}
}

View File

@ -34,7 +34,7 @@ impl PartialEq for Bctv14Proof {
impl Eq 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[..])?;
Ok(())
}

View File

@ -34,7 +34,7 @@ impl PartialEq for Groth16Proof {
impl Eq 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[..])?;
Ok(())
}

View File

@ -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<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.
@ -56,7 +61,7 @@ pub trait ZcashDeserialize: Sized {
}
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)?;
for x in self {
x.zcash_serialize(&mut writer)?;

View File

@ -154,7 +154,7 @@ impl PartialEq for EncryptedCiphertext {
impl Eq 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[..])?;
Ok(())
}

View File

@ -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<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_u32::<LittleEndian>(self.index)?;
Ok(())
@ -33,7 +33,7 @@ impl ZcashDeserialize for OutPoint {
}
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.signature_script.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(self.sequence)?;
@ -52,7 +52,7 @@ impl ZcashDeserialize for TransparentInput {
}
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)?;
self.pk_script.zcash_serialize(&mut writer)?;
Ok(())
@ -69,7 +69,7 @@ impl ZcashDeserialize for TransparentOutput {
}
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_new)?;
writer.write_all(&self.anchor[..])?;
@ -109,7 +109,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<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)?;
for joinsplit in self.joinsplits() {
joinsplit.zcash_serialize(&mut writer)?;
@ -145,7 +145,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> {
}
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.anchor.0[..])?;
writer.write_all(&self.nullifier[..])?;
@ -171,7 +171,7 @@ impl ZcashDeserialize for SpendDescription {
}
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.cmu[..])?;
writer.write_all(&self.ephemeral_key[..])?;
@ -196,7 +196,7 @@ impl ZcashDeserialize for OutputDescription {
}
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 {
Transaction::V1 {
inputs,

View File

@ -237,7 +237,7 @@ impl PartialEq for EncryptedCiphertext {
impl Eq 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[..])?;
Ok(())
}
@ -300,7 +300,7 @@ impl PartialEq for OutCiphertext {
impl Eq 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[..])?;
Ok(())
}

View File

@ -60,7 +60,7 @@ pub enum 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
// 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<u8>);
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_all(&self.0[..])?;
Ok(())

View File

@ -67,7 +67,7 @@ impl PartialOrd 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_u64::<LittleEndian>(self.services.bits())?;
writer.write_socket_addr(self.addr)?;

View File

@ -55,7 +55,7 @@ impl From<BlockHeaderHash> 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 {
InventoryHash::Error => (0, [0; 32]),
InventoryHash::Tx(hash) => (1, hash.0),