1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Tendermint Light Client JavaScript/WASM interface.
//!
//! This crate exposes some of the [`tendermint-light-client`] crate's
//! functionality to be used from the JavaScript ecosystem.
//!
//! For a detailed example, please see the [`verifier-web` example] in the
//! repository.
//!
//! [`tendermint-light-client`]: https://github.com/informalsystems/tendermint-rs/tree/master/light-client
//! [`verifier-web` example]: https://github.com/informalsystems/tendermint-rs/tree/master/light-client-js/examples/verifier-web

mod utils;

use serde::{Deserialize, Serialize};
use std::time::Duration;
use tendermint::Time;
use tendermint_light_client::components::verifier::{ProdVerifier, Verifier};
use tendermint_light_client::light_client::Options;
use tendermint_light_client::types::{LightBlock, TrustThreshold};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

/// Check whether a given untrusted block can be trusted.
#[wasm_bindgen]
pub fn verify(untrusted: &JsValue, trusted: &JsValue, options: &JsValue, now: &JsValue) -> JsValue {
    let result = deserialize_params(untrusted, trusted, options, now).map(
        |(untrusted, trusted, options, now)| {
            let verifier = ProdVerifier::default();
            verifier.verify(&untrusted, &trusted, &options, now)
        },
    );
    JsValue::from_serde(&result).unwrap()
}

fn deserialize_params(
    untrusted: &JsValue,
    trusted: &JsValue,
    options: &JsValue,
    now: &JsValue,
) -> Result<(LightBlock, LightBlock, Options, Time), Error> {
    let untrusted = untrusted.into_serde().map_err(|e| Error::Serialization {
        param: "untrusted".to_string(),
        msg: e.to_string(),
    })?;

    let trusted = trusted.into_serde().map_err(|e| Error::Serialization {
        param: "trusted".to_string(),
        msg: e.to_string(),
    })?;

    let options = options
        .into_serde::<JsOptions>()
        .map(Into::into)
        .map_err(|e| Error::Serialization {
            param: "options".to_string(),
            msg: e.to_string(),
        })?;

    let now = now.into_serde().map_err(|e| Error::Serialization {
        param: "now".to_string(),
        msg: e.to_string(),
    })?;

    Ok((untrusted, trusted, options, now))
}

/// Errors produced by this crate.
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum Error {
    /// A serialization/deserialization error occurred.
    #[serde(rename = "serialization")]
    Serialization { param: String, msg: String },
}

// Simplified options supplied from JavaScript.
#[derive(Debug, Serialize, Deserialize)]
pub struct JsOptions {
    pub trust_threshold: (u64, u64),
    pub trusting_period: u64,
    pub clock_drift: u64,
}

impl From<JsOptions> for Options {
    fn from(o: JsOptions) -> Self {
        let (num, den) = o.trust_threshold;
        Self {
            trust_threshold: TrustThreshold::new(num, den).unwrap(),
            trusting_period: Duration::from_secs(o.trusting_period),
            clock_drift: Duration::from_secs(o.clock_drift),
        }
    }
}