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>
This commit is contained in:
Alfredo Garcia 2022-02-08 21:39:09 -03:00 committed by GitHub
parent 294ea7ebae
commit 88aca26d30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 6 deletions

View File

@ -418,7 +418,8 @@ impl ZcashSerialize for Transaction {
// Since we checkpoint on Canopy activation, we won't ever need // Since we checkpoint on Canopy activation, we won't ever need
// to check the smaller pre-Sapling transaction size limit. // 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 overwintered_flag = if self.is_overwintered() { 1 << 31 } else { 0 };
let version = overwintered_flag | self.version(); let version = overwintered_flag | self.version();
@ -432,6 +433,8 @@ impl ZcashSerialize for Transaction {
} => { } => {
inputs.zcash_serialize(&mut writer)?; inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?;
// Denoted as `lock_time` in the spec.
lock_time.zcash_serialize(&mut writer)?; lock_time.zcash_serialize(&mut writer)?;
} }
Transaction::V2 { Transaction::V2 {
@ -442,7 +445,10 @@ impl ZcashSerialize for Transaction {
} => { } => {
inputs.zcash_serialize(&mut writer)?; inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?;
// Denoted as `lock_time` in the spec.
lock_time.zcash_serialize(&mut writer)?; lock_time.zcash_serialize(&mut writer)?;
match joinsplit_data { match joinsplit_data {
// Write 0 for nJoinSplits to signal no JoinSplitData. // Write 0 for nJoinSplits to signal no JoinSplitData.
None => zcash_serialize_empty_list(writer)?, None => zcash_serialize_empty_list(writer)?,
@ -456,10 +462,15 @@ impl ZcashSerialize for Transaction {
expiry_height, expiry_height,
joinsplit_data, joinsplit_data,
} => { } => {
// Denoted as `nVersionGroupId` in the spec.
writer.write_u32::<LittleEndian>(OVERWINTER_VERSION_GROUP_ID)?; writer.write_u32::<LittleEndian>(OVERWINTER_VERSION_GROUP_ID)?;
inputs.zcash_serialize(&mut writer)?; inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?;
// Denoted as `lock_time` in the spec.
lock_time.zcash_serialize(&mut writer)?; lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?; writer.write_u32::<LittleEndian>(expiry_height.0)?;
match joinsplit_data { match joinsplit_data {
// Write 0 for nJoinSplits to signal no JoinSplitData. // Write 0 for nJoinSplits to signal no JoinSplitData.
@ -475,10 +486,19 @@ impl ZcashSerialize for Transaction {
sapling_shielded_data, sapling_shielded_data,
joinsplit_data, joinsplit_data,
} => { } => {
// Transaction V4 spec:
// https://zips.z.cash/protocol/protocol.pdf#txnencoding
// Denoted as `nVersionGroupId` in the spec.
writer.write_u32::<LittleEndian>(SAPLING_VERSION_GROUP_ID)?; writer.write_u32::<LittleEndian>(SAPLING_VERSION_GROUP_ID)?;
inputs.zcash_serialize(&mut writer)?; inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?; outputs.zcash_serialize(&mut writer)?;
// Denoted as `lock_time` in the spec.
lock_time.zcash_serialize(&mut writer)?; lock_time.zcash_serialize(&mut writer)?;
// Denoted as `nExpiryHeight` in the spec.
writer.write_u32::<LittleEndian>(expiry_height.0)?; writer.write_u32::<LittleEndian>(expiry_height.0)?;
// The previous match arms serialize in one go, because the // The previous match arms serialize in one go, because the
@ -535,19 +555,22 @@ impl ZcashSerialize for Transaction {
orchard_shielded_data, orchard_shielded_data,
} => { } => {
// Transaction V5 spec: // 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::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?; writer.write_u32::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
// header: Write the nConsensusBranchId // Denoted as `nConsensusBranchId` in the spec.
writer.write_u32::<LittleEndian>(u32::from( writer.write_u32::<LittleEndian>(u32::from(
network_upgrade network_upgrade
.branch_id() .branch_id()
.expect("valid transactions must have a network upgrade with a 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)?; lock_time.zcash_serialize(&mut writer)?;
// Denoted as `nExpiryHeight` in the spec.
writer.write_u32::<LittleEndian>(expiry_height.0)?; writer.write_u32::<LittleEndian>(expiry_height.0)?;
// transparent // transparent
@ -583,6 +606,7 @@ impl ZcashDeserialize for Transaction {
let (version, overwintered) = { let (version, overwintered) = {
const LOW_31_BITS: u32 = (1 << 31) - 1; 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::<LittleEndian>()?; let header = limited_reader.read_u32::<LittleEndian>()?;
(header & LOW_31_BITS, header >> 31 != 0) (header & LOW_31_BITS, header >> 31 != 0)
}; };
@ -625,6 +649,7 @@ impl ZcashDeserialize for Transaction {
(1, false) => Ok(Transaction::V1 { (1, false) => Ok(Transaction::V1 {
inputs: Vec::zcash_deserialize(&mut limited_reader)?, inputs: Vec::zcash_deserialize(&mut limited_reader)?,
outputs: 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)?, lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
}), }),
(2, false) => { (2, false) => {
@ -633,11 +658,13 @@ impl ZcashDeserialize for Transaction {
Ok(Transaction::V2 { Ok(Transaction::V2 {
inputs: Vec::zcash_deserialize(&mut limited_reader)?, inputs: Vec::zcash_deserialize(&mut limited_reader)?,
outputs: 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)?, lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?, joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?,
}) })
} }
(3, true) => { (3, true) => {
// Denoted as `nVersionGroupId` in the spec.
let id = limited_reader.read_u32::<LittleEndian>()?; let id = limited_reader.read_u32::<LittleEndian>()?;
if id != OVERWINTER_VERSION_GROUP_ID { if id != OVERWINTER_VERSION_GROUP_ID {
return Err(SerializationError::Parse( return Err(SerializationError::Parse(
@ -649,12 +676,18 @@ impl ZcashDeserialize for Transaction {
Ok(Transaction::V3 { Ok(Transaction::V3 {
inputs: Vec::zcash_deserialize(&mut limited_reader)?, inputs: Vec::zcash_deserialize(&mut limited_reader)?,
outputs: 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)?, lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
// Denoted as `nExpiryHeight` in the spec.
expiry_height: block::Height(limited_reader.read_u32::<LittleEndian>()?), expiry_height: block::Height(limited_reader.read_u32::<LittleEndian>()?),
joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?, joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?,
}) })
} }
(4, true) => { (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::<LittleEndian>()?; let id = limited_reader.read_u32::<LittleEndian>()?;
if id != SAPLING_VERSION_GROUP_ID { if id != SAPLING_VERSION_GROUP_ID {
return Err(SerializationError::Parse( return Err(SerializationError::Parse(
@ -674,7 +707,11 @@ impl ZcashDeserialize for Transaction {
let inputs = Vec::zcash_deserialize(&mut limited_reader)?; let inputs = Vec::zcash_deserialize(&mut limited_reader)?;
let outputs = 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)?; let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
// Denoted as `nExpiryHeight` in the spec.
let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?); let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
let value_balance = (&mut limited_reader).zcash_deserialize_into()?; let value_balance = (&mut limited_reader).zcash_deserialize_into()?;
@ -729,19 +766,26 @@ impl ZcashDeserialize for Transaction {
}) })
} }
(5, true) => { (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::<LittleEndian>()?; let id = limited_reader.read_u32::<LittleEndian>()?;
if id != TX_V5_VERSION_GROUP_ID { if id != TX_V5_VERSION_GROUP_ID {
return Err(SerializationError::Parse("expected 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 = let network_upgrade =
NetworkUpgrade::from_branch_id(limited_reader.read_u32::<LittleEndian>()?) NetworkUpgrade::from_branch_id(limited_reader.read_u32::<LittleEndian>()?)
.ok_or(SerializationError::Parse( .ok_or(SerializationError::Parse(
"expected a valid network upgrade from the consensus branch id", "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)?; let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
// Denoted as `nExpiryHeight` in the spec.
let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?); let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
// transparent // transparent