# lib.rs - source
Source of the Rust file `browser/src/lib.rs`.
Keywords: rust, rustlang, rust-lang

      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
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151

    /*
     * mCaptcha is a PoW based DoS protection software.
     * This is the frontend web component of the mCaptcha system
     * Copyright © 2021 Aravinth Manivnanan <realaravinth@batsense.net>.
     *
     * Use of this source code is governed by Apache 2.0 or MIT license.
     * You shoud have received a copy of MIT and Apache 2.0 along with
     * this program. If not, see <https://spdx.org/licenses/MIT.html> for
     * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
     */
    //! mCaptcha is a proof of work based Denaial-of-Service attack protection system.
    //! This is is a WASM library that you can embed in your frontend code to protect your
    //! service.
    //!
    //! A commercial managed solution is in the works but I'd much rather prefer
    //! folks host their own instances as it will make the more decentralized and free.
    //!
    //! ## Workflow:
    //! mCaptcha workflow in the frontend is simple.
    //! 1. Call service to get a proof of work(PoW) configuration
    //! 2. Call into mCaptcha to get PoW
    //! 3. Send PoW to mCaptcha service
    //! 4. If proof is valid, the service will return a token to the client
    //! 5. Submit token to your backend along with your app data(if any)
    //! 6. In backend, validate client's token with mCaptcha service
    //!
    //! ## Example:
    //!
    //! generate proof-of-work
    //! ```rust
    //!  use mcaptcha_browser::*;
    //!  use pow_sha256::*;
    //!
    //!
    //!  // salt using which PoW should be computed
    //!  const SALT: &str = "yrandomsaltisnotlongenoug";
    //!  // one-time phrase over which PoW should be computed
    //!  const PHRASE: &str = "ironmansucks";
    //!  // and the difficulty factor
    //!  const DIFFICULTY: u32 = 1000;
    //!
    //!  // currently gen_pow() returns a JSON formated string to better communicate
    //!  // with JavaScript. See [PoW<T>][pow_sha256::PoW] for schema
    //!  let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY);
    //!
    //!
    //!  let work: Work = serde_json::from_str(&serialised_work).unwrap();
    //!
    //!  let work = PoWBuilder::default()
    //!      .result(work.result)
    //!      .nonce(work.nonce)
    //!      .build()
    //!      .unwrap();
    //!
    //!  let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap();
    //!  assert!(config.is_valid_proof(&work, &PHRASE.to_string()));
    //!  assert!(config.is_sufficient_difficulty(&work, DIFFICULTY));
    //! ```

    use serde::{Deserialize, Serialize};
    use wasm_bindgen::prelude::*;

    // 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;

    use pow_sha256::{ConfigBuilder, PoW};

    #[derive(Deserialize, Serialize)]
    pub struct Work {
        pub result: String,
        pub nonce: u64,
    }

    impl From<PoW<String>> for Work {
        fn from(p: PoW<String>) -> Self {
            Work {
                result: p.result,
                nonce: p.nonce,
            }
        }
    }

    /// generate proof-of-work
    /// ```rust
    /// fn main() {
    ///    use mcaptcha_browser::*;
    ///    use pow_sha256::*;
    ///
    ///
    ///    // salt using which PoW should be computed
    ///    const SALT: &str = "yrandomsaltisnotlongenoug";
    ///    // one-time phrase over which PoW should be computed
    ///    const PHRASE: &str = "ironmansucks";
    ///    // and the difficulty factor
    ///    const DIFFICULTY: u32 = 1000;
    ///
    ///    // currently gen_pow() returns a JSON formated string to better communicate
    ///    // with JavaScript. See [PoW<T>][pow_sha256::PoW] for schema
    ///    let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY);
    ///
    ///
    ///    let work: Work = serde_json::from_str(&serialised_work).unwrap();
    ///
    ///    let work = PoWBuilder::default()
    ///        .result(work.result)
    ///        .nonce(work.nonce)
    ///        .build()
    ///        .unwrap();
    ///
    ///    let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap();
    ///    assert!(config.is_valid_proof(&work, &PHRASE.to_string()));
    ///    assert!(config.is_sufficient_difficulty(&work, DIFFICULTY));
    /// }
    /// ```
    #[wasm_bindgen]
    pub fn gen_pow(salt: String, phrase: String, difficulty_factor: u32) -> String {
        let config = ConfigBuilder::default().salt(salt).build().unwrap();

        let work = config.prove_work(&phrase, difficulty_factor).unwrap();
        let work: Work = work.into();

        serde_json::to_string(&work).unwrap()
    }

    #[cfg(test)]
    mod tests {
        use super::*;
        use pow_sha256::PoWBuilder;

        const SALT: &str = "yrandomsaltisnotlongenoug";
        const PHRASE: &str = "ironmansucks";
        const DIFFICULTY: u32 = 1000;
        #[test]
        fn it_works() {
            let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY);
            let work: Work = serde_json::from_str(&serialised_work).unwrap();

            let work = PoWBuilder::default()
                .result(work.result)
                .nonce(work.nonce)
                .build()
                .unwrap();

            let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap();
            assert!(config.is_valid_proof(&work, &PHRASE.to_string()));
            assert!(config.is_sufficient_difficulty(&work, DIFFICULTY));
        }
    }