Document consensus rules from 4.6 Action Descriptions (#3549)
* docs: document consensus rules from 4.6 Action Descriptions * Apply suggestions from code review Co-authored-by: Marek <mail@marek.onl> * Apply suggestions from code review Co-authored-by: Marek <mail@marek.onl> Co-authored-by: Marek <mail@marek.onl>
This commit is contained in:
parent
1896943f62
commit
b7b62af2f2
|
|
@ -59,13 +59,47 @@ impl ZcashSerialize for Action {
|
||||||
|
|
||||||
impl ZcashDeserialize for Action {
|
impl ZcashDeserialize for Action {
|
||||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||||
|
// # Consensus
|
||||||
|
//
|
||||||
|
// > Elements of an Action description MUST be canonical encodings of the types given above.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
|
//
|
||||||
|
// > LEOS2IP_{256}(cmx) MUST be less than 𝑞_ℙ.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus
|
||||||
|
//
|
||||||
|
// See comments below for each specific type.
|
||||||
Ok(Action {
|
Ok(Action {
|
||||||
|
// Type is ValueCommit^{Orchard}.Output, i.e. ℙ.
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#abstractcommit
|
||||||
|
// See [`ValueCommitment::zcash_deserialize`].
|
||||||
cv: ValueCommitment::zcash_deserialize(&mut reader)?,
|
cv: ValueCommitment::zcash_deserialize(&mut reader)?,
|
||||||
|
// Type is `{0 .. 𝑞_ℙ − 1}`. See [`Nullifier::try_from`].
|
||||||
nullifier: Nullifier::try_from(reader.read_32_bytes()?)?,
|
nullifier: Nullifier::try_from(reader.read_32_bytes()?)?,
|
||||||
|
// Type is SpendAuthSig^{Orchard}.Public, i.e. ℙ.
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#concretespendauthsig
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#concretereddsa
|
||||||
|
// This only reads the 32-byte buffer. The type is enforced
|
||||||
|
// on signature verification; see [`redpallas::batch`]
|
||||||
rk: reader.read_32_bytes()?.into(),
|
rk: reader.read_32_bytes()?.into(),
|
||||||
|
// Type is `{0 .. 𝑞_ℙ − 1}`. Note that the second rule quoted above
|
||||||
|
// is also enforced here and it is technically redundant with the first.
|
||||||
|
// See [`pallas::Base::zcash_deserialize`].
|
||||||
cm_x: pallas::Base::zcash_deserialize(&mut reader)?,
|
cm_x: pallas::Base::zcash_deserialize(&mut reader)?,
|
||||||
|
// Denoted by `epk` in the spec. Type is KA^{Orchard}.Public, i.e. ℙ^*.
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
|
||||||
|
// See [`keys::EphemeralPublicKey::zcash_deserialize`].
|
||||||
ephemeral_key: keys::EphemeralPublicKey::zcash_deserialize(&mut reader)?,
|
ephemeral_key: keys::EphemeralPublicKey::zcash_deserialize(&mut reader)?,
|
||||||
|
// Type is `Sym.C`, i.e. `𝔹^Y^{[N]}`, i.e. arbitrary-sized byte arrays
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to
|
||||||
|
// 580 bytes in https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus
|
||||||
|
// See [`note::EncryptedNote::zcash_deserialize`].
|
||||||
enc_ciphertext: note::EncryptedNote::zcash_deserialize(&mut reader)?,
|
enc_ciphertext: note::EncryptedNote::zcash_deserialize(&mut reader)?,
|
||||||
|
// Type is `Sym.C`, i.e. `𝔹^Y^{[N]}`, i.e. arbitrary-sized byte arrays
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to
|
||||||
|
// 80 bytes in https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus
|
||||||
|
// See [`note::WrappedNoteKey::zcash_deserialize`].
|
||||||
out_ciphertext: note::WrappedNoteKey::zcash_deserialize(&mut reader)?,
|
out_ciphertext: note::WrappedNoteKey::zcash_deserialize(&mut reader)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,14 @@ impl Item {
|
||||||
Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
|
Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
|
||||||
.and_then(|vk| vk.verify_prehashed(&sig, c)),
|
.and_then(|vk| vk.verify_prehashed(&sig, c)),
|
||||||
Inner::SpendAuth { vk_bytes, sig, c } => {
|
Inner::SpendAuth { vk_bytes, sig, c } => {
|
||||||
|
// # Consensus
|
||||||
|
//
|
||||||
|
// > Elements of an Action description MUST be canonical encodings of the types given above.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
|
//
|
||||||
|
// This validates the `rk` element, whose type is
|
||||||
|
// SpendAuthSig^{Orchard}.Public, i.e. ℙ.
|
||||||
VerificationKey::<SpendAuth>::try_from(vk_bytes)
|
VerificationKey::<SpendAuth>::try_from(vk_bytes)
|
||||||
.and_then(|vk| vk.verify_prehashed(&sig, c))
|
.and_then(|vk| vk.verify_prehashed(&sig, c))
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +252,14 @@ impl Verifier {
|
||||||
|
|
||||||
let VK = match item.inner {
|
let VK = match item.inner {
|
||||||
Inner::SpendAuth { vk_bytes, .. } => {
|
Inner::SpendAuth { vk_bytes, .. } => {
|
||||||
|
// # Consensus
|
||||||
|
//
|
||||||
|
// > Elements of an Action description MUST be canonical encodings of the types given above.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
|
//
|
||||||
|
// This validates the `rk` element, whose type is
|
||||||
|
// SpendAuthSig^{Orchard}.Public, i.e. ℙ.
|
||||||
VerificationKey::<SpendAuth>::try_from(vk_bytes.bytes)?.point
|
VerificationKey::<SpendAuth>::try_from(vk_bytes.bytes)?.point
|
||||||
}
|
}
|
||||||
Inner::Binding { vk_bytes, .. } => {
|
Inner::Binding { vk_bytes, .. } => {
|
||||||
|
|
|
||||||
|
|
@ -388,19 +388,36 @@ impl ZcashDeserialize for Option<orchard::ShieldedData> {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// # Consensus
|
||||||
|
//
|
||||||
|
// > Elements of an Action description MUST be canonical encodings of the types given above.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
|
//
|
||||||
|
// Some Action elements are validated in this function; they are described below.
|
||||||
|
|
||||||
// Denoted as `flagsOrchard` in the spec.
|
// Denoted as `flagsOrchard` in the spec.
|
||||||
|
// Consensus: type of each flag is 𝔹, i.e. a bit. This is enforced implicitly
|
||||||
|
// in [`Flags::zcash_deserialized`].
|
||||||
let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?;
|
let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?;
|
||||||
|
|
||||||
// Denoted as `valueBalanceOrchard` in the spec.
|
// Denoted as `valueBalanceOrchard` in the spec.
|
||||||
let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?;
|
let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?;
|
||||||
|
|
||||||
// Denoted as `anchorOrchard` in the spec.
|
// Denoted as `anchorOrchard` in the spec.
|
||||||
|
// Consensus: type is `{0 .. 𝑞_ℙ − 1}`. See [`orchard::tree::Root::zcash_deserialize`].
|
||||||
let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?;
|
let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?;
|
||||||
|
|
||||||
// Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec.
|
// Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec.
|
||||||
|
// Consensus: type is `ZKAction.Proof`, i.e. a byte sequence.
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#halo2encoding
|
||||||
let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?;
|
let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?;
|
||||||
|
|
||||||
// Denoted as `vSpendAuthSigsOrchard` in the spec.
|
// Denoted as `vSpendAuthSigsOrchard` in the spec.
|
||||||
|
// Consensus: this validates the `spendAuthSig` elements, whose type is
|
||||||
|
// SpendAuthSig^{Orchard}.Signature, i.e.
|
||||||
|
// B^Y^{[ceiling(ℓ_G/8) + ceiling(bitlength(𝑟_G)/8)]} i.e. 64 bytes
|
||||||
|
// See [`Signature::zcash_deserialize`].
|
||||||
let sigs: Vec<Signature<SpendAuth>> =
|
let sigs: Vec<Signature<SpendAuth>> =
|
||||||
zcash_deserialize_external_count(actions.len(), &mut reader)?;
|
zcash_deserialize_external_count(actions.len(), &mut reader)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -907,8 +907,10 @@ where
|
||||||
for authorized_action in orchard_shielded_data.actions.iter().cloned() {
|
for authorized_action in orchard_shielded_data.actions.iter().cloned() {
|
||||||
let (action, spend_auth_sig) = authorized_action.into_parts();
|
let (action, spend_auth_sig) = authorized_action.into_parts();
|
||||||
|
|
||||||
// Consensus rule: The proof 𝜋 MUST be valid given a primary
|
// # Consensus
|
||||||
// input (cv, rtOrchard, nf, rk, cm𝑥, enableSpends, enableOutputs)
|
//
|
||||||
|
// > The proof 𝜋 MUST be valid given a primary input (cv, rt^{Orchard},
|
||||||
|
// > nf, rk, cm_x, enableSpends, enableOutputs)
|
||||||
//
|
//
|
||||||
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
//
|
//
|
||||||
|
|
@ -922,9 +924,21 @@ where
|
||||||
.oneshot(primitives::halo2::Item::from(orchard_shielded_data)),
|
.oneshot(primitives::halo2::Item::from(orchard_shielded_data)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Consensus rule: The spend authorization signature
|
// # Consensus
|
||||||
// MUST be a valid SpendAuthSig signature over
|
//
|
||||||
// SigHash using rk as the validating key.
|
// > - Let SigHash be the SIGHASH transaction hash of this transaction, not
|
||||||
|
// > associated with an input, as defined in § 4.10 using SIGHASH_ALL.
|
||||||
|
// > - The spend authorization signature MUST be a valid SpendAuthSig^{Orchard}
|
||||||
|
// > signature over SigHash using rk as the validating key — i.e.
|
||||||
|
// > SpendAuthSig^{Orchard}.Validate_{rk}(SigHash, spendAuthSig) = 1.
|
||||||
|
// > As specified in § 5.4.7, validation of the 𝑅 component of the
|
||||||
|
// > signature prohibits non-canonical encodings.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#actiondesc
|
||||||
|
//
|
||||||
|
// This is validated by the verifier, inside the [`primitives::redpallas`] module.
|
||||||
|
// It calls [`pallas::Affine::from_bytes`] to parse R and
|
||||||
|
// that enforces the canonical encoding.
|
||||||
//
|
//
|
||||||
// Queue the validation of the RedPallas spend
|
// Queue the validation of the RedPallas spend
|
||||||
// authorization signature for each Action
|
// authorization signature for each Action
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue