Skip to content

Commit

Permalink
Include Lagrange
Browse files Browse the repository at this point in the history
  • Loading branch information
emmorais committed Aug 19, 2024
1 parent b0d074b commit acbfc83
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 19 deletions.
3 changes: 2 additions & 1 deletion examples/threaded_example/threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,8 @@ impl Worker {
let key_shares = self.key_gen_material.retrieve(&key_id).public_key_shares();
let record = self.presign_records.take(&key_id);

let inputs = sign::Input::new(b"hello world", record, key_shares.to_vec());
let threshold = key_shares.len();
let inputs = sign::Input::new(b"hello world", record, key_shares.to_vec(), threshold);
self.new_sub_protocol::<SignParticipant>(sid, inputs, key_id)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ pub mod tshare;
mod utils;
mod zkp;
mod zkstar;
mod threshold;

pub use gmp_zeroize::enable_zeroize;
pub use participant::ProtocolParticipant;
Expand Down
2 changes: 1 addition & 1 deletion src/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ pub enum Status {
/// This variant is used by
/// [`InteractiveSignParticipant`](crate::sign::InteractiveSignParticipant)
RunningPresign,
/// Participant completed presign and is running sign.
/// Participant received a ready message and is running tshare.
///
/// This variant is used by
/// [`InteractiveSignParticipant`](crate::sign::InteractiveSignParticipant)
Expand Down
21 changes: 18 additions & 3 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,9 +815,16 @@ mod tests {

#[cfg_attr(feature = "flame_it", flame)]
#[test]
fn full_protocol_execution_with_noninteractive_signing_works() -> Result<()> {
fn test_full_protocol_execution_with_noninteractive_signing_works() {
let result = full_protocol_execution_with_noninteractive_signing_works(3, 3, 3);
assert!(result.is_ok());
}

fn full_protocol_execution_with_noninteractive_signing_works(r: usize, t: usize, n: usize) -> Result<()> {
let mut rng = init_testing();
let QUORUM_SIZE = 3;
let QUORUM_REAL = r; // only r participants are going to participate
let QUORUM_THRESHOLD = t; // threshold t
let QUORUM_SIZE = n; // total number of participants
// Set GLOBAL config for participants
let configs = ParticipantConfig::random_quorum(QUORUM_SIZE, &mut rng).unwrap();

Expand Down Expand Up @@ -973,12 +980,20 @@ mod tests {
let digest = Keccak256::new_with_prefix(message);
let sign_sid = Identifier::random(&mut rng);

// TODO: tshare phase

// TODO: delete n-r participants from configs

// TODO: if less than t participants are present, then the protocol should fail

// TODO: adapt signature for threshold

// Make signing participants
let mut sign_quorum = configs
.into_iter()
.map(|config| {
let record = presign_outputs.remove(&config.id()).unwrap();
let input = sign::Input::new(message, record, public_key_shares.clone());
let input = sign::Input::new(message, record, public_key_shares.clone(), QUORUM_THRESHOLD);
Participant::<SignParticipant>::from_config(config, sign_sid, input)
})
.collect::<Result<Vec<_>>>()?;
Expand Down
6 changes: 4 additions & 2 deletions src/sign/interactive_sign/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ impl SigningMaterial {
digest,
public_keys,
} => {
let signing_input = sign::Input::new_from_digest(*digest, record, public_keys);
// TODO: threhsold is not implemented yet for the interactive signing, must use the same as the public keys size
let threshold = public_keys.len();
let signing_input = sign::Input::new_from_digest(*digest, record, public_keys, threshold);
// Note: this shouldn't throw an error because the only failure case should have
// also been checked by the presign constructor, and computation
// halted far before we reach this point.
Expand Down Expand Up @@ -256,7 +258,7 @@ impl ProtocolParticipant for InteractiveSignParticipant {
// and sign -- e.g. we will not pass a ready message to the `signer` until
// the `presigner` is sucessfully completed.
// Another option would be to maintain a status field and update it at
// the appropriate poitns.
// the appropriate points.
if !self.presigner.status().is_ready() {
return &Status::NotReady;
}
Expand Down
20 changes: 8 additions & 12 deletions src/sign/non_interactive_sign/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,7 @@ use tracing::{error, info};
use zeroize::Zeroize;

use crate::{
errors::{CallerError, InternalError, Result},
keygen::KeySharePublic,
local_storage::LocalStorage,
messages::{Message, MessageType, SignMessageType},
participant::{InnerProtocolParticipant, ProcessOutcome, Status},
protocol::{ProtocolType, SharedContext},
run_only_once,
sign::{non_interactive_sign::share::SignatureShare, Signature},
utils::CurvePoint,
zkp::ProofContext,
Identifier, ParticipantConfig, ParticipantIdentifier, PresignRecord, ProtocolParticipant,
errors::{CallerError, InternalError, Result}, keygen::KeySharePublic, local_storage::LocalStorage, messages::{Message, MessageType, SignMessageType}, participant::{InnerProtocolParticipant, ProcessOutcome, Status}, protocol::{ProtocolType, SharedContext}, run_only_once, sign::{non_interactive_sign::share::SignatureShare, Signature}, threshold, utils::CurvePoint, zkp::ProofContext, Identifier, ParticipantConfig, ParticipantIdentifier, PresignRecord, ProtocolParticipant
};

/// A participant that runs the non-interactive signing protocol in Figure 8 of
Expand Down Expand Up @@ -79,6 +69,7 @@ pub struct Input {
digest: Keccak256,
presign_record: PresignRecord,
public_key_shares: Vec<KeySharePublic>,
threshold: usize,
}

impl Input {
Expand All @@ -90,11 +81,13 @@ impl Input {
message: &[u8],
record: PresignRecord,
public_key_shares: Vec<KeySharePublic>,
threshold: usize,
) -> Self {
Self {
digest: Keccak256::new_with_prefix(message),
presign_record: record,
public_key_shares,
threshold,
}
}

Expand All @@ -106,11 +99,13 @@ impl Input {
digest: Keccak256,
record: PresignRecord,
public_key_shares: Vec<KeySharePublic>,
threshold: usize,
) -> Self {
Self {
digest,
presign_record: record,
public_key_shares,
threshold,
}
}

Expand Down Expand Up @@ -409,6 +404,7 @@ impl SignParticipant {

// Sum up the signature shares and convert to BIP-0062 format (negating if the
// sum is > group order /2)
// TODO: replace by Lagrange interpolation
let mut sum = shares.into_iter().fold(Scalar::ZERO, |a, b| a + b);
sum.conditional_assign(&sum.negate(), sum.is_high());

Expand Down Expand Up @@ -550,7 +546,7 @@ mod test {

// Form signing inputs and participants
let inputs = std::iter::zip(keygen_outputs, presign_records).map(|(keygen, record)| {
sign::Input::new(message, record, keygen.public_key_shares().to_vec())
sign::Input::new(message, record, keygen.public_key_shares().to_vec(), quorum_size)
});
let mut quorum = std::iter::zip(configs, inputs)
.map(|(config, input)| {
Expand Down
91 changes: 91 additions & 0 deletions src/threshold/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use k256::{elliptic_curve::Field, Scalar};
use rand::Rng;

fn generate_polynomial<R: Rng>(t: usize, rng: &mut R) -> Vec<Scalar> {
let mut coefficients = Vec::with_capacity(t);
for _ in 0..t {
coefficients.push(Scalar::random(&mut *rng));
}
coefficients
}

fn evaluate_polynomial(coefficients: &Vec<Scalar>, x: &Scalar) -> Scalar {
coefficients
.iter()
.rev()
.fold(Scalar::ZERO, |acc, coef| acc * x + coef)
}

fn lagrange_coefficient(my_point: &Scalar, other_points: &Vec<Scalar>) -> Scalar {
let mut result = Scalar::ONE;
for point in other_points {
if point != my_point {
let denominator = my_point - point;
let inv = denominator.invert().unwrap();
result *= point * &inv;
}
}
result
}

fn evaluate_at_points(coefficients: &Vec<Scalar>, points: &Vec<Scalar>) -> Vec<Scalar> {
points
.iter()
.map(|x| evaluate_polynomial(coefficients, x))
.collect()
}

#[cfg(test)]
mod tests {
use super::*;
use rand::thread_rng;

#[test]
fn test_generate_and_evaluate_polynomial() {
let mut rng = thread_rng();
let t = 3;
let coefficients = generate_polynomial(t, &mut rng);

let x = Scalar::random(&mut rng);
let value = evaluate_polynomial(&coefficients, &x);

// Just to check if we got something
// non-trivial
assert!(!bool::from(value.is_zero()));
}

#[test]
fn test_lagrange_coefficients() {
let points: Vec<Scalar> = (1..=3).map(|i:u32| Scalar::from(i)).collect();
let evaluated_values = vec![
points[0] * Scalar::from(2u32),
points[1] * Scalar::from(3u32),
points[2] * Scalar::from(4u32),
];

let reconstructed_zero = evaluated_values
.iter()
.zip(&points)
.map(|(value, point)| *value * lagrange_coefficient(point, &points))
.fold(Scalar::ZERO, |acc, x| acc + x);

// Check that reconstructed value
// at X=0 is correct
assert!(bool::from(reconstructed_zero.is_zero()));
}

#[test]
fn test_evaluate_at_points() {
let mut rng = thread_rng();
let t = 3;
let n = 5;
let coefficients = generate_polynomial(t, &mut rng);

let points: Vec<Scalar> = (1..=n).map(|i: u32| Scalar::from(i)).collect();
let values = evaluate_at_points(&coefficients, &points);

for (x, y) in points.iter().zip(values.iter()) {
assert_eq!(evaluate_polynomial(&coefficients, x), *y);
}
}
}
Loading

0 comments on commit acbfc83

Please sign in to comment.