update monero-serai

This commit is contained in:
Boog900 2024-06-28 22:17:11 +01:00
parent 7e9891de5b
commit 075e9b15cb
No known key found for this signature in database
GPG key ID: 42AB1287CB0041C2
23 changed files with 411 additions and 413 deletions

278
Cargo.lock generated
View file

@ -56,17 +56,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-stream"
version = "0.3.5"
@ -121,28 +110,12 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base58-monero"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978e81a45367d2409ecd33369a45dda2e9a3ca516153ec194de1fbda4b9fb79d"
dependencies = [
"thiserror",
"tiny-keccak",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bincode"
version = "1.3.3"
@ -343,15 +316,6 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -433,12 +397,6 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-bigint"
version = "0.5.5"
@ -523,12 +481,10 @@ dependencies = [
"cuprate-test-utils",
"cuprate-types",
"curve25519-dalek",
"dalek-ff-group",
"futures",
"hex",
"hex-literal",
"monero-serai",
"multiexp",
"proptest",
"proptest-derive",
"randomx-rs",
@ -549,11 +505,9 @@ dependencies = [
"cuprate-cryptonight",
"cuprate-helper",
"curve25519-dalek",
"dalek-ff-group",
"hex",
"hex-literal",
"monero-serai",
"multiexp",
"proptest",
"proptest-derive",
"rand",
@ -772,7 +726,9 @@ dependencies = [
"futures",
"hex",
"hex-literal",
"monero-rpc",
"monero-serai",
"monero-simple-request-rpc",
"pretty_assertions",
"serde",
"serde_json",
@ -838,7 +794,7 @@ dependencies = [
[[package]]
name = "dalek-ff-group"
version = "0.4.1"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"crypto-bigint",
"curve25519-dalek",
@ -957,27 +913,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "event-listener"
version = "5.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "2.1.0"
@ -1004,7 +939,7 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "flexible-transcript"
version = "0.3.2"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"blake2",
"digest",
@ -1238,15 +1173,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "1.1.0"
@ -1283,9 +1209,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.9.3"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "hyper"
@ -1656,63 +1582,151 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "monero-borromean"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"std-shims",
"zeroize",
]
[[package]]
name = "monero-bulletproofs"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"rand_core",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-clsag"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"dalek-ff-group",
"flexible-transcript",
"group",
"monero-generators",
"monero-io",
"monero-primitives",
"rand_chacha",
"rand_core",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-generators"
version = "0.4.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"dalek-ff-group",
"group",
"monero-io",
"sha3",
"std-shims",
"subtle",
]
[[package]]
name = "monero-io"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"std-shims",
]
[[package]]
name = "monero-mlsag"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"std-shims",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-primitives"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"sha3",
"std-shims",
"zeroize",
]
[[package]]
name = "monero-rpc"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"async-trait",
"curve25519-dalek",
"hex",
"monero-serai",
"serde",
"serde_json",
"std-shims",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-serai"
version = "0.1.4-alpha"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"async-lock",
"async-trait",
"base58-monero",
"curve25519-dalek",
"dalek-ff-group",
"digest_auth",
"flexible-transcript",
"group",
"hex",
"hex-literal",
"monero-borromean",
"monero-bulletproofs",
"monero-clsag",
"monero-generators",
"multiexp",
"pbkdf2",
"rand",
"rand_chacha",
"monero-io",
"monero-mlsag",
"monero-primitives",
"rand_core",
"rand_distr",
"serde",
"serde_json",
"sha3",
"simple-request",
"std-shims",
"subtle",
"thiserror",
"tokio",
"zeroize",
]
[[package]]
name = "multiexp"
version = "0.4.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
name = "monero-simple-request-rpc"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"ff",
"group",
"rand_core",
"rustversion",
"std-shims",
"zeroize",
"async-trait",
"digest_auth",
"hex",
"monero-rpc",
"simple-request",
"tokio",
]
[[package]]
@ -1772,12 +1786,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -1801,35 +1809,12 @@ dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -2382,7 +2367,7 @@ dependencies = [
[[package]]
name = "simple-request"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"http-body-util",
"hyper",
@ -2438,7 +2423,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "std-shims"
version = "0.1.1"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"hashbrown 0.14.5",
"spin",
@ -2552,15 +2537,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "tinystr"
version = "0.7.6"

View file

@ -57,15 +57,15 @@ chrono = { version = "0.4.31", default-features = false }
crypto-bigint = { version = "0.5.5", default-features = false }
crossbeam = { version = "0.8.4", default-features = false }
curve25519-dalek = { version = "4.1.3", default-features = false }
dalek-ff-group = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false }
dashmap = { version = "5.5.3", default-features = false }
dirs = { version = "5.0.1", default-features = false }
futures = { version = "0.3.29", default-features = false }
hex = { version = "0.4.3", default-features = false }
hex-literal = { version = "0.4", default-features = false }
indexmap = { version = "2.2.5", default-features = false }
monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false }
multiexp = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false }
monero-rpc = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
monero-serai = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
paste = { version = "1.0.14", default-features = false }
pin-project = { version = "1.1.3", default-features = false }
randomx-rs = { git = "https://github.com/Cuprate/randomx-rs.git", rev = "0028464", default-features = false }

View file

@ -19,8 +19,6 @@ futures = { workspace = true, features = ["std", "async-await"] }
randomx-rs = { workspace = true }
monero-serai = { workspace = true, features = ["std"] }
multiexp = { workspace = true }
dalek-ff-group = { workspace = true }
curve25519-dalek = { workspace = true }
rayon = { workspace = true }

View file

@ -244,7 +244,7 @@ where
let block_blob = block.serialize();
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else {
let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
return Err(FastSyncError::MinerTx(MinerTxError::InputNotOfTypeGen));
};
if *height != block_chain_ctx.chain_height {
@ -270,7 +270,7 @@ where
let total_fees = verified_txs.iter().map(|tx| tx.fee).sum::<u64>();
let total_outputs = block
.miner_tx
.prefix
.prefix()
.outputs
.iter()
.map(|output| output.amount.unwrap_or(0))

View file

@ -15,8 +15,6 @@ cuprate-helper = { path = "../../helper", default-features = false, features = [
cuprate-cryptonight = {path = "../../cryptonight"}
monero-serai = { workspace = true, features = ["std"] }
multiexp = { workspace = true, features = ["std", "batch"] }
dalek-ff-group = { workspace = true, features = ["std"] }
curve25519-dalek = { workspace = true, features = ["alloc", "zeroize", "precomputed-tables"] }
rand = { workspace = true, features = ["std", "std_rng"] }

View file

@ -1,4 +1,4 @@
use multiexp::BatchVerifier as InternalBatchVerifier;
use monero_serai::ringct::bulletproofs::BatchVerifier as InternalBatchVerifier;
/// This trait represents a batch verifier.
///
@ -12,18 +12,12 @@ pub trait BatchVerifier {
/// # Panics
/// This function may panic if `stmt` contains calls to `rayon`'s parallel iterators, e.g. `par_iter()`.
// TODO: remove the panics by adding a generic API upstream.
fn queue_statement<R>(
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R;
fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R;
}
// impl this for a single threaded batch verifier.
impl BatchVerifier for &'_ mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint> {
fn queue_statement<R>(
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R {
impl BatchVerifier for &'_ mut InternalBatchVerifier {
fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R {
stmt(self)
}
}

View file

@ -29,8 +29,8 @@ fn genesis_miner_tx(network: &Network) -> Transaction {
pub fn generate_genesis_block(network: &Network) -> Block {
Block {
header: BlockHeader {
major_version: 1,
minor_version: 0,
hardfork_version: 1,
hardfork_signal: 0,
timestamp: 0,
previous: [0; 32],
nonce: genesis_nonce(network),

View file

@ -202,8 +202,8 @@ impl HardFork {
#[inline]
pub fn from_block_header(header: &BlockHeader) -> Result<(HardFork, HardFork), HardForkError> {
Ok((
HardFork::from_version(header.major_version)?,
HardFork::from_vote(header.minor_version),
HardFork::from_version(header.hardfork_version)?,
HardFork::from_vote(header.hardfork_signal),
))
}

View file

@ -1,7 +1,4 @@
use monero_serai::{
ringct::RctType,
transaction::{Input, Output, Timelock, Transaction},
};
use monero_serai::transaction::{Input, Output, Timelock, Transaction};
use crate::{is_decomposed_amount, transactions::check_output_types, HardFork, TxVersion};
@ -188,22 +185,27 @@ pub fn check_miner_tx(
already_generated_coins: u64,
hf: &HardFork,
) -> Result<u64, MinerTxError> {
let tx_version = TxVersion::from_raw(tx.prefix.version).ok_or(MinerTxError::VersionInvalid)?;
let tx_version = TxVersion::from_raw(tx.version()).ok_or(MinerTxError::VersionInvalid)?;
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 {
return Err(MinerTxError::RCTTypeNotNULL);
match tx {
Transaction::V1 { .. } => (),
Transaction::V2 { proofs, .. } => {
if hf >= &HardFork::V12 && proofs.is_some() {
return Err(MinerTxError::RCTTypeNotNULL);
}
}
}
check_time_lock(&tx.prefix.timelock, chain_height)?;
check_time_lock(&tx.prefix().timelock, chain_height)?;
check_inputs(&tx.prefix.inputs, chain_height)?;
check_inputs(&tx.prefix().inputs, chain_height)?;
check_output_types(&tx.prefix.outputs, hf).map_err(|_| MinerTxError::InvalidOutputType)?;
check_output_types(&tx.prefix().outputs, hf).map_err(|_| MinerTxError::InvalidOutputType)?;
let reward = calculate_block_reward(block_weight, median_bw, already_generated_coins, hf);
let total_outs = sum_outputs(&tx.prefix.outputs, hf, &tx_version)?;
let total_outs = sum_outputs(&tx.prefix().outputs, hf, &tx_version)?;
check_total_output_amt(total_outs, reward, total_fees, hf)
}

View file

@ -91,7 +91,7 @@ impl TxVersion {
///
/// 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: u8) -> Option<TxVersion> {
Some(match version {
1 => TxVersion::RingSignatures,
2 => TxVersion::RingCT,
@ -205,7 +205,7 @@ fn check_number_of_outputs(
outputs: usize,
hf: &HardFork,
tx_version: &TxVersion,
rct_type: &RctType,
bp_or_bpp: bool,
) -> Result<(), TransactionError> {
if tx_version == &TxVersion::RingSignatures {
return Ok(());
@ -215,18 +215,10 @@ fn check_number_of_outputs(
return Err(TransactionError::InvalidNumberOfOutputs);
}
match rct_type {
RctType::Bulletproofs
| RctType::BulletproofsCompactAmount
| RctType::Clsag
| RctType::BulletproofsPlus => {
if outputs <= MAX_BULLETPROOFS_OUTPUTS {
Ok(())
} else {
Err(TransactionError::InvalidNumberOfOutputs)
}
}
_ => Ok(()),
if bp_or_bpp && outputs > MAX_BULLETPROOFS_OUTPUTS {
Err(TransactionError::InvalidNumberOfOutputs)
} else {
Ok(())
}
}
@ -239,11 +231,11 @@ fn check_outputs_semantics(
outputs: &[Output],
hf: &HardFork,
tx_version: &TxVersion,
rct_type: &RctType,
bp_or_bpp: bool,
) -> Result<u64, TransactionError> {
check_output_types(outputs, hf)?;
check_output_keys(outputs)?;
check_number_of_outputs(outputs.len(), hf, tx_version, rct_type)?;
check_number_of_outputs(outputs.len(), hf, tx_version, bp_or_bpp)?;
sum_outputs(outputs, hf, tx_version)
}
@ -615,28 +607,41 @@ pub fn check_transaction_semantic(
Err(TransactionError::TooBig)?;
}
let tx_version = TxVersion::from_raw(tx.prefix.version)
.ok_or(TransactionError::TransactionVersionInvalid)?;
let tx_version =
TxVersion::from_raw(tx.version()).ok_or(TransactionError::TransactionVersionInvalid)?;
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 bp_or_bpp = match tx {
Transaction::V2 {
proofs: Some(proofs),
..
} => match proofs.rct_type() {
RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => false,
RctType::MlsagBulletproofs
| RctType::MlsagBulletproofsCompactAmount
| RctType::ClsagBulletproof
| RctType::ClsagBulletproofPlus => true,
},
_ => false,
};
let fee = match tx_version {
TxVersion::RingSignatures => {
let outputs_sum = check_outputs_semantics(&tx.prefix().outputs, hf, &tx_version, bp_or_bpp)?;
let inputs_sum = check_inputs_semantics(&tx.prefix().inputs, hf)?;
let fee = match tx {
Transaction::V1 { .. } => {
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)?;
Transaction::V2 { proofs, .. } => {
let proofs = proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?;
tx.rct_signatures.base.fee
ring_ct::ring_ct_semantic_checks(proofs, tx_hash, verifier, hf)?;
proofs.base.fee
}
};
@ -658,11 +663,11 @@ pub fn check_transaction_contextual(
current_time_lock_timestamp: u64,
hf: &HardFork,
) -> Result<(), TransactionError> {
let tx_version = TxVersion::from_raw(tx.prefix.version)
.ok_or(TransactionError::TransactionVersionInvalid)?;
let tx_version =
TxVersion::from_raw(tx.version()).ok_or(TransactionError::TransactionVersionInvalid)?;
check_inputs_contextual(
&tx.prefix.inputs,
&tx.prefix().inputs,
tx_ring_members_info,
current_chain_height,
hf,
@ -676,17 +681,22 @@ pub fn check_transaction_contextual(
hf,
)?;
match tx_version {
TxVersion::RingSignatures => ring_signatures::check_input_signatures(
&tx.prefix.inputs,
&tx.signatures,
match &tx {
Transaction::V1 { prefix, signatures } => ring_signatures::check_input_signatures(
&prefix.inputs,
signatures,
&tx_ring_members_info.rings,
&tx.signature_hash(),
// This will only return None on v2 miner txs.
&tx.signature_hash()
.ok_or(TransactionError::TransactionVersionInvalid)?,
),
TxVersion::RingCT => Ok(ring_ct::check_input_signatures(
&tx.signature_hash(),
&tx.prefix.inputs,
&tx.rct_signatures,
Transaction::V2 { prefix, proofs } => Ok(ring_ct::check_input_signatures(
&tx.signature_hash()
.ok_or(TransactionError::TransactionVersionInvalid)?,
&prefix.inputs,
proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?,
&tx_ring_members_info.rings,
)?),
}

View file

@ -1,13 +1,13 @@
use curve25519_dalek::{EdwardsPoint, Scalar};
use hex_literal::hex;
use monero_serai::{
generators::H,
ringct::{
clsag::ClsagError,
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
RctPrunable, RctSignatures, RctType,
RctProofs, RctPrunable, RctType,
},
transaction::{Input, Transaction},
H,
transaction::Input,
};
use rand::thread_rng;
#[cfg(feature = "rayon")]
@ -48,12 +48,12 @@ fn check_rct_type(ty: &RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(),
use RctType as T;
match ty {
T::MlsagAggregate | T::MlsagIndividual if hf >= F::V4 && hf < F::V9 => Ok(()),
T::Bulletproofs if hf >= F::V8 && hf < F::V11 => Ok(()),
T::BulletproofsCompactAmount if hf >= F::V10 && hf < F::V14 => Ok(()),
T::BulletproofsCompactAmount if GRANDFATHERED_TRANSACTIONS.contains(tx_hash) => Ok(()),
T::Clsag if hf >= F::V13 && hf < F::V16 => Ok(()),
T::BulletproofsPlus if hf >= F::V15 => Ok(()),
T::AggregateMlsagBorromean | T::MlsagBorromean if hf >= F::V4 && hf < F::V9 => Ok(()),
T::MlsagBulletproofs if hf >= F::V8 && hf < F::V11 => Ok(()),
T::MlsagBulletproofsCompactAmount if hf >= F::V10 && hf < F::V14 => Ok(()),
T::MlsagBulletproofsCompactAmount if GRANDFATHERED_TRANSACTIONS.contains(tx_hash) => Ok(()),
T::ClsagBulletproof if hf >= F::V13 && hf < F::V16 => Ok(()),
T::ClsagBulletproofPlus if hf >= F::V15 => Ok(()),
_ => Err(RingCTError::TypeNotAllowed),
}
}
@ -61,14 +61,16 @@ fn check_rct_type(ty: &RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(),
/// 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>
fn simple_type_balances(rct_sig: &RctSignatures) -> Result<(), RingCTError> {
let pseudo_outs = if rct_sig.rct_type() == RctType::MlsagIndividual {
fn simple_type_balances(rct_sig: &RctProofs) -> Result<(), RingCTError> {
let pseudo_outs = if rct_sig.rct_type() == RctType::MlsagBorromean {
&rct_sig.base.pseudo_outs
} else {
match &rct_sig.prunable {
RctPrunable::Clsag { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofs { pseudo_outs, .. } => pseudo_outs,
_ => panic!("RingCT type is not simple!"),
RctPrunable::MlsagBorromean { .. } => &rct_sig.base.pseudo_outs,
RctPrunable::AggregateMlsagBorromean { .. } => panic!("RingCT type is not simple!"),
}
};
@ -89,13 +91,12 @@ fn simple_type_balances(rct_sig: &RctSignatures) -> Result<(), RingCTError> {
/// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs.html>
/// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs+.html>
fn check_output_range_proofs(
rct_sig: &RctSignatures,
proofs: &RctProofs,
mut verifier: impl BatchVerifier,
) -> Result<(), RingCTError> {
let commitments = &rct_sig.base.commitments;
let commitments = &proofs.base.commitments;
match &rct_sig.prunable {
RctPrunable::Null => Err(RingCTError::TypeNotAllowed)?,
match &proofs.prunable {
RctPrunable::MlsagBorromean { borromean, .. }
| RctPrunable::AggregateMlsagBorromean { borromean, .. } => try_par_iter(borromean)
.zip(commitments)
@ -106,10 +107,11 @@ fn check_output_range_proofs(
Err(RingCTError::BorromeanRangeInvalid)
}
}),
RctPrunable::MlsagBulletproofs { bulletproofs, .. }
| RctPrunable::Clsag { bulletproofs, .. } => {
RctPrunable::MlsagBulletproofs { bulletproof, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, .. }
| RctPrunable::Clsag { bulletproof, .. } => {
if verifier.queue_statement(|verifier| {
bulletproofs.batch_verify(&mut thread_rng(), verifier, (), commitments)
bulletproof.batch_verify(&mut thread_rng(), verifier, commitments)
}) {
Ok(())
} else {
@ -120,18 +122,18 @@ fn check_output_range_proofs(
}
pub(crate) fn ring_ct_semantic_checks(
tx: &Transaction,
proofs: &RctProofs,
tx_hash: &[u8; 32],
verifier: impl BatchVerifier,
hf: &HardFork,
) -> Result<(), RingCTError> {
let rct_type = tx.rct_signatures.rct_type();
let rct_type = proofs.rct_type();
check_rct_type(&rct_type, *hf, tx_hash)?;
check_output_range_proofs(&tx.rct_signatures, verifier)?;
check_output_range_proofs(&proofs, verifier)?;
if rct_type != RctType::MlsagAggregate {
simple_type_balances(&tx.rct_signatures)?;
if rct_type != RctType::AggregateMlsagBorromean {
simple_type_balances(&proofs)?;
}
Ok(())
@ -144,7 +146,7 @@ pub(crate) fn ring_ct_semantic_checks(
pub(crate) fn check_input_signatures(
msg: &[u8; 32],
inputs: &[Input],
rct_sig: &RctSignatures,
proofs: &RctProofs,
rings: &Rings,
) -> Result<(), RingCTError> {
let Rings::RingCT(rings) = rings else {
@ -155,15 +157,15 @@ pub(crate) fn check_input_signatures(
Err(RingCTError::RingInvalid)?;
}
let pseudo_outs = match &rct_sig.prunable {
let pseudo_outs = match &proofs.prunable {
RctPrunable::MlsagBulletproofs { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { pseudo_outs, .. }
| RctPrunable::Clsag { pseudo_outs, .. } => pseudo_outs.as_slice(),
RctPrunable::MlsagBorromean { .. } => rct_sig.base.pseudo_outs.as_slice(),
RctPrunable::AggregateMlsagBorromean { .. } | RctPrunable::Null => &[],
RctPrunable::MlsagBorromean { .. } => proofs.base.pseudo_outs.as_slice(),
RctPrunable::AggregateMlsagBorromean { .. } => &[],
};
match &rct_sig.prunable {
RctPrunable::Null => Err(RingCTError::TypeNotAllowed)?,
match &proofs.prunable {
RctPrunable::AggregateMlsagBorromean { mlsag, .. } => {
let key_images = inputs
.iter()
@ -176,11 +178,14 @@ pub(crate) fn check_input_signatures(
.collect::<Vec<_>>();
let mut matrix =
AggregateRingMatrixBuilder::new(&rct_sig.base.commitments, rct_sig.base.fee);
AggregateRingMatrixBuilder::new(&proofs.base.commitments, proofs.base.fee);
rings.iter().try_for_each(|ring| matrix.push_ring(ring))?;
Ok(mlsag.verify(msg, &matrix.build()?, &key_images)?)
}
RctPrunable::MlsagBorromean { mlsags, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { mlsags, .. }
| RctPrunable::MlsagBulletproofs { mlsags, .. } => try_par_iter(mlsags)
.zip(pseudo_outs)
.zip(inputs)
@ -216,18 +221,21 @@ mod tests {
#[test]
fn grandfathered_bulletproofs2() {
assert!(
check_rct_type(&RctType::BulletproofsCompactAmount, HardFork::V14, &[0; 32]).is_err()
);
assert!(check_rct_type(
&RctType::MlsagBulletproofsCompactAmount,
HardFork::V14,
&[0; 32]
)
.is_err());
assert!(check_rct_type(
&RctType::BulletproofsCompactAmount,
&RctType::MlsagBulletproofsCompactAmount,
HardFork::V14,
&GRANDFATHERED_TRANSACTIONS[0]
)
.is_ok());
assert!(check_rct_type(
&RctType::BulletproofsCompactAmount,
&RctType::MlsagBulletproofsCompactAmount,
HardFork::V14,
&GRANDFATHERED_TRANSACTIONS[1]
)

View file

@ -97,31 +97,6 @@ fn test_torsion_ki() {
}
}
/// Returns a strategy that resolves to a [`RctType`] that uses
/// BPs(+).
#[allow(unreachable_code)]
#[allow(clippy::diverging_sub_expression)]
fn bulletproof_rct_type() -> BoxedStrategy<RctType> {
return prop_oneof![
Just(RctType::Bulletproofs),
Just(RctType::BulletproofsCompactAmount),
Just(RctType::Clsag),
Just(RctType::BulletproofsPlus),
]
.boxed();
// Here to make sure this is updated when needed.
match unreachable!() {
RctType::Null => {}
RctType::MlsagAggregate => {}
RctType::MlsagIndividual => {}
RctType::Bulletproofs => {}
RctType::BulletproofsCompactAmount => {}
RctType::Clsag => {}
RctType::BulletproofsPlus => {}
};
}
prop_compose! {
/// Returns a valid prime-order point.
fn random_point()(bytes in any::<[u8; 32]>()) -> EdwardsPoint {
@ -240,13 +215,13 @@ proptest! {
}
#[test]
fn test_valid_number_of_outputs(valid_numb_outs in 2..17_usize, rct_type in bulletproof_rct_type()) {
prop_assert!(check_number_of_outputs(valid_numb_outs, &HardFork::V16, &TxVersion::RingCT, &rct_type).is_ok());
fn test_valid_number_of_outputs(valid_numb_outs in 2..17_usize) {
prop_assert!(check_number_of_outputs(valid_numb_outs, &HardFork::V16, &TxVersion::RingCT, true).is_ok());
}
#[test]
fn test_invalid_number_of_outputs(numb_outs in 17..usize::MAX, rct_type in bulletproof_rct_type()) {
prop_assert!(check_number_of_outputs(numb_outs, &HardFork::V16, &TxVersion::RingCT, &rct_type).is_err());
fn test_invalid_number_of_outputs(numb_outs in 17..usize::MAX) {
prop_assert!(check_number_of_outputs(numb_outs, &HardFork::V16, &TxVersion::RingCT, true).is_err());
}
#[test]

View file

@ -1,12 +1,14 @@
use std::{cell::RefCell, ops::DerefMut};
use multiexp::BatchVerifier as InternalBatchVerifier;
use monero_serai::ringct::bulletproofs::BatchVerifier as InternalBatchVerifier;
use rayon::prelude::*;
use thread_local::ThreadLocal;
use cuprate_consensus_rules::batch_verifier::BatchVerifier;
/// A multithreaded batch verifier.
pub struct MultiThreadedBatchVerifier {
internal: ThreadLocal<RefCell<InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>>>,
internal: ThreadLocal<RefCell<InternalBatchVerifier>>,
}
impl MultiThreadedBatchVerifier {
@ -22,19 +24,22 @@ impl MultiThreadedBatchVerifier {
.into_iter()
.map(RefCell::into_inner)
.par_bridge()
.find_any(|batch_verifier| !batch_verifier.verify_vartime())
.is_none()
.try_for_each(|batch_verifier| {
if batch_verifier.verify() {
Ok(())
} else {
Err(())
}
})
.is_ok()
}
}
impl cuprate_consensus_rules::batch_verifier::BatchVerifier for &'_ MultiThreadedBatchVerifier {
fn queue_statement<R>(
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R {
impl BatchVerifier for &'_ MultiThreadedBatchVerifier {
fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R {
let mut verifier = self
.internal
.get_or(|| RefCell::new(InternalBatchVerifier::new(32)))
.get_or(|| RefCell::new(InternalBatchVerifier::new()))
.borrow_mut();
stmt(verifier.deref_mut())

View file

@ -70,7 +70,7 @@ impl PreparedBlockExPow {
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 {
let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
Err(ConsensusError::Block(BlockError::MinerTxError(
MinerTxError::InputNotOfTypeGen,
)))?
@ -124,7 +124,7 @@ impl PreparedBlock {
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 {
let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
Err(ConsensusError::Block(BlockError::MinerTxError(
MinerTxError::InputNotOfTypeGen,
)))?
@ -138,7 +138,7 @@ impl PreparedBlock {
block_hash: block.hash(),
pow_hash: calculate_pow_hash(
randomx_vm,
&block.serialize_hashable(),
&block.serialize_pow_hash(),
*height,
&hf_version,
)?,
@ -168,7 +168,7 @@ impl PreparedBlock {
block_hash: block.block_hash,
pow_hash: calculate_pow_hash(
randomx_vm,
&block.block.serialize_hashable(),
&block.block.serialize_pow_hash(),
block.height,
&block.hf_version,
)?,
@ -527,9 +527,8 @@ where
);
// Set up the block and just pass it to [`verify_prepped_main_chain_block`]
// We just use the raw `major_version` here, no need to turn it into a `HardFork`.
let rx_vms = if block.header.major_version < 12 {
// We just use the raw `hardfork_version` here, no need to turn it into a `HardFork`.
let rx_vms = if block.header.hardfork_version < 12 {
HashMap::new()
} else {
let BlockChainContextResponse::RxVms(rx_vms) = context_svc

View file

@ -12,6 +12,7 @@ use std::{
};
use futures::FutureExt;
use monero_serai::ringct::bulletproofs::Bulletproof;
use monero_serai::{
ringct::RctType,
transaction::{Input, Timelock, Transaction},
@ -104,21 +105,62 @@ impl TransactionVerificationData {
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 tx_weight = match &tx {
Transaction::V1 { .. } | Transaction::V2 { proofs: None, .. } => tx_blob.len(),
Transaction::V2 {
proofs: Some(proofs),
..
} => match proofs.rct_type() {
RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => tx_blob.len(),
RctType::MlsagBulletproofs
| RctType::MlsagBulletproofsCompactAmount
| RctType::ClsagBulletproof => {
tx_blob.len()
+ Bulletproof::calculate_bp_clawback(false, tx.prefix().outputs.len()).0
}
RctType::ClsagBulletproofPlus => {
tx_blob.len()
+ Bulletproof::calculate_bp_clawback(true, tx.prefix().outputs.len()).0
}
},
};
let mut fee = 0_u64;
match &tx {
Transaction::V1 { prefix, .. } => {
for input in &prefix.inputs {
match input {
Input::ToKey { amount, .. } => {
fee = fee
.checked_add(amount.unwrap_or(0))
.ok_or(TransactionError::InputsOverflow)?;
}
_ => (),
}
}
for output in &prefix.outputs {
fee.checked_sub(output.amount.unwrap_or(0))
.ok_or(TransactionError::OutputsTooHigh)?;
}
}
Transaction::V2 { proofs, .. } => {
fee = proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?
.base
.fee;
}
};
Ok(TransactionVerificationData {
tx_hash,
tx_blob,
tx_weight,
fee: tx.rct_signatures.base.fee,
fee,
cached_verification_state: StdMutex::new(CachedVerificationState::NotVerified),
version: TxVersion::from_raw(tx.prefix.version)
version: TxVersion::from_raw(tx.version())
.ok_or(TransactionError::TransactionVersionInvalid)?,
tx,
})
@ -296,7 +338,7 @@ where
let mut spent_kis = HashSet::with_capacity(txs.len());
txs.iter().try_for_each(|tx| {
tx.tx.prefix.inputs.iter().try_for_each(|input| {
tx.tx.prefix().inputs.iter().try_for_each(|input| {
if let Input::ToKey { key_image, .. } = input {
if !spent_kis.insert(key_image.compress().0) {
tracing::debug!("Duplicate key image found in batch.");
@ -499,7 +541,7 @@ where
&hf,
&batch_verifier,
)?;
// make sure monero-serai calculated the same fee.
// make sure we calculated the right fee.
assert_eq!(fee, tx.fee);
}

View file

@ -149,7 +149,7 @@ pub async fn batch_get_ring_member_info<D: Database>(
let mut output_ids = HashMap::new();
for tx_v_data in txs_verification_data.clone() {
insert_ring_member_ids(&tx_v_data.tx.prefix.inputs, &mut output_ids)
insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut output_ids)
.map_err(ConsensusError::Transaction)?;
}
@ -179,14 +179,14 @@ pub async fn batch_get_ring_member_info<D: Database>(
let ring_members_for_tx = get_ring_members_for_inputs(
|amt, idx| outputs.get(&amt)?.get(&idx).copied(),
&tx_v_data.tx.prefix.inputs,
&tx_v_data.tx.prefix().inputs,
)
.map_err(ConsensusError::Transaction)?;
let decoy_info = if hf != &HardFork::V1 {
// this data is only needed after hard-fork 1.
Some(
DecoyInfo::new(&tx_v_data.tx.prefix.inputs, numb_outputs, hf)
DecoyInfo::new(&tx_v_data.tx.prefix().inputs, numb_outputs, hf)
.map_err(ConsensusError::Transaction)?,
)
} else {
@ -222,7 +222,7 @@ pub async fn batch_get_decoy_info<'a, D: Database + Clone + Send + 'static>(
let unique_input_amounts = txs_verification_data
.iter()
.flat_map(|tx_info| {
tx_info.tx.prefix.inputs.iter().map(|input| match input {
tx_info.tx.prefix().inputs.iter().map(|input| match input {
Input::ToKey { amount, .. } => amount.unwrap_or(0),
_ => 0,
})
@ -247,7 +247,7 @@ pub async fn batch_get_decoy_info<'a, D: Database + Clone + Send + 'static>(
Ok(txs_verification_data.iter().map(move |tx_v_data| {
DecoyInfo::new(
&tx_v_data.tx.prefix.inputs,
&tx_v_data.tx.prefix().inputs,
|amt| outputs_with_amount.get(&amt).copied().unwrap_or(0),
&hf,
)

View file

@ -11,7 +11,6 @@ use futures::{FutureExt, StreamExt};
use indexmap::IndexMap;
use monero_serai::{
block::{Block, BlockHeader},
ringct::{RctBase, RctPrunable, RctSignatures},
transaction::{Input, Timelock, Transaction, TransactionPrefix},
};
use proptest::{collection::vec, prelude::*};
@ -98,24 +97,14 @@ prop_compose! {
timelock in 0_usize..50_000_000,
)
-> Transaction {
Transaction {
Transaction::V1 {
prefix: TransactionPrefix {
version: 1,
timelock: Timelock::Block(timelock),
inputs: vec![Input::Gen(height)],
outputs: vec![],
extra,
},
signatures: vec![],
rct_signatures: RctSignatures {
base: RctBase {
fee: 0,
pseudo_outs: vec![],
encrypted_amounts: vec![],
commitments: vec![],
},
prunable: RctPrunable::Null
},
}
}
}
@ -134,8 +123,8 @@ prop_compose! {
(
Block {
header: BlockHeader {
major_version: 0,
minor_version: 0,
hardfork_version: 0,
hardfork_signal: 0,
timestamp: 0,
previous,
nonce: 0,
@ -169,7 +158,7 @@ prop_compose! {
for (height, mut block) in blocks.into_iter().enumerate() {
if let Some(last) = blockchain.last() {
block.0.header.previous = *last.0;
block.0.miner_tx.prefix.inputs = vec![Input::Gen(height as u64)]
block.0.miner_tx.prefix_mut().inputs = vec![Input::Gen(height as u64)]
}
blockchain.insert(block.0.hash(), block);

View file

@ -200,8 +200,8 @@ pub fn get_block_extended_header_from_height(
#[allow(clippy::cast_possible_truncation)]
Ok(ExtendedBlockHeader {
cumulative_difficulty,
version: block.header.major_version,
vote: block.header.minor_version,
version: block.header.hardfork_version,
vote: block.header.hardfork_signal,
timestamp: block.header.timestamp,
block_weight: block_info.weight as usize,
long_term_weight: block_info.long_term_weight as usize,
@ -369,8 +369,8 @@ mod test {
let b1 = block_header_from_hash;
let b2 = block;
assert_eq!(b1, block_header_from_height);
assert_eq!(b1.version, b2.block.header.major_version);
assert_eq!(b1.vote, b2.block.header.minor_version);
assert_eq!(b1.version, b2.block.header.hardfork_version);
assert_eq!(b1.vote, b2.block.header.hardfork_signal);
assert_eq!(b1.timestamp, b2.block.header.timestamp);
assert_eq!(b1.cumulative_difficulty, b2.cumulative_difficulty);
assert_eq!(b1.block_weight, b2.weight);

View file

@ -2,7 +2,7 @@
//---------------------------------------------------------------------------------------------------- Import
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar};
use monero_serai::{transaction::Timelock, H};
use monero_serai::{generators::H, transaction::Timelock};
use cuprate_database::{
RuntimeError, {DatabaseRo, DatabaseRw},

View file

@ -68,7 +68,7 @@ pub fn add_tx(
// so the `u64/usize` is stored without any tag.
//
// <https://github.com/Cuprate/cuprate/pull/102#discussion_r1558504285>
match tx.prefix.timelock {
match tx.prefix().timelock {
Timelock::None => (),
Timelock::Block(height) => tables.tx_unlock_time_mut().put(&tx_id, &(height as u64))?,
Timelock::Time(time) => tables.tx_unlock_time_mut().put(&tx_id, &time)?,
@ -92,7 +92,7 @@ pub fn add_tx(
let mut miner_tx = false;
// Key images.
for inputs in &tx.prefix.inputs {
for inputs in &tx.prefix().inputs {
match inputs {
// Key images.
Input::ToKey { key_image, .. } => {
@ -106,70 +106,69 @@ pub fn add_tx(
//------------------------------------------------------ Outputs
// Output bit flags.
// Set to a non-zero bit value if the unlock time is non-zero.
let output_flags = match tx.prefix.timelock {
let output_flags = match tx.prefix().timelock {
Timelock::None => OutputFlags::empty(),
Timelock::Block(_) | Timelock::Time(_) => OutputFlags::NON_ZERO_UNLOCK_TIME,
};
let mut amount_indices = Vec::with_capacity(tx.prefix.outputs.len());
for (i, output) in tx.prefix.outputs.iter().enumerate() {
let key = *output.key.as_bytes();
// Outputs with clear amounts.
let amount_index = if let Some(amount) = output.amount {
// RingCT (v2 transaction) miner outputs.
if miner_tx && tx.prefix.version == 2 {
// Create commitment.
// <https://github.com/Cuprate/cuprate/pull/102#discussion_r1559489302>
// FIXME: implement lookup table for common values:
// <https://github.com/monero-project/monero/blob/c8214782fb2a769c57382a999eaf099691c836e7/src/ringct/rctOps.cpp#L322>
let commitment = (ED25519_BASEPOINT_POINT
+ monero_serai::H() * Scalar::from(amount))
.compress()
.to_bytes();
add_rct_output(
&RctOutput {
key,
height,
output_flags,
tx_idx: tx_id,
commitment,
},
tables.rct_outputs_mut(),
)?
// Pre-RingCT outputs.
} else {
add_output(
amount,
let amount_indices = match &tx {
Transaction::V1 { prefix, .. } => prefix
.outputs
.iter()
.map(|output| {
// Pre-RingCT outputs.
Ok(add_output(
output.amount.unwrap_or(0),
&Output {
key,
key: output.key.0,
height,
output_flags,
tx_idx: tx_id,
},
tables,
)?
.amount_index
}
// RingCT outputs.
} else {
let commitment = tx.rct_signatures.base.commitments[i].compress().to_bytes();
add_rct_output(
&RctOutput {
key,
height,
output_flags,
tx_idx: tx_id,
commitment,
},
tables.rct_outputs_mut(),
)?
};
.amount_index)
})
.collect::<Result<Vec<_>, RuntimeError>>()?,
Transaction::V2 { prefix, proofs } => prefix
.outputs
.iter()
.enumerate()
.map(|(i, output)| {
// Create commitment.
// <https://github.com/Cuprate/cuprate/pull/102#discussion_r1559489302>
// FIXME: implement lookup table for common values:
// <https://github.com/monero-project/monero/blob/c8214782fb2a769c57382a999eaf099691c836e7/src/ringct/rctOps.cpp#L322>
let commitment = if miner_tx {
ED25519_BASEPOINT_POINT
+ monero_serai::generators::H() * Scalar::from(output.amount.unwrap_or(0))
} else {
let commitment = proofs
.as_ref()
.expect("A V2 transaction with no RCT proofs is a miner tx")
.base
.commitments[i];
amount_indices.push(amount_index);
} // for each output
commitment
};
(commitment, output.key.0)
})
.map(|(commitment, key)| {
// Add the RCT output.
add_rct_output(
&RctOutput {
key,
height,
output_flags,
tx_idx: tx_id,
commitment: commitment.compress().0,
},
tables.rct_outputs_mut(),
)
})
.collect::<Result<Vec<_>, _>>()?,
};
tables
.tx_outputs_mut()
@ -227,7 +226,7 @@ pub fn remove_tx(
//------------------------------------------------------ Key Images
// Is this a miner transaction?
let mut miner_tx = false;
for inputs in &tx.prefix.inputs {
for inputs in &tx.prefix().inputs {
match inputs {
// Key images.
Input::ToKey { key_image, .. } => {
@ -240,11 +239,11 @@ pub fn remove_tx(
//------------------------------------------------------ Outputs
// Remove each output in the transaction.
for output in &tx.prefix.outputs {
for output in &tx.prefix().outputs {
// Outputs with clear amounts.
if let Some(amount) = output.amount {
// RingCT miner outputs.
if miner_tx && tx.prefix.version == 2 {
if miner_tx && tx.version() == 2 {
let amount_index = get_rct_num_outputs(tables.rct_outputs())? - 1;
remove_rct_output(&amount_index, tables.rct_outputs_mut())?;
// Pre-RingCT outputs.

View file

@ -13,7 +13,9 @@ cuprate-p2p-core = { path = "../p2p/p2p-core", features = ["borsh"] }
hex = { workspace = true }
hex-literal = { workspace = true }
monero-serai = { workspace = true, features = ["std", "http-rpc"] }
monero-serai = { workspace = true, features = ["std"] }
monero-simple-request-rpc = { workspace = true }
monero-rpc = { workspace = true }
futures = { workspace = true, features = ["std"] }
async-trait = { workspace = true }
tokio = { workspace = true, features = ["full"] }

View file

@ -103,7 +103,8 @@ fn to_tx_verification_data(tx_blob: impl AsRef<[u8]>) -> VerifiedTransactionInfo
let tx = Transaction::read(&mut tx_blob.as_slice()).unwrap();
VerifiedTransactionInformation {
tx_weight: tx.weight(),
fee: tx.rct_signatures.base.fee,
// TODO:
fee: 0,
tx_hash: tx.hash(),
tx_blob,
tx,
@ -255,7 +256,7 @@ macro_rules! transaction_verification_data_fn {
#[doc = concat!("assert_eq!(tx.tx_blob, ", stringify!($tx_blob), ");")]
#[doc = concat!("assert_eq!(tx.tx_weight, ", $weight, ");")]
#[doc = concat!("assert_eq!(tx.tx_hash, hex!(\"", $hash, "\"));")]
#[doc = "assert_eq!(tx.fee, tx.tx.rct_signatures.base.fee);"]
// #[doc = "assert_eq!(tx.fee, tx.tx.rct_signatures.base.fee);"]
/// ```
pub fn $fn_name() -> &'static VerifiedTransactionInformation {
static TX: OnceLock<VerifiedTransactionInformation> = OnceLock::new();

View file

@ -5,10 +5,9 @@ use serde::Deserialize;
use serde_json::json;
use tokio::task::spawn_blocking;
use monero_serai::{
block::Block,
rpc::{HttpRpc, Rpc},
};
use monero_rpc::Rpc;
use monero_serai::block::Block;
use monero_simple_request_rpc::SimpleRequestRpc;
use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation};
@ -18,7 +17,7 @@ use crate::rpc::constants::LOCALHOST_RPC_URL;
/// An HTTP RPC client for Monero.
pub struct HttpRpcClient {
address: String,
rpc: Rpc<HttpRpc>,
rpc: SimpleRequestRpc,
}
impl HttpRpcClient {
@ -38,7 +37,7 @@ impl HttpRpcClient {
let address = address.unwrap_or_else(|| LOCALHOST_RPC_URL.to_string());
Self {
rpc: HttpRpc::new(address.clone()).await.unwrap(),
rpc: SimpleRequestRpc::new(address.clone()).await.unwrap(),
address,
}
}
@ -51,7 +50,7 @@ impl HttpRpcClient {
/// Access to the inner RPC client for other usage.
#[allow(dead_code)]
const fn rpc(&self) -> &Rpc<HttpRpc> {
const fn rpc(&self) -> &SimpleRequestRpc {
&self.rpc
}
@ -123,7 +122,7 @@ impl HttpRpcClient {
let total_tx_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
let generated_coins = block
.miner_tx
.prefix
.prefix()
.outputs
.iter()
.map(|output| output.amount.expect("miner_tx amount was None"))
@ -171,7 +170,8 @@ impl HttpRpcClient {
tx_blob: tx.serialize(),
tx_weight: tx.weight(),
tx_hash,
fee: tx.rct_signatures.base.fee,
// TODO: fix this.
fee: 0,
tx,
}
})