mirror of
https://github.com/serai-dex/serai.git
synced 2025-04-23 14:38:14 +00:00
Make a trait out of sign::StateMachine for more complex Transaction flows
This commit is contained in:
parent
27396a6291
commit
d0506e2e9b
4 changed files with 82 additions and 58 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue