1use crate::{Block, Finalized, Identity, Notarized, Scheme, Seed, Signature, EPOCH, NAMESPACE};
2use commonware_codec::{DecodeExt, Encode};
3use commonware_consensus::{
4 simplex::elector::Random,
5 types::{Round, View},
6 Viewable,
7};
8use commonware_cryptography::{bls12381::primitives::variant::MinSig, Digestible};
9use commonware_parallel::Sequential;
10use serde::{Deserialize, Serialize};
11use wasm_bindgen::prelude::*;
12
13#[derive(Deserialize, Serialize)]
14pub struct SeedJs {
15 pub view: u64,
16 pub signature: Vec<u8>,
17}
18
19#[derive(Serialize)]
20pub struct ProofJs {
21 pub view: u64,
22 pub parent: u64,
23 pub payload: Vec<u8>,
24 pub signature: Vec<u8>,
25}
26
27#[derive(Serialize)]
28pub struct BlockJs {
29 pub parent: Vec<u8>,
30 pub height: u64,
31 pub timestamp: u64,
32 pub digest: Vec<u8>,
33}
34
35#[derive(Serialize)]
36pub struct NotarizedJs {
37 pub proof: ProofJs,
38 pub block: BlockJs,
39}
40
41#[derive(Serialize)]
42pub struct FinalizedJs {
43 pub proof: ProofJs,
44 pub block: BlockJs,
45}
46
47#[wasm_bindgen]
48pub fn parse_seed(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
49 let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
50 let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
51
52 let Ok(seed) = Seed::decode(bytes.as_ref()) else {
53 return JsValue::NULL;
54 };
55 if !seed.verify(&certificate_verifier) {
56 return JsValue::NULL;
57 }
58 let seed_js = SeedJs {
59 view: seed.view().get(),
60 signature: seed.signature.encode().to_vec(),
61 };
62 serde_wasm_bindgen::to_value(&seed_js).unwrap_or(JsValue::NULL)
63}
64
65#[wasm_bindgen]
66pub fn parse_notarized(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
67 let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
68 let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
69
70 let Ok(notarized) = Notarized::decode(bytes.as_ref()) else {
71 return JsValue::NULL;
72 };
73 if !notarized.verify(&certificate_verifier, &Sequential) {
74 return JsValue::NULL;
75 }
76 let Some(certificate) = notarized.proof.certificate.get() else {
77 return JsValue::NULL;
78 };
79 let notarized_js = NotarizedJs {
80 proof: ProofJs {
81 view: notarized.proof.view().get(),
82 parent: notarized.proof.proposal.parent.get(),
83 payload: notarized.proof.proposal.payload.to_vec(),
84 signature: certificate.vote_signature.encode().to_vec(),
85 },
86 block: BlockJs {
87 parent: notarized.block.parent.to_vec(),
88 height: notarized.block.height.get(),
89 timestamp: notarized.block.timestamp,
90 digest: notarized.block.digest().to_vec(),
91 },
92 };
93 serde_wasm_bindgen::to_value(¬arized_js).unwrap_or(JsValue::NULL)
94}
95
96#[wasm_bindgen]
97pub fn parse_finalized(identity: Vec<u8>, bytes: Vec<u8>) -> JsValue {
98 let identity = Identity::decode(identity.as_ref()).expect("invalid identity");
99 let certificate_verifier = Scheme::certificate_verifier(NAMESPACE, identity);
100 let Ok(finalized) = Finalized::decode(bytes.as_ref()) else {
101 return JsValue::NULL;
102 };
103 if !finalized.verify(&certificate_verifier, &Sequential) {
104 return JsValue::NULL;
105 }
106 let Some(certificate) = finalized.proof.certificate.get() else {
107 return JsValue::NULL;
108 };
109 let finalized_js = FinalizedJs {
110 proof: ProofJs {
111 view: finalized.proof.view().get(),
112 parent: finalized.proof.proposal.parent.get(),
113 payload: finalized.proof.proposal.payload.to_vec(),
114 signature: certificate.vote_signature.encode().to_vec(),
115 },
116 block: BlockJs {
117 parent: finalized.block.parent.to_vec(),
118 height: finalized.block.height.get(),
119 timestamp: finalized.block.timestamp,
120 digest: finalized.block.digest().to_vec(),
121 },
122 };
123 serde_wasm_bindgen::to_value(&finalized_js).unwrap_or(JsValue::NULL)
124}
125
126#[wasm_bindgen]
127pub fn parse_block(bytes: Vec<u8>) -> JsValue {
128 let Ok(block) = Block::decode(bytes.as_ref()) else {
129 return JsValue::NULL;
130 };
131 let block_js = BlockJs {
132 parent: block.parent.to_vec(),
133 height: block.height.get(),
134 timestamp: block.timestamp,
135 digest: block.digest().to_vec(),
136 };
137 serde_wasm_bindgen::to_value(&block_js).unwrap_or(JsValue::NULL)
138}
139
140#[wasm_bindgen]
141pub fn leader_index(seed: JsValue, participants: usize) -> usize {
142 let Ok(seed) = serde_wasm_bindgen::from_value::<SeedJs>(seed) else {
143 return 0;
144 };
145
146 let Ok(signature) = Signature::decode(seed.signature.as_ref()) else {
147 return 0;
148 };
149
150 let round = Round::new(EPOCH, View::new(seed.view));
151 let seed = Seed::new(round, signature);
152
153 Random::select_leader::<MinSig>(
154 round,
155 u32::try_from(participants).expect("too many participants"),
156 (round.view().get() != 1).then_some(seed.signature),
157 )
158 .get() as usize
159}