Skip to content

Commit

Permalink
feat: add util/multisig
Browse files Browse the repository at this point in the history
  • Loading branch information
jjyr committed May 23, 2019
1 parent ca645a2 commit 8b305de
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 0 deletions.
12 changes: 12 additions & 0 deletions util/multisig/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "multisig"
version = "0.1.0"
authors = ["Nervos Core Dev <[email protected]>"]
edition = "2018"

[dependencies]
secp256k1 = "0.12.2"
lazy_static = "1.3.0"
failure = "0.1.5"
rand = "0.6.5"

36 changes: 36 additions & 0 deletions util/multisig/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use failure::Context;

#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "The count of sigs should less than pks.")]
SigCountOverflow,
#[fail(display = "The count of sigs less than threshold.")]
SigNotEnough,
#[fail(display = "Threshold not reach, sigs: {} required sigs: {}.", _0, _1)]
Threshold(usize, usize),
}

impl Error {
pub fn kind(&self) -> ErrorKind {
*self.inner.get_context()
}
}

impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}

impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}
7 changes: 7 additions & 0 deletions util/multisig/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate failure;

pub mod error;
pub mod secp256k1;
138 changes: 138 additions & 0 deletions util/multisig/src/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use crate::error::{Error, ErrorKind};
pub use secp256k1::{
All, Error as Secp256k1Error, Message, PublicKey, RecoverableSignature, Secp256k1, SecretKey,
Signature,
};

lazy_static! {
pub static ref SECP256K1: Secp256k1<All> = Secp256k1::new();
}

pub fn sign(message: &Message, sk: &SecretKey) -> Signature {
SECP256K1.sign(message, sk)
}

/// position of each sigs must according to the pk that sined the sig
/// Example 2 of 3 sigs: [s1, None, s3], pks: [pk1, pk2, pk3]
pub fn verify_m_of_n(
message: &Message,
m_threshold: usize,
sigs: Vec<Option<Signature>>,
pks: Vec<PublicKey>,
) -> Result<(), Error> {
if sigs.len() > pks.len() {
Err(ErrorKind::SigCountOverflow)?;
}
if m_threshold > sigs.len() {
Err(ErrorKind::SigNotEnough)?;
}
let verified_sig_count = sigs
.iter()
.zip(pks.iter())
.filter_map(|(sig, pk)| sig.and_then(|sig| SECP256K1.verify(&message, &sig, pk).ok()))
.take(m_threshold)
.count();
if verified_sig_count < m_threshold {
Err(ErrorKind::Threshold(verified_sig_count, m_threshold))?
}
Ok(())
}

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

fn random_message() -> Message {
let mut data = [0; 32];
thread_rng().fill(&mut data[..]);
loop {
if let Ok(msg) = Message::from_slice(&data) {
return msg;
}
}
}

fn random_signature(message: &Message) -> Signature {
let secret_key = random_secret_key();
sign(message, &secret_key)
}

fn random_secret_key() -> SecretKey {
let mut data = [0; 32];
thread_rng().fill(&mut data[..]);
loop {
if let Ok(key) = SecretKey::from_slice(&data) {
return key;
}
}
}

#[test]
fn test_m_of_n() {
// (thresholds, sigs: [is_valid], pks, result)
let test_set = [
(2, vec![true, true], 3, Ok(())),
(2, vec![true, true, true], 3, Ok(())),
(3, vec![true, true, true], 3, Ok(())),
(3, vec![true, false, true, true], 4, Ok(())),
(
2,
vec![true, true, true],
1,
Err(ErrorKind::SigCountOverflow),
),
(3, vec![true, true], 3, Err(ErrorKind::SigNotEnough)),
(
3,
vec![true, true, false],
3,
Err(ErrorKind::Threshold(2, 3)),
),
];
for (threshold, sigs, pks, result) in test_set.into_iter() {
let message = random_message();
let sks: Vec<SecretKey> = (0..sigs.len())
.into_iter()
.map(|_| random_secret_key())
.collect();
let pks: Vec<PublicKey> = sks
.iter()
.enumerate()
.map(|(_i, sk)| PublicKey::from_secret_key(&SECP256K1, sk))
.take(*pks)
.collect();
let sigs: Vec<Option<Signature>> = sigs
.into_iter()
.enumerate()
.map(|(i, valid)| {
if *valid {
Some(sign(&message, &sks[i]))
} else {
None
}
})
.collect();
let verify_result =
verify_m_of_n(&message, *threshold, sigs, pks).map_err(|err| err.kind());
assert_eq!(&verify_result, result);
}
}

#[test]
fn test_2_of_3_with_wrong_signature() {
let message = random_message();
let sks: Vec<SecretKey> = (0..3).into_iter().map(|_| random_secret_key()).collect();
let pks: Vec<PublicKey> = sks
.iter()
.map(|sk| PublicKey::from_secret_key(&SECP256K1, sk))
.collect();
let sigs: Vec<Option<Signature>> = vec![
Some(sign(&message, &sks[0])),
Some(random_signature(&message)),
Some(sign(&message, &sks[2])),
];
let verify_result = verify_m_of_n(&message, 2, sigs, pks);
assert!(verify_result.is_ok());
}
}

0 comments on commit 8b305de

Please sign in to comment.