add rules for blocks

TODO: the tests need re-ordering they are just all chucked in at the moment.
This commit is contained in:
Boog900 2023-10-24 23:02:19 +01:00
parent b727062e97
commit 2033a2d16c
No known key found for this signature in database
GPG key ID: 5401367FB7302004
11 changed files with 86 additions and 42 deletions

View file

@ -57,5 +57,3 @@ dirs = {version="5.0", optional = true}
# here to help cargo to pick a version - remove me # here to help cargo to pick a version - remove me
syn = "2.0.37" syn = "2.0.37"
[profile.dev]
opt-level = 3

View file

@ -132,7 +132,28 @@ where
if current_height % 500 == 0 { if current_height % 500 == 0 {
tracing::info!("Saving cache to: {}", save_file.display()); tracing::info!("Saving cache to: {}", save_file.display());
cache.write().unwrap().save(&save_file)?; cache.read().unwrap().save(&save_file)?;
let DatabaseResponse::BlockExtendedHeader(header) = database
.ready()
.await?
.call(DatabaseRequest::BlockExtendedHeader(
verified_block_info.height.into(),
))
.await?
else {
panic!();
};
assert_eq!(header.block_weight, verified_block_info.weight);
assert_eq!(
header.cumulative_difficulty,
verified_block_info.cumulative_difficulty
);
assert_eq!(
header.long_term_weight,
verified_block_info.long_term_weight
);
} }
} }
} }

View file

@ -15,7 +15,7 @@ use crate::{
ConsensusError, HardFork, ConsensusError, HardFork,
}; };
//mod checks; mod checks;
mod hash_worker; mod hash_worker;
mod miner_tx; mod miner_tx;
@ -127,6 +127,8 @@ where
tracing::debug!("got blockchain context: {:?}", context); tracing::debug!("got blockchain context: {:?}", context);
// TODO: reorder these tests so we do the cheap tests first.
let txs = if !txs.is_empty() { let txs = if !txs.is_empty() {
let VerifyTxResponse::BatchSetupOk(txs) = tx_verifier_svc let VerifyTxResponse::BatchSetupOk(txs) = tx_verifier_svc
.oneshot(VerifyTxRequest::BatchSetupVerifyBlock { .oneshot(VerifyTxRequest::BatchSetupVerifyBlock {
@ -159,6 +161,17 @@ where
let hashing_blob = block.serialize_hashable(); let hashing_blob = block.serialize_hashable();
checks::block_size_sanity_check(block.serialize().len(), context.effective_median_weight)?;
checks::block_weight_check(block_weight, context.median_weight_for_block_reward)?;
checks::check_amount_txs(block.txs.len())?;
checks::check_prev_id(&block, &context.top_hash)?;
if let Some(median_timestamp) = context.median_block_timestamp {
// will only be None for the first 60 blocks
checks::check_timestamp(&block, median_timestamp)?;
}
// do POW test last
let pow_hash = tokio::task::spawn_blocking(move || { let pow_hash = tokio::task::spawn_blocking(move || {
hash_worker::calculate_pow_hash( hash_worker::calculate_pow_hash(
&hashing_blob, &hashing_blob,
@ -169,6 +182,12 @@ where
.await .await
.unwrap()?; .unwrap()?;
checks::check_block_pow(&pow_hash, context.next_difficulty)?;
context
.current_hard_fork
.check_block_version_vote(&block.header)?;
Ok(VerifiedBlockInformation { Ok(VerifiedBlockInformation {
block_hash: block.hash(), block_hash: block.hash(),
block, block,

View file

@ -15,18 +15,22 @@ const BLOCK_FUTURE_TIME_LIMIT: u64 = 60 * 60 * 2;
/// Returns if the blocks POW hash is valid for the current difficulty. /// Returns if the blocks POW hash is valid for the current difficulty.
/// ///
/// See: https://cuprate.github.io/monero-book/consensus_rules/blocks/difficulty.html#checking-a-blocks-proof-of-work /// See: https://cuprate.github.io/monero-book/consensus_rules/blocks/difficulty.html#checking-a-blocks-proof-of-work
pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> bool { pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> Result<(), ConsensusError> {
let int_hash = U256::from_le_slice(hash); let int_hash = U256::from_le_slice(hash);
let difficulty = U256::from_u128(difficulty); let difficulty = U256::from_u128(difficulty);
int_hash.checked_mul(&difficulty).is_some().unwrap_u8() == 1 if int_hash.checked_mul(&difficulty).is_some().unwrap_u8() != 1 {
Err(ConsensusError::BlockPOWInvalid)
} else {
Ok(())
}
} }
/// Sanity check on the block blob size. /// Sanity check on the block blob size.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#block-weight-and-size /// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#block-weight-and-size
fn block_size_sanity_check( pub fn block_size_sanity_check(
block_blob_len: usize, block_blob_len: usize,
effective_median: usize, effective_median: usize,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
@ -37,10 +41,21 @@ fn block_size_sanity_check(
} }
} }
/// Sanity check on number of txs in the block.
///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#amount-of-transactions
pub fn check_amount_txs(number_none_miner_txs: usize) -> Result<(), ConsensusError> {
if number_none_miner_txs + 1 > 0x10000000 {
Err(ConsensusError::BlockIsTooLarge)
} else {
Ok(())
}
}
/// Sanity check on the block weight. /// Sanity check on the block weight.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#block-weight-and-siz /// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#block-weight-and-siz
fn block_weight_check( pub fn block_weight_check(
block_weight: usize, block_weight: usize,
median_for_block_reward: usize, median_for_block_reward: usize,
) -> Result<(), ConsensusError> { ) -> Result<(), ConsensusError> {
@ -54,7 +69,7 @@ fn block_weight_check(
/// Verifies the previous id is the last blocks hash /// Verifies the previous id is the last blocks hash
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#previous-id /// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#previous-id
fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), ConsensusError> { pub fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), ConsensusError> {
if &block.header.previous != top_hash { if &block.header.previous != top_hash {
Err(ConsensusError::BlockIsNotApartOfChain) Err(ConsensusError::BlockIsNotApartOfChain)
} else { } else {
@ -65,7 +80,7 @@ fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), ConsensusErro
/// Checks the blocks timestamp is in the valid range. /// Checks the blocks timestamp is in the valid range.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#timestamp /// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#timestamp
fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), ConsensusError> { pub fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), ConsensusError> {
if block.header.timestamp < median_timestamp if block.header.timestamp < median_timestamp
|| block.header.timestamp > current_time() + BLOCK_FUTURE_TIME_LIMIT || block.header.timestamp > current_time() + BLOCK_FUTURE_TIME_LIMIT
{ {

View file

@ -46,7 +46,8 @@ pub fn calculate_block_reward(
/// Checks the miner transactions version. /// Checks the miner transactions version.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#version /// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#version
fn check_tx_version(tx_version: &TxVersion, hf: &HardFork) -> Result<(), ConsensusError> { fn check_miner_tx_version(tx_version: &TxVersion, hf: &HardFork) -> Result<(), ConsensusError> {
// The TxVersion enum checks if the version is not 1 or 2
if hf >= &HardFork::V12 && tx_version != &TxVersion::RingCT { if hf >= &HardFork::V12 && tx_version != &TxVersion::RingCT {
Err(ConsensusError::MinerTransaction("Version invalid")) Err(ConsensusError::MinerTransaction("Version invalid"))
} else { } else {
@ -162,7 +163,7 @@ pub fn check_miner_tx(
hf: &HardFork, hf: &HardFork,
) -> Result<u64, ConsensusError> { ) -> Result<u64, ConsensusError> {
let tx_version = TxVersion::from_raw(tx.prefix.version)?; let tx_version = TxVersion::from_raw(tx.prefix.version)?;
check_tx_version(&tx_version, hf)?; check_miner_tx_version(&tx_version, hf)?;
if hf >= &HardFork::V12 && tx.rct_signatures.rct_type() != RctType::Null { if hf >= &HardFork::V12 && tx.rct_signatures.rct_type() != RctType::Null {
return Err(ConsensusError::MinerTransaction("RctType is not null")); return Err(ConsensusError::MinerTransaction("RctType is not null"));

View file

@ -137,7 +137,7 @@ pub struct BlockChainContext {
/// The current cumulative difficulty. /// The current cumulative difficulty.
pub cumulative_difficulty: u128, pub cumulative_difficulty: u128,
/// The current effective median block weight. /// The current effective median block weight.
effective_median_weight: usize, pub effective_median_weight: usize,
/// The median long term block weight. /// The median long term block weight.
median_long_term_weight: usize, median_long_term_weight: usize,
/// Median weight to use for block reward calculations. /// Median weight to use for block reward calculations.

View file

@ -18,28 +18,6 @@ const BLOCK_TIME_V2: Duration = Duration::from_secs(120);
const NUMB_OF_HARD_FORKS: usize = 16; const NUMB_OF_HARD_FORKS: usize = 16;
#[derive(Debug, Clone, Copy)]
pub struct BlockHFInfo {
pub version: HardFork,
pub vote: HardFork,
}
impl BlockHFInfo {
pub fn from_block_header(block_header: &BlockHeader) -> Result<BlockHFInfo, ConsensusError> {
BlockHFInfo::from_major_minor(block_header.major_version, block_header.minor_version)
}
pub fn from_major_minor(
major_version: u8,
minor_version: u8,
) -> Result<BlockHFInfo, ConsensusError> {
Ok(BlockHFInfo {
version: HardFork::from_version(&major_version)?,
vote: HardFork::from_vote(&minor_version),
})
}
}
/// Information about a given hard-fork. /// Information about a given hard-fork.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct HFInfo { pub struct HFInfo {
@ -187,8 +165,20 @@ impl HardFork {
/// Checks a blocks version and vote, assuming that `self` is the current hard-fork. /// Checks a blocks version and vote, assuming that `self` is the current hard-fork.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#version-and-vote /// https://cuprate.github.io/monero-book/consensus_rules/blocks.html#version-and-vote
pub fn check_block_version_vote(&self, block_hf_info: &BlockHFInfo) -> bool { pub fn check_block_version_vote(
self == &block_hf_info.version && &block_hf_info.vote >= self &self,
block_header: &BlockHeader,
) -> Result<(), ConsensusError> {
let version = HardFork::from_version(&block_header.major_version)?;
let vote = HardFork::from_vote(&block_header.minor_version);
if self == &version && &vote >= self {
Ok(())
} else {
Err(ConsensusError::InvalidHardForkVersion(
"Block version or vote incorrect",
))
}
} }
} }

View file

@ -236,7 +236,9 @@ where
spent_kis.clone(), spent_kis.clone(),
) )
}) })
}); })
.await
.unwrap()?;
Ok(VerifyTxResponse::Ok) Ok(VerifyTxResponse::Ok)
} }

View file

@ -58,7 +58,7 @@ fn output_unlocked(
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#block-height /// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#block-height
fn check_block_time_lock(unlock_height: u64, current_chain_height: u64) -> bool { fn check_block_time_lock(unlock_height: u64, current_chain_height: u64) -> bool {
// current_chain_height = 1 + top height // current_chain_height = 1 + top height
unlock_height >= current_chain_height unlock_height <= current_chain_height
} }
/// /// /// ///

View file

@ -21,7 +21,7 @@ fn main() {
.file("c/CryptonightR_JIT.c") .file("c/CryptonightR_JIT.c")
.file("c/CryptonightR_template.S") .file("c/CryptonightR_template.S")
.flag("-maes") .flag("-maes")
.flag("-Ofast") .flag("-O2")
.flag("-fexceptions") .flag("-fexceptions")
.compile("cryptonight") .compile("cryptonight")
} }

View file

@ -1,5 +1,3 @@
use std::fmt::Debug;
pub(crate) fn default_false() -> bool { pub(crate) fn default_false() -> bool {
false false
} }