docs: document consensus rules from 4.3 JoinSplit Descriptions (#3452)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
3bbb8f0531
commit
e9f1aa60ea
|
@ -121,25 +121,50 @@ impl<P: ZkSnarkProof> JoinSplit<P> {
|
|||
|
||||
impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<P> {
|
||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||
// # Consensus
|
||||
//
|
||||
// > Elements of a JoinSplit description MUST have the types given above
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
//
|
||||
// See comments below for each specific type.
|
||||
Ok(JoinSplit::<P> {
|
||||
// Type is `{0 .. MAX_MONEY}`; see [`NonNegative::valid_range()`].
|
||||
vpub_old: (&mut reader).zcash_deserialize_into()?,
|
||||
vpub_new: (&mut reader).zcash_deserialize_into()?,
|
||||
// Type is `B^{ℓ^{Sprout}_{Merkle}}` i.e. 32 bytes.
|
||||
anchor: tree::Root::from(reader.read_32_bytes()?),
|
||||
// Types are `B^{ℓ^{Sprout}_{PRF}}` i.e. 32 bytes.
|
||||
nullifiers: [
|
||||
reader.read_32_bytes()?.into(),
|
||||
reader.read_32_bytes()?.into(),
|
||||
],
|
||||
// Types are `NoteCommit^{Sprout}.Output`, i.e. `B^{ℓ^{Sprout}_{Merkle}}`,
|
||||
// i.e. 32 bytes. https://zips.z.cash/protocol/protocol.pdf#abstractcommit
|
||||
commitments: [
|
||||
commitment::NoteCommitment::from(reader.read_32_bytes()?),
|
||||
commitment::NoteCommitment::from(reader.read_32_bytes()?),
|
||||
],
|
||||
// Type is `KA^{Sprout}.Public`, i.e. `B^Y^{[32]}`, i.e. 32 bytes.
|
||||
// https://zips.z.cash/protocol/protocol.pdf#concretesproutkeyagreement
|
||||
ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?),
|
||||
// Type is `B^{[ℓ_{Seed}]}`, i.e. 32 bytes
|
||||
random_seed: RandomSeed::from(reader.read_32_bytes()?),
|
||||
// Types are `B^{ℓ^{Sprout}_{PRF}}` i.e. 32 bytes.
|
||||
// See [`note::Mac::zcash_deserialize`].
|
||||
vmacs: [
|
||||
note::Mac::zcash_deserialize(&mut reader)?,
|
||||
note::Mac::zcash_deserialize(&mut reader)?,
|
||||
],
|
||||
// Type is 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: P::zcash_deserialize(&mut reader)?,
|
||||
// Types are `Sym.C`, i.e. `B^Y^{[N]}`, i.e. arbitrary-sized byte arrays
|
||||
// https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to
|
||||
// 601 bytes in https://zips.z.cash/protocol/protocol.pdf#joinsplitencodingandconsensus
|
||||
// See [`note::EncryptedNote::zcash_deserialize`].
|
||||
enc_ciphertexts: [
|
||||
note::EncryptedNote::zcash_deserialize(&mut reader)?,
|
||||
note::EncryptedNote::zcash_deserialize(&mut reader)?,
|
||||
|
|
|
@ -314,6 +314,14 @@ where
|
|||
type Error = TransactionError;
|
||||
|
||||
fn try_from(input: DescriptionWrapper<&T>) -> Result<Self, Self::Error> {
|
||||
// # Consensus
|
||||
//
|
||||
// > Elements of a JoinSplit description MUST have the types given above
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
//
|
||||
// This validates the 𝜋_{ZKJoinSplit} element. In #3179 we plan to validate
|
||||
// during deserialization, see [`JoinSplit::zcash_deserialize`].
|
||||
Ok(Item::from((
|
||||
bellman::groth16::Proof::read(&input.0.proof().0[..])
|
||||
.map_err(|e| TransactionError::MalformedGroth16(e.to_string()))?,
|
||||
|
|
|
@ -714,16 +714,18 @@ where
|
|||
|
||||
if let Some(joinsplit_data) = joinsplit_data {
|
||||
for joinsplit in joinsplit_data.joinsplits() {
|
||||
// Consensus rule: The proof π_ZKSpend MUST be valid given a
|
||||
// primary input formed from the relevant other fields and h_{Sig}
|
||||
// # Consensus
|
||||
//
|
||||
// > The proof π_ZKJoinSplit MUST be valid given a
|
||||
// > primary input formed from the relevant other fields and h_{Sig}
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
//
|
||||
// Queue the verification of the Groth16 spend proof
|
||||
// for each JoinSplit description while adding the
|
||||
// resulting future to our collection of async
|
||||
// checks that (at a minimum) must pass for the
|
||||
// transaction to verify.
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
checks.push(primitives::groth16::JOINSPLIT_VERIFIER.oneshot(
|
||||
DescriptionWrapper(&(joinsplit, &joinsplit_data.pub_key)).try_into()?,
|
||||
));
|
||||
|
|
|
@ -144,7 +144,10 @@ pub fn joinsplit_has_vpub_zero(tx: &Transaction) -> Result<(), TransactionError>
|
|||
.output_values_to_sprout()
|
||||
.zip(tx.input_values_from_sprout());
|
||||
for (vpub_old, vpub_new) in vpub_pairs {
|
||||
// # Consensus
|
||||
//
|
||||
// > Either v_{pub}^{old} or v_{pub}^{new} MUST be zero.
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
if *vpub_old != zero && *vpub_new != zero {
|
||||
return Err(TransactionError::BothVPubsNonZero);
|
||||
|
@ -168,7 +171,10 @@ pub fn disabled_add_to_sprout_pool(
|
|||
.activation_height(network)
|
||||
.expect("Canopy activation height must be present for both networks");
|
||||
|
||||
// [Canopy onward]: `vpub_old` MUST be zero.
|
||||
// # Consensus
|
||||
//
|
||||
// > [Canopy onward]: `vpub_old` MUST be zero.
|
||||
//
|
||||
// https://zips.z.cash/protocol/protocol.pdf#joinsplitdesc
|
||||
if height >= canopy_activation_height {
|
||||
let zero = Amount::<NonNegative>::try_from(0).expect("an amount of 0 is always valid");
|
||||
|
|
Loading…
Reference in New Issue