refactor: Move the coinbase first check to a Block method
This commit is contained in:
parent
47bc02ea44
commit
5548dffd3b
|
|
@ -9,7 +9,7 @@ mod serialize;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::{error, sync::Arc};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use proptest_derive::Arbitrary;
|
use proptest_derive::Arbitrary;
|
||||||
|
|
@ -43,6 +43,10 @@ pub struct Block {
|
||||||
/// transaction in the chain is approximately 1.5 kB smaller.)
|
/// transaction in the chain is approximately 1.5 kB smaller.)
|
||||||
pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
|
pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
|
||||||
|
|
||||||
|
/// The error type for Block checks.
|
||||||
|
// TODO(jlusby): Error = Report ?
|
||||||
|
type Error = Box<dyn error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
/// Return the block height reported in the coinbase transaction, if any.
|
/// Return the block height reported in the coinbase transaction, if any.
|
||||||
pub fn coinbase_height(&self) -> Option<BlockHeight> {
|
pub fn coinbase_height(&self) -> Option<BlockHeight> {
|
||||||
|
|
@ -55,6 +59,31 @@ impl Block {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that there is exactly one coinbase transaction in `Block`, and that
|
||||||
|
/// the coinbase transaction is the first transaction in the block.
|
||||||
|
///
|
||||||
|
/// "The first (and only the first) transaction in a block is a coinbase
|
||||||
|
/// transaction, which collects and spends any miner subsidy and transaction
|
||||||
|
/// fees paid by transactions included in this block."[S 3.10][3.10]
|
||||||
|
///
|
||||||
|
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions
|
||||||
|
pub fn is_coinbase_first(&self) -> Result<(), Error> {
|
||||||
|
if self.coinbase_height().is_some() {
|
||||||
|
// No coinbase inputs in additional transactions allowed
|
||||||
|
if self
|
||||||
|
.transactions
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.any(|tx| tx.contains_coinbase_input())
|
||||||
|
{
|
||||||
|
Err("coinbase input found in additional transaction")?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("no coinbase transaction in block")?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Block> for BlockHeaderHash {
|
impl<'a> From<&'a Block> for BlockHeaderHash {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
|
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
|
||||||
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
|
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
#![allow(clippy::try_err)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
|
||||||
|
|
@ -53,31 +53,6 @@ pub(crate) fn node_time_check(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that there is exactly one coinbase transaction in `Block`, and that
|
|
||||||
/// the coinbase transaction is the first transaction in the block.
|
|
||||||
///
|
|
||||||
/// "The first (and only the first) transaction in a block is a coinbase
|
|
||||||
/// transaction, which collects and spends any miner subsidy and transaction
|
|
||||||
/// fees paid by transactions included in this block."[S 3.10][3.10]
|
|
||||||
///
|
|
||||||
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions
|
|
||||||
pub(crate) fn coinbase_is_first_check(block: &Block) -> Result<(), Error> {
|
|
||||||
if block.coinbase_height().is_some() {
|
|
||||||
// No coinbase inputs in additional transactions allowed
|
|
||||||
if block
|
|
||||||
.transactions
|
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.any(|tx| tx.contains_coinbase_input())
|
|
||||||
{
|
|
||||||
Err("coinbase input found in additional transaction")?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("no coinbase transaction in block")?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BlockVerifier<S> {
|
struct BlockVerifier<S> {
|
||||||
/// The underlying `ZebraState`, possibly wrapped in other services.
|
/// The underlying `ZebraState`, possibly wrapped in other services.
|
||||||
state_service: S,
|
state_service: S,
|
||||||
|
|
@ -123,7 +98,7 @@ where
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
node_time_check(block.header.time, now)?;
|
node_time_check(block.header.time, now)?;
|
||||||
block.header.is_equihash_solution_valid()?;
|
block.header.is_equihash_solution_valid()?;
|
||||||
coinbase_is_first_check(block.as_ref())?;
|
block.is_coinbase_first()?;
|
||||||
|
|
||||||
// `Tower::Buffer` requires a 1:1 relationship between `poll()`s
|
// `Tower::Buffer` requires a 1:1 relationship between `poll()`s
|
||||||
// and `call()`s, because it reserves a buffer slot in each
|
// and `call()`s, because it reserves a buffer slot in each
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue