Represent coinbase inputs explicitly.
Coinbase inputs are handled differently from other inputs and have different consensus rules, so they should be represented differently in the source code. This lets us discard extraneous details (for instance, it's not necessary to maintain the all-zero hash) and specialize logic.
This commit is contained in:
parent
be56e10abe
commit
4957567409
|
|
@ -152,19 +152,13 @@ impl ZcashDeserialize for BlockHeader {
|
|||
}
|
||||
}
|
||||
|
||||
/// A block in your blockchain.
|
||||
///
|
||||
/// A block is a data structure with two fields:
|
||||
///
|
||||
/// Block header: a data structure containing the block's metadata
|
||||
/// Transactions: an array (vector in Rust) of transactions
|
||||
/// A Zcash block, containing a [`BlockHeader`] and a sequence of
|
||||
/// [`Transaction`]s.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(test, derive(Arbitrary))]
|
||||
pub struct Block {
|
||||
/// First 80 bytes of the block as defined by the encoding used by
|
||||
/// "block" messages.
|
||||
/// The block header, containing block metadata.
|
||||
pub header: BlockHeader,
|
||||
|
||||
/// The block transactions.
|
||||
pub transactions: Vec<Transaction>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! transaction types, so that all of the serialization logic is in one place.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io;
|
||||
use std::io::{self, Read};
|
||||
|
||||
use crate::proofs::ZkSnarkProof;
|
||||
use crate::serialization::{
|
||||
|
|
@ -34,20 +34,56 @@ impl ZcashDeserialize for OutPoint {
|
|||
|
||||
impl ZcashSerialize for TransparentInput {
|
||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||
self.previous_output.zcash_serialize(&mut writer)?;
|
||||
self.signature_script.zcash_serialize(&mut writer)?;
|
||||
writer.write_u32::<LittleEndian>(self.sequence)?;
|
||||
match self {
|
||||
TransparentInput::PrevOut {
|
||||
outpoint,
|
||||
script,
|
||||
sequence,
|
||||
} => {
|
||||
outpoint.zcash_serialize(&mut writer)?;
|
||||
script.zcash_serialize(&mut writer)?;
|
||||
writer.write_u32::<LittleEndian>(*sequence)?;
|
||||
}
|
||||
TransparentInput::Coinbase { data, sequence } => {
|
||||
writer.write_all(&[0; 32][..])?;
|
||||
writer.write_u32::<LittleEndian>(0xffff_ffff)?;
|
||||
assert!(data.len() <= 100);
|
||||
writer.write_compactsize(data.len() as u64)?;
|
||||
writer.write_all(&data[..])?;
|
||||
writer.write_u32::<LittleEndian>(*sequence)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ZcashDeserialize for TransparentInput {
|
||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||
Ok(TransparentInput {
|
||||
previous_output: OutPoint::zcash_deserialize(&mut reader)?,
|
||||
signature_script: Script::zcash_deserialize(&mut reader)?,
|
||||
sequence: reader.read_u32::<LittleEndian>()?,
|
||||
})
|
||||
// This inlines the OutPoint deserialization to peek at the hash value
|
||||
// and detect whether we have a coinbase input.
|
||||
let bytes = reader.read_32_bytes()?;
|
||||
if bytes == [0; 32] {
|
||||
if reader.read_u32::<LittleEndian>()? != 0xffff_ffff {
|
||||
return Err(SerializationError::Parse("wrong index in coinbase"));
|
||||
}
|
||||
let len = reader.read_compactsize()?;
|
||||
if len > 100 {
|
||||
return Err(SerializationError::Parse("coinbase has too much data"));
|
||||
}
|
||||
let mut data = Vec::with_capacity(len as usize);
|
||||
(&mut reader).take(len).read_to_end(&mut data)?;
|
||||
let sequence = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(TransparentInput::Coinbase { data, sequence })
|
||||
} else {
|
||||
Ok(TransparentInput::PrevOut {
|
||||
outpoint: OutPoint {
|
||||
hash: TransactionHash(bytes),
|
||||
index: reader.read_u32::<LittleEndian>()?,
|
||||
},
|
||||
script: Script::zcash_deserialize(&mut reader)?,
|
||||
sequence: reader.read_u32::<LittleEndian>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use proptest::{
|
|||
|
||||
use crate::{
|
||||
serialization::{ZcashDeserialize, ZcashSerialize},
|
||||
types::LockTime,
|
||||
types::{LockTime, Script},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
|
@ -116,6 +116,31 @@ impl Arbitrary for Transaction {
|
|||
type Strategy = BoxedStrategy<Self>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Arbitrary for TransparentInput {
|
||||
type Parameters = ();
|
||||
|
||||
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
||||
prop_oneof![
|
||||
(any::<OutPoint>(), any::<Script>(), any::<u32>())
|
||||
.prop_map(|(outpoint, script, sequence)| {
|
||||
TransparentInput::PrevOut {
|
||||
outpoint,
|
||||
script,
|
||||
sequence,
|
||||
}
|
||||
})
|
||||
.boxed(),
|
||||
(vec(any::<u8>(), 0..100), any::<u32>())
|
||||
.prop_map(|(data, sequence)| { TransparentInput::Coinbase { data, sequence } })
|
||||
.boxed(),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn librustzcash_tx_deserialize_and_round_trip() {
|
||||
let tx = Transaction::zcash_deserialize(&test_vectors::GENERIC_TESTNET_TX[..])
|
||||
|
|
|
|||
|
|
@ -23,18 +23,23 @@ pub struct OutPoint {
|
|||
|
||||
/// A transparent input to a transaction.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(test, derive(Arbitrary))]
|
||||
pub struct TransparentInput {
|
||||
/// The previous output transaction reference.
|
||||
pub previous_output: OutPoint,
|
||||
|
||||
/// Computational Script for confirming transaction authorization.
|
||||
pub signature_script: Script,
|
||||
|
||||
/// Transaction version as defined by the sender. Intended for
|
||||
/// "replacement" of transactions when information is updated
|
||||
/// before inclusion into a block.
|
||||
pub sequence: u32,
|
||||
pub enum TransparentInput {
|
||||
/// A reference to an output of a previous transaction.
|
||||
PrevOut {
|
||||
/// The previous output transaction reference.
|
||||
outpoint: OutPoint,
|
||||
/// The script that authorizes spending `outpoint`.
|
||||
script: Script,
|
||||
/// The sequence number for the output.
|
||||
sequence: u32,
|
||||
},
|
||||
/// New coins created by the block reward.
|
||||
Coinbase {
|
||||
/// 100 bytes of arbitrary data.
|
||||
data: Vec<u8>,
|
||||
/// The sequence number for the output.
|
||||
sequence: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// A transparent output from a transaction.
|
||||
|
|
|
|||
Loading…
Reference in New Issue