mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-22 19:49:22 +00:00
Implement a CLSAG algorithm extension which also does key images
Practically, this should be mergeable. There's little reason to do a CLSAG and not also a key image. Keeps them isolated for now.
This commit is contained in:
parent
45559e14ee
commit
27396a6291
10 changed files with 213 additions and 103 deletions
|
@ -24,7 +24,7 @@ use crate::{
|
|||
#[cfg(feature = "multisig")]
|
||||
mod multisig;
|
||||
#[cfg(feature = "multisig")]
|
||||
pub use multisig::Multisig;
|
||||
pub use multisig::{Msg, Multisig, InputMultisig};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -38,16 +38,14 @@ pub enum Error {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Input {
|
||||
pub image: EdwardsPoint,
|
||||
// Ring, the index we're signing for, and the actual commitment behind it
|
||||
pub ring: Vec<[EdwardsPoint; 2]>,
|
||||
pub i: usize,
|
||||
pub commitment: Commitment
|
||||
pub commitment: Commitment,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new(
|
||||
image: EdwardsPoint,
|
||||
ring: Vec<[EdwardsPoint; 2]>,
|
||||
i: u8,
|
||||
commitment: Commitment
|
||||
|
@ -66,16 +64,13 @@ impl Input {
|
|||
Err(Error::InvalidCommitment)?;
|
||||
}
|
||||
|
||||
Ok(Input { image, ring, i, commitment })
|
||||
Ok(Input { ring, i, commitment })
|
||||
}
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
pub fn context(&self) -> Vec<u8> {
|
||||
// image is extraneous in practice as the image should be in the msg AND the addendum when TX
|
||||
// signing. This just ensures CLSAG guarantees its integrity, even when others won't
|
||||
let mut context = self.image.compress().to_bytes().to_vec();
|
||||
// Ring index
|
||||
context.extend(&u8::try_from(self.i).unwrap().to_le_bytes());
|
||||
let mut context = u8::try_from(self.i).unwrap().to_le_bytes().to_vec();
|
||||
// Ring
|
||||
for pair in &self.ring {
|
||||
// Doesn't include key offsets as CLSAG doesn't care and won't be affected by it
|
||||
|
@ -92,6 +87,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
|||
rng: &mut R,
|
||||
msg: &[u8; 32],
|
||||
input: &Input,
|
||||
image: &EdwardsPoint,
|
||||
mask: Scalar,
|
||||
A: EdwardsPoint,
|
||||
AH: EdwardsPoint
|
||||
|
@ -126,7 +122,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
|||
let mut D = H * z;
|
||||
|
||||
// Doesn't use a constant time table as dalek takes longer to generate those then they save
|
||||
let images_precomp = VartimeEdwardsPrecomputation::new(&[input.image, D]);
|
||||
let images_precomp = VartimeEdwardsPrecomputation::new([image, &D]);
|
||||
D = Scalar::from(8 as u8).invert() * D;
|
||||
|
||||
let mut to_hash = vec![];
|
||||
|
@ -145,7 +141,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
|||
to_hash.extend(C_non_zero[i].compress().to_bytes());
|
||||
}
|
||||
|
||||
to_hash.extend(input.image.compress().to_bytes());
|
||||
to_hash.extend(image.compress().to_bytes());
|
||||
let D_bytes = D.compress().to_bytes();
|
||||
to_hash.extend(D_bytes);
|
||||
to_hash.extend(C_out.compress().to_bytes());
|
||||
|
@ -208,7 +204,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
|||
pub fn sign<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
msg: [u8; 32],
|
||||
inputs: &[(Scalar, Input)],
|
||||
inputs: &[(Scalar, Input, EdwardsPoint)],
|
||||
sum_outputs: Scalar
|
||||
) -> Option<Vec<(Clsag, EdwardsPoint)>> {
|
||||
if inputs.len() == 0 {
|
||||
|
@ -235,6 +231,7 @@ pub fn sign<R: RngCore + CryptoRng>(
|
|||
rng,
|
||||
&msg,
|
||||
&inputs[i].1,
|
||||
&inputs[i].2,
|
||||
mask,
|
||||
&nonce * &ED25519_BASEPOINT_TABLE, nonce * hash_to_point(&inputs[i].1.ring[inputs[i].1.i][0])
|
||||
);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use core::fmt::Debug;
|
||||
|
||||
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
||||
use rand_chacha::ChaCha12Rng;
|
||||
|
||||
|
@ -5,6 +7,7 @@ use blake2::{Digest, Blake2b512};
|
|||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE,
|
||||
traits::Identity,
|
||||
scalar::Scalar,
|
||||
edwards::EdwardsPoint
|
||||
};
|
||||
|
@ -19,9 +22,14 @@ use crate::{
|
|||
random_scalar,
|
||||
hash_to_point,
|
||||
frost::{MultisigError, Ed25519, DLEqProof},
|
||||
key_image,
|
||||
clsag::{Input, sign_core, verify}
|
||||
};
|
||||
|
||||
pub trait Msg: Clone + Debug {
|
||||
fn msg(&self, image: EdwardsPoint) -> [u8; 32];
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct ClsagSignInterim {
|
||||
|
@ -34,44 +42,43 @@ struct ClsagSignInterim {
|
|||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Multisig {
|
||||
pub struct Multisig<M: Msg> {
|
||||
b: Vec<u8>,
|
||||
AH0: dfg::EdwardsPoint,
|
||||
AH1: dfg::EdwardsPoint,
|
||||
AH: (dfg::EdwardsPoint, dfg::EdwardsPoint),
|
||||
|
||||
input: Input,
|
||||
|
||||
msg: Option<[u8; 32]>,
|
||||
image: Option<EdwardsPoint>,
|
||||
msg: M,
|
||||
|
||||
interim: Option<ClsagSignInterim>
|
||||
}
|
||||
|
||||
impl Multisig {
|
||||
impl<M: Msg> Multisig<M> {
|
||||
pub fn new(
|
||||
input: Input
|
||||
) -> Result<Multisig, MultisigError> {
|
||||
input: Input,
|
||||
msg: M
|
||||
) -> Result<Multisig<M>, MultisigError> {
|
||||
Ok(
|
||||
Multisig {
|
||||
b: vec![],
|
||||
AH0: dfg::EdwardsPoint::identity(),
|
||||
AH1: dfg::EdwardsPoint::identity(),
|
||||
AH: (dfg::EdwardsPoint::identity(), dfg::EdwardsPoint::identity()),
|
||||
|
||||
input,
|
||||
|
||||
msg: None,
|
||||
image: None,
|
||||
msg,
|
||||
interim: None
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_msg(
|
||||
&mut self,
|
||||
msg: [u8; 32]
|
||||
) {
|
||||
self.msg = Some(msg);
|
||||
pub fn set_image(&mut self, image: EdwardsPoint) {
|
||||
self.image = Some(image);
|
||||
}
|
||||
}
|
||||
|
||||
impl Algorithm<Ed25519> for Multisig {
|
||||
impl<M: Msg> Algorithm<Ed25519> for Multisig<M> {
|
||||
type Signature = (Clsag, EdwardsPoint);
|
||||
|
||||
// We arguably don't have to commit to at all thanks to xG and yG being committed to, both of
|
||||
|
@ -113,7 +120,6 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
|
||||
let h0 = <Ed25519 as Curve>::G_from_slice(&serialized[0 .. 32]).map_err(|_| FrostError::InvalidCommitment(l))?;
|
||||
DLEqProof::deserialize(&serialized[64 .. 128]).ok_or(FrostError::InvalidCommitment(l))?.verify(
|
||||
l,
|
||||
&alt,
|
||||
&commitments[0],
|
||||
&h0
|
||||
|
@ -121,7 +127,6 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
|
||||
let h1 = <Ed25519 as Curve>::G_from_slice(&serialized[32 .. 64]).map_err(|_| FrostError::InvalidCommitment(l))?;
|
||||
DLEqProof::deserialize(&serialized[128 .. 192]).ok_or(FrostError::InvalidCommitment(l))?.verify(
|
||||
l,
|
||||
&alt,
|
||||
&commitments[1],
|
||||
&h1
|
||||
|
@ -129,33 +134,34 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
|
||||
self.b.extend(&l.to_le_bytes());
|
||||
self.b.extend(&serialized[0 .. 64]);
|
||||
self.AH0 += h0;
|
||||
self.AH1 += h1;
|
||||
self.AH.0 += h0;
|
||||
self.AH.1 += h1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn context(&self) -> Vec<u8> {
|
||||
let mut context = vec![];
|
||||
context.extend(&self.msg.unwrap());
|
||||
// This should be redundant as the image should be in the addendum if using InputMultisig and
|
||||
// in msg if signing a Transaction, yet this ensures CLSAG takes responsibility for its own
|
||||
// security boundaries
|
||||
context.extend(&self.image.unwrap().compress().to_bytes());
|
||||
context.extend(&self.msg.msg(self.image.unwrap()));
|
||||
context.extend(&self.input.context());
|
||||
context
|
||||
}
|
||||
|
||||
fn process_binding(
|
||||
&mut self,
|
||||
p: &dfg::Scalar,
|
||||
) {
|
||||
self.AH0 += self.AH1 * p;
|
||||
}
|
||||
|
||||
fn sign_share(
|
||||
&mut self,
|
||||
view: &ParamsView<Ed25519>,
|
||||
nonce_sum: dfg::EdwardsPoint,
|
||||
b: dfg::Scalar,
|
||||
nonce: dfg::Scalar,
|
||||
_: &[u8]
|
||||
) -> dfg::Scalar {
|
||||
// Apply the binding factor to the H variant of the nonce
|
||||
self.AH.0 += self.AH.1 * b;
|
||||
|
||||
// Use everyone's commitments to derive a random source all signers can agree upon
|
||||
// Cannot be manipulated to effect and all signers must, and will, know this
|
||||
// Uses the context as well to prevent passive observers of messages from being able to break
|
||||
|
@ -170,11 +176,12 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
#[allow(non_snake_case)]
|
||||
let (clsag, c, mu_C, z, mu_P, C_out) = sign_core(
|
||||
&mut rng,
|
||||
&self.msg.unwrap(),
|
||||
&self.msg.msg(self.image.unwrap()),
|
||||
&self.input,
|
||||
&self.image.unwrap(),
|
||||
mask,
|
||||
nonce_sum.0,
|
||||
self.AH0.0
|
||||
self.AH.0.0
|
||||
);
|
||||
self.interim = Some(ClsagSignInterim { c: c * mu_P, s: c * mu_C * z, clsag, C_out });
|
||||
|
||||
|
@ -193,7 +200,7 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
|
||||
let mut clsag = interim.clsag.clone();
|
||||
clsag.s[self.input.i] = Key { key: (sum.0 - interim.s).to_bytes() };
|
||||
if verify(&clsag, &self.msg.unwrap(), self.input.image, &self.input.ring, interim.C_out) {
|
||||
if verify(&clsag, &self.msg.msg(self.image.unwrap()), self.image.unwrap(), &self.input.ring, interim.C_out) {
|
||||
return Some((clsag, interim.C_out));
|
||||
}
|
||||
return None;
|
||||
|
@ -211,3 +218,87 @@ impl Algorithm<Ed25519> for Multisig {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InputMultisig<M: Msg>(EdwardsPoint, Multisig<M>);
|
||||
|
||||
impl<M: Msg> InputMultisig<M> {
|
||||
pub fn new(
|
||||
input: Input,
|
||||
msg: M
|
||||
) -> Result<InputMultisig<M>, MultisigError> {
|
||||
Ok(InputMultisig(EdwardsPoint::identity(), Multisig::new(input, msg)?))
|
||||
}
|
||||
|
||||
pub fn image(&self) -> EdwardsPoint {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Msg> Algorithm<Ed25519> for InputMultisig<M> {
|
||||
type Signature = (Clsag, EdwardsPoint);
|
||||
|
||||
fn addendum_commit_len() -> usize {
|
||||
32 + Multisig::<M>::addendum_commit_len()
|
||||
}
|
||||
|
||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
view: &ParamsView<Ed25519>,
|
||||
nonces: &[dfg::Scalar; 2]
|
||||
) -> Vec<u8> {
|
||||
let (mut serialized, end) = key_image::generate_share(rng, view);
|
||||
serialized.extend(Multisig::<M>::preprocess_addendum(rng, view, nonces));
|
||||
serialized.extend(end);
|
||||
serialized
|
||||
}
|
||||
|
||||
fn process_addendum(
|
||||
&mut self,
|
||||
view: &ParamsView<Ed25519>,
|
||||
l: usize,
|
||||
commitments: &[dfg::EdwardsPoint; 2],
|
||||
serialized: &[u8]
|
||||
) -> Result<(), FrostError> {
|
||||
let (image, serialized) = key_image::verify_share(view, l, serialized).map_err(|_| FrostError::InvalidShare(l))?;
|
||||
self.0 += image;
|
||||
if l == *view.included().last().unwrap() {
|
||||
self.1.set_image(self.0);
|
||||
}
|
||||
self.1.process_addendum(view, l, commitments, &serialized)
|
||||
}
|
||||
|
||||
fn context(&self) -> Vec<u8> {
|
||||
self.1.context()
|
||||
}
|
||||
|
||||
fn sign_share(
|
||||
&mut self,
|
||||
view: &ParamsView<Ed25519>,
|
||||
nonce_sum: dfg::EdwardsPoint,
|
||||
b: dfg::Scalar,
|
||||
nonce: dfg::Scalar,
|
||||
msg: &[u8]
|
||||
) -> dfg::Scalar {
|
||||
self.1.sign_share(view, nonce_sum, b, nonce, msg)
|
||||
}
|
||||
|
||||
fn verify(
|
||||
&self,
|
||||
group_key: dfg::EdwardsPoint,
|
||||
nonce: dfg::EdwardsPoint,
|
||||
sum: dfg::Scalar
|
||||
) -> Option<Self::Signature> {
|
||||
self.1.verify(group_key, nonce, sum)
|
||||
}
|
||||
|
||||
fn verify_share(
|
||||
&self,
|
||||
verification_share: dfg::EdwardsPoint,
|
||||
nonce: dfg::EdwardsPoint,
|
||||
share: dfg::Scalar,
|
||||
) -> bool {
|
||||
self.1.verify_share(verification_share, nonce, share)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ use crate::random_scalar;
|
|||
pub enum MultisigError {
|
||||
#[error("internal error ({0})")]
|
||||
InternalError(String),
|
||||
#[error("invalid discrete log equality proof {0}")]
|
||||
InvalidDLEqProof(usize),
|
||||
#[error("invalid discrete log equality proof")]
|
||||
InvalidDLEqProof,
|
||||
#[error("invalid key image {0}")]
|
||||
InvalidKeyImage(usize)
|
||||
}
|
||||
|
@ -145,7 +145,6 @@ impl DLEqProof {
|
|||
|
||||
pub fn verify(
|
||||
&self,
|
||||
l: usize,
|
||||
H: &DPoint,
|
||||
primary: &DPoint,
|
||||
alt: &DPoint
|
||||
|
@ -166,7 +165,7 @@ impl DLEqProof {
|
|||
|
||||
// Take the opportunity to ensure a lack of torsion in key images/randomness commitments
|
||||
if (!primary.is_torsion_free()) || (!alt.is_torsion_free()) || (c != expected_c) {
|
||||
Err(MultisigError::InvalidDLEqProof(l))?;
|
||||
Err(MultisigError::InvalidDLEqProof)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -32,16 +32,15 @@ pub fn verify_share(
|
|||
share: &[u8]
|
||||
) -> Result<(EdwardsPoint, Vec<u8>), MultisigError> {
|
||||
if share.len() < 96 {
|
||||
Err(MultisigError::InvalidDLEqProof(l))?;
|
||||
Err(MultisigError::InvalidDLEqProof)?;
|
||||
}
|
||||
let image = CompressedEdwardsY(
|
||||
share[0 .. 32].try_into().unwrap()
|
||||
).decompress().ok_or(MultisigError::InvalidKeyImage(l))?;
|
||||
let proof = DLEqProof::deserialize(
|
||||
&share[(share.len() - 64) .. share.len()]
|
||||
).ok_or(MultisigError::InvalidDLEqProof(l))?;
|
||||
).ok_or(MultisigError::InvalidDLEqProof)?;
|
||||
proof.verify(
|
||||
l,
|
||||
&hash_to_point(&view.group_key().0),
|
||||
&view.verification_share(l),
|
||||
&image
|
||||
|
|
|
@ -327,7 +327,7 @@ async fn prepare_inputs(
|
|||
spend: &Scalar,
|
||||
inputs: &[SpendableOutput],
|
||||
tx: &mut Transaction
|
||||
) -> Result<Vec<(Scalar, clsag::Input)>, TransactionError> {
|
||||
) -> Result<Vec<(Scalar, clsag::Input, EdwardsPoint)>, TransactionError> {
|
||||
let mut mixins = Vec::with_capacity(inputs.len());
|
||||
let mut signable = Vec::with_capacity(inputs.len());
|
||||
for (i, input) in inputs.iter().enumerate() {
|
||||
|
@ -340,51 +340,71 @@ async fn prepare_inputs(
|
|||
signable.push((
|
||||
spend + input.key_offset,
|
||||
clsag::Input::new(
|
||||
key_image::generate(&(spend + input.key_offset)),
|
||||
rpc.get_ring(&mixins[i]).await.map_err(|e| TransactionError::RpcError(e))?,
|
||||
m,
|
||||
input.commitment
|
||||
).map_err(|e| TransactionError::ClsagError(e))?
|
||||
).map_err(|e| TransactionError::ClsagError(e))?,
|
||||
key_image::generate(&(spend + input.key_offset))
|
||||
));
|
||||
|
||||
tx.prefix.inputs.push(TxIn::ToKey {
|
||||
amount: VarInt(0),
|
||||
key_offsets: mixins::offset(&mixins[i]).iter().map(|x| VarInt(*x)).collect(),
|
||||
k_image: KeyImage { image: Hash(signable[i].1.image.compress().to_bytes()) }
|
||||
k_image: KeyImage { image: Hash(signable[i].2.compress().to_bytes()) }
|
||||
});
|
||||
}
|
||||
|
||||
Ok(signable)
|
||||
}
|
||||
|
||||
pub async fn send<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
spend: &Scalar,
|
||||
inputs: &[SpendableOutput],
|
||||
payments: &[(Address, u64)],
|
||||
pub struct SignableTransaction {
|
||||
inputs: Vec<SpendableOutput>,
|
||||
payments: Vec<(Address, u64)>,
|
||||
change: Address,
|
||||
fee_per_byte: u64
|
||||
) -> Result<Transaction, TransactionError> {
|
||||
let (_, mask_sum, mut tx) = prepare_outputs(
|
||||
&mut Preparation::Leader(rng),
|
||||
inputs,
|
||||
payments,
|
||||
change,
|
||||
fee_per_byte
|
||||
)?;
|
||||
|
||||
let signable = prepare_inputs(rpc, spend, inputs, &mut tx).await?;
|
||||
|
||||
let clsags = clsag::sign(
|
||||
rng,
|
||||
tx.signature_hash().expect("Couldn't get the signature hash").0,
|
||||
&signable,
|
||||
mask_sum
|
||||
).ok_or(TransactionError::NoInputs)?;
|
||||
let mut prunable = tx.rct_signatures.p.unwrap();
|
||||
prunable.Clsags = clsags.iter().map(|clsag| clsag.0.clone()).collect();
|
||||
prunable.pseudo_outs = clsags.iter().map(|clsag| Key { key: clsag.1.compress().to_bytes() }).collect();
|
||||
tx.rct_signatures.p = Some(prunable);
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
impl SignableTransaction {
|
||||
pub fn new(
|
||||
inputs: Vec<SpendableOutput>,
|
||||
payments: Vec<(Address, u64)>,
|
||||
change: Address,
|
||||
fee_per_byte: u64
|
||||
) -> SignableTransaction {
|
||||
SignableTransaction {
|
||||
inputs,
|
||||
payments,
|
||||
change,
|
||||
fee_per_byte
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sign<R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
spend: &Scalar
|
||||
) -> Result<Transaction, TransactionError> {
|
||||
let (_, mask_sum, mut tx) = prepare_outputs(
|
||||
&mut Preparation::Leader(rng),
|
||||
&self.inputs,
|
||||
&self.payments,
|
||||
self.change,
|
||||
self.fee_per_byte
|
||||
)?;
|
||||
|
||||
let signable = prepare_inputs(rpc, spend, &self.inputs, &mut tx).await?;
|
||||
|
||||
let clsags = clsag::sign(
|
||||
rng,
|
||||
tx.signature_hash().expect("Couldn't get the signature hash").0,
|
||||
&signable,
|
||||
mask_sum
|
||||
).ok_or(TransactionError::NoInputs)?;
|
||||
let mut prunable = tx.rct_signatures.p.unwrap();
|
||||
prunable.Clsags = clsags.iter().map(|clsag| clsag.0.clone()).collect();
|
||||
prunable.pseudo_outs = clsags.iter().map(|clsag| Key { key: clsag.1.compress().to_bytes() }).collect();
|
||||
tx.rct_signatures.p = Some(prunable);
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, clsag};
|
||||
|
||||
|
@ -39,17 +39,27 @@ fn test_single() {
|
|||
&vec![(
|
||||
secrets[0],
|
||||
clsag::Input::new(
|
||||
image,
|
||||
ring.clone(),
|
||||
RING_INDEX,
|
||||
Commitment::new(secrets[1], AMOUNT)
|
||||
).unwrap()
|
||||
).unwrap(),
|
||||
image
|
||||
)],
|
||||
Scalar::zero()
|
||||
).unwrap().swap_remove(0);
|
||||
assert!(clsag::verify(&clsag, &msg, image, &ring, pseudo_out));
|
||||
}
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
#[derive(Clone, Debug)]
|
||||
struct Msg([u8; 32]);
|
||||
#[cfg(feature = "multisig")]
|
||||
impl clsag::Msg for Msg {
|
||||
fn msg(&self, _: EdwardsPoint) -> [u8; 32] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
#[test]
|
||||
fn test_multisig() -> Result<(), MultisigError> {
|
||||
|
@ -58,8 +68,6 @@ fn test_multisig() -> Result<(), MultisigError> {
|
|||
|
||||
let msg = [1; 32];
|
||||
|
||||
let image = key_image::generate(&group_private.0);
|
||||
|
||||
let randomness = random_scalar(&mut OsRng);
|
||||
let mut ring = vec![];
|
||||
for i in 0 .. RING_LEN {
|
||||
|
@ -79,13 +87,13 @@ fn test_multisig() -> Result<(), MultisigError> {
|
|||
}
|
||||
|
||||
let mut algorithms = Vec::with_capacity(t);
|
||||
for i in 1 ..= t {
|
||||
for _ in 1 ..= t {
|
||||
algorithms.push(
|
||||
clsag::Multisig::new(
|
||||
clsag::Input::new(image, ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap()
|
||||
clsag::InputMultisig::new(
|
||||
clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(),
|
||||
Msg(msg)
|
||||
).unwrap()
|
||||
);
|
||||
algorithms[i - 1].set_msg(msg);
|
||||
}
|
||||
|
||||
let mut signatures = sign(algorithms, keys);
|
||||
|
|
|
@ -41,13 +41,12 @@ impl Algorithm<Ed25519> for DummyAlgorithm {
|
|||
|
||||
fn context(&self) -> Vec<u8> { unimplemented!() }
|
||||
|
||||
fn process_binding(&mut self, _: &Scalar) { unimplemented!() }
|
||||
|
||||
fn sign_share(
|
||||
&mut self,
|
||||
_: &sign::ParamsView<Ed25519>,
|
||||
_: EdwardsPoint,
|
||||
_: Scalar,
|
||||
_: Scalar,
|
||||
_: &[u8],
|
||||
) -> Scalar { unimplemented!() }
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use monero::{
|
|||
|
||||
use monero_serai::{
|
||||
random_scalar,
|
||||
transaction,
|
||||
transaction::{self, SignableTransaction},
|
||||
rpc::Rpc
|
||||
};
|
||||
|
||||
|
@ -48,9 +48,9 @@ pub async fn send() {
|
|||
output = transaction::scan(&tx, view, spend_pub).swap_remove(0);
|
||||
// Test creating a zero change output and a non-zero change output
|
||||
amount = output.commitment.amount - fee - u64::try_from(i).unwrap();
|
||||
let tx = transaction::send(
|
||||
&mut OsRng, &rpc, &spend, &vec![output], &vec![(addr, amount)], addr, fee_per_byte
|
||||
).await.unwrap();
|
||||
let tx = SignableTransaction::new(
|
||||
vec![output], vec![(addr, amount)], addr, fee_per_byte
|
||||
).sign(&mut OsRng, &rpc, &spend).await.unwrap();
|
||||
rpc.publish_transaction(&tx).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,9 +33,6 @@ pub trait Algorithm<C: Curve>: Clone {
|
|||
/// Context for this algorithm to be hashed into b, and therefore committed to
|
||||
fn context(&self) -> Vec<u8>;
|
||||
|
||||
/// Process the binding factor generated from all the committed to data
|
||||
fn process_binding(&mut self, p: &C::F);
|
||||
|
||||
/// Sign a share with the given secret/nonce
|
||||
/// The secret will already have been its lagrange coefficient applied so it is the necessary
|
||||
/// key share
|
||||
|
@ -44,6 +41,7 @@ pub trait Algorithm<C: Curve>: Clone {
|
|||
&mut self,
|
||||
params: &sign::ParamsView<C>,
|
||||
nonce_sum: C::G,
|
||||
b: C::F,
|
||||
nonce: C::F,
|
||||
msg: &[u8],
|
||||
) -> C::F;
|
||||
|
@ -120,12 +118,11 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
|
|||
vec![]
|
||||
}
|
||||
|
||||
fn process_binding(&mut self, _: &C::F) {}
|
||||
|
||||
fn sign_share(
|
||||
&mut self,
|
||||
params: &sign::ParamsView<C>,
|
||||
nonce_sum: C::G,
|
||||
_: C::F,
|
||||
nonce: C::F,
|
||||
msg: &[u8],
|
||||
) -> C::F {
|
||||
|
|
|
@ -287,7 +287,6 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
|
|||
}
|
||||
|
||||
let b = C::hash_to_F(&b);
|
||||
params.algorithm.process_binding(&b);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let mut Ris = vec![];
|
||||
|
@ -305,6 +304,7 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
|
|||
let share = params.algorithm.sign_share(
|
||||
view,
|
||||
R,
|
||||
b,
|
||||
our_preprocess.nonces[0] + (our_preprocess.nonces[1] * b),
|
||||
msg
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue