mirror of
https://github.com/hinto-janai/cuprate.git
synced 2025-03-26 17:18:53 +00:00
link transaction rules to monero-book
This commit is contained in:
parent
13957a5e7f
commit
730bc8fb42
7 changed files with 209 additions and 91 deletions
|
@ -15,6 +15,10 @@ const BLOCK_FUTURE_TIME_LIMIT: u64 = 60 * 60 * 2;
|
||||||
const BLOCK_202612_POW_HASH: [u8; 32] =
|
const BLOCK_202612_POW_HASH: [u8; 32] =
|
||||||
hex_literal::hex!("84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000");
|
hex_literal::hex!("84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000");
|
||||||
|
|
||||||
|
pub const PENALTY_FREE_ZONE_1: usize = 20000;
|
||||||
|
pub const PENALTY_FREE_ZONE_2: usize = 60000;
|
||||||
|
pub const PENALTY_FREE_ZONE_5: usize = 300000;
|
||||||
|
|
||||||
const RX_SEEDHASH_EPOCH_BLOCKS: u64 = 2048;
|
const RX_SEEDHASH_EPOCH_BLOCKS: u64 = 2048;
|
||||||
const RX_SEEDHASH_EPOCH_LAG: u64 = 64;
|
const RX_SEEDHASH_EPOCH_LAG: u64 = 64;
|
||||||
|
|
||||||
|
@ -109,6 +113,19 @@ pub fn check_block_pow(hash: &[u8; 32], difficulty: u128) -> Result<(), BlockErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the penalty free zone
|
||||||
|
///
|
||||||
|
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/weight_limit.html#penalty-free-zone
|
||||||
|
pub fn penalty_free_zone(hf: &HardFork) -> usize {
|
||||||
|
if hf == &HardFork::V1 {
|
||||||
|
PENALTY_FREE_ZONE_1
|
||||||
|
} else if hf >= &HardFork::V2 && hf < &HardFork::V5 {
|
||||||
|
PENALTY_FREE_ZONE_2
|
||||||
|
} else {
|
||||||
|
PENALTY_FREE_ZONE_5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sanity check on the block blob size.
|
/// Sanity check on the block blob size.
|
||||||
///
|
///
|
||||||
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#block-weight-and-size
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#block-weight-and-size
|
||||||
|
@ -177,7 +194,7 @@ fn check_timestamp(block: &Block, median_timestamp: u64) -> Result<(), BlockErro
|
||||||
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#no-duplicate-transactions
|
/// ref: https://monero-book.cuprate.org/consensus_rules/blocks.html#no-duplicate-transactions
|
||||||
fn check_txs_unique(txs: &[[u8; 32]]) -> Result<(), BlockError> {
|
fn check_txs_unique(txs: &[[u8; 32]]) -> Result<(), BlockError> {
|
||||||
txs.windows(2).try_for_each(|window| {
|
txs.windows(2).try_for_each(|window| {
|
||||||
if window[0] != window[1] {
|
if window[0] == window[1] {
|
||||||
Err(BlockError::DuplicateTransaction)?;
|
Err(BlockError::DuplicateTransaction)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use monero_serai::ringct::RctType;
|
||||||
use std::{cmp::Ordering, collections::HashSet, sync::Arc};
|
use std::{cmp::Ordering, collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use monero_serai::transaction::{Input, Output, Timelock, Transaction};
|
use monero_serai::transaction::{Input, Output, Timelock, Transaction};
|
||||||
use multiexp::BatchVerifier;
|
use multiexp::BatchVerifier;
|
||||||
|
|
||||||
use crate::{check_point_canonically_encoded, is_decomposed_amount, HardFork};
|
use crate::{
|
||||||
|
blocks::penalty_free_zone, check_point_canonically_encoded, is_decomposed_amount, HardFork,
|
||||||
|
};
|
||||||
|
|
||||||
mod contextual_data;
|
mod contextual_data;
|
||||||
mod ring_ct;
|
mod ring_ct;
|
||||||
|
@ -12,10 +15,15 @@ mod ring_signatures;
|
||||||
pub use contextual_data::*;
|
pub use contextual_data::*;
|
||||||
pub use ring_ct::RingCTError;
|
pub use ring_ct::RingCTError;
|
||||||
|
|
||||||
|
const MAX_BULLETPROOFS_OUTPUTS: usize = 16;
|
||||||
|
const MAX_TX_BLOB_SIZE: usize = 1_000_000;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
pub enum TransactionError {
|
pub enum TransactionError {
|
||||||
#[error("The transactions version is incorrect.")]
|
#[error("The transactions version is incorrect.")]
|
||||||
TransactionVersionInvalid,
|
TransactionVersionInvalid,
|
||||||
|
#[error("The transactions is too big.")]
|
||||||
|
TooBig,
|
||||||
//-------------------------------------------------------- OUTPUTS
|
//-------------------------------------------------------- OUTPUTS
|
||||||
#[error("Output is not a valid point.")]
|
#[error("Output is not a valid point.")]
|
||||||
OutputNotValidPoint,
|
OutputNotValidPoint,
|
||||||
|
@ -23,12 +31,16 @@ pub enum TransactionError {
|
||||||
OutputTypeInvalid,
|
OutputTypeInvalid,
|
||||||
#[error("The transaction is v1 with a 0 amount output.")]
|
#[error("The transaction is v1 with a 0 amount output.")]
|
||||||
ZeroOutputForV1,
|
ZeroOutputForV1,
|
||||||
|
#[error("The transaction is v2 with a non 0 amount output.")]
|
||||||
|
NonZeroOutputForV2,
|
||||||
#[error("The transaction has an output which is not decomposed.")]
|
#[error("The transaction has an output which is not decomposed.")]
|
||||||
AmountNotDecomposed,
|
AmountNotDecomposed,
|
||||||
#[error("The transactions outputs overflow.")]
|
#[error("The transactions outputs overflow.")]
|
||||||
OutputsOverflow,
|
OutputsOverflow,
|
||||||
#[error("The transactions outputs too much.")]
|
#[error("The transactions outputs too much.")]
|
||||||
OutputsTooHigh,
|
OutputsTooHigh,
|
||||||
|
#[error("The transactions has too many outputs.")]
|
||||||
|
InvalidNumberOfOutputs,
|
||||||
//-------------------------------------------------------- INPUTS
|
//-------------------------------------------------------- INPUTS
|
||||||
#[error("One or more inputs don't have the expected number of decoys.")]
|
#[error("One or more inputs don't have the expected number of decoys.")]
|
||||||
InputDoesNotHaveExpectedNumbDecoys,
|
InputDoesNotHaveExpectedNumbDecoys,
|
||||||
|
@ -57,16 +69,25 @@ pub enum TransactionError {
|
||||||
RingSignatureIncorrect,
|
RingSignatureIncorrect,
|
||||||
//-------------------------------------------------------- RingCT
|
//-------------------------------------------------------- RingCT
|
||||||
#[error("RingCT Error: {0}.")]
|
#[error("RingCT Error: {0}.")]
|
||||||
RingCTError(#[from] ring_ct::RingCTError),
|
RingCTError(#[from] RingCTError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An enum representing all valid Monero transaction versions.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum TxVersion {
|
pub enum TxVersion {
|
||||||
|
/// Legacy ring signatures.
|
||||||
RingSignatures,
|
RingSignatures,
|
||||||
|
/// RingCT
|
||||||
RingCT,
|
RingCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxVersion {
|
impl TxVersion {
|
||||||
|
/// Converts a `raw` version value to a [`TxVersion`].
|
||||||
|
///
|
||||||
|
/// This will return `None` on invalid values.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions.html#version
|
||||||
|
/// && https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#version
|
||||||
pub fn from_raw(version: u64) -> Option<TxVersion> {
|
pub fn from_raw(version: u64) -> Option<TxVersion> {
|
||||||
Some(match version {
|
Some(match version {
|
||||||
1 => TxVersion::RingSignatures,
|
1 => TxVersion::RingSignatures,
|
||||||
|
@ -80,7 +101,7 @@ impl TxVersion {
|
||||||
|
|
||||||
/// Checks the output keys are canonically encoded points.
|
/// Checks the output keys are canonically encoded points.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#output-keys-canonical
|
/// https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#output-keys-canonical
|
||||||
fn check_output_keys(outputs: &[Output]) -> Result<(), TransactionError> {
|
fn check_output_keys(outputs: &[Output]) -> Result<(), TransactionError> {
|
||||||
for out in outputs {
|
for out in outputs {
|
||||||
if !check_point_canonically_encoded(&out.key) {
|
if !check_point_canonically_encoded(&out.key) {
|
||||||
|
@ -91,12 +112,12 @@ fn check_output_keys(outputs: &[Output]) -> Result<(), TransactionError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the output types are allowed.
|
/// Checks the output types are allowed for the given hard-fork.
|
||||||
///
|
///
|
||||||
/// 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.html#output-type
|
/// https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#output-type
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/miner_tx.html#output-type
|
/// https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#output-type
|
||||||
pub(crate) fn check_output_types(
|
pub(crate) fn check_output_types(
|
||||||
outputs: &[Output],
|
outputs: &[Output],
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
|
@ -120,9 +141,9 @@ pub(crate) fn check_output_types(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the outputs amount for version 1 txs.
|
/// Checks the individual outputs amount for version 1 txs.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#output-amount
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#output-amount
|
||||||
fn check_output_amount_v1(amount: u64, hf: &HardFork) -> Result<(), TransactionError> {
|
fn check_output_amount_v1(amount: u64, hf: &HardFork) -> Result<(), TransactionError> {
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
return Err(TransactionError::ZeroOutputForV1);
|
return Err(TransactionError::ZeroOutputForV1);
|
||||||
|
@ -135,20 +156,35 @@ fn check_output_amount_v1(amount: u64, hf: &HardFork) -> Result<(), TransactionE
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the individual outputs amount for version 2 txs.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#output-amount
|
||||||
|
fn check_output_amount_v2(amount: u64) -> Result<(), TransactionError> {
|
||||||
|
if amount == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(TransactionError::NonZeroOutputForV2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sums the outputs, checking for overflow and other consensus rules.
|
/// Sums the outputs, checking for overflow and other consensus rules.
|
||||||
///
|
///
|
||||||
/// Should only be used on v1 transactions.
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#output-amount
|
||||||
///
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#outputs-must-not-overflow
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#inputs-and-outputs-must-not-overflow
|
fn sum_outputs(
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#output-amount
|
outputs: &[Output],
|
||||||
fn sum_outputs_v1(outputs: &[Output], hf: &HardFork) -> Result<u64, TransactionError> {
|
hf: &HardFork,
|
||||||
|
tx_version: &TxVersion,
|
||||||
|
) -> Result<u64, TransactionError> {
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
|
|
||||||
for out in outputs {
|
for out in outputs {
|
||||||
let raw_amount = out.amount.unwrap_or(0);
|
let raw_amount = out.amount.unwrap_or(0);
|
||||||
|
|
||||||
check_output_amount_v1(raw_amount, hf)?;
|
match tx_version {
|
||||||
|
TxVersion::RingSignatures => check_output_amount_v1(raw_amount, hf)?,
|
||||||
|
TxVersion::RingCT => check_output_amount_v2(raw_amount)?,
|
||||||
|
}
|
||||||
sum = sum
|
sum = sum
|
||||||
.checked_add(raw_amount)
|
.checked_add(raw_amount)
|
||||||
.ok_or(TransactionError::OutputsOverflow)?;
|
.ok_or(TransactionError::OutputsOverflow)?;
|
||||||
|
@ -157,26 +193,60 @@ fn sum_outputs_v1(outputs: &[Output], hf: &HardFork) -> Result<u64, TransactionE
|
||||||
Ok(sum)
|
Ok(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the number of outputs is allowed.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html#2-outputs
|
||||||
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/ring_ct/bulletproofs.html#max-outputs
|
||||||
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/ring_ct/bulletproofs+.html#max-outputs
|
||||||
|
fn check_number_of_outputs(
|
||||||
|
outputs: usize,
|
||||||
|
hf: &HardFork,
|
||||||
|
tx_version: &TxVersion,
|
||||||
|
rct_type: &RctType,
|
||||||
|
) -> Result<(), TransactionError> {
|
||||||
|
if tx_version == &TxVersion::RingSignatures {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if hf >= &HardFork::V12 && outputs < 2 {
|
||||||
|
return Err(TransactionError::InvalidNumberOfOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
match rct_type {
|
||||||
|
RctType::Bulletproofs | RctType::BulletproofsCompactAmount | RctType::BulletproofsPlus => {
|
||||||
|
if outputs <= MAX_BULLETPROOFS_OUTPUTS {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(TransactionError::InvalidNumberOfOutputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks the outputs against all output consensus rules, returning the sum of the output amounts.
|
/// Checks the outputs against all output consensus rules, returning the sum of the output amounts.
|
||||||
|
///
|
||||||
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/outputs.html
|
||||||
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/ring_ct/bulletproofs.html#max-outputs
|
||||||
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/ring_ct/bulletproofs+.html#max-outputs
|
||||||
fn check_outputs_semantics(
|
fn check_outputs_semantics(
|
||||||
outputs: &[Output],
|
outputs: &[Output],
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
tx_version: &TxVersion,
|
tx_version: &TxVersion,
|
||||||
|
rct_type: &RctType,
|
||||||
) -> Result<u64, TransactionError> {
|
) -> Result<u64, TransactionError> {
|
||||||
check_output_types(outputs, hf)?;
|
check_output_types(outputs, hf)?;
|
||||||
check_output_keys(outputs)?;
|
check_output_keys(outputs)?;
|
||||||
|
check_number_of_outputs(outputs.len(), hf, tx_version, rct_type)?;
|
||||||
|
|
||||||
match tx_version {
|
sum_outputs(outputs, hf, tx_version)
|
||||||
TxVersion::RingSignatures => sum_outputs_v1(outputs, hf),
|
|
||||||
TxVersion::RingCT => Ok(0), // RCT outputs are checked to be zero in RCT checks.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------- TIME LOCKS
|
//----------------------------------------------------------------------------------------------------------- TIME LOCKS
|
||||||
|
|
||||||
/// Checks if an outputs unlock time has passed.
|
/// Checks if an outputs unlock time has passed.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#unlock-time
|
/// https://monero-book.cuprate.org/consensus_rules/transactions/unlock_time.html
|
||||||
fn output_unlocked(
|
fn output_unlocked(
|
||||||
time_lock: &Timelock,
|
time_lock: &Timelock,
|
||||||
current_chain_height: u64,
|
current_chain_height: u64,
|
||||||
|
@ -194,18 +264,17 @@ fn output_unlocked(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if a locked output, which uses a block height, can be spend.
|
/// Returns if a locked output, which uses a block height, can be spent.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#block-height
|
/// ref: https://monero-book.cuprate.org/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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ///
|
|
||||||
/// Returns if a locked output, which uses a block height, can be spend.
|
/// Returns if a locked output, which uses a block height, can be spend.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#timestamp
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/unlock_time.html#timestamp
|
||||||
fn check_timestamp_time_lock(
|
fn check_timestamp_time_lock(
|
||||||
unlock_timestamp: u64,
|
unlock_timestamp: u64,
|
||||||
current_time_lock_timestamp: u64,
|
current_time_lock_timestamp: u64,
|
||||||
|
@ -216,9 +285,10 @@ fn check_timestamp_time_lock(
|
||||||
|
|
||||||
/// Checks all the time locks are unlocked.
|
/// Checks all the time locks are unlocked.
|
||||||
///
|
///
|
||||||
/// `current_time_lock_timestamp` must be: https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#getting-the-current-time
|
/// `current_time_lock_timestamp` must be: https://monero-book.cuprate.org/consensus_rules/transactions/unlock_time.html#getting-the-current-time
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#unlock-time
|
/// https://monero-book.cuprate.org/consensus_rules/transactions/unlock_time.html
|
||||||
|
/// https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#the-output-must-not-be-locked
|
||||||
fn check_all_time_locks(
|
fn check_all_time_locks(
|
||||||
time_locks: &[Timelock],
|
time_locks: &[Timelock],
|
||||||
current_chain_height: u64,
|
current_chain_height: u64,
|
||||||
|
@ -243,8 +313,8 @@ fn check_all_time_locks(
|
||||||
|
|
||||||
/// Checks the decoys are allowed.
|
/// Checks the decoys are allowed.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#minimum-decoys
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#minimum-decoys
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#equal-number-of-decoys
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#equal-number-of-decoys
|
||||||
fn check_decoy_info(decoy_info: &DecoyInfo, hf: &HardFork) -> Result<(), TransactionError> {
|
fn check_decoy_info(decoy_info: &DecoyInfo, hf: &HardFork) -> Result<(), TransactionError> {
|
||||||
if hf == &HardFork::V15 {
|
if hf == &HardFork::V15 {
|
||||||
// Hard-fork 15 allows both v14 and v16 rules
|
// Hard-fork 15 allows both v14 and v16 rules
|
||||||
|
@ -276,13 +346,13 @@ fn check_decoy_info(decoy_info: &DecoyInfo, hf: &HardFork) -> Result<(), Transac
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the inputs key images for torsion and for duplicates in the transaction.
|
/// Checks the inputs key images for torsion and for duplicates in the spent_kis list.
|
||||||
///
|
///
|
||||||
/// The `spent_kis` parameter is not meant to be a complete list of key images, just a list of related transactions
|
/// The `spent_kis` parameter is not meant to be a complete list of key images, just a list of related transactions
|
||||||
/// key images, for example transactions in a block. The chain will be checked for duplicates later.
|
/// key images, for example transactions in a block. The chain will be checked for duplicates later.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#unique-key-image
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#unique-key-image
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#torsion-free-key-image
|
/// && https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#torsion-free-key-image
|
||||||
fn check_key_images(
|
fn check_key_images(
|
||||||
input: &Input,
|
input: &Input,
|
||||||
spent_kis: &mut HashSet<[u8; 32]>,
|
spent_kis: &mut HashSet<[u8; 32]>,
|
||||||
|
@ -305,7 +375,7 @@ fn check_key_images(
|
||||||
|
|
||||||
/// Checks that the input is of type [`Input::ToKey`] aka txin_to_key.
|
/// Checks that the input is of type [`Input::ToKey`] aka txin_to_key.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#input-type
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#input-type
|
||||||
fn check_input_type(input: &Input) -> Result<(), TransactionError> {
|
fn check_input_type(input: &Input) -> Result<(), TransactionError> {
|
||||||
match input {
|
match input {
|
||||||
Input::ToKey { .. } => Ok(()),
|
Input::ToKey { .. } => Ok(()),
|
||||||
|
@ -315,7 +385,7 @@ fn check_input_type(input: &Input) -> Result<(), TransactionError> {
|
||||||
|
|
||||||
/// Checks that the input has decoys.
|
/// Checks that the input has decoys.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#inputs-must-have-decoys
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#no-empty-decoys
|
||||||
fn check_input_has_decoys(input: &Input) -> Result<(), TransactionError> {
|
fn check_input_has_decoys(input: &Input) -> Result<(), TransactionError> {
|
||||||
match input {
|
match input {
|
||||||
Input::ToKey { key_offsets, .. } => {
|
Input::ToKey { key_offsets, .. } => {
|
||||||
|
@ -331,7 +401,7 @@ fn check_input_has_decoys(input: &Input) -> Result<(), TransactionError> {
|
||||||
|
|
||||||
/// Checks that the ring members for the input are unique after hard-fork 6.
|
/// Checks that the ring members for the input are unique after hard-fork 6.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#unique-ring-members
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#unique-ring-members
|
||||||
fn check_ring_members_unique(input: &Input, hf: &HardFork) -> Result<(), TransactionError> {
|
fn check_ring_members_unique(input: &Input, hf: &HardFork) -> Result<(), TransactionError> {
|
||||||
if hf >= &HardFork::V6 {
|
if hf >= &HardFork::V6 {
|
||||||
match input {
|
match input {
|
||||||
|
@ -351,7 +421,7 @@ fn check_ring_members_unique(input: &Input, hf: &HardFork) -> Result<(), Transac
|
||||||
|
|
||||||
/// Checks that from hf 7 the inputs are sorted by key image.
|
/// Checks that from hf 7 the inputs are sorted by key image.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#sorted-inputs
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#sorted-inputs
|
||||||
fn check_inputs_sorted(inputs: &[Input], hf: &HardFork) -> Result<(), TransactionError> {
|
fn check_inputs_sorted(inputs: &[Input], hf: &HardFork) -> Result<(), TransactionError> {
|
||||||
let get_ki = |inp: &Input| match inp {
|
let get_ki = |inp: &Input| match inp {
|
||||||
Input::ToKey { key_image, .. } => Ok(key_image.compress().to_bytes()),
|
Input::ToKey { key_image, .. } => Ok(key_image.compress().to_bytes()),
|
||||||
|
@ -373,7 +443,7 @@ fn check_inputs_sorted(inputs: &[Input], hf: &HardFork) -> Result<(), Transactio
|
||||||
|
|
||||||
/// Checks the youngest output is at least 10 blocks old.
|
/// Checks the youngest output is at least 10 blocks old.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#10-block-lock
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#10-block-lock
|
||||||
fn check_10_block_lock(
|
fn check_10_block_lock(
|
||||||
youngest_used_out_height: u64,
|
youngest_used_out_height: u64,
|
||||||
current_chain_height: u64,
|
current_chain_height: u64,
|
||||||
|
@ -392,7 +462,7 @@ fn check_10_block_lock(
|
||||||
|
|
||||||
/// Sums the inputs checking for overflow.
|
/// Sums the inputs checking for overflow.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#inputs-and-outputs-must-not-overflow
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#inputs-must-not-overflow
|
||||||
fn sum_inputs_check_overflow(inputs: &[Input]) -> Result<u64, TransactionError> {
|
fn sum_inputs_check_overflow(inputs: &[Input]) -> Result<u64, TransactionError> {
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
for inp in inputs {
|
for inp in inputs {
|
||||||
|
@ -409,7 +479,13 @@ fn sum_inputs_check_overflow(inputs: &[Input]) -> Result<u64, TransactionError>
|
||||||
Ok(sum)
|
Ok(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the inputs semantically validity, returning the sum of the inputs.
|
||||||
|
///
|
||||||
|
/// Semantic rules are rules that don't require blockchain context, the hard-fork does not require blockchain context as:
|
||||||
|
/// - The tx-pool will use the current hard-fork
|
||||||
|
/// - When syncing the hard-fork is in the block header.
|
||||||
fn check_inputs_semantics(inputs: &[Input], hf: &HardFork) -> Result<u64, TransactionError> {
|
fn check_inputs_semantics(inputs: &[Input], hf: &HardFork) -> Result<u64, TransactionError> {
|
||||||
|
// https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#no-empty-inputs
|
||||||
if inputs.is_empty() {
|
if inputs.is_empty() {
|
||||||
return Err(TransactionError::NoInputs);
|
return Err(TransactionError::NoInputs);
|
||||||
}
|
}
|
||||||
|
@ -426,6 +502,14 @@ fn check_inputs_semantics(inputs: &[Input], hf: &HardFork) -> Result<u64, Transa
|
||||||
sum_inputs_check_overflow(inputs)
|
sum_inputs_check_overflow(inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the inputs contextual validity.
|
||||||
|
///
|
||||||
|
/// Contextual rules are rules that require blockchain context to check.
|
||||||
|
///
|
||||||
|
/// This function does not check signatures.
|
||||||
|
///
|
||||||
|
/// The `spent_kis` parameter is not meant to be a complete list of key images, just a list of related transactions
|
||||||
|
/// key images, for example transactions in a block. The chain should be checked for duplicates later.
|
||||||
fn check_inputs_contextual(
|
fn check_inputs_contextual(
|
||||||
inputs: &[Input],
|
inputs: &[Input],
|
||||||
tx_ring_members_info: &TxRingMembersInfo,
|
tx_ring_members_info: &TxRingMembersInfo,
|
||||||
|
@ -454,9 +538,11 @@ fn check_inputs_contextual(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------------- OVERALL
|
||||||
|
|
||||||
/// Checks the version is in the allowed range.
|
/// Checks the version is in the allowed range.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#version
|
/// https://monero-book.cuprate.org/consensus_rules/transactions.html#version
|
||||||
fn check_tx_version(
|
fn check_tx_version(
|
||||||
decoy_info: &Option<DecoyInfo>,
|
decoy_info: &Option<DecoyInfo>,
|
||||||
version: &TxVersion,
|
version: &TxVersion,
|
||||||
|
@ -468,7 +554,6 @@ fn check_tx_version(
|
||||||
return Err(TransactionError::TransactionVersionInvalid);
|
return Err(TransactionError::TransactionVersionInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Doc is wrong here
|
|
||||||
let min = min_tx_version(hf);
|
let min = min_tx_version(hf);
|
||||||
if version < &min && decoy_info.not_mixable == 0 {
|
if version < &min && decoy_info.not_mixable == 0 {
|
||||||
return Err(TransactionError::TransactionVersionInvalid);
|
return Err(TransactionError::TransactionVersionInvalid);
|
||||||
|
@ -483,6 +568,7 @@ fn check_tx_version(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the default maximum tx version for the given hard-fork.
|
||||||
fn max_tx_version(hf: &HardFork) -> TxVersion {
|
fn max_tx_version(hf: &HardFork) -> TxVersion {
|
||||||
if hf <= &HardFork::V3 {
|
if hf <= &HardFork::V3 {
|
||||||
TxVersion::RingSignatures
|
TxVersion::RingSignatures
|
||||||
|
@ -491,6 +577,7 @@ fn max_tx_version(hf: &HardFork) -> TxVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the default minimum tx version for the given hard-fork.
|
||||||
fn min_tx_version(hf: &HardFork) -> TxVersion {
|
fn min_tx_version(hf: &HardFork) -> TxVersion {
|
||||||
if hf >= &HardFork::V6 {
|
if hf >= &HardFork::V6 {
|
||||||
TxVersion::RingCT
|
TxVersion::RingCT
|
||||||
|
@ -499,16 +586,42 @@ fn min_tx_version(hf: &HardFork) -> TxVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transaction_weight_limit(hf: &HardFork) -> usize {
|
||||||
|
penalty_free_zone(hf) / 2 - 600
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the transaction is semantically valid.
|
||||||
|
///
|
||||||
|
/// Semantic rules are rules that don't require blockchain context, the hard-fork does not require blockchain context as:
|
||||||
|
/// - The tx-pool will use the current hard-fork
|
||||||
|
/// - When syncing the hard-fork is in the block header.
|
||||||
|
///
|
||||||
|
/// To fully verify a transaction this must be accompanied with [`check_transaction_contextual`]
|
||||||
|
///
|
||||||
pub fn check_transaction_semantic(
|
pub fn check_transaction_semantic(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
|
tx_blob_size: usize,
|
||||||
|
tx_weight: usize,
|
||||||
tx_hash: &[u8; 32],
|
tx_hash: &[u8; 32],
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
||||||
) -> Result<u64, TransactionError> {
|
) -> Result<u64, TransactionError> {
|
||||||
|
// https://monero-book.cuprate.org/consensus_rules/transactions.html#transaction-size
|
||||||
|
if tx_blob_size > MAX_TX_BLOB_SIZE
|
||||||
|
|| (hf >= &HardFork::V8 && tx_weight > transaction_weight_limit(hf))
|
||||||
|
{
|
||||||
|
Err(TransactionError::TooBig)?;
|
||||||
|
}
|
||||||
|
|
||||||
let tx_version = TxVersion::from_raw(tx.prefix.version)
|
let tx_version = TxVersion::from_raw(tx.prefix.version)
|
||||||
.ok_or(TransactionError::TransactionVersionInvalid)?;
|
.ok_or(TransactionError::TransactionVersionInvalid)?;
|
||||||
|
|
||||||
let outputs_sum = check_outputs_semantics(&tx.prefix.outputs, hf, &tx_version)?;
|
let outputs_sum = check_outputs_semantics(
|
||||||
|
&tx.prefix.outputs,
|
||||||
|
hf,
|
||||||
|
&tx_version,
|
||||||
|
&tx.rct_signatures.rct_type(),
|
||||||
|
)?;
|
||||||
let inputs_sum = check_inputs_semantics(&tx.prefix.inputs, hf)?;
|
let inputs_sum = check_inputs_semantics(&tx.prefix.inputs, hf)?;
|
||||||
|
|
||||||
let fee = match tx_version {
|
let fee = match tx_version {
|
||||||
|
@ -528,6 +641,12 @@ pub fn check_transaction_semantic(
|
||||||
Ok(fee)
|
Ok(fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the transaction is contextually valid.
|
||||||
|
///
|
||||||
|
/// To fully verify a transaction this must be accompanied with [`check_transaction_semantic`]
|
||||||
|
///
|
||||||
|
/// `current_time_lock_timestamp` must be: https://monero-book.cuprate.org/consensus_rules/transactions/unlock_time.html#getting-the-current-time
|
||||||
|
|
||||||
pub fn check_transaction_contextual(
|
pub fn check_transaction_contextual(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
tx_ring_members_info: &TxRingMembersInfo,
|
tx_ring_members_info: &TxRingMembersInfo,
|
||||||
|
@ -556,7 +675,7 @@ pub fn check_transaction_contextual(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match tx_version {
|
match tx_version {
|
||||||
TxVersion::RingSignatures => ring_signatures::verify_inputs_signatures(
|
TxVersion::RingSignatures => ring_signatures::check_input_signatures(
|
||||||
&tx.prefix.inputs,
|
&tx.prefix.inputs,
|
||||||
&tx.signatures,
|
&tx.signatures,
|
||||||
&tx_ring_members_info.rings,
|
&tx_ring_members_info.rings,
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl Rings {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TxRingMembersInfo {
|
pub struct TxRingMembersInfo {
|
||||||
pub rings: Rings,
|
pub rings: Rings,
|
||||||
/// Information on the structure of the decoys, will be [`None`] for txs before [`HardFork::V1`]
|
/// Information on the structure of the decoys, must be [`None`] for txs before [`HardFork::V1`]
|
||||||
pub decoy_info: Option<DecoyInfo>,
|
pub decoy_info: Option<DecoyInfo>,
|
||||||
pub youngest_used_out_height: u64,
|
pub youngest_used_out_height: u64,
|
||||||
pub time_locked_outs: Vec<Timelock>,
|
pub time_locked_outs: Vec<Timelock>,
|
||||||
|
@ -277,7 +277,7 @@ impl DecoyInfo {
|
||||||
/// Returns the default minimum amount of decoys for a hard-fork.
|
/// Returns the default minimum amount of decoys for a hard-fork.
|
||||||
/// **There are exceptions to this always being the minimum decoys**
|
/// **There are exceptions to this always being the minimum decoys**
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/decoys.html#minimum-amount-of-decoys
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#default-minimum-decoys
|
||||||
pub(crate) fn minimum_decoys(hf: &HardFork) -> usize {
|
pub(crate) fn minimum_decoys(hf: &HardFork) -> usize {
|
||||||
use HardFork as HF;
|
use HardFork as HF;
|
||||||
match hf {
|
match hf {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use monero_serai::{
|
||||||
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
|
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
|
||||||
RctPrunable, RctSignatures, RctType,
|
RctPrunable, RctSignatures, RctType,
|
||||||
},
|
},
|
||||||
transaction::{Input, Output, Transaction},
|
transaction::{Input, Transaction},
|
||||||
H,
|
H,
|
||||||
};
|
};
|
||||||
use multiexp::BatchVerifier;
|
use multiexp::BatchVerifier;
|
||||||
|
@ -27,8 +27,6 @@ const GRANDFATHERED_TRANSACTIONS: [[u8; 32]; 2] = [
|
||||||
pub enum RingCTError {
|
pub enum RingCTError {
|
||||||
#[error("The RingCT type used is not allowed.")]
|
#[error("The RingCT type used is not allowed.")]
|
||||||
TypeNotAllowed,
|
TypeNotAllowed,
|
||||||
#[error("One or more of the outputs do not have a zero amount.")]
|
|
||||||
OutputNotZero,
|
|
||||||
#[error("RingCT simple: sum pseudo-outs does not equal outputs.")]
|
#[error("RingCT simple: sum pseudo-outs does not equal outputs.")]
|
||||||
SimpleAmountDoNotBalance,
|
SimpleAmountDoNotBalance,
|
||||||
#[error("The borromean range proof is invalid.")]
|
#[error("The borromean range proof is invalid.")]
|
||||||
|
@ -61,19 +59,6 @@ fn check_rct_type(ty: &RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks all the outputs have a zero amount.
|
|
||||||
///
|
|
||||||
/// https://monero-book.cuprate.org/consensus_rules/ring_ct.html#output-amount
|
|
||||||
fn check_output_amount(outputs: &[Output]) -> Result<(), RingCTError> {
|
|
||||||
outputs.iter().try_for_each(|out| {
|
|
||||||
if out.amount.is_none() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(RingCTError::OutputNotZero)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the pseudo-outs sum to the same point as the output commitments.
|
/// Checks that the pseudo-outs sum to the same point as the output commitments.
|
||||||
///
|
///
|
||||||
/// https://monero-book.cuprate.org/consensus_rules/ring_ct.html#pseudo-outs-outpks-balance
|
/// https://monero-book.cuprate.org/consensus_rules/ring_ct.html#pseudo-outs-outpks-balance
|
||||||
|
@ -133,28 +118,29 @@ fn check_output_range_proofs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ring_ct_semantic_checks(
|
pub(crate) fn ring_ct_semantic_checks(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
tx_hash: &[u8; 32],
|
tx_hash: &[u8; 32],
|
||||||
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
) -> Result<(), RingCTError> {
|
) -> Result<(), RingCTError> {
|
||||||
check_output_amount(&tx.prefix.outputs)?;
|
let rct_type = tx.rct_signatures.rct_type();
|
||||||
check_rct_type(&tx.rct_signatures.rct_type(), *hf, tx_hash)?;
|
|
||||||
|
check_rct_type(&rct_type, *hf, tx_hash)?;
|
||||||
check_output_range_proofs(&tx.rct_signatures, verifier)?;
|
check_output_range_proofs(&tx.rct_signatures, verifier)?;
|
||||||
|
|
||||||
if tx.rct_signatures.rct_type() != RctType::MlsagAggregate {
|
if rct_type != RctType::MlsagAggregate {
|
||||||
simple_type_balances(&tx.rct_signatures)?;
|
simple_type_balances(&tx.rct_signatures)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check the input signatures, MLSAG, CLSAG.
|
/// Check the input signatures: MLSAG, CLSAG.
|
||||||
///
|
///
|
||||||
/// https://monero-book.cuprate.org/consensus_rules/ring_ct/mlsag.html
|
/// https://monero-book.cuprate.org/consensus_rules/ring_ct/mlsag.html
|
||||||
/// https://monero-book.cuprate.org/consensus_rules/ring_ct/clsag.html
|
/// https://monero-book.cuprate.org/consensus_rules/ring_ct/clsag.html
|
||||||
pub fn check_input_signatures(
|
pub(crate) fn check_input_signatures(
|
||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
inputs: &[Input],
|
inputs: &[Input],
|
||||||
rct_sig: &RctSignatures,
|
rct_sig: &RctSignatures,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Version 1 ring signature verification.
|
//! Version 1 ring signature verification.
|
||||||
//!
|
//!
|
||||||
//! Some checks have to be done at deserialization or with data we don't have so we can't do them here, those checks are:
|
//! Some checks have to be done at deserialization or with data we don't have so we can't do them here, those checks are:
|
||||||
//! https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#signatures-must-be-canonical
|
//! https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html#signatures-must-be-canonical
|
||||||
//! this happens at deserialization in monero-serai.
|
//! this happens at deserialization in monero-serai.
|
||||||
//! https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#amount-of-signatures-in-a-ring
|
//! https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html#amount-of-signatures-in-a-ring
|
||||||
//! and this happens during ring signature verification in monero-serai.
|
//! and this happens during ring signature verification in monero-serai.
|
||||||
//!
|
//!
|
||||||
use monero_serai::{ring_signatures::RingSignature, transaction::Input};
|
use monero_serai::{ring_signatures::RingSignature, transaction::Input};
|
||||||
|
@ -16,9 +16,8 @@ use crate::try_par_iter;
|
||||||
|
|
||||||
/// Verifies the ring signature.
|
/// Verifies the ring signature.
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#the-ring-signature-must-be-valid
|
/// ref: https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#amount-of-ring-signatures
|
pub fn check_input_signatures(
|
||||||
pub fn verify_inputs_signatures(
|
|
||||||
inputs: &[Input],
|
inputs: &[Input],
|
||||||
signatures: &[RingSignature],
|
signatures: &[RingSignature],
|
||||||
rings: &Rings,
|
rings: &Rings,
|
||||||
|
@ -26,6 +25,7 @@ pub fn verify_inputs_signatures(
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
match rings {
|
match rings {
|
||||||
Rings::Legacy(rings) => {
|
Rings::Legacy(rings) => {
|
||||||
|
// https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html#amount-of-ring-signatures
|
||||||
// rings.len() != inputs.len() can't happen but check any way.
|
// rings.len() != inputs.len() can't happen but check any way.
|
||||||
if signatures.len() != inputs.len() || rings.len() != inputs.len() {
|
if signatures.len() != inputs.len() || rings.len() != inputs.len() {
|
||||||
return Err(TransactionError::RingSignatureIncorrect);
|
return Err(TransactionError::RingSignatureIncorrect);
|
||||||
|
|
|
@ -16,6 +16,8 @@ use rayon::prelude::*;
|
||||||
use tower::ServiceExt;
|
use tower::ServiceExt;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
use monero_consensus::blocks::{penalty_free_zone, PENALTY_FREE_ZONE_5};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
helper::{median, rayon_spawn_async},
|
helper::{median, rayon_spawn_async},
|
||||||
Database, DatabaseRequest, DatabaseResponse, ExtendedConsensusError, HardFork,
|
Database, DatabaseRequest, DatabaseResponse, ExtendedConsensusError, HardFork,
|
||||||
|
@ -24,26 +26,9 @@ use crate::{
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) mod tests;
|
pub(super) mod tests;
|
||||||
|
|
||||||
const PENALTY_FREE_ZONE_1: usize = 20000;
|
|
||||||
const PENALTY_FREE_ZONE_2: usize = 60000;
|
|
||||||
const PENALTY_FREE_ZONE_5: usize = 300000;
|
|
||||||
|
|
||||||
const SHORT_TERM_WINDOW: u64 = 100;
|
const SHORT_TERM_WINDOW: u64 = 100;
|
||||||
const LONG_TERM_WINDOW: u64 = 100000;
|
const LONG_TERM_WINDOW: u64 = 100000;
|
||||||
|
|
||||||
/// Returns the penalty free zone
|
|
||||||
///
|
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/weight_limit.html#penalty-free-zone
|
|
||||||
pub fn penalty_free_zone(hf: &HardFork) -> usize {
|
|
||||||
if hf == &HardFork::V1 {
|
|
||||||
PENALTY_FREE_ZONE_1
|
|
||||||
} else if hf >= &HardFork::V2 && hf < &HardFork::V5 {
|
|
||||||
PENALTY_FREE_ZONE_2
|
|
||||||
} else {
|
|
||||||
PENALTY_FREE_ZONE_5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration for the block weight cache.
|
/// Configuration for the block weight cache.
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use monero_serai::ringct::RctType;
|
||||||
use monero_serai::transaction::Transaction;
|
use monero_serai::transaction::Transaction;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
@ -50,16 +51,26 @@ impl TransactionVerificationData {
|
||||||
verifier: Arc<MultiThreadedBatchVerifier>,
|
verifier: Arc<MultiThreadedBatchVerifier>,
|
||||||
) -> Result<TransactionVerificationData, ConsensusError> {
|
) -> Result<TransactionVerificationData, ConsensusError> {
|
||||||
let tx_hash = tx.hash();
|
let tx_hash = tx.hash();
|
||||||
|
let tx_blob = tx.serialize();
|
||||||
|
|
||||||
|
// the tx weight is only different from the blobs length for bp(+) txs.
|
||||||
|
let tx_weight = match tx.rct_signatures.rct_type() {
|
||||||
|
RctType::Bulletproofs
|
||||||
|
| RctType::BulletproofsCompactAmount
|
||||||
|
| RctType::Clsag
|
||||||
|
| RctType::BulletproofsPlus => tx.weight(),
|
||||||
|
_ => tx_blob.len(),
|
||||||
|
};
|
||||||
|
|
||||||
let fee = verifier.queue_statement(|verifier| {
|
let fee = verifier.queue_statement(|verifier| {
|
||||||
check_transaction_semantic(&tx, &tx_hash, hf, verifier)
|
check_transaction_semantic(&tx, tx_blob.len(), tx_weight, &tx_hash, hf, verifier)
|
||||||
.map_err(ConsensusError::Transaction)
|
.map_err(ConsensusError::Transaction)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(TransactionVerificationData {
|
Ok(TransactionVerificationData {
|
||||||
tx_hash,
|
tx_hash,
|
||||||
tx_blob: tx.serialize(),
|
tx_blob,
|
||||||
tx_weight: tx.weight(),
|
tx_weight,
|
||||||
fee,
|
fee,
|
||||||
rings_member_info: std::sync::Mutex::new(None),
|
rings_member_info: std::sync::Mutex::new(None),
|
||||||
version: TxVersion::from_raw(tx.prefix.version)
|
version: TxVersion::from_raw(tx.prefix.version)
|
||||||
|
|
Loading…
Reference in a new issue