Add a CoinbaseData field, replacing Vec<u8>.
The CoinbaseData field can only be constructed by the transaction parser, so we can ensure that a coinbase input is always serializable, as CoinbaseData instances can't be constructed outside of the parser that maintains the data size invariant.
This commit is contained in:
parent
cd8e67b47c
commit
2d2603cf65
|
@ -14,7 +14,7 @@ mod tests;
|
|||
pub use hash::TransactionHash;
|
||||
pub use joinsplit::{JoinSplit, JoinSplitData};
|
||||
pub use shielded_data::{OutputDescription, ShieldedData, SpendDescription};
|
||||
pub use transparent::{OutPoint, TransparentInput, TransparentOutput};
|
||||
pub use transparent::{CoinbaseData, OutPoint, TransparentInput, TransparentOutput};
|
||||
|
||||
use crate::proofs::{Bctv14Proof, Groth16Proof};
|
||||
use crate::types::{BlockHeight, LockTime};
|
||||
|
|
|
@ -46,23 +46,28 @@ impl ZcashDeserialize for OutPoint {
|
|||
// unrepresentable, we need just enough parsing of Bitcoin scripts to parse the
|
||||
// coinbase height and split off the rest of the (inert) coinbase data.
|
||||
|
||||
fn parse_coinbase_height(mut data: Vec<u8>) -> Result<(BlockHeight, Vec<u8>), SerializationError> {
|
||||
fn parse_coinbase_height(
|
||||
mut data: Vec<u8>,
|
||||
) -> Result<(BlockHeight, CoinbaseData), SerializationError> {
|
||||
match (data.get(0), data.len()) {
|
||||
// Blocks 1 through 16 inclusive encode block height with OP_N opcodes.
|
||||
(Some(op_n @ 0x51..=0x60), len) if len >= 1 => {
|
||||
Ok((BlockHeight((op_n - 0x50) as u32), data.split_off(1)))
|
||||
}
|
||||
(Some(op_n @ 0x51..=0x60), len) if len >= 1 => Ok((
|
||||
BlockHeight((op_n - 0x50) as u32),
|
||||
CoinbaseData(data.split_off(1)),
|
||||
)),
|
||||
// Blocks 17 through 256 exclusive encode block height with the `0x01` opcode.
|
||||
(Some(0x01), len) if len >= 2 => Ok((BlockHeight(data[1] as u32), data.split_off(2))),
|
||||
(Some(0x01), len) if len >= 2 => {
|
||||
Ok((BlockHeight(data[1] as u32), CoinbaseData(data.split_off(2))))
|
||||
}
|
||||
// Blocks 256 through 65536 exclusive encode block height with the `0x02` opcode.
|
||||
(Some(0x02), len) if len >= 3 => Ok((
|
||||
BlockHeight(data[1] as u32 + ((data[2] as u32) << 8)),
|
||||
data.split_off(3),
|
||||
CoinbaseData(data.split_off(3)),
|
||||
)),
|
||||
// Blocks 65536 through 2**24 exclusive encode block height with the `0x03` opcode.
|
||||
(Some(0x03), len) if len >= 4 => Ok((
|
||||
BlockHeight(data[1] as u32 + ((data[2] as u32) << 8) + ((data[3] as u32) << 16)),
|
||||
data.split_off(4),
|
||||
CoinbaseData(data.split_off(4)),
|
||||
)),
|
||||
// The genesis block does not encode the block height by mistake; special case it.
|
||||
// The first five bytes are [4, 255, 255, 7, 31], the little-endian encoding of
|
||||
|
@ -70,7 +75,9 @@ fn parse_coinbase_height(mut data: Vec<u8>) -> Result<(BlockHeight, Vec<u8>), Se
|
|||
// while remaining below the maximum `BlockHeight` of 500_000_000 forced by `LockTime`.
|
||||
// While it's unlikely this code will ever process a block height that high, this means
|
||||
// we don't need to maintain a cascade of different invariants for allowable `BlockHeight`s.
|
||||
(Some(0x04), _) if data[..] == GENESIS_COINBASE_DATA[..] => Ok((BlockHeight(0), data)),
|
||||
(Some(0x04), _) if data[..] == GENESIS_COINBASE_DATA[..] => {
|
||||
Ok((BlockHeight(0), CoinbaseData(data)))
|
||||
}
|
||||
// As noted above, this is included for completeness.
|
||||
(Some(0x04), len) if len >= 5 => {
|
||||
let h = data[1] as u32
|
||||
|
@ -78,7 +85,7 @@ fn parse_coinbase_height(mut data: Vec<u8>) -> Result<(BlockHeight, Vec<u8>), Se
|
|||
+ ((data[3] as u32) << 16)
|
||||
+ ((data[4] as u32) << 24);
|
||||
if h < 500_000_000 {
|
||||
Ok((BlockHeight(h), data.split_off(5)))
|
||||
Ok((BlockHeight(h), CoinbaseData(data.split_off(5))))
|
||||
} else {
|
||||
Err(SerializationError::Parse("Invalid block height"))
|
||||
}
|
||||
|
@ -156,11 +163,10 @@ impl ZcashSerialize for TransparentInput {
|
|||
writer.write_all(&[0; 32][..])?;
|
||||
writer.write_u32::<LittleEndian>(0xffff_ffff)?;
|
||||
let height_len = coinbase_height_len(*height);
|
||||
let total_len = height_len + data.len();
|
||||
assert!(total_len <= 100);
|
||||
let total_len = height_len + data.as_ref().len();
|
||||
writer.write_compactsize(total_len as u64)?;
|
||||
write_coinbase_height(*height, &mut writer)?;
|
||||
writer.write_all(&data[..])?;
|
||||
writer.write_all(&data.as_ref()[..])?;
|
||||
writer.write_u32::<LittleEndian>(*sequence)?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ impl Arbitrary for TransparentInput {
|
|||
.prop_map(|(height, data, sequence)| {
|
||||
TransparentInput::Coinbase {
|
||||
height,
|
||||
data,
|
||||
data: CoinbaseData(data),
|
||||
sequence,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -7,6 +7,24 @@ use crate::types::{BlockHeight, Script};
|
|||
|
||||
use super::TransactionHash;
|
||||
|
||||
/// Arbitrary data inserted by miners into a coinbase transaction.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CoinbaseData(
|
||||
/// Invariant: this vec, together with the coinbase height, must be less than
|
||||
/// 100 bytes. We enforce this by only constructing CoinbaseData fields by
|
||||
/// parsing blocks with 100-byte data fields. When we implement block
|
||||
/// creation, we should provide a constructor for the coinbase data field
|
||||
/// that restricts it to 95 = 100 -1 -4 bytes (safe for any block height up
|
||||
/// to 500_000_000).
|
||||
pub(super) Vec<u8>,
|
||||
);
|
||||
|
||||
impl AsRef<[u8]> for CoinbaseData {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// OutPoint
|
||||
///
|
||||
/// A particular transaction output reference.
|
||||
|
@ -37,9 +55,8 @@ pub enum TransparentInput {
|
|||
Coinbase {
|
||||
/// The height of this block.
|
||||
height: BlockHeight,
|
||||
/// Approximately 100 bytes of data (95 to be safe).
|
||||
/// XXX refine this type.
|
||||
data: Vec<u8>,
|
||||
/// Free data inserted by miners after the block height.
|
||||
data: CoinbaseData,
|
||||
/// The sequence number for the output.
|
||||
sequence: u32,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue