From 5c2b56c78ea16d55dc4f25a3e0e74a3e80660bf0 Mon Sep 17 00:00:00 2001 From: Boog900 <boog900@tutanota.com> Date: Thu, 6 Mar 2025 19:03:25 +0000 Subject: [PATCH] Update to experimental monero oxide api (#386) * add specific method for context * add new statemachine for tx verification * fix consensus crates build * working builds * fix CI * add docs * fix CI * fix docs * fix clippy * cleanup * add docs to `blockchain_context` * fix doc tests * add output cache * new monero-serai * todo * todo * Revert "new monero-serai" This reverts commit fe3f6acc676fe59e794d5f92f07f76445db35199. * use indexmap to request outputs * clean up * fix typos * fix CI * fix cargo hack * fix reorgs * check if a block is already present before adding it to the alt block cache * fmt * update to new monero oxide API * fmt & fix cache * update config values * fix tests * fix no-std builds --- Cargo.lock | 30 ++++++++-------- Cargo.toml | 6 ++-- binaries/cuprated/config/0.0.1.toml | 4 +-- .../src/blockchain/manager/handler.rs | 2 +- binaries/cuprated/src/config/p2p.rs | 4 +-- consensus/rules/src/transactions.rs | 14 +++++--- .../rules/src/transactions/contextual_data.rs | 6 ++-- consensus/rules/src/transactions/ring_ct.rs | 23 +++++++++--- consensus/rules/src/transactions/tests.rs | 8 ++--- consensus/src/transactions.rs | 2 +- consensus/src/transactions/contextual_data.rs | 34 ++++++------------ consensus/tests/verify_correct_txs.rs | 17 +++------ helper/src/crypto.rs | 35 ++++++++++++------- helper/src/tx.rs | 4 +-- storage/blockchain/src/ops/output.rs | 13 ++----- storage/blockchain/src/ops/tx.rs | 6 ++-- storage/txpool/src/ops/key_images.rs | 2 +- types/src/json/tx.rs | 4 +-- types/src/output_cache.rs | 7 ++-- types/src/types.rs | 6 ++-- 20 files changed, 113 insertions(+), 114 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ba27d66..2ad83bd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1152,7 +1152,7 @@ dependencies = [ [[package]] name = "dalek-ff-group" version = "0.4.1" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "crypto-bigint", "curve25519-dalek", @@ -1317,7 +1317,7 @@ dependencies = [ [[package]] name = "flexible-transcript" version = "0.3.2" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "blake2", "digest", @@ -2023,7 +2023,7 @@ dependencies = [ [[package]] name = "monero-address" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "monero-io", @@ -2036,7 +2036,7 @@ dependencies = [ [[package]] name = "monero-borromean" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "monero-generators", @@ -2049,7 +2049,7 @@ dependencies = [ [[package]] name = "monero-bulletproofs" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "monero-generators", @@ -2064,7 +2064,7 @@ dependencies = [ [[package]] name = "monero-clsag" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "dalek-ff-group", @@ -2084,7 +2084,7 @@ dependencies = [ [[package]] name = "monero-generators" version = "0.4.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "dalek-ff-group", @@ -2098,7 +2098,7 @@ dependencies = [ [[package]] name = "monero-io" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "std-shims", @@ -2107,7 +2107,7 @@ dependencies = [ [[package]] name = "monero-mlsag" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "monero-generators", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "monero-primitives" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "monero-generators", @@ -2134,7 +2134,7 @@ dependencies = [ [[package]] name = "monero-rpc" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "hex", @@ -2150,7 +2150,7 @@ dependencies = [ [[package]] name = "monero-serai" version = "0.1.4-alpha" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", "hex-literal", @@ -2168,7 +2168,7 @@ dependencies = [ [[package]] name = "monero-simple-request-rpc" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "digest_auth", "hex", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "simple-request" version = "0.1.0" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "http-body-util", "hyper", @@ -2931,7 +2931,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "std-shims" version = "0.1.1" -source = "git+https://github.com/Cuprate/serai.git?rev=e6fdef6#e6fdef6d0b4481932ac9647796eb3fa56197ed66" +source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "hashbrown 0.14.5", "spin", diff --git a/Cargo.toml b/Cargo.toml index 70cf0b5b..1192a6b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ futures = { version = "0.3", default-features = false } hex = { version = "0.4", default-features = false } hex-literal = { version = "0.4", default-features = false } indexmap = { version = "2", default-features = false } -monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "e6fdef6", default-features = false } +monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2", default-features = false } nu-ansi-term = { version = "0.46", default-features = false } paste = { version = "1", default-features = false } pin-project = { version = "1", default-features = false } @@ -145,8 +145,8 @@ tracing-subscriber = { version = "0.3", default-features = false } tracing = { version = "0.1", default-features = false } ## workspace.dev-dependencies -monero-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "e6fdef6" } -monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "e6fdef6" } +monero-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2" } +monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2" } tempfile = { version = "3" } pretty_assertions = { version = "1" } proptest = { version = "1" } diff --git a/binaries/cuprated/config/0.0.1.toml b/binaries/cuprated/config/0.0.1.toml index 95804dcc..75995e06 100644 --- a/binaries/cuprated/config/0.0.1.toml +++ b/binaries/cuprated/config/0.0.1.toml @@ -43,9 +43,9 @@ peer_save_period = { secs = 90, nanos = 0 } ## The block downloader config. [p2p.block_downloader] ## The size of the buffer of sequential blocks waiting to be verified and added to the chain (bytes). -buffer_bytes = 50_000_000 +buffer_bytes = 1_000_000_000 ## The size of the queue of blocks which are waiting for a parent block to be downloaded (bytes). -in_progress_queue_bytes = 50_000_000 +in_progress_queue_bytes = 500_000_000 ## The target size of a batch of blocks (bytes), must not exceed 100MB. target_batch_bytes = 10_000_000 ## The amount of time between checking the pool of connected peers for free peers to download blocks. diff --git a/binaries/cuprated/src/blockchain/manager/handler.rs b/binaries/cuprated/src/blockchain/manager/handler.rs index c1ad7b17..0c8264bf 100644 --- a/binaries/cuprated/src/blockchain/manager/handler.rs +++ b/binaries/cuprated/src/blockchain/manager/handler.rs @@ -452,7 +452,7 @@ impl super::BlockchainManager { .iter() .flat_map(|tx| { tx.tx.prefix().inputs.iter().map(|input| match input { - Input::ToKey { key_image, .. } => key_image.compress().0, + Input::ToKey { key_image, .. } => key_image.0, Input::Gen(_) => unreachable!(), }) }) diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs index da9c7bc2..0fe71e53 100644 --- a/binaries/cuprated/src/config/p2p.rs +++ b/binaries/cuprated/src/config/p2p.rs @@ -47,8 +47,8 @@ impl From<BlockDownloaderConfig> for cuprate_p2p::block_downloader::BlockDownloa impl Default for BlockDownloaderConfig { fn default() -> Self { Self { - buffer_bytes: 50_000_000, - in_progress_queue_bytes: 50_000_000, + buffer_bytes: 1_000_000_000, + in_progress_queue_bytes: 500_000_000, check_client_pool_interval: Duration::from_secs(30), target_batch_bytes: 10_000_000, } diff --git a/consensus/rules/src/transactions.rs b/consensus/rules/src/transactions.rs index b4eac191..bb3bec78 100644 --- a/consensus/rules/src/transactions.rs +++ b/consensus/rules/src/transactions.rs @@ -1,17 +1,20 @@ use std::cmp::Ordering; +use curve25519_dalek::EdwardsPoint; use monero_serai::{ + io::decompress_point, ringct::RctType, transaction::{Input, Output, Timelock, Transaction}, }; -pub use cuprate_types::TxVersion; - use crate::{ batch_verifier::BatchVerifier, blocks::penalty_free_zone, check_point_canonically_encoded, is_decomposed_amount, HardFork, }; +// re-export. +pub use cuprate_types::TxVersion; + mod contextual_data; mod ring_ct; mod ring_signatures; @@ -327,7 +330,10 @@ fn check_key_images(input: &Input) -> Result<(), TransactionError> { match input { Input::ToKey { key_image, .. } => { // this happens in monero-serai but we may as well duplicate the check. - if !key_image.is_torsion_free() { + if !decompress_point(*key_image) + .as_ref() + .is_some_and(EdwardsPoint::is_torsion_free) + { return Err(TransactionError::KeyImageIsNotInPrimeSubGroup); } } @@ -388,7 +394,7 @@ fn check_ring_members_unique(input: &Input, hf: HardFork) -> Result<(), Transact /// ref: <https://monero-book.cuprate.org/consensus_rules/transactions/inputs.html#sorted-inputs> fn check_inputs_sorted(inputs: &[Input], hf: HardFork) -> Result<(), TransactionError> { let get_ki = |inp: &Input| match inp { - Input::ToKey { key_image, .. } => Ok(key_image.compress().to_bytes()), + Input::ToKey { key_image, .. } => Ok(key_image.to_bytes()), Input::Gen(_) => Err(TransactionError::IncorrectInputType), }; diff --git a/consensus/rules/src/transactions/contextual_data.rs b/consensus/rules/src/transactions/contextual_data.rs index 35f8e47d..50e5895d 100644 --- a/consensus/rules/src/transactions/contextual_data.rs +++ b/consensus/rules/src/transactions/contextual_data.rs @@ -1,6 +1,6 @@ use std::cmp::{max, min}; -use curve25519_dalek::EdwardsPoint; +use curve25519_dalek::edwards::CompressedEdwardsY; use indexmap::{IndexMap, IndexSet}; use monero_serai::transaction::{Input, Timelock}; @@ -57,9 +57,9 @@ pub fn insert_ring_member_ids( #[derive(Debug)] pub enum Rings { /// Legacy, pre-ringCT, rings. - Legacy(Vec<Vec<EdwardsPoint>>), + Legacy(Vec<Vec<CompressedEdwardsY>>), /// `RingCT` rings, (outkey, amount commitment). - RingCT(Vec<Vec<[EdwardsPoint; 2]>>), + RingCT(Vec<Vec<[CompressedEdwardsY; 2]>>), } /// Information on the outputs the transaction is referencing for inputs (ring members). diff --git a/consensus/rules/src/transactions/ring_ct.rs b/consensus/rules/src/transactions/ring_ct.rs index 32cedd47..1d56e90e 100644 --- a/consensus/rules/src/transactions/ring_ct.rs +++ b/consensus/rules/src/transactions/ring_ct.rs @@ -2,6 +2,7 @@ use curve25519_dalek::{EdwardsPoint, Scalar}; use hex_literal::hex; use monero_serai::{ generators::H, + io::decompress_point, ringct::{ clsag::ClsagError, mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix}, @@ -74,9 +75,21 @@ fn simple_type_balances(rct_sig: &RctProofs) -> Result<(), RingCTError> { } }; - let sum_inputs = pseudo_outs.iter().sum::<EdwardsPoint>(); - let sum_outputs = - rct_sig.base.commitments.iter().sum::<EdwardsPoint>() + Scalar::from(rct_sig.base.fee) * *H; + let sum_inputs = pseudo_outs + .iter() + .copied() + .map(decompress_point) + .sum::<Option<EdwardsPoint>>() + .ok_or(RingCTError::SimpleAmountDoNotBalance)?; + let sum_outputs = rct_sig + .base + .commitments + .iter() + .copied() + .map(decompress_point) + .sum::<Option<EdwardsPoint>>() + .ok_or(RingCTError::SimpleAmountDoNotBalance)? + + Scalar::from(rct_sig.base.fee) * *H; if sum_inputs == sum_outputs { Ok(()) @@ -178,7 +191,7 @@ pub(crate) fn check_input_signatures( .collect::<Vec<_>>(); let mut matrix = - AggregateRingMatrixBuilder::new(&proofs.base.commitments, proofs.base.fee); + AggregateRingMatrixBuilder::new(&proofs.base.commitments, proofs.base.fee)?; rings.iter().try_for_each(|ring| matrix.push_ring(ring))?; @@ -210,7 +223,7 @@ pub(crate) fn check_input_signatures( panic!("How did we build a ring with no decoys?"); }; - Ok(clsags.verify(ring, key_image, pseudo_out, msg)?) + Ok(clsags.verify(ring.clone(), key_image, pseudo_out, msg)?) }), } } diff --git a/consensus/rules/src/transactions/tests.rs b/consensus/rules/src/transactions/tests.rs index e154396b..2ce37fca 100644 --- a/consensus/rules/src/transactions/tests.rs +++ b/consensus/rules/src/transactions/tests.rs @@ -1,7 +1,7 @@ use std::ops::Range; use curve25519_dalek::{ - constants::{ED25519_BASEPOINT_POINT, EIGHT_TORSION}, + constants::{ED25519_BASEPOINT_COMPRESSED, EIGHT_TORSION}, edwards::CompressedEdwardsY, EdwardsPoint, }; @@ -92,7 +92,7 @@ fn test_decoy_info() { fn test_torsion_ki() { for &key_image in &EIGHT_TORSION[1..] { assert!(check_key_images(&Input::ToKey { - key_image, + key_image: key_image.compress(), amount: None, key_offsets: vec![], }) @@ -262,13 +262,13 @@ proptest! { #[test] fn test_check_input_has_decoys(key_offsets in vec(any::<u64>(), 1..10_000)) { assert!(check_input_has_decoys(&Input::ToKey { - key_image: ED25519_BASEPOINT_POINT, + key_image: ED25519_BASEPOINT_COMPRESSED, amount: None, key_offsets, }).is_ok()); assert!(check_input_has_decoys(&Input::ToKey { - key_image: ED25519_BASEPOINT_POINT, + key_image: ED25519_BASEPOINT_COMPRESSED, amount: None, key_offsets: vec![], }).is_err()); diff --git a/consensus/src/transactions.rs b/consensus/src/transactions.rs index 7b6222eb..ec6dfdad 100644 --- a/consensus/src/transactions.rs +++ b/consensus/src/transactions.rs @@ -289,7 +289,7 @@ pub(crate) async fn check_kis_unique<D: Database>( txs.try_for_each(|tx| { tx.tx.prefix().inputs.iter().try_for_each(|input| { if let Input::ToKey { key_image, .. } = input { - if !spent_kis.insert(key_image.compress().0) { + if !spent_kis.insert(key_image.0) { tracing::debug!("Duplicate key image found in batch."); return Err(ConsensusError::Transaction(TransactionError::KeyImageSpent)); } diff --git a/consensus/src/transactions/contextual_data.rs b/consensus/src/transactions/contextual_data.rs index 72236690..46533731 100644 --- a/consensus/src/transactions/contextual_data.rs +++ b/consensus/src/transactions/contextual_data.rs @@ -96,27 +96,19 @@ pub fn new_ring_member_info( .collect::<Vec<_>>() }) .collect(), - rings: new_rings(used_outs, tx_version)?, + rings: new_rings(used_outs, tx_version), decoy_info, }) } /// Builds the [`Rings`] for the transaction inputs, from the given outputs. -fn new_rings( - outputs: Vec<Vec<OutputOnChain>>, - tx_version: TxVersion, -) -> Result<Rings, TransactionError> { - Ok(match tx_version { +fn new_rings(outputs: Vec<Vec<OutputOnChain>>, tx_version: TxVersion) -> Rings { + match tx_version { TxVersion::RingSignatures => Rings::Legacy( outputs .into_iter() - .map(|inp_outs| { - inp_outs - .into_iter() - .map(|out| out.key.ok_or(TransactionError::RingMemberNotFoundOrInvalid)) - .collect::<Result<Vec<_>, TransactionError>>() - }) - .collect::<Result<Vec<_>, TransactionError>>()?, + .map(|inp_outs| inp_outs.into_iter().map(|out| out.key).collect::<Vec<_>>()) + .collect::<Vec<_>>(), ), TxVersion::RingCT => Rings::RingCT( outputs @@ -124,18 +116,12 @@ fn new_rings( .map(|inp_outs| { inp_outs .into_iter() - .map(|out| { - Ok([ - out.key - .ok_or(TransactionError::RingMemberNotFoundOrInvalid)?, - out.commitment, - ]) - }) - .collect::<Result<_, TransactionError>>() + .map(|out| [out.key, out.commitment]) + .collect::<_>() }) - .collect::<Result<_, _>>()?, + .collect::<_>(), ), - }) + } } /// Retrieves an [`OutputCache`] for the list of transactions. @@ -158,7 +144,7 @@ pub async fn get_output_cache<D: Database>( .call(BlockchainReadRequest::Outputs(output_ids)) .await? else { - panic!("Database sent incorrect response!") + unreachable!(); }; Ok(outputs) diff --git a/consensus/tests/verify_correct_txs.rs b/consensus/tests/verify_correct_txs.rs index 1166e7aa..0c8171e0 100644 --- a/consensus/tests/verify_correct_txs.rs +++ b/consensus/tests/verify_correct_txs.rs @@ -7,7 +7,7 @@ use std::{ sync::Arc, }; -use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY}; +use curve25519_dalek::{constants::ED25519_BASEPOINT_COMPRESSED, edwards::CompressedEdwardsY}; use indexmap::IndexMap; use monero_serai::transaction::{Timelock, Transaction}; use tower::service_fn; @@ -71,13 +71,8 @@ macro_rules! test_verify_valid_v2_tx { OutputOnChain { height: 0, time_lock: Timelock::None, - commitment: CompressedEdwardsY::from_slice(&hex_literal::hex!($commitment)) - .unwrap() - .decompress() - .unwrap(), - key: CompressedEdwardsY::from_slice(&hex_literal::hex!($ring_member)) - .unwrap() - .decompress(), + commitment: CompressedEdwardsY(hex_literal::hex!($commitment)), + key: CompressedEdwardsY(hex_literal::hex!($ring_member)), }),)+)+ ]; @@ -103,10 +98,8 @@ macro_rules! test_verify_valid_v2_tx { OutputOnChain { height: 0, time_lock: Timelock::None, - commitment: ED25519_BASEPOINT_POINT, - key: CompressedEdwardsY::from_slice(&hex_literal::hex!($ring_member)) - .unwrap() - .decompress(), + commitment: ED25519_BASEPOINT_COMPRESSED, + key: CompressedEdwardsY(hex_literal::hex!($ring_member)), }),)+)+ ]; diff --git a/helper/src/crypto.rs b/helper/src/crypto.rs index 1a27cd30..8a2328e2 100644 --- a/helper/src/crypto.rs +++ b/helper/src/crypto.rs @@ -4,8 +4,11 @@ use std::sync::LazyLock; use curve25519_dalek::{ - constants::ED25519_BASEPOINT_POINT, edwards::VartimeEdwardsPrecomputation, - traits::VartimePrecomputedMultiscalarMul, EdwardsPoint, Scalar, + constants::{ED25519_BASEPOINT_COMPRESSED, ED25519_BASEPOINT_POINT}, + edwards::CompressedEdwardsY, + edwards::VartimeEdwardsPrecomputation, + traits::VartimePrecomputedMultiscalarMul, + Scalar, }; use monero_serai::generators::H; @@ -49,15 +52,16 @@ static H_PRECOMP: LazyLock<VartimeEdwardsPrecomputation> = /// # Invariant /// This function assumes that the [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`] /// table is sorted. -pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[EdwardsPoint; 172]> = LazyLock::new(|| { - let mut lookup_table: [EdwardsPoint; 172] = [ED25519_BASEPOINT_POINT; 172]; +pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[CompressedEdwardsY; 172]> = + LazyLock::new(|| { + let mut lookup_table: [CompressedEdwardsY; 172] = [ED25519_BASEPOINT_COMPRESSED; 172]; - for (i, amount) in ZERO_COMMITMENT_DECOMPOSED_AMOUNT.into_iter().enumerate() { - lookup_table[i] = ED25519_BASEPOINT_POINT + *H * Scalar::from(amount); - } + for (i, amount) in ZERO_COMMITMENT_DECOMPOSED_AMOUNT.into_iter().enumerate() { + lookup_table[i] = (ED25519_BASEPOINT_POINT + *H * Scalar::from(amount)).compress(); + } - lookup_table -}); + lookup_table + }); //---------------------------------------------------------------------------------------------------- Free functions @@ -66,7 +70,7 @@ pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[EdwardsPoint; 172]> = LazyLoc /// It will first attempt to lookup into the table of known Pre-RCT value. /// Compute it otherwise. #[expect(clippy::cast_possible_truncation)] -pub fn compute_zero_commitment(amount: u64) -> EdwardsPoint { +pub fn compute_zero_commitment(amount: u64) -> CompressedEdwardsY { // OPTIMIZATION: Unlike monerod which execute a linear search across its lookup // table (O(n)). Cuprate is making use of an arithmetic based constant time // version (O(1)). It has been benchmarked in both hit and miss scenarios against @@ -78,7 +82,7 @@ pub fn compute_zero_commitment(amount: u64) -> EdwardsPoint { // the amount without its most significant digit. let Some(log) = amount.checked_ilog10() else { // amount = 0 so H component is 0. - return ED25519_BASEPOINT_POINT; + return ED25519_BASEPOINT_COMPRESSED; }; let div = 10_u64.pow(log); @@ -89,7 +93,9 @@ pub fn compute_zero_commitment(amount: u64) -> EdwardsPoint { // there aren't only trailing zeroes behind the most significant digit. // The amount is not part of the table and can calculated apart. if most_significant_digit * div != amount { - return H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]); + return H_PRECOMP + .vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]) + .compress(); } // Calculating the index back by progressing within the powers of 10. @@ -116,7 +122,10 @@ mod test { fn compare_lookup_with_computation() { for amount in ZERO_COMMITMENT_DECOMPOSED_AMOUNT { let commitment = H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]); - assert!(commitment == compute_zero_commitment(amount)); + assert_eq!( + commitment, + compute_zero_commitment(amount).decompress().unwrap() + ); } } } diff --git a/helper/src/tx.rs b/helper/src/tx.rs index 53706ecf..263bb1e9 100644 --- a/helper/src/tx.rs +++ b/helper/src/tx.rs @@ -35,7 +35,7 @@ pub fn tx_fee(tx: &Transaction) -> u64 { #[cfg(test)] mod test { - use curve25519_dalek::{edwards::CompressedEdwardsY, EdwardsPoint}; + use curve25519_dalek::edwards::CompressedEdwardsY; use monero_serai::transaction::{NotPruned, Output, Timelock, TransactionPrefix}; use super::*; @@ -46,7 +46,7 @@ mod test { let input = Input::ToKey { amount: Some(u64::MAX), key_offsets: vec![], - key_image: EdwardsPoint::default(), + key_image: CompressedEdwardsY::default(), }; let output = Output { diff --git a/storage/blockchain/src/ops/output.rs b/storage/blockchain/src/ops/output.rs index 96d94bb1..002ac189 100644 --- a/storage/blockchain/src/ops/output.rs +++ b/storage/blockchain/src/ops/output.rs @@ -165,9 +165,7 @@ pub fn output_to_output_on_chain( Timelock::None }; - let key = CompressedEdwardsY::from_slice(&output.key) - .map(|y| y.decompress()) - .unwrap_or(None); + let key = CompressedEdwardsY(output.key); Ok(OutputOnChain { height: output.height as usize, @@ -191,10 +189,7 @@ pub fn rct_output_to_output_on_chain( table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>, ) -> DbResult<OutputOnChain> { // INVARIANT: Commitments stored are valid when stored by the database. - let commitment = CompressedEdwardsY::from_slice(&rct_output.commitment) - .unwrap() - .decompress() - .unwrap(); + let commitment = CompressedEdwardsY(rct_output.commitment); let time_lock = if rct_output .output_flags @@ -205,9 +200,7 @@ pub fn rct_output_to_output_on_chain( Timelock::None }; - let key = CompressedEdwardsY::from_slice(&rct_output.key) - .map(|y| y.decompress()) - .unwrap_or(None); + let key = CompressedEdwardsY(rct_output.key); Ok(OutputOnChain { height: rct_output.height as usize, diff --git a/storage/blockchain/src/ops/tx.rs b/storage/blockchain/src/ops/tx.rs index 0312f215..0617f8db 100644 --- a/storage/blockchain/src/ops/tx.rs +++ b/storage/blockchain/src/ops/tx.rs @@ -96,7 +96,7 @@ pub fn add_tx( match inputs { // Key images. Input::ToKey { key_image, .. } => { - add_key_image(key_image.compress().as_bytes(), tables.key_images_mut())?; + add_key_image(key_image.as_bytes(), tables.key_images_mut())?; } // This is a miner transaction, set it for later use. Input::Gen(_) => miner_tx = true, @@ -154,7 +154,7 @@ pub fn add_tx( height, output_flags, tx_idx: tx_id, - commitment: commitment.compress().0, + commitment: commitment.0, }, tables.rct_outputs_mut(), ) @@ -219,7 +219,7 @@ pub fn remove_tx(tx_hash: &TxHash, tables: &mut impl TablesMut) -> DbResult<(TxI match inputs { // Key images. Input::ToKey { key_image, .. } => { - remove_key_image(key_image.compress().as_bytes(), tables.key_images_mut())?; + remove_key_image(key_image.as_bytes(), tables.key_images_mut())?; } // This is a miner transaction, set it for later use. Input::Gen(_) => miner_tx = true, diff --git a/storage/txpool/src/ops/key_images.rs b/storage/txpool/src/ops/key_images.rs index 76cae141..7933f3af 100644 --- a/storage/txpool/src/ops/key_images.rs +++ b/storage/txpool/src/ops/key_images.rs @@ -48,7 +48,7 @@ pub(super) fn remove_tx_key_images( /// This function will panic if the [`Input`] is not [`Input::ToKey`] fn ki_from_input(input: &Input) -> [u8; 32] { match input { - Input::ToKey { key_image, .. } => key_image.compress().0, + Input::ToKey { key_image, .. } => key_image.0, Input::Gen(_) => panic!("miner tx cannot be added to the txpool"), } } diff --git a/types/src/json/tx.rs b/types/src/json/tx.rs index a18dc89a..8aa6b20b 100644 --- a/types/src/json/tx.rs +++ b/types/src/json/tx.rs @@ -71,7 +71,7 @@ impl From<transaction::Transaction> for Transaction { let key = Key { amount: amount.unwrap_or(0), key_offsets, - k_image: HexBytes::<32>(key_image.compress().0), + k_image: HexBytes::<32>(key_image.0), }; Some(Input { key }) @@ -169,7 +169,7 @@ impl From<transaction::Transaction> for Transaction { .base .commitments .into_iter() - .map(|point| HexBytes::<32>(point.compress().0)) + .map(|point| HexBytes::<32>(point.0)) .collect(); let rct_signatures = RctSignatures::NonCoinbase { diff --git a/types/src/output_cache.rs b/types/src/output_cache.rs index 04e53bf6..00338e25 100644 --- a/types/src/output_cache.rs +++ b/types/src/output_cache.rs @@ -1,4 +1,4 @@ -use curve25519_dalek::EdwardsPoint; +use curve25519_dalek::edwards::CompressedEdwardsY; use indexmap::{IndexMap, IndexSet}; use monero_serai::transaction::Transaction; @@ -87,8 +87,7 @@ impl OutputCache { OutputOnChain { height, time_lock: tx.prefix().additional_timelock, - // TODO: this needs to check the point is canonical. - key: out.key.decompress(), + key: out.key, commitment: get_output_commitment(tx, i), }, ); @@ -111,7 +110,7 @@ impl OutputCache { } /// Returns the amount commitment for the output at the given index `i` in the [`Transaction`] -fn get_output_commitment(tx: &Transaction, i: usize) -> EdwardsPoint { +fn get_output_commitment(tx: &Transaction, i: usize) -> CompressedEdwardsY { match tx { Transaction::V1 { prefix, .. } => { compute_zero_commitment(prefix.outputs[i].amount.unwrap_or_default()) diff --git a/types/src/types.rs b/types/src/types.rs index 8a5b5aad..61de9c32 100644 --- a/types/src/types.rs +++ b/types/src/types.rs @@ -2,7 +2,7 @@ use std::num::NonZero; -use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::edwards::CompressedEdwardsY; use monero_serai::{ block::Block, transaction::{Timelock, Transaction}, @@ -142,9 +142,9 @@ pub struct OutputOnChain { /// The timelock of this output, if any. pub time_lock: Timelock, /// The public key of this output, if any. - pub key: Option<EdwardsPoint>, + pub key: CompressedEdwardsY, /// The output's commitment. - pub commitment: EdwardsPoint, + pub commitment: CompressedEdwardsY, } /// Input required to generate an output histogram.