Merge rust-bitcoin/rust-bitcoin#3582: Re-design and move `Block` to `primitives`
7819e50055
Move Block to primitives (Tobin C. Harding)55200924e4
Move Sealed to bottom of file (Tobin C. Harding)d5b148d400
Make block_size free standing and rename (Tobin C. Harding)5016a73207
Manually implement consensus traits for Block (Tobin C. Harding) Pull request description: Re-design the `Block` API and move the type to `primitives`. ACKs for top commit: apoelstra: ACK 7819e50055c1be1d09b7d08734955f43229e6ed5; successfully ran local tests; let's go Tree-SHA512: 13dc27508348966392aab4a80a3009bd53dd604d98ef392b9655bb97d669e01c4399901043ca3558b9b339cec5a1521ed0abfe7d666a66c6cc84ee4a97772de1
This commit is contained in:
commit
9f68802887
|
@ -18,7 +18,7 @@ use crate::internal_macros::{
|
|||
};
|
||||
use crate::prelude::Vec;
|
||||
use crate::transaction::TxIdentifier;
|
||||
use crate::{block, consensus, Block, BlockHash, Transaction};
|
||||
use crate::{block, consensus, Block, BlockChecked, BlockHash, Transaction};
|
||||
|
||||
/// A BIP-152 error
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -203,7 +203,7 @@ impl HeaderAndShortIds {
|
|||
///
|
||||
/// > Nodes SHOULD NOT use the same nonce across multiple different blocks.
|
||||
pub fn from_block(
|
||||
block: &Block,
|
||||
block: &Block<BlockChecked>,
|
||||
nonce: u64,
|
||||
version: u32,
|
||||
mut prefill: &[usize],
|
||||
|
@ -212,12 +212,12 @@ impl HeaderAndShortIds {
|
|||
return Err(Error::UnknownVersion);
|
||||
}
|
||||
|
||||
let siphash_keys = ShortId::calculate_siphash_keys(&block.header, nonce);
|
||||
let siphash_keys = ShortId::calculate_siphash_keys(block.header(), nonce);
|
||||
|
||||
let mut prefilled = Vec::with_capacity(prefill.len() + 1); // +1 for coinbase tx
|
||||
let mut short_ids = Vec::with_capacity(block.txdata.len() - prefill.len());
|
||||
let mut short_ids = Vec::with_capacity(block.transactions().len() - prefill.len());
|
||||
let mut last_prefill = 0;
|
||||
for (idx, tx) in block.txdata.iter().enumerate() {
|
||||
for (idx, tx) in block.transactions().iter().enumerate() {
|
||||
// Check if we should prefill this tx.
|
||||
let prefill_tx = if prefill.first() == Some(&idx) {
|
||||
prefill = &prefill[1..];
|
||||
|
@ -263,7 +263,7 @@ impl HeaderAndShortIds {
|
|||
}
|
||||
|
||||
Ok(HeaderAndShortIds {
|
||||
header: block.header,
|
||||
header: *block.header(),
|
||||
nonce,
|
||||
// Provide coinbase prefilled.
|
||||
prefilled_txs: prefilled,
|
||||
|
@ -382,17 +382,17 @@ impl BlockTransactions {
|
|||
/// the corresponding full [`Block`] by providing all requested transactions.
|
||||
pub fn from_request(
|
||||
request: &BlockTransactionsRequest,
|
||||
block: &Block,
|
||||
block: &Block<BlockChecked>,
|
||||
) -> Result<BlockTransactions, TxIndexOutOfRangeError> {
|
||||
Ok(BlockTransactions {
|
||||
block_hash: request.block_hash,
|
||||
transactions: {
|
||||
let mut txs = Vec::with_capacity(request.indexes.len());
|
||||
for idx in &request.indexes {
|
||||
if *idx >= block.txdata.len().to_u64() {
|
||||
if *idx >= block.transactions().len().to_u64() {
|
||||
return Err(TxIndexOutOfRangeError(*idx));
|
||||
}
|
||||
txs.push(block.txdata[*idx as usize].clone());
|
||||
txs.push(block.transactions()[*idx as usize].clone());
|
||||
}
|
||||
txs
|
||||
},
|
||||
|
@ -410,8 +410,8 @@ mod test {
|
|||
use crate::merkle_tree::TxMerkleNode;
|
||||
use crate::transaction::OutPointExt;
|
||||
use crate::{
|
||||
transaction, Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid,
|
||||
Witness,
|
||||
transaction, Amount, BlockChecked, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn,
|
||||
TxOut, Txid, Witness,
|
||||
};
|
||||
|
||||
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
||||
|
@ -429,18 +429,17 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
fn dummy_block() -> Block {
|
||||
Block {
|
||||
header: block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::from_byte_array([0x99; 32]),
|
||||
merkle_root: TxMerkleNode::from_byte_array([0x77; 32]),
|
||||
time: 2,
|
||||
bits: CompactTarget::from_consensus(3),
|
||||
nonce: 4,
|
||||
},
|
||||
txdata: vec![dummy_tx(&[2]), dummy_tx(&[3]), dummy_tx(&[4])],
|
||||
}
|
||||
fn dummy_block() -> Block<BlockChecked> {
|
||||
let header = block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::from_byte_array([0x99; 32]),
|
||||
merkle_root: TxMerkleNode::from_byte_array([0x77; 32]),
|
||||
time: 2,
|
||||
bits: CompactTarget::from_consensus(3),
|
||||
nonce: 4,
|
||||
};
|
||||
let transactions = vec![dummy_tx(&[2]), dummy_tx(&[3]), dummy_tx(&[4])];
|
||||
Block::new_unchecked(header, transactions).assume_checked(None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -452,7 +451,7 @@ mod test {
|
|||
assert_eq!(compact.short_ids.len(), 2);
|
||||
assert_eq!(compact.prefilled_txs.len(), 1);
|
||||
assert_eq!(compact.prefilled_txs[0].idx, 0);
|
||||
assert_eq!(&compact.prefilled_txs[0].tx, &block.txdata[0]);
|
||||
assert_eq!(&compact.prefilled_txs[0].tx, &block.transactions()[0]);
|
||||
|
||||
let compact = HeaderAndShortIds::from_block(&block, 42, 2, &[0, 1, 2]).unwrap();
|
||||
let idxs = compact.prefilled_txs.iter().map(|t| t.idx).collect::<Vec<_>>();
|
||||
|
@ -470,6 +469,7 @@ mod test {
|
|||
let raw_compact = Vec::<u8>::from_hex("000000206c750a364035aefd5f81508a08769975116d9195312ee4520dceac39e1fdc62c4dc67473b8e354358c1e610afeaff7410858bd45df43e2940f8a62bd3d5e3ac943c2975cffff7f2000000000a4df3c3744da89fa010a6979e971450100020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04016b0101ffffffff020006062a0100000001510000000000000000266a24aa21a9ed4a3d9f3343dafcc0d6f6d4310f2ee5ce273ed34edca6c75db3a73e7f368734200120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
|
||||
let block: Block = deserialize(&raw_block).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
let nonce = 18053200567810711460;
|
||||
let compact = HeaderAndShortIds::from_block(&block, nonce, 2, &[]).unwrap();
|
||||
let compact_expected = deserialize(&raw_compact).unwrap();
|
||||
|
|
|
@ -44,7 +44,7 @@ use hashes::{sha256d, siphash24, HashEngine as _};
|
|||
use internals::{write_err, ToU64 as _};
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use crate::block::{Block, BlockHash};
|
||||
use crate::block::{Block, BlockHash, Checked};
|
||||
use crate::consensus::{ReadExt, WriteExt};
|
||||
use crate::internal_macros::impl_hashencode;
|
||||
use crate::prelude::{BTreeSet, Borrow, Vec};
|
||||
|
@ -126,7 +126,10 @@ impl BlockFilter {
|
|||
pub fn new(content: &[u8]) -> BlockFilter { BlockFilter { content: content.to_vec() } }
|
||||
|
||||
/// Computes a SCRIPT_FILTER that contains spent and output scripts.
|
||||
pub fn new_script_filter<M, S>(block: &Block, script_for_coin: M) -> Result<BlockFilter, Error>
|
||||
pub fn new_script_filter<M, S>(
|
||||
block: &Block<Checked>,
|
||||
script_for_coin: M,
|
||||
) -> Result<BlockFilter, Error>
|
||||
where
|
||||
M: Fn(&OutPoint) -> Result<S, Error>,
|
||||
S: Borrow<Script>,
|
||||
|
@ -177,13 +180,13 @@ impl BlockFilter {
|
|||
|
||||
/// Compiles and writes a block filter.
|
||||
pub struct BlockFilterWriter<'a, W> {
|
||||
block: &'a Block,
|
||||
block: &'a Block<Checked>,
|
||||
writer: GcsFilterWriter<'a, W>,
|
||||
}
|
||||
|
||||
impl<'a, W: Write> BlockFilterWriter<'a, W> {
|
||||
/// Constructs a new [`BlockFilterWriter`] from `block`.
|
||||
pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> {
|
||||
pub fn new(writer: &'a mut W, block: &'a Block<Checked>) -> BlockFilterWriter<'a, W> {
|
||||
let block_hash_as_int = block.block_hash().to_byte_array();
|
||||
let k0 = u64::from_le_bytes(block_hash_as_int[0..8].try_into().expect("8 byte slice"));
|
||||
let k1 = u64::from_le_bytes(block_hash_as_int[8..16].try_into().expect("8 byte slice"));
|
||||
|
@ -193,7 +196,7 @@ impl<'a, W: Write> BlockFilterWriter<'a, W> {
|
|||
|
||||
/// Adds output scripts of the block to filter (excluding OP_RETURN scripts).
|
||||
pub fn add_output_scripts(&mut self) {
|
||||
for transaction in &self.block.txdata {
|
||||
for transaction in self.block.transactions() {
|
||||
for output in &transaction.output {
|
||||
if !output.script_pubkey.is_op_return() {
|
||||
self.add_element(output.script_pubkey.as_bytes());
|
||||
|
@ -210,7 +213,7 @@ impl<'a, W: Write> BlockFilterWriter<'a, W> {
|
|||
{
|
||||
for script in self
|
||||
.block
|
||||
.txdata
|
||||
.transactions()
|
||||
.iter()
|
||||
.skip(1) // skip coinbase
|
||||
.flat_map(|t| t.input.iter().map(|i| &i.previous_output))
|
||||
|
@ -583,6 +586,7 @@ mod test {
|
|||
for t in testdata.iter().skip(1) {
|
||||
let block_hash = t.get(1).unwrap().as_str().unwrap().parse::<BlockHash>().unwrap();
|
||||
let block: Block = deserialize(&hex!(t.get(2).unwrap().as_str().unwrap())).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
assert_eq!(block.block_hash(), block_hash);
|
||||
let scripts = t.get(3).unwrap().as_array().unwrap();
|
||||
let previous_filter_header =
|
||||
|
@ -593,7 +597,7 @@ mod test {
|
|||
|
||||
let mut txmap = HashMap::new();
|
||||
let mut si = scripts.iter();
|
||||
for tx in block.txdata.iter().skip(1) {
|
||||
for tx in block.transactions().iter().skip(1) {
|
||||
for input in tx.input.iter() {
|
||||
txmap.insert(
|
||||
input.previous_output,
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use hashes::{sha256d, HashEngine};
|
||||
use internals::compact_size;
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use super::Weight;
|
||||
use crate::consensus::encode::WriteExt as _;
|
||||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
|
||||
use crate::merkle_tree::{MerkleNode as _, TxMerkleNode, WitnessMerkleNode};
|
||||
|
@ -27,7 +26,7 @@ use crate::transaction::{Transaction, TransactionExt as _, Wtxid};
|
|||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[doc(inline)]
|
||||
pub use primitives::block::{Version, BlockHash, Header, WitnessCommitment};
|
||||
pub use primitives::block::{Block, Checked, Unchecked, Validation, Version, BlockHash, Header, WitnessCommitment};
|
||||
#[doc(inline)]
|
||||
pub use units::block::{BlockHeight, BlockInterval, TooBigForRelativeBlockHeightError};
|
||||
|
||||
|
@ -73,11 +72,6 @@ crate::internal_macros::define_extension_trait! {
|
|||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for super::Header {}
|
||||
}
|
||||
|
||||
impl Encodable for Version {
|
||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
self.to_consensus().consensus_encode(w)
|
||||
|
@ -90,150 +84,202 @@ impl Decodable for Version {
|
|||
}
|
||||
}
|
||||
|
||||
/// Bitcoin block.
|
||||
///
|
||||
/// A collection of transactions with an attached proof of work.
|
||||
///
|
||||
/// See [Bitcoin Wiki: Block][wiki-block] for more information.
|
||||
///
|
||||
/// [wiki-block]: https://en.bitcoin.it/wiki/Block
|
||||
///
|
||||
/// ### Bitcoin Core References
|
||||
///
|
||||
/// * [CBlock definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L62)
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Block {
|
||||
/// The block header
|
||||
pub header: Header,
|
||||
/// List of transactions contained in the block
|
||||
pub txdata: Vec<Transaction>,
|
||||
/// Extension functionality for the [`Block<Unchecked>`] type.
|
||||
pub trait BlockUncheckedExt: sealed::Sealed {
|
||||
/// Validates (or checks) a block.
|
||||
///
|
||||
/// We define valid as:
|
||||
///
|
||||
/// * The Merkle root of the header matches Merkle root of the transaction list.
|
||||
/// * The witness commitment in coinbase matches the transaction list.
|
||||
fn validate(self) -> Result<Block<Checked>, InvalidBlockError>;
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(Block, header, txdata);
|
||||
impl BlockUncheckedExt for Block<Unchecked> {
|
||||
fn validate(self) -> Result<Block<Checked>, InvalidBlockError> {
|
||||
let (header, transactions) = self.into_parts();
|
||||
|
||||
impl Block {
|
||||
/// Returns the block hash.
|
||||
pub fn block_hash(&self) -> BlockHash { self.header.block_hash() }
|
||||
|
||||
/// Checks if Merkle root of header matches Merkle root of the transaction list.
|
||||
pub fn check_merkle_root(&self) -> bool {
|
||||
match self.compute_merkle_root() {
|
||||
Some(merkle_root) => self.header.merkle_root == merkle_root,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if witness commitment in coinbase matches the transaction list.
|
||||
pub fn check_witness_commitment(&self) -> bool {
|
||||
// Consists of OP_RETURN, OP_PUSHBYTES_36, and four "witness header" bytes.
|
||||
const MAGIC: [u8; 6] = [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
|
||||
// Witness commitment is optional if there are no transactions using SegWit in the block.
|
||||
if self.txdata.iter().all(|t| t.input.iter().all(|i| i.witness.is_empty())) {
|
||||
return true;
|
||||
if !check_merkle_root(&header, &transactions) {
|
||||
return Err(InvalidBlockError::InvalidMerkleRoot);
|
||||
}
|
||||
|
||||
if self.txdata.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let coinbase = &self.txdata[0];
|
||||
if !coinbase.is_coinbase() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Commitment is in the last output that starts with magic bytes.
|
||||
if let Some(pos) = coinbase
|
||||
.output
|
||||
.iter()
|
||||
.rposition(|o| o.script_pubkey.len() >= 38 && o.script_pubkey.as_bytes()[0..6] == MAGIC)
|
||||
{
|
||||
let bytes = <[u8; 32]>::try_from(&coinbase.output[pos].script_pubkey.as_bytes()[6..38])
|
||||
.unwrap();
|
||||
let commitment = WitnessCommitment::from_byte_array(bytes);
|
||||
// Witness reserved value is in coinbase input witness.
|
||||
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
|
||||
if witness_vec.len() == 1 && witness_vec[0].len() == 32 {
|
||||
if let Some(witness_root) = self.witness_root() {
|
||||
return commitment
|
||||
== Self::compute_witness_commitment(witness_root, witness_vec[0]);
|
||||
}
|
||||
match check_witness_commitment(&transactions) {
|
||||
(false, _) => Err(InvalidBlockError::InvalidWitnessCommitment),
|
||||
(true, witness_root) => {
|
||||
let block = Block::new_unchecked(header, transactions);
|
||||
Ok(block.assume_checked(witness_root))
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the transaction Merkle root.
|
||||
pub fn compute_merkle_root(&self) -> Option<TxMerkleNode> {
|
||||
let hashes = self.txdata.iter().map(|obj| obj.compute_txid());
|
||||
TxMerkleNode::calculate_root(hashes)
|
||||
}
|
||||
/// Computes the Merkle root for a list of transactions.
|
||||
pub fn compute_merkle_root(transactions: &[Transaction]) -> Option<TxMerkleNode> {
|
||||
let hashes = transactions.iter().map(|obj| obj.compute_txid());
|
||||
TxMerkleNode::calculate_root(hashes)
|
||||
}
|
||||
|
||||
/// Computes the witness commitment for the block's transaction list.
|
||||
pub fn compute_witness_commitment(
|
||||
witness_root: WitnessMerkleNode,
|
||||
witness_reserved_value: &[u8],
|
||||
) -> WitnessCommitment {
|
||||
/// Computes the witness commitment for a list of transactions.
|
||||
pub fn compute_witness_commitment(
|
||||
transactions: &[Transaction],
|
||||
witness_reserved_value: &[u8],
|
||||
) -> Option<(WitnessMerkleNode, WitnessCommitment)> {
|
||||
compute_witness_root(transactions).map(|witness_root| {
|
||||
let mut encoder = sha256d::Hash::engine();
|
||||
witness_root.consensus_encode(&mut encoder).expect("engines don't error");
|
||||
encoder.input(witness_reserved_value);
|
||||
WitnessCommitment::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
|
||||
let witness_commitment =
|
||||
WitnessCommitment::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array());
|
||||
(witness_root, witness_commitment)
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the Merkle root of transactions hashed for witness.
|
||||
pub fn compute_witness_root(transactions: &[Transaction]) -> Option<WitnessMerkleNode> {
|
||||
let hashes = transactions.iter().enumerate().map(|(i, t)| {
|
||||
if i == 0 {
|
||||
// Replace the first hash with zeroes.
|
||||
Wtxid::COINBASE
|
||||
} else {
|
||||
t.compute_wtxid()
|
||||
}
|
||||
});
|
||||
WitnessMerkleNode::calculate_root(hashes)
|
||||
}
|
||||
|
||||
/// Checks if Merkle root of header matches Merkle root of the transaction list.
|
||||
fn check_merkle_root(header: &Header, transactions: &[Transaction]) -> bool {
|
||||
match compute_merkle_root(transactions) {
|
||||
Some(merkle_root) => header.merkle_root == merkle_root,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if witness commitment in coinbase matches the transaction list.
|
||||
// Returns the Merkle root if it was computed (so it can be cached in `assume_checked`).
|
||||
fn check_witness_commitment(transactions: &[Transaction]) -> (bool, Option<WitnessMerkleNode>) {
|
||||
// Witness commitment is optional if there are no transactions using SegWit in the block.
|
||||
if transactions.iter().all(|t| t.input.iter().all(|i| i.witness.is_empty())) {
|
||||
return (true, None);
|
||||
}
|
||||
|
||||
/// Computes the Merkle root of transactions hashed for witness.
|
||||
pub fn witness_root(&self) -> Option<WitnessMerkleNode> {
|
||||
let hashes = self.txdata.iter().enumerate().map(|(i, t)| {
|
||||
if i == 0 {
|
||||
// Replace the first hash with zeroes.
|
||||
Wtxid::COINBASE
|
||||
} else {
|
||||
t.compute_wtxid()
|
||||
}
|
||||
});
|
||||
WitnessMerkleNode::calculate_root(hashes)
|
||||
if transactions.is_empty() {
|
||||
return (false, None);
|
||||
}
|
||||
|
||||
if transactions[0].is_coinbase() {
|
||||
let coinbase = transactions[0].clone();
|
||||
if let Some(commitment) = witness_commitment_from_coinbase(&coinbase) {
|
||||
// Witness reserved value is in coinbase input witness.
|
||||
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
|
||||
if witness_vec.len() == 1 && witness_vec[0].len() == 32 {
|
||||
if let Some((witness_root, witness_commitment)) =
|
||||
compute_witness_commitment(transactions, witness_vec[0])
|
||||
{
|
||||
if commitment == witness_commitment {
|
||||
return (true, Some(witness_root));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(false, None)
|
||||
}
|
||||
|
||||
fn witness_commitment_from_coinbase(coinbase: &Transaction) -> Option<WitnessCommitment> {
|
||||
// Consists of OP_RETURN, OP_PUSHBYTES_36, and four "witness header" bytes.
|
||||
const MAGIC: [u8; 6] = [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
|
||||
|
||||
if !coinbase.is_coinbase() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Commitment is in the last output that starts with magic bytes.
|
||||
if let Some(pos) = coinbase
|
||||
.output
|
||||
.iter()
|
||||
.rposition(|o| o.script_pubkey.len() >= 38 && o.script_pubkey.as_bytes()[0..6] == MAGIC)
|
||||
{
|
||||
let bytes =
|
||||
<[u8; 32]>::try_from(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
|
||||
Some(WitnessCommitment::from_byte_array(bytes))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension functionality for the [`Block<Checked>`] type.
|
||||
pub trait BlockCheckedExt: sealed::Sealed {
|
||||
/// Constructs a new [`Block`].
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Return the block if it is valid, `None` if not. See [`Block::validate`].
|
||||
fn new_checked(
|
||||
header: Header,
|
||||
transactions: Vec<Transaction>,
|
||||
) -> Result<Block<Checked>, InvalidBlockError>;
|
||||
|
||||
/// Returns the transaction Merkle root.
|
||||
fn merkle_root(&self) -> TxMerkleNode;
|
||||
|
||||
/// Returns the Merkle root of transactions hashed for witness.
|
||||
///
|
||||
/// This value was computed during block validation and was cached at that time.
|
||||
fn witness_root(&mut self) -> Option<WitnessMerkleNode>;
|
||||
|
||||
/// Returns the weight of the block.
|
||||
///
|
||||
/// > Block weight is defined as Base size * 3 + Total size.
|
||||
pub fn weight(&self) -> Weight {
|
||||
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
|
||||
let wu = self.base_size() * 3 + self.total_size();
|
||||
Weight::from_wu_usize(wu)
|
||||
}
|
||||
|
||||
/// Returns the base block size.
|
||||
///
|
||||
/// > Base size is the block size in bytes with the original transaction serialization without
|
||||
/// > any witness-related data, as seen by a non-upgraded node.
|
||||
fn base_size(&self) -> usize {
|
||||
let mut size = Header::SIZE;
|
||||
|
||||
size += compact_size::encoded_size(self.txdata.len());
|
||||
size += self.txdata.iter().map(|tx| tx.base_size()).sum::<usize>();
|
||||
|
||||
size
|
||||
}
|
||||
fn weight(&self) -> Weight;
|
||||
|
||||
/// Returns the total block size.
|
||||
///
|
||||
/// > Total size is the block size in bytes with transactions serialized as described in BIP144,
|
||||
/// > including base data and witness data.
|
||||
pub fn total_size(&self) -> usize {
|
||||
fn total_size(&self) -> usize;
|
||||
|
||||
/// Returns the coinbase transaction, if one is present.
|
||||
fn coinbase(&self) -> Option<&Transaction>;
|
||||
|
||||
/// Returns the block height, as encoded in the coinbase transaction according to BIP34.
|
||||
fn bip34_block_height(&self) -> Result<u64, Bip34Error>;
|
||||
}
|
||||
|
||||
impl BlockCheckedExt for Block<Checked> {
|
||||
fn new_checked(
|
||||
header: Header,
|
||||
transactions: Vec<Transaction>,
|
||||
) -> Result<Block<Checked>, InvalidBlockError> {
|
||||
let block = Block::new_unchecked(header, transactions);
|
||||
block.validate()
|
||||
}
|
||||
|
||||
fn merkle_root(&self) -> TxMerkleNode { self.header().merkle_root }
|
||||
|
||||
fn witness_root(&mut self) -> Option<WitnessMerkleNode> { self.cached_witness_root() }
|
||||
|
||||
fn weight(&self) -> Weight {
|
||||
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
|
||||
let wu = block_base_size(self.transactions()) * 3 + self.total_size();
|
||||
Weight::from_wu_usize(wu)
|
||||
}
|
||||
|
||||
fn total_size(&self) -> usize {
|
||||
let mut size = Header::SIZE;
|
||||
|
||||
size += compact_size::encoded_size(self.txdata.len());
|
||||
size += self.txdata.iter().map(|tx| tx.total_size()).sum::<usize>();
|
||||
size += compact_size::encoded_size(self.transactions().len());
|
||||
size += self.transactions().iter().map(|tx| tx.total_size()).sum::<usize>();
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
/// Returns the coinbase transaction, if one is present.
|
||||
pub fn coinbase(&self) -> Option<&Transaction> { self.txdata.first() }
|
||||
fn coinbase(&self) -> Option<&Transaction> { self.transactions().first() }
|
||||
|
||||
/// Returns the block height, as encoded in the coinbase transaction according to BIP34.
|
||||
pub fn bip34_block_height(&self) -> Result<u64, Bip34Error> {
|
||||
fn bip34_block_height(&self) -> Result<u64, Bip34Error> {
|
||||
// Citing the spec:
|
||||
// Add height as the first item in the coinbase transaction's scriptSig,
|
||||
// and increase block version to 2. The format of the height is
|
||||
|
@ -243,7 +289,7 @@ impl Block {
|
|||
// number (including a sign bit). Height is the height of the mined
|
||||
// block in the block chain, where the genesis block is height zero (0).
|
||||
|
||||
if self.header.version < Version::TWO {
|
||||
if self.header().version < Version::TWO {
|
||||
return Err(Bip34Error::Unsupported);
|
||||
}
|
||||
|
||||
|
@ -264,14 +310,95 @@ impl Block {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Block> for BlockHash {
|
||||
fn from(block: Block) -> BlockHash { block.block_hash() }
|
||||
fn block_base_size(transactions: &[Transaction]) -> usize {
|
||||
let mut size = Header::SIZE;
|
||||
|
||||
size += compact_size::encoded_size(transactions.len());
|
||||
size += transactions.iter().map(|tx| tx.base_size()).sum::<usize>();
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
impl From<&Block> for BlockHash {
|
||||
fn from(block: &Block) -> BlockHash { block.block_hash() }
|
||||
impl Encodable for Block<Unchecked> {
|
||||
#[inline]
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
// TODO: Should we be able to encode without cloning?
|
||||
// This is ok, we decode as unchecked anyway.
|
||||
let block = self.clone().assume_checked(None);
|
||||
block.consensus_encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Block<Checked> {
|
||||
#[inline]
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
let mut len = 0;
|
||||
len += self.header().consensus_encode(w)?;
|
||||
|
||||
let transactions = self.transactions();
|
||||
len += w.emit_compact_size(transactions.len())?;
|
||||
for c in transactions.iter() {
|
||||
len += c.consensus_encode(w)?;
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Block<Unchecked> {
|
||||
#[inline]
|
||||
fn consensus_decode_from_finite_reader<R: io::BufRead + ?Sized>(
|
||||
r: &mut R,
|
||||
) -> Result<Block, encode::Error> {
|
||||
let header = Decodable::consensus_decode_from_finite_reader(r)?;
|
||||
let transactions = Decodable::consensus_decode_from_finite_reader(r)?;
|
||||
|
||||
Ok(Block::new_unchecked(header, transactions))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consensus_decode<R: io::BufRead + ?Sized>(r: &mut R) -> Result<Block, encode::Error> {
|
||||
let mut r = r.take(internals::ToU64::to_u64(encode::MAX_VEC_SIZE));
|
||||
let header = Decodable::consensus_decode(&mut r)?;
|
||||
let transactions = Decodable::consensus_decode(&mut r)?;
|
||||
|
||||
Ok(Block::new_unchecked(header, transactions))
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
/// Seals the extension traits.
|
||||
pub trait Sealed {}
|
||||
impl Sealed for super::Header {}
|
||||
impl<V: super::Validation> Sealed for super::Block<V> {}
|
||||
}
|
||||
|
||||
/// Invalid block error.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum InvalidBlockError {
|
||||
/// Header Merkle root does not match the calculated Merkle root.
|
||||
InvalidMerkleRoot,
|
||||
/// The witness commitment in coinbase transaction does not match the calculated witness_root.
|
||||
InvalidWitnessCommitment,
|
||||
}
|
||||
|
||||
internals::impl_from_infallible!(InvalidBlockError);
|
||||
|
||||
impl fmt::Display for InvalidBlockError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use InvalidBlockError::*;
|
||||
|
||||
match *self {
|
||||
InvalidMerkleRoot => write!(f, "header Merkle root does not match the calculated Merkle root"),
|
||||
InvalidWitnessCommitment => write!(f, "the witness commitment in coinbase transaction does not match the calculated witness_root"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for InvalidBlockError {}
|
||||
|
||||
/// An error when looking up a BIP34 block height.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
|
@ -354,13 +481,6 @@ impl std::error::Error for ValidationError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for Block {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
Ok(Block { header: Header::arbitrary(u)?, txdata: Vec::<Transaction>::arbitrary(u)? })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::test_hex_unwrap as hex;
|
||||
|
@ -368,14 +488,31 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use crate::consensus::encode::{deserialize, serialize};
|
||||
use crate::{CompactTarget, Network, TestnetVersion};
|
||||
use crate::pow::test_utils::{u128_to_work, u64_to_work};
|
||||
use crate::{block, CompactTarget, Network, TestnetVersion};
|
||||
|
||||
#[test]
|
||||
fn static_vector() {
|
||||
// testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b
|
||||
let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw");
|
||||
let block: Block = deserialize(&segwit_block[..]).expect("failed to deserialize block");
|
||||
let (header, transactions) = block.into_parts();
|
||||
|
||||
assert!(block::check_merkle_root(&header, &transactions));
|
||||
let block = Block::new_unchecked(header, transactions).assume_checked(None);
|
||||
|
||||
// Same as `block.check_merkle_root` but do it explicitly.
|
||||
let hashes_iter = block.transactions().iter().map(|obj| obj.compute_txid());
|
||||
let from_iter = TxMerkleNode::calculate_root(hashes_iter.clone());
|
||||
assert_eq!(from_iter, Some(block.header().merkle_root));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coinbase_and_bip34() {
|
||||
// testnet block 100,000
|
||||
const BLOCK_HEX: &str = "0200000035ab154183570282ce9afc0b494c9fc6a3cfea05aa8c1add2ecc56490000000038ba3d78e4500a5a7570dbe61960398add4410d278b21cd9708e6d9743f374d544fc055227f1001c29c1ea3b0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3703a08601000427f1001c046a510100522cfabe6d6d0000000000000000000068692066726f6d20706f6f6c7365727665726aac1eeeed88ffffffff0100f2052a010000001976a914912e2b234f941f30b18afbb4fa46171214bf66c888ac00000000";
|
||||
let block: Block = deserialize(&hex!(BLOCK_HEX)).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
|
||||
let cb_txid = "d574f343976d8e70d91cb278d21044dd8a396019e6db70755a0a50e4783dba38";
|
||||
assert_eq!(block.coinbase().unwrap().compute_txid().to_string(), cb_txid);
|
||||
|
@ -385,24 +522,28 @@ mod tests {
|
|||
// block with 3-byte bip34 push for height 0x03010000 (non-minimal 1)
|
||||
const BAD_HEX: &str = "0200000035ab154183570282ce9afc0b494c9fc6a3cfea05aa8c1add2ecc56490000000038ba3d78e4500a5a7570dbe61960398add4410d278b21cd9708e6d9743f374d544fc055227f1001c29c1ea3b0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3703010000000427f1001c046a510100522cfabe6d6d0000000000000000000068692066726f6d20706f6f6c7365727665726aac1eeeed88ffffffff0100f2052a010000001976a914912e2b234f941f30b18afbb4fa46171214bf66c888ac00000000";
|
||||
let bad: Block = deserialize(&hex!(BAD_HEX)).unwrap();
|
||||
let bad = bad.assume_checked(None);
|
||||
|
||||
assert_eq!(bad.bip34_block_height(), Err(super::Bip34Error::NonMinimalPush));
|
||||
|
||||
// Block 15 on Testnet4 has height of 0x5f (15 PUSHNUM)
|
||||
const BLOCK_HEX_SMALL_HEIGHT_15: &str = "000000200fd8c4c1e88f313b561b2724542ff9be1bc54a7dab8db8ef6359d48a00000000705bf9145e6d3c413702cc61f32e4e7bfe3117b1eb928071a59adcf75694a3fb07d83866ffff001dcf4c5e8401010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff095f00062f4077697a2fffffffff0200f2052a010000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
let block: Block = deserialize(&hex!(BLOCK_HEX_SMALL_HEIGHT_15)).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
|
||||
assert_eq!(block.bip34_block_height(), Ok(15));
|
||||
|
||||
// Block 42 on Testnet4 has height of 0x012a (42)
|
||||
const BLOCK_HEX_SMALL_HEIGHT_42: &str = "000000202803addb5a3f42f3e8d6c8536598b2d872b04f3b4f0698c26afdb17300000000463dd9a37a5d3d5c05f9c80a1485b41f1f513dee00338bbc33f5a6e836fce0345dda3866ffff001d872b9def01010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff09012a062f4077697a2fffffffff0200f2052a010000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
let block: Block = deserialize(&hex!(BLOCK_HEX_SMALL_HEIGHT_42)).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
|
||||
assert_eq!(block.bip34_block_height(), Ok(42));
|
||||
|
||||
// Block 42 on Testnet4 using OP_PUSHDATA1 0x4c012a (42) instead of 0x012a (42)
|
||||
const BLOCK_HEX_SMALL_HEIGHT_42_WRONG: &str = "000000202803addb5a3f42f3e8d6c8536598b2d872b04f3b4f0698c26afdb17300000000463dd9a37a5d3d5c05f9c80a1485b41f1f513dee00338bbc33f5a6e836fce0345dda3866ffff001d872b9def01010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0a4c012a062f4077697a2fffffffff0200f2052a010000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
let block: Block = deserialize(&hex!(BLOCK_HEX_SMALL_HEIGHT_42_WRONG)).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
|
||||
assert_eq!(block.bip34_block_height(), Err(super::Bip34Error::NonMinimalPush));
|
||||
|
||||
|
@ -410,6 +551,7 @@ mod tests {
|
|||
// this is an overflow for ScriptNum (i32) parsing
|
||||
const BLOCK_HEX_5_BYTE_HEIGHT: &str = "000000202803addb5a3f42f3e8d6c8536598b2d872b04f3b4f0698c26afdb17300000000463dd9a37a5d3d5c05f9c80a1485b41f1f513dee00338bbc33f5a6e836fce0345dda3866ffff001d872b9def01010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0d052a2a2a2a2a062f4077697a2fffffffff0200f2052a010000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
let block: Block = deserialize(&hex!(BLOCK_HEX_5_BYTE_HEIGHT)).unwrap();
|
||||
let block = block.assume_checked(None);
|
||||
|
||||
assert_eq!(block.bip34_block_height(), Err(super::Bip34Error::NotPresent));
|
||||
}
|
||||
|
@ -430,32 +572,42 @@ mod tests {
|
|||
|
||||
assert!(decode.is_ok());
|
||||
assert!(bad_decode.is_err());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.header.version, Version::from_consensus(1));
|
||||
assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash);
|
||||
assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
|
||||
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
|
||||
assert_eq!(real_decode.header.time, 1231965655);
|
||||
assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(486604799));
|
||||
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||
assert_eq!(real_decode.header.work(), work);
|
||||
|
||||
let (header, transactions) = decode.unwrap().into_parts();
|
||||
// should be also ok for a non-witness block as commitment is optional in that case
|
||||
let (witness_commitment_matches, witness_root) =
|
||||
block::check_witness_commitment(&transactions);
|
||||
assert!(witness_commitment_matches);
|
||||
|
||||
let real_decode =
|
||||
Block::new_unchecked(header, transactions.clone()).assume_checked(witness_root);
|
||||
|
||||
assert_eq!(real_decode.header().version, Version::from_consensus(1));
|
||||
assert_eq!(serialize(&real_decode.header().prev_blockhash), prevhash);
|
||||
assert_eq!(
|
||||
real_decode.header.validate_pow(real_decode.header.target()).unwrap(),
|
||||
real_decode.header().merkle_root,
|
||||
block::compute_merkle_root(&transactions).unwrap()
|
||||
);
|
||||
assert_eq!(serialize(&real_decode.header().merkle_root), merkle);
|
||||
assert_eq!(real_decode.header().time, 1231965655);
|
||||
assert_eq!(real_decode.header().bits, CompactTarget::from_consensus(486604799));
|
||||
assert_eq!(real_decode.header().nonce, 2067413810);
|
||||
assert_eq!(real_decode.header().work(), work);
|
||||
|
||||
assert_eq!(real_decode.header().difficulty(¶ms), 1);
|
||||
assert_eq!(real_decode.header().difficulty_float(¶ms), 1.0);
|
||||
|
||||
assert_eq!(
|
||||
real_decode.header().validate_pow(real_decode.header().target()).unwrap(),
|
||||
real_decode.block_hash()
|
||||
);
|
||||
assert_eq!(real_decode.header.difficulty(¶ms), 1);
|
||||
assert_eq!(real_decode.header.difficulty_float(¶ms), 1.0);
|
||||
|
||||
assert_eq!(real_decode.total_size(), some_block.len());
|
||||
assert_eq!(real_decode.base_size(), some_block.len());
|
||||
assert_eq!(block_base_size(real_decode.transactions()), some_block.len());
|
||||
assert_eq!(
|
||||
real_decode.weight(),
|
||||
Weight::from_non_witness_data_size(some_block.len().to_u64())
|
||||
);
|
||||
|
||||
// should be also ok for a non-witness block as commitment is optional in that case
|
||||
assert!(real_decode.check_witness_commitment());
|
||||
|
||||
assert_eq!(serialize(&real_decode), some_block);
|
||||
}
|
||||
|
||||
|
@ -472,28 +624,37 @@ mod tests {
|
|||
let work = u64_to_work(0x257c3becdacc64_u64);
|
||||
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.header.version, Version::from_consensus(0x2000_0000)); // VERSIONBITS but no bits set
|
||||
assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash);
|
||||
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
|
||||
assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
|
||||
assert_eq!(real_decode.header.time, 1472004949);
|
||||
assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(0x1a06d450));
|
||||
assert_eq!(real_decode.header.nonce, 1879759182);
|
||||
assert_eq!(real_decode.header.work(), work);
|
||||
|
||||
let (header, transactions) = decode.unwrap().into_parts();
|
||||
let (witness_commitment_matches, witness_root) =
|
||||
block::check_witness_commitment(&transactions);
|
||||
assert!(witness_commitment_matches);
|
||||
|
||||
let real_decode =
|
||||
Block::new_unchecked(header, transactions.clone()).assume_checked(witness_root);
|
||||
|
||||
assert_eq!(real_decode.header().version, Version::from_consensus(0x2000_0000)); // VERSIONBITS but no bits set
|
||||
assert_eq!(serialize(&real_decode.header().prev_blockhash), prevhash);
|
||||
assert_eq!(serialize(&real_decode.header().merkle_root), merkle);
|
||||
assert_eq!(
|
||||
real_decode.header.validate_pow(real_decode.header.target()).unwrap(),
|
||||
real_decode.header().merkle_root,
|
||||
block::compute_merkle_root(&transactions).unwrap()
|
||||
);
|
||||
assert_eq!(real_decode.header().time, 1472004949);
|
||||
assert_eq!(real_decode.header().bits, CompactTarget::from_consensus(0x1a06d450));
|
||||
assert_eq!(real_decode.header().nonce, 1879759182);
|
||||
assert_eq!(real_decode.header().work(), work);
|
||||
assert_eq!(real_decode.header().difficulty(¶ms), 2456598);
|
||||
assert_eq!(real_decode.header().difficulty_float(¶ms), 2456598.4399242126);
|
||||
|
||||
assert_eq!(
|
||||
real_decode.header().validate_pow(real_decode.header().target()).unwrap(),
|
||||
real_decode.block_hash()
|
||||
);
|
||||
assert_eq!(real_decode.header.difficulty(¶ms), 2456598);
|
||||
assert_eq!(real_decode.header.difficulty_float(¶ms), 2456598.4399242126);
|
||||
|
||||
assert_eq!(real_decode.total_size(), segwit_block.len());
|
||||
assert_eq!(real_decode.base_size(), 4283);
|
||||
assert_eq!(block_base_size(real_decode.transactions()), 4283);
|
||||
assert_eq!(real_decode.weight(), Weight::from_wu(17168));
|
||||
|
||||
assert!(real_decode.check_witness_commitment());
|
||||
|
||||
assert_eq!(serialize(&real_decode), segwit_block);
|
||||
}
|
||||
|
||||
|
@ -502,14 +663,15 @@ mod tests {
|
|||
let block = hex!("ffffff7f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
let decode: Result<Block, _> = deserialize(&block);
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.header.version, Version::from_consensus(2147483647));
|
||||
|
||||
let real_decode = decode.unwrap().assume_checked(None);
|
||||
assert_eq!(real_decode.header().version, Version::from_consensus(2147483647));
|
||||
|
||||
let block2 = hex!("000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
let decode2: Result<Block, _> = deserialize(&block2);
|
||||
assert!(decode2.is_ok());
|
||||
let real_decode2 = decode2.unwrap();
|
||||
assert_eq!(real_decode2.header.version, Version::from_consensus(-2147483648));
|
||||
let real_decode2 = decode2.unwrap().assume_checked(None);
|
||||
assert_eq!(real_decode2.header().version, Version::from_consensus(-2147483648));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
use hashes::sha256d;
|
||||
|
||||
use crate::block::{self, Block};
|
||||
use crate::block::{self, Block, Checked};
|
||||
use crate::internal_macros::{impl_array_newtype, impl_array_newtype_stringify};
|
||||
use crate::locktime::absolute;
|
||||
use crate::network::{Network, Params};
|
||||
|
@ -119,15 +119,16 @@ fn bitcoin_genesis_tx(params: &Params) -> Transaction {
|
|||
}
|
||||
|
||||
/// Constructs and returns the genesis block.
|
||||
pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
||||
pub fn genesis_block(params: impl AsRef<Params>) -> Block<Checked> {
|
||||
let params = params.as_ref();
|
||||
let txdata = vec![bitcoin_genesis_tx(params)];
|
||||
let hash: sha256d::Hash = txdata[0].compute_txid().into();
|
||||
let transactions = vec![bitcoin_genesis_tx(params)];
|
||||
let hash: sha256d::Hash = transactions[0].compute_txid().into();
|
||||
let merkle_root: crate::TxMerkleNode = hash.into();
|
||||
let witness_root = block::compute_witness_root(&transactions);
|
||||
|
||||
match params.network {
|
||||
Network::Bitcoin => Block {
|
||||
header: block::Header {
|
||||
Network::Bitcoin => Block::new_unchecked(
|
||||
block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
|
||||
merkle_root,
|
||||
|
@ -135,10 +136,11 @@ pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
|||
bits: CompactTarget::from_consensus(0x1d00ffff),
|
||||
nonce: 2083236893,
|
||||
},
|
||||
txdata,
|
||||
},
|
||||
Network::Testnet(TestnetVersion::V3) => Block {
|
||||
header: block::Header {
|
||||
transactions,
|
||||
)
|
||||
.assume_checked(witness_root),
|
||||
Network::Testnet(TestnetVersion::V3) => Block::new_unchecked(
|
||||
block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
|
||||
merkle_root,
|
||||
|
@ -146,10 +148,11 @@ pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
|||
bits: CompactTarget::from_consensus(0x1d00ffff),
|
||||
nonce: 414098458,
|
||||
},
|
||||
txdata,
|
||||
},
|
||||
Network::Testnet(TestnetVersion::V4) => Block {
|
||||
header: block::Header {
|
||||
transactions,
|
||||
)
|
||||
.assume_checked(witness_root),
|
||||
Network::Testnet(TestnetVersion::V4) => Block::new_unchecked(
|
||||
block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
|
||||
merkle_root,
|
||||
|
@ -157,10 +160,11 @@ pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
|||
bits: CompactTarget::from_consensus(0x1d00ffff),
|
||||
nonce: 393743547,
|
||||
},
|
||||
txdata,
|
||||
},
|
||||
Network::Signet => Block {
|
||||
header: block::Header {
|
||||
transactions,
|
||||
)
|
||||
.assume_checked(witness_root),
|
||||
Network::Signet => Block::new_unchecked(
|
||||
block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
|
||||
merkle_root,
|
||||
|
@ -168,10 +172,11 @@ pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
|||
bits: CompactTarget::from_consensus(0x1e0377ae),
|
||||
nonce: 52613770,
|
||||
},
|
||||
txdata,
|
||||
},
|
||||
Network::Regtest => Block {
|
||||
header: block::Header {
|
||||
transactions,
|
||||
)
|
||||
.assume_checked(witness_root),
|
||||
Network::Regtest => Block::new_unchecked(
|
||||
block::Header {
|
||||
version: block::Version::ONE,
|
||||
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
|
||||
merkle_root,
|
||||
|
@ -179,8 +184,9 @@ pub fn genesis_block(params: impl AsRef<Params>) -> Block {
|
|||
bits: CompactTarget::from_consensus(0x207fffff),
|
||||
nonce: 2,
|
||||
},
|
||||
txdata,
|
||||
},
|
||||
transactions,
|
||||
)
|
||||
.assume_checked(witness_root),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,18 +313,18 @@ mod test {
|
|||
fn bitcoin_genesis_full_block() {
|
||||
let gen = genesis_block(¶ms::MAINNET);
|
||||
|
||||
assert_eq!(gen.header.version, block::Version::ONE);
|
||||
assert_eq!(gen.header.prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(gen.header().version, block::Version::ONE);
|
||||
assert_eq!(gen.header().prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(
|
||||
gen.header.merkle_root.to_string(),
|
||||
gen.header().merkle_root.to_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
||||
);
|
||||
|
||||
assert_eq!(gen.header.time, 1231006505);
|
||||
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1d00ffff));
|
||||
assert_eq!(gen.header.nonce, 2083236893);
|
||||
assert_eq!(gen.header().time, 1231006505);
|
||||
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1d00ffff));
|
||||
assert_eq!(gen.header().nonce, 2083236893);
|
||||
assert_eq!(
|
||||
gen.header.block_hash().to_string(),
|
||||
gen.header().block_hash().to_string(),
|
||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
||||
);
|
||||
}
|
||||
|
@ -326,17 +332,17 @@ mod test {
|
|||
#[test]
|
||||
fn testnet_genesis_full_block() {
|
||||
let gen = genesis_block(¶ms::TESTNET3);
|
||||
assert_eq!(gen.header.version, block::Version::ONE);
|
||||
assert_eq!(gen.header.prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(gen.header().version, block::Version::ONE);
|
||||
assert_eq!(gen.header().prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(
|
||||
gen.header.merkle_root.to_string(),
|
||||
gen.header().merkle_root.to_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
||||
);
|
||||
assert_eq!(gen.header.time, 1296688602);
|
||||
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1d00ffff));
|
||||
assert_eq!(gen.header.nonce, 414098458);
|
||||
assert_eq!(gen.header().time, 1296688602);
|
||||
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1d00ffff));
|
||||
assert_eq!(gen.header().nonce, 414098458);
|
||||
assert_eq!(
|
||||
gen.header.block_hash().to_string(),
|
||||
gen.header().block_hash().to_string(),
|
||||
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
||||
);
|
||||
}
|
||||
|
@ -344,17 +350,17 @@ mod test {
|
|||
#[test]
|
||||
fn signet_genesis_full_block() {
|
||||
let gen = genesis_block(¶ms::SIGNET);
|
||||
assert_eq!(gen.header.version, block::Version::ONE);
|
||||
assert_eq!(gen.header.prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(gen.header().version, block::Version::ONE);
|
||||
assert_eq!(gen.header().prev_blockhash, BlockHash::GENESIS_PREVIOUS_BLOCK_HASH);
|
||||
assert_eq!(
|
||||
gen.header.merkle_root.to_string(),
|
||||
gen.header().merkle_root.to_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
||||
);
|
||||
assert_eq!(gen.header.time, 1598918400);
|
||||
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1e0377ae));
|
||||
assert_eq!(gen.header.nonce, 52613770);
|
||||
assert_eq!(gen.header().time, 1598918400);
|
||||
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1e0377ae));
|
||||
assert_eq!(gen.header().nonce, 52613770);
|
||||
assert_eq!(
|
||||
gen.header.block_hash().to_string(),
|
||||
gen.header().block_hash().to_string(),
|
||||
"00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1263,7 +1263,7 @@ mod tests {
|
|||
use crate::network::Network;
|
||||
|
||||
let genesis = constants::genesis_block(Network::Bitcoin);
|
||||
assert!(genesis.txdata[0].is_coinbase());
|
||||
assert!(genesis.transactions()[0].is_coinbase());
|
||||
let tx_bytes = hex!("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000");
|
||||
let tx: Transaction = deserialize(&tx_bytes).unwrap();
|
||||
assert!(!tx.is_coinbase());
|
||||
|
|
|
@ -118,7 +118,10 @@ pub mod taproot;
|
|||
// that it is available in the event that we add some functionality there.
|
||||
#[doc(inline)]
|
||||
pub use primitives::{
|
||||
block::{BlockHash, Header as BlockHeader, WitnessCommitment},
|
||||
block::{
|
||||
Block, BlockHash, Checked as BlockChecked, Header as BlockHeader,
|
||||
Unchecked as BockUnchecked, Validation as BlockValidation, WitnessCommitment,
|
||||
},
|
||||
merkle_tree::{TxMerkleNode, WitnessMerkleNode},
|
||||
opcodes::Opcode,
|
||||
pow::CompactTarget, // No `pow` module outside of `primitives`.
|
||||
|
@ -157,7 +160,6 @@ pub use crate::{
|
|||
#[doc(inline)]
|
||||
pub use crate::{
|
||||
// Also, re-export types and modules from `blockdata` that don't come from `primitives`.
|
||||
blockdata::block::Block, // TODO: Move this after `Block` is in primitives.
|
||||
blockdata::locktime::{absolute, relative},
|
||||
blockdata::script::witness_program::{self, WitnessProgram},
|
||||
blockdata::script::witness_version::{self, WitnessVersion},
|
||||
|
|
|
@ -15,7 +15,7 @@ use internals::ToU64 as _;
|
|||
use io::{BufRead, Write};
|
||||
|
||||
use self::MerkleBlockError::*;
|
||||
use crate::block::{self, Block};
|
||||
use crate::block::{self, Block, Checked};
|
||||
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt, MAX_VEC_SIZE};
|
||||
use crate::merkle_tree::{MerkleNode as _, TxMerkleNode};
|
||||
use crate::prelude::Vec;
|
||||
|
@ -43,9 +43,9 @@ impl MerkleBlock {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use bitcoin::hash_types::Txid;
|
||||
/// use bitcoin::block::BlockUncheckedExt as _;
|
||||
/// use bitcoin::hex::FromHex;
|
||||
/// use bitcoin::{Block, MerkleBlock};
|
||||
/// use bitcoin::{Block, MerkleBlock, Txid};
|
||||
///
|
||||
/// // Block 80000
|
||||
/// let block_bytes = Vec::from_hex("01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad2\
|
||||
|
@ -59,6 +59,7 @@ impl MerkleBlock {
|
|||
/// 5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b6\
|
||||
/// 5d35549d88ac00000000").unwrap();
|
||||
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
|
||||
/// let block = block.validate().expect("valid block");
|
||||
///
|
||||
/// // Constructs a new Merkle block containing a single transaction
|
||||
/// let txid = "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2".parse::<Txid>().unwrap();
|
||||
|
@ -71,12 +72,13 @@ impl MerkleBlock {
|
|||
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
|
||||
/// assert_eq!(txid, matches[0]);
|
||||
/// ```
|
||||
pub fn from_block_with_predicate<F>(block: &Block, match_txids: F) -> Self
|
||||
pub fn from_block_with_predicate<F>(block: &Block<Checked>, match_txids: F) -> Self
|
||||
where
|
||||
F: Fn(&Txid) -> bool,
|
||||
{
|
||||
let block_txids: Vec<_> = block.txdata.iter().map(Transaction::compute_txid).collect();
|
||||
Self::from_header_txids_with_predicate(&block.header, &block_txids, match_txids)
|
||||
let block_txids: Vec<_> =
|
||||
block.transactions().iter().map(Transaction::compute_txid).collect();
|
||||
Self::from_header_txids_with_predicate(block.header(), &block_txids, match_txids)
|
||||
}
|
||||
|
||||
/// Constructs a new MerkleBlock from the block's header and txids, that contain proofs for specific txids.
|
||||
|
@ -511,6 +513,7 @@ mod tests {
|
|||
use {core::cmp, secp256k1::rand::prelude::*};
|
||||
|
||||
use super::*;
|
||||
use crate::block::{BlockUncheckedExt as _, Unchecked};
|
||||
use crate::consensus::encode;
|
||||
use crate::hash_types::Txid;
|
||||
use crate::hex::{test_hex_unwrap as hex, DisplayHex, FromHex};
|
||||
|
@ -673,14 +676,14 @@ mod tests {
|
|||
|
||||
let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t));
|
||||
|
||||
assert_eq!(merkle_block.header.block_hash(), block.block_hash());
|
||||
assert_eq!(merkle_block.header.block_hash(), block.clone().block_hash());
|
||||
|
||||
let mut matches: Vec<Txid> = vec![];
|
||||
let mut index: Vec<u32> = vec![];
|
||||
|
||||
assert_eq!(
|
||||
merkle_block.txn.extract_matches(&mut matches, &mut index).unwrap(),
|
||||
block.header.merkle_root
|
||||
block.header().merkle_root
|
||||
);
|
||||
assert_eq!(matches.len(), 2);
|
||||
|
||||
|
@ -703,14 +706,14 @@ mod tests {
|
|||
|
||||
let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t));
|
||||
|
||||
assert_eq!(merkle_block.header.block_hash(), block.block_hash());
|
||||
assert_eq!(merkle_block.header.block_hash(), block.clone().block_hash());
|
||||
|
||||
let mut matches: Vec<Txid> = vec![];
|
||||
let mut index: Vec<u32> = vec![];
|
||||
|
||||
assert_eq!(
|
||||
merkle_block.txn.extract_matches(&mut matches, &mut index).unwrap(),
|
||||
block.header.merkle_root
|
||||
block.header().merkle_root
|
||||
);
|
||||
assert_eq!(matches.len(), 0);
|
||||
assert_eq!(index.len(), 0);
|
||||
|
@ -731,9 +734,11 @@ mod tests {
|
|||
|
||||
/// Returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
|
||||
/// with 9 txs.
|
||||
fn get_block_13b8a() -> Block {
|
||||
fn get_block_13b8a() -> Block<Checked> {
|
||||
let block_hex = include_str!("../../tests/data/block_13b8a.hex");
|
||||
encode::deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap()
|
||||
let block: Block<Unchecked> =
|
||||
encode::deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap();
|
||||
block.validate().expect("block should be valid")
|
||||
}
|
||||
|
||||
macro_rules! check_calc_tree_width {
|
||||
|
|
|
@ -113,24 +113,3 @@ impl MerkleNode for WitnessMerkleNode {
|
|||
Self::from_byte_array(sha256d::Hash::from_engine(encoder).to_byte_array())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::block::Block;
|
||||
use crate::consensus::encode::deserialize;
|
||||
|
||||
#[test]
|
||||
fn static_vector() {
|
||||
// testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b
|
||||
let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw");
|
||||
let block: Block = deserialize(&segwit_block[..]).expect("failed to deserialize block");
|
||||
|
||||
assert!(block.check_merkle_root());
|
||||
|
||||
// Same as `block.check_merkle_root` but do it explicitly.
|
||||
let hashes_iter = block.txdata.iter().map(|obj| obj.compute_txid());
|
||||
let from_iter = TxMerkleNode::calculate_root(hashes_iter.clone());
|
||||
assert_eq!(from_iter, Some(block.header.merkle_root));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1784,7 +1784,7 @@ mod tests {
|
|||
use crate::constants::genesis_block;
|
||||
use crate::TxMerkleNode;
|
||||
let params = Params::new(crate::Network::Signet);
|
||||
let epoch_start = genesis_block(¶ms).header;
|
||||
let epoch_start = *genesis_block(¶ms).header();
|
||||
|
||||
// Block 2015, the only information used are `bits` and `time`
|
||||
let current = Header {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use bitcoin::block::{self, Block, BlockCheckedExt as _};
|
||||
use honggfuzz::fuzz;
|
||||
|
||||
fn do_test(data: &[u8]) {
|
||||
|
@ -9,12 +10,18 @@ fn do_test(data: &[u8]) {
|
|||
Ok(block) => {
|
||||
let ser = bitcoin::consensus::encode::serialize(&block);
|
||||
assert_eq!(&ser[..], data);
|
||||
let _ = block.bip34_block_height();
|
||||
block.block_hash();
|
||||
block.check_merkle_root();
|
||||
block.check_witness_commitment();
|
||||
block.weight();
|
||||
block.witness_root();
|
||||
|
||||
// Manually call all compute functions with unchecked block data.
|
||||
let (header, transactions) = block.into_parts();
|
||||
block::compute_merkle_root(&transactions);
|
||||
block::compute_witness_commitment(&transactions, &[]); // TODO: Is empty slice ok?
|
||||
block::compute_witness_root(&transactions);
|
||||
|
||||
if let Ok(block) = Block::new_checked(header, transactions) {
|
||||
let _ = block.bip34_block_height();
|
||||
block.block_hash();
|
||||
block.weight();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,146 @@
|
|||
//! these blocks and the blockchain.
|
||||
|
||||
use core::fmt;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use hashes::{sha256d, HashEngine as _};
|
||||
|
||||
use crate::merkle_tree::TxMerkleNode;
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::merkle_tree::WitnessMerkleNode;
|
||||
use crate::pow::CompactTarget;
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::prelude::Vec;
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::transaction::Transaction;
|
||||
|
||||
/// Marker for whether or not a block has been validated.
|
||||
///
|
||||
/// We define valid as:
|
||||
///
|
||||
/// * The Merkle root of the header matches Merkle root of the transaction list.
|
||||
/// * The witness commitment in coinbase matches the transaction list.
|
||||
///
|
||||
/// See `bitcoin::block::BlockUncheckedExt::validate()`.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub trait Validation: sealed::Validation + Sync + Send + Sized + Unpin {
|
||||
/// Indicates whether this `Validation` is `Checked` or not.
|
||||
const IS_CHECKED: bool;
|
||||
}
|
||||
|
||||
/// Bitcoin block.
|
||||
///
|
||||
/// A collection of transactions with an attached proof of work.
|
||||
///
|
||||
/// See [Bitcoin Wiki: Block][wiki-block] for more information.
|
||||
///
|
||||
/// [wiki-block]: https://en.bitcoin.it/wiki/Block
|
||||
///
|
||||
/// ### Bitcoin Core References
|
||||
///
|
||||
/// * [CBlock definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L62)
|
||||
#[cfg(feature = "alloc")]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Block<V = Unchecked>
|
||||
where
|
||||
V: Validation,
|
||||
{
|
||||
/// The block header
|
||||
header: Header,
|
||||
/// List of transactions contained in the block
|
||||
transactions: Vec<Transaction>,
|
||||
/// Cached witness root if its been computed.
|
||||
#[cfg_attr(feature = "serde", serde(skip_serializing))]
|
||||
witness_root: Option<WitnessMerkleNode>,
|
||||
/// Validation marker.
|
||||
marker: PhantomData<V>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Block<Unchecked> {
|
||||
/// Constructs a new `Block` without doing any validation.
|
||||
pub fn new_unchecked(header: Header, transactions: Vec<Transaction>) -> Block<Unchecked> {
|
||||
Block { header, transactions, witness_root: None, marker: PhantomData::<Unchecked> }
|
||||
}
|
||||
|
||||
/// Ignores block validation logic and just assumes you know what you are doing.
|
||||
///
|
||||
/// You should only use this function if you trust the block i.e., it comes from a trusted node.
|
||||
pub fn assume_checked(self, witness_root: Option<WitnessMerkleNode>) -> Block<Checked> {
|
||||
Block {
|
||||
header: self.header,
|
||||
transactions: self.transactions,
|
||||
witness_root,
|
||||
marker: PhantomData::<Checked>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decomposes block into its constituent parts.
|
||||
pub fn into_parts(self) -> (Header, Vec<Transaction>) { (self.header, self.transactions) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Block<Checked> {
|
||||
/// Gets a reference to the block header.
|
||||
pub fn header(&self) -> &Header { &self.header }
|
||||
|
||||
/// Gets a reference to the block's list of transactions.
|
||||
pub fn transactions(&self) -> &[Transaction] { &self.transactions }
|
||||
|
||||
/// Returns the cached witness root if one is present.
|
||||
///
|
||||
/// It is assumed that a block will have the witness root calculated and cached as part of the
|
||||
/// validation process.
|
||||
pub fn cached_witness_root(&self) -> Option<WitnessMerkleNode> { self.witness_root }
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<V: Validation> Block<V> {
|
||||
/// Returns the block hash.
|
||||
pub fn block_hash(&self) -> BlockHash { self.header.block_hash() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<Block> for BlockHash {
|
||||
fn from(block: Block) -> BlockHash { block.block_hash() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<&Block> for BlockHash {
|
||||
fn from(block: &Block) -> BlockHash { block.block_hash() }
|
||||
}
|
||||
|
||||
/// Marker that the block's merkle root has been successfully validated.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub enum Checked {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Validation for Checked {
|
||||
const IS_CHECKED: bool = true;
|
||||
}
|
||||
|
||||
/// Marker that the block's merkle root has not been validated.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub enum Unchecked {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Validation for Unchecked {
|
||||
const IS_CHECKED: bool = false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod sealed {
|
||||
/// Seals the block validation marker traits.
|
||||
pub trait Validation {}
|
||||
impl Validation for super::Checked {}
|
||||
impl Validation for super::Unchecked {}
|
||||
}
|
||||
|
||||
/// Bitcoin block header.
|
||||
///
|
||||
|
@ -167,6 +300,16 @@ impl BlockHash {
|
|||
pub const GENESIS_PREVIOUS_BLOCK_HASH: Self = Self::from_byte_array([0; 32]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'a> Arbitrary<'a> for Block {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let header = Header::arbitrary(u)?;
|
||||
let transactions = Vec::<Transaction>::arbitrary(u)?;
|
||||
Ok(Block::new_unchecked(header, transactions))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for Header {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
|
|
|
@ -53,6 +53,16 @@ pub use units::{
|
|||
weight::{self, Weight},
|
||||
};
|
||||
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::{
|
||||
block::{
|
||||
Block, Checked as BlockChecked, Unchecked as BlockUnchecked, Validation as BlockValidation,
|
||||
},
|
||||
locktime::{absolute, relative},
|
||||
transaction::{Transaction, TxIn, TxOut},
|
||||
witness::Witness,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use self::{
|
||||
block::{BlockHash, Header as BlockHeader, WitnessCommitment},
|
||||
|
@ -62,13 +72,6 @@ pub use self::{
|
|||
taproot::{TapBranchTag, TapLeafHash, TapLeafTag, TapNodeHash, TapTweakHash, TapTweakTag},
|
||||
transaction::{Txid, Wtxid},
|
||||
};
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::{
|
||||
locktime::{absolute, relative},
|
||||
transaction::{Transaction, TxIn, TxOut},
|
||||
witness::Witness,
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_imports)]
|
||||
|
|
Loading…
Reference in New Issue