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:
parent
294ea7ebae
commit
88aca26d30
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue