diff --git a/coins/monero/src/clsag/mod.rs b/coins/monero/src/clsag/mod.rs index 5039de23..99ced700 100644 --- a/coins/monero/src/clsag/mod.rs +++ b/coins/monero/src/clsag/mod.rs @@ -24,7 +24,7 @@ use crate::{ #[cfg(feature = "multisig")] mod multisig; #[cfg(feature = "multisig")] -pub use multisig::{Msg, Multisig, InputMultisig}; +pub use multisig::{TransactionData, Multisig, InputMultisig}; #[derive(Error, Debug)] pub enum Error { diff --git a/coins/monero/src/clsag/multisig.rs b/coins/monero/src/clsag/multisig.rs index a6f805d5..f1df8a51 100644 --- a/coins/monero/src/clsag/multisig.rs +++ b/coins/monero/src/clsag/multisig.rs @@ -19,15 +19,15 @@ use frost::{Curve, FrostError, algorithm::Algorithm, sign::ParamsView}; use monero::util::ringct::{Key, Clsag}; 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]; +pub trait TransactionData: Clone + Debug { + fn msg(&self) -> [u8; 32]; + fn mask_sum(&self) -> Scalar; } #[allow(non_snake_case)] @@ -42,23 +42,23 @@ struct ClsagSignInterim { #[allow(non_snake_case)] #[derive(Clone, Debug)] -pub struct Multisig { +pub struct Multisig { b: Vec, AH: (dfg::EdwardsPoint, dfg::EdwardsPoint), input: Input, image: Option, - msg: M, + data: D, interim: Option } -impl Multisig { +impl Multisig { pub fn new( input: Input, - msg: M - ) -> Result, MultisigError> { + data: D + ) -> Result, MultisigError> { Ok( Multisig { b: vec![], @@ -67,7 +67,7 @@ impl Multisig { input, image: None, - msg, + data, interim: None } ) @@ -78,7 +78,7 @@ impl Multisig { } } -impl Algorithm for Multisig { +impl Algorithm for Multisig { type Signature = (Clsag, EdwardsPoint); // We arguably don't have to commit to at all thanks to xG and yG being committed to, both of @@ -146,7 +146,7 @@ impl Algorithm for Multisig { // 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.data.msg()); context.extend(&self.input.context()); context } @@ -171,15 +171,14 @@ impl Algorithm for Multisig { seed.extend(&self.context()); seed.extend(&self.b); let mut rng = ChaCha12Rng::from_seed(Blake2b512::digest(seed)[0 .. 32].try_into().unwrap()); - let mask = random_scalar(&mut rng); #[allow(non_snake_case)] let (clsag, c, mu_C, z, mu_P, C_out) = sign_core( &mut rng, - &self.msg.msg(self.image.unwrap()), + &self.data.msg(), &self.input, &self.image.unwrap(), - mask, + self.data.mask_sum(), nonce_sum.0, self.AH.0.0 ); @@ -200,7 +199,7 @@ impl Algorithm 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.msg(self.image.unwrap()), self.image.unwrap(), &self.input.ring, interim.C_out) { + if verify(&clsag, &self.data.msg(), self.image.unwrap(), &self.input.ring, interim.C_out) { return Some((clsag, interim.C_out)); } return None; @@ -221,13 +220,13 @@ impl Algorithm for Multisig { #[allow(non_snake_case)] #[derive(Clone, Debug)] -pub struct InputMultisig(EdwardsPoint, Multisig); +pub struct InputMultisig(EdwardsPoint, Multisig); -impl InputMultisig { +impl InputMultisig { pub fn new( input: Input, - msg: M - ) -> Result, MultisigError> { + msg: D + ) -> Result, MultisigError> { Ok(InputMultisig(EdwardsPoint::identity(), Multisig::new(input, msg)?)) } @@ -236,11 +235,11 @@ impl InputMultisig { } } -impl Algorithm for InputMultisig { +impl Algorithm for InputMultisig { type Signature = (Clsag, EdwardsPoint); fn addendum_commit_len() -> usize { - 32 + Multisig::::addendum_commit_len() + 32 + Multisig::::addendum_commit_len() } fn preprocess_addendum( @@ -249,7 +248,7 @@ impl Algorithm for InputMultisig { nonces: &[dfg::Scalar; 2] ) -> Vec { let (mut serialized, end) = key_image::generate_share(rng, view); - serialized.extend(Multisig::::preprocess_addendum(rng, view, nonces)); + serialized.extend(Multisig::::preprocess_addendum(rng, view, nonces)); serialized.extend(end); serialized } diff --git a/coins/monero/src/transaction/mod.rs b/coins/monero/src/transaction/mod.rs index 3c39498d..069833f8 100644 --- a/coins/monero/src/transaction/mod.rs +++ b/coins/monero/src/transaction/mod.rs @@ -47,6 +47,8 @@ pub enum TransactionError { InvalidPreparation(String), #[error("no inputs")] NoInputs, + #[error("no outputs")] + NoOutputs, #[error("too many outputs")] TooManyOutputs, #[error("not enough funds (in {0}, out {1})")] @@ -370,13 +372,22 @@ impl SignableTransaction { payments: Vec<(Address, u64)>, change: Address, fee_per_byte: u64 - ) -> SignableTransaction { - SignableTransaction { - inputs, - payments, - change, - fee_per_byte + ) -> Result { + if inputs.len() == 0 { + Err(TransactionError::NoInputs)?; } + if payments.len() == 0 { + Err(TransactionError::NoOutputs)?; + } + + Ok( + SignableTransaction { + inputs, + payments, + change, + fee_per_byte + } + ) } pub async fn sign( @@ -400,7 +411,7 @@ impl SignableTransaction { tx.signature_hash().expect("Couldn't get the signature hash").0, &signable, mask_sum - ).ok_or(TransactionError::NoInputs)?; + ).unwrap(); // None if no inputs which new checks for 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(); diff --git a/coins/monero/tests/clsag.rs b/coins/monero/tests/clsag.rs index eb6bcf9b..2d99b7e4 100644 --- a/coins/monero/tests/clsag.rs +++ b/coins/monero/tests/clsag.rs @@ -1,6 +1,6 @@ use rand::{RngCore, rngs::OsRng}; -use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint}; +use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar}; use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, clsag}; @@ -52,11 +52,15 @@ fn test_single() { #[cfg(feature = "multisig")] #[derive(Clone, Debug)] -struct Msg([u8; 32]); +struct TransactionData; #[cfg(feature = "multisig")] -impl clsag::Msg for Msg { - fn msg(&self, _: EdwardsPoint) -> [u8; 32] { - self.0 +impl clsag::TransactionData for TransactionData { + fn msg(&self) -> [u8; 32] { + [1; 32] + } + + fn mask_sum(&self) -> Scalar { + Scalar::from(21u64) } } @@ -66,8 +70,6 @@ fn test_multisig() -> Result<(), MultisigError> { let (keys, group_private) = generate_keys(); let t = keys[0].params().t(); - let msg = [1; 32]; - let randomness = random_scalar(&mut OsRng); let mut ring = vec![]; for i in 0 .. RING_LEN { @@ -92,7 +94,7 @@ fn test_multisig() -> Result<(), MultisigError> { sign::AlgorithmMachine::new( clsag::InputMultisig::new( clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(), - Msg(msg) + TransactionData ).unwrap(), keys[i - 1].clone(), &(1 ..= THRESHOLD).collect::>() diff --git a/coins/monero/tests/send.rs b/coins/monero/tests/send.rs index 01c43075..cfa1e56f 100644 --- a/coins/monero/tests/send.rs +++ b/coins/monero/tests/send.rs @@ -50,7 +50,7 @@ pub async fn send() { amount = output.commitment.amount - fee - u64::try_from(i).unwrap(); let tx = SignableTransaction::new( vec![output], vec![(addr, amount)], addr, fee_per_byte - ).sign(&mut OsRng, &rpc, &spend).await.unwrap(); + ).unwrap().sign(&mut OsRng, &rpc, &spend).await.unwrap(); rpc.publish_transaction(&tx).await.unwrap(); } }