mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 20:59:23 +00:00
Make a trait out of sign::StateMachine for more complex Transaction flows
This commit is contained in:
@@ -7,7 +7,7 @@ use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, c
|
|||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
mod frost;
|
mod frost;
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
use crate::frost::{generate_keys, sign};
|
use crate::frost::{THRESHOLD, generate_keys, sign};
|
||||||
|
|
||||||
const RING_INDEX: u8 = 3;
|
const RING_INDEX: u8 = 3;
|
||||||
const RING_LEN: u64 = 11;
|
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()]);
|
ring.push([&dest * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut algorithms = Vec::with_capacity(t);
|
let mut machines = Vec::with_capacity(t);
|
||||||
for _ in 1 ..= t {
|
for i in 1 ..= t {
|
||||||
algorithms.push(
|
machines.push(
|
||||||
clsag::InputMultisig::new(
|
sign::AlgorithmMachine::new(
|
||||||
clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(),
|
clsag::InputMultisig::new(
|
||||||
Msg(msg)
|
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()
|
).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut signatures = sign(algorithms, keys);
|
let mut signatures = sign(&mut machines, keys);
|
||||||
let signature = signatures.swap_remove(0);
|
let signature = signatures.swap_remove(0);
|
||||||
for s in 0 .. (t - 1) {
|
for s in 0 .. (t - 1) {
|
||||||
// Verify the commitments and the non-decoy s scalar are identical to every other signature
|
// 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 ff::Field;
|
||||||
use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint};
|
use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint};
|
||||||
|
|
||||||
use frost::{
|
pub use frost::{
|
||||||
FrostError, MultisigParams, MultisigKeys,
|
FrostError, MultisigParams, MultisigKeys,
|
||||||
key_gen, algorithm::Algorithm, sign::{self, lagrange}
|
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
|
#[allow(dead_code)] // Currently has some false positive
|
||||||
pub fn sign<S, A: Algorithm<Ed25519, Signature = S>>(
|
pub fn sign<S, M: sign::StateMachine<Signature = S>>(
|
||||||
algorithms: Vec<A>,
|
machines: &mut Vec<M>,
|
||||||
keys: Vec<Rc<MultisigKeys<Ed25519>>>
|
keys: Vec<Rc<MultisigKeys<Ed25519>>>
|
||||||
) -> Vec<S> {
|
) -> Vec<S> {
|
||||||
assert!(algorithms.len() >= THRESHOLD);
|
assert!(machines.len() >= THRESHOLD);
|
||||||
assert!(keys.len() >= algorithms.len());
|
assert!(keys.len() >= machines.len());
|
||||||
|
|
||||||
let mut machines = vec![];
|
|
||||||
let mut commitments = Vec::with_capacity(PARTICIPANTS + 1);
|
let mut commitments = Vec::with_capacity(PARTICIPANTS + 1);
|
||||||
commitments.resize(PARTICIPANTS + 1, None);
|
commitments.resize(PARTICIPANTS + 1, None);
|
||||||
for i in 1 ..= THRESHOLD {
|
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());
|
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>,
|
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> {
|
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
algorithm: A,
|
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)]
|
#[allow(non_snake_case)]
|
||||||
pub struct StateMachine<C: Curve, A: Algorithm<C>> {
|
pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
|
||||||
params: Params<C, A>,
|
params: Params<C, A>,
|
||||||
state: State,
|
state: State,
|
||||||
preprocess: Option<PreprocessPackage<C>>,
|
preprocess: Option<PreprocessPackage<C>>,
|
||||||
sign: Option<Package<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
|
/// 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> {
|
pub fn new(
|
||||||
StateMachine {
|
algorithm: A,
|
||||||
params,
|
keys: Rc<MultisigKeys<C>>,
|
||||||
state: State::Fresh,
|
included: &[usize],
|
||||||
preprocess: None,
|
) -> Result<AlgorithmMachine<C, A>, FrostError> {
|
||||||
sign: None,
|
Ok(
|
||||||
}
|
AlgorithmMachine {
|
||||||
|
params: Params::new(algorithm, keys, included)?,
|
||||||
|
state: State::Fresh,
|
||||||
|
preprocess: None,
|
||||||
|
sign: None,
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform the preprocessing round required in order to sign
|
impl<C: Curve, A: Algorithm<C>> StateMachine for AlgorithmMachine<C, A> {
|
||||||
/// Returns a byte vector which must be transmitted to all parties selected for this signing
|
type Signature = A::Signature;
|
||||||
/// process, over an authenticated channel
|
|
||||||
pub fn preprocess<R: RngCore + CryptoRng>(
|
fn preprocess<R: RngCore + CryptoRng>(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut R
|
rng: &mut R
|
||||||
) -> Result<Vec<u8>, FrostError> {
|
) -> Result<Vec<u8>, FrostError> {
|
||||||
@@ -437,12 +478,7 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
|
|||||||
Ok(serialized)
|
Ok(serialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a message
|
fn sign(
|
||||||
/// 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(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
commitments: &[Option<Vec<u8>>],
|
commitments: &[Option<Vec<u8>>],
|
||||||
msg: &[u8],
|
msg: &[u8],
|
||||||
@@ -463,11 +499,7 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
|
|||||||
Ok(serialized)
|
Ok(serialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete signing
|
fn complete(&mut self, shares: &[Option<Vec<u8>>]) -> Result<A::Signature, FrostError> {
|
||||||
/// 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> {
|
|
||||||
if self.state != State::Signed {
|
if self.state != State::Signed {
|
||||||
Err(FrostError::InvalidSignTransition(State::Signed, self.state))?;
|
Err(FrostError::InvalidSignTransition(State::Signed, self.state))?;
|
||||||
}
|
}
|
||||||
@@ -482,11 +514,11 @@ impl<C: Curve, A: Algorithm<C>> StateMachine<C, A> {
|
|||||||
Ok(signature)
|
Ok(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multisig_params(&self) -> MultisigParams {
|
fn multisig_params(&self) -> MultisigParams {
|
||||||
self.params.multisig_params().clone()
|
self.params.multisig_params().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> State {
|
fn state(&self) -> State {
|
||||||
self.state
|
self.state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use frost::{
|
|||||||
MultisigParams, MultisigKeys,
|
MultisigParams, MultisigKeys,
|
||||||
key_gen,
|
key_gen,
|
||||||
algorithm::{Algorithm, Schnorr, SchnorrSignature},
|
algorithm::{Algorithm, Schnorr, SchnorrSignature},
|
||||||
sign
|
sign::{StateMachine, AlgorithmMachine}
|
||||||
};
|
};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
@@ -28,13 +28,11 @@ fn sign<C: Curve, A: Algorithm<C, Signature = SchnorrSignature<C>>>(
|
|||||||
commitments.resize(PARTICIPANTS + 1, None);
|
commitments.resize(PARTICIPANTS + 1, None);
|
||||||
for i in 1 ..= t {
|
for i in 1 ..= t {
|
||||||
machines.push(
|
machines.push(
|
||||||
sign::StateMachine::new(
|
AlgorithmMachine::new(
|
||||||
sign::Params::new(
|
algorithm.clone(),
|
||||||
algorithm.clone(),
|
keys[i - 1].clone(),
|
||||||
keys[i - 1].clone(),
|
&(1 ..= t).collect::<Vec<usize>>()
|
||||||
&(1 ..= t).collect::<Vec<usize>>()
|
).unwrap()
|
||||||
).unwrap()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap());
|
commitments[i] = Some(machines[i - 1].preprocess(&mut OsRng).unwrap());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user