Make a trait out of sign::StateMachine for more complex Transaction flows

This commit is contained in:
Luke Parker 2022-04-29 22:36:43 -04:00
parent 27396a6291
commit d0506e2e9b
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 82 additions and 58 deletions
coins/monero/tests
sign/frost

View file

@ -7,7 +7,7 @@ use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, c
#[cfg(feature = "multisig")]
mod frost;
#[cfg(feature = "multisig")]
use crate::frost::{generate_keys, sign};
use crate::frost::{THRESHOLD, generate_keys, sign};
const RING_INDEX: u8 = 3;
const RING_LEN: u64 = 11;
@ -86,17 +86,21 @@ fn test_multisig() -> Result<(), MultisigError> {
ring.push([&dest * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
}
let mut algorithms = Vec::with_capacity(t);
for _ in 1 ..= t {
algorithms.push(
clsag::InputMultisig::new(
clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(),
Msg(msg)
let mut machines = Vec::with_capacity(t);
for i in 1 ..= t {
machines.push(
sign::AlgorithmMachine::new(
clsag::InputMultisig::new(
clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(),
Msg(msg)
).unwrap(),
keys[i - 1].clone(),
&(1 ..= THRESHOLD).collect::<Vec<usize>>()
).unwrap()
);
}
let mut signatures = sign(algorithms, keys);
let mut signatures = sign(&mut machines, keys);
let signature = signatures.swap_remove(0);
for s in 0 .. (t - 1) {
// Verify the commitments and the non-decoy s scalar are identical to every other signature

View file

@ -8,7 +8,7 @@ use rand::rngs::OsRng;
use ff::Field;
use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint};
use frost::{
pub use frost::{
FrostError, MultisigParams, MultisigKeys,
key_gen, algorithm::Algorithm, sign::{self, lagrange}
};
@ -113,26 +113,16 @@ pub fn generate_keys() -> (Vec<Rc<MultisigKeys<Ed25519>>>, Scalar) {
}
#[allow(dead_code)] // Currently has some false positive
pub fn sign<S, A: Algorithm<Ed25519, Signature = S>>(
algorithms: Vec<A>,
pub fn sign<S, M: sign::StateMachine<Signature = S>>(
machines: &mut Vec<M>,
keys: Vec<Rc<MultisigKeys<Ed25519>>>
) -> Vec<S> {
assert!(algorithms.len() >= THRESHOLD);
assert!(keys.len() >= algorithms.len());
assert!(machines.len() >= THRESHOLD);
assert!(keys.len() >= machines.len());
let mut machines = vec![];
let mut commitments = Vec::with_capacity(PARTICIPANTS + 1);
commitments.resize(PARTICIPANTS + 1, None);
for i in 1 ..= THRESHOLD {
machines.push(
sign::StateMachine::new(
sign::Params::new(
algorithms[i - 1].clone(),
keys[i - 1].clone(),
&(1 ..= THRESHOLD).collect::<Vec<usize>>()
).unwrap()
)
);
commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap());
}

View file

@ -65,6 +65,7 @@ pub struct Params<C: Curve, A: Algorithm<C>> {
view: ParamsView<C>,
}
// Currently public to enable more complex operations as desired, yet solely used in testing
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
pub fn new(
algorithm: A,
@ -400,30 +401,70 @@ impl fmt::Display for State {
}
}
/// State machine which manages signing
pub trait StateMachine {
type Signature;
/// Perform the preprocessing round required in order to sign
/// Returns a byte vector which must be transmitted to all parties selected for this signing
/// process, over an authenticated channel
fn preprocess<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R
) -> Result<Vec<u8>, FrostError>;
/// Sign a message
/// Takes in the participant's commitments, which are expected to be in a Vec where participant
/// index = Vec index. None is expected at index 0 to allow for this. None is also expected at
/// index i which is locally handled. Returns a byte vector representing a share of the signature
/// for every other participant to receive, over an authenticated channel
fn sign(
&mut self,
commitments: &[Option<Vec<u8>>],
msg: &[u8],
) -> Result<Vec<u8>, FrostError>;
/// Complete signing
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
/// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized
/// signature
fn complete(&mut self, shares: &[Option<Vec<u8>>]) -> Result<Self::Signature, FrostError>;
fn multisig_params(&self) -> MultisigParams;
fn state(&self) -> State;
}
/// State machine which manages signing for an arbitrary signature algorithm
#[allow(non_snake_case)]
pub struct StateMachine<C: Curve, A: Algorithm<C>> {
pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
state: State,
preprocess: Option<PreprocessPackage<C>>,
sign: Option<Package<C>>,
}
impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
/// Creates a new machine to generate a key for the specified curve in the specified multisig
pub fn new(params: Params<C, A>) -> StateMachine<C, A> {
StateMachine {
params,
state: State::Fresh,
preprocess: None,
sign: None,
}
pub fn new(
algorithm: A,
keys: Rc<MultisigKeys<C>>,
included: &[usize],
) -> Result<AlgorithmMachine<C, A>, FrostError> {
Ok(
AlgorithmMachine {
params: Params::new(algorithm, keys, included)?,
state: State::Fresh,
preprocess: None,
sign: None,
}
)
}
}
/// Perform the preprocessing round required in order to sign
/// Returns a byte vector which must be transmitted to all parties selected for this signing
/// process, over an authenticated channel
pub fn preprocess<R: RngCore + CryptoRng>(
impl<C: Curve, A: Algorithm<C>> StateMachine for AlgorithmMachine<C, A> {
type Signature = A::Signature;
fn preprocess<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R
) -> Result<Vec<u8>, FrostError> {
@ -437,12 +478,7 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
Ok(serialized)
}
/// Sign a message
/// Takes in the participant's commitments, which are expected to be in a Vec where participant
/// index = Vec index. None is expected at index 0 to allow for this. None is also expected at
/// index i which is locally handled. Returns a byte vector representing a share of the signature
/// for every other participant to receive, over an authenticated channel
pub fn sign(
fn sign(
&mut self,
commitments: &[Option<Vec<u8>>],
msg: &[u8],
@ -463,11 +499,7 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
Ok(serialized)
}
/// Complete signing
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
/// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized
/// signature
pub fn complete(&mut self, shares: &[Option<Vec<u8>>]) -> Result<A::Signature, FrostError> {
fn complete(&mut self, shares: &[Option<Vec<u8>>]) -> Result<A::Signature, FrostError> {
if self.state != State::Signed {
Err(FrostError::InvalidSignTransition(State::Signed, self.state))?;
}
@ -482,11 +514,11 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
Ok(signature)
}
pub fn multisig_params(&self) -> MultisigParams {
fn multisig_params(&self) -> MultisigParams {
self.params.multisig_params().clone()
}
pub fn state(&self) -> State {
fn state(&self) -> State {
self.state
}
}

View file

@ -10,7 +10,7 @@ use frost::{
MultisigParams, MultisigKeys,
key_gen,
algorithm::{Algorithm, Schnorr, SchnorrSignature},
sign
sign::{StateMachine, AlgorithmMachine}
};
mod common;
@ -28,13 +28,11 @@ fn sign<C: Curve, A: Algorithm<C, Signature = SchnorrSignature<C>>>(
commitments.resize(PARTICIPANTS + 1, None);
for i in 1 ..= t {
machines.push(
sign::StateMachine::new(
sign::Params::new(
algorithm.clone(),
keys[i - 1].clone(),
&(1 ..= t).collect::<Vec<usize>>()
).unwrap()
)
AlgorithmMachine::new(
algorithm.clone(),
keys[i - 1].clone(),
&(1 ..= t).collect::<Vec<usize>>()
).unwrap()
);
commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap());
}