Skip to main content

keymaster_multisig/
lib.rs

1pub mod arbitrated_pool;
2mod error;
3mod multisig;
4mod types;
5mod version;
6
7pub use arbitrated_pool::*;
8pub use error::{MultisigError, Result};
9pub use multisig::Multisig;
10pub use types::*;
11pub use version::{PROTOCOL, PROTOCOL_VERSION, RELEASE_VERSION, VERSION};
12
13use serde_wasm_bindgen as swbg;
14use wasm_bindgen::prelude::*;
15
16#[wasm_bindgen]
17pub fn build_arbitrated_pool_lock_v4(roles: JsValue) -> std::result::Result<JsValue, JsValue> {
18    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
19    let script = arbitrated_pool::build_arbitrated_pool_lock(&roles)
20        .map_err(|error| JsValue::from_str(&error.to_string()))?;
21    Ok(swbg::to_value(&script)?)
22}
23
24#[wasm_bindgen]
25pub fn build_arbitrated_pool_state_v4(input: JsValue) -> std::result::Result<JsValue, JsValue> {
26    let input: ArbitratedPoolStateInput = swbg::from_value(input)?;
27    let state = arbitrated_pool::build_arbitrated_pool_state(input)
28        .map_err(|error| JsValue::from_str(&error.to_string()))?;
29    Ok(swbg::to_value(&state)?)
30}
31
32#[wasm_bindgen]
33pub fn build_arbitrated_pool_funding_v4(
34    utxos: JsValue,
35    pool_amount: u64,
36    buyer_private_key: JsValue,
37    roles: JsValue,
38    fee_rate: u64,
39) -> std::result::Result<JsValue, JsValue> {
40    let utxos: Vec<Utxo> = swbg::from_value(utxos)?;
41    let buyer_private_key: PrivateKey = swbg::from_value(buyer_private_key)?;
42    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
43    let result = arbitrated_pool::build_arbitrated_pool_funding_tx(
44        &utxos,
45        pool_amount,
46        &buyer_private_key,
47        &roles,
48        fee_rate,
49    )
50    .map_err(|error| JsValue::from_str(&error.to_string()))?;
51    Ok(swbg::to_value(&result)?)
52}
53
54#[wasm_bindgen]
55pub fn build_arbitrated_pool_opening_state_v4(
56    funding_tx: JsValue,
57    pool_amount: u64,
58    roles: JsValue,
59    lock_time: u32,
60    fee_rate: u64,
61) -> std::result::Result<JsValue, JsValue> {
62    let funding_tx: Transaction = swbg::from_value(funding_tx)?;
63    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
64    let state = arbitrated_pool::build_arbitrated_pool_opening_state(
65        &funding_tx,
66        pool_amount,
67        roles,
68        lock_time,
69        fee_rate,
70    )
71    .map_err(|error| JsValue::from_str(&error.to_string()))?;
72    Ok(swbg::to_value(&state)?)
73}
74
75#[wasm_bindgen]
76pub fn build_arbitrated_pool_final_state_v4(
77    input: JsValue,
78) -> std::result::Result<JsValue, JsValue> {
79    let input: ArbitratedPoolStateInput = swbg::from_value(input)?;
80    let state = arbitrated_pool::build_arbitrated_pool_final_state(input)
81        .map_err(|error| JsValue::from_str(&error.to_string()))?;
82    Ok(swbg::to_value(&state)?)
83}
84
85fn wasm_sign_role(
86    state: JsValue,
87    pool_amount: u64,
88    roles: JsValue,
89    key: JsValue,
90    sign: fn(&Transaction, u64, &ArbitratedPoolRoles, &PrivateKey) -> Result<Vec<u8>>,
91) -> std::result::Result<JsValue, JsValue> {
92    let state: Transaction = swbg::from_value(state)?;
93    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
94    let key: PrivateKey = swbg::from_value(key)?;
95    let signature = sign(&state, pool_amount, &roles, &key)
96        .map_err(|error| JsValue::from_str(&error.to_string()))?;
97    Ok(swbg::to_value(&signature)?)
98}
99
100#[wasm_bindgen]
101pub fn sign_arbitrated_pool_as_buyer_v4(
102    state: JsValue,
103    pool_amount: u64,
104    roles: JsValue,
105    key: JsValue,
106) -> std::result::Result<JsValue, JsValue> {
107    wasm_sign_role(
108        state,
109        pool_amount,
110        roles,
111        key,
112        arbitrated_pool::sign_arbitrated_pool_as_buyer,
113    )
114}
115
116#[wasm_bindgen]
117pub fn sign_arbitrated_pool_as_seller_v4(
118    state: JsValue,
119    pool_amount: u64,
120    roles: JsValue,
121    key: JsValue,
122) -> std::result::Result<JsValue, JsValue> {
123    wasm_sign_role(
124        state,
125        pool_amount,
126        roles,
127        key,
128        arbitrated_pool::sign_arbitrated_pool_as_seller,
129    )
130}
131
132#[wasm_bindgen]
133pub fn sign_arbitrated_pool_as_arbiter_v4(
134    state: JsValue,
135    pool_amount: u64,
136    roles: JsValue,
137    key: JsValue,
138) -> std::result::Result<JsValue, JsValue> {
139    wasm_sign_role(
140        state,
141        pool_amount,
142        roles,
143        key,
144        arbitrated_pool::sign_arbitrated_pool_as_arbiter,
145    )
146}
147
148fn wasm_verify_role(
149    state: JsValue,
150    pool_amount: u64,
151    roles: JsValue,
152    signature: JsValue,
153    verify: fn(&Transaction, u64, &ArbitratedPoolRoles, &[u8]) -> Result<bool>,
154) -> std::result::Result<bool, JsValue> {
155    let state: Transaction = swbg::from_value(state)?;
156    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
157    let signature: Vec<u8> = swbg::from_value(signature)?;
158    verify(&state, pool_amount, &roles, &signature)
159        .map_err(|error| JsValue::from_str(&error.to_string()))
160}
161
162#[wasm_bindgen]
163pub fn verify_arbitrated_pool_buyer_signature_v4(
164    state: JsValue,
165    pool_amount: u64,
166    roles: JsValue,
167    signature: JsValue,
168) -> std::result::Result<bool, JsValue> {
169    wasm_verify_role(
170        state,
171        pool_amount,
172        roles,
173        signature,
174        arbitrated_pool::verify_arbitrated_pool_buyer_signature,
175    )
176}
177
178#[wasm_bindgen]
179pub fn verify_arbitrated_pool_seller_signature_v4(
180    state: JsValue,
181    pool_amount: u64,
182    roles: JsValue,
183    signature: JsValue,
184) -> std::result::Result<bool, JsValue> {
185    wasm_verify_role(
186        state,
187        pool_amount,
188        roles,
189        signature,
190        arbitrated_pool::verify_arbitrated_pool_seller_signature,
191    )
192}
193
194#[wasm_bindgen]
195pub fn verify_arbitrated_pool_arbiter_signature_v4(
196    state: JsValue,
197    pool_amount: u64,
198    roles: JsValue,
199    signature: JsValue,
200) -> std::result::Result<bool, JsValue> {
201    wasm_verify_role(
202        state,
203        pool_amount,
204        roles,
205        signature,
206        arbitrated_pool::verify_arbitrated_pool_arbiter_signature,
207    )
208}
209
210type ArbitratedPoolMerge =
211    fn(&Transaction, u64, &ArbitratedPoolRoles, &[u8], &[u8]) -> Result<Transaction>;
212
213fn wasm_merge(
214    state: JsValue,
215    pool_amount: u64,
216    roles: JsValue,
217    first: JsValue,
218    second: JsValue,
219    merge: ArbitratedPoolMerge,
220) -> std::result::Result<JsValue, JsValue> {
221    let state: Transaction = swbg::from_value(state)?;
222    let roles: ArbitratedPoolRoles = swbg::from_value(roles)?;
223    let first: Vec<u8> = swbg::from_value(first)?;
224    let second: Vec<u8> = swbg::from_value(second)?;
225    let merged = merge(&state, pool_amount, &roles, &first, &second)
226        .map_err(|error| JsValue::from_str(&error.to_string()))?;
227    Ok(swbg::to_value(&merged)?)
228}
229
230#[wasm_bindgen]
231pub fn merge_arbitrated_pool_buyer_seller_signatures_v4(
232    state: JsValue,
233    pool_amount: u64,
234    roles: JsValue,
235    buyer: JsValue,
236    seller: JsValue,
237) -> std::result::Result<JsValue, JsValue> {
238    wasm_merge(
239        state,
240        pool_amount,
241        roles,
242        buyer,
243        seller,
244        arbitrated_pool::merge_arbitrated_pool_buyer_seller_signatures,
245    )
246}
247
248#[wasm_bindgen]
249pub fn merge_arbitrated_pool_buyer_arbiter_signatures_v4(
250    state: JsValue,
251    pool_amount: u64,
252    roles: JsValue,
253    buyer: JsValue,
254    arbiter: JsValue,
255) -> std::result::Result<JsValue, JsValue> {
256    wasm_merge(
257        state,
258        pool_amount,
259        roles,
260        buyer,
261        arbiter,
262        arbitrated_pool::merge_arbitrated_pool_buyer_arbiter_signatures,
263    )
264}
265
266#[wasm_bindgen]
267pub fn merge_arbitrated_pool_seller_arbiter_signatures_v4(
268    state: JsValue,
269    pool_amount: u64,
270    roles: JsValue,
271    seller: JsValue,
272    arbiter: JsValue,
273) -> std::result::Result<JsValue, JsValue> {
274    wasm_merge(
275        state,
276        pool_amount,
277        roles,
278        seller,
279        arbiter,
280        arbitrated_pool::merge_arbitrated_pool_seller_arbiter_signatures,
281    )
282}
283
284#[wasm_bindgen]
285pub fn create_multisig(
286    private_keys: JsValue,
287    public_keys: JsValue,
288    m: usize,
289) -> std::result::Result<MultisigWasm, JsValue> {
290    let pub_keys: Vec<PublicKey> = swbg::from_value(public_keys)?;
291    let priv_keys: Option<Vec<PrivateKey>> = swbg::from_value(private_keys).ok();
292
293    let multisig =
294        Multisig::new(priv_keys, pub_keys, m).map_err(|e| JsValue::from_str(&e.to_string()))?;
295
296    Ok(MultisigWasm { multisig })
297}
298
299#[wasm_bindgen]
300pub fn create_locking_script(
301    public_keys: JsValue,
302    m: usize,
303) -> std::result::Result<JsValue, JsValue> {
304    let pub_keys: Vec<PublicKey> = swbg::from_value(public_keys)?;
305
306    if m == 0 || m > pub_keys.len() {
307        return Err(JsValue::from_str("Invalid m value"));
308    }
309    if pub_keys.is_empty() || pub_keys.len() > 20 {
310        return Err(JsValue::from_str("Invalid public keys"));
311    }
312
313    let mut script = vec![0x01 + (m as u8) - 1];
314
315    for pub_key in &pub_keys {
316        script.push(pub_key.key.len() as u8);
317        script.extend(&pub_key.key);
318    }
319
320    script.push(0x01 + (pub_keys.len() as u8) - 1);
321    script.push(0xae);
322
323    Ok(swbg::to_value(&script)?)
324}
325
326#[wasm_bindgen]
327pub fn estimate_multisig_length(m: usize) -> usize {
328    1 + m * (71 + 1)
329}
330
331#[wasm_bindgen]
332pub struct MultisigWasm {
333    multisig: Multisig,
334}
335
336#[wasm_bindgen]
337impl MultisigWasm {
338    #[wasm_bindgen(constructor)]
339    pub fn new(
340        private_keys: JsValue,
341        public_keys: JsValue,
342        m: usize,
343    ) -> std::result::Result<MultisigWasm, JsValue> {
344        create_multisig(private_keys, public_keys, m)
345    }
346
347    pub fn lock(&self) -> std::result::Result<JsValue, JsValue> {
348        let script = self
349            .multisig
350            .lock()
351            .map_err(|e| JsValue::from_str(&e.to_string()))?;
352        Ok(swbg::to_value(&script)?)
353    }
354
355    pub fn sign(
356        &self,
357        transaction: JsValue,
358        input_index: usize,
359    ) -> std::result::Result<JsValue, JsValue> {
360        let tx: Transaction = swbg::from_value(transaction)?;
361        let signatures = self
362            .multisig
363            .sign(&tx, input_index)
364            .map_err(|e| JsValue::from_str(&e.to_string()))?;
365        Ok(swbg::to_value(&signatures)?)
366    }
367
368    pub fn sign_one(
369        &self,
370        transaction: JsValue,
371        input_index: usize,
372        private_key: JsValue,
373    ) -> std::result::Result<JsValue, JsValue> {
374        let tx: Transaction = swbg::from_value(transaction)?;
375        let priv_key: PrivateKey = swbg::from_value(private_key)?;
376
377        let signature = self
378            .multisig
379            .sign_one(&tx, input_index, &priv_key)
380            .map_err(|e| JsValue::from_str(&e.to_string()))?;
381        Ok(swbg::to_value(&signature)?)
382    }
383
384    pub fn estimate_length(&self) -> usize {
385        self.multisig.estimate_length()
386    }
387
388    pub fn create_fake_sign(&self) -> std::result::Result<JsValue, JsValue> {
389        let script = self
390            .multisig
391            .create_fake_sign()
392            .map_err(|e| JsValue::from_str(&e.to_string()))?;
393        Ok(swbg::to_value(&script)?)
394    }
395
396    pub fn build_sign_script(&self, signatures: JsValue) -> std::result::Result<JsValue, JsValue> {
397        let sigs: Vec<Vec<u8>> = swbg::from_value(signatures)?;
398        let script = self
399            .multisig
400            .build_sign_script(&sigs)
401            .map_err(|e| JsValue::from_str(&e.to_string()))?;
402        Ok(swbg::to_value(&script)?)
403    }
404
405    pub fn get_m(&self) -> usize {
406        self.multisig.get_m()
407    }
408
409    pub fn get_n(&self) -> usize {
410        self.multisig.get_n()
411    }
412
413    pub fn get_sig_hash_type(&self) -> u8 {
414        self.multisig.get_sig_hash_type()
415    }
416}
417
418#[cfg(not(feature = "wasm-test"))]
419#[wasm_bindgen(start)]
420pub fn main() {
421    console_error_panic_hook::set_once();
422}
423
424#[wasm_bindgen]
425extern "C" {
426    #[wasm_bindgen(js_namespace = console)]
427    fn error(s: &str);
428}