#[cfg(feature = "future_snark")]
use anyhow::Context;
use anyhow::anyhow;
use rand_core::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use crate::{
ClosedKeyRegistration, MembershipDigest, Parameters, RegisterError, RegistrationEntry,
RegistrationEntryForConcatenation, Stake, StmResult,
VerificationKeyProofOfPossessionForConcatenation, proof_system::ConcatenationProofSigner,
signature_scheme::BlsSigningKey,
};
#[cfg(feature = "future_snark")]
use crate::{
ClosedRegistrationEntry, RegistrationEntryForSnark, VerificationKeyForSnark,
proof_system::SnarkProofSigner, signature_scheme::SchnorrSigningKey,
};
use crate::codec;
use super::Signer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Initializer {
pub stake: Stake,
#[serde(rename = "params")]
pub parameters: Parameters,
#[serde(rename = "sk")]
pub bls_signing_key: BlsSigningKey,
#[serde(rename = "pk")]
pub bls_verification_key_proof_of_possession: VerificationKeyProofOfPossessionForConcatenation,
#[cfg(feature = "future_snark")]
#[serde(skip_serializing_if = "Option::is_none")]
pub schnorr_signing_key: Option<SchnorrSigningKey>,
#[cfg(feature = "future_snark")]
#[serde(skip_serializing_if = "Option::is_none")]
pub schnorr_verification_key: Option<VerificationKeyForSnark>,
}
impl Initializer {
pub fn new<R: RngCore + CryptoRng>(parameters: Parameters, stake: Stake, rng: &mut R) -> Self {
let bls_signing_key = BlsSigningKey::generate(rng);
let bls_verification_key_proof_of_possession =
VerificationKeyProofOfPossessionForConcatenation::from(&bls_signing_key);
#[cfg(feature = "future_snark")]
let (schnorr_signing_key, schnorr_verification_key) = {
let sk = SchnorrSigningKey::generate(rng);
let vk = VerificationKeyForSnark::new_from_signing_key(sk.clone());
(Some(sk), Some(vk))
};
Self {
stake,
parameters,
bls_signing_key,
bls_verification_key_proof_of_possession,
#[cfg(feature = "future_snark")]
schnorr_signing_key,
#[cfg(feature = "future_snark")]
schnorr_verification_key,
}
}
pub fn try_create_signer<D: MembershipDigest>(
self,
closed_key_registration: &ClosedKeyRegistration,
) -> StmResult<Signer<D>> {
let registration_entry = RegistrationEntry::new(
self.bls_verification_key_proof_of_possession,
self.stake,
#[cfg(feature = "future_snark")]
self.schnorr_verification_key,
)?;
let signer_index = closed_key_registration
.get_signer_index_for_registration(
&(
registration_entry,
closed_key_registration.total_stake,
self.parameters.phi_f,
)
.try_into()?,
)
.ok_or_else(|| anyhow!(RegisterError::UnregisteredInitializer))?;
let key_registration_commitment_for_concatenation = closed_key_registration
.to_merkle_tree::<D::ConcatenationHash, RegistrationEntryForConcatenation>()
.to_merkle_tree_batch_commitment();
let concatenation_proof_signer = ConcatenationProofSigner::new(
registration_entry.get_stake(),
closed_key_registration.total_stake,
self.parameters,
self.bls_signing_key,
self.bls_verification_key_proof_of_possession.vk,
key_registration_commitment_for_concatenation,
);
#[cfg(feature = "future_snark")]
let snark_proof_signer = {
match (self.schnorr_signing_key, self.schnorr_verification_key) {
(Some(schnorr_signing_key), Some(schnorr_verification_key)) => {
let key_registration_commitment_for_snark = closed_key_registration
.to_merkle_tree::<D::SnarkHash, RegistrationEntryForSnark>()
.to_merkle_tree_commitment();
let lottery_target_value = ClosedRegistrationEntry::try_from((
registration_entry,
closed_key_registration.total_stake,
self.parameters.phi_f,
))?
.get_lottery_target_value()
.ok_or(RegisterError::SnarkProofSignerCreation)
.with_context(|| "missing lottery target value")?;
Some(SnarkProofSigner::new(
self.parameters,
schnorr_signing_key,
schnorr_verification_key,
lottery_target_value,
key_registration_commitment_for_snark,
))
}
_ => None,
}
};
Ok(Signer::new(
signer_index,
concatenation_proof_signer,
closed_key_registration.clone(),
self.parameters,
registration_entry.get_stake(),
#[cfg(feature = "future_snark")]
snark_proof_signer,
))
}
pub fn get_verification_key_proof_of_possession_for_concatenation(
&self,
) -> VerificationKeyProofOfPossessionForConcatenation {
self.bls_verification_key_proof_of_possession
}
#[cfg(feature = "future_snark")]
pub fn get_verification_key_for_snark(&self) -> Option<VerificationKeyForSnark> {
self.schnorr_verification_key
}
#[cfg(feature = "future_snark")]
pub fn strip_snark_keys(&mut self) {
self.schnorr_signing_key = None;
self.schnorr_verification_key = None;
}
pub fn to_bytes(&self) -> StmResult<Vec<u8>> {
codec::to_cbor_bytes(self)
}
pub fn from_bytes(bytes: &[u8]) -> StmResult<Initializer> {
codec::from_versioned_bytes(bytes, Self::from_bytes_legacy)
}
fn from_bytes_legacy(bytes: &[u8]) -> StmResult<Initializer> {
let mut u64_bytes = [0u8; 8];
u64_bytes.copy_from_slice(bytes.get(..8).ok_or(RegisterError::SerializationError)?);
let stake = u64::from_be_bytes(u64_bytes);
let params =
Parameters::from_bytes(bytes.get(8..32).ok_or(RegisterError::SerializationError)?)?;
let bls_signing_key =
BlsSigningKey::from_bytes(bytes.get(32..64).ok_or(RegisterError::SerializationError)?)?;
let bls_verification_key_proof_of_possession =
VerificationKeyProofOfPossessionForConcatenation::from_bytes(
bytes.get(64..256).ok_or(RegisterError::SerializationError)?,
)?;
#[cfg(feature = "future_snark")]
let (schnorr_signing_key, schnorr_verification_key) = {
let schnorr_signing_key =
bytes.get(256..288).map(SchnorrSigningKey::from_bytes).transpose()?;
let schnorr_verification_key = bytes
.get(288..352)
.map(VerificationKeyForSnark::from_bytes)
.transpose()?;
match (&schnorr_signing_key, &schnorr_verification_key) {
(Some(_), None) | (None, Some(_)) => {
return Err(RegisterError::SerializationError.into());
}
_ => {}
}
(schnorr_signing_key, schnorr_verification_key)
};
Ok(Self {
stake,
parameters: params,
bls_signing_key,
bls_verification_key_proof_of_possession,
#[cfg(feature = "future_snark")]
schnorr_signing_key,
#[cfg(feature = "future_snark")]
schnorr_verification_key,
})
}
}
impl PartialEq for Initializer {
fn eq(&self, other: &Self) -> bool {
let base_eq = self.stake == other.stake
&& self.parameters == other.parameters
&& self.bls_signing_key.to_bytes() == other.bls_signing_key.to_bytes()
&& self.get_verification_key_proof_of_possession_for_concatenation()
== other.get_verification_key_proof_of_possession_for_concatenation();
#[cfg(feature = "future_snark")]
let base_eq = base_eq
&& self.schnorr_signing_key == other.schnorr_signing_key
&& self.schnorr_verification_key == other.schnorr_verification_key;
base_eq
}
}
#[cfg(test)]
mod tests {
use super::*;
mod golden {
use rand_chacha::ChaCha20Rng;
use rand_core::SeedableRng;
use super::*;
const GOLDEN_JSON: &str = r#"
{
"stake":1,
"params":
{
"m":20973,
"k":2422,
"phi_f":0.2
},
"sk":[64,129,87,121,27,239,221,215,2,103,45,207,207,201,157,163,81,47,156,14,168,24,137,15,203,106,183,73,88,14,242,207],
"pk":
{
"vk":[143,161,255,48,78,57,204,220,25,221,164,252,248,14,56,126,186,135,228,188,145,181,52,200,97,99,213,46,0,199,193,89,187,88,29,135,173,244,86,36,83,54,67,164,6,137,94,72,6,105,128,128,93,48,176,11,4,246,138,48,180,133,90,142,192,24,193,111,142,31,76,111,110,234,153,90,208,192,31,124,95,102,49,158,99,52,220,165,94,251,68,69,121,16,224,194],
"pop":[168,50,233,193,15,136,65,72,123,148,129,176,38,198,209,47,28,204,176,144,57,251,42,28,66,76,89,97,158,63,54,198,194,176,135,221,14,185,197,225,202,98,243,74,233,225,143,151,147,177,170,117,66,165,66,62,33,216,232,75,68,114,195,22,100,65,44,198,4,166,102,233,253,240,59,175,60,117,142,114,140,122,17,87,110,187,1,17,10,195,154,13,249,86,54,226]
}
}
"#;
fn golden_value() -> Initializer {
let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
let sk = BlsSigningKey::generate(&mut rng);
let pk = VerificationKeyProofOfPossessionForConcatenation::from(&sk);
Initializer {
stake: 1,
parameters: Parameters {
m: 20973,
k: 2422,
phi_f: 0.2,
},
bls_signing_key: sk,
bls_verification_key_proof_of_possession: pk,
#[cfg(feature = "future_snark")]
schnorr_signing_key: None,
#[cfg(feature = "future_snark")]
schnorr_verification_key: None,
}
}
#[test]
fn golden_conversions() {
let value = serde_json::from_str(GOLDEN_JSON)
.expect("This JSON deserialization should not fail");
assert_eq!(golden_value(), value);
let serialized =
serde_json::to_string(&value).expect("This JSON serialization should not fail");
let golden_serialized = serde_json::to_string(&golden_value())
.expect("This JSON serialization should not fail");
assert_eq!(golden_serialized, serialized);
}
const GOLDEN_CBOR_BYTES: &[u8; 490] = &[
1, 164, 101, 115, 116, 97, 107, 101, 1, 102, 112, 97, 114, 97, 109, 115, 163, 97, 109,
25, 81, 237, 97, 107, 25, 9, 118, 101, 112, 104, 105, 95, 102, 251, 63, 201, 153, 153,
153, 153, 153, 154, 98, 115, 107, 152, 32, 24, 64, 24, 129, 24, 87, 24, 121, 24, 27,
24, 239, 24, 221, 24, 215, 2, 24, 103, 24, 45, 24, 207, 24, 207, 24, 201, 24, 157, 24,
163, 24, 81, 24, 47, 24, 156, 14, 24, 168, 24, 24, 24, 137, 15, 24, 203, 24, 106, 24,
183, 24, 73, 24, 88, 14, 24, 242, 24, 207, 98, 112, 107, 162, 98, 118, 107, 152, 96,
24, 143, 24, 161, 24, 255, 24, 48, 24, 78, 24, 57, 24, 204, 24, 220, 24, 25, 24, 221,
24, 164, 24, 252, 24, 248, 14, 24, 56, 24, 126, 24, 186, 24, 135, 24, 228, 24, 188, 24,
145, 24, 181, 24, 52, 24, 200, 24, 97, 24, 99, 24, 213, 24, 46, 0, 24, 199, 24, 193,
24, 89, 24, 187, 24, 88, 24, 29, 24, 135, 24, 173, 24, 244, 24, 86, 24, 36, 24, 83, 24,
54, 24, 67, 24, 164, 6, 24, 137, 24, 94, 24, 72, 6, 24, 105, 24, 128, 24, 128, 24, 93,
24, 48, 24, 176, 11, 4, 24, 246, 24, 138, 24, 48, 24, 180, 24, 133, 24, 90, 24, 142,
24, 192, 24, 24, 24, 193, 24, 111, 24, 142, 24, 31, 24, 76, 24, 111, 24, 110, 24, 234,
24, 153, 24, 90, 24, 208, 24, 192, 24, 31, 24, 124, 24, 95, 24, 102, 24, 49, 24, 158,
24, 99, 24, 52, 24, 220, 24, 165, 24, 94, 24, 251, 24, 68, 24, 69, 24, 121, 16, 24,
224, 24, 194, 99, 112, 111, 112, 152, 96, 24, 168, 24, 50, 24, 233, 24, 193, 15, 24,
136, 24, 65, 24, 72, 24, 123, 24, 148, 24, 129, 24, 176, 24, 38, 24, 198, 24, 209, 24,
47, 24, 28, 24, 204, 24, 176, 24, 144, 24, 57, 24, 251, 24, 42, 24, 28, 24, 66, 24, 76,
24, 89, 24, 97, 24, 158, 24, 63, 24, 54, 24, 198, 24, 194, 24, 176, 24, 135, 24, 221,
14, 24, 185, 24, 197, 24, 225, 24, 202, 24, 98, 24, 243, 24, 74, 24, 233, 24, 225, 24,
143, 24, 151, 24, 147, 24, 177, 24, 170, 24, 117, 24, 66, 24, 165, 24, 66, 24, 62, 24,
33, 24, 216, 24, 232, 24, 75, 24, 68, 24, 114, 24, 195, 22, 24, 100, 24, 65, 24, 44,
24, 198, 4, 24, 166, 24, 102, 24, 233, 24, 253, 24, 240, 24, 59, 24, 175, 24, 60, 24,
117, 24, 142, 24, 114, 24, 140, 24, 122, 17, 24, 87, 24, 110, 24, 187, 1, 17, 10, 24,
195, 24, 154, 13, 24, 249, 24, 86, 24, 54, 24, 226,
];
#[test]
fn cbor_golden_bytes_can_be_decoded() {
let decoded = Initializer::from_bytes(GOLDEN_CBOR_BYTES)
.expect("CBOR golden bytes deserialization should not fail");
assert_eq!(golden_value(), decoded);
}
#[test]
fn cbor_encoding_is_stable() {
let bytes = golden_value()
.to_bytes()
.expect("Initializer serialization should not fail");
assert_eq!(GOLDEN_CBOR_BYTES.as_slice(), bytes.as_slice());
}
}
}