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
// 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::<LittleEndian>(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::<LittleEndian>(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::<LittleEndian>(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::<LittleEndian>(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::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
// header: Write the nConsensusBranchId
// Denoted as `nConsensusBranchId` in the spec.
writer.write_u32::<LittleEndian>(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::<LittleEndian>(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::<LittleEndian>()?;
(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::<LittleEndian>()?;
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::<LittleEndian>()?),
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::<LittleEndian>()?;
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::<LittleEndian>()?);
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::<LittleEndian>()?;
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::<LittleEndian>()?)
.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::<LittleEndian>()?);
// transparent