mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 06:29:25 +00:00
Document and clean clsag
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
use core::ops::Deref;
|
||||
#[cfg(feature = "multisig")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
use rand_core::{RngCore, OsRng};
|
||||
@@ -17,11 +16,11 @@ use crate::{
|
||||
wallet::Decoys,
|
||||
ringct::{
|
||||
generate_key_image,
|
||||
clsag::{ClsagInput, Clsag},
|
||||
clsag::{ClsagContext, Clsag},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "multisig")]
|
||||
use crate::ringct::clsag::{ClsagDetails, ClsagMultisig};
|
||||
use crate::ringct::clsag::ClsagMultisig;
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
use frost::{
|
||||
@@ -56,24 +55,24 @@ fn clsag() {
|
||||
.push([dest.deref() * ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
|
||||
}
|
||||
|
||||
let image = generate_key_image(&secrets.0);
|
||||
let (mut clsag, pseudo_out) = Clsag::sign(
|
||||
&mut OsRng,
|
||||
vec![(
|
||||
secrets.0,
|
||||
image,
|
||||
ClsagInput::new(
|
||||
Commitment::new(secrets.1, AMOUNT),
|
||||
secrets.0.clone(),
|
||||
ClsagContext::new(
|
||||
Decoys::new((1 ..= RING_LEN).collect(), u8::try_from(real).unwrap(), ring.clone())
|
||||
.unwrap(),
|
||||
Commitment::new(secrets.1, AMOUNT),
|
||||
)
|
||||
.unwrap(),
|
||||
)],
|
||||
Scalar::random(&mut OsRng),
|
||||
msg,
|
||||
)
|
||||
.unwrap()
|
||||
.swap_remove(0);
|
||||
|
||||
let image = generate_key_image(&secrets.0);
|
||||
clsag.verify(&ring, &image, &pseudo_out, &msg).unwrap();
|
||||
|
||||
// make sure verification fails if we throw a random `c1` at it.
|
||||
@@ -105,18 +104,14 @@ fn clsag_multisig() {
|
||||
ring.push([dest, Commitment::new(mask, amount).calculate()]);
|
||||
}
|
||||
|
||||
let mask_sum = Scalar::random(&mut OsRng);
|
||||
let algorithm = ClsagMultisig::new(
|
||||
RecommendedTranscript::new(b"Monero Serai CLSAG Test"),
|
||||
keys[&Participant::new(1).unwrap()].group_key().0,
|
||||
Arc::new(RwLock::new(Some(ClsagDetails::new(
|
||||
ClsagInput::new(
|
||||
Commitment::new(randomness, AMOUNT),
|
||||
Decoys::new((1 ..= RING_LEN).collect(), RING_INDEX, ring.clone()).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
mask_sum,
|
||||
)))),
|
||||
ClsagContext::new(
|
||||
Decoys::new((1 ..= RING_LEN).collect(), RING_INDEX, ring.clone()).unwrap(),
|
||||
Commitment::new(randomness, AMOUNT),
|
||||
)
|
||||
.unwrap(),
|
||||
Arc::new(Mutex::new(Some(Scalar::random(&mut OsRng)))),
|
||||
);
|
||||
|
||||
sign(
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::{
|
||||
},
|
||||
ringct::{
|
||||
generate_key_image,
|
||||
clsag::{ClsagError, ClsagInput, Clsag},
|
||||
clsag::{ClsagError, ClsagContext, Clsag},
|
||||
bulletproofs::{MAX_OUTPUTS, Bulletproof},
|
||||
RctBase, RctPrunable, RctSignatures,
|
||||
},
|
||||
@@ -168,28 +168,34 @@ fn prepare_inputs(
|
||||
inputs: &[(SpendableOutput, Decoys)],
|
||||
spend: &Zeroizing<Scalar>,
|
||||
tx: &mut Transaction,
|
||||
) -> Result<Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>, TransactionError> {
|
||||
) -> Result<Vec<(Zeroizing<Scalar>, ClsagContext)>, TransactionError> {
|
||||
let mut signable = Vec::with_capacity(inputs.len());
|
||||
|
||||
for (i, (input, decoys)) in inputs.iter().enumerate() {
|
||||
for (input, decoys) in inputs {
|
||||
let input_spend = Zeroizing::new(input.key_offset() + spend.deref());
|
||||
let image = generate_key_image(&input_spend);
|
||||
signable.push((
|
||||
input_spend,
|
||||
image,
|
||||
ClsagInput::new(input.commitment().clone(), decoys.clone())
|
||||
ClsagContext::new(decoys.clone(), input.commitment().clone())
|
||||
.map_err(TransactionError::ClsagError)?,
|
||||
));
|
||||
|
||||
tx.prefix.inputs.push(Input::ToKey {
|
||||
amount: None,
|
||||
key_offsets: decoys.offsets().to_vec(),
|
||||
key_image: signable[i].1,
|
||||
key_image: image,
|
||||
});
|
||||
}
|
||||
|
||||
signable.sort_by(|x, y| x.1.compress().to_bytes().cmp(&y.1.compress().to_bytes()).reverse());
|
||||
tx.prefix.inputs.sort_by(|x, y| {
|
||||
// We now need to sort the inputs by their key image
|
||||
// We take the transaction's inputs, temporarily
|
||||
let mut tx_inputs = Vec::with_capacity(inputs.len());
|
||||
std::mem::swap(&mut tx_inputs, &mut tx.prefix.inputs);
|
||||
|
||||
// Then we join them with their signable contexts
|
||||
let mut joint = tx_inputs.into_iter().zip(signable).collect::<Vec<_>>();
|
||||
// Perform the actual sort
|
||||
joint.sort_by(|(x, _), (y, _)| {
|
||||
if let (Input::ToKey { key_image: x, .. }, Input::ToKey { key_image: y, .. }) = (x, y) {
|
||||
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
|
||||
} else {
|
||||
@@ -197,6 +203,14 @@ fn prepare_inputs(
|
||||
}
|
||||
});
|
||||
|
||||
// We now re-create the consumed signable (tx.prefix.inputs already having an empty vector) and
|
||||
// split the joint iterator back into two Vecs
|
||||
let mut signable = Vec::with_capacity(inputs.len());
|
||||
for (input, signable_i) in joint {
|
||||
tx.prefix.inputs.push(input);
|
||||
signable.push(signable_i);
|
||||
}
|
||||
|
||||
Ok(signable)
|
||||
}
|
||||
|
||||
@@ -875,7 +889,8 @@ impl SignableTransaction {
|
||||
|
||||
let signable = prepare_inputs(&self.inputs, spend, &mut tx)?;
|
||||
|
||||
let clsag_pairs = Clsag::sign(rng, signable, mask_sum, tx.signature_hash());
|
||||
let clsag_pairs = Clsag::sign(rng, signable, mask_sum, tx.signature_hash())
|
||||
.map_err(|_| TransactionError::WrongPrivateKey)?;
|
||||
match tx.rct_signatures.prunable {
|
||||
RctPrunable::Null => panic!("Signing for RctPrunable::Null"),
|
||||
RctPrunable::Clsag { ref mut clsags, ref mut pseudo_outs, .. } => {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std_shims::{
|
||||
io::{self, Read},
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
@@ -27,7 +27,7 @@ use frost::{
|
||||
|
||||
use crate::{
|
||||
ringct::{
|
||||
clsag::{ClsagInput, ClsagDetails, ClsagAddendum, ClsagMultisig},
|
||||
clsag::{ClsagContext, ClsagAddendum, ClsagMultisig},
|
||||
RctPrunable,
|
||||
},
|
||||
transaction::{Input, Transaction},
|
||||
@@ -43,7 +43,7 @@ pub struct TransactionMachine {
|
||||
|
||||
// Hashed key and scalar offset
|
||||
key_images: Vec<(EdwardsPoint, Scalar)>,
|
||||
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
||||
clsag_mask_mutexes: Vec<Arc<Mutex<Option<Scalar>>>>,
|
||||
clsags: Vec<AlgorithmMachine<Ed25519, ClsagMultisig>>,
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub struct TransactionSignMachine {
|
||||
transcript: RecommendedTranscript,
|
||||
|
||||
key_images: Vec<(EdwardsPoint, Scalar)>,
|
||||
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
||||
clsag_mask_mutexes: Vec<Arc<Mutex<Option<Scalar>>>>,
|
||||
clsags: Vec<AlgorithmSignMachine<Ed25519, ClsagMultisig>>,
|
||||
|
||||
our_preprocess: Vec<Preprocess<Ed25519, ClsagAddendum>>,
|
||||
@@ -73,10 +73,10 @@ impl SignableTransaction {
|
||||
keys: &ThresholdKeys<Ed25519>,
|
||||
mut transcript: RecommendedTranscript,
|
||||
) -> Result<TransactionMachine, TransactionError> {
|
||||
let mut inputs = vec![];
|
||||
let mut clsag_mask_mutexes = vec![];
|
||||
for _ in 0 .. self.inputs.len() {
|
||||
// Doesn't resize as that will use a single Rc for the entire Vec
|
||||
inputs.push(Arc::new(RwLock::new(None)));
|
||||
clsag_mask_mutexes.push(Arc::new(Mutex::new(None)));
|
||||
}
|
||||
let mut clsags = vec![];
|
||||
|
||||
@@ -139,16 +139,18 @@ impl SignableTransaction {
|
||||
}
|
||||
|
||||
let mut key_images = vec![];
|
||||
for (i, (input, _)) in self.inputs.iter().enumerate() {
|
||||
for (i, (input, decoys)) in self.inputs.iter().enumerate() {
|
||||
// Check this the right set of keys
|
||||
let offset = keys.offset(dfg::Scalar(input.key_offset()));
|
||||
if offset.group_key().0 != input.key() {
|
||||
Err(TransactionError::WrongPrivateKey)?;
|
||||
}
|
||||
|
||||
let clsag = ClsagMultisig::new(transcript.clone(), input.key(), inputs[i].clone());
|
||||
let context = ClsagContext::new(decoys.clone(), input.commitment())
|
||||
.map_err(TransactionError::ClsagError)?;
|
||||
let clsag = ClsagMultisig::new(transcript.clone(), context, clsag_mask_mutexes[i].clone());
|
||||
key_images.push((
|
||||
clsag.H,
|
||||
clsag.key_image_generator(),
|
||||
keys.current_offset().unwrap_or(dfg::Scalar::ZERO).0 + self.inputs[i].0.key_offset(),
|
||||
));
|
||||
clsags.push(AlgorithmMachine::new(clsag, offset));
|
||||
@@ -156,12 +158,10 @@ impl SignableTransaction {
|
||||
|
||||
Ok(TransactionMachine {
|
||||
signable: self,
|
||||
|
||||
i: keys.params().i(),
|
||||
transcript,
|
||||
|
||||
key_images,
|
||||
inputs,
|
||||
clsag_mask_mutexes,
|
||||
clsags,
|
||||
})
|
||||
}
|
||||
@@ -206,7 +206,7 @@ impl PreprocessMachine for TransactionMachine {
|
||||
transcript: self.transcript,
|
||||
|
||||
key_images: self.key_images,
|
||||
inputs: self.inputs,
|
||||
clsag_mask_mutexes: self.clsag_mask_mutexes,
|
||||
clsags,
|
||||
|
||||
our_preprocess,
|
||||
@@ -296,7 +296,8 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
// provides the easiest API overall, as this is where the TX is (which needs the key
|
||||
// images in its message), along with where the outputs are determined (where our
|
||||
// outputs may need these in order to guarantee uniqueness)
|
||||
images[c] += preprocess.addendum.key_image.0 * lagrange::<dfg::Scalar>(*l, &included).0;
|
||||
images[c] +=
|
||||
preprocess.addendum.key_image_share().0 * lagrange::<dfg::Scalar>(*l, &included).0;
|
||||
|
||||
Ok((*l, preprocess))
|
||||
})
|
||||
@@ -330,12 +331,10 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
// Sort the inputs, as expected
|
||||
let mut sorted = Vec::with_capacity(self.clsags.len());
|
||||
while !self.clsags.is_empty() {
|
||||
let (inputs, decoys) = self.signable.inputs.swap_remove(0);
|
||||
sorted.push((
|
||||
images.swap_remove(0),
|
||||
inputs,
|
||||
decoys,
|
||||
self.inputs.swap_remove(0),
|
||||
self.signable.inputs.swap_remove(0).1,
|
||||
self.clsag_mask_mutexes.swap_remove(0),
|
||||
self.clsags.swap_remove(0),
|
||||
commitments.swap_remove(0),
|
||||
));
|
||||
@@ -353,22 +352,16 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
} else {
|
||||
sum_pseudo_outs += mask;
|
||||
}
|
||||
*value.2.lock().unwrap() = Some(mask);
|
||||
|
||||
tx.prefix.inputs.push(Input::ToKey {
|
||||
amount: None,
|
||||
key_offsets: value.2.offsets().to_vec(),
|
||||
key_offsets: value.1.offsets().to_vec(),
|
||||
key_image: value.0,
|
||||
});
|
||||
|
||||
*value.3.write().unwrap() = Some(ClsagDetails::new(
|
||||
ClsagInput::new(value.1.commitment().clone(), value.2).map_err(|_| {
|
||||
panic!("Signing an input which isn't present in the ring we created for it")
|
||||
})?,
|
||||
mask,
|
||||
));
|
||||
|
||||
self.clsags.push(value.4);
|
||||
commitments.push(value.5);
|
||||
self.clsags.push(value.3);
|
||||
commitments.push(value.4);
|
||||
}
|
||||
|
||||
let msg = tx.signature_hash();
|
||||
|
||||
Reference in New Issue
Block a user