Skip to main content

alto_types/
wasm.rs

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(&notarized_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}