mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
link rules to monero-book.
need to do transactions.
This commit is contained in:
parent
40e64cc9c3
commit
13957a5e7f
9 changed files with 124 additions and 58 deletions
|
@ -30,22 +30,29 @@ pub enum BlockError {
|
||||||
PreviousIDIncorrect,
|
PreviousIDIncorrect,
|
||||||
#[error("The blocks timestamp is invalid.")]
|
#[error("The blocks timestamp is invalid.")]
|
||||||
TimeStampInvalid,
|
TimeStampInvalid,
|
||||||
|
#[error("The block contains a duplicate transaction.")]
|
||||||
|
DuplicateTransaction,
|
||||||
#[error("Hard-fork error: {0}")]
|
#[error("Hard-fork error: {0}")]
|
||||||
HardForkError(#[from] HardForkError),
|
HardForkError(#[from] HardForkError),
|
||||||
#[error("Miner transaction error: {0}")]
|
#[error("Miner transaction error: {0}")]
|
||||||
MinerTxError(#[from] MinerTxError),
|
MinerTxError(#[from] MinerTxError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait to represent the RandomX VM.
|
||||||
pub trait RandomX {
|
pub trait RandomX {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn calculate_hash(&self, buf: &[u8]) -> Result<[u8; 32], Self::Error>;
|
fn calculate_hash(&self, buf: &[u8]) -> Result<[u8; 32], Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns if this height is a RandomX seed height.
|
||||||
pub fn is_randomx_seed_height(height: u64) -> bool {
|
pub fn is_randomx_seed_height(height: u64) -> bool {
|
||||||
height % RX_SEEDHASH_EPOCH_BLOCKS == 0
|
height % RX_SEEDHASH_EPOCH_BLOCKS == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the RandomX seed height for this block.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#randomx-seed
|
||||||
pub fn randomx_seed_height(height: u64) -> u64 {
|
pub fn randomx_seed_height(height: u64) -> u64 {
|
||||||
if height <= RX_SEEDHASH_EPOCH_BLOCKS + RX_SEEDHASH_EPOCH_LAG {
|
if height <= RX_SEEDHASH_EPOCH_BLOCKS + RX_SEEDHASH_EPOCH_LAG {
|
||||||
0
|
0
|
||||||
|
@ -55,6 +62,8 @@ pub fn randomx_seed_height(height: u64) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the POW hash of this block.
|
/// Calculates the POW hash of this block.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#pow-function
|
||||||
pub fn calculate_pow_hash<R: RandomX>(
|
pub fn calculate_pow_hash<R: RandomX>(
|
||||||
randomx_vm: &R,
|
randomx_vm: &R,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
|
@ -82,7 +91,7 @@ pub fn calculate_pow_hash<R: RandomX>(
|
||||||
|
|
||||||
/// 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#checking-pow-hash
|
||||||
pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> Result<(), BlockError> {
|
pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> Result<(), BlockError> {
|
||||||
let int_hash = U256::from_little_endian(hash);
|
let int_hash = U256::from_little_endian(hash);
|
||||||
|
|
||||||
|
@ -102,7 +111,7 @@ pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> Result<(), BlockErr
|
||||||
|
|
||||||
/// 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#block-weight-and-size
|
||||||
fn block_size_sanity_check(
|
fn block_size_sanity_check(
|
||||||
block_blob_len: usize,
|
block_blob_len: usize,
|
||||||
effective_median: usize,
|
effective_median: usize,
|
||||||
|
@ -114,20 +123,9 @@ 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
|
|
||||||
fn check_amount_txs(number_none_miner_txs: usize) -> Result<(), BlockError> {
|
|
||||||
if number_none_miner_txs + 1 > 0x10000000 {
|
|
||||||
Err(BlockError::TooManyTxs)
|
|
||||||
} 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#block-weight-and-size
|
||||||
fn check_block_weight(
|
fn check_block_weight(
|
||||||
block_weight: usize,
|
block_weight: usize,
|
||||||
median_for_block_reward: usize,
|
median_for_block_reward: usize,
|
||||||
|
@ -139,9 +137,20 @@ fn check_block_weight(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sanity check on number of txs in the block.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#amount-of-transactions
|
||||||
|
fn check_amount_txs(number_none_miner_txs: usize) -> Result<(), BlockError> {
|
||||||
|
if number_none_miner_txs + 1 > 0x10000000 {
|
||||||
|
Err(BlockError::TooManyTxs)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#previous-id
|
||||||
fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), BlockError> {
|
fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), BlockError> {
|
||||||
if &block.header.previous != top_hash {
|
if &block.header.previous != top_hash {
|
||||||
Err(BlockError::PreviousIDIncorrect)
|
Err(BlockError::PreviousIDIncorrect)
|
||||||
|
@ -152,7 +161,7 @@ fn check_prev_id(block: &Block, top_hash: &[u8; 32]) -> Result<(), BlockError> {
|
||||||
|
|
||||||
/// 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#timestamp
|
||||||
fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), BlockError> {
|
fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), BlockError> {
|
||||||
if block.header.timestamp < median_timestamp
|
if block.header.timestamp < median_timestamp
|
||||||
|| block.header.timestamp > current_unix_timestamp() + BLOCK_FUTURE_TIME_LIMIT
|
|| block.header.timestamp > current_unix_timestamp() + BLOCK_FUTURE_TIME_LIMIT
|
||||||
|
@ -163,21 +172,52 @@ fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), BlockErro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks that all txs in the block have a unique hash.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#no-duplicate-transactions
|
||||||
|
fn check_txs_unique(txs: &[[u8; 32]]) -> Result<(), BlockError> {
|
||||||
|
txs.windows(2).try_for_each(|window| {
|
||||||
|
if window[0] != window[1] {
|
||||||
|
Err(BlockError::DuplicateTransaction)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This struct contains the data needed to verify a block, implementers MUST make sure
|
||||||
|
/// the data in this struct is calculated correctly.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ContextToVerifyBlock {
|
pub struct ContextToVerifyBlock {
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/weights.html#median-weight-for-coinbase-checks
|
||||||
pub median_weight_for_block_reward: usize,
|
pub median_weight_for_block_reward: usize,
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/weights.html#effective-median-weight
|
||||||
pub effective_median_weight: usize,
|
pub effective_median_weight: usize,
|
||||||
|
/// The top hash of the blockchain, aka the block hash of the previous block to the one we are verifying.
|
||||||
pub top_hash: [u8; 32],
|
pub top_hash: [u8; 32],
|
||||||
|
/// Contains the median timestamp over the last 60 blocks, if there is less than 60 blocks this should be [`None`]
|
||||||
pub median_block_timestamp: Option<u64>,
|
pub median_block_timestamp: Option<u64>,
|
||||||
|
/// The current chain height.
|
||||||
pub chain_height: u64,
|
pub chain_height: u64,
|
||||||
|
/// The current hard-fork.
|
||||||
pub current_hf: HardFork,
|
pub current_hf: HardFork,
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#calculating-difficulty
|
||||||
pub next_difficulty: u128,
|
pub next_difficulty: u128,
|
||||||
|
/// The amount of coins already minted.
|
||||||
pub already_generated_coins: u64,
|
pub already_generated_coins: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the block is valid returning the blocks hard-fork vote and the amount of coins generated.
|
/// Checks the block is valid returning the blocks hard-fork `VOTE` and the amount of coins generated in this block.
|
||||||
|
///
|
||||||
|
/// This does not check the POW nor does it calculate the POW hash, this is because checking POW is very expensive and
|
||||||
|
/// to allow the computation of the POW hashes to be done separately. This also does not check the transactions in the
|
||||||
|
/// block are valid.
|
||||||
|
///
|
||||||
|
/// Missed block checks in this function:
|
||||||
|
///
|
||||||
|
/// https://monero-book.cuprate.org/consensus_rules/blocks.html#key-images
|
||||||
|
/// https://monero-book.cuprate.org/consensus_rules/blocks.html#checking-pow-hash
|
||||||
|
///
|
||||||
///
|
///
|
||||||
/// Does not check the proof of work as that check is expensive and should be done last.
|
|
||||||
pub fn check_block(
|
pub fn check_block(
|
||||||
block: &Block,
|
block: &Block,
|
||||||
total_fees: u64,
|
total_fees: u64,
|
||||||
|
@ -201,6 +241,7 @@ pub fn check_block(
|
||||||
block_size_sanity_check(block_blob_len, block_chain_ctx.effective_median_weight)?;
|
block_size_sanity_check(block_blob_len, block_chain_ctx.effective_median_weight)?;
|
||||||
|
|
||||||
check_amount_txs(block.txs.len())?;
|
check_amount_txs(block.txs.len())?;
|
||||||
|
check_txs_unique(&block.txs)?;
|
||||||
|
|
||||||
let generated_coins = check_miner_tx(
|
let generated_coins = check_miner_tx(
|
||||||
&block.miner_tx,
|
&block.miner_tx,
|
||||||
|
|
|
@ -36,8 +36,8 @@ pub fn decomposed_amounts() -> &'static [u64; 172] {
|
||||||
///
|
///
|
||||||
/// This is also used during miner tx verification.
|
/// This is also used during miner tx verification.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#output-amount
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html#output-amount
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#output-amounts
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#output-amounts
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_decomposed_amount(amount: &u64) -> bool {
|
pub fn is_decomposed_amount(amount: &u64) -> bool {
|
||||||
decomposed_amounts().binary_search(amount).is_ok()
|
decomposed_amounts().binary_search(amount).is_ok()
|
||||||
|
|
|
@ -25,7 +25,7 @@ fn genesis_miner_tx(network: &Network) -> Transaction {
|
||||||
|
|
||||||
/// Generates the Monero genesis block.
|
/// Generates the Monero genesis block.
|
||||||
///
|
///
|
||||||
/// ref: https://cuprate.github.io/monero-docs/consensus_rules/genesis_block.html
|
/// ref: https://monero-book.cuprate.org/consensus_rules/genesis_block.html
|
||||||
pub fn generate_genesis_block(network: &Network) -> Block {
|
pub fn generate_genesis_block(network: &Network) -> Block {
|
||||||
Block {
|
Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
|
|
|
@ -17,8 +17,12 @@ use std::{
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
/// Target block time for hf 1.
|
/// Target block time for hf 1.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#target-seconds
|
||||||
const BLOCK_TIME_V1: Duration = Duration::from_secs(60);
|
const BLOCK_TIME_V1: Duration = Duration::from_secs(60);
|
||||||
/// Target block time from v2.
|
/// Target block time from v2.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#target-seconds
|
||||||
const BLOCK_TIME_V2: Duration = Duration::from_secs(120);
|
const BLOCK_TIME_V2: Duration = Duration::from_secs(120);
|
||||||
|
|
||||||
pub const NUMB_OF_HARD_FORKS: usize = 16;
|
pub const NUMB_OF_HARD_FORKS: usize = 16;
|
||||||
|
@ -60,7 +64,7 @@ impl HFsInfo {
|
||||||
|
|
||||||
/// Returns the main-net hard-fork information.
|
/// Returns the main-net hard-fork information.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/hardforks.html#Mainnet-Hard-Forks
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#Mainnet-Hard-Forks
|
||||||
pub const fn main_net() -> HFsInfo {
|
pub const fn main_net() -> HFsInfo {
|
||||||
Self([
|
Self([
|
||||||
HFInfo::new(0, 0),
|
HFInfo::new(0, 0),
|
||||||
|
@ -82,6 +86,9 @@ impl HFsInfo {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the test-net hard-fork information.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#Testnet-Hard-Forks
|
||||||
pub const fn test_net() -> HFsInfo {
|
pub const fn test_net() -> HFsInfo {
|
||||||
Self([
|
Self([
|
||||||
HFInfo::new(0, 0),
|
HFInfo::new(0, 0),
|
||||||
|
@ -103,6 +110,9 @@ impl HFsInfo {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the test-net hard-fork information.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#Stagenet-Hard-Forks
|
||||||
pub const fn stage_net() -> HFsInfo {
|
pub const fn stage_net() -> HFsInfo {
|
||||||
Self([
|
Self([
|
||||||
HFInfo::new(0, 0),
|
HFInfo::new(0, 0),
|
||||||
|
@ -152,7 +162,7 @@ pub enum HardFork {
|
||||||
impl HardFork {
|
impl HardFork {
|
||||||
/// Returns the hard-fork for a blocks `major_version` field.
|
/// Returns the hard-fork for a blocks `major_version` field.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-docs/consensus_rules/hardforks.html#blocks-version-and-vote
|
/// https://monero-book.cuprate.org/consensus_rules/hardforks.html#blocks-version-and-vote
|
||||||
pub fn from_version(version: u8) -> Result<HardFork, HardForkError> {
|
pub fn from_version(version: u8) -> Result<HardFork, HardForkError> {
|
||||||
Ok(match version {
|
Ok(match version {
|
||||||
1 => HardFork::V1,
|
1 => HardFork::V1,
|
||||||
|
@ -177,7 +187,7 @@ impl HardFork {
|
||||||
|
|
||||||
/// Returns the hard-fork for a blocks `minor_version` (vote) field.
|
/// Returns the hard-fork for a blocks `minor_version` (vote) field.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-docs/consensus_rules/hardforks.html#blocks-version-and-vote
|
/// https://monero-book.cuprate.org/consensus_rules/hardforks.html#blocks-version-and-vote
|
||||||
pub fn from_vote(vote: u8) -> HardFork {
|
pub fn from_vote(vote: u8) -> HardFork {
|
||||||
if vote == 0 {
|
if vote == 0 {
|
||||||
// A vote of 0 is interpreted as 1 as that's what Monero used to default to.
|
// A vote of 0 is interpreted as 1 as that's what Monero used to default to.
|
||||||
|
@ -200,6 +210,8 @@ impl HardFork {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the target block time for this hardfork.
|
/// Returns the target block time for this hardfork.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#target-seconds
|
||||||
pub fn block_time(&self) -> Duration {
|
pub fn block_time(&self) -> Duration {
|
||||||
match self {
|
match self {
|
||||||
HardFork::V1 => BLOCK_TIME_V1,
|
HardFork::V1 => BLOCK_TIME_V1,
|
||||||
|
@ -209,7 +221,7 @@ 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#blocks-version-and-vote
|
||||||
pub fn check_block_version_vote(
|
pub fn check_block_version_vote(
|
||||||
&self,
|
&self,
|
||||||
version: &HardFork,
|
version: &HardFork,
|
||||||
|
@ -280,7 +292,7 @@ impl HFVotes {
|
||||||
|
|
||||||
/// Returns the total votes for a hard-fork.
|
/// Returns the total votes for a hard-fork.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-docs/consensus_rules/hardforks.html#accepting-a-fork
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#accepting-a-fork
|
||||||
pub fn votes_for_hf(&self, hf: &HardFork) -> u64 {
|
pub fn votes_for_hf(&self, hf: &HardFork) -> u64 {
|
||||||
self.votes[*hf as usize - 1..].iter().sum()
|
self.votes[*hf as usize - 1..].iter().sum()
|
||||||
}
|
}
|
||||||
|
@ -293,7 +305,7 @@ impl HFVotes {
|
||||||
/// Checks if a future hard fork should be activated, returning the next hard-fork that should be
|
/// Checks if a future hard fork should be activated, returning the next hard-fork that should be
|
||||||
/// activated.
|
/// activated.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/hardforks.html#accepting-a-fork
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#accepting-a-fork
|
||||||
pub fn current_fork(
|
pub fn current_fork(
|
||||||
&self,
|
&self,
|
||||||
current_hf: &HardFork,
|
current_hf: &HardFork,
|
||||||
|
@ -323,7 +335,7 @@ impl HFVotes {
|
||||||
|
|
||||||
/// Returns the votes needed for a hard-fork.
|
/// Returns the votes needed for a hard-fork.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/hardforks.html#accepting-a-fork
|
/// ref: https://monero-book.cuprate.org/consensus_rules/hardforks.html#accepting-a-fork
|
||||||
pub fn votes_needed(threshold: u64, window: u64) -> u64 {
|
pub fn votes_needed(threshold: u64, window: u64) -> u64 {
|
||||||
(threshold * window).div_ceil(100)
|
(threshold * window).div_ceil(100)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ fn check_point_canonically_encoded(point: &curve25519_dalek::edwards::Compressed
|
||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current UNIX timestamp.
|
||||||
pub fn current_unix_timestamp() -> u64 {
|
pub fn current_unix_timestamp() -> u64 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
|
@ -39,6 +40,8 @@ pub fn current_unix_timestamp() -> u64 {
|
||||||
.as_secs()
|
.as_secs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An internal function that returns an iterator or a parallel iterator if the
|
||||||
|
/// `rayon` feature is enabled.
|
||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
fn try_par_iter<T>(t: T) -> T::Iter
|
fn try_par_iter<T>(t: T) -> T::Iter
|
||||||
where
|
where
|
||||||
|
@ -47,6 +50,8 @@ where
|
||||||
t.into_par_iter()
|
t.into_par_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An internal function that returns an iterator or a parallel iterator if the
|
||||||
|
/// `rayon` feature is enabled.
|
||||||
#[cfg(not(feature = "rayon"))]
|
#[cfg(not(feature = "rayon"))]
|
||||||
fn try_par_iter<T>(t: T) -> impl std::iter::Iterator<Item = T::Item>
|
fn try_par_iter<T>(t: T) -> impl std::iter::Iterator<Item = T::Item>
|
||||||
where
|
where
|
||||||
|
|
|
@ -39,6 +39,8 @@ const MINER_TX_TIME_LOCKED_BLOCKS: u64 = 60;
|
||||||
|
|
||||||
/// Calculates the base block reward without taking away the penalty for expanding
|
/// Calculates the base block reward without taking away the penalty for expanding
|
||||||
/// the block.
|
/// the block.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/reward.html#calculating-base-block-reward
|
||||||
fn calculate_base_reward(already_generated_coins: u64, hf: &HardFork) -> u64 {
|
fn calculate_base_reward(already_generated_coins: u64, hf: &HardFork) -> u64 {
|
||||||
let target_mins = hf.block_time().as_secs() / 60;
|
let target_mins = hf.block_time().as_secs() / 60;
|
||||||
let emission_speed_factor = 20 - (target_mins - 1);
|
let emission_speed_factor = 20 - (target_mins - 1);
|
||||||
|
@ -47,6 +49,8 @@ fn calculate_base_reward(already_generated_coins: u64, hf: &HardFork) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the miner reward for this block.
|
/// Calculates the miner reward for this block.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/reward.html#calculating-block-reward
|
||||||
pub fn calculate_block_reward(
|
pub fn calculate_block_reward(
|
||||||
block_weight: usize,
|
block_weight: usize,
|
||||||
median_bw: usize,
|
median_bw: usize,
|
||||||
|
@ -71,7 +75,7 @@ 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
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#version
|
||||||
fn check_miner_tx_version(tx_version: &TxVersion, hf: &HardFork) -> Result<(), MinerTxError> {
|
fn check_miner_tx_version(tx_version: &TxVersion, hf: &HardFork) -> Result<(), MinerTxError> {
|
||||||
// The TxVersion enum checks if the version is not 1 or 2
|
// 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 {
|
||||||
|
@ -83,8 +87,7 @@ fn check_miner_tx_version(tx_version: &TxVersion, hf: &HardFork) -> Result<(), M
|
||||||
|
|
||||||
/// Checks the miner transactions inputs.
|
/// Checks the miner transactions inputs.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#input
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#input
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#height
|
|
||||||
fn check_inputs(inputs: &[Input], chain_height: u64) -> Result<(), MinerTxError> {
|
fn check_inputs(inputs: &[Input], chain_height: u64) -> Result<(), MinerTxError> {
|
||||||
if inputs.len() != 1 {
|
if inputs.len() != 1 {
|
||||||
return Err(MinerTxError::IncorrectNumbOfInputs);
|
return Err(MinerTxError::IncorrectNumbOfInputs);
|
||||||
|
@ -104,13 +107,13 @@ fn check_inputs(inputs: &[Input], chain_height: u64) -> Result<(), MinerTxError>
|
||||||
|
|
||||||
/// Checks the miner transaction has a correct time lock.
|
/// Checks the miner transaction has a correct time lock.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#unlock-time
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#unlock-time
|
||||||
fn check_time_lock(time_lock: &Timelock, chain_height: u64) -> Result<(), MinerTxError> {
|
fn check_time_lock(time_lock: &Timelock, chain_height: u64) -> Result<(), MinerTxError> {
|
||||||
match time_lock {
|
match time_lock {
|
||||||
Timelock::Block(till_height) => {
|
Timelock::Block(till_height) => {
|
||||||
// Lock times above this amount are timestamps not blocks.
|
// Lock times above this amount are timestamps not blocks.
|
||||||
// This is just for safety though and shouldn't actually be hit.
|
// This is just for safety though and shouldn't actually be hit.
|
||||||
if till_height >= &500_000_000 {
|
if till_height > &500_000_000 {
|
||||||
Err(MinerTxError::InvalidLockTime)?;
|
Err(MinerTxError::InvalidLockTime)?;
|
||||||
}
|
}
|
||||||
if u64::try_from(*till_height).unwrap() != chain_height + MINER_TX_TIME_LOCKED_BLOCKS {
|
if u64::try_from(*till_height).unwrap() != chain_height + MINER_TX_TIME_LOCKED_BLOCKS {
|
||||||
|
@ -125,7 +128,7 @@ fn check_time_lock(time_lock: &Timelock, chain_height: u64) -> Result<(), MinerT
|
||||||
|
|
||||||
/// Sums the outputs checking for overflow.
|
/// Sums the outputs checking for overflow.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#output-amounts
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#output-amounts
|
||||||
fn sum_outputs(outputs: &[Output], hf: &HardFork) -> Result<u64, MinerTxError> {
|
fn sum_outputs(outputs: &[Output], hf: &HardFork) -> Result<u64, MinerTxError> {
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
for out in outputs {
|
for out in outputs {
|
||||||
|
@ -140,7 +143,7 @@ fn sum_outputs(outputs: &[Output], hf: &HardFork) -> Result<u64, MinerTxError> {
|
||||||
|
|
||||||
/// Checks the total outputs amount is correct returning the amount of coins collected by the miner.
|
/// Checks the total outputs amount is correct returning the amount of coins collected by the miner.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#total-outputs
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#total-outputs
|
||||||
fn check_total_output_amt(
|
fn check_total_output_amt(
|
||||||
total_output: u64,
|
total_output: u64,
|
||||||
reward: u64,
|
reward: u64,
|
||||||
|
@ -160,6 +163,12 @@ fn check_total_output_amt(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks all miner transactions rules.
|
||||||
|
///
|
||||||
|
/// Excluding:
|
||||||
|
/// https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#v2-output-pool
|
||||||
|
///
|
||||||
|
/// as this needs to be done in a database.
|
||||||
pub fn check_miner_tx(
|
pub fn check_miner_tx(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
total_fees: u64,
|
total_fees: u64,
|
||||||
|
@ -172,6 +181,7 @@ pub fn check_miner_tx(
|
||||||
let tx_version = TxVersion::from_raw(tx.prefix.version).ok_or(MinerTxError::VersionInvalid)?;
|
let tx_version = TxVersion::from_raw(tx.prefix.version).ok_or(MinerTxError::VersionInvalid)?;
|
||||||
check_miner_tx_version(&tx_version, hf)?;
|
check_miner_tx_version(&tx_version, hf)?;
|
||||||
|
|
||||||
|
// ref: https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#ringct-type
|
||||||
if hf >= &HardFork::V12 && tx.rct_signatures.rct_type() != RctType::Null {
|
if hf >= &HardFork::V12 && tx.rct_signatures.rct_type() != RctType::Null {
|
||||||
return Err(MinerTxError::RCTTypeNotNULL);
|
return Err(MinerTxError::RCTTypeNotNULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
#![cfg(feature = "binaries")]
|
#![cfg(feature = "binaries")]
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::{
|
||||||
use std::ops::Deref;
|
collections::{HashMap, HashSet},
|
||||||
use std::time::Duration;
|
ops::Range,
|
||||||
use std::{ops::Range, path::PathBuf, sync::Arc};
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
SinkExt, StreamExt, TryFutureExt,
|
SinkExt, StreamExt,
|
||||||
};
|
};
|
||||||
use monero_serai::{block::Block, transaction::Transaction};
|
use monero_serai::{block::Block, transaction::Transaction};
|
||||||
use rayon::prelude::*;
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
|
@ -221,7 +222,7 @@ where
|
||||||
let mut randomx_vms: Option<HashMap<u64, RandomXVM>> = Some(HashMap::new());
|
let mut randomx_vms: Option<HashMap<u64, RandomXVM>> = Some(HashMap::new());
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(mut blocks) = incoming_blocks.next().await {
|
while let Some(blocks) = incoming_blocks.next().await {
|
||||||
let unwrapped_rx_vms = randomx_vms.as_mut().unwrap();
|
let unwrapped_rx_vms = randomx_vms.as_mut().unwrap();
|
||||||
|
|
||||||
let blocks = rayon_spawn_async(move || {
|
let blocks = rayon_spawn_async(move || {
|
||||||
|
@ -243,12 +244,9 @@ where
|
||||||
unwrapped_rx_vms.retain(|seed_height, _| seeds_needed.contains(seed_height));
|
unwrapped_rx_vms.retain(|seed_height, _| seeds_needed.contains(seed_height));
|
||||||
|
|
||||||
for seed_height in seeds_needed {
|
for seed_height in seeds_needed {
|
||||||
if !unwrapped_rx_vms.contains_key(&seed_height) {
|
unwrapped_rx_vms.entry(seed_height).or_insert_with(|| {
|
||||||
unwrapped_rx_vms.insert(
|
RandomXVM::new(rx_seed_cache.get_seeds_hash(seed_height)).unwrap()
|
||||||
seed_height,
|
});
|
||||||
RandomXVM::new(rx_seed_cache.get_seeds_hash(seed_height)).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let arc_rx_vms = Arc::new(randomx_vms.take().unwrap());
|
let arc_rx_vms = Arc::new(randomx_vms.take().unwrap());
|
||||||
|
|
|
@ -10,16 +10,14 @@ use monero_serai::block::Block;
|
||||||
use monero_serai::transaction::Input;
|
use monero_serai::transaction::Input;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
|
||||||
use monero_consensus::blocks::{BlockError, RandomX};
|
|
||||||
use monero_consensus::miner_tx::MinerTxError;
|
|
||||||
use monero_consensus::{
|
use monero_consensus::{
|
||||||
blocks::{calculate_pow_hash, check_block, check_block_pow},
|
blocks::{calculate_pow_hash, check_block, check_block_pow, BlockError, RandomX},
|
||||||
|
miner_tx::MinerTxError,
|
||||||
ConsensusError, HardFork,
|
ConsensusError, HardFork,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{BlockChainContextRequest, BlockChainContextResponse},
|
context::{BlockChainContextRequest, BlockChainContextResponse},
|
||||||
helper::rayon_spawn_async,
|
|
||||||
transactions::{TransactionVerificationData, VerifyTxRequest, VerifyTxResponse},
|
transactions::{TransactionVerificationData, VerifyTxRequest, VerifyTxResponse},
|
||||||
ExtendedConsensusError, TxNotInPool, TxPoolRequest, TxPoolResponse,
|
ExtendedConsensusError, TxNotInPool, TxPoolRequest, TxPoolResponse,
|
||||||
};
|
};
|
||||||
|
@ -294,10 +292,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify_main_chain_block<C, TxV, TxP>(
|
async fn verify_main_chain_block<C, TxV, TxP>(
|
||||||
block: Block,
|
_block: Block,
|
||||||
context_svc: C,
|
_context_svc: C,
|
||||||
tx_verifier_svc: TxV,
|
_tx_verifier_svc: TxV,
|
||||||
tx_pool: TxP,
|
_tx_pool: TxP,
|
||||||
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
|
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
|
||||||
where
|
where
|
||||||
C: Service<
|
C: Service<
|
||||||
|
@ -313,6 +311,9 @@ where
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
todo!("Single main chain block.");
|
||||||
|
|
||||||
|
/*
|
||||||
tracing::debug!("getting blockchain context");
|
tracing::debug!("getting blockchain context");
|
||||||
let BlockChainContextResponse::Context(checked_context) = context_svc
|
let BlockChainContextResponse::Context(checked_context) = context_svc
|
||||||
.oneshot(BlockChainContextRequest::Get)
|
.oneshot(BlockChainContextRequest::Get)
|
||||||
|
@ -379,4 +380,5 @@ where
|
||||||
hf_vote,
|
hf_vote,
|
||||||
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
|
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
|
||||||
}))
|
}))
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ pub struct RandomXVM {
|
||||||
vms: ThreadLocal<VMInner>,
|
vms: ThreadLocal<VMInner>,
|
||||||
cache: RandomXCache,
|
cache: RandomXCache,
|
||||||
flags: RandomXFlag,
|
flags: RandomXFlag,
|
||||||
seed: [u8; 32],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RandomXVM {
|
impl RandomXVM {
|
||||||
|
@ -20,7 +19,6 @@ impl RandomXVM {
|
||||||
vms: ThreadLocal::new(),
|
vms: ThreadLocal::new(),
|
||||||
cache,
|
cache,
|
||||||
flags,
|
flags,
|
||||||
seed,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue