use crate::{Block, Finalized, Identity, Notarized, Scheme, Seed, Signature, EPOCH, NAMESPACE};
use commonware_codec::{DecodeExt, Encode};
use commonware_consensus::{
simplex::elector::Random,
types::{Round, View},
Viewable,
};
use commonware_cryptography::{bls12381::primitives::variant::MinSig, Digestible};
use commonware_parallel::Sequential;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
#[derive(Deserialize, Serialize)]
pub struct SeedJs {
pub view: u64,
pub signature: Vec<u8>,
}
#[derive(Serialize)]
pub struct ProofJs {
pub view: u64,
pub parent: u64,
pub payload: Vec<u8>,
pub signature: Vec<u8>,
}
#[derive(Serialize)]
pub struct BlockJs {
pub parent: Vec<u8>,
pub height: u64,
pub timestamp: u64,
pub digest: Vec<u8>,
}
#[derive(Serialize)]
pub struct NotarizedJs {
pub proof: ProofJs,
pub block: BlockJs,
}
#[derive(Serialize)]
pub struct FinalizedJs {
pub proof: ProofJs,
pub block: BlockJs,
}
#[wasm_bindgen]
pub fn parse_seed(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
let Ok(seed) = Seed::decode(bytes.as_ref()) else {
return JsValue::NULL;
};
if !seed.verify(&certificate_verifier) {
return JsValue::NULL;
}
let seed_js = SeedJs {
view: seed.view().get(),
signature: seed.signature.encode().to_vec(),
};
serde_wasm_bindgen::to_value(&seed_js).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub fn parse_notarized(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
let Ok(notarized) = Notarized::decode(bytes.as_ref()) else {
return JsValue::NULL;
};
if !notarized.verify(&certificate_verifier, &Sequential) {
return JsValue::NULL;
}
let Some(certificate) = notarized.proof.certificate.get() else {
return JsValue::NULL;
};
let notarized_js = NotarizedJs {
proof: ProofJs {
view: notarized.proof.view().get(),
parent: notarized.proof.proposal.parent.get(),
payload: notarized.proof.proposal.payload.to_vec(),
signature: certificate.vote_signature.encode().to_vec(),
},
block: BlockJs {
parent: notarized.block.parent.to_vec(),
height: notarized.block.height.get(),
timestamp: notarized.block.timestamp,
digest: notarized.block.digest().to_vec(),
},
};
serde_wasm_bindgen::to_value(¬arized_js).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub fn parse_finalized(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
let Ok(finalized) = Finalized::decode(bytes.as_ref()) else {
return JsValue::NULL;
};
if !finalized.verify(&certificate_verifier, &Sequential) {
return JsValue::NULL;
}
let Some(certificate) = finalized.proof.certificate.get() else {
return JsValue::NULL;
};
let finalized_js = FinalizedJs {
proof: ProofJs {
view: finalized.proof.view().get(),
parent: finalized.proof.proposal.parent.get(),
payload: finalized.proof.proposal.payload.to_vec(),
signature: certificate.vote_signature.encode().to_vec(),
},
block: BlockJs {
parent: finalized.block.parent.to_vec(),
height: finalized.block.height.get(),
timestamp: finalized.block.timestamp,
digest: finalized.block.digest().to_vec(),
},
};
serde_wasm_bindgen::to_value(&finalized_js).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub fn parse_block(bytes: Vec<u8>) -> JsValue {
let Ok(block) = Block::decode(bytes.as_ref()) else {
return JsValue::NULL;
};
let block_js = BlockJs {
parent: block.parent.to_vec(),
height: block.height.get(),
timestamp: block.timestamp,
digest: block.digest().to_vec(),
};
serde_wasm_bindgen::to_value(&block_js).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub fn leader_index(seed: JsValue, participants: usize) -> usize {
let Ok(seed) = serde_wasm_bindgen::from_value::<SeedJs>(seed) else {
return 0;
};
let Ok(signature) = Signature::decode(seed.signature.as_ref()) else {
return 0;
};
let round = Round::new(EPOCH, View::new(seed.view));
let seed = Seed::new(round, signature);
Random::select_leader::<MinSig>(
round,
u32::try_from(participants).expect("too many participants"),
(round.view().get() != 1).then_some(seed.signature),
)
.get() as usize
}