Working multisig TXs

This commit is contained in:
Luke Parker
2022-04-30 04:32:19 -04:00
parent d6649fffb1
commit 9ccf683e9d
12 changed files with 577 additions and 325 deletions

View File

@@ -4,7 +4,7 @@ use rand_core::{RngCore, CryptoRng};
use group::Group;
use crate::{Curve, FrostError, sign};
use crate::{Curve, FrostError, MultisigView};
/// Algorithm to use FROST with
pub trait Algorithm<C: Curve>: Clone {
@@ -17,14 +17,14 @@ pub trait Algorithm<C: Curve>: Clone {
/// Generate an addendum to FROST"s preprocessing stage
fn preprocess_addendum<R: RngCore + CryptoRng>(
rng: &mut R,
params: &sign::ParamsView<C>,
params: &MultisigView<C>,
nonces: &[C::F; 2],
) -> Vec<u8>;
/// Proccess the addendum for the specified participant. Guaranteed to be ordered
fn process_addendum(
&mut self,
params: &sign::ParamsView<C>,
params: &MultisigView<C>,
l: usize,
commitments: &[C::G; 2],
serialized: &[u8],
@@ -39,7 +39,7 @@ pub trait Algorithm<C: Curve>: Clone {
/// The nonce will already have been processed into the combined form d + (e * p)
fn sign_share(
&mut self,
params: &sign::ParamsView<C>,
params: &MultisigView<C>,
nonce_sum: C::G,
b: C::F,
nonce: C::F,
@@ -98,7 +98,7 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
fn preprocess_addendum<R: RngCore + CryptoRng>(
_: &mut R,
_: &sign::ParamsView<C>,
_: &MultisigView<C>,
_: &[C::F; 2],
) -> Vec<u8> {
vec![]
@@ -106,7 +106,7 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
fn process_addendum(
&mut self,
_: &sign::ParamsView<C>,
_: &MultisigView<C>,
_: usize,
_: &[C::G; 2],
_: &[u8],
@@ -120,7 +120,7 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
fn sign_share(
&mut self,
params: &sign::ParamsView<C>,
params: &MultisigView<C>,
nonce_sum: C::G,
_: C::F,
nonce: C::F,

View File

@@ -1,6 +1,6 @@
use core::{ops::Mul, fmt::Debug};
use ff::PrimeField;
use ff::{Field, PrimeField};
use group::{Group, GroupOps, ScalarMul};
use thiserror::Error;
@@ -8,6 +8,7 @@ use thiserror::Error;
pub mod key_gen;
pub mod algorithm;
pub mod sign;
use sign::lagrange;
/// Set of errors for curve-related operations, namely encoding and decoding
#[derive(Error, Debug)]
@@ -190,6 +191,33 @@ pub enum FrostError {
InternalError(String),
}
// View of keys passable to algorithm implementations
#[derive(Clone)]
pub struct MultisigView<C: Curve> {
group_key: C::G,
included: Vec<usize>,
secret_share: C::F,
verification_shares: Vec<C::G>,
}
impl<C: Curve> MultisigView<C> {
pub fn group_key(&self) -> C::G {
self.group_key
}
pub fn included(&self) -> Vec<usize> {
self.included.clone()
}
pub fn secret_share(&self) -> C::F {
self.secret_share
}
pub fn verification_share(&self, l: usize) -> C::G {
self.verification_shares[l]
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct MultisigKeys<C: Curve> {
/// Multisig Parameters
@@ -229,6 +257,30 @@ impl<C: Curve> MultisigKeys<C> {
self.verification_shares.clone()
}
pub fn view(&self, included: &[usize]) -> Result<MultisigView<C>, FrostError> {
if (included.len() < self.params.t) || (self.params.n < included.len()) {
Err(FrostError::InvalidSigningSet("invalid amount of participants included".to_string()))?;
}
let secret_share = self.secret_share * lagrange::<C::F>(self.params.i, &included);
let (offset, offset_share) = if self.offset.is_some() {
let offset = self.offset.unwrap();
(offset, offset * C::F::from(included.len().try_into().unwrap()).invert().unwrap())
} else {
(C::F::zero(), C::F::zero())
};
Ok(MultisigView {
group_key: self.group_key + (C::generator_table() * offset),
secret_share: secret_share + offset_share,
verification_shares: self.verification_shares.clone().iter().enumerate().map(
|(l, share)| (*share * lagrange::<C::F>(l, &included)) +
(C::generator_table() * offset_share)
).collect(),
included: included.to_vec(),
})
}
pub fn serialized_len(n: usize) -> usize {
1 + usize::from(C::id_len()) + (3 * 8) + C::F_len() + C::G_len() + (n * C::G_len())
}

View File

@@ -1,4 +1,4 @@
use core::{convert::{TryFrom, TryInto}, cmp::min, fmt};
use core::{convert::TryFrom, cmp::min, fmt};
use std::rc::Rc;
use rand_core::{RngCore, CryptoRng};
@@ -6,7 +6,7 @@ use rand_core::{RngCore, CryptoRng};
use ff::{Field, PrimeField};
use group::Group;
use crate::{Curve, MultisigParams, MultisigKeys, FrostError, algorithm::Algorithm};
use crate::{Curve, FrostError, MultisigParams, MultisigKeys, MultisigView, algorithm::Algorithm};
/// Calculate the lagrange coefficient
pub fn lagrange<F: PrimeField>(
@@ -30,39 +30,12 @@ pub fn lagrange<F: PrimeField>(
num * denom.invert().unwrap()
}
// View of params passable to algorithm implementations
#[derive(Clone)]
pub struct ParamsView<C: Curve> {
group_key: C::G,
included: Vec<usize>,
secret_share: C::F,
verification_shares: Vec<C::G>,
}
impl<C: Curve> ParamsView<C> {
pub fn group_key(&self) -> C::G {
self.group_key
}
pub fn included(&self) -> Vec<usize> {
self.included.clone()
}
pub fn secret_share(&self) -> C::F {
self.secret_share
}
pub fn verification_share(&self, l: usize) -> C::G {
self.verification_shares[l]
}
}
/// Pairing of an Algorithm with a MultisigKeys instance and this specific signing set
#[derive(Clone)]
pub struct Params<C: Curve, A: Algorithm<C>> {
algorithm: A,
keys: Rc<MultisigKeys<C>>,
view: ParamsView<C>,
view: MultisigView<C>,
}
// Currently public to enable more complex operations as desired, yet solely used in testing
@@ -75,7 +48,7 @@ impl<C: Curve, A: Algorithm<C>> Params<C, A> {
let mut included = included.to_vec();
(&mut included).sort_unstable();
// included < threshold
// Included < threshold
if included.len() < keys.params.t {
Err(FrostError::InvalidSigningSet("not enough signers".to_string()))?;
}
@@ -98,37 +71,15 @@ impl<C: Curve, A: Algorithm<C>> Params<C, A> {
Err(FrostError::InvalidSigningSet("signing despite not being included".to_string()))?;
}
let secret_share = keys.secret_share * lagrange::<C::F>(keys.params.i, &included);
let (offset, offset_share) = if keys.offset.is_some() {
let offset = keys.offset.unwrap();
(offset, offset * C::F::from(included.len().try_into().unwrap()).invert().unwrap())
} else {
(C::F::zero(), C::F::zero())
};
Ok(
Params {
algorithm,
// Out of order arguments to prevent additional cloning
view: ParamsView {
group_key: keys.group_key + (C::generator_table() * offset),
secret_share: secret_share + offset_share,
verification_shares: keys.verification_shares.clone().iter().enumerate().map(
|(l, share)| (*share * lagrange::<C::F>(l, &included)) +
(C::generator_table() * offset_share)
).collect(),
included: included,
},
keys
}
)
// Out of order arguments to prevent additional cloning
Ok(Params { algorithm, view: keys.view(&included).unwrap(), keys })
}
pub fn multisig_params(&self) -> MultisigParams {
self.keys.params
}
pub fn view(&self) -> ParamsView<C> {
pub fn view(&self) -> MultisigView<C> {
self.view.clone()
}
}