Skip to content

Commit 956e259

Browse files
committed
Single signer produce a signature #144, #145
1 parent 21caf2c commit 956e259

File tree

4 files changed

+181
-15
lines changed

4 files changed

+181
-15
lines changed

mithril-network/mithril-aggregator/src/fake_data.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,10 @@ pub fn signers(total: u64) -> Vec<entities::Signer> {
106106
// TODO: To delete once key registration is implemented
107107
#[derive(Serialize, Deserialize, Clone)]
108108
pub struct SignerWithSecretKeys {
109-
party_id: u64,
110-
verification_key: String,
111-
secret_key: String,
109+
pub party_id: u64,
110+
pub stake: u64,
111+
pub verification_key: String,
112+
pub secret_key: String,
112113
}
113114

114115
/// Fake SignerKeys returns Verification/Secret keys for a party_id

mithril-network/mithril-signer/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-network/mithril-signer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ tar = "0.4.38"
2424
flate2 = "1.0.23"
2525
thiserror = "1.0.31"
2626

27+
hex = "0.4.3"
2728
rand_core = "0.6.3"
2829
rand_chacha = "0.3.1"
2930
rand = "0.7"

mithril-network/mithril-signer/src/single_signer.rs

Lines changed: 175 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// @todo: remove this
22
#![allow(dead_code)]
3-
use mithril_aggregator::entities;
43

54
use ark_bls12_377::Bls12_377;
5+
use hex::{FromHex, ToHex};
6+
use std::io::Cursor;
7+
use thiserror::Error;
8+
69
use mithril::key_reg::KeyReg;
710
use mithril::merkle_tree::MTHashLeaf;
811
use mithril::mithril_proof::concat_proofs::{ConcatProof, TrivialEnv};
@@ -11,7 +14,7 @@ use mithril::stm::{
1114
Index, MTValue, PartyId, Stake, StmClerk, StmInitializer, StmMultiSig, StmParameters, StmSig,
1215
StmSigner,
1316
};
14-
use thiserror::Error;
17+
use mithril_aggregator::entities;
1518

1619
pub type Bytes = Vec<u8>;
1720

@@ -36,6 +39,9 @@ pub type ProtocolSignerSecretKey = MspSk<Bls12_377>;
3639
use mithril_aggregator::entities::{SignerWithStake, SingleSignature};
3740
#[cfg(test)]
3841
use mockall::automock;
42+
use rand_chacha::ChaCha20Rng;
43+
use rand_core::SeedableRng;
44+
use slog_scope::{trace, warn};
3945

4046
#[cfg_attr(test, automock)]
4147
pub trait SingleSigner {
@@ -49,38 +55,163 @@ pub trait SingleSigner {
4955

5056
#[derive(Error, Debug, PartialEq)]
5157
pub enum SingleSignerError {
52-
#[error("the signer is not registered in the stake distribution")]
58+
#[error("the signer verification key is not registered in the stake distribution")]
5359
UnregisteredVerificationKey(),
60+
61+
#[error("the signer party id is not registered in the stake distribution")]
62+
UnregisteredPartyId(),
63+
64+
#[error("the protocol signer creation failed: `{0}`")]
65+
ProtocolSignerCreationFailure(String),
5466
}
5567

56-
pub struct MithrilSingleSigner {}
68+
pub struct MithrilSingleSigner {
69+
party_id: u64,
70+
secret_key: ProtocolSignerSecretKey,
71+
}
5772

5873
impl MithrilSingleSigner {
59-
pub fn new() -> Self {
60-
Self {}
74+
pub fn new(party_id: u64, secret_key: ProtocolSignerSecretKey) -> Self {
75+
Self {
76+
party_id,
77+
secret_key,
78+
}
79+
}
80+
81+
pub fn create_protocol_signer(
82+
&self,
83+
current_player_stake: ProtocolStake,
84+
stake_distribution: &[SignerWithStake], // @todo : use a hmap to prevent party id duplication
85+
protocol_parameters: &entities::ProtocolParameters,
86+
) -> Result<ProtocolSigner, String> {
87+
let players = stake_distribution
88+
.into_iter()
89+
.map(|s| (s.party_id as ProtocolPartyId, s.stake as ProtocolStake))
90+
.collect::<Vec<_>>();
91+
let protocol_parameters = ProtocolParameters {
92+
k: protocol_parameters.k,
93+
m: protocol_parameters.m,
94+
phi_f: protocol_parameters.phi_f as f64,
95+
};
96+
97+
let mut key_reg = KeyReg::new(&players);
98+
for s in stake_distribution {
99+
let decoded_key = key_decode_hex(&s.verification_key)?;
100+
key_reg
101+
.register(s.party_id as ProtocolPartyId, decoded_key)
102+
.unwrap();
103+
}
104+
let closed_reg = key_reg.close();
105+
106+
let seed = [0u8; 32];
107+
let mut rng = ChaCha20Rng::from_seed(seed);
108+
let p = StmInitializer::setup(
109+
protocol_parameters,
110+
self.party_id as ProtocolPartyId,
111+
current_player_stake,
112+
&mut rng,
113+
);
114+
Ok(p.new_signer(closed_reg))
61115
}
62116
}
63117

64118
impl SingleSigner for MithrilSingleSigner {
65119
fn compute_single_signatures(
66120
&self,
67-
_message: Bytes,
68-
_stake_distribution: Vec<SignerWithStake>,
69-
_protocol_parameters: &entities::ProtocolParameters,
121+
message: Bytes,
122+
stake_distribution: Vec<SignerWithStake>, // @todo : use a hmap to prevent party id duplication
123+
protocol_parameters: &entities::ProtocolParameters,
70124
) -> Result<Vec<SingleSignature>, SingleSignerError> {
71-
Err(SingleSignerError::UnregisteredVerificationKey())
125+
let current_signer_with_stake = stake_distribution
126+
.iter()
127+
.find(|s| s.party_id == self.party_id)
128+
.ok_or(SingleSignerError::UnregisteredPartyId())?;
129+
130+
if current_signer_with_stake.verification_key.is_empty() {
131+
Err(SingleSignerError::UnregisteredVerificationKey())
132+
} else {
133+
let protocol_signer = self
134+
.create_protocol_signer(
135+
current_signer_with_stake.stake,
136+
&stake_distribution,
137+
protocol_parameters,
138+
)
139+
.map_err(SingleSignerError::ProtocolSignerCreationFailure)?;
140+
141+
trace!(
142+
"Party #{}: sign message {}",
143+
self.party_id,
144+
message.encode_hex::<String>()
145+
);
146+
147+
let mut signatures = Vec::new();
148+
for i in 1..=protocol_parameters.m {
149+
if let Some(signature) = protocol_signer.sign(&message, i) {
150+
trace!("Party #{}: lottery #{} won", self.party_id, i,);
151+
let encoded_signature = key_encode_hex(signature);
152+
153+
if encoded_signature.is_err() {
154+
warn!("couldn't compute signature: `{:?}`", encoded_signature); // @todo: structured log
155+
continue;
156+
}
157+
158+
signatures.push(SingleSignature::new(
159+
self.party_id,
160+
i,
161+
encoded_signature.unwrap(),
162+
));
163+
}
164+
}
165+
Ok(signatures)
166+
}
72167
}
73168
}
74169

170+
/// Encode key to hex helper
171+
pub fn key_encode_hex<T: ark_ff::ToBytes>(from: T) -> Result<String, String> {
172+
Ok(ark_ff::to_bytes!(from)
173+
.map_err(|e| format!("can't convert to hex: {}", e))?
174+
.encode_hex::<String>())
175+
}
176+
177+
/// Decode key from hex helper
178+
pub fn key_decode_hex<T: ark_ff::FromBytes>(from: &str) -> Result<T, String> {
179+
ark_ff::FromBytes::read(Cursor::new(
180+
Vec::from_hex(from).map_err(|e| format!("can't parse from hex: {}", e))?,
181+
))
182+
.map_err(|e| format!("can't convert to bytes: {}", e))
183+
}
184+
75185
#[cfg(test)]
76186
mod tests {
77187
use super::*;
78188
use mithril_aggregator::fake_data;
79189

190+
fn create_clerk(signer: &ProtocolSigner) -> ProtocolClerk {
191+
StmClerk::from_signer(signer, TrivialEnv)
192+
}
193+
80194
#[test]
81195
fn cant_compute_if_signer_verification_key_is_not_registered() {
82-
let single_signer = MithrilSingleSigner::new();
83-
let stake_distribution = fake_data::signers_with_stakes(5);
196+
let signer_with_keys = fake_data::signer_keys(0).unwrap();
197+
let single_signer = MithrilSingleSigner::new(
198+
signer_with_keys.party_id,
199+
key_decode_hex(&signer_with_keys.secret_key).unwrap(),
200+
);
201+
let stake_distribution = fake_data::signers_with_stakes(5)
202+
.into_iter()
203+
.map(|s| {
204+
if s.party_id == signer_with_keys.party_id {
205+
SignerWithStake {
206+
party_id: s.party_id,
207+
verification_key: "".to_string(),
208+
stake: s.stake,
209+
}
210+
} else {
211+
s
212+
}
213+
})
214+
.collect();
84215
let protocol_parameters = fake_data::protocol_parameters();
85216

86217
let sign_result = single_signer.compute_single_signatures(
@@ -94,4 +225,36 @@ mod tests {
94225
sign_result.unwrap_err()
95226
)
96227
}
228+
229+
#[test]
230+
fn should_produce_a_single_signature() {
231+
let signer_with_keys = fake_data::signer_keys(0).unwrap();
232+
let single_signer = MithrilSingleSigner::new(
233+
signer_with_keys.party_id,
234+
key_decode_hex(&signer_with_keys.secret_key).unwrap(),
235+
);
236+
let stake_distribution = fake_data::signers_with_stakes(5);
237+
let protocol_parameters = fake_data::protocol_parameters();
238+
let protocol_signer: ProtocolSigner = single_signer
239+
.create_protocol_signer(
240+
signer_with_keys.stake,
241+
&stake_distribution,
242+
&protocol_parameters,
243+
)
244+
.unwrap();
245+
let clerk = StmClerk::from_signer(&protocol_signer, TrivialEnv);
246+
247+
let message = "message".as_bytes();
248+
let sign_result = single_signer.compute_single_signatures(
249+
message.to_vec(),
250+
stake_distribution,
251+
&protocol_parameters,
252+
);
253+
254+
assert!(!sign_result.as_ref().unwrap().is_empty());
255+
for sig in sign_result.unwrap() {
256+
let decoded_sig = key_decode_hex(&sig.signature).unwrap();
257+
assert!(clerk.verify_sig(&decoded_sig, sig.index, message).is_ok());
258+
}
259+
}
97260
}

0 commit comments

Comments
 (0)