diff --git a/zebra-chain/src/orchard/shielded_data.rs b/zebra-chain/src/orchard/shielded_data.rs index db2f361e54a..41b2b826afc 100644 --- a/zebra-chain/src/orchard/shielded_data.rs +++ b/zebra-chain/src/orchard/shielded_data.rs @@ -26,16 +26,22 @@ use crate::{ #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct ShieldedData { /// The orchard flags for this transaction. + /// Denoted as `flagsOrchard` in the spec. pub flags: Flags, /// The net value of Orchard spends minus outputs. + /// Denoted as `valueBalanceOrchard` in the spec. pub value_balance: Amount, /// The shared anchor for all `Spend`s in this transaction. + /// Denoted as `anchorOrchard` in the spec. pub shared_anchor: tree::Root, /// The aggregated zk-SNARK proof for all the actions in this transaction. + /// Denoted as `proofsOrchard` in the spec. pub proof: Halo2Proof, /// The Orchard Actions, in the order they appear in the transaction. + /// Denoted as `vActionsOrchard` and `vSpendAuthSigsOrchard` in the spec. pub actions: AtLeastOne, /// A signature on the transaction `sighash`. + /// Denoted as `bindingSigOrchard` in the spec. pub binding_sig: Signature, } diff --git a/zebra-chain/src/sapling/shielded_data.rs b/zebra-chain/src/sapling/shielded_data.rs index f6d4fceb7fa..cc1e0f8ae6b 100644 --- a/zebra-chain/src/sapling/shielded_data.rs +++ b/zebra-chain/src/sapling/shielded_data.rs @@ -90,6 +90,7 @@ where AnchorV: AnchorVariant + Clone, { /// The net value of Sapling spend transfers minus output transfers. + /// Denoted as `valueBalanceSapling` in the spec. pub value_balance: Amount, /// A bundle of spends and outputs, containing at least one spend or @@ -100,6 +101,7 @@ where pub transfers: TransferData, /// A signature on the transaction hash. + /// Denoted as `bindingSigSapling` in the spec. pub binding_sig: Signature, } diff --git a/zebra-chain/src/transaction/joinsplit.rs b/zebra-chain/src/transaction/joinsplit.rs index bb4fe528ce8..105ba09c091 100644 --- a/zebra-chain/src/transaction/joinsplit.rs +++ b/zebra-chain/src/transaction/joinsplit.rs @@ -42,9 +42,9 @@ pub struct JoinSplitData { deserialize = "JoinSplit

: Deserialize<'de>" ))] pub rest: Vec>, - /// The public key for the JoinSplit signature. + /// The public key for the JoinSplit signature, denoted as `joinSplitPubKey` in the spec. pub pub_key: ed25519::VerificationKeyBytes, - /// The JoinSplit signature. + /// The JoinSplit signature, denoted as `joinSplitSig` in the spec. pub sig: ed25519::Signature, } diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 669b73fedc7..f3b09be892e 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -69,10 +69,14 @@ impl ZcashDeserialize for pallas::Base { impl ZcashSerialize for JoinSplitData

{ fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + // Denoted as `nJoinSplit` and `vJoinSplit` in the spec. let joinsplits: Vec<_> = self.joinsplits().cloned().collect(); joinsplits.zcash_serialize(&mut writer)?; + // Denoted as `joinSplitPubKey` in the spec. writer.write_all(&<[u8; 32]>::from(self.pub_key)[..])?; + + // Denoted as `joinSplitSig` in the spec. writer.write_all(&<[u8; 64]>::from(self.sig)[..])?; Ok(()) } @@ -84,11 +88,14 @@ where sprout::JoinSplit

: TrustedPreallocate, { fn zcash_deserialize(mut reader: R) -> Result { + // Denoted as `nJoinSplit` and `vJoinSplit` in the spec. let joinsplits: Vec> = (&mut reader).zcash_deserialize_into()?; match joinsplits.split_first() { None => Ok(None), Some((first, rest)) => { + // Denoted as `joinSplitPubKey` in the spec. let pub_key = reader.read_32_bytes()?.into(); + // Denoted as `joinSplitSig` in the spec. let sig = reader.read_64_bytes()?.into(); Ok(Some(JoinSplitData { first: first.clone(), @@ -109,9 +116,9 @@ impl ZcashSerialize for Option> { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { None => { - // nSpendsSapling + // Denoted as `nSpendsSapling` in the spec. zcash_serialize_empty_list(&mut writer)?; - // nOutputsSapling + // Denoted as `nOutputsSapling` in the spec. zcash_serialize_empty_list(&mut writer)?; } Some(sapling_shielded_data) => { @@ -138,30 +145,30 @@ impl ZcashSerialize for sapling::ShieldedData { let (output_prefixes, output_proofs): (Vec<_>, _) = self.outputs().cloned().map(Output::into_v5_parts).unzip(); - // nSpendsSapling and vSpendsSapling + // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. spend_prefixes.zcash_serialize(&mut writer)?; - // nOutputsSapling and vOutputsSapling + // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec. output_prefixes.zcash_serialize(&mut writer)?; - // valueBalanceSapling + // Denoted as `valueBalanceSapling` in the spec. self.value_balance.zcash_serialize(&mut writer)?; - // anchorSapling + // Denoted as `anchorSapling` in the spec. // `TransferData` ensures this field is only present when there is at // least one spend. if let Some(shared_anchor) = self.shared_anchor() { writer.write_all(&<[u8; 32]>::from(shared_anchor)[..])?; } - // vSpendProofsSapling + // Denoted as `vSpendProofsSapling` in the spec. zcash_serialize_external_count(&spend_proofs, &mut writer)?; - // vSpendAuthSigsSapling + // Denoted as `vSpendAuthSigsSapling` in the spec. zcash_serialize_external_count(&spend_sigs, &mut writer)?; - // vOutputProofsSapling + // Denoted as `vOutputProofsSapling` in the spec. zcash_serialize_external_count(&output_proofs, &mut writer)?; - // bindingSigSapling + // Denoted as `bindingSigSapling` in the spec. writer.write_all(&<[u8; 64]>::from(self.binding_sig)[..])?; Ok(()) @@ -172,10 +179,10 @@ impl ZcashSerialize for sapling::ShieldedData { // because the counts are read along with the arrays. impl ZcashDeserialize for Option> { fn zcash_deserialize(mut reader: R) -> Result { - // nSpendsSapling and vSpendsSapling + // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. let spend_prefixes: Vec<_> = (&mut reader).zcash_deserialize_into()?; - // nOutputsSapling and vOutputsSapling + // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec. let output_prefixes: Vec<_> = (&mut reader).zcash_deserialize_into()?; // nSpendsSapling and nOutputsSapling as variables @@ -187,10 +194,10 @@ impl ZcashDeserialize for Option> { return Ok(None); } - // valueBalanceSapling + // Denoted as `valueBalanceSapling` in the spec. let value_balance = (&mut reader).zcash_deserialize_into()?; - // anchorSapling + // Denoted as `anchorSapling` in the spec. // // # Consensus // @@ -205,7 +212,7 @@ impl ZcashDeserialize for Option> { None }; - // vSpendProofsSapling + // Denoted as `vSpendProofsSapling` in the spec. // // # Consensus // @@ -220,7 +227,7 @@ impl ZcashDeserialize for Option> { // [`groth16::Item::try_from`]. In #3179 we plan to validate here instead. let spend_proofs = zcash_deserialize_external_count(spends_count, &mut reader)?; - // vSpendAuthSigsSapling + // Denoted as `vSpendAuthSigsSapling` in the spec. // // # Consensus // @@ -234,10 +241,10 @@ impl ZcashDeserialize for Option> { // See [`redjubjub::Signature::zcash_deserialize`]. let spend_sigs = zcash_deserialize_external_count(spends_count, &mut reader)?; - // vOutputProofsSapling + // Denoted as `vOutputProofsSapling` in the spec. let output_proofs = zcash_deserialize_external_count(outputs_count, &mut reader)?; - // bindingSigSapling + // Denoted as `bindingSigSapling` in the spec. let binding_sig = reader.read_64_bytes()?.into(); // Create shielded spends from deserialized parts @@ -298,14 +305,13 @@ impl ZcashSerialize for Option { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { None => { - // nActionsOrchard + // Denoted as `nActionsOrchard` in the spec. zcash_serialize_empty_list(writer)?; // We don't need to write anything else here. // "The fields flagsOrchard, valueBalanceOrchard, anchorOrchard, sizeProofsOrchard, // proofsOrchard , and bindingSigOrchard are present if and only if nActionsOrchard > 0." - // https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus notes of the second - // table, section sign. + // `§` note of the second table of https://zips.z.cash/protocol/protocol.pdf#txnencoding } Some(orchard_shielded_data) => { orchard_shielded_data.zcash_serialize(&mut writer)?; @@ -325,25 +331,25 @@ impl ZcashSerialize for orchard::ShieldedData { .map(orchard::AuthorizedAction::into_parts) .unzip(); - // nActionsOrchard and vActionsOrchard + // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. actions.zcash_serialize(&mut writer)?; - // flagsOrchard + // Denoted as `flagsOrchard` in the spec. self.flags.zcash_serialize(&mut writer)?; - // valueBalanceOrchard + // Denoted as `valueBalanceOrchard` in the spec. self.value_balance.zcash_serialize(&mut writer)?; - // anchorOrchard + // Denoted as `anchorOrchard` in the spec. self.shared_anchor.zcash_serialize(&mut writer)?; - // sizeProofsOrchard and proofsOrchard + // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec. self.proof.zcash_serialize(&mut writer)?; - // vSpendAuthSigsOrchard + // Denoted as `vSpendAuthSigsOrchard` in the spec. zcash_serialize_external_count(&sigs, &mut writer)?; - // bindingSigOrchard + // Denoted as `bindingSigOrchard` in the spec. self.binding_sig.zcash_serialize(&mut writer)?; Ok(()) @@ -354,32 +360,33 @@ impl ZcashSerialize for orchard::ShieldedData { // because the counts are read along with the arrays. impl ZcashDeserialize for Option { fn zcash_deserialize(mut reader: R) -> Result { - // nActionsOrchard and vActionsOrchard + // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. let actions: Vec = (&mut reader).zcash_deserialize_into()?; - // "sizeProofsOrchard ... [is] present if and only if nActionsOrchard > 0" - // https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus + // "The fields flagsOrchard, valueBalanceOrchard, anchorOrchard, sizeProofsOrchard, + // proofsOrchard , and bindingSigOrchard are present if and only if nActionsOrchard > 0." + // `§` note of the second table of https://zips.z.cash/protocol/protocol.pdf#txnencoding if actions.is_empty() { return Ok(None); } - // flagsOrchard + // Denoted as `flagsOrchard` in the spec. let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?; - // valueBalanceOrchard + // Denoted as `valueBalanceOrchard` in the spec. let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?; - // anchorOrchard + // Denoted as `anchorOrchard` in the spec. let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?; - // sizeProofsOrchard and proofsOrchard + // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec. let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?; - // vSpendAuthSigsOrchard + // Denoted as `vSpendAuthSigsOrchard` in the spec. let sigs: Vec> = zcash_deserialize_external_count(actions.len(), &mut reader)?; - // bindingSigOrchard + // Denoted as `bindingSigOrchard` in the spec. let binding_sig: Signature = (&mut reader).zcash_deserialize_into()?; // Create the AuthorizedAction from deserialized parts @@ -431,7 +438,10 @@ impl ZcashSerialize for Transaction { outputs, lock_time, } => { + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs.zcash_serialize(&mut writer)?; // Denoted as `lock_time` in the spec. @@ -443,12 +453,17 @@ impl ZcashSerialize for Transaction { lock_time, joinsplit_data, } => { + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs.zcash_serialize(&mut writer)?; // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. match joinsplit_data { // Write 0 for nJoinSplits to signal no JoinSplitData. None => zcash_serialize_empty_list(writer)?, @@ -465,13 +480,19 @@ impl ZcashSerialize for Transaction { // Denoted as `nVersionGroupId` in the spec. writer.write_u32::(OVERWINTER_VERSION_GROUP_ID)?; + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs.zcash_serialize(&mut writer)?; // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; writer.write_u32::(expiry_height.0)?; + + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. match joinsplit_data { // Write 0 for nJoinSplits to signal no JoinSplitData. None => zcash_serialize_empty_list(writer)?, @@ -492,7 +513,10 @@ impl ZcashSerialize for Transaction { // Denoted as `nVersionGroupId` in the spec. writer.write_u32::(SAPLING_VERSION_GROUP_ID)?; + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs.zcash_serialize(&mut writer)?; // Denoted as `lock_time` in the spec. @@ -518,13 +542,16 @@ impl ZcashSerialize for Transaction { zcash_serialize_empty_list(&mut writer)?; } Some(sapling_shielded_data) => { + // Denoted as `valueBalanceSapling` in the spec. sapling_shielded_data .value_balance .zcash_serialize(&mut writer)?; + // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. let spends: Vec<_> = sapling_shielded_data.spends().cloned().collect(); spends.zcash_serialize(&mut writer)?; + // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec. let outputs: Vec<_> = sapling_shielded_data .outputs() .cloned() @@ -534,11 +561,14 @@ impl ZcashSerialize for Transaction { } } + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. match joinsplit_data { None => zcash_serialize_empty_list(&mut writer)?, Some(jsd) => jsd.zcash_serialize(&mut writer)?, } + // Denoted as `bindingSigSapling` in the spec. match sapling_shielded_data { Some(sd) => writer.write_all(&<[u8; 64]>::from(sd.binding_sig)[..])?, None => {} @@ -573,14 +603,21 @@ impl ZcashSerialize for Transaction { // Denoted as `nExpiryHeight` in the spec. writer.write_u32::(expiry_height.0)?; - // transparent + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs.zcash_serialize(&mut writer)?; - // sapling + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. sapling_shielded_data.zcash_serialize(&mut writer)?; - // orchard + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. orchard_shielded_data.zcash_serialize(&mut writer)?; } } @@ -647,7 +684,9 @@ impl ZcashDeserialize for Transaction { // https://zips.z.cash/protocol/protocol.pdf#txnconsensus match (version, overwintered) { (1, false) => Ok(Transaction::V1 { + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs: Vec::zcash_deserialize(&mut limited_reader)?, + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs: Vec::zcash_deserialize(&mut limited_reader)?, // Denoted as `lock_time` in the spec. lock_time: LockTime::zcash_deserialize(&mut limited_reader)?, @@ -656,10 +695,14 @@ impl ZcashDeserialize for Transaction { // Version 2 transactions use Sprout-on-BCTV14. type OptV2Jsd = Option>; Ok(Transaction::V2 { + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs: Vec::zcash_deserialize(&mut limited_reader)?, + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs: Vec::zcash_deserialize(&mut limited_reader)?, // Denoted as `lock_time` in the spec. lock_time: LockTime::zcash_deserialize(&mut limited_reader)?, + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?, }) } @@ -674,12 +717,16 @@ impl ZcashDeserialize for Transaction { // Version 3 transactions use Sprout-on-BCTV14. type OptV3Jsd = Option>; Ok(Transaction::V3 { + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs: Vec::zcash_deserialize(&mut limited_reader)?, + // Denoted as `tx_out_count` and `tx_out` in the spec. outputs: Vec::zcash_deserialize(&mut limited_reader)?, // Denoted as `lock_time` in the spec. lock_time: LockTime::zcash_deserialize(&mut limited_reader)?, // Denoted as `nExpiryHeight` in the spec. expiry_height: block::Height(limited_reader.read_u32::()?), + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?, }) } @@ -705,7 +752,10 @@ impl ZcashDeserialize for Transaction { // instead we have to pull the component parts out manually and // then assemble them. + // Denoted as `tx_in_count` and `tx_in` in the spec. let inputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. let outputs = Vec::zcash_deserialize(&mut limited_reader)?; // Denoted as `lock_time` in the spec. @@ -714,14 +764,21 @@ impl ZcashDeserialize for Transaction { // Denoted as `nExpiryHeight` in the spec. let expiry_height = block::Height(limited_reader.read_u32::()?); + // Denoted as `valueBalanceSapling` in the spec. let value_balance = (&mut limited_reader).zcash_deserialize_into()?; + + // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. let shielded_spends = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec. let shielded_outputs = Vec::::zcash_deserialize(&mut limited_reader)? .into_iter() .map(Output::from_v4) .collect(); + // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, + // `joinSplitPubKey` and `joinSplitSig`. let joinsplit_data = OptV4Jsd::zcash_deserialize(&mut limited_reader)?; let sapling_transfers = if !shielded_spends.is_empty() { @@ -751,6 +808,7 @@ impl ZcashDeserialize for Transaction { Some(transfers) => Some(sapling::ShieldedData { value_balance, transfers, + // Denoted as `bindingSigSapling` in the spec. binding_sig: limited_reader.read_64_bytes()?.into(), }), None => None, @@ -788,14 +846,21 @@ impl ZcashDeserialize for Transaction { // Denoted as `nExpiryHeight` in the spec. let expiry_height = block::Height(limited_reader.read_u32::()?); - // transparent + // Denoted as `tx_in_count` and `tx_in` in the spec. let inputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. let outputs = Vec::zcash_deserialize(&mut limited_reader)?; - // sapling + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. let sapling_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; - // orchard + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; Ok(Transaction::V5 {