mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-01-23 22:01:21 -05:00
[WIP] Conform to Monero CLSAG
This commit is contained in:
parent
cbdda9b9c4
commit
05c1b63aa2
@ -4,23 +4,17 @@
|
|||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
||||||
use curve25519_dalek::edwards::EdwardsPoint;
|
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
use hash_edwards_to_edwards::hash_point_to_point;
|
use hash_edwards_to_edwards::hash_point_to_point;
|
||||||
use rand::{CryptoRng, Rng};
|
use rand::{CryptoRng, Rng};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use tiny_keccak::{Hasher, Keccak};
|
use tiny_keccak::Hasher;
|
||||||
|
|
||||||
pub const RING_SIZE: usize = 11;
|
pub const RING_SIZE: usize = 11;
|
||||||
const DOMAIN_TAG: &str = "CSLAG_c";
|
const HASH_KEY_CLSAG_AGG_0: &str = "CLSAG_agg_0";
|
||||||
|
const HASH_KEY_CLSAG_AGG_1: &str = "CLSAG_agg_1";
|
||||||
#[rustfmt::skip]
|
const HASH_KEY_CLSAG_ROUND: &str = "CLSAG_round";
|
||||||
// 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.
|
|
||||||
|
|
||||||
// for every iteration we compute:
|
// for every iteration we compute:
|
||||||
// c_p = h_prev * mu_P; and
|
// c_p = h_prev * mu_P; and
|
||||||
@ -31,34 +25,133 @@ const DOMAIN_TAG: &str = "CSLAG_c";
|
|||||||
//
|
//
|
||||||
// h = keccak256("CLSAG_round" || ring
|
// h = keccak256("CLSAG_round" || ring
|
||||||
// ring of commitments || pseudooutput commitment || msg || L_i || R_i)
|
// 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::<Vec<u8>>();
|
||||||
|
let commitment_ring = commitment_ring
|
||||||
|
.iter()
|
||||||
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
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(
|
fn challenge(
|
||||||
|
prefix: &[u8],
|
||||||
s_i: Scalar,
|
s_i: Scalar,
|
||||||
pk_i: EdwardsPoint,
|
pk_i: EdwardsPoint,
|
||||||
h_prev: Scalar,
|
h_prev: Scalar,
|
||||||
I: EdwardsPoint,
|
I: EdwardsPoint,
|
||||||
mut prefix: Keccak,
|
|
||||||
) -> Result<Scalar> {
|
) -> Result<Scalar> {
|
||||||
let L_i = s_i * ED25519_BASEPOINT_POINT + h_prev * pk_i;
|
let L_i = s_i * ED25519_BASEPOINT_POINT + h_prev * pk_i;
|
||||||
|
|
||||||
let H_p_pk_i = hash_point_to_point(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;
|
let R_i = s_i * H_p_pk_i + h_prev * I;
|
||||||
|
|
||||||
prefix.update(&L_i.compress().as_bytes().to_vec());
|
let mut hasher = tiny_keccak::Keccak::v256();
|
||||||
prefix.update(&R_i.compress().as_bytes().to_vec());
|
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];
|
let mut output = [0u8; 32];
|
||||||
prefix.finalize(&mut output);
|
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 ||
|
||||||
// h_0 = keccak256("CLSAG_round" || ring
|
// ring of commitments || pseudooutput commitment || msg || alpha * G ||
|
||||||
// ring of commitments || pseudooutput commitment || msg || alpha * G || alpha * hash_to_point(signing pk))
|
// alpha * hash_to_point(signing pk))
|
||||||
//
|
//
|
||||||
// where alpha is random
|
// 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<u8> {
|
||||||
|
// 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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn final_challenge(
|
fn final_challenge(
|
||||||
fake_responses: [Scalar; RING_SIZE - 1],
|
fake_responses: [Scalar; RING_SIZE - 1],
|
||||||
@ -70,18 +163,16 @@ fn final_challenge(
|
|||||||
I_hat_b: EdwardsPoint,
|
I_hat_b: EdwardsPoint,
|
||||||
R_prime_a: EdwardsPoint,
|
R_prime_a: EdwardsPoint,
|
||||||
I: EdwardsPoint,
|
I: EdwardsPoint,
|
||||||
msg: [u8; 32],
|
msg: &[u8],
|
||||||
) -> Result<(Scalar, Scalar)> {
|
) -> Result<(Scalar, Scalar)> {
|
||||||
|
let ring_concat = ring
|
||||||
|
.iter()
|
||||||
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
let prefix = clsag_round_hash_prefix(&ring_concat, todo!(), todo!(), msg);
|
||||||
let h_0 = {
|
let h_0 = {
|
||||||
let ring = ring
|
let mut keccak = tiny_keccak::Keccak::v256();
|
||||||
.iter()
|
keccak.update(&prefix);
|
||||||
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
|
||||||
.collect::<Vec<u8>>();
|
|
||||||
|
|
||||||
let mut keccak = tiny_keccak::Keccak::v512();
|
|
||||||
keccak.update(DOMAIN_TAG.as_bytes());
|
|
||||||
keccak.update(ring.as_slice());
|
|
||||||
keccak.update(&msg);
|
|
||||||
keccak.update((T_a + T_b + R_a).compress().as_bytes());
|
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());
|
keccak.update((I_hat_a + I_hat_b + R_prime_a).compress().as_bytes());
|
||||||
let mut output = [0u8; 64];
|
let mut output = [0u8; 64];
|
||||||
@ -89,25 +180,19 @@ fn final_challenge(
|
|||||||
|
|
||||||
Scalar::from_bytes_mod_order_wide(&output)
|
Scalar::from_bytes_mod_order_wide(&output)
|
||||||
};
|
};
|
||||||
// ring size is 11
|
|
||||||
|
|
||||||
let ring_concat = ring
|
let ring_concat = ring
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
||||||
.collect::<Vec<u8>>();
|
.collect::<Vec<u8>>();
|
||||||
|
|
||||||
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
|
let h_last = fake_responses
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold(h_0, |h_prev, (i, s_i)| {
|
.fold(h_0, |h_prev, (i, s_i)| {
|
||||||
let pk_i = ring[i + 1];
|
let pk_i = ring[i + 1];
|
||||||
// TODO: Do not unwrap here
|
// 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))
|
Ok((h_last, h_0))
|
||||||
@ -158,16 +243,17 @@ impl Signature {
|
|||||||
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
||||||
.collect::<Vec<u8>>();
|
.collect::<Vec<u8>>();
|
||||||
|
|
||||||
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;
|
let mut h = self.h_0;
|
||||||
|
|
||||||
for (i, s_i) in self.responses.iter().enumerate() {
|
for (i, s_i) in self.responses.iter().enumerate() {
|
||||||
let pk_i = ring[(i + 1) % RING_SIZE];
|
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)
|
Ok(h == self.h_0)
|
||||||
@ -276,11 +362,10 @@ impl Alice0 {
|
|||||||
msg.I_hat_b,
|
msg.I_hat_b,
|
||||||
self.R_prime_a,
|
self.R_prime_a,
|
||||||
self.I_a + msg.I_b,
|
self.I_a + msg.I_b,
|
||||||
self.msg,
|
&self.msg,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: Final scalar is computed slightly differentley for Monero (involves
|
// TODO: alpha_a - h_last * (mu_P * s_prime_a + mu_C * z)
|
||||||
// mu_P and mu_C constants)
|
|
||||||
let s_0_a = self.alpha_a - h_last * self.s_prime_a;
|
let s_0_a = self.alpha_a - h_last * self.s_prime_a;
|
||||||
|
|
||||||
Ok(Alice1 {
|
Ok(Alice1 {
|
||||||
@ -449,9 +534,10 @@ impl Bob1 {
|
|||||||
self.I_hat_b,
|
self.I_hat_b,
|
||||||
self.R_prime_a,
|
self.R_prime_a,
|
||||||
I_a + self.I_b,
|
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 s_0_b = self.alpha_b - h_last * self.s_b;
|
||||||
|
|
||||||
let adaptor_sig = AdaptorSignature {
|
let adaptor_sig = AdaptorSignature {
|
||||||
|
@ -13,7 +13,6 @@ use monero::{
|
|||||||
PrivateKey, PublicKey, Transaction, TransactionPrefix, TxIn, TxOut, VarInt, ViewPair,
|
PrivateKey, PublicKey, Transaction, TransactionPrefix, TxIn, TxOut, VarInt, ViewPair,
|
||||||
};
|
};
|
||||||
use monero_harness::Monero;
|
use monero_harness::Monero;
|
||||||
use monero_rpc::monerod;
|
|
||||||
use monero_rpc::monerod::{GetOutputsOut, MonerodRpc};
|
use monero_rpc::monerod::{GetOutputsOut, MonerodRpc};
|
||||||
use monero_wallet::MonerodClientExt;
|
use monero_wallet::MonerodClientExt;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
@ -22,41 +21,14 @@ use std::convert::TryInto;
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use testcontainers::clients::Cli;
|
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]
|
#[tokio::test]
|
||||||
async fn monerod_integration_test() {
|
async fn monerod_integration_test() {
|
||||||
let mut rng = rand::rngs::StdRng::from_seed([0u8; 32]);
|
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_a = curve25519_dalek::scalar::Scalar::random(&mut rng);
|
||||||
let s_b = curve25519_dalek::scalar::Scalar::random(&mut rng);
|
let s_b = curve25519_dalek::scalar::Scalar::random(&mut rng);
|
||||||
let lock_kp = monero::KeyPair {
|
let lock_kp = monero::KeyPair {
|
||||||
@ -72,7 +44,27 @@ async fn monerod_integration_test() {
|
|||||||
|
|
||||||
dbg!(lock_address.to_string()); // 45BcRKAHaA4b5A9SdamF2f1w7zk1mKkBPhaqVoDWzuAtMoSAytzm5A6b2fE6ruupkAFmStrQzdojUExt96mR3oiiSKp8Exf
|
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();
|
let o_indexes_response = client.get_o_indexes(lock_tx).await.unwrap();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl<'c> Monero {
|
|||||||
let monerod = &self.monerod;
|
let monerod = &self.monerod;
|
||||||
let res = monerod
|
let res = monerod
|
||||||
.client()
|
.client()
|
||||||
.generateblocks(70, miner_address.clone())
|
.generateblocks(150, miner_address.clone())
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!("Generated {:?} blocks", res.blocks.len());
|
tracing::info!("Generated {:?} blocks", res.blocks.len());
|
||||||
miner_wallet.refresh().await?;
|
miner_wallet.refresh().await?;
|
||||||
|
Loading…
Reference in New Issue
Block a user