mirror of
https://github.com/hinto-janai/cuprate.git
synced 2024-12-22 19:49:33 +00:00
integrate ringCT rules into the RPC scanner.
This commit is contained in:
parent
f037532610
commit
5d6fb3f6b9
12 changed files with 357 additions and 151 deletions
|
@ -5,7 +5,6 @@ mod decomposed_amount;
|
||||||
pub mod genesis;
|
pub mod genesis;
|
||||||
pub mod hard_forks;
|
pub mod hard_forks;
|
||||||
pub mod miner_tx;
|
pub mod miner_tx;
|
||||||
pub mod signatures;
|
|
||||||
pub mod transactions;
|
pub mod transactions;
|
||||||
|
|
||||||
pub use decomposed_amount::is_decomposed_amount;
|
pub use decomposed_amount::is_decomposed_amount;
|
||||||
|
@ -18,8 +17,6 @@ pub enum ConsensusError {
|
||||||
Block(#[from] blocks::BlockError),
|
Block(#[from] blocks::BlockError),
|
||||||
#[error("Transaction error: {0}")]
|
#[error("Transaction error: {0}")]
|
||||||
Transaction(#[from] transactions::TransactionError),
|
Transaction(#[from] transactions::TransactionError),
|
||||||
#[error("Signatures error: {0}")]
|
|
||||||
Signatures(#[from] signatures::SignatureError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that a point is canonically encoded.
|
/// Checks that a point is canonically encoded.
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
use monero_serai::transaction::Transaction;
|
|
||||||
|
|
||||||
use crate::transactions::Rings;
|
|
||||||
|
|
||||||
mod ring_signatures;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
|
||||||
pub enum SignatureError {
|
|
||||||
#[error("Number of signatures is different to the amount required.")]
|
|
||||||
MismatchSignatureSize,
|
|
||||||
#[error("The signature is incorrect.")]
|
|
||||||
IncorrectSignature,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_contextual_signatures(tx: &Transaction, rings: &Rings) -> Result<(), SignatureError> {
|
|
||||||
match rings {
|
|
||||||
Rings::Legacy(_) => ring_signatures::verify_inputs_signatures(
|
|
||||||
&tx.prefix.inputs,
|
|
||||||
&tx.signatures,
|
|
||||||
rings,
|
|
||||||
&tx.signature_hash(),
|
|
||||||
),
|
|
||||||
_ => panic!("TODO: RCT"),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,16 @@
|
||||||
use std::{cmp::Ordering, collections::HashSet, sync::Arc};
|
use std::{cmp::Ordering, collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use monero_serai::transaction::{Input, Output, Timelock};
|
use monero_serai::transaction::{Input, Output, Timelock, Transaction};
|
||||||
|
use multiexp::BatchVerifier;
|
||||||
|
|
||||||
use crate::{check_point_canonically_encoded, is_decomposed_amount, HardFork};
|
use crate::{check_point_canonically_encoded, is_decomposed_amount, HardFork};
|
||||||
|
|
||||||
mod contextual_data;
|
mod contextual_data;
|
||||||
mod ring_ct;
|
mod ring_ct;
|
||||||
|
mod ring_signatures;
|
||||||
|
|
||||||
pub use contextual_data::*;
|
pub use contextual_data::*;
|
||||||
|
pub use ring_ct::RingCTError;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
pub enum TransactionError {
|
pub enum TransactionError {
|
||||||
|
@ -48,6 +52,12 @@ pub enum TransactionError {
|
||||||
NoInputs,
|
NoInputs,
|
||||||
#[error("Ring member not in database")]
|
#[error("Ring member not in database")]
|
||||||
RingMemberNotFound,
|
RingMemberNotFound,
|
||||||
|
//-------------------------------------------------------- Ring Signatures
|
||||||
|
#[error("Ring signature incorrect.")]
|
||||||
|
RingSignatureIncorrect,
|
||||||
|
//-------------------------------------------------------- RingCT
|
||||||
|
#[error("RingCT Error: {0}.")]
|
||||||
|
RingCTError(#[from] ring_ct::RingCTError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
@ -148,7 +158,7 @@ fn sum_outputs_v1(outputs: &[Output], hf: &HardFork) -> Result<u64, TransactionE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
pub fn check_outputs(
|
fn check_outputs_semantics(
|
||||||
outputs: &[Output],
|
outputs: &[Output],
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
tx_version: &TxVersion,
|
tx_version: &TxVersion,
|
||||||
|
@ -158,37 +168,12 @@ pub fn check_outputs(
|
||||||
|
|
||||||
match tx_version {
|
match tx_version {
|
||||||
TxVersion::RingSignatures => sum_outputs_v1(outputs, hf),
|
TxVersion::RingSignatures => sum_outputs_v1(outputs, hf),
|
||||||
_ => todo!("RingCT"),
|
TxVersion::RingCT => Ok(0), // RCT outputs are checked to be zero in RCT checks.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------- TIME LOCKS
|
//----------------------------------------------------------------------------------------------------------- TIME LOCKS
|
||||||
|
|
||||||
/// 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
|
|
||||||
///
|
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#unlock-time
|
|
||||||
pub fn check_all_time_locks(
|
|
||||||
time_locks: &[Timelock],
|
|
||||||
current_chain_height: u64,
|
|
||||||
current_time_lock_timestamp: u64,
|
|
||||||
hf: &HardFork,
|
|
||||||
) -> Result<(), TransactionError> {
|
|
||||||
time_locks.iter().try_for_each(|time_lock| {
|
|
||||||
if !output_unlocked(
|
|
||||||
time_lock,
|
|
||||||
current_chain_height,
|
|
||||||
current_time_lock_timestamp,
|
|
||||||
hf,
|
|
||||||
) {
|
|
||||||
Err(TransactionError::OneOrMoreDecoysLocked)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#unlock-time
|
||||||
|
@ -229,6 +214,31 @@ fn check_timestamp_time_lock(
|
||||||
current_time_lock_timestamp + hf.block_time().as_secs() >= unlock_timestamp
|
current_time_lock_timestamp + hf.block_time().as_secs() >= unlock_timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
///
|
||||||
|
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/unlock_time.html#unlock-time
|
||||||
|
fn check_all_time_locks(
|
||||||
|
time_locks: &[Timelock],
|
||||||
|
current_chain_height: u64,
|
||||||
|
current_time_lock_timestamp: u64,
|
||||||
|
hf: &HardFork,
|
||||||
|
) -> Result<(), TransactionError> {
|
||||||
|
time_locks.iter().try_for_each(|time_lock| {
|
||||||
|
if !output_unlocked(
|
||||||
|
time_lock,
|
||||||
|
current_chain_height,
|
||||||
|
current_time_lock_timestamp,
|
||||||
|
hf,
|
||||||
|
) {
|
||||||
|
Err(TransactionError::OneOrMoreDecoysLocked)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------- INPUTS
|
//----------------------------------------------------------------------------------------------------------- INPUTS
|
||||||
|
|
||||||
/// Checks the decoys are allowed.
|
/// Checks the decoys are allowed.
|
||||||
|
@ -273,7 +283,7 @@ fn check_decoy_info(decoy_info: &DecoyInfo, hf: &HardFork) -> Result<(), Transac
|
||||||
///
|
///
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#unique-key-image
|
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#unique-key-image
|
||||||
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#torsion-free-key-image
|
/// https://cuprate.github.io/monero-book/consensus_rules/transactions.html#torsion-free-key-image
|
||||||
pub(crate) fn check_key_images(
|
fn check_key_images(
|
||||||
input: &Input,
|
input: &Input,
|
||||||
spent_kis: &mut HashSet<[u8; 32]>,
|
spent_kis: &mut HashSet<[u8; 32]>,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
|
@ -351,7 +361,7 @@ fn check_inputs_sorted(inputs: &[Input], hf: &HardFork) -> Result<(), Transactio
|
||||||
if hf >= &HardFork::V7 {
|
if hf >= &HardFork::V7 {
|
||||||
for inps in inputs.windows(2) {
|
for inps in inputs.windows(2) {
|
||||||
match get_ki(&inps[0])?.cmp(&get_ki(&inps[1])?) {
|
match get_ki(&inps[0])?.cmp(&get_ki(&inps[1])?) {
|
||||||
Ordering::Less => (),
|
Ordering::Greater => (),
|
||||||
_ => return Err(TransactionError::InputsAreNotOrdered),
|
_ => return Err(TransactionError::InputsAreNotOrdered),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,7 +393,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
|
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/pre_rct.html#inputs-and-outputs-must-not-overflow
|
||||||
fn sum_inputs_v1(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 {
|
||||||
match inp {
|
match inp {
|
||||||
|
@ -399,22 +409,30 @@ fn sum_inputs_v1(inputs: &[Input]) -> Result<u64, TransactionError> {
|
||||||
Ok(sum)
|
Ok(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks all input consensus rules.
|
fn check_inputs_semantics(inputs: &[Input], hf: &HardFork) -> Result<u64, TransactionError> {
|
||||||
///
|
|
||||||
/// TODO: list rules.
|
|
||||||
///
|
|
||||||
pub fn check_inputs(
|
|
||||||
inputs: &[Input],
|
|
||||||
tx_ring_members_info: &TxRingMembersInfo,
|
|
||||||
current_chain_height: u64,
|
|
||||||
hf: &HardFork,
|
|
||||||
tx_version: &TxVersion,
|
|
||||||
spent_kis: Arc<std::sync::Mutex<HashSet<[u8; 32]>>>,
|
|
||||||
) -> Result<u64, TransactionError> {
|
|
||||||
if inputs.is_empty() {
|
if inputs.is_empty() {
|
||||||
return Err(TransactionError::NoInputs);
|
return Err(TransactionError::NoInputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
check_input_type(input)?;
|
||||||
|
check_input_has_decoys(input)?;
|
||||||
|
|
||||||
|
check_ring_members_unique(input, hf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_inputs_sorted(inputs, hf)?;
|
||||||
|
|
||||||
|
sum_inputs_check_overflow(inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_inputs_contextual(
|
||||||
|
inputs: &[Input],
|
||||||
|
tx_ring_members_info: &TxRingMembersInfo,
|
||||||
|
current_chain_height: u64,
|
||||||
|
hf: &HardFork,
|
||||||
|
spent_kis: Arc<std::sync::Mutex<HashSet<[u8; 32]>>>,
|
||||||
|
) -> Result<(), TransactionError> {
|
||||||
check_10_block_lock(
|
check_10_block_lock(
|
||||||
tx_ring_members_info.youngest_used_out_height,
|
tx_ring_members_info.youngest_used_out_height,
|
||||||
current_chain_height,
|
current_chain_height,
|
||||||
|
@ -427,32 +445,20 @@ pub fn check_inputs(
|
||||||
assert_eq!(hf, &HardFork::V1);
|
assert_eq!(hf, &HardFork::V1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut spent_kis_lock = spent_kis.lock().unwrap();
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
check_input_type(input)?;
|
|
||||||
check_input_has_decoys(input)?;
|
|
||||||
|
|
||||||
check_ring_members_unique(input, hf)?;
|
|
||||||
|
|
||||||
let mut spent_kis_lock = spent_kis.lock().unwrap();
|
|
||||||
check_key_images(input, &mut spent_kis_lock)?;
|
check_key_images(input, &mut spent_kis_lock)?;
|
||||||
// Adding this here for clarity so we don't add more work here while the mutex guard is still
|
|
||||||
// in scope.
|
|
||||||
drop(spent_kis_lock);
|
|
||||||
}
|
}
|
||||||
|
drop(spent_kis_lock);
|
||||||
|
|
||||||
check_inputs_sorted(inputs, hf)?;
|
Ok(())
|
||||||
|
|
||||||
match tx_version {
|
|
||||||
TxVersion::RingSignatures => sum_inputs_v1(inputs),
|
|
||||||
_ => panic!("TODO: RCT"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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://cuprate.github.io/monero-book/consensus_rules/transactions.html#version
|
||||||
pub fn check_tx_version(
|
fn check_tx_version(
|
||||||
decoy_info: &Option<contextual_data::DecoyInfo>,
|
decoy_info: &Option<DecoyInfo>,
|
||||||
version: &TxVersion,
|
version: &TxVersion,
|
||||||
hf: &HardFork,
|
hf: &HardFork,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
|
@ -492,3 +498,75 @@ fn min_tx_version(hf: &HardFork) -> TxVersion {
|
||||||
TxVersion::RingSignatures
|
TxVersion::RingSignatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_transaction_semantic(
|
||||||
|
tx: &Transaction,
|
||||||
|
tx_hash: &[u8; 32],
|
||||||
|
hf: &HardFork,
|
||||||
|
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
||||||
|
) -> Result<u64, TransactionError> {
|
||||||
|
let tx_version = TxVersion::from_raw(tx.prefix.version)
|
||||||
|
.ok_or(TransactionError::TransactionVersionInvalid)?;
|
||||||
|
|
||||||
|
let outputs_sum = check_outputs_semantics(&tx.prefix.outputs, hf, &tx_version)?;
|
||||||
|
let inputs_sum = check_inputs_semantics(&tx.prefix.inputs, hf)?;
|
||||||
|
|
||||||
|
let fee = match tx_version {
|
||||||
|
TxVersion::RingSignatures => {
|
||||||
|
if outputs_sum >= inputs_sum {
|
||||||
|
Err(TransactionError::OutputsTooHigh)?;
|
||||||
|
}
|
||||||
|
inputs_sum - outputs_sum
|
||||||
|
}
|
||||||
|
TxVersion::RingCT => {
|
||||||
|
ring_ct::ring_ct_semantic_checks(tx, tx_hash, verifier, hf)?;
|
||||||
|
|
||||||
|
tx.rct_signatures.base.fee
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_transaction_contextual(
|
||||||
|
tx: &Transaction,
|
||||||
|
tx_ring_members_info: &TxRingMembersInfo,
|
||||||
|
current_chain_height: u64,
|
||||||
|
current_time_lock_timestamp: u64,
|
||||||
|
hf: &HardFork,
|
||||||
|
spent_kis: Arc<std::sync::Mutex<HashSet<[u8; 32]>>>,
|
||||||
|
) -> Result<(), TransactionError> {
|
||||||
|
let tx_version = TxVersion::from_raw(tx.prefix.version)
|
||||||
|
.ok_or(TransactionError::TransactionVersionInvalid)?;
|
||||||
|
|
||||||
|
check_inputs_contextual(
|
||||||
|
&tx.prefix.inputs,
|
||||||
|
tx_ring_members_info,
|
||||||
|
current_chain_height,
|
||||||
|
hf,
|
||||||
|
spent_kis,
|
||||||
|
)?;
|
||||||
|
check_tx_version(&tx_ring_members_info.decoy_info, &tx_version, hf)?;
|
||||||
|
|
||||||
|
check_all_time_locks(
|
||||||
|
&tx_ring_members_info.time_locked_outs,
|
||||||
|
current_chain_height,
|
||||||
|
current_time_lock_timestamp,
|
||||||
|
hf,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match tx_version {
|
||||||
|
TxVersion::RingSignatures => ring_signatures::verify_inputs_signatures(
|
||||||
|
&tx.prefix.inputs,
|
||||||
|
&tx.signatures,
|
||||||
|
&tx_ring_members_info.rings,
|
||||||
|
&tx.signature_hash(),
|
||||||
|
),
|
||||||
|
TxVersion::RingCT => Ok(ring_ct::check_input_signatures(
|
||||||
|
&tx.signature_hash(),
|
||||||
|
&tx.prefix.inputs,
|
||||||
|
&tx.rct_signatures,
|
||||||
|
&tx_ring_members_info.rings,
|
||||||
|
)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ impl DecoyInfo {
|
||||||
/// **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
|
/// https://cuprate.github.io/monero-book/consensus_rules/transactions/decoys.html#minimum-amount-of-decoys
|
||||||
pub 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 {
|
||||||
HF::V1 => panic!("hard-fork 1 does not use these rules!"),
|
HF::V1 => panic!("hard-fork 1 does not use these rules!"),
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use curve25519_dalek::{EdwardsPoint, Scalar};
|
use curve25519_dalek::{EdwardsPoint, Scalar};
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use monero_serai::{
|
use monero_serai::{
|
||||||
|
@ -8,7 +6,7 @@ use monero_serai::{
|
||||||
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
|
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
|
||||||
RctPrunable, RctSignatures, RctType,
|
RctPrunable, RctSignatures, RctType,
|
||||||
},
|
},
|
||||||
transaction::{Input, Output},
|
transaction::{Input, Output, Transaction},
|
||||||
H,
|
H,
|
||||||
};
|
};
|
||||||
use multiexp::BatchVerifier;
|
use multiexp::BatchVerifier;
|
||||||
|
@ -135,11 +133,28 @@ fn check_output_range_proofs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ring_ct_semantic_checks(
|
||||||
|
tx: &Transaction,
|
||||||
|
tx_hash: &[u8; 32],
|
||||||
|
verifier: &mut BatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
||||||
|
hf: &HardFork,
|
||||||
|
) -> Result<(), RingCTError> {
|
||||||
|
check_output_amount(&tx.prefix.outputs)?;
|
||||||
|
check_rct_type(&tx.rct_signatures.rct_type(), *hf, tx_hash)?;
|
||||||
|
check_output_range_proofs(&tx.rct_signatures, verifier)?;
|
||||||
|
|
||||||
|
if tx.rct_signatures.rct_type() != RctType::MlsagAggregate {
|
||||||
|
simple_type_balances(&tx.rct_signatures)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
fn check_input_signatures(
|
pub fn check_input_signatures(
|
||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
inputs: &[Input],
|
inputs: &[Input],
|
||||||
rct_sig: &RctSignatures,
|
rct_sig: &RctSignatures,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use monero_serai::{ring_signatures::RingSignature, transaction::Input};
|
||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use super::{Rings, SignatureError};
|
use super::{Rings, TransactionError};
|
||||||
use crate::try_par_iter;
|
use crate::try_par_iter;
|
||||||
|
|
||||||
/// Verifies the ring signature.
|
/// Verifies the ring signature.
|
||||||
|
@ -23,12 +23,12 @@ pub fn verify_inputs_signatures(
|
||||||
signatures: &[RingSignature],
|
signatures: &[RingSignature],
|
||||||
rings: &Rings,
|
rings: &Rings,
|
||||||
tx_sig_hash: &[u8; 32],
|
tx_sig_hash: &[u8; 32],
|
||||||
) -> Result<(), SignatureError> {
|
) -> Result<(), TransactionError> {
|
||||||
match rings {
|
match rings {
|
||||||
Rings::Legacy(rings) => {
|
Rings::Legacy(rings) => {
|
||||||
// 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(SignatureError::MismatchSignatureSize);
|
return Err(TransactionError::RingSignatureIncorrect);
|
||||||
}
|
}
|
||||||
|
|
||||||
try_par_iter(inputs)
|
try_par_iter(inputs)
|
||||||
|
@ -40,7 +40,7 @@ pub fn verify_inputs_signatures(
|
||||||
};
|
};
|
||||||
|
|
||||||
if !sig.verify(tx_sig_hash, ring, key_image) {
|
if !sig.verify(tx_sig_hash, ring, key_image) {
|
||||||
return Err(SignatureError::IncorrectSignature);
|
return Err(TransactionError::RingSignatureIncorrect);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
|
@ -8,7 +8,7 @@ use crate::ConsensusError;
|
||||||
|
|
||||||
/// A multi threaded batch verifier.
|
/// A multi threaded batch verifier.
|
||||||
pub struct MultiThreadedBatchVerifier {
|
pub struct MultiThreadedBatchVerifier {
|
||||||
internal: ThreadLocal<UnsafeCell<InternalBatchVerifier<usize, dalek_ff_group::EdwardsPoint>>>,
|
internal: ThreadLocal<UnsafeCell<InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiThreadedBatchVerifier {
|
impl MultiThreadedBatchVerifier {
|
||||||
|
@ -19,12 +19,12 @@ impl MultiThreadedBatchVerifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn queue_statement(
|
pub fn queue_statement<R>(
|
||||||
&self,
|
&self,
|
||||||
stmt: impl FnOnce(
|
stmt: impl FnOnce(
|
||||||
&mut InternalBatchVerifier<usize, dalek_ff_group::EdwardsPoint>,
|
&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>,
|
||||||
) -> Result<(), ConsensusError>,
|
) -> Result<R, ConsensusError>,
|
||||||
) -> Result<(), ConsensusError> {
|
) -> Result<R, ConsensusError> {
|
||||||
let verifier_cell = self
|
let verifier_cell = self
|
||||||
.internal
|
.internal
|
||||||
.get_or(|| UnsafeCell::new(InternalBatchVerifier::new(0)));
|
.get_or(|| UnsafeCell::new(InternalBatchVerifier::new(0)));
|
||||||
|
|
|
@ -8,6 +8,7 @@ use futures::{
|
||||||
SinkExt, StreamExt,
|
SinkExt, StreamExt,
|
||||||
};
|
};
|
||||||
use monero_serai::block::Block;
|
use monero_serai::block::Block;
|
||||||
|
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;
|
||||||
|
@ -21,13 +22,13 @@ use cuprate_consensus::{
|
||||||
},
|
},
|
||||||
initialize_blockchain_context, initialize_verifier,
|
initialize_blockchain_context, initialize_verifier,
|
||||||
rpc::{cache::ScanningCache, init_rpc_load_balancer, RpcConfig},
|
rpc::{cache::ScanningCache, init_rpc_load_balancer, RpcConfig},
|
||||||
Database, DatabaseRequest, DatabaseResponse, VerifiedBlockInformation, VerifyBlockRequest,
|
Database, DatabaseRequest, DatabaseResponse, PrePreparedBlock, VerifiedBlockInformation,
|
||||||
VerifyBlockResponse,
|
VerifyBlockRequest, VerifyBlockResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod tx_pool;
|
mod tx_pool;
|
||||||
|
|
||||||
const MAX_BLOCKS_IN_RANGE: u64 = 1000;
|
const MAX_BLOCKS_IN_RANGE: u64 = 500;
|
||||||
const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 1000;
|
const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 1000;
|
||||||
|
|
||||||
/// Calls for a batch of blocks, returning the response and the time it took.
|
/// Calls for a batch of blocks, returning the response and the time it took.
|
||||||
|
@ -166,21 +167,33 @@ where
|
||||||
|
|
||||||
let (block_tx, mut incoming_blocks) = mpsc::channel(3);
|
let (block_tx, mut incoming_blocks) = mpsc::channel(3);
|
||||||
|
|
||||||
|
let (mut prepped_blocks_tx, mut prepped_blocks_rx) = mpsc::channel(3);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
call_blocks(new_tx_chan, block_tx, start_height, chain_height, database).await
|
call_blocks(new_tx_chan, block_tx, start_height, chain_height, database).await
|
||||||
});
|
});
|
||||||
|
|
||||||
while let Some(incoming_blocks) = incoming_blocks.next().await {
|
tokio::spawn(async move {
|
||||||
|
while let Some(blocks) = incoming_blocks.next().await {
|
||||||
|
let blocks = rayon_spawn_async(|| {
|
||||||
|
blocks
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|block| PrePreparedBlock::new(block).unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
prepped_blocks_tx.send(blocks).await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while let Some(incoming_blocks) = prepped_blocks_rx.next().await {
|
||||||
let mut height;
|
let mut height;
|
||||||
for block in incoming_blocks {
|
for block in incoming_blocks {
|
||||||
let VerifyBlockResponse::MainChain(verified_block_info) = block_verifier
|
let VerifyBlockResponse::MainChain(verified_block_info) = block_verifier
|
||||||
.ready()
|
.ready()
|
||||||
.await?
|
.await?
|
||||||
.call(VerifyBlockRequest::MainChain(block))
|
.call(VerifyBlockRequest::MainChainPrepared(block))
|
||||||
.await?
|
.await?;
|
||||||
else {
|
|
||||||
panic!("Block verifier sent incorrect response!");
|
|
||||||
};
|
|
||||||
|
|
||||||
height = verified_block_info.height;
|
height = verified_block_info.height;
|
||||||
|
|
||||||
|
@ -316,3 +329,15 @@ async fn main() {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn rayon_spawn_async<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
R: Send + 'static,
|
||||||
|
{
|
||||||
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
rayon::spawn(|| {
|
||||||
|
let _ = tx.send(f());
|
||||||
|
});
|
||||||
|
rx.await.expect("The sender must not be dropped")
|
||||||
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@ use std::{
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use monero_serai::block::Block;
|
use monero_serai::block::Block;
|
||||||
|
use monero_serai::transaction::Input;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
|
||||||
|
use monero_consensus::blocks::BlockError;
|
||||||
|
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},
|
||||||
ConsensusError, HardFork,
|
ConsensusError, HardFork,
|
||||||
|
@ -35,6 +38,31 @@ pub struct PrePreparedBlock {
|
||||||
pub miner_tx_weight: usize,
|
pub miner_tx_weight: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrePreparedBlock {
|
||||||
|
pub fn new(block: Block) -> Result<PrePreparedBlock, ConsensusError> {
|
||||||
|
let (hf_version, hf_vote) =
|
||||||
|
HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?;
|
||||||
|
|
||||||
|
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else {
|
||||||
|
Err(ConsensusError::Block(BlockError::MinerTxError(
|
||||||
|
MinerTxError::InputNotOfTypeGen,
|
||||||
|
)))?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(PrePreparedBlock {
|
||||||
|
block_blob: block.serialize(),
|
||||||
|
hf_vote,
|
||||||
|
hf_version,
|
||||||
|
|
||||||
|
block_hash: block.hash(),
|
||||||
|
pow_hash: calculate_pow_hash(&block.serialize_hashable(), *height, &hf_vote)?,
|
||||||
|
|
||||||
|
miner_tx_weight: block.miner_tx.weight(),
|
||||||
|
block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerifiedBlockInformation {
|
pub struct VerifiedBlockInformation {
|
||||||
pub block: Block,
|
pub block: Block,
|
||||||
|
@ -51,12 +79,11 @@ pub struct VerifiedBlockInformation {
|
||||||
|
|
||||||
pub enum VerifyBlockRequest {
|
pub enum VerifyBlockRequest {
|
||||||
MainChain(Block),
|
MainChain(Block),
|
||||||
|
MainChainPrepared(PrePreparedBlock),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VerifyBlockResponse {
|
pub enum VerifyBlockResponse {
|
||||||
MainChain(VerifiedBlockInformation),
|
MainChain(VerifiedBlockInformation),
|
||||||
|
|
||||||
BatchSetup(Vec<PrePreparedBlock>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: it is probably a bad idea for this to derive clone, if 2 places (RPC, P2P) receive valid but different blocks
|
// TODO: it is probably a bad idea for this to derive clone, if 2 places (RPC, P2P) receive valid but different blocks
|
||||||
|
@ -138,12 +165,98 @@ where
|
||||||
VerifyBlockRequest::MainChain(block) => {
|
VerifyBlockRequest::MainChain(block) => {
|
||||||
verify_main_chain_block(block, context_svc, tx_verifier_svc, tx_pool).await
|
verify_main_chain_block(block, context_svc, tx_verifier_svc, tx_pool).await
|
||||||
}
|
}
|
||||||
|
VerifyBlockRequest::MainChainPrepared(prepped_block) => {
|
||||||
|
verify_main_chain_block_prepared(
|
||||||
|
prepped_block,
|
||||||
|
context_svc,
|
||||||
|
tx_verifier_svc,
|
||||||
|
tx_pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn verify_main_chain_block_prepared<C, TxV, TxP>(
|
||||||
|
prepped_block: PrePreparedBlock,
|
||||||
|
context_svc: C,
|
||||||
|
tx_verifier_svc: TxV,
|
||||||
|
tx_pool: TxP,
|
||||||
|
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
|
||||||
|
where
|
||||||
|
C: Service<
|
||||||
|
BlockChainContextRequest,
|
||||||
|
Response = BlockChainContextResponse,
|
||||||
|
Error = tower::BoxError,
|
||||||
|
> + Send
|
||||||
|
+ 'static,
|
||||||
|
C::Future: Send + 'static,
|
||||||
|
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>,
|
||||||
|
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
|
||||||
|
+ Clone
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
tracing::debug!("getting blockchain context");
|
||||||
|
let BlockChainContextResponse::Context(checked_context) = context_svc
|
||||||
|
.oneshot(BlockChainContextRequest::Get)
|
||||||
|
.await
|
||||||
|
.map_err(Into::<ExtendedConsensusError>::into)?
|
||||||
|
else {
|
||||||
|
panic!("Context service returned wrong response!");
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = checked_context.unchecked_blockchain_context().clone();
|
||||||
|
|
||||||
|
tracing::debug!("got blockchain context: {:?}", context);
|
||||||
|
|
||||||
|
let TxPoolResponse::Transactions(txs) = tx_pool
|
||||||
|
.oneshot(TxPoolRequest::Transactions(prepped_block.block.txs.clone()))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
tx_verifier_svc
|
||||||
|
.oneshot(VerifyTxRequest::Block {
|
||||||
|
txs: txs.clone(),
|
||||||
|
current_chain_height: context.chain_height,
|
||||||
|
time_for_time_lock: context.current_adjusted_timestamp_for_time_lock(),
|
||||||
|
hf: context.current_hf,
|
||||||
|
re_org_token: context.re_org_token.clone(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let block_weight =
|
||||||
|
prepped_block.miner_tx_weight + txs.iter().map(|tx| tx.tx_weight).sum::<usize>();
|
||||||
|
let total_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
|
||||||
|
|
||||||
|
let (hf_vote, generated_coins) = check_block(
|
||||||
|
&prepped_block.block,
|
||||||
|
total_fees,
|
||||||
|
block_weight,
|
||||||
|
prepped_block.block_blob.len(),
|
||||||
|
&context.context_to_verify_block,
|
||||||
|
)
|
||||||
|
.map_err(ConsensusError::Block)?;
|
||||||
|
|
||||||
|
check_block_pow(&prepped_block.pow_hash, context.next_difficulty)
|
||||||
|
.map_err(ConsensusError::Block)?;
|
||||||
|
|
||||||
|
Ok(VerifyBlockResponse::MainChain(VerifiedBlockInformation {
|
||||||
|
block_hash: prepped_block.block_hash,
|
||||||
|
block: prepped_block.block,
|
||||||
|
txs,
|
||||||
|
pow_hash: prepped_block.pow_hash,
|
||||||
|
generated_coins,
|
||||||
|
weight: block_weight,
|
||||||
|
height: context.chain_height,
|
||||||
|
long_term_weight: context.next_block_long_term_weight(block_weight),
|
||||||
|
hf_vote,
|
||||||
|
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
||||||
use monero_consensus::{transactions::OutputOnChain, ConsensusError, HardFork};
|
use monero_consensus::{transactions::OutputOnChain, ConsensusError, HardFork};
|
||||||
|
|
||||||
//mod batch_verifier;
|
//mod batch_verifier;
|
||||||
|
mod batch_verifier;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
mod helper;
|
mod helper;
|
||||||
|
|
|
@ -45,7 +45,8 @@ impl ScanningCache {
|
||||||
pub fn load(file: &Path) -> Result<ScanningCache, tower::BoxError> {
|
pub fn load(file: &Path) -> Result<ScanningCache, tower::BoxError> {
|
||||||
let mut file = std::fs::OpenOptions::new().read(true).open(file)?;
|
let mut file = std::fs::OpenOptions::new().read(true).open(file)?;
|
||||||
|
|
||||||
Ok(borsh::from_reader(&mut file)?)
|
let data: ScanningCache = borsh::from_reader(&mut file)?;
|
||||||
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_new_block_data(
|
pub fn add_new_block_data(
|
||||||
|
|
|
@ -14,17 +14,16 @@ use tower::{Service, ServiceExt};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use monero_consensus::{
|
use monero_consensus::{
|
||||||
signatures::verify_contextual_signatures,
|
|
||||||
transactions::{
|
transactions::{
|
||||||
check_all_time_locks, check_inputs, check_outputs, check_tx_version, TransactionError,
|
check_transaction_contextual, check_transaction_semantic, RingCTError, TransactionError,
|
||||||
TxRingMembersInfo,
|
TxRingMembersInfo,
|
||||||
},
|
},
|
||||||
ConsensusError, HardFork, TxVersion,
|
ConsensusError, HardFork, TxVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::ReOrgToken, helper::rayon_spawn_async, Database, DatabaseRequest, DatabaseResponse,
|
batch_verifier::MultiThreadedBatchVerifier, context::ReOrgToken, helper::rayon_spawn_async,
|
||||||
ExtendedConsensusError,
|
Database, DatabaseRequest, DatabaseResponse, ExtendedConsensusError,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod contextual_data;
|
mod contextual_data;
|
||||||
|
@ -45,12 +44,23 @@ pub struct TransactionVerificationData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionVerificationData {
|
impl TransactionVerificationData {
|
||||||
pub fn new(tx: Transaction) -> Result<TransactionVerificationData, ConsensusError> {
|
pub fn new(
|
||||||
|
tx: Transaction,
|
||||||
|
hf: &HardFork,
|
||||||
|
verifier: Arc<MultiThreadedBatchVerifier>,
|
||||||
|
) -> Result<TransactionVerificationData, ConsensusError> {
|
||||||
|
let tx_hash = tx.hash();
|
||||||
|
|
||||||
|
let fee = verifier.queue_statement(|verifier| {
|
||||||
|
check_transaction_semantic(&tx, &tx_hash, hf, verifier)
|
||||||
|
.map_err(ConsensusError::Transaction)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(TransactionVerificationData {
|
Ok(TransactionVerificationData {
|
||||||
tx_hash: tx.hash(),
|
tx_hash,
|
||||||
tx_blob: tx.serialize(),
|
tx_blob: tx.serialize(),
|
||||||
tx_weight: tx.weight(),
|
tx_weight: tx.weight(),
|
||||||
fee: tx.rct_signatures.base.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)
|
||||||
.ok_or(TransactionError::TransactionVersionInvalid)?,
|
.ok_or(TransactionError::TransactionVersionInvalid)?,
|
||||||
|
@ -68,7 +78,7 @@ pub enum VerifyTxRequest {
|
||||||
hf: HardFork,
|
hf: HardFork,
|
||||||
re_org_token: ReOrgToken,
|
re_org_token: ReOrgToken,
|
||||||
},
|
},
|
||||||
/// Batches the setup of [`TransactionVerificationData`], does *minimal* verification, you need to call [`VerifyTxRequest::Block`]
|
/// Batches the setup of [`TransactionVerificationData`], does *some* verification, you need to call [`VerifyTxRequest::Block`]
|
||||||
/// with the returned data.
|
/// with the returned data.
|
||||||
BatchSetup {
|
BatchSetup {
|
||||||
txs: Vec<Transaction>,
|
txs: Vec<Transaction>,
|
||||||
|
@ -148,14 +158,29 @@ async fn batch_setup_transactions<D>(
|
||||||
where
|
where
|
||||||
D: Database + Clone + Sync + Send + 'static,
|
D: Database + Clone + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
|
let batch_verifier = Arc::new(MultiThreadedBatchVerifier::new(rayon::current_num_threads()));
|
||||||
|
|
||||||
|
let cloned_verifier = batch_verifier.clone();
|
||||||
// Move out of the async runtime and use rayon to parallelize the serialisation and hashing of the txs.
|
// Move out of the async runtime and use rayon to parallelize the serialisation and hashing of the txs.
|
||||||
let txs = rayon_spawn_async(|| {
|
let txs = rayon_spawn_async(move || {
|
||||||
txs.into_par_iter()
|
txs.into_par_iter()
|
||||||
.map(|tx| Ok(Arc::new(TransactionVerificationData::new(tx)?)))
|
.map(|tx| {
|
||||||
|
Ok(Arc::new(TransactionVerificationData::new(
|
||||||
|
tx,
|
||||||
|
&hf,
|
||||||
|
cloned_verifier.clone(),
|
||||||
|
)?))
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, ConsensusError>>()
|
.collect::<Result<Vec<_>, ConsensusError>>()
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if !Arc::into_inner(batch_verifier).unwrap().verify() {
|
||||||
|
Err(ConsensusError::Transaction(TransactionError::RingCTError(
|
||||||
|
RingCTError::BulletproofsRangeInvalid,
|
||||||
|
)))?
|
||||||
|
}
|
||||||
|
|
||||||
contextual_data::batch_fill_ring_member_info(&txs, &hf, re_org_token, database).await?;
|
contextual_data::batch_fill_ring_member_info(&txs, &hf, re_org_token, database).await?;
|
||||||
|
|
||||||
Ok(VerifyTxResponse::BatchSetupOk(txs))
|
Ok(VerifyTxResponse::BatchSetupOk(txs))
|
||||||
|
@ -223,44 +248,20 @@ fn verify_transaction_for_block(
|
||||||
hex::encode(tx_verification_data.tx_hash)
|
hex::encode(tx_verification_data.tx_hash)
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx_version = &tx_verification_data.version;
|
|
||||||
|
|
||||||
let rings_member_info_lock = tx_verification_data.rings_member_info.lock().unwrap();
|
let rings_member_info_lock = tx_verification_data.rings_member_info.lock().unwrap();
|
||||||
let rings_member_info = match rings_member_info_lock.deref() {
|
let rings_member_info = match rings_member_info_lock.deref() {
|
||||||
Some(rings_member_info) => rings_member_info,
|
Some(rings_member_info) => rings_member_info,
|
||||||
None => panic!("rings_member_info needs to be set to be able to verify!"),
|
None => panic!("rings_member_info needs to be set to be able to verify!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
check_tx_version(&rings_member_info.0.decoy_info, tx_version, &hf)?;
|
check_transaction_contextual(
|
||||||
|
&tx_verification_data.tx,
|
||||||
check_all_time_locks(
|
&rings_member_info.0,
|
||||||
&rings_member_info.0.time_locked_outs,
|
|
||||||
current_chain_height,
|
current_chain_height,
|
||||||
time_for_time_lock,
|
time_for_time_lock,
|
||||||
&hf,
|
&hf,
|
||||||
)?;
|
|
||||||
|
|
||||||
let sum_outputs = check_outputs(&tx_verification_data.tx.prefix.outputs, &hf, tx_version)?;
|
|
||||||
|
|
||||||
let sum_inputs = check_inputs(
|
|
||||||
tx_verification_data.tx.prefix.inputs.as_slice(),
|
|
||||||
&rings_member_info.0,
|
|
||||||
current_chain_height,
|
|
||||||
&hf,
|
|
||||||
tx_version,
|
|
||||||
spent_kis,
|
spent_kis,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if tx_version == &TxVersion::RingSignatures {
|
|
||||||
if sum_outputs >= sum_inputs {
|
|
||||||
Err(TransactionError::OutputsTooHigh)?;
|
|
||||||
}
|
|
||||||
// check that monero-serai is calculating the correct value here, why can't we just use this
|
|
||||||
// value? because we don't have this when we create the object.
|
|
||||||
assert_eq!(tx_verification_data.fee, sum_inputs - sum_outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
verify_contextual_signatures(&tx_verification_data.tx, &rings_member_info.0.rings)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue