diff --git a/monero-adaptor/src/lib.rs b/monero-adaptor/src/lib.rs index 2fff4af4..48a22392 100644 --- a/monero-adaptor/src/lib.rs +++ b/monero-adaptor/src/lib.rs @@ -4,23 +4,17 @@ use anyhow::{bail, Result}; use curve25519_dalek::constants::ED25519_BASEPOINT_POINT; -use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint}; use curve25519_dalek::scalar::Scalar; use hash_edwards_to_edwards::hash_point_to_point; use rand::{CryptoRng, Rng}; use std::convert::TryInto; -use tiny_keccak::{Hasher, Keccak}; +use tiny_keccak::Hasher; pub const RING_SIZE: usize = 11; -const DOMAIN_TAG: &str = "CSLAG_c"; - -#[rustfmt::skip] -// aggregation hashes: -// mu_{P, C} = -// keccak256("CLSAG_agg_{0, 1}" || -// ring || ring of commitments || I || z * hash_to_point(signing pk) || pseudooutput commitment) -// -// where z = blinding of real commitment - blinding of pseudooutput commitment. +const HASH_KEY_CLSAG_AGG_0: &str = "CLSAG_agg_0"; +const HASH_KEY_CLSAG_AGG_1: &str = "CLSAG_agg_1"; +const HASH_KEY_CLSAG_ROUND: &str = "CLSAG_round"; // for every iteration we compute: // c_p = h_prev * mu_P; and @@ -31,34 +25,133 @@ const DOMAIN_TAG: &str = "CSLAG_c"; // // h = keccak256("CLSAG_round" || ring // ring of commitments || pseudooutput commitment || msg || L_i || R_i) + +struct AggregationHashes { + mu_P: Scalar, + mu_C: Scalar, +} + +impl AggregationHashes { + pub fn new( + ring: [EdwardsPoint; RING_SIZE], + commitment_ring: [EdwardsPoint; RING_SIZE], + I: EdwardsPoint, + z: Scalar, + H_p_pk: EdwardsPoint, + pseudo_output_commitment: EdwardsPoint, + ) -> Self { + let z_key_image = z * H_p_pk; + + let ring = ring + .iter() + .flat_map(|pk| pk.compress().as_bytes().to_vec()) + .collect::>(); + let commitment_ring = commitment_ring + .iter() + .flat_map(|pk| pk.compress().as_bytes().to_vec()) + .collect::>(); + let I = I.compress(); + let z_key_image = z_key_image.compress(); + let pseudo_output_commitment = pseudo_output_commitment.compress(); + + let mu_P = Self::hash( + HASH_KEY_CLSAG_AGG_0, + &ring, + &commitment_ring, + &I, + &z_key_image, + &pseudo_output_commitment, + ); + let mu_C = Self::hash( + HASH_KEY_CLSAG_AGG_1, + &ring, + &commitment_ring, + &I, + &z_key_image, + &pseudo_output_commitment, + ); + + Self { mu_P, mu_C } + } + + // aggregation hashes: + // mu_{P, C} = + // keccak256("CLSAG_agg_{0, 1}" || + // ring || ring of commitments || I || z * hash_to_point(signing pk) || + // pseudooutput commitment) + // + // where z = blinding of real commitment - blinding of pseudooutput commitment. + fn hash( + domain_prefix: &str, + ring: &[u8], + commitment_ring: &[u8], + I: &CompressedEdwardsY, + z_key_image: &CompressedEdwardsY, + pseudo_output_commitment: &CompressedEdwardsY, + ) -> Scalar { + let mut hasher = tiny_keccak::Keccak::v256(); + hasher.update(domain_prefix.as_bytes()); + hasher.update(ring); + hasher.update(commitment_ring); + hasher.update(I.as_bytes()); + hasher.update(z_key_image.as_bytes()); + hasher.update(pseudo_output_commitment.as_bytes()); + + let mut hash = [0u8; 32]; + hasher.finalize(&mut hash); + + Scalar::from_bytes_mod_order(hash) + } +} + fn challenge( + prefix: &[u8], s_i: Scalar, pk_i: EdwardsPoint, h_prev: Scalar, I: EdwardsPoint, - mut prefix: Keccak, ) -> Result { let L_i = s_i * ED25519_BASEPOINT_POINT + h_prev * pk_i; let H_p_pk_i = hash_point_to_point(pk_i); - let R_i = s_i * H_p_pk_i + h_prev * I; - prefix.update(&L_i.compress().as_bytes().to_vec()); - prefix.update(&R_i.compress().as_bytes().to_vec()); + let mut hasher = tiny_keccak::Keccak::v256(); + hasher.update(prefix); + hasher.update(&L_i.compress().as_bytes().to_vec()); + hasher.update(&R_i.compress().as_bytes().to_vec()); - let mut output = [0u8; 64]; - prefix.finalize(&mut output); + let mut output = [0u8; 32]; + hasher.finalize(&mut output); - Ok(Scalar::from_bytes_mod_order_wide(&output)) + Ok(Scalar::from_bytes_mod_order(output)) } -#[rustfmt::skip] -// h_0 = keccak256("CLSAG_round" || ring -// ring of commitments || pseudooutput commitment || msg || alpha * G || alpha * hash_to_point(signing pk)) +// h_0 = keccak256("CLSAG_round" || ring || +// ring of commitments || pseudooutput commitment || msg || alpha * G || +// alpha * hash_to_point(signing pk)) // // where alpha is random +// TODO: Create ring newtype +fn clsag_round_hash_prefix( + ring: &[u8], + commitment_ring: &[u8], + pseudo_output_commitment: &EdwardsPoint, + msg: &[u8], +) -> Vec { + // TODO: Set capacity + let mut prefix = Vec::new(); + + prefix.extend(HASH_KEY_CLSAG_ROUND.as_bytes()); + prefix.extend(ring); + prefix.extend(commitment_ring); + prefix.extend(pseudo_output_commitment.compress().as_bytes()); + prefix.extend(msg); + + prefix +} + #[allow(clippy::too_many_arguments)] fn final_challenge( fake_responses: [Scalar; RING_SIZE - 1], @@ -70,18 +163,16 @@ fn final_challenge( I_hat_b: EdwardsPoint, R_prime_a: EdwardsPoint, I: EdwardsPoint, - msg: [u8; 32], + msg: &[u8], ) -> Result<(Scalar, Scalar)> { + let ring_concat = ring + .iter() + .flat_map(|pk| pk.compress().as_bytes().to_vec()) + .collect::>(); + let prefix = clsag_round_hash_prefix(&ring_concat, todo!(), todo!(), msg); let h_0 = { - let ring = ring - .iter() - .flat_map(|pk| pk.compress().as_bytes().to_vec()) - .collect::>(); - - let mut keccak = tiny_keccak::Keccak::v512(); - keccak.update(DOMAIN_TAG.as_bytes()); - keccak.update(ring.as_slice()); - keccak.update(&msg); + let mut keccak = tiny_keccak::Keccak::v256(); + keccak.update(&prefix); keccak.update((T_a + T_b + R_a).compress().as_bytes()); keccak.update((I_hat_a + I_hat_b + R_prime_a).compress().as_bytes()); let mut output = [0u8; 64]; @@ -89,25 +180,19 @@ fn final_challenge( Scalar::from_bytes_mod_order_wide(&output) }; - // ring size is 11 let ring_concat = ring .iter() .flat_map(|pk| pk.compress().as_bytes().to_vec()) .collect::>(); - let mut keccak = tiny_keccak::Keccak::v512(); - keccak.update(DOMAIN_TAG.as_bytes()); - keccak.update(ring_concat.as_slice()); - keccak.update(&msg); - let h_last = fake_responses .iter() .enumerate() .fold(h_0, |h_prev, (i, s_i)| { let pk_i = ring[i + 1]; // TODO: Do not unwrap here - challenge(*s_i, pk_i, h_prev, I, keccak.clone()).unwrap() + challenge(&prefix, *s_i, pk_i, h_prev, I).unwrap() }); Ok((h_last, h_0)) @@ -158,16 +243,17 @@ impl Signature { .flat_map(|pk| pk.compress().as_bytes().to_vec()) .collect::>(); - let mut prefix = tiny_keccak::Keccak::v512(); - prefix.update(DOMAIN_TAG.as_bytes()); - prefix.update(ring_concat.as_slice()); - prefix.update(msg); - let mut h = self.h_0; for (i, s_i) in self.responses.iter().enumerate() { let pk_i = ring[(i + 1) % RING_SIZE]; - h = challenge(*s_i, pk_i, h, self.I, prefix.clone())?; + h = challenge( + &clsag_round_hash_prefix(&ring_concat, todo!(), todo!(), msg), + *s_i, + pk_i, + h, + self.I, + )?; } Ok(h == self.h_0) @@ -276,11 +362,10 @@ impl Alice0 { msg.I_hat_b, self.R_prime_a, self.I_a + msg.I_b, - self.msg, + &self.msg, )?; - // TODO: Final scalar is computed slightly differentley for Monero (involves - // mu_P and mu_C constants) + // TODO: alpha_a - h_last * (mu_P * s_prime_a + mu_C * z) let s_0_a = self.alpha_a - h_last * self.s_prime_a; Ok(Alice1 { @@ -449,9 +534,10 @@ impl Bob1 { self.I_hat_b, self.R_prime_a, I_a + self.I_b, - self.msg, + &self.msg, )?; + // TODO: alpha_b - h_last * (mu_P * s_b + mu_C * z); let s_0_b = self.alpha_b - h_last * self.s_b; let adaptor_sig = AdaptorSignature { diff --git a/monero-adaptor/tests/integration_test.rs b/monero-adaptor/tests/integration_test.rs index 90f42513..8e82af0c 100644 --- a/monero-adaptor/tests/integration_test.rs +++ b/monero-adaptor/tests/integration_test.rs @@ -13,7 +13,6 @@ use monero::{ PrivateKey, PublicKey, Transaction, TransactionPrefix, TxIn, TxOut, VarInt, ViewPair, }; use monero_harness::Monero; -use monero_rpc::monerod; use monero_rpc::monerod::{GetOutputsOut, MonerodRpc}; use monero_wallet::MonerodClientExt; use rand::rngs::OsRng; @@ -22,41 +21,14 @@ use std::convert::TryInto; use std::iter; use testcontainers::clients::Cli; -async fn prepare_nodes(address: monero::Address, amount: u64) -> (monerod::Client, monero::Hash) { - let cli = Cli::default(); - - let (monero, _monerod_container, _monero_wallet_rpc_containers) = - Monero::new(&cli, vec![]).await.unwrap(); - - monero.init_miner().await.unwrap(); - - let wallet = monero.wallet("miner").expect("wallet to exist"); - - let transfer = wallet - .transfer(&address.to_string(), amount) - .await - .expect("lock to succeed"); - - let monerod = monero.monerod().client(); - let miner_address = wallet - .address() - .await - .expect("miner address to exist") - .address; - monerod - .generateblocks(10, miner_address) - .await - .expect("can generate blocks"); - - let lock_tx_hash = transfer.tx_hash.parse().unwrap(); - - (monerod.clone(), lock_tx_hash) -} - #[tokio::test] async fn monerod_integration_test() { let mut rng = rand::rngs::StdRng::from_seed([0u8; 32]); + let cli = Cli::default(); + let (monero, _monerod_container, _monero_wallet_rpc_containers) = + Monero::new(&cli, vec![]).await.unwrap(); + let s_a = curve25519_dalek::scalar::Scalar::random(&mut rng); let s_b = curve25519_dalek::scalar::Scalar::random(&mut rng); let lock_kp = monero::KeyPair { @@ -72,7 +44,27 @@ async fn monerod_integration_test() { dbg!(lock_address.to_string()); // 45BcRKAHaA4b5A9SdamF2f1w7zk1mKkBPhaqVoDWzuAtMoSAytzm5A6b2fE6ruupkAFmStrQzdojUExt96mR3oiiSKp8Exf - let (client, lock_tx) = prepare_nodes(lock_address, lock_amount).await; + monero.init_miner().await.unwrap(); + let wallet = monero.wallet("miner").expect("wallet to exist"); + + let transfer = wallet + .transfer(&lock_address.to_string(), lock_amount) + .await + .expect("lock to succeed"); + + let client = monero.monerod().client(); + + let miner_address = wallet + .address() + .await + .expect("miner address to exist") + .address; + client + .generateblocks(10, miner_address) + .await + .expect("can generate blocks"); + + let lock_tx = transfer.tx_hash.parse().unwrap(); let o_indexes_response = client.get_o_indexes(lock_tx).await.unwrap(); diff --git a/monero-harness/src/lib.rs b/monero-harness/src/lib.rs index 3c7214af..5f45ad53 100644 --- a/monero-harness/src/lib.rs +++ b/monero-harness/src/lib.rs @@ -109,7 +109,7 @@ impl<'c> Monero { let monerod = &self.monerod; let res = monerod .client() - .generateblocks(70, miner_address.clone()) + .generateblocks(150, miner_address.clone()) .await?; tracing::info!("Generated {:?} blocks", res.blocks.len()); miner_wallet.refresh().await?;