From 88aca26d30689587fc676c1abbe7773a119f83e3 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 8 Feb 2022 21:39:09 -0300 Subject: [PATCH] docs(transaction encoding): header fields (#3491) * document the `header` field * document the `nVersionGroupId` field * document the `nConsensusBranchId` field * document the `lock_time` field * document the `nExpiryHeight` field (and some missing `lock_time`) * add missing note to `header` field in serialization Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- zebra-chain/src/transaction/serialize.rs | 56 +++++++++++++++++++++--- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 6c58007b5..669b73fed 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -418,7 +418,8 @@ impl ZcashSerialize for Transaction { // Since we checkpoint on Canopy activation, we won't ever need // to check the smaller pre-Sapling transaction size limit. - // header: Write version and set the fOverwintered bit if necessary + // Denoted as `header` in the spec, contains the `fOverwintered` flag and the `version` field. + // Write `version` and set the `fOverwintered` bit if necessary let overwintered_flag = if self.is_overwintered() { 1 << 31 } else { 0 }; let version = overwintered_flag | self.version(); @@ -432,6 +433,8 @@ impl ZcashSerialize for Transaction { } => { inputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?; + + // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; } Transaction::V2 { @@ -442,7 +445,10 @@ impl ZcashSerialize for Transaction { } => { inputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?; + + // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; + match joinsplit_data { // Write 0 for nJoinSplits to signal no JoinSplitData. None => zcash_serialize_empty_list(writer)?, @@ -456,10 +462,15 @@ impl ZcashSerialize for Transaction { expiry_height, joinsplit_data, } => { + // Denoted as `nVersionGroupId` in the spec. writer.write_u32::(OVERWINTER_VERSION_GROUP_ID)?; + inputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?; + + // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; + writer.write_u32::(expiry_height.0)?; match joinsplit_data { // Write 0 for nJoinSplits to signal no JoinSplitData. @@ -475,10 +486,19 @@ impl ZcashSerialize for Transaction { sapling_shielded_data, joinsplit_data, } => { + // Transaction V4 spec: + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + + // Denoted as `nVersionGroupId` in the spec. writer.write_u32::(SAPLING_VERSION_GROUP_ID)?; + inputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?; + + // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; + + // Denoted as `nExpiryHeight` in the spec. writer.write_u32::(expiry_height.0)?; // The previous match arms serialize in one go, because the @@ -535,19 +555,22 @@ impl ZcashSerialize for Transaction { orchard_shielded_data, } => { // Transaction V5 spec: - // https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + // Denoted as `nVersionGroupId` in the spec. writer.write_u32::(TX_V5_VERSION_GROUP_ID)?; - // header: Write the nConsensusBranchId + // Denoted as `nConsensusBranchId` in the spec. writer.write_u32::(u32::from( network_upgrade .branch_id() .expect("valid transactions must have a network upgrade with a branch id"), ))?; - // transaction validity time and height limits + // Denoted as `lock_time` in the spec. lock_time.zcash_serialize(&mut writer)?; + + // Denoted as `nExpiryHeight` in the spec. writer.write_u32::(expiry_height.0)?; // transparent @@ -583,6 +606,7 @@ impl ZcashDeserialize for Transaction { let (version, overwintered) = { const LOW_31_BITS: u32 = (1 << 31) - 1; + // Denoted as `header` in the spec, contains the `fOverwintered` flag and the `version` field. let header = limited_reader.read_u32::()?; (header & LOW_31_BITS, header >> 31 != 0) }; @@ -625,6 +649,7 @@ impl ZcashDeserialize for Transaction { (1, false) => Ok(Transaction::V1 { inputs: Vec::zcash_deserialize(&mut limited_reader)?, outputs: Vec::zcash_deserialize(&mut limited_reader)?, + // Denoted as `lock_time` in the spec. lock_time: LockTime::zcash_deserialize(&mut limited_reader)?, }), (2, false) => { @@ -633,11 +658,13 @@ impl ZcashDeserialize for Transaction { Ok(Transaction::V2 { inputs: Vec::zcash_deserialize(&mut limited_reader)?, outputs: Vec::zcash_deserialize(&mut limited_reader)?, + // Denoted as `lock_time` in the spec. lock_time: LockTime::zcash_deserialize(&mut limited_reader)?, joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?, }) } (3, true) => { + // Denoted as `nVersionGroupId` in the spec. let id = limited_reader.read_u32::()?; if id != OVERWINTER_VERSION_GROUP_ID { return Err(SerializationError::Parse( @@ -649,12 +676,18 @@ impl ZcashDeserialize for Transaction { Ok(Transaction::V3 { inputs: Vec::zcash_deserialize(&mut limited_reader)?, 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::()?), joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?, }) } (4, true) => { + // Transaction V4 spec: + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + + // Denoted as `nVersionGroupId` in the spec. let id = limited_reader.read_u32::()?; if id != SAPLING_VERSION_GROUP_ID { return Err(SerializationError::Parse( @@ -674,7 +707,11 @@ impl ZcashDeserialize for Transaction { let inputs = Vec::zcash_deserialize(&mut limited_reader)?; let outputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `lock_time` in the spec. let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `nExpiryHeight` in the spec. let expiry_height = block::Height(limited_reader.read_u32::()?); let value_balance = (&mut limited_reader).zcash_deserialize_into()?; @@ -729,19 +766,26 @@ impl ZcashDeserialize for Transaction { }) } (5, true) => { + // Transaction V5 spec: + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + + // Denoted as `nVersionGroupId` in the spec. let id = limited_reader.read_u32::()?; if id != TX_V5_VERSION_GROUP_ID { return Err(SerializationError::Parse("expected TX_V5_VERSION_GROUP_ID")); } - // convert the nConsensusBranchId to a NetworkUpgrade + // Denoted as `nConsensusBranchId` in the spec. + // Convert it to a NetworkUpgrade let network_upgrade = NetworkUpgrade::from_branch_id(limited_reader.read_u32::()?) .ok_or(SerializationError::Parse( "expected a valid network upgrade from the consensus branch id", ))?; - // transaction validity time and height limits + // Denoted as `lock_time` in the spec. let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `nExpiryHeight` in the spec. let expiry_height = block::Height(limited_reader.read_u32::()?); // transparent