From 3bbb8f0531da979205bd428e20268e728c2d75a0 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Tue, 8 Feb 2022 06:56:59 -0300 Subject: [PATCH] docs: document consensus rules from 4.4 Spend Descriptions (#3460) --- zebra-chain/src/sapling/commitment.rs | 2 ++ zebra-chain/src/sapling/keys.rs | 2 ++ zebra-chain/src/sapling/spend.rs | 37 ++++++++++++++++++++++++ zebra-chain/src/transaction/serialize.rs | 32 ++++++++++++++++++++ zebra-consensus/src/transaction.rs | 30 +++++++++++++++---- 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/zebra-chain/src/sapling/commitment.rs b/zebra-chain/src/sapling/commitment.rs index 1952a5228..1d62136db 100644 --- a/zebra-chain/src/sapling/commitment.rs +++ b/zebra-chain/src/sapling/commitment.rs @@ -314,6 +314,8 @@ impl TryFrom for NotSmallOrderValueCommitment { /// /// Returns an error if the point is of small order. /// + /// # Consensus + /// /// > cv and rk [MUST NOT be of small order][1], i.e. [h_J]cv MUST NOT be π’ͺ_J /// > and [h_J]rk MUST NOT be π’ͺ_J. /// diff --git a/zebra-chain/src/sapling/keys.rs b/zebra-chain/src/sapling/keys.rs index e8808f113..a049f99b1 100644 --- a/zebra-chain/src/sapling/keys.rs +++ b/zebra-chain/src/sapling/keys.rs @@ -1101,6 +1101,8 @@ impl TryFrom> for ValidatingKey { /// /// Returns an error if the key is malformed or [is of small order][1]. /// + /// # Consensus + /// /// > Check that a Spend description's cv and rk are not of small order, /// > i.e. [h_J]cv MUST NOT be π’ͺ_J and [h_J]rk MUST NOT be π’ͺ_J. /// diff --git a/zebra-chain/src/sapling/spend.rs b/zebra-chain/src/sapling/spend.rs index 578619d23..391a47792 100644 --- a/zebra-chain/src/sapling/spend.rs +++ b/zebra-chain/src/sapling/spend.rs @@ -186,15 +186,38 @@ impl ZcashDeserialize for Spend { /// /// The "anchor encoding for v4 transactions" is implemented here. fn zcash_deserialize(mut reader: R) -> Result { + // # Consensus + // + // > Elements of a Spend description MUST be valid encodings of the types given above. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc + // + // See comments below for each specific type. Ok(Spend { + // Type is `ValueCommit^{Sapling}.Output`, i.e. J + // https://zips.z.cash/protocol/protocol.pdf#abstractcommit + // See [`commitment::NotSmallOrderValueCommitment::zcash_deserialize`]. cv: commitment::NotSmallOrderValueCommitment::zcash_deserialize(&mut reader)?, + // Type is `B^{[β„“_{Sapling}_{Merkle}]}`, i.e. 32 bytes per_spend_anchor: tree::Root(reader.read_32_bytes()?), + // Type is `B^Y^{[β„“_{PRFnfSapling}/8]}`, i.e. 32 bytes nullifier: note::Nullifier::from(reader.read_32_bytes()?), + // Type is `SpendAuthSig^{Sapling}.Public`, i.e. J + // https://zips.z.cash/protocol/protocol.pdf#concretereddsa + // See [`ValidatingKey::try_from`]. rk: reader .read_32_bytes()? .try_into() .map_err(SerializationError::Parse)?, + // Type is `ZKSpend.Proof`, described in + // https://zips.z.cash/protocol/protocol.pdf#grothencoding + // It is not enforced here; this just reads 192 bytes. + // The type is validated when validating the proof, see + // [`groth16::Item::try_from`]. In #3179 we plan to validate here instead. zkproof: Groth16Proof::zcash_deserialize(&mut reader)?, + // Type is SpendAuthSig^{Sapling}.Signature, i.e. + // B^Y^{[ceiling(β„“_G/8) + ceiling(bitlength(π‘Ÿ_G)/8)]} i.e. 64 bytes + // https://zips.z.cash/protocol/protocol.pdf#concretereddsa spend_auth_sig: reader.read_64_bytes()?.into(), }) } @@ -217,9 +240,23 @@ impl ZcashSerialize for SpendPrefixInTransactionV5 { impl ZcashDeserialize for SpendPrefixInTransactionV5 { fn zcash_deserialize(mut reader: R) -> Result { + // # Consensus + // + // > Elements of a Spend description MUST be valid encodings of the types given above. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc + // + // See comments below for each specific type. Ok(SpendPrefixInTransactionV5 { + // Type is `ValueCommit^{Sapling}.Output`, i.e. J + // https://zips.z.cash/protocol/protocol.pdf#abstractcommit + // See [`commitment::NotSmallOrderValueCommitment::zcash_deserialize`]. cv: commitment::NotSmallOrderValueCommitment::zcash_deserialize(&mut reader)?, + // Type is `B^Y^{[β„“_{PRFnfSapling}/8]}`, i.e. 32 bytes nullifier: note::Nullifier::from(reader.read_32_bytes()?), + // Type is `SpendAuthSig^{Sapling}.Public`, i.e. J + // https://zips.z.cash/protocol/protocol.pdf#concretereddsa + // See [`ValidatingKey::try_from`]. rk: reader .read_32_bytes()? .try_into() diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 9997c2cac..27fd0f2f0 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -191,6 +191,14 @@ impl ZcashDeserialize for Option> { let value_balance = (&mut reader).zcash_deserialize_into()?; // anchorSapling + // + // # Consensus + // + // > Elements of a Spend description MUST be valid encodings of the types given above. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc + // + // Type is `B^{[β„“_{Sapling}_{Merkle}]}`, i.e. 32 bytes let shared_anchor = if spends_count > 0 { Some(reader.read_32_bytes()?.into()) } else { @@ -198,8 +206,32 @@ impl ZcashDeserialize for Option> { }; // vSpendProofsSapling + // + // # Consensus + // + // > Elements of a Spend description MUST be valid encodings of the types given above. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc + // + // Type is `ZKSpend.Proof`, described in + // https://zips.z.cash/protocol/protocol.pdf#grothencoding + // It is not enforced here; this just reads 192 bytes. + // The type is validated when validating the proof, see + // [`groth16::Item::try_from`]. In #3179 we plan to validate here instead. let spend_proofs = zcash_deserialize_external_count(spends_count, &mut reader)?; + // vSpendAuthSigsSapling + // + // # Consensus + // + // > Elements of a Spend description MUST be valid encodings of the types given above. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc + // + // Type is SpendAuthSig^{Sapling}.Signature, i.e. + // B^Y^{[ceiling(β„“_G/8) + ceiling(bitlength(π‘Ÿ_G)/8)]} i.e. 64 bytes + // https://zips.z.cash/protocol/protocol.pdf#concretereddsa + // See [`redjubjub::Signature::zcash_deserialize`]. let spend_sigs = zcash_deserialize_external_count(spends_count, &mut reader)?; // vOutputProofsSapling diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 69e95cea2..48f7480d1 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -763,9 +763,13 @@ where if let Some(sapling_shielded_data) = sapling_shielded_data { for spend in sapling_shielded_data.spends_per_anchor() { - // Consensus rule: The proof Ο€_ZKSpend MUST be valid - // given a primary input formed from the other - // fields except spendAuthSig. + // # Consensus + // + // > The proof Ο€_ZKSpend MUST be valid + // > given a primary input formed from the other + // > fields except spendAuthSig. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc // // Queue the verification of the Groth16 spend proof // for each Spend description while adding the @@ -778,9 +782,23 @@ where .oneshot(DescriptionWrapper(&spend).try_into()?), ); - // Consensus rule: The spend authorization signature - // MUST be a valid SpendAuthSig signature over - // SigHash using rk as the validating key. + // # Consensus + // + // > The spend authorization signature + // > MUST be a valid SpendAuthSig signature over + // > SigHash using rk as the validating key. + // + // This is validated by the verifier. + // + // > [NU5 onward] As specified in Β§ 5.4.7 β€˜RedDSA, RedJubjub, + // > and RedPallas’ on p. 88, the validation of the 𝑅 + // > component of the signature changes to prohibit non-canonical encodings. + // + // This is validated by the verifier, inside the `redjubjub` crate. + // It calls [`jubjub::AffinePoint::from_bytes`] to parse R and + // that enforces the canonical encoding. + // + // https://zips.z.cash/protocol/protocol.pdf#spenddesc // // Queue the validation of the RedJubjub spend // authorization signature for each Spend