diff --git a/Cargo.lock b/Cargo.lock index 68ccc3ae..5eab7ff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index b00a4b98..88755bf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 624eb637..c39e1c26 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -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 } diff --git a/consensus/fast-sync/src/fast_sync.rs b/consensus/fast-sync/src/fast_sync.rs index a97040a6..67b7e5fd 100644 --- a/consensus/fast-sync/src/fast_sync.rs +++ b/consensus/fast-sync/src/fast_sync.rs @@ -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::(); let total_outputs = block .miner_tx - .prefix + .prefix() .outputs .iter() .map(|output| output.amount.unwrap_or(0)) diff --git a/consensus/rules/Cargo.toml b/consensus/rules/Cargo.toml index fd86a61e..311bcc95 100644 --- a/consensus/rules/Cargo.toml +++ b/consensus/rules/Cargo.toml @@ -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"] } diff --git a/consensus/rules/src/batch_verifier.rs b/consensus/rules/src/batch_verifier.rs index c8d3f104..bce6eb9d 100644 --- a/consensus/rules/src/batch_verifier.rs +++ b/consensus/rules/src/batch_verifier.rs @@ -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( - &mut self, - stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R, - ) -> R; + fn queue_statement(&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( - &mut self, - stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R, - ) -> R { +impl BatchVerifier for &'_ mut InternalBatchVerifier { + fn queue_statement(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R { stmt(self) } } diff --git a/consensus/rules/src/genesis.rs b/consensus/rules/src/genesis.rs index 73bc9516..a66e9caa 100644 --- a/consensus/rules/src/genesis.rs +++ b/consensus/rules/src/genesis.rs @@ -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), diff --git a/consensus/rules/src/hard_forks.rs b/consensus/rules/src/hard_forks.rs index b34b93d7..7ae49090 100644 --- a/consensus/rules/src/hard_forks.rs +++ b/consensus/rules/src/hard_forks.rs @@ -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), )) } diff --git a/consensus/rules/src/miner_tx.rs b/consensus/rules/src/miner_tx.rs index 90f1a7ee..296ffb5e 100644 --- a/consensus/rules/src/miner_tx.rs +++ b/consensus/rules/src/miner_tx.rs @@ -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 { - 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: - 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) } diff --git a/consensus/rules/src/transactions.rs b/consensus/rules/src/transactions.rs index 91697087..0b8ac56c 100644 --- a/consensus/rules/src/transactions.rs +++ b/consensus/rules/src/transactions.rs @@ -91,7 +91,7 @@ impl TxVersion { /// /// ref: /// && - pub fn from_raw(version: u64) -> Option { + pub fn from_raw(version: u8) -> Option { 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 { 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, )?), } diff --git a/consensus/rules/src/transactions/ring_ct.rs b/consensus/rules/src/transactions/ring_ct.rs index 38b56ebd..8b9a0f8f 100644 --- a/consensus/rules/src/transactions/ring_ct.rs +++ b/consensus/rules/src/transactions/ring_ct.rs @@ -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. /// /// -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> { /// /// 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::>(); 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] ) diff --git a/consensus/rules/src/transactions/tests.rs b/consensus/rules/src/transactions/tests.rs index 1d7591b3..df9bbb20 100644 --- a/consensus/rules/src/transactions/tests.rs +++ b/consensus/rules/src/transactions/tests.rs @@ -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 { - 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] diff --git a/consensus/src/batch_verifier.rs b/consensus/src/batch_verifier.rs index 44493a62..69018acc 100644 --- a/consensus/src/batch_verifier.rs +++ b/consensus/src/batch_verifier.rs @@ -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>>, + internal: ThreadLocal>, } 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( - &mut self, - stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R, - ) -> R { +impl BatchVerifier for &'_ MultiThreadedBatchVerifier { + fn queue_statement(&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()) diff --git a/consensus/src/block.rs b/consensus/src/block.rs index d3d06722..fd17eb1b 100644 --- a/consensus/src/block.rs +++ b/consensus/src/block.rs @@ -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 diff --git a/consensus/src/transactions.rs b/consensus/src/transactions.rs index 417eb487..c08b6bae 100644 --- a/consensus/src/transactions.rs +++ b/consensus/src/transactions.rs @@ -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); } diff --git a/consensus/src/transactions/contextual_data.rs b/consensus/src/transactions/contextual_data.rs index 95e5262d..858d731b 100644 --- a/consensus/src/transactions/contextual_data.rs +++ b/consensus/src/transactions/contextual_data.rs @@ -149,7 +149,7 @@ pub async fn batch_get_ring_member_info( 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( 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, ) diff --git a/p2p/p2p/src/block_downloader/tests.rs b/p2p/p2p/src/block_downloader/tests.rs index 07c30d5d..4d529730 100644 --- a/p2p/p2p/src/block_downloader/tests.rs +++ b/p2p/p2p/src/block_downloader/tests.rs @@ -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); diff --git a/storage/blockchain/src/ops/block.rs b/storage/blockchain/src/ops/block.rs index 9097f0ee..67c97ac2 100644 --- a/storage/blockchain/src/ops/block.rs +++ b/storage/blockchain/src/ops/block.rs @@ -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); diff --git a/storage/blockchain/src/ops/output.rs b/storage/blockchain/src/ops/output.rs index f08f7b30..49857da7 100644 --- a/storage/blockchain/src/ops/output.rs +++ b/storage/blockchain/src/ops/output.rs @@ -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}, diff --git a/storage/blockchain/src/ops/tx.rs b/storage/blockchain/src/ops/tx.rs index 6edfbb21..c7beee53 100644 --- a/storage/blockchain/src/ops/tx.rs +++ b/storage/blockchain/src/ops/tx.rs @@ -68,7 +68,7 @@ pub fn add_tx( // so the `u64/usize` is stored without any tag. // // - 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. - // - // FIXME: implement lookup table for common values: - // - 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::, RuntimeError>>()?, + Transaction::V2 { prefix, proofs } => prefix + .outputs + .iter() + .enumerate() + .map(|(i, output)| { + // Create commitment. + // + // FIXME: implement lookup table for common values: + // + 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::, _>>()?, + }; 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. diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index f9a5c6d9..76b4cd97 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -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"] } diff --git a/test-utils/src/data/free.rs b/test-utils/src/data/free.rs index e80bdda0..88e2edd9 100644 --- a/test-utils/src/data/free.rs +++ b/test-utils/src/data/free.rs @@ -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 = OnceLock::new(); diff --git a/test-utils/src/rpc/client.rs b/test-utils/src/rpc/client.rs index 22ae11f5..d6e23853 100644 --- a/test-utils/src/rpc/client.rs +++ b/test-utils/src/rpc/client.rs @@ -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, + 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 { + const fn rpc(&self) -> &SimpleRequestRpc { &self.rpc } @@ -123,7 +122,7 @@ impl HttpRpcClient { let total_tx_fees = txs.iter().map(|tx| tx.fee).sum::(); 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, } })