From fe16c874489c2257deaf151146ca37484275edfe Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Mon, 3 Dec 2018 13:54:33 +0000 Subject: [PATCH 01/33] wasm binding using wasm-pack and wasm-bindgen --- cardano-wallet/.appveyor.yml | 11 + cardano-wallet/.gitignore | 3 + cardano-wallet/.travis.yml | 69 ++ cardano-wallet/Cargo.toml | 40 + cardano-wallet/LICENSE_APACHE | 176 ++++ cardano-wallet/LICENSE_MIT | 25 + cardano-wallet/README.md | 54 ++ cardano-wallet/package.json | 16 + cardano-wallet/src/lib.rs | 903 ++++++++++++++++++ cardano-wallet/src/utils.rs | 17 + cardano-wallet/tests/index.js | 28 + .../tests/password_encrypted_data.rs | 52 + cardano-wallet/tests/wallet.rs | 41 + cardano-wallet/webpack.config.js | 21 + 14 files changed, 1456 insertions(+) create mode 100644 cardano-wallet/.appveyor.yml create mode 100644 cardano-wallet/.gitignore create mode 100644 cardano-wallet/.travis.yml create mode 100644 cardano-wallet/Cargo.toml create mode 100644 cardano-wallet/LICENSE_APACHE create mode 100644 cardano-wallet/LICENSE_MIT create mode 100644 cardano-wallet/README.md create mode 100644 cardano-wallet/package.json create mode 100644 cardano-wallet/src/lib.rs create mode 100644 cardano-wallet/src/utils.rs create mode 100644 cardano-wallet/tests/index.js create mode 100644 cardano-wallet/tests/password_encrypted_data.rs create mode 100644 cardano-wallet/tests/wallet.rs create mode 100644 cardano-wallet/webpack.config.js diff --git a/cardano-wallet/.appveyor.yml b/cardano-wallet/.appveyor.yml new file mode 100644 index 0000000..50910bd --- /dev/null +++ b/cardano-wallet/.appveyor.yml @@ -0,0 +1,11 @@ +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --locked diff --git a/cardano-wallet/.gitignore b/cardano-wallet/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/cardano-wallet/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/cardano-wallet/.travis.yml b/cardano-wallet/.travis.yml new file mode 100644 index 0000000..7a91325 --- /dev/null +++ b/cardano-wallet/.travis.yml @@ -0,0 +1,69 @@ +language: rust +sudo: false + +cache: cargo + +matrix: + include: + + # Builds with wasm-pack. + - rust: beta + env: RUST_BACKTRACE=1 + addons: + firefox: latest + chrome: stable + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + script: + - cargo generate --git . --name testing + # Having a broken Cargo.toml (in that it has curlies in fields) anywhere + # in any of our parent dirs is problematic. + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - wasm-pack build + - wasm-pack test --chrome --firefox --headless + + # Builds on nightly. + - rust: nightly + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" + - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" + + # Builds on beta. + - rust: beta + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + # Note: no enabling the `wee_alloc` feature here because it requires + # nightly for now. diff --git a/cardano-wallet/Cargo.toml b/cardano-wallet/Cargo.toml new file mode 100644 index 0000000..1028524 --- /dev/null +++ b/cardano-wallet/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "cardano-wallet" +version = "0.1.0" +authors = ["Nicolas Di Prima "] + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +cfg-if = "0.1.6" +wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } +serde = "1.0" +serde_derive = "1.0" +cryptoxide = "0.1" +cbor_event = "1.0" +cardano = { path = "../rust/cardano", features = ["generic-serialization"] } + + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.5", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.2", optional = true } + +[dev-dependencies] +wasm-bindgen-test = "0.2" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/cardano-wallet/LICENSE_APACHE b/cardano-wallet/LICENSE_APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/cardano-wallet/LICENSE_APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/cardano-wallet/LICENSE_MIT b/cardano-wallet/LICENSE_MIT new file mode 100644 index 0000000..edfe89d --- /dev/null +++ b/cardano-wallet/LICENSE_MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 Nicolas Di Prima + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/cardano-wallet/README.md b/cardano-wallet/README.md new file mode 100644 index 0000000..1659776 --- /dev/null +++ b/cardano-wallet/README.md @@ -0,0 +1,54 @@ + + +# πŸ¦€πŸ•ΈοΈ `wasm-pack-template` + +A template for kick starting a Rust and WebAssembly project using +[`wasm-pack`](https://github.com/rustwasm/wasm-pack). + +This template is designed for compiling Rust libraries into WebAssembly and +publishing the resulting package to NPM. + +* Want to use the published NPM package in a Website? [Check out + `create-wasm-app`.](https://github.com/rustwasm/create-wasm-app) +* Want to make a monorepo-style Website without publishing to NPM? Check out + [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) + and/or + [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). + +## πŸ”‹ Batteries Included + +* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating + between WebAssembly and JavaScript. +* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) + for logging panic messages to the developer console. +* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized + for small code size. + +## 🚴 Usage + +### πŸ‘ Use `cargo generate` to Clone this Template + +[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) + +``` +cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project +cd my-project +``` + +### πŸ› οΈ Build with `wasm-pack build` + +``` +wasm-pack build +``` + +### πŸ”¬ Test in Headless Browsers with `wasm-pack test` + +``` +wasm-pack test --headless --firefox +``` + +### 🎁 Publish to NPM with `wasm-pack publish` + +``` +wasm-pack publish +``` diff --git a/cardano-wallet/package.json b/cardano-wallet/package.json new file mode 100644 index 0000000..985a43f --- /dev/null +++ b/cardano-wallet/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "build": "webpack", + "serve": "webpack-dev-server" + }, + "devDependencies": { + "text-encoding": "^0.7.0", + "html-webpack-plugin": "^3.2.0", + "webpack": "^4.11.1", + "webpack-cli": "^3.1.1", + "webpack-dev-server": "^3.1.0" + }, + "dependencies": { + "cardano-wallet": "file:pkg" + } +} diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs new file mode 100644 index 0000000..4e36d43 --- /dev/null +++ b/cardano-wallet/src/lib.rs @@ -0,0 +1,903 @@ +extern crate cfg_if; +extern crate serde; +extern crate wasm_bindgen; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate cbor_event; +extern crate cardano; +extern crate cryptoxide; + +mod utils; + +use cfg_if::cfg_if; +use cryptoxide::{chacha20poly1305::ChaCha20Poly1305, hmac::Hmac, pbkdf2::pbkdf2, sha2::Sha512}; +use wasm_bindgen::prelude::*; + +use self::cardano::{ + address, + bip::{bip39, bip44}, + coin, config, fee, hdwallet, tx, txbuild, txutils, util, wallet, +}; + +/// setting of the blockchain +/// +#[wasm_bindgen] +#[derive(Serialize, Deserialize)] +pub struct BlockchainSettings { + /// code of a specific blockchain used to sign transactions and blocks + protocol_magic: config::ProtocolMagic, +} +#[wasm_bindgen] +impl BlockchainSettings { + pub fn to_json(&self) -> Result { + JsValue::from_serde(self).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + pub fn mainnet() -> BlockchainSettings { + BlockchainSettings { + protocol_magic: config::ProtocolMagic::default(), + } + } +} + +/// There is a special function to use when deriving Addresses. This function +/// has been revised to offer stronger properties. This is why there is a +/// V2 derivation scheme. The V1 being the legacy one still used in daedalus +/// now a days. +/// +/// It is strongly advised to use V2 as the V1 is deprecated since April 2018. +/// Its support is already provided for backward compatibility with old +/// addresses. +#[wasm_bindgen] +#[derive(Copy, Clone)] +pub struct DerivationScheme(hdwallet::DerivationScheme); +#[wasm_bindgen] +impl DerivationScheme { + /// deprecated, provided here only for backward compatibility with + /// Daedalus' addresses + pub fn v1() -> DerivationScheme { + DerivationScheme(hdwallet::DerivationScheme::V1) + } + + pub fn v2() -> DerivationScheme { + DerivationScheme(hdwallet::DerivationScheme::V2) + } +} + +/// the entropy associated to mnemonics. This is a bytes representation of the +/// mnemonics the user has to remember how to generate the root key of an +/// HD Wallet. +/// +/// TODO: interface to generate a new entropy +/// +/// # Security considerations +/// +/// * do not store this value without encrypting it; +/// * do not leak the mnemonics; +/// * make sure the user remembers the mnemonics string; +/// +#[wasm_bindgen] +pub struct Entropy(bip39::Entropy); +#[wasm_bindgen] +impl Entropy { + pub fn from_english_mnemonics(mnemonics: &str) -> Result { + Self::from_mnemonics(&bip39::dictionary::ENGLISH, mnemonics) + } + pub fn to_english_mnemonics(&self) -> String { + self.to_mnemonics(&bip39::dictionary::ENGLISH) + } +} +impl Entropy { + fn from_mnemonics( + dic: &D, + mnemonics: &str, + ) -> Result { + let mnemonics = bip39::Mnemonics::from_string(dic, mnemonics) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + bip39::Entropy::from_mnemonics(&mnemonics) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Entropy) + } + fn to_mnemonics(&self, dic: &D) -> String { + format!("{}", self.0.to_mnemonics().to_string(dic)) + } +} + +/* ************************************************************************* * + * Low level Key management * + * ************************************************************************* * + * + * Manage keys by hand. If you don't know what you are doing, prefer to use + * BIP44 style wallets instead. + */ + +/// A given private key. You can use this key to sign transactions. +/// +/// # security considerations +/// +/// * do not store this key without encrypting it; +/// * if leaked anyone can _spend_ a UTxO (Unspent Transaction Output) +/// with it; +/// +#[wasm_bindgen] +pub struct PrivateKey(hdwallet::XPrv); +#[wasm_bindgen] +impl PrivateKey { + /// retrieve a private key from the given hexadecimal string + pub fn from_hex(hex: &str) -> Result { + use std::str::FromStr; + hdwallet::XPrv::from_str(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PrivateKey) + } + /// convert the private key to an hexadecimal string + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + + /// get the public key associated to this private key + pub fn public(&self) -> PublicKey { + PublicKey(self.0.public()) + } + + /// sign some bytes with this private key + pub fn sign(&self, data: &[u8]) -> Signature { + let signature = self.0.sign(data); + Signature(signature) + } + + /// derive this private key with the given index. + /// + /// # Security considerations + /// + /// * prefer the use of DerivationScheme::v2 when possible; + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive(&self, derivation_scheme: DerivationScheme, index: u32) -> PrivateKey { + PrivateKey(self.0.derive(derivation_scheme.0, index)) + } +} + +/// The public key associated to a given private key. +/// +/// It is not possible to sign (and then spend) with a private key. +/// However it is possible to verify a Signature. +/// +/// # Security Consideration +/// +/// * it is rather harmless to leak a public key, in the worst case +/// only the privacy is leaked; +/// +#[wasm_bindgen] +pub struct PublicKey(hdwallet::XPub); +#[wasm_bindgen] +impl PublicKey { + pub fn from_hex(hex: &str) -> Result { + use std::str::FromStr; + hdwallet::XPub::from_str(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PublicKey) + } + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + + pub fn verify(&self, data: &[u8], signature: &Signature) -> bool { + self.0.verify(data, &signature.0) + } + + /// derive this public key with the given index. + /// + /// # Errors + /// + /// If the index is not a soft derivation index (< 0x80000000) then + /// calling this method will fail. + /// + /// # Security considerations + /// + /// * prefer the use of DerivationScheme::v2 when possible; + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive( + &self, + derivation_scheme: DerivationScheme, + index: u32, + ) -> Result { + self.0 + .derive(derivation_scheme.0, index) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PublicKey) + } + + /// get the bootstrap era address. I.E. this is an address without + /// stake delegation. + pub fn bootstrap_era_address(&self) -> Address { + Address(address::ExtendedAddr::new_simple(self.0.clone())) + } +} + +#[wasm_bindgen] +pub struct Address(address::ExtendedAddr); +#[wasm_bindgen] +impl Address { + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + pub fn from_hex(s: &str) -> Result { + use std::str::FromStr; + address::ExtendedAddr::from_str(s) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Address) + } +} + +#[wasm_bindgen] +pub struct Signature(hdwallet::Signature<()>); +#[wasm_bindgen] +impl Signature { + pub fn from_hex(hex: &str) -> Result { + hdwallet::Signature::from_hex(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Signature) + } + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } +} + +/* ************************************************************************* * + * BIP44 style Wallet (Icarus/Yoroi/Rust) * + * ************************************************************************* * + * + * Manage BIP44 Style wallet. This is the preferred way to create new wallet + * as it provides stronger security guarantees and is more flexible. + */ + +#[wasm_bindgen] +pub struct AccountIndex(u32); +#[wasm_bindgen] +impl AccountIndex { + pub fn new(index: u32) -> Result { + if index < bip44::BIP44_SOFT_UPPER_BOUND { + Err(JsValue::from( + "index out of bound. Expected value between 0x80000000 and 0xFFFFFFFF", + )) + } else { + Ok(AccountIndex(index)) + } + } +} +#[wasm_bindgen] +pub struct AddressKeyIndex(u32); +#[wasm_bindgen] +impl AddressKeyIndex { + pub fn new(index: u32) -> Result { + if index >= bip44::BIP44_SOFT_UPPER_BOUND { + Err(JsValue::from( + "index out of bound. Expected value between 0 and 0x80000000", + )) + } else { + Ok(AddressKeyIndex(index)) + } + } +} + +/// Root Private Key of a BIP44 HD Wallet +#[wasm_bindgen] +pub struct Bip44RootPrivateKey { + key: PrivateKey, + derivation_scheme: DerivationScheme, +} +#[wasm_bindgen] +impl Bip44RootPrivateKey { + pub fn new(key: PrivateKey, derivation_scheme: DerivationScheme) -> Bip44RootPrivateKey { + Bip44RootPrivateKey { + key: key, + derivation_scheme: derivation_scheme, + } + } + + /// recover a wallet from the given mnemonic words and the given password + /// + /// To recover an icarus wallet: + /// * 15 mnemonic words; + /// * empty password; + /// + pub fn recover(entropy: &Entropy, password: &str) -> Result { + let mut bytes = [0; hdwallet::XPRV_SIZE]; + wallet::keygen::generate_seed(&entropy.0, password.as_bytes(), &mut bytes); + let key = PrivateKey(hdwallet::XPrv::normalize_bytes(bytes)); + + let rpk = Bip44RootPrivateKey { + key: key, + derivation_scheme: DerivationScheme::v2(), + }; + + Ok(rpk) + } + + pub fn bip44_account(&self, index: AccountIndex) -> Bip44AccountPrivate { + Bip44AccountPrivate { + key: self + .key + .derive(self.derivation_scheme, bip44::BIP44_PURPOSE) + .derive(self.derivation_scheme, bip44::BIP44_COIN_TYPE) + .derive(self.derivation_scheme, index.0), + derivation_scheme: self.derivation_scheme, + } + } +} + +#[wasm_bindgen] +pub struct Bip44AccountPrivate { + key: PrivateKey, + derivation_scheme: DerivationScheme, +} +#[wasm_bindgen] +impl Bip44AccountPrivate { + pub fn new(key: PrivateKey, derivation_scheme: DerivationScheme) -> Bip44AccountPrivate { + Bip44AccountPrivate { + key: key, + derivation_scheme: derivation_scheme, + } + } + pub fn public(&self) -> Bip44AccountPublic { + Bip44AccountPublic { + key: self.key.public(), + derivation_scheme: self.derivation_scheme, + } + } + pub fn address_key(&self, internal: bool, index: AddressKeyIndex) -> PrivateKey { + self.key + .derive(self.derivation_scheme, if internal { 1 } else { 0 }) + .derive(self.derivation_scheme, index.0) + } +} + +#[wasm_bindgen] +pub struct Bip44AccountPublic { + key: PublicKey, + derivation_scheme: DerivationScheme, +} +#[wasm_bindgen] +impl Bip44AccountPublic { + pub fn new(key: PublicKey, derivation_scheme: DerivationScheme) -> Bip44AccountPublic { + Bip44AccountPublic { + key: key, + derivation_scheme: derivation_scheme, + } + } + pub fn address_key( + &self, + internal: bool, + index: AddressKeyIndex, + ) -> Result { + self.key + .derive(self.derivation_scheme, if internal { 1 } else { 0 })? + .derive(self.derivation_scheme, index.0) + } +} + +/* ************************************************************************* * + * Daedalus Wallet Compatibility * + * ************************************************************************* * + * + * Provide tooling for compatibility with Daedalus wallets. If you are creating + * a new wallet, prefer Bip44 way. + */ + +#[wasm_bindgen] +pub struct DaedalusWallet(PrivateKey); +#[wasm_bindgen] +impl DaedalusWallet { + pub fn recover(mnemonics: &str) -> Result { + let mnemonics = bip39::Mnemonics::from_string(&bip39::dictionary::ENGLISH, mnemonics) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + let entropy = bip39::Entropy::from_mnemonics(&mnemonics) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + let entropy_bytes = cbor_event::Value::Bytes(Vec::from(entropy.as_ref())); + let entropy_cbor = + cbor!(&entropy_bytes).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + let seed: Vec = { + use cryptoxide::digest::Digest; + let mut blake2b = cryptoxide::blake2b::Blake2b::new(32); + blake2b.input(&entropy_cbor); + let mut out = [0; 32]; + blake2b.result(&mut out); + cbor_event::se::Serializer::new_vec() + .write_bytes(&Vec::from(&out[..])) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))? + .finalize() + }; + + let key = PrivateKey(hdwallet::XPrv::generate_from_daedalus_seed(&seed)); + + let rpk = DaedalusWallet(key); + Ok(rpk) + } +} + +/* ************************************************************************* * + * Transaction Builder * + * ************************************************************************* * + * + * New transaction build engine + */ + +#[wasm_bindgen] +pub struct CoinDiff(coin::CoinDiff); +#[wasm_bindgen] +impl CoinDiff { + pub fn is_zero(&self) -> bool { + match self.0 { + coin::CoinDiff::Zero => true, + _ => false, + } + } + pub fn is_negative(&self) -> bool { + match self.0 { + coin::CoinDiff::Negative(_) => true, + _ => false, + } + } + pub fn is_positive(&self) -> bool { + match self.0 { + coin::CoinDiff::Positive(_) => true, + _ => false, + } + } + + pub fn value(&self) -> Coin { + match self.0 { + coin::CoinDiff::Positive(coin) => Coin(coin), + coin::CoinDiff::Zero => Coin::new(), + coin::CoinDiff::Negative(coin) => Coin(coin), + } + } +} + +#[wasm_bindgen] +#[derive(Copy, Clone)] +pub struct Coin(coin::Coin); +impl serde::Serialize for Coin { + #[inline] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let v: u64 = *self.0; + serializer.serialize_str(&format!("{}", v)) + } +} +struct CoinVisitor(); +impl<'de> serde::de::Visitor<'de> for CoinVisitor { + type Value = Coin; + + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Lovelace Ada") + } + + fn visit_str<'a, E>(self, v: &'a str) -> std::result::Result + where + E: serde::de::Error, + { + let i: u64 = match v.parse::() { + Ok(v) => v, + Err(err) => return Err(E::custom(format!("{:?}", err))), + }; + match coin::Coin::new(i) { + Err(err) => Err(E::custom(format!("{}", err))), + Ok(h) => Ok(Coin(h)), + } + } +} +impl<'de> serde::Deserialize<'de> for Coin { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(CoinVisitor()) + } +} +#[wasm_bindgen] +impl Coin { + #[wasm_bindgen(constructor)] + pub fn new() -> Coin { + Coin(coin::Coin::zero()) + } + + pub fn from_str(s: &str) -> Result { + s.parse() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Coin) + } + + pub fn to_str(&self) -> String { + format!("{}", self.0) + } + + pub fn from(ada: u32, lovelace: u32) -> Result { + let value = (ada as u64 * 1_000_000) + (lovelace as u64); + coin::Coin::new(value) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Coin) + } + + pub fn ada(&self) -> u32 { + let v = *self.0 / 1_000_000; + assert!(v < 0xFFFF_FFFF); + v as u32 + } + + pub fn lovelace(&self) -> u32 { + (*self.0 % 1_000_000) as u32 + } + + pub fn add(&self, other: &Coin) -> Result { + use std::ops::Add; + self.0 + .add(other.0) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Coin) + } +} + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +pub struct TxoPointer { + id: tx::TxId, + index: u32, +} +impl TxoPointer { + fn convert(&self) -> tx::TxoPointer { + tx::TxoPointer { + id: self.id, + index: self.index, + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct TxOut { + address: address::ExtendedAddr, + value: Coin, +} +impl TxOut { + fn convert(&self) -> tx::TxOut { + tx::TxOut { + address: self.address.clone(), + value: self.value.0, + } + } +} + +#[wasm_bindgen] +pub struct Transaction(tx::Tx); +#[wasm_bindgen] +impl Transaction { + pub fn id(&self) -> String { + format!("{}", self.0.id()) + } + pub fn to_json(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + pub fn to_hex(&self) -> Result { + let bytes = cbor!(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + Ok(util::hex::encode(&bytes)) + } + pub fn to_base58(&self) -> Result { + let bytes = cbor!(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + Ok(util::base58::encode(&bytes)) + } +} + +#[wasm_bindgen] +pub struct SignedTransaction(tx::TxAux); +#[wasm_bindgen] +impl SignedTransaction { + pub fn id(&self) -> String { + format!("{}", self.0.tx.id()) + } + pub fn to_json(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + pub fn to_hex(&self) -> Result { + let bytes = cbor!(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + Ok(util::hex::encode(&bytes)) + } + pub fn to_base58(&self) -> Result { + let bytes = cbor!(&self.0).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + Ok(util::base58::encode(&bytes)) + } +} + +#[wasm_bindgen] +pub struct LinearFeeAlgorithm(fee::LinearFee); +#[wasm_bindgen] +impl LinearFeeAlgorithm { + pub fn default() -> LinearFeeAlgorithm { + LinearFeeAlgorithm(fee::LinearFee::default()) + } +} + +#[wasm_bindgen] +pub struct OutputPolicy(txutils::OutputPolicy); +#[wasm_bindgen] +impl OutputPolicy { + pub fn change_to_one_address(address: Address) -> OutputPolicy { + OutputPolicy(txutils::OutputPolicy::One(address.0)) + } +} + +#[wasm_bindgen] +pub struct TransactionBuilder(txbuild::TxBuilder); +#[wasm_bindgen] +impl TransactionBuilder { + #[wasm_bindgen(constructor)] + pub fn new() -> TransactionBuilder { + TransactionBuilder(txbuild::TxBuilder::new()) + } + + pub fn add_input(&mut self, input_ptr: &JsValue, value: Coin) -> Result<(), JsValue> { + let txo_pointer: TxoPointer = input_ptr + .into_serde() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + + self.0.add_input(&txo_pointer.convert(), value.0); + Ok(()) + } + + pub fn get_input_total(&self) -> Result { + self.0 + .get_input_total() + .map(Coin) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn add_output(&mut self, output: &JsValue) -> Result<(), JsValue> { + let output: TxOut = output + .into_serde() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + self.0.add_output_value(&output.convert()); + Ok(()) + } + + pub fn apply_output_policy( + &mut self, + fee_algorithm: &LinearFeeAlgorithm, + policy: &OutputPolicy, + ) -> Result { + self.0 + .add_output_policy(&fee_algorithm.0, &policy.0) + .map(|all_txout| { + all_txout + .into_iter() + .map(|txout| TxOut { + address: txout.address, + value: Coin(txout.value), + }) + .collect::>() + }) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .and_then(|v| { + JsValue::from_serde(&v).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + }) + } + + pub fn get_output_total(&self) -> Result { + self.0 + .get_output_total() + .map(Coin) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn estimate_fee(&self, fee_algorithm: &LinearFeeAlgorithm) -> Result { + self.0 + .calculate_fee(&fee_algorithm.0) + .map(|fee| Coin(fee.to_coin())) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn get_balance(&self, fee_algorithm: &LinearFeeAlgorithm) -> Result { + self.0 + .balance(&fee_algorithm.0) + .map(CoinDiff) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn get_balance_without_fees(&self) -> Result { + self.0 + .balance_without_fees() + .map(CoinDiff) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn make_transaction(self) -> Result { + self.0 + .make_tx() + .map(Transaction) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } +} + +#[wasm_bindgen] +pub struct TransactionFinalized { + tx_id: tx::TxId, + finalized: txbuild::TxFinalized, +} +#[wasm_bindgen] +impl TransactionFinalized { + pub fn new(transaction: Transaction) -> TransactionFinalized { + TransactionFinalized { + tx_id: transaction.0.id(), + finalized: txbuild::TxFinalized::new(transaction.0), + } + } + + pub fn sign( + &mut self, + blockchain_settings: &BlockchainSettings, + key: &PrivateKey, + ) -> Result<(), JsValue> { + let signature = + tx::TxInWitness::new(blockchain_settings.protocol_magic, &key.0, &self.tx_id); + self.finalized + .add_witness(signature) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + pub fn finalize(self) -> Result { + self.finalized + .make_txaux() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(SignedTransaction) + } +} + +/* ************************************************************************* * + * Password Encrypted Data * + * ************************************************************************* * + * + * provide a function with strong enough parameter to encrypt data in a safe + * and secure manner. + */ + +mod password_encryption_parameter { + pub const ITER: u32 = 19_162; + pub const SALT_SIZE: usize = 32; + pub const NONCE_SIZE: usize = 12; + pub const KEY_SIZE: usize = 32; + pub const TAG_SIZE: usize = 16; + + pub const METADATA_SIZE: usize = SALT_SIZE + NONCE_SIZE + TAG_SIZE; + + pub const SALT_START: usize = 0; + pub const SALT_END: usize = SALT_START + SALT_SIZE; + pub const NONCE_START: usize = SALT_END; + pub const NONCE_END: usize = NONCE_START + NONCE_SIZE; + pub const TAG_START: usize = NONCE_END; + pub const TAG_END: usize = TAG_START + TAG_SIZE; + pub const ENCRYPTED_START: usize = TAG_END; +} + +/// encrypt the given data with a password, a salt and a nonce +/// +/// Salt: must be 32 bytes long; +/// Nonce: must be 12 bytes long; +/// +#[wasm_bindgen] +pub fn password_encrypt( + password: &str, + salt: &[u8], + nonce: &[u8], + data: &[u8], +) -> Result { + let password = password.as_bytes(); + if salt.len() != password_encryption_parameter::SALT_SIZE { + return Err(JsValue::from("Invalid Salt Size, expected 32 bytes")); + } + if nonce.len() != password_encryption_parameter::NONCE_SIZE { + return Err(JsValue::from("Invalid Nonce Size, expected 12 bytes")); + } + + let key = { + let mut mac = Hmac::new(Sha512::new(), &password); + let mut key = [0u8; password_encryption_parameter::KEY_SIZE]; + pbkdf2( + &mut mac, + &salt[..], + password_encryption_parameter::ITER, + &mut key, + ); + key + }; + + let mut tag = [0u8; password_encryption_parameter::TAG_SIZE]; + let mut encrypted: Vec = std::iter::repeat(0).take(data.len()).collect(); + { + ChaCha20Poly1305::new(&key, &nonce, &[]).encrypt(&data, &mut encrypted, &mut tag); + } + + let mut output = Vec::with_capacity(data.len() + password_encryption_parameter::METADATA_SIZE); + output.extend_from_slice(&salt); + output.extend_from_slice(&nonce); + output.extend_from_slice(&tag); + output.extend_from_slice(&encrypted); + + JsValue::from_serde(&output).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) +} + +/// decrypt the data with the password +/// +#[wasm_bindgen] +pub fn password_decrypt(password: &str, encrypted_data: &[u8]) -> Result { + if encrypted_data.len() <= password_encryption_parameter::METADATA_SIZE { + return Err(JsValue::from_str("Not enough data to decrypt")); + } + + let password = password.as_bytes(); + let salt = &encrypted_data + [password_encryption_parameter::SALT_START..password_encryption_parameter::SALT_END]; + let nonce = &encrypted_data + [password_encryption_parameter::NONCE_START..password_encryption_parameter::NONCE_END]; + let tag = &encrypted_data + [password_encryption_parameter::TAG_START..password_encryption_parameter::TAG_END]; + let encrypted = &encrypted_data[password_encryption_parameter::ENCRYPTED_START..]; + + let key = { + let mut mac = Hmac::new(Sha512::new(), &password); + let mut key = [0u8; password_encryption_parameter::KEY_SIZE]; + pbkdf2( + &mut mac, + &salt[..], + password_encryption_parameter::ITER, + &mut key, + ); + key + }; + + let mut decrypted: Vec = std::iter::repeat(0).take(encrypted.len()).collect(); + let decryption_succeed = + { ChaCha20Poly1305::new(&key, &nonce, &[]).decrypt(&encrypted, &mut decrypted, &tag) }; + + if decryption_succeed { + JsValue::from_serde(&decrypted).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } else { + Err(JsValue::from_str("Cannot decrypt the data")) + } +} + +cfg_if! { + // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global + // allocator. + if #[cfg(feature = "wee_alloc")] { + extern crate wee_alloc; + #[global_allocator] + static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + } +} diff --git a/cardano-wallet/src/utils.rs b/cardano-wallet/src/utils.rs new file mode 100644 index 0000000..2ffc954 --- /dev/null +++ b/cardano-wallet/src/utils.rs @@ -0,0 +1,17 @@ +use cfg_if::cfg_if; + +cfg_if! { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + if #[cfg(feature = "console_error_panic_hook")] { + extern crate console_error_panic_hook; + pub use self::console_error_panic_hook::set_once as set_panic_hook; + } else { + #[inline] + pub fn set_panic_hook() {} + } +} diff --git a/cardano-wallet/tests/index.js b/cardano-wallet/tests/index.js new file mode 100644 index 0000000..c273c5b --- /dev/null +++ b/cardano-wallet/tests/index.js @@ -0,0 +1,28 @@ +// Note that a dynamic `import` statement here is required due to +// webpack/webpack#6615, but in theory `import { greet } from './hello_world';` +// will work here one day as well! +const Wallet = import('cardano-wallet'); + +let RustWallet = null; + +Wallet + .then(Wallet => { + console.log("loaded..."); + + const MNEMONICS = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; + const PASSWORD = "Cardano Rust for the winners!"; + + let entropy = Wallet.Entropy.from_english_mnemonics(MNEMONICS); + let wallet = Wallet.Bip44RootPrivateKey.recover(entropy, PASSWORD); + + let account = wallet.bip44_account(Wallet.AccountIndex.new(0 | 0x80000000)); + let account_public = account.public(); + + let key_prv = account.address_key(false, Wallet.AddressKeyIndex.new(0)); + let key_pub = account_public.address_key(false, Wallet.AddressKeyIndex.new(0)); + + let address = key_pub.bootstrap_era_address(); + + console.log("Address m/bip44/ada/'0/0/0", address.to_hex()); + }) + .catch(console.error); diff --git a/cardano-wallet/tests/password_encrypted_data.rs b/cardano-wallet/tests/password_encrypted_data.rs new file mode 100644 index 0000000..aa4d650 --- /dev/null +++ b/cardano-wallet/tests/password_encrypted_data.rs @@ -0,0 +1,52 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate cardano_wallet; +extern crate wasm_bindgen; +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +use cardano_wallet::*; + +wasm_bindgen_test_configure!(run_in_browser); + +const MESSAGE: &'static str = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; +const PASSWORD: &'static str = "Cardano Rust for the winners!"; +const SALT: [u8; 32] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, +]; +const NONCE: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]; + +#[wasm_bindgen_test] +fn encrypt_decrypt_with_password_success() { + let encrypted = password_encrypt(PASSWORD, &SALT, &NONCE, MESSAGE.as_bytes()).unwrap(); + let encrypted: Vec = encrypted.into_serde().unwrap(); + let decrypted = password_decrypt(PASSWORD, &encrypted).unwrap(); + let decrypted: Vec = decrypted.into_serde().unwrap(); + + assert_eq!(MESSAGE.as_bytes(), decrypted.as_slice()); +} +#[wasm_bindgen_test] +fn encrypt_with_password_invalid_salt() { + const INVALID_SALT: [u8; 31] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, + ]; + + assert!(password_encrypt(PASSWORD, &INVALID_SALT, &NONCE, MESSAGE.as_bytes()).is_err()); +} +#[wasm_bindgen_test] +fn encrypt_with_password_invalid_nonce() { + const INVALID_NONCE: [u8; 13] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]; + + assert!(password_encrypt(PASSWORD, &SALT, &INVALID_NONCE, MESSAGE.as_bytes()).is_err()); +} +#[wasm_bindgen_test] +fn encrypt_decrypt_with_password_invalid_password() { + const INVALID_PASSWORD: &'static str = "This is just so wrong..."; + + let encrypted = password_encrypt(PASSWORD, &SALT, &NONCE, MESSAGE.as_bytes()).unwrap(); + let encrypted: Vec = encrypted.into_serde().unwrap(); + assert!(password_decrypt(INVALID_PASSWORD, &encrypted).is_err()); +} diff --git a/cardano-wallet/tests/wallet.rs b/cardano-wallet/tests/wallet.rs new file mode 100644 index 0000000..ce0e60c --- /dev/null +++ b/cardano-wallet/tests/wallet.rs @@ -0,0 +1,41 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate cardano_wallet; +extern crate wasm_bindgen; +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +use cardano_wallet::*; + +wasm_bindgen_test_configure!(run_in_browser); + +const MNEMONICS: &'static str = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; +const ENTROPY: [u8;20] = [ + 0x34, 0x44, 0x45, 0xbd, 0x73, 0xda, 0x93, 0x20, 0xcb, 0x34, 0xdc, 0x8f, 0x78, 0xaa, 0x80, 0xc9, + 0x3e, 0x55, 0x6e, 0x9c, +]; +const PASSWORD: &'static str = "Cardano Rust for the winners!"; + +#[wasm_bindgen_test] +fn mnemonics_invalid_checksum() { + const INVALID_MNEMONICS: &'static str = "crowd captain hungry tray zero motor coast oppose zero zero parent mystery torch resemble abandon"; + assert!(Entropy::from_english_mnemonics(INVALID_MNEMONICS).is_err()); +} +#[wasm_bindgen_test] +fn mnemonics_invalid_length() { + const INVALID_MNEMONICS: &'static str = "crowd captain hungry tray zero motor coast oppose"; + assert!(Entropy::from_english_mnemonics(INVALID_MNEMONICS).is_err()); +} +#[wasm_bindgen_test] +fn recover_mnemonics() { + let entropy = Entropy::from_english_mnemonics(MNEMONICS).unwrap(); + // TODO: check entropy +} + +#[wasm_bindgen_test] +fn recover_root_key() { + let entropy = Entropy::from_english_mnemonics(MNEMONICS).unwrap(); + let root_key = Bip44RootPrivateKey::recover(&entropy, PASSWORD).unwrap(); +} diff --git a/cardano-wallet/webpack.config.js b/cardano-wallet/webpack.config.js new file mode 100644 index 0000000..65e6f9f --- /dev/null +++ b/cardano-wallet/webpack.config.js @@ -0,0 +1,21 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const webpack = require('webpack'); + +module.exports = { + entry: './tests/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + }, + plugins: [ + new HtmlWebpackPlugin(), + // Have this example work in Edge which doesn't ship `TextEncoder` or + // `TextDecoder` at this time. + new webpack.ProvidePlugin({ + TextDecoder: ['text-encoding', 'TextDecoder'], + TextEncoder: ['text-encoding', 'TextEncoder'] + }) + ], + mode: 'development' +}; From 12de03a9ac0be2c9f91c486e6ea9ce3a4a5d51e4 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Mon, 3 Dec 2018 13:56:53 +0000 Subject: [PATCH 02/33] udate the rust module --- rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust b/rust index 13e342a..8b441cb 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit 13e342a3b7cd99676e330d3f36368c063f76544b +Subproject commit 8b441cbbeba32ada20d065399083a16d85a5fe29 From 73695b6297256b914a169c654dff62123e4db462 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 11:08:59 +0100 Subject: [PATCH 03/33] update the rust toolchain --- cardano-wallet/Cargo.toml | 2 +- rust | 2 +- rust-toolchain | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 rust-toolchain diff --git a/cardano-wallet/Cargo.toml b/cardano-wallet/Cargo.toml index 1028524..6b0a65e 100644 --- a/cardano-wallet/Cargo.toml +++ b/cardano-wallet/Cargo.toml @@ -15,7 +15,7 @@ wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } serde = "1.0" serde_derive = "1.0" cryptoxide = "0.1" -cbor_event = "1.0" +cbor_event = "^2.1.2" cardano = { path = "../rust/cardano", features = ["generic-serialization"] } diff --git a/rust b/rust index 8b441cb..2f8d57f 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit 8b441cbbeba32ada20d065399083a16d85a5fe29 +Subproject commit 2f8d57f325b47997f466f2e274eec4ee37909843 diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index ea64626..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2018-06-05 From c5ffcbf3b90d8cc212f401afa834170021e29f7e Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 11:12:22 +0100 Subject: [PATCH 04/33] now because of the address discrimination we need to provide the Settings too --- cardano-wallet/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 4e36d43..07b6651 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -237,8 +237,11 @@ impl PublicKey { /// get the bootstrap era address. I.E. this is an address without /// stake delegation. - pub fn bootstrap_era_address(&self) -> Address { - Address(address::ExtendedAddr::new_simple(self.0.clone())) + pub fn bootstrap_era_address(&self, blockchain_settings: &BlockchainSettings) -> Address { + Address(address::ExtendedAddr::new_simple( + self.0.clone(), + blockchain_settings.protocol_magic.into(), + )) } } From 122b36d5de01a967bc61d964718e2e6b89542db3 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 11:12:43 +0100 Subject: [PATCH 05/33] update the cbor_event serialisation to the new version --- cardano-wallet/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 07b6651..b891701 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -433,10 +433,10 @@ impl DaedalusWallet { blake2b.input(&entropy_cbor); let mut out = [0; 32]; blake2b.result(&mut out); - cbor_event::se::Serializer::new_vec() - .write_bytes(&Vec::from(&out[..])) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))? - .finalize() + let mut se = cbor_event::se::Serializer::new_vec(); + se.write_bytes(&Vec::from(&out[..])) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + se.finalize() }; let key = PrivateKey(hdwallet::XPrv::generate_from_daedalus_seed(&seed)); From 2da6efafc688c0f6781f1ffb5c08104fdbd4e7a8 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 12:00:59 +0100 Subject: [PATCH 06/33] add a bit of documentation --- cardano-wallet/src/lib.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index b891701..73c2c2f 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -22,6 +22,8 @@ use self::cardano::{ /// setting of the blockchain /// +/// This includes the `ProtocolMagic` a discriminant value to differentiate +/// different instances of the cardano blockchain (Mainnet, Testnet... ). #[wasm_bindgen] #[derive(Serialize, Deserialize)] pub struct BlockchainSettings { @@ -30,9 +32,24 @@ pub struct BlockchainSettings { } #[wasm_bindgen] impl BlockchainSettings { + /// serialize into a JsValue object. Allowing the client to store the settings + /// or see changes in the settings or change the settings. + /// + /// Note that this is not recommended to change the settings on the fly. Doing + /// so you might not be able to recover your funds anymore or to send new + /// transactions. pub fn to_json(&self) -> Result { JsValue::from_serde(self).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) } + + /// retrieve the object from a JsValue. + pub fn from_json(value: JsValue) -> Result { + value + .into_serde() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + /// default settings to work with Cardano Mainnet pub fn mainnet() -> BlockchainSettings { BlockchainSettings { protocol_magic: config::ProtocolMagic::default(), @@ -59,6 +76,7 @@ impl DerivationScheme { DerivationScheme(hdwallet::DerivationScheme::V1) } + /// the recommended settings pub fn v2() -> DerivationScheme { DerivationScheme(hdwallet::DerivationScheme::V2) } @@ -80,6 +98,8 @@ impl DerivationScheme { pub struct Entropy(bip39::Entropy); #[wasm_bindgen] impl Entropy { + /// retrieve the initial entropy of a wallet from the given + /// english mnemonics. pub fn from_english_mnemonics(mnemonics: &str) -> Result { Self::from_mnemonics(&bip39::dictionary::ENGLISH, mnemonics) } @@ -357,10 +377,9 @@ impl Bip44RootPrivateKey { } } -#[wasm_bindgen] pub struct Bip44AccountPrivate { - key: PrivateKey, - derivation_scheme: DerivationScheme, + pub key: PrivateKey, + pub derivation_scheme: DerivationScheme, } #[wasm_bindgen] impl Bip44AccountPrivate { @@ -383,10 +402,9 @@ impl Bip44AccountPrivate { } } -#[wasm_bindgen] pub struct Bip44AccountPublic { - key: PublicKey, - derivation_scheme: DerivationScheme, + pub key: PublicKey, + pub derivation_scheme: DerivationScheme, } #[wasm_bindgen] impl Bip44AccountPublic { @@ -619,6 +637,7 @@ impl Transaction { } } +/// a signed transaction, ready to be sent to the network. #[wasm_bindgen] pub struct SignedTransaction(tx::TxAux); #[wasm_bindgen] From 336cb0fc201b8a2979fc5670063bd2c9176aac26 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 16:46:48 +0100 Subject: [PATCH 07/33] changes in the API to facilitate its usage --- cardano-wallet/src/lib.rs | 155 ++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 21 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 73c2c2f..e73db45 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -17,7 +17,7 @@ use wasm_bindgen::prelude::*; use self::cardano::{ address, bip::{bip39, bip44}, - coin, config, fee, hdwallet, tx, txbuild, txutils, util, wallet, + coin, config, fee, hash, hdpayload, hdwallet, tx, txbuild, txutils, util, wallet, }; /// setting of the blockchain @@ -43,7 +43,7 @@ impl BlockchainSettings { } /// retrieve the object from a JsValue. - pub fn from_json(value: JsValue) -> Result { + pub fn from_json(value: JsValue) -> Result { value .into_serde() .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) @@ -140,6 +140,7 @@ impl Entropy { /// with it; /// #[wasm_bindgen] +#[derive(Clone)] pub struct PrivateKey(hdwallet::XPrv); #[wasm_bindgen] impl PrivateKey { @@ -266,6 +267,7 @@ impl PublicKey { } #[wasm_bindgen] +#[derive(Serialize, Deserialize, Clone)] pub struct Address(address::ExtendedAddr); #[wasm_bindgen] impl Address { @@ -377,9 +379,10 @@ impl Bip44RootPrivateKey { } } +#[wasm_bindgen] pub struct Bip44AccountPrivate { - pub key: PrivateKey, - pub derivation_scheme: DerivationScheme, + key: PrivateKey, + derivation_scheme: DerivationScheme, } #[wasm_bindgen] impl Bip44AccountPrivate { @@ -402,9 +405,10 @@ impl Bip44AccountPrivate { } } +#[wasm_bindgen] pub struct Bip44AccountPublic { - pub key: PublicKey, - pub derivation_scheme: DerivationScheme, + key: PublicKey, + derivation_scheme: DerivationScheme, } #[wasm_bindgen] impl Bip44AccountPublic { @@ -464,6 +468,46 @@ impl DaedalusWallet { } } +#[wasm_bindgen] +pub struct DaedalusAddressChecker { + wallet: PrivateKey, + payload_key: hdpayload::HDKey, +} +#[wasm_bindgen] +impl DaedalusAddressChecker { + /// create a new address checker for the given daedalus address + pub fn new(wallet: &DaedalusWallet) -> Self { + let wallet = wallet.0.clone(); + let payload_key = hdpayload::HDKey::new(&wallet.0.public()); + DaedalusAddressChecker { + wallet, + payload_key, + } + } + + /// check that we own the given address. + /// + /// This is only possible like this because some payload is embedded in the + /// address that only our wallet can decode. Once decoded we can retrieve + /// the associated private key. + /// + /// The return private key is the key needed to sign the transaction to unlock + /// UTxO associated to the address. + pub fn check_address(&self, address: &Address) -> Option { + if let Some(hdpa) = &address.0.attributes.derivation_path.clone() { + if let Ok(path) = self.payload_key.decrypt_path(hdpa) { + let mut key = self.wallet.clone(); + for index in path.iter() { + key = key.derive(DerivationScheme::v1(), *index); + } + return Some(key); + } + } + + None + } +} + /* ************************************************************************* * * Transaction Builder * * ************************************************************************* * @@ -589,34 +633,89 @@ impl Coin { } } +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +pub struct TransactionId(tx::TxId); +impl TransactionId { + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + pub fn from_hex(s: &str) -> Result { + use std::str::FromStr; + hash::Blake2b256::from_str(s) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(TransactionId) + } + fn convert(&self) -> tx::TxId { + self.0.clone() + } +} + +#[wasm_bindgen] #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct TxoPointer { - id: tx::TxId, + id: TransactionId, index: u32, } +#[wasm_bindgen] +impl TxoPointer { + /// serialize into a JsValue object + pub fn to_json(&self) -> Result { + JsValue::from_serde(self).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + /// retrieve the object from a JsValue. + pub fn from_json(value: JsValue) -> Result { + value + .into_serde() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } +} impl TxoPointer { fn convert(&self) -> tx::TxoPointer { tx::TxoPointer { - id: self.id, + id: self.id.convert(), index: self.index, } } } -#[derive(Serialize, Deserialize)] +#[wasm_bindgen] +#[derive(Serialize, Deserialize, Clone)] pub struct TxOut { - address: address::ExtendedAddr, + address: Address, value: Coin, } +#[wasm_bindgen] +impl TxOut { + /// serialize into a JsValue object + pub fn to_json(&self) -> Result { + JsValue::from_serde(self).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + /// retrieve the object from a JsValue. + pub fn from_json(value: JsValue) -> Result { + value + .into_serde() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } +} impl TxOut { fn convert(&self) -> tx::TxOut { tx::TxOut { - address: self.address.clone(), + address: self.address.0.clone(), value: self.value.0, } } } +/// a transaction type, this is not ready for sending to the network. It is only an +/// intermediate type to use between the transaction builder and the transaction +/// finalizer. It allows separation of concerns: +/// +/// 1. build the transaction on one side/thread/machine/...; +/// 2. sign the transaction on the other/thread/machines/cold-wallet...; +/// #[wasm_bindgen] pub struct Transaction(tx::Tx); #[wasm_bindgen] @@ -658,38 +757,50 @@ impl SignedTransaction { } } +/// This is the linear fee algorithm used buy the current cardano blockchain. +/// +/// However it is possible the linear fee algorithm may change its settings: +/// +/// It is currently a function `fee(n) = a * x + b`. `a` and `b` can be +/// re-configured by a protocol update. Users of this object need to be aware +/// that it may change and that they might need to update its settings. +/// #[wasm_bindgen] pub struct LinearFeeAlgorithm(fee::LinearFee); #[wasm_bindgen] impl LinearFeeAlgorithm { + /// this is the default mainnet linear fee algorithm. It is also known to work + /// with the staging network and the current testnet. + /// pub fn default() -> LinearFeeAlgorithm { LinearFeeAlgorithm(fee::LinearFee::default()) } } +/// This is the Output policy for automatic Input selection. #[wasm_bindgen] pub struct OutputPolicy(txutils::OutputPolicy); #[wasm_bindgen] impl OutputPolicy { + /// requires to send back all the spare changes to only one given address pub fn change_to_one_address(address: Address) -> OutputPolicy { OutputPolicy(txutils::OutputPolicy::One(address.0)) } } +/// The transaction builder provides a set of tools to help build +/// a valid Transaction. #[wasm_bindgen] pub struct TransactionBuilder(txbuild::TxBuilder); #[wasm_bindgen] impl TransactionBuilder { + /// create a new transaction builder #[wasm_bindgen(constructor)] pub fn new() -> TransactionBuilder { TransactionBuilder(txbuild::TxBuilder::new()) } - pub fn add_input(&mut self, input_ptr: &JsValue, value: Coin) -> Result<(), JsValue> { - let txo_pointer: TxoPointer = input_ptr - .into_serde() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; - + pub fn add_input(&mut self, txo_pointer: &TxoPointer, value: Coin) -> Result<(), JsValue> { self.0.add_input(&txo_pointer.convert(), value.0); Ok(()) } @@ -701,10 +812,7 @@ impl TransactionBuilder { .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) } - pub fn add_output(&mut self, output: &JsValue) -> Result<(), JsValue> { - let output: TxOut = output - .into_serde() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; + pub fn add_output(&mut self, output: &TxOut) -> Result<(), JsValue> { self.0.add_output_value(&output.convert()); Ok(()) } @@ -720,7 +828,7 @@ impl TransactionBuilder { all_txout .into_iter() .map(|txout| TxOut { - address: txout.address, + address: Address(txout.address), value: Coin(txout.value), }) .collect::>() @@ -781,6 +889,11 @@ impl TransactionFinalized { } } + /// sign the inputs of the transaction (i.e. unlock the funds the input are + /// referring to). + /// + /// The signature must be added one by one in the same order the inputs have + /// been added. pub fn sign( &mut self, blockchain_settings: &BlockchainSettings, From 813426bc9eef0bed3b6caa64505d3a50c45b9155 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 19:05:35 +0100 Subject: [PATCH 08/33] add redeem keys in the system too --- cardano-wallet/src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index e73db45..6e0545d 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -914,6 +914,77 @@ impl TransactionFinalized { } } +/* ************************************************************************* * + * Redemption keys * + * ************************************************************************* * + * + * Retrieve the redemption keys and redeem to a given address + */ + +use self::cardano::redeem; + +#[wasm_bindgen] +pub struct PrivateRedeemKey(redeem::PrivateKey); +#[wasm_bindgen] +impl PrivateRedeemKey { + /// retrieve a private key from the given hexadecimal string + pub fn from_hex(hex: &str) -> Result { + redeem::PrivateKey::from_hex(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PrivateRedeemKey) + } + /// convert the private key to an hexadecimal string + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + + /// get the public key associated to this private key + pub fn public(&self) -> PublicRedeemKey { + PublicRedeemKey(self.0.public()) + } + + /// sign some bytes with this private key + pub fn sign(&self, data: &[u8]) -> RedeemSignature { + let signature = self.0.sign(data); + RedeemSignature(signature) + } +} + +#[wasm_bindgen] +pub struct PublicRedeemKey(redeem::PublicKey); +#[wasm_bindgen] +impl PublicRedeemKey { + /// retrieve a public key from the given hexadecimal string + pub fn from_hex(hex: &str) -> Result { + redeem::PublicKey::from_hex(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PublicRedeemKey) + } + /// convert the public key to an hexadecimal string + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } + + /// verify the signature with the given public key + pub fn verify(&self, data: &[u8], signature: &RedeemSignature) -> bool { + self.0.verify(&signature.0, data) + } +} + +#[wasm_bindgen] +pub struct RedeemSignature(redeem::Signature); +#[wasm_bindgen] +impl RedeemSignature { + pub fn from_hex(hex: &str) -> Result { + redeem::Signature::from_hex(hex) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(RedeemSignature) + } + pub fn to_hex(&self) -> String { + format!("{}", self.0) + } +} + /* ************************************************************************* * * Password Encrypted Data * * ************************************************************************* * From 26134dd77e72746224458a6c0a699c9da272a7ab Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 23:19:17 +0100 Subject: [PATCH 09/33] add support for paperwallet and redemption --- cardano-wallet/Cargo.toml | 1 + cardano-wallet/src/lib.rs | 73 +++++++++++++++++++++++------ cardano-wallet/tests/paperwallet.rs | 68 +++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 cardano-wallet/tests/paperwallet.rs diff --git a/cardano-wallet/Cargo.toml b/cardano-wallet/Cargo.toml index 6b0a65e..5b82619 100644 --- a/cardano-wallet/Cargo.toml +++ b/cardano-wallet/Cargo.toml @@ -34,6 +34,7 @@ wee_alloc = { version = "0.4.2", optional = true } [dev-dependencies] wasm-bindgen-test = "0.2" +lazy_static = "^1.2" [profile.release] # Tell `rustc` to optimize for small code size. diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 6e0545d..23f2cbd 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -17,7 +17,7 @@ use wasm_bindgen::prelude::*; use self::cardano::{ address, bip::{bip39, bip44}, - coin, config, fee, hash, hdpayload, hdwallet, tx, txbuild, txutils, util, wallet, + coin, config, fee, hash, hdpayload, hdwallet, paperwallet, tx, txbuild, txutils, util, wallet, }; /// setting of the blockchain @@ -25,7 +25,7 @@ use self::cardano::{ /// This includes the `ProtocolMagic` a discriminant value to differentiate /// different instances of the cardano blockchain (Mainnet, Testnet... ). #[wasm_bindgen] -#[derive(Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BlockchainSettings { /// code of a specific blockchain used to sign transactions and blocks protocol_magic: config::ProtocolMagic, @@ -66,7 +66,7 @@ impl BlockchainSettings { /// Its support is already provided for backward compatibility with old /// addresses. #[wasm_bindgen] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DerivationScheme(hdwallet::DerivationScheme); #[wasm_bindgen] impl DerivationScheme { @@ -95,6 +95,7 @@ impl DerivationScheme { /// * make sure the user remembers the mnemonics string; /// #[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Entropy(bip39::Entropy); #[wasm_bindgen] impl Entropy { @@ -140,7 +141,7 @@ impl Entropy { /// with it; /// #[wasm_bindgen] -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct PrivateKey(hdwallet::XPrv); #[wasm_bindgen] impl PrivateKey { @@ -203,6 +204,7 @@ impl PrivateKey { /// only the privacy is leaked; /// #[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct PublicKey(hdwallet::XPub); #[wasm_bindgen] impl PublicKey { @@ -267,7 +269,7 @@ impl PublicKey { } #[wasm_bindgen] -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Address(address::ExtendedAddr); #[wasm_bindgen] impl Address { @@ -283,6 +285,7 @@ impl Address { } #[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Signature(hdwallet::Signature<()>); #[wasm_bindgen] impl Signature { @@ -305,6 +308,7 @@ impl Signature { */ #[wasm_bindgen] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct AccountIndex(u32); #[wasm_bindgen] impl AccountIndex { @@ -319,6 +323,7 @@ impl AccountIndex { } } #[wasm_bindgen] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct AddressKeyIndex(u32); #[wasm_bindgen] impl AddressKeyIndex { @@ -335,6 +340,7 @@ impl AddressKeyIndex { /// Root Private Key of a BIP44 HD Wallet #[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Bip44RootPrivateKey { key: PrivateKey, derivation_scheme: DerivationScheme, @@ -441,12 +447,8 @@ impl Bip44AccountPublic { pub struct DaedalusWallet(PrivateKey); #[wasm_bindgen] impl DaedalusWallet { - pub fn recover(mnemonics: &str) -> Result { - let mnemonics = bip39::Mnemonics::from_string(&bip39::dictionary::ENGLISH, mnemonics) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; - let entropy = bip39::Entropy::from_mnemonics(&mnemonics) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; - let entropy_bytes = cbor_event::Value::Bytes(Vec::from(entropy.as_ref())); + pub fn recover(entropy: &Entropy) -> Result { + let entropy_bytes = cbor_event::Value::Bytes(Vec::from(entropy.0.as_ref())); let entropy_cbor = cbor!(&entropy_bytes).map_err(|e| JsValue::from_str(&format! {"{:?}", e}))?; let seed: Vec = { @@ -493,21 +495,24 @@ impl DaedalusAddressChecker { /// /// The return private key is the key needed to sign the transaction to unlock /// UTxO associated to the address. - pub fn check_address(&self, address: &Address) -> Option { + pub fn check_address(&self, address: &Address) -> Result { if let Some(hdpa) = &address.0.attributes.derivation_path.clone() { if let Ok(path) = self.payload_key.decrypt_path(hdpa) { let mut key = self.wallet.clone(); for index in path.iter() { key = key.derive(DerivationScheme::v1(), *index); } - return Some(key); + return Ok(CheckedAddress(Some(key))); } } - None + Ok(CheckedAddress(None)) } } +#[wasm_bindgen] +pub struct CheckedAddress(Option); + /* ************************************************************************* * * Transaction Builder * * ************************************************************************* * @@ -985,6 +990,46 @@ impl RedeemSignature { } } +/* ************************************************************************* * + * Paper wallet scrambling * + * ************************************************************************* * + * + * the API for the paper wallet + */ + +#[wasm_bindgen] +pub fn paper_wallet_scramble( + entropy: &Entropy, + iv: &[u8], + password: &str, +) -> Result { + if iv.len() != paperwallet::IV_SIZE { + return Err(JsValue::from_str(&format!( + "Invalid IV size, expected 8 random bytes but received {} bytes", + iv.len(), + ))); + } + + let bytes = paperwallet::scramble(iv, password.as_bytes(), entropy.0.as_ref()); + + JsValue::from_serde(&bytes).map_err(|e| JsValue::from_str(&format! {"{:?}", e})) +} + +#[wasm_bindgen] +pub fn paper_wallet_unscramble(paper: &[u8], password: &str) -> Result { + if paper.len() <= paperwallet::IV_SIZE { + return Err(JsValue::from_str(&format!( + "Not enough data to decode the paper wallet, expecting at least 8 bytes but received {} bytes", + paper.len(), + ))); + } + let bytes = paperwallet::unscramble(password.as_bytes(), paper); + + bip39::Entropy::from_slice(&bytes) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(Entropy) +} + /* ************************************************************************* * * Password Encrypted Data * * ************************************************************************* * diff --git a/cardano-wallet/tests/paperwallet.rs b/cardano-wallet/tests/paperwallet.rs new file mode 100644 index 0000000..63d6b08 --- /dev/null +++ b/cardano-wallet/tests/paperwallet.rs @@ -0,0 +1,68 @@ +#![cfg(target_arch = "wasm32")] + +extern crate cardano_wallet; +extern crate wasm_bindgen; +extern crate wasm_bindgen_test; +#[macro_use] +extern crate lazy_static; +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; + +use cardano_wallet::*; + +wasm_bindgen_test_configure!(run_in_browser); + +struct Test { + iv: Vec, + password: &'static str, + entropy: Entropy, +} +lazy_static! { + static ref TESTS: Vec = { + vec![ + Test { + iv: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + password: "", + entropy: Entropy::from_english_mnemonics( + "legal winner thank year wave sausage worth useful legal winner thank yellow", + ).unwrap() + }, + Test { + iv: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], + password: "Cardano Ada", + entropy: Entropy::from_english_mnemonics( + "fold parrot feature figure stay blanket woman grain huge orphan key exile" + ).unwrap() + }, + Test { + iv: vec![0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a], + password: "This is a very long passphrase. This is a very long passphrase. This is a very long passphrase. This is a very long passphrase.", + entropy: Entropy::from_english_mnemonics( + "clay eyebrow melody february pencil betray build cart insane great coconut champion ancient catch provide horn merit cinnamon" + ).unwrap() + }, + ] + }; +} + +fn test(i: usize) { + let test = &TESTS[i]; + let bytes = paper_wallet_scramble(&test.entropy, &test.iv, test.password).unwrap(); + let bytes: Vec = JsValue::into_serde(&bytes).unwrap(); + let entropy = paper_wallet_unscramble(&bytes, test.password).unwrap(); + + assert_eq!(&test.entropy, &entropy); +} + +#[wasm_bindgen_test] +fn test_valid_test_0() { + test(0) +} +#[wasm_bindgen_test] +fn test_valid_test_1() { + test(1) +} +#[wasm_bindgen_test] +fn test_valid_test_2() { + test(2) +} From 80ec8844e805e94f96b4ced8e023262d04c42382 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 23:31:55 +0100 Subject: [PATCH 10/33] add some documentation --- cardano-wallet/src/lib.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 23f2cbd..e583038 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -495,23 +495,43 @@ impl DaedalusAddressChecker { /// /// The return private key is the key needed to sign the transaction to unlock /// UTxO associated to the address. - pub fn check_address(&self, address: &Address) -> Result { + pub fn check_address(&self, address: &Address) -> Result { if let Some(hdpa) = &address.0.attributes.derivation_path.clone() { if let Ok(path) = self.payload_key.decrypt_path(hdpa) { let mut key = self.wallet.clone(); for index in path.iter() { key = key.derive(DerivationScheme::v1(), *index); } - return Ok(CheckedAddress(Some(key))); + return Ok(DaedalusCheckedAddress(Some(key))); } } - Ok(CheckedAddress(None)) + Ok(DaedalusCheckedAddress(None)) } } +/// result value of the check_address function of the DaedalusAddressChecker. +/// +/// If the address passed to check_address was recognised by the daedalus wallet +/// then this object will contain the private key associated to this wallet +/// private key necessary to sign transactions +#[wasm_bindgen] +pub struct DaedalusCheckedAddress(Option); #[wasm_bindgen] -pub struct CheckedAddress(Option); +impl DaedalusCheckedAddress { + /// return if the value contains the private key (i.e. the check_address + /// recognised an address). + pub fn is_checked(&self) -> bool { + self.0.is_some() + } + + pub fn private_key(&self) -> Result { + match &self.0 { + None => Err(JsValue::from_str(&format!("Daedalus Address didn't check"))), + Some(ref sk) => Ok(sk.clone()), + } + } +} /* ************************************************************************* * * Transaction Builder * From 09b4d45c645a4b44e3c362014d902299652635d0 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 23:39:08 +0100 Subject: [PATCH 11/33] rename the Address::to_hex to Address::to_base58 --- cardano-wallet/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index e583038..d3b6ca1 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -273,10 +273,10 @@ impl PublicKey { pub struct Address(address::ExtendedAddr); #[wasm_bindgen] impl Address { - pub fn to_hex(&self) -> String { + pub fn to_base58(&self) -> String { format!("{}", self.0) } - pub fn from_hex(s: &str) -> Result { + pub fn from_base58(s: &str) -> Result { use std::str::FromStr; address::ExtendedAddr::from_str(s) .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) From 633b48f99015881595f7ef021fcdc51f2541181c Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Thu, 10 Jan 2019 23:39:32 +0100 Subject: [PATCH 12/33] fix the in browset JS file --- cardano-wallet/tests/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cardano-wallet/tests/index.js b/cardano-wallet/tests/index.js index c273c5b..83a1616 100644 --- a/cardano-wallet/tests/index.js +++ b/cardano-wallet/tests/index.js @@ -12,6 +12,8 @@ Wallet const MNEMONICS = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; const PASSWORD = "Cardano Rust for the winners!"; + let settings = Wallet.BlockchainSettings.mainnet(); + let entropy = Wallet.Entropy.from_english_mnemonics(MNEMONICS); let wallet = Wallet.Bip44RootPrivateKey.recover(entropy, PASSWORD); @@ -21,8 +23,8 @@ Wallet let key_prv = account.address_key(false, Wallet.AddressKeyIndex.new(0)); let key_pub = account_public.address_key(false, Wallet.AddressKeyIndex.new(0)); - let address = key_pub.bootstrap_era_address(); + let address = key_pub.bootstrap_era_address(settings); - console.log("Address m/bip44/ada/'0/0/0", address.to_hex()); + console.log("Address m/bip44/ada/'0/0/0", address.to_base58()); }) .catch(console.error); From e3dd5065eb2d88a97a701e4ab6ea175ac9c031ca Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Fri, 11 Jan 2019 00:38:48 +0100 Subject: [PATCH 13/33] add more features to the Redeem Keys (Address creation and from_bytes) --- cardano-wallet/src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index d3b6ca1..ff1bced 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -952,6 +952,13 @@ use self::cardano::redeem; pub struct PrivateRedeemKey(redeem::PrivateKey); #[wasm_bindgen] impl PrivateRedeemKey { + /// retrieve the private redeeming key from the given bytes (expect 64 bytes) + pub fn from_bytes(bytes: &[u8]) -> Result { + redeem::PrivateKey::from_slice(bytes) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map(PrivateRedeemKey) + } + /// retrieve a private key from the given hexadecimal string pub fn from_hex(hex: &str) -> Result { redeem::PrivateKey::from_hex(hex) @@ -994,6 +1001,19 @@ impl PublicRedeemKey { pub fn verify(&self, data: &[u8], signature: &RedeemSignature) -> bool { self.0.verify(&signature.0, data) } + + /// generate the address for this redeeming key + pub fn address(&self, settings: &BlockchainSettings) -> Address { + let address_type = address::AddrType::ATRedeem; + let spending_data = address::SpendingData::RedeemASD(self.0.clone()); + let attributes = + address::Attributes::new_bootstrap_era(None, settings.protocol_magic.into()); + Address(address::ExtendedAddr::new( + address_type, + spending_data, + attributes, + )) + } } #[wasm_bindgen] From 4ef87a399cb8e304549eb3eb6a4aec8332d793d8 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Fri, 11 Jan 2019 20:14:13 +0100 Subject: [PATCH 14/33] remove the need for the Result --- cardano-wallet/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index ff1bced..80b7ba0 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -495,18 +495,18 @@ impl DaedalusAddressChecker { /// /// The return private key is the key needed to sign the transaction to unlock /// UTxO associated to the address. - pub fn check_address(&self, address: &Address) -> Result { + pub fn check_address(&self, address: &Address) -> DaedalusCheckedAddress { if let Some(hdpa) = &address.0.attributes.derivation_path.clone() { if let Ok(path) = self.payload_key.decrypt_path(hdpa) { let mut key = self.wallet.clone(); for index in path.iter() { key = key.derive(DerivationScheme::v1(), *index); } - return Ok(DaedalusCheckedAddress(Some(key))); + return DaedalusCheckedAddress(Some(key)); } } - Ok(DaedalusCheckedAddress(None)) + DaedalusCheckedAddress(None) } } From 315f511d3822d214ac95d44d76f37bd23fc28323 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Fri, 11 Jan 2019 20:14:37 +0100 Subject: [PATCH 15/33] add unit test for the redeem pub key to address --- cardano-wallet/tests/redeem.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 cardano-wallet/tests/redeem.rs diff --git a/cardano-wallet/tests/redeem.rs b/cardano-wallet/tests/redeem.rs new file mode 100644 index 0000000..5681741 --- /dev/null +++ b/cardano-wallet/tests/redeem.rs @@ -0,0 +1,23 @@ +#![cfg(target_arch = "wasm32")] + +extern crate cardano_wallet; +extern crate wasm_bindgen; +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +use cardano_wallet::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn public_key_to_address() { + const REDEEM_HEX : &'static str = "fb40490e2fa06aeca59382e9b504e08cc7a8ee463d95309b66fd76bf03924d99"; + const ADDRESS : &'static str = "Ae2tdPwUPEZHFQnrr2dYB4GEQ8WVKspEyrg29pJ3f7qdjzaxjeShEEokF5f"; + + let blockchain_settings = BlockchainSettings::mainnet(); + let key = PublicRedeemKey::from_hex(REDEEM_HEX).unwrap(); + let address = key.address(&blockchain_settings); + let address_base58 = address.to_base58(); + + assert_eq!(ADDRESS, address_base58); +} From 666d0d785185a7a10427515c3d06061846d78fa2 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Sun, 13 Jan 2019 13:54:32 +0000 Subject: [PATCH 16/33] don't display-debug the error, use a neat string now --- cardano-wallet/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cardano-wallet/src/lib.rs b/cardano-wallet/src/lib.rs index 80b7ba0..7ca1818 100644 --- a/cardano-wallet/src/lib.rs +++ b/cardano-wallet/src/lib.rs @@ -955,14 +955,14 @@ impl PrivateRedeemKey { /// retrieve the private redeeming key from the given bytes (expect 64 bytes) pub fn from_bytes(bytes: &[u8]) -> Result { redeem::PrivateKey::from_slice(bytes) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map_err(|e| JsValue::from_str(&format! {"{}", e})) .map(PrivateRedeemKey) } /// retrieve a private key from the given hexadecimal string pub fn from_hex(hex: &str) -> Result { redeem::PrivateKey::from_hex(hex) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + .map_err(|e| JsValue::from_str(&format! {"{}", e})) .map(PrivateRedeemKey) } /// convert the private key to an hexadecimal string From 6ee0422a01406662ef2c8345511333f04c462f1e Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Sun, 13 Jan 2019 13:54:59 +0000 Subject: [PATCH 17/33] add tests for the redeem private-key too --- cardano-wallet/tests/redeem.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cardano-wallet/tests/redeem.rs b/cardano-wallet/tests/redeem.rs index 5681741..28982ef 100644 --- a/cardano-wallet/tests/redeem.rs +++ b/cardano-wallet/tests/redeem.rs @@ -11,8 +11,9 @@ wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test] fn public_key_to_address() { - const REDEEM_HEX : &'static str = "fb40490e2fa06aeca59382e9b504e08cc7a8ee463d95309b66fd76bf03924d99"; - const ADDRESS : &'static str = "Ae2tdPwUPEZHFQnrr2dYB4GEQ8WVKspEyrg29pJ3f7qdjzaxjeShEEokF5f"; + const REDEEM_HEX: &'static str = + "fb40490e2fa06aeca59382e9b504e08cc7a8ee463d95309b66fd76bf03924d99"; + const ADDRESS: &'static str = "Ae2tdPwUPEZHFQnrr2dYB4GEQ8WVKspEyrg29pJ3f7qdjzaxjeShEEokF5f"; let blockchain_settings = BlockchainSettings::mainnet(); let key = PublicRedeemKey::from_hex(REDEEM_HEX).unwrap(); @@ -21,3 +22,19 @@ fn public_key_to_address() { assert_eq!(ADDRESS, address_base58); } + +#[wasm_bindgen_test] +fn private_key_decode() { + const REDEEM_HEX: &'static str = + "96555162f5bb2c0caa98332750ebebb398a1e0e1df2e22d9af3e4d4fe891b93c"; + + let blockchain_settings = BlockchainSettings::mainnet(); + let private_key = PrivateRedeemKey::from_hex(REDEEM_HEX).unwrap(); + let public_key = private_key.public(); + let address = public_key.address(&blockchain_settings); + let address_base58 = address.to_base58(); + + let signature = private_key.sign(address_base58.as_bytes()); + + assert!(public_key.verify(address_base58.as_bytes(), &signature)); +} From 9b828c09a2c0d1e29363ff1f1430763dbc07c492 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Sun, 13 Jan 2019 13:55:15 +0000 Subject: [PATCH 18/33] update to latest rust master fixing issue with the redeem private key that had a wrong size see input-output-hk/rust-cardano#393 --- rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust b/rust index 2f8d57f..e869c9d 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit 2f8d57f325b47997f466f2e274eec4ee37909843 +Subproject commit e869c9d7d231114526f68196e53ffea6c131160d From 9f7c765982e76c9aa49fb4cbec52433398590666 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Mon, 14 Jan 2019 14:18:08 +0000 Subject: [PATCH 19/33] update the README --- cardano-wallet/Cargo.toml | 2 +- cardano-wallet/README.md | 72 ++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/cardano-wallet/Cargo.toml b/cardano-wallet/Cargo.toml index 5b82619..76e1a12 100644 --- a/cardano-wallet/Cargo.toml +++ b/cardano-wallet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cardano-wallet" -version = "0.1.0" +version = "0.1.1" authors = ["Nicolas Di Prima "] [lib] diff --git a/cardano-wallet/README.md b/cardano-wallet/README.md index 1659776..151252a 100644 --- a/cardano-wallet/README.md +++ b/cardano-wallet/README.md @@ -1,54 +1,42 @@ - +# Cardano Wallet -# πŸ¦€πŸ•ΈοΈ `wasm-pack-template` +This is a new version of the Cardano Wasm binding for Icarus/Yoroi. +It exposes everything one needs to be able to build a cardano wallet +in javascript. -A template for kick starting a Rust and WebAssembly project using -[`wasm-pack`](https://github.com/rustwasm/wasm-pack). - -This template is designed for compiling Rust libraries into WebAssembly and -publishing the resulting package to NPM. - -* Want to use the published NPM package in a Website? [Check out - `create-wasm-app`.](https://github.com/rustwasm/create-wasm-app) -* Want to make a monorepo-style Website without publishing to NPM? Check out - [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) - and/or - [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). - -## πŸ”‹ Batteries Included - -* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating - between WebAssembly and JavaScript. -* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) - for logging panic messages to the developer console. -* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized - for small code size. - -## 🚴 Usage - -### πŸ‘ Use `cargo generate` to Clone this Template - -[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) +# How to install ``` -cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project -cd my-project +npm i --save cardano-wallet ``` -### πŸ› οΈ Build with `wasm-pack build` +# How to use -``` -wasm-pack build -``` +```js +import * as Cardano from "hello-wasm-pack"; -### πŸ”¬ Test in Headless Browsers with `wasm-pack test` +Cardano + .then(Cardano => { + const MNEMONICS = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; + const PASSWORD = "Cardano Rust for the winners!"; -``` -wasm-pack test --headless --firefox -``` + // to connect the wallet to mainnet + let settings = Cardano.BlockchainSettings.mainnet(); -### 🎁 Publish to NPM with `wasm-pack publish` + // recover the entropy + let entropy = Cardano.Entropy.from_english_mnemonics(MNEMONICS); + // recover the wallet + let wallet = Cardano.Bip44RootPrivateKey.recover(entropy, PASSWORD); -``` -wasm-pack publish + // create a wallet account + let account = wallet.bip44_account(Wallet.AccountIndex.new(0 | 0x80000000)); + let account_public = account.public(); + + // create an address + let key_pub = account_public.address_key(false, Wallet.AddressKeyIndex.new(0)); + let address = key_pub.bootstrap_era_address(settings); + + console.log("Address m/bip44/ada/'0/0/0", address.to_base58()); + }) + .catch(console.error); ``` From badeedd2c6786204ae5a69569c0b9febf0cbd26d Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Mon, 14 Jan 2019 15:33:18 +0000 Subject: [PATCH 20/33] update README and metadata --- cardano-wallet/Cargo.toml | 8 ++++++- cardano-wallet/README.md | 44 +++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/cardano-wallet/Cargo.toml b/cardano-wallet/Cargo.toml index 76e1a12..7336bb5 100644 --- a/cardano-wallet/Cargo.toml +++ b/cardano-wallet/Cargo.toml @@ -1,7 +1,13 @@ [package] name = "cardano-wallet" -version = "0.1.1" +version = "0.2.1" authors = ["Nicolas Di Prima "] +description = "Cardano Wallet, from rust to JS via Wasm" +homepage = "https://github.com/input-output-hk/js-cardano-wasm#README.md" +repository = "https://github.com/input-output-hk/js-cardano-wasm" +readme = "README.md" +keywords = ["wasm", "cardano", "wallet", "crypto", "ada"] +license = "MIT OR Apache-2.0" [lib] crate-type = ["cdylib", "rlib"] diff --git a/cardano-wallet/README.md b/cardano-wallet/README.md index 151252a..638421b 100644 --- a/cardano-wallet/README.md +++ b/cardano-wallet/README.md @@ -12,31 +12,35 @@ npm i --save cardano-wallet # How to use +You can seek documentation +[here](https://github.com/rustwasm/create-wasm-app#create-wasm-app) +regarding how to use this package in your project. + +Now remember, with great power comes great responsibility. You can now +write a cardano wallet, redeem your certificates, create and sign +transactions. + ```js -import * as Cardano from "hello-wasm-pack"; +import * as Cardano from "cardano-wallet"; -Cardano - .then(Cardano => { - const MNEMONICS = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; - const PASSWORD = "Cardano Rust for the winners!"; +const MNEMONICS = "crowd captain hungry tray powder motor coast oppose month shed parent mystery torch resemble index"; +const PASSWORD = "Cardano Rust for the winners!"; - // to connect the wallet to mainnet - let settings = Cardano.BlockchainSettings.mainnet(); +// to connect the wallet to mainnet +let settings = Cardano.BlockchainSettings.mainnet(); - // recover the entropy - let entropy = Cardano.Entropy.from_english_mnemonics(MNEMONICS); - // recover the wallet - let wallet = Cardano.Bip44RootPrivateKey.recover(entropy, PASSWORD); +// recover the entropy +let entropy = Cardano.Entropy.from_english_mnemonics(MNEMONICS); +// recover the wallet +let wallet = Cardano.Bip44RootPrivateKey.recover(entropy, PASSWORD); - // create a wallet account - let account = wallet.bip44_account(Wallet.AccountIndex.new(0 | 0x80000000)); - let account_public = account.public(); +// create a wallet account +let account = wallet.bip44_account(Cardano.AccountIndex.new(0 | 0x80000000)); +let account_public = account.public(); - // create an address - let key_pub = account_public.address_key(false, Wallet.AddressKeyIndex.new(0)); - let address = key_pub.bootstrap_era_address(settings); +// create an address +let key_pub = account_public.address_key(false, Cardano.AddressKeyIndex.new(0)); +let address = key_pub.bootstrap_era_address(settings); - console.log("Address m/bip44/ada/'0/0/0", address.to_base58()); - }) - .catch(console.error); +console.log("Address m/bip44/ada/'0/0/0", address.to_base58()); ``` From 9ca290541650115a1c397ac581dbd09bfd1faf5c Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Wed, 16 Jan 2019 19:15:34 +0000 Subject: [PATCH 21/33] update the default require toolchain to be nightly --- build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build b/build index fa35a31..1dc2c05 100755 --- a/build +++ b/build @@ -1,6 +1,6 @@ #!/bin/bash -RUST_TOOLCHAIN="$(cat rust-toolchain)" +RUST_TOOLCHAIN="nightly" set +e git submodule update --init --recursive From c0ff939b5fe17201116ccb83e92cfb37ae6258b1 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Wed, 16 Jan 2019 19:15:54 +0000 Subject: [PATCH 22/33] remove the console.log from the test that was poluting the output --- js/tests/from-seed.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/js/tests/from-seed.js b/js/tests/from-seed.js index 2b5e6f7..1d16cde 100644 --- a/js/tests/from-seed.js +++ b/js/tests/from-seed.js @@ -3,7 +3,7 @@ const CardanoCrypto = require('../../dist/index.js'); const SEED = Array(32).fill(0); -describe('Wallet FromSeed', async function() { +describe('Wallet FromSeed', async function () { let xprv = null; let wallet = null; let account = null; @@ -12,12 +12,11 @@ describe('Wallet FromSeed', async function() { await CardanoCrypto.loadRustModule() }); - it("check seed size", function() { + it("check seed size", function () { expect(SEED.length).equals(CardanoCrypto.HdWallet.SEED_SIZE); }); - it("create a wallet", function() { - const result = CardanoCrypto.Wallet.fromSeed(SEED); - console.log(result); + it("create a wallet", function () { + const result = CardanoCrypto.Wallet.fromSeed(SEED); expect(result.failed).equals(false); wallet = result.result; }); From 7c7b460df213b2e8234090cbb6c7704a53ee8a26 Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Wed, 16 Jan 2019 19:16:53 +0000 Subject: [PATCH 23/33] update the old wallet code too --- wallet-wasm/Cargo.lock | 26 +- wallet-wasm/Cargo.toml | 2 +- wallet-wasm/src/lib.rs | 708 +++++++++++++++++++++++++---------------- 3 files changed, 459 insertions(+), 277 deletions(-) diff --git a/wallet-wasm/Cargo.lock b/wallet-wasm/Cargo.lock index 7acc1fb..9734ef0 100644 --- a/wallet-wasm/Cargo.lock +++ b/wallet-wasm/Cargo.lock @@ -1,8 +1,9 @@ [[package]] name = "cardano" -version = "0.1.0" +version = "0.1.1" dependencies = [ - "cbor_event 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cbor_event 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chain-core 0.1.0", "cryptoxide 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -10,9 +11,21 @@ dependencies = [ [[package]] name = "cbor_event" -version = "1.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cfg-if" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chain-core" +version = "0.1.0" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cryptoxide" version = "0.1.0" @@ -88,8 +101,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "wallet-wasm" version = "0.1.0" dependencies = [ - "cardano 0.1.0", - "cbor_event 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cardano 0.1.1", + "cbor_event 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "cryptoxide 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -97,7 +110,8 @@ dependencies = [ ] [metadata] -"checksum cbor_event 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd1877f29511f8bbca55667b55e29e8d4ed80729155c9c9236d99bc340282a6f" +"checksum cbor_event 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d4b31c9ab733e0689ff212327ebdd7d07379bebc2b7a6ecb0654c3bf77740e" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum cryptoxide 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9be687df90da186ed5c6ada0b6b4d69f5ad914071870bc5d41277377d30427" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" diff --git a/wallet-wasm/Cargo.toml b/wallet-wasm/Cargo.toml index e06263c..fb67fe1 100644 --- a/wallet-wasm/Cargo.toml +++ b/wallet-wasm/Cargo.toml @@ -13,7 +13,7 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" cryptoxide = "0.1" -cbor_event = "1.0" +cbor_event = "2.1.2" [dependencies.cardano] path = "../rust/cardano" diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index c1e2db9..c17465d 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -1,34 +1,37 @@ extern crate serde; #[macro_use] extern crate serde_derive; -extern crate serde_json; -extern crate cryptoxide; extern crate cardano; +extern crate cryptoxide; +extern crate serde_json; #[macro_use] extern crate cbor_event; -use self::cryptoxide::hmac::{Hmac}; +use self::cryptoxide::blake2b::Blake2b; +use self::cryptoxide::chacha20poly1305::ChaCha20Poly1305; +use self::cryptoxide::digest::Digest; +use self::cryptoxide::hmac::Hmac; +use self::cryptoxide::pbkdf2::pbkdf2; use self::cryptoxide::sha2::{Sha256, Sha512}; -use self::cryptoxide::pbkdf2::{pbkdf2}; -use self::cryptoxide::blake2b::{Blake2b}; -use self::cryptoxide::digest::{Digest}; -use self::cryptoxide::chacha20poly1305::{ChaCha20Poly1305}; -use self::cardano::hdwallet; -use self::cardano::paperwallet; use self::cardano::address; +use self::cardano::bip::bip39; +use self::cardano::config::Config; use self::cardano::hdpayload; -use self::cardano::{util::{hex}, tx, fee, coin, txutils}; -use self::cardano::config::{Config}; -use self::cardano::wallet::{self, bip44, rindex, scheme::{Wallet, SelectionPolicy}}; -use self::cardano::bip::{bip39}; +use self::cardano::hdwallet; +use self::cardano::paperwallet; +use self::cardano::wallet::{ + self, bip44, rindex, + scheme::{SelectionPolicy, Wallet}, +}; +use self::cardano::{coin, fee, tx, txutils, util::hex}; -use self::cardano::util::try_from_slice::{TryFromSlice}; +use self::cardano::util::try_from_slice::TryFromSlice; -use std::{mem, result, string, convert, fmt}; use std::ffi::{CStr, CString}; -use std::os::raw::{c_uint, c_uchar, c_char, c_void}; use std::iter::repeat; +use std::os::raw::{c_char, c_uchar, c_uint, c_void}; +use std::{convert, fmt, mem, result, string}; //use std::slice::{from_raw_parts}; // In order to work with the memory we expose (de)allocation methods @@ -42,7 +45,7 @@ pub extern "C" fn alloc(size: usize) -> *mut c_void { #[no_mangle] pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) { - unsafe { + unsafe { let _buf = Vec::from_raw_parts(ptr, 0, cap); } } @@ -55,9 +58,13 @@ pub extern "C" fn dealloc_str(ptr: *mut c_char) { } #[no_mangle] -pub extern "C" fn pbkdf2_sha256(password: *mut c_char, salt: *mut c_char, iters: u32, output: u32) -> *mut c_char { +pub extern "C" fn pbkdf2_sha256( + password: *mut c_char, + salt: *mut c_char, + iters: u32, + output: u32, +) -> *mut c_char { unsafe { - let salt = CStr::from_ptr(salt); let password = CStr::from_ptr(password); @@ -73,16 +80,16 @@ pub extern "C" fn pbkdf2_sha256(password: *mut c_char, salt: *mut c_char, iters: } unsafe fn read_data(data_ptr: *const c_uchar, sz: usize) -> Vec { - let data_slice = std::slice::from_raw_parts(data_ptr, sz); - let mut data = Vec::with_capacity(sz); - data.extend_from_slice(data_slice); - data + let data_slice = std::slice::from_raw_parts(data_ptr, sz); + let mut data = Vec::with_capacity(sz); + data.extend_from_slice(data_slice); + data } unsafe fn write_data(data: &[u8], data_ptr: *mut c_uchar) { - let sz = data.len(); - let out = std::slice::from_raw_parts_mut(data_ptr, sz); - out[0..sz].clone_from_slice(data) + let sz = data.len(); + let out = std::slice::from_raw_parts_mut(data_ptr, sz); + out[0..sz].clone_from_slice(data) } unsafe fn read_data_u32(data_ptr: *const c_uint, sz: usize) -> Vec { @@ -93,66 +100,65 @@ unsafe fn read_data_u32(data_ptr: *const c_uint, sz: usize) -> Vec { } unsafe fn write_data_u32(data: &[u32], data_ptr: *mut c_uint) { - let sz = data.len(); - let out = std::slice::from_raw_parts_mut(data_ptr, sz); - out[0..sz].clone_from_slice(data) + let sz = data.len(); + let out = std::slice::from_raw_parts_mut(data_ptr, sz); + out[0..sz].clone_from_slice(data) } unsafe fn read_xprv(xprv_ptr: *const c_uchar) -> hdwallet::XPrv { - let xprv_slice = std::slice::from_raw_parts(xprv_ptr, hdwallet::XPRV_SIZE); - let mut xprv_bytes = [0;hdwallet::XPRV_SIZE]; - xprv_bytes[..].clone_from_slice(xprv_slice); - hdwallet::XPrv::from_bytes_verified(xprv_bytes).unwrap() + let xprv_slice = std::slice::from_raw_parts(xprv_ptr, hdwallet::XPRV_SIZE); + let mut xprv_bytes = [0; hdwallet::XPRV_SIZE]; + xprv_bytes[..].clone_from_slice(xprv_slice); + hdwallet::XPrv::from_bytes_verified(xprv_bytes).unwrap() } unsafe fn write_xprv(xprv: &hdwallet::XPrv, xprv_ptr: *mut c_uchar) { - let out = std::slice::from_raw_parts_mut(xprv_ptr, hdwallet::XPRV_SIZE); - out[0..hdwallet::XPRV_SIZE].clone_from_slice(xprv.as_ref()); + let out = std::slice::from_raw_parts_mut(xprv_ptr, hdwallet::XPRV_SIZE); + out[0..hdwallet::XPRV_SIZE].clone_from_slice(xprv.as_ref()); } unsafe fn read_xpub(xpub_ptr: *const c_uchar) -> hdwallet::XPub { - let xpub_slice = std::slice::from_raw_parts(xpub_ptr, hdwallet::XPUB_SIZE); - hdwallet::XPub::from_slice(xpub_slice).unwrap() + let xpub_slice = std::slice::from_raw_parts(xpub_ptr, hdwallet::XPUB_SIZE); + hdwallet::XPub::from_slice(xpub_slice).unwrap() } unsafe fn write_xpub(xpub: &hdwallet::XPub, xpub_ptr: *mut c_uchar) { - let out = std::slice::from_raw_parts_mut(xpub_ptr, hdwallet::XPUB_SIZE); - out[0..hdwallet::XPUB_SIZE].clone_from_slice(xpub.as_ref()); + let out = std::slice::from_raw_parts_mut(xpub_ptr, hdwallet::XPUB_SIZE); + out[0..hdwallet::XPUB_SIZE].clone_from_slice(xpub.as_ref()); } unsafe fn read_signature(sig_ptr: *const c_uchar) -> hdwallet::Signature { - let signature_slice = std::slice::from_raw_parts(sig_ptr, hdwallet::SIGNATURE_SIZE); - hdwallet::Signature::from_slice(signature_slice).unwrap() + let signature_slice = std::slice::from_raw_parts(sig_ptr, hdwallet::SIGNATURE_SIZE); + hdwallet::Signature::from_slice(signature_slice).unwrap() } unsafe fn write_signature(signature: &hdwallet::Signature, out_ptr: *mut c_uchar) { - let out = std::slice::from_raw_parts_mut(out_ptr, hdwallet::SIGNATURE_SIZE); - out[0..hdwallet::SIGNATURE_SIZE].clone_from_slice(signature.as_ref()); + let out = std::slice::from_raw_parts_mut(out_ptr, hdwallet::SIGNATURE_SIZE); + out[0..hdwallet::SIGNATURE_SIZE].clone_from_slice(signature.as_ref()); } unsafe fn read_seed(seed_ptr: *const c_uchar) -> hdwallet::Seed { - let seed_slice = std::slice::from_raw_parts(seed_ptr, hdwallet::SEED_SIZE); - hdwallet::Seed::from_slice(seed_slice).unwrap() + let seed_slice = std::slice::from_raw_parts(seed_ptr, hdwallet::SEED_SIZE); + hdwallet::Seed::from_slice(seed_slice).unwrap() } #[no_mangle] -pub extern "C" fn wallet_from_enhanced_entropy( entropy_ptr: *const c_uchar - , entropy_size: usize - , password_ptr: *const c_uchar - , password_size: usize - , out: *mut c_uchar - ) - -> usize -{ +pub extern "C" fn wallet_from_enhanced_entropy( + entropy_ptr: *const c_uchar, + entropy_size: usize, + password_ptr: *const c_uchar, + password_size: usize, + out: *mut c_uchar, +) -> usize { match entropy_size { - 16 | 20 | 24 | 28 | 32 => {}, - _ => return 1 + 16 | 20 | 24 | 28 | 32 => {} + _ => return 1, } - let entropy = unsafe { read_data(entropy_ptr, entropy_size) }; + let entropy = unsafe { read_data(entropy_ptr, entropy_size) }; let password = unsafe { read_data(password_ptr, password_size) }; // it is okay to unwrap here, we already checked the size - let entropy = bip39::Entropy::from_slice(&entropy).unwrap(); - let mut bytes = [0;hdwallet::XPRV_SIZE]; + let entropy = bip39::Entropy::from_slice(&entropy).unwrap(); + let mut bytes = [0; hdwallet::XPRV_SIZE]; wallet::keygen::generate_seed(&entropy, &password, &mut bytes); let xprv = hdwallet::XPrv::normalize_bytes(bytes); unsafe { write_xprv(&xprv, out) }; @@ -189,24 +195,41 @@ pub extern "C" fn wallet_derive_private(xprv_ptr: *const c_uchar, index: u32, ou } #[no_mangle] -pub extern "C" fn wallet_derive_public(xpub_ptr: *const c_uchar, index: u32, out: *mut c_uchar) -> bool { +pub extern "C" fn wallet_derive_public( + xpub_ptr: *const c_uchar, + index: u32, + out: *mut c_uchar, +) -> bool { let xpub = unsafe { read_xpub(xpub_ptr) }; match xpub.derive(hdwallet::DerivationScheme::V2, index) { - Ok(child) => { unsafe { write_xpub(&child, out) }; true } - Err(_) => { false } + Ok(child) => { + unsafe { write_xpub(&child, out) }; + true + } + Err(_) => false, } } #[no_mangle] -pub extern "C" fn wallet_sign(xprv_ptr: *const c_uchar, msg_ptr: *const c_uchar, msg_sz: usize, out: *mut c_uchar) { +pub extern "C" fn wallet_sign( + xprv_ptr: *const c_uchar, + msg_ptr: *const c_uchar, + msg_sz: usize, + out: *mut c_uchar, +) { let xprv = unsafe { read_xprv(xprv_ptr) }; let msg = unsafe { read_data(msg_ptr, msg_sz) }; - let signature : hdwallet::Signature> = xprv.sign(&msg[..]); + let signature: hdwallet::Signature> = xprv.sign(&msg[..]); unsafe { write_signature(&signature, out) } } #[no_mangle] -pub extern "C" fn wallet_verify(xpub_ptr: *const c_uchar, msg_ptr: *const c_uchar, msg_sz: usize, sig_ptr: *const c_uchar) -> bool { +pub extern "C" fn wallet_verify( + xpub_ptr: *const c_uchar, + msg_ptr: *const c_uchar, + msg_sz: usize, + sig_ptr: *const c_uchar, +) -> bool { let xpub = unsafe { read_xpub(xpub_ptr) }; let msg = unsafe { read_data(msg_ptr, msg_sz) }; let signature = unsafe { read_signature::>(sig_ptr) }; @@ -214,7 +237,14 @@ pub extern "C" fn wallet_verify(xpub_ptr: *const c_uchar, msg_ptr: *const c_ucha } #[no_mangle] -pub extern "C" fn paper_scramble(iv_ptr: *const c_uchar, pass_ptr: *const c_uchar, pass_sz: usize, input_ptr: *const c_uchar, input_sz: usize, out: *mut c_uchar) { +pub extern "C" fn paper_scramble( + iv_ptr: *const c_uchar, + pass_ptr: *const c_uchar, + pass_sz: usize, + input_ptr: *const c_uchar, + input_sz: usize, + out: *mut c_uchar, +) { let iv = unsafe { read_data(iv_ptr, paperwallet::IV_SIZE) }; let pass = unsafe { read_data(pass_ptr, pass_sz) }; let input = unsafe { read_data(input_ptr, input_sz) }; @@ -223,7 +253,13 @@ pub extern "C" fn paper_scramble(iv_ptr: *const c_uchar, pass_ptr: *const c_ucha } #[no_mangle] -pub extern "C" fn paper_unscramble(pass_ptr: *const c_uchar, pass_sz: usize, input_ptr: *const c_uchar, input_sz: usize, out: *mut c_uchar) { +pub extern "C" fn paper_unscramble( + pass_ptr: *const c_uchar, + pass_sz: usize, + input_ptr: *const c_uchar, + input_sz: usize, + out: *mut c_uchar, +) { let pass = unsafe { read_data(pass_ptr, pass_sz) }; let input = unsafe { read_data(input_ptr, input_sz) }; let output = paperwallet::unscramble(&pass[..], &input[..]); @@ -233,15 +269,24 @@ pub extern "C" fn paper_unscramble(pass_ptr: *const c_uchar, pass_sz: usize, inp #[no_mangle] pub extern "C" fn blake2b_256(msg_ptr: *const c_uchar, msg_sz: usize, out: *mut c_uchar) { let mut b2b = Blake2b::new(32); - let mut outv = [0;32]; + let mut outv = [0; 32]; let msg = unsafe { read_data(msg_ptr, msg_sz) }; b2b.input(&msg); b2b.result(&mut outv); unsafe { write_data(&outv, out) } } +fn default_network_magic() -> cardano::config::NetworkMagic { + cardano::config::ProtocolMagic::default().into() +} + #[no_mangle] -pub extern "C" fn wallet_public_to_address(xpub_ptr: *const c_uchar, payload_ptr: *const c_uchar, payload_sz: usize, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_public_to_address( + xpub_ptr: *const c_uchar, + payload_ptr: *const c_uchar, + payload_sz: usize, + out: *mut c_uchar, +) -> u32 { let xpub = unsafe { read_xpub(xpub_ptr) }; let payload = unsafe { read_data(payload_ptr, payload_sz) }; @@ -249,7 +294,7 @@ pub extern "C" fn wallet_public_to_address(xpub_ptr: *const c_uchar, payload_ptr let addr_type = address::AddrType::ATPubKey; let sd = address::SpendingData::PubKeyASD(xpub.clone()); - let attrs = address::Attributes::new_bootstrap_era(Some(hdap)); + let attrs = address::Attributes::new_bootstrap_era(Some(hdap), default_network_magic()); let ea = address::ExtendedAddr::new(addr_type, sd, attrs); let ea_bytes = cbor!(ea).unwrap(); @@ -260,19 +305,21 @@ pub extern "C" fn wallet_public_to_address(xpub_ptr: *const c_uchar, payload_ptr } #[no_mangle] -pub extern "C" fn wallet_address_get_payload(addr_ptr: *const c_uchar, addr_sz: usize, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_address_get_payload( + addr_ptr: *const c_uchar, + addr_sz: usize, + out: *mut c_uchar, +) -> u32 { let addr_bytes = unsafe { read_data(addr_ptr, addr_sz) }; match address::ExtendedAddr::try_from_slice(&addr_bytes).ok() { None => (-1i32) as u32, - Some(r) => { - match r.attributes.derivation_path { - None => 0, - Some(dpath) => { - unsafe { write_data(dpath.as_ref(), out) }; - dpath.as_ref().len() as u32 - } + Some(r) => match r.attributes.derivation_path { + None => 0, + Some(dpath) => { + unsafe { write_data(dpath.as_ref(), out) }; + dpath.as_ref().len() as u32 } - } + }, } } @@ -280,11 +327,18 @@ pub extern "C" fn wallet_address_get_payload(addr_ptr: *const c_uchar, addr_sz: pub extern "C" fn wallet_payload_initiate(xpub_ptr: *const c_uchar, out: *mut c_uchar) { let xpub = unsafe { read_xpub(xpub_ptr) }; let hdkey = hdpayload::HDKey::new(&xpub); - unsafe { write_data(hdkey.as_ref(), out); } + unsafe { + write_data(hdkey.as_ref(), out); + } } #[no_mangle] -pub extern "C" fn wallet_payload_encrypt(key_ptr: *const c_uchar, path_array: *const c_uint, path_sz: usize, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_payload_encrypt( + key_ptr: *const c_uchar, + path_array: *const c_uint, + path_sz: usize, + out: *mut c_uchar, +) -> u32 { let key_bytes = unsafe { read_data(key_ptr, hdpayload::HDKEY_SIZE) }; let path_vec = unsafe { read_data_u32(path_array, path_sz) }; let hdkey = hdpayload::HDKey::from_slice(&key_bytes).unwrap(); @@ -298,7 +352,12 @@ pub extern "C" fn wallet_payload_encrypt(key_ptr: *const c_uchar, path_array: *c } #[no_mangle] -pub extern "C" fn wallet_payload_decrypt(key_ptr: *const c_uchar, payload_ptr: *const c_uchar, payload_sz: usize, out: *mut c_uint) -> u32 { +pub extern "C" fn wallet_payload_decrypt( + key_ptr: *const c_uchar, + payload_ptr: *const c_uchar, + payload_sz: usize, + out: *mut c_uint, +) -> u32 { let key_bytes = unsafe { read_data(key_ptr, hdpayload::HDKEY_SIZE) }; let payload_bytes = unsafe { read_data(payload_ptr, payload_sz) }; @@ -306,7 +365,7 @@ pub extern "C" fn wallet_payload_decrypt(key_ptr: *const c_uchar, payload_ptr: * let payload = hdpayload::HDAddressPayload::from_bytes(&payload_bytes); match hdkey.decrypt_path(&payload) { - Err(_) => 0xffffffff, + Err(_) => 0xffffffff, Ok(path) => { let v = path.as_ref(); unsafe { write_data_u32(v, out) }; @@ -316,7 +375,11 @@ pub extern "C" fn wallet_payload_decrypt(key_ptr: *const c_uchar, payload_ptr: * } #[no_mangle] -pub extern "C" fn wallet_txin_create(txid_ptr: *const c_uchar, index: u32, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_txin_create( + txid_ptr: *const c_uchar, + index: u32, + out: *mut c_uchar, +) -> u32 { let txid_bytes = unsafe { read_data(txid_ptr, tx::TxId::HASH_SIZE) }; let txid = tx::TxId::try_from_slice(&txid_bytes).unwrap(); @@ -329,7 +392,12 @@ pub extern "C" fn wallet_txin_create(txid_ptr: *const c_uchar, index: u32, out: } #[no_mangle] -pub extern "C" fn wallet_txout_create(ea_ptr: *const c_uchar, ea_sz: usize, amount: u32, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_txout_create( + ea_ptr: *const c_uchar, + ea_sz: usize, + amount: u32, + out: *mut c_uchar, +) -> u32 { let ea_bytes = unsafe { read_data(ea_ptr, ea_sz) }; let ea = address::ExtendedAddr::try_from_slice(&ea_bytes).unwrap(); @@ -351,12 +419,20 @@ pub extern "C" fn wallet_tx_new(out: *mut c_uchar) -> u32 { } #[no_mangle] -pub extern "C" fn wallet_tx_add_txin(tx_ptr: *const c_uchar, tx_sz: usize, txin_ptr: *const c_uchar, txin_sz: usize, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_tx_add_txin( + tx_ptr: *const c_uchar, + tx_sz: usize, + txin_ptr: *const c_uchar, + txin_sz: usize, + out: *mut c_uchar, +) -> u32 { let tx_bytes = unsafe { read_data(tx_ptr, tx_sz) }; let txin_bytes = unsafe { read_data(txin_ptr, txin_sz) }; - let mut tx : tx::Tx = cbor_event::de::RawCbor::from(&tx_bytes).deserialize().unwrap(); - let txin = cbor_event::de::RawCbor::from(&txin_bytes).deserialize().unwrap(); + let mut deserialiser = cbor_event::de::Deserializer::from(tx_bytes.as_slice()); + let mut tx: tx::Tx = deserialiser.deserialize_complete().unwrap(); + deserialiser = cbor_event::de::Deserializer::from(txin_bytes.as_slice()); + let txin = deserialiser.deserialize_complete().unwrap(); tx.add_input(txin); @@ -366,12 +442,20 @@ pub extern "C" fn wallet_tx_add_txin(tx_ptr: *const c_uchar, tx_sz: usize, txin_ } #[no_mangle] -pub extern "C" fn wallet_tx_add_txout(tx_ptr: *const c_uchar, tx_sz: usize, txout_ptr: *const c_uchar, txout_sz: usize, out: *mut c_uchar) -> u32 { +pub extern "C" fn wallet_tx_add_txout( + tx_ptr: *const c_uchar, + tx_sz: usize, + txout_ptr: *const c_uchar, + txout_sz: usize, + out: *mut c_uchar, +) -> u32 { let tx_bytes = unsafe { read_data(tx_ptr, tx_sz) }; let txout_bytes = unsafe { read_data(txout_ptr, txout_sz) }; - let mut tx : tx::Tx = cbor_event::de::RawCbor::from(&tx_bytes).deserialize().unwrap(); - let txout = cbor_event::de::RawCbor::from(&txout_bytes).deserialize().unwrap(); + let mut deserialiser = cbor_event::de::Deserializer::from(tx_bytes.as_slice()); + let mut tx: tx::Tx = deserialiser.deserialize_complete().unwrap(); + deserialiser = cbor_event::de::Deserializer::from(txout_bytes.as_slice()); + let txout = deserialiser.deserialize_complete().unwrap(); tx.add_output(txout); @@ -381,53 +465,80 @@ pub extern "C" fn wallet_tx_add_txout(tx_ptr: *const c_uchar, tx_sz: usize, txou } #[no_mangle] -pub extern "C" fn wallet_tx_sign(cfg_ptr: *const c_uchar, cfg_size: usize, xprv_ptr: *const c_uchar, tx_ptr: *const c_uchar, tx_sz: usize, out: *mut c_uchar) { - let cfg_bytes : Vec = unsafe { read_data(cfg_ptr, cfg_size) }; +pub extern "C" fn wallet_tx_sign( + cfg_ptr: *const c_uchar, + cfg_size: usize, + xprv_ptr: *const c_uchar, + tx_ptr: *const c_uchar, + tx_sz: usize, + out: *mut c_uchar, +) { + let cfg_bytes: Vec = unsafe { read_data(cfg_ptr, cfg_size) }; let cfg_str = String::from_utf8(cfg_bytes).unwrap(); - let cfg : Config = serde_json::from_str(cfg_str.as_str()).unwrap(); + let cfg: Config = serde_json::from_str(cfg_str.as_str()).unwrap(); let xprv = unsafe { read_xprv(xprv_ptr) }; let tx_bytes = unsafe { read_data(tx_ptr, tx_sz) }; - let tx : tx::Tx = cbor_event::de::RawCbor::from(&tx_bytes).deserialize().unwrap(); + let mut deserialiser = cbor_event::de::Deserializer::from(tx_bytes.as_slice()); + let tx: tx::Tx = deserialiser.deserialize_complete().unwrap(); let txinwitness = tx::TxInWitness::new(cfg.protocol_magic, &xprv, &tx.id()); let signature = match txinwitness { tx::TxInWitness::PkWitness(_, sig) => sig, - _ => unimplemented!() // this should never happen as we are signing for the tx anyway + _ => unimplemented!(), // this should never happen as we are signing for the tx anyway }; unsafe { write_signature(&signature, out) } } #[no_mangle] -pub extern "C" fn wallet_tx_verify(cfg_ptr: *const c_uchar, cfg_size: usize, xpub_ptr: *const c_uchar, tx_ptr: *const c_uchar, tx_sz: usize, sig_ptr: *const c_uchar) -> i32 { - let cfg_bytes : Vec = unsafe { read_data(cfg_ptr, cfg_size) }; +pub extern "C" fn wallet_tx_verify( + cfg_ptr: *const c_uchar, + cfg_size: usize, + xpub_ptr: *const c_uchar, + tx_ptr: *const c_uchar, + tx_sz: usize, + sig_ptr: *const c_uchar, +) -> i32 { + let cfg_bytes: Vec = unsafe { read_data(cfg_ptr, cfg_size) }; let cfg_str = String::from_utf8(cfg_bytes).unwrap(); - let cfg : Config = serde_json::from_str(cfg_str.as_str()).unwrap(); + let cfg: Config = serde_json::from_str(cfg_str.as_str()).unwrap(); let xpub = unsafe { read_xpub(xpub_ptr) }; let signature = unsafe { read_signature(sig_ptr) }; let tx_bytes = unsafe { read_data(tx_ptr, tx_sz) }; - let tx : tx::Tx = cbor_event::de::RawCbor::from(&tx_bytes).deserialize().unwrap(); + + let mut deserialiser = cbor_event::de::Deserializer::from(tx_bytes.as_slice()); + let tx: tx::Tx = deserialiser.deserialize_complete().unwrap(); let txinwitness = tx::TxInWitness::PkWitness(xpub, signature); - if txinwitness.verify_tx(cfg.protocol_magic, &tx) { 0 } else { -1 } + if txinwitness.verify_tx(cfg.protocol_magic, &tx) { + 0 + } else { + -1 + } } mod jrpc { - use serde::{Serialize}; + use serde::Serialize; use serde_json; - use std::os::raw::{c_uchar}; + use std::os::raw::c_uchar; #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] struct Error { failed: bool, loc: String, - msg: String + msg: String, } impl Error { - fn new(loc: String, msg: String) -> Self { Error { failed: true, loc: loc, msg: msg } } + fn new(loc: String, msg: String) -> Self { + Error { + failed: true, + loc: loc, + msg: msg, + } + } } pub fn fail(output_ptr: *mut c_uchar, file: &str, line: u32, msg: String) -> i32 { @@ -443,14 +554,20 @@ mod jrpc { #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] struct Success { failed: bool, - result: T + result: T, } impl Success { - fn new(result: T) -> Self { Success { failed: false, result: result } } + fn new(result: T) -> Self { + Success { + failed: false, + result: result, + } + } } pub fn ok(output_ptr: *mut c_uchar, result: T) -> i32 - where T: Serialize + where + T: Serialize, { let succ = Success::new(result); @@ -482,20 +599,26 @@ macro_rules! jrpc_fail { } macro_rules! jrpc_ok { - ($output_ptr:ident, $result:expr) => ({ + ($output_ptr:ident, $result:expr) => {{ jrpc::ok($output_ptr, $result) - }); - ($output_ptr:ident, $result:expr,) => ({ + }}; + ($output_ptr:ident, $result:expr,) => {{ jrpc_ok!($output_ptr, $result) - }); + }}; } macro_rules! jrpc_try { - ($output_ptr:ident, $expr:expr) => (match $expr { - Ok(val) => val, - Err(err) => { return jrpc_fail!($output_ptr, "{:?}", err); } - }); - ($output_ptr:ident, $expr:expr,) => (jrpc_try!($output_ptr, $expr)); + ($output_ptr:ident, $expr:expr) => { + match $expr { + Ok(val) => val, + Err(err) => { + return jrpc_fail!($output_ptr, "{:?}", err); + } + } + }; + ($output_ptr:ident, $expr:expr,) => { + jrpc_try!($output_ptr, $expr) + }; } #[derive(Debug)] @@ -508,41 +631,53 @@ enum Error { ErrorRindex(rindex::Error), } impl convert::From for Error { - fn from(j: string::FromUtf8Error) -> Self { Error::ErrorUtf8(j) } + fn from(j: string::FromUtf8Error) -> Self { + Error::ErrorUtf8(j) + } } impl convert::From for Error { - fn from(j: serde_json::error::Error) -> Self { Error::ErrorJSON(j) } + fn from(j: serde_json::error::Error) -> Self { + Error::ErrorJSON(j) + } } impl convert::From for Error { - fn from(j: cbor_event::Error) -> Self { Error::ErrorCBOR(j) } + fn from(j: cbor_event::Error) -> Self { + Error::ErrorCBOR(j) + } } impl convert::From for Error { - fn from(j: fee::Error) -> Self { Error::ErrorFEE(j) } + fn from(j: fee::Error) -> Self { + Error::ErrorFEE(j) + } } impl convert::From for Error { - fn from(j: bip39::Error) -> Self { Error::ErrorBip39(j) } + fn from(j: bip39::Error) -> Self { + Error::ErrorBip39(j) + } } impl convert::From for Error { - fn from(j: rindex::Error) -> Self { Error::ErrorRindex(j) } + fn from(j: rindex::Error) -> Self { + Error::ErrorRindex(j) + } } type Result = result::Result; fn input_string_(input_ptr: *const c_uchar, input_sz: usize) -> Result { - let input_bytes : Vec = unsafe { read_data(input_ptr, input_sz) }; + let input_bytes: Vec = unsafe { read_data(input_ptr, input_sz) }; let input = String::from_utf8(input_bytes)?; Ok(input) } macro_rules! input_json { - ($output_ptr:ident, $input_ptr:ident, $input_sz:ident) => ({ + ($output_ptr:ident, $input_ptr:ident, $input_sz:ident) => {{ let input = jrpc_try!($output_ptr, input_string_($input_ptr, $input_sz)); jrpc_try!($output_ptr, serde_json::from_str(input.as_str())) - }); - ($output_ptr:ident, $input_ptr:ident, $input_sz:ident,) => ({ + }}; + ($output_ptr:ident, $input_ptr:ident, $input_sz:ident,) => {{ input_json!($output_ptr, $input_ptr, $input_sz) - }); + }}; } #[derive(Serialize, Deserialize, Debug)] @@ -556,15 +691,16 @@ pub struct Bip44Wallet { impl Bip44Wallet { fn to_wallet(&self) -> bip44::Wallet { let root_key = bip44::RootLevel::from(self.root_cached_key.clone()); - bip44::Wallet::from_cached_key( - root_key, - self.derivation_scheme - ) + bip44::Wallet::from_cached_key(root_key, self.derivation_scheme) } } #[no_mangle] -pub extern "C" fn xwallet_create(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { +pub extern "C" fn xwallet_create( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { let seed = input_json!(output_ptr, input_ptr, input_sz); let derivation_scheme = hdwallet::DerivationScheme::V2; @@ -580,14 +716,17 @@ pub extern "C" fn xwallet_create(input_ptr: *const c_uchar, input_sz: usize, out root_cached_key: root_key.clone(), config: config, selection_policy: selection_policy, - derivation_scheme: derivation_scheme + derivation_scheme: derivation_scheme, }; jrpc_ok!(output_ptr, wallet) } #[no_mangle] -pub extern "C" fn xwallet_from_master_key(input_ptr: *const c_uchar, output_ptr: *mut c_uchar) -> i32 { +pub extern "C" fn xwallet_from_master_key( + input_ptr: *const c_uchar, + output_ptr: *mut c_uchar, +) -> i32 { let xprv = unsafe { read_xprv(input_ptr) }; let derivation_scheme = hdwallet::DerivationScheme::V2; @@ -602,7 +741,7 @@ pub extern "C" fn xwallet_from_master_key(input_ptr: *const c_uchar, output_ptr: root_cached_key: root_key.clone(), config: config, selection_policy: selection_policy, - derivation_scheme: derivation_scheme + derivation_scheme: derivation_scheme, }; jrpc_ok!(output_ptr, wallet) @@ -619,48 +758,51 @@ pub struct DaedalusWallet { impl DaedalusWallet { pub fn to_wallet(&self) -> rindex::Wallet { let root_key = rindex::RootKey::new(self.root_cached_key.clone(), self.derivation_scheme); - rindex::Wallet::from_root_key( - self.derivation_scheme, - root_key - ) + rindex::Wallet::from_root_key(self.derivation_scheme, root_key) } } #[no_mangle] -pub extern "C" fn xwallet_create_daedalus_mnemonic(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let mnemonics_phrase : String = input_json!(output_ptr, input_ptr, input_sz); +pub extern "C" fn xwallet_create_daedalus_mnemonic( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let mnemonics_phrase: String = input_json!(output_ptr, input_ptr, input_sz); let derivation_scheme = hdwallet::DerivationScheme::V1; let selection_policy = SelectionPolicy::FirstMatchFirst; let config = Config::default(); - let daedalus_wallet = jrpc_try!(output_ptr, rindex::Wallet::from_daedalus_mnemonics( - derivation_scheme, - &bip39::dictionary::ENGLISH, - mnemonics_phrase - )); + let daedalus_wallet = jrpc_try!( + output_ptr, + rindex::Wallet::from_daedalus_mnemonics( + derivation_scheme, + &bip39::dictionary::ENGLISH, + &mnemonics_phrase + ) + ); let wallet = DaedalusWallet { root_cached_key: (**daedalus_wallet).clone(), config: config, selection_policy: selection_policy, - derivation_scheme: derivation_scheme + derivation_scheme: derivation_scheme, }; jrpc_ok!(output_ptr, wallet) } - // TODO: write custom Serialize and Deserialize with String serialisation #[derive(PartialEq, Eq, Debug)] pub struct Coin(coin::Coin); -impl serde::Serialize for Coin -{ +impl serde::Serialize for Coin { #[inline] fn serialize(&self, serializer: S) -> result::Result - where S: serde::Serializer, + where + S: serde::Serializer, { - let v : u64 = *self.0; + let v: u64 = *self.0; serializer.serialize_str(&format!("{}", v)) } } @@ -673,22 +815,23 @@ impl<'de> serde::de::Visitor<'de> for CoinVisitor { } fn visit_str<'a, E>(self, v: &'a str) -> result::Result - where E: serde::de::Error + where + E: serde::de::Error, { - let i : u64 = match v.parse::() { + let i: u64 = match v.parse::() { Ok(v) => v, Err(err) => return Err(E::custom(format!("{:?}", err))), }; match coin::Coin::new(i) { Err(err) => Err(E::custom(format!("{}", err))), - Ok(h) => Ok(Coin(h)) + Ok(h) => Ok(Coin(h)), } } } -impl<'de> serde::Deserialize<'de> for Coin -{ +impl<'de> serde::Deserialize<'de> for Coin { fn deserialize(deserializer: D) -> result::Result - where D: serde::Deserializer<'de> + where + D: serde::Deserializer<'de>, { deserializer.deserialize_str(CoinVisitor()) } @@ -697,7 +840,7 @@ impl<'de> serde::Deserialize<'de> for Coin #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct TxOut { address: address::ExtendedAddr, - value: Coin + value: Coin, } impl TxOut { fn convert(&self) -> tx::TxOut { @@ -710,21 +853,21 @@ impl TxOut { #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct TxIn { - id: tx::TxId, - index: u32 + id: tx::TxId, + index: u32, } impl TxIn { fn convert(&self) -> tx::TxoPointer { tx::TxoPointer { id: self.id, - index: self.index + index: self.index, } } } #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct Input { - pub ptr: TxIn, + pub ptr: TxIn, pub value: TxOut, pub addressing: bip44::Addressing, } @@ -733,7 +876,7 @@ impl Input { txutils::Input { ptr: self.ptr.convert(), value: self.value.convert(), - addressing: self.addressing.clone() + addressing: self.addressing.clone(), } } } @@ -743,7 +886,7 @@ struct WalletSpendInput { wallet: Bip44Wallet, inputs: Vec, outputs: Vec, - change_addr: address::ExtendedAddr + change_addr: address::ExtendedAddr, } impl WalletSpendInput { fn get_inputs(&self) -> Vec::Addressing>> { @@ -763,8 +906,12 @@ struct WalletSpendOutput { } #[no_mangle] -pub extern "C" fn xwallet_spend(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : WalletSpendInput = input_json!(output_ptr, input_ptr, input_sz); +pub extern "C" fn xwallet_spend( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: WalletSpendInput = input_json!(output_ptr, input_ptr, input_sz); let change = input.change_addr.clone(); let wallet = input.wallet.to_wallet(); let config = input.wallet.config; @@ -775,7 +922,8 @@ pub extern "C" fn xwallet_spend(input_ptr: *const c_uchar, input_sz: usize, outp input.wallet.selection_policy, input.get_inputs().iter(), input.get_outputs(), - &txutils::OutputPolicy::One(input.change_addr)) + &txutils::OutputPolicy::One(input.change_addr) + ) ); let changed_used = txaux.tx.outputs.iter().any(|out| out.address == change); let cbor = jrpc_try!(output_ptr, cbor!(&txaux)); @@ -793,14 +941,14 @@ pub extern "C" fn xwallet_spend(input_ptr: *const c_uchar, input_sz: usize, outp pub struct TxInInfo { pub ptr: TxIn, pub value: Coin, - pub addressing: [u32;2] + pub addressing: [u32; 2], } impl TxInInfo { fn convert(&self) -> txutils::TxoPointerInfo<::Addressing> { txutils::TxoPointerInfo { txin: self.ptr.convert(), value: self.value.0, - address_identified: rindex::Addressing::new(self.addressing[0], self.addressing[1]) + address_identified: rindex::Addressing::new(self.addressing[0], self.addressing[1]), } } } @@ -809,7 +957,7 @@ impl TxInInfo { struct WalletMoveInput { wallet: DaedalusWallet, inputs: Vec, - output: address::ExtendedAddr + output: address::ExtendedAddr, } impl WalletMoveInput { fn get_inputs(&self) -> Vec::Addressing>> { @@ -818,8 +966,12 @@ impl WalletMoveInput { } #[no_mangle] -pub extern "C" fn xwallet_move(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : WalletMoveInput = input_json!(output_ptr, input_ptr, input_sz); +pub extern "C" fn xwallet_move( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: WalletMoveInput = input_json!(output_ptr, input_ptr, input_sz); let wallet = input.wallet.to_wallet(); let txaux = jrpc_try!( output_ptr, @@ -843,12 +995,12 @@ pub extern "C" fn xwallet_move(input_ptr: *const c_uchar, input_sz: usize, outpu #[derive(Serialize, Deserialize, Debug)] struct CreateWalletAccount { wallet: Bip44Wallet, - account: u32 + account: u32, } #[derive(Serialize, Deserialize, Debug)] struct Bip44Account { root_cached_key: hdwallet::XPub, - derivation_scheme: hdwallet::DerivationScheme + derivation_scheme: hdwallet::DerivationScheme, } impl Bip44Account { fn to_account(&self) -> bip44::Account { @@ -858,9 +1010,16 @@ impl Bip44Account { } #[no_mangle] -pub extern "C" fn xwallet_account(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : CreateWalletAccount = input_json!(output_ptr, input_ptr, input_sz); - let xprv = input.wallet.to_wallet().account(input.wallet.derivation_scheme, input.account); +pub extern "C" fn xwallet_account( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: CreateWalletAccount = input_json!(output_ptr, input_ptr, input_sz); + let xprv = input + .wallet + .to_wallet() + .account(input.wallet.derivation_scheme, input.account); let xpub = (*xprv).public(); jrpc_ok!( @@ -876,146 +1035,160 @@ pub extern "C" fn xwallet_account(input_ptr: *const c_uchar, input_sz: usize, ou struct GenAddressesInput { account: Bip44Account, address_type: bip44::AddrType, - indices: Vec + indices: Vec, } #[no_mangle] -pub extern "C" fn xwallet_addresses(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : GenAddressesInput = input_json!(output_ptr, input_ptr, input_sz); +pub extern "C" fn xwallet_addresses( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: GenAddressesInput = input_json!(output_ptr, input_ptr, input_sz); let account = input.account.to_account(); - let changelevel = jrpc_try!(output_ptr, account.change(input.account.derivation_scheme, input.address_type)); + let changelevel = jrpc_try!( + output_ptr, + account.change(input.account.derivation_scheme, input.address_type) + ); - let mut addresses : Vec = Vec::with_capacity(input.indices.len()); + let mut addresses: Vec = Vec::with_capacity(input.indices.len()); for index in input.indices.into_iter() { - let xpub = jrpc_try!(output_ptr, changelevel.index(input.account.derivation_scheme, index)); - let addr = address::ExtendedAddr::new_simple(*xpub); - addresses.push( - addr + let xpub = jrpc_try!( + output_ptr, + changelevel.index(input.account.derivation_scheme, index) ); + let addr = address::ExtendedAddr::new_simple(*xpub, default_network_magic()); + addresses.push(addr); } - jrpc_ok!( - output_ptr, - addresses - ) + jrpc_ok!(output_ptr, addresses) } #[no_mangle] -pub extern "C" fn xwallet_checkaddress(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : String = input_json!(output_ptr, input_ptr, input_sz); - let bytes : Vec = jrpc_try!(output_ptr, hex::decode(&input)); - let _ : address::ExtendedAddr = jrpc_try!(output_ptr, cbor_event::de::RawCbor::from(&bytes).deserialize()); +pub extern "C" fn xwallet_checkaddress( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: String = input_json!(output_ptr, input_ptr, input_sz); + let bytes: Vec = jrpc_try!(output_ptr, hex::decode(&input)); + let mut deserializer = cbor_event::de::Deserializer::from(bytes.as_slice()); + let _: address::ExtendedAddr = jrpc_try!(output_ptr, deserializer.deserialize_complete()); jrpc_ok!(output_ptr, true) } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct RandomAddressChecker { root_key: hdwallet::XPrv, - payload_key: hdpayload::HDKey + payload_key: hdpayload::HDKey, } #[no_mangle] -pub extern "C" fn random_address_checker_new(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let input : hdwallet::XPrv = input_json!(output_ptr, input_ptr, input_sz); +pub extern "C" fn random_address_checker_new( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let input: hdwallet::XPrv = input_json!(output_ptr, input_ptr, input_sz); let key = hdpayload::HDKey::new(&input.public()); let rac = RandomAddressChecker { root_key: input, - payload_key: key + payload_key: key, }; - jrpc_ok!( - output_ptr, - rac - ) + jrpc_ok!(output_ptr, rac) } #[no_mangle] -pub extern "C" fn random_address_checker_from_mnemonics(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { - let mnemonics_phrase : String = input_json!(output_ptr, input_ptr, input_sz); - - let wallet = jrpc_try!(output_ptr, rindex::Wallet::from_daedalus_mnemonics( - hdwallet::DerivationScheme::V1, - &bip39::dictionary::ENGLISH, - mnemonics_phrase - )); +pub extern "C" fn random_address_checker_from_mnemonics( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + let mnemonics_phrase: String = input_json!(output_ptr, input_ptr, input_sz); + + let wallet = jrpc_try!( + output_ptr, + rindex::Wallet::from_daedalus_mnemonics( + hdwallet::DerivationScheme::V1, + &bip39::dictionary::ENGLISH, + &mnemonics_phrase + ) + ); let xprv = (**wallet).clone(); let key = hdpayload::HDKey::new(&xprv.public()); let rac = RandomAddressChecker { root_key: xprv, - payload_key: key + payload_key: key, }; - jrpc_ok!( - output_ptr, - rac - ) + jrpc_ok!(output_ptr, rac) } - #[derive(Serialize, Deserialize, PartialEq, Debug)] struct RandomAddressCheck { checker: RandomAddressChecker, - addresses: Vec + addresses: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct FoundRandomAddress { address: address::ExtendedAddr, - addressing: [u32;2] + addressing: [u32; 2], } #[no_mangle] -pub extern "C" fn random_address_check(input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar) -> i32 { +pub extern "C" fn random_address_check( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { let RandomAddressCheck { checker, addresses } = input_json!(output_ptr, input_ptr, input_sz); let mut results = Vec::new(); for addr in addresses { if let Some(hdpa) = &addr.attributes.derivation_path.clone() { if let Ok(path) = checker.payload_key.decrypt_path(hdpa) { - results.push(FoundRandomAddress{ + results.push(FoundRandomAddress { address: addr, - addressing: [path.as_ref()[0], path.as_ref()[1]] + addressing: [path.as_ref()[0], path.as_ref()[1]], }) } } } - jrpc_ok!( - output_ptr, - results - ) + jrpc_ok!(output_ptr, results) } mod password_encryption_parameter { - pub const ITER : u32 = 19_162; - pub const SALT_SIZE : usize = 32; - pub const NONCE_SIZE : usize = 12; - pub const KEY_SIZE : usize = 32; - pub const TAG_SIZE : usize = 16; + pub const ITER: u32 = 19_162; + pub const SALT_SIZE: usize = 32; + pub const NONCE_SIZE: usize = 12; + pub const KEY_SIZE: usize = 32; + pub const TAG_SIZE: usize = 16; - pub const METADATA_SIZE : usize = SALT_SIZE + NONCE_SIZE + TAG_SIZE; + pub const METADATA_SIZE: usize = SALT_SIZE + NONCE_SIZE + TAG_SIZE; - pub const SALT_START : usize = 0; - pub const SALT_END : usize = SALT_START + SALT_SIZE; - pub const NONCE_START : usize = SALT_END; - pub const NONCE_END : usize = NONCE_START + NONCE_SIZE; - pub const TAG_START : usize = NONCE_END; - pub const TAG_END : usize = TAG_START + TAG_SIZE; - pub const ENCRYPTED_START : usize = TAG_END; + pub const SALT_START: usize = 0; + pub const SALT_END: usize = SALT_START + SALT_SIZE; + pub const NONCE_START: usize = SALT_END; + pub const NONCE_END: usize = NONCE_START + NONCE_SIZE; + pub const TAG_START: usize = NONCE_END; + pub const TAG_END: usize = TAG_START + TAG_SIZE; + pub const ENCRYPTED_START: usize = TAG_END; } #[no_mangle] -pub extern "C" fn encrypt_with_password( password_ptr: *const c_uchar - , password_sz: usize - , salt_ptr: *const c_uchar // expect 32 bytes - , nonce_ptr: *const c_uchar // expect 12 bytes - , data_ptr: *const c_uchar - , data_sz: usize - , output_ptr: *mut c_uchar - ) - -> i32 -{ +pub extern "C" fn encrypt_with_password( + password_ptr: *const c_uchar, + password_sz: usize, + salt_ptr: *const c_uchar, // expect 32 bytes + nonce_ptr: *const c_uchar, // expect 12 bytes + data_ptr: *const c_uchar, + data_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { use password_encryption_parameter::*; let password = unsafe { read_data(password_ptr, password_sz) }; - let salt = unsafe { read_data(salt_ptr, SALT_SIZE) }; - let nonce = unsafe { read_data(nonce_ptr, NONCE_SIZE) }; - let data = unsafe { read_data(data_ptr, data_sz) }; + let salt = unsafe { read_data(salt_ptr, SALT_SIZE) }; + let nonce = unsafe { read_data(nonce_ptr, NONCE_SIZE) }; + let data = unsafe { read_data(data_ptr, data_sz) }; let key = { let mut mac = Hmac::new(Sha512::new(), &password); @@ -1024,11 +1197,10 @@ pub extern "C" fn encrypt_with_password( password_ptr: *const c_uchar key }; - let mut tag = [0;TAG_SIZE]; - let mut encrypted : Vec = repeat(0).take(data.len()).collect(); + let mut tag = [0; TAG_SIZE]; + let mut encrypted: Vec = repeat(0).take(data.len()).collect(); { - ChaCha20Poly1305::new(&key, &nonce, &[]) - .encrypt(&data, &mut encrypted, &mut tag); + ChaCha20Poly1305::new(&key, &nonce, &[]).encrypt(&data, &mut encrypted, &mut tag); } let mut output = Vec::with_capacity(data.len() + METADATA_SIZE); @@ -1042,16 +1214,14 @@ pub extern "C" fn encrypt_with_password( password_ptr: *const c_uchar output.len() as i32 } - #[no_mangle] -pub extern "C" fn decrypt_with_password( password_ptr: *const c_uchar - , password_sz: usize - , data_ptr: *const c_uchar - , data_sz: usize - , output_ptr: *mut c_uchar - ) - -> i32 -{ +pub extern "C" fn decrypt_with_password( + password_ptr: *const c_uchar, + password_sz: usize, + data_ptr: *const c_uchar, + data_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { use password_encryption_parameter::*; let password = unsafe { read_data(password_ptr, password_sz) }; let data = unsafe { read_data(data_ptr, data_sz) }; @@ -1073,11 +1243,9 @@ pub extern "C" fn decrypt_with_password( password_ptr: *const c_uchar key }; - let mut decrypted : Vec = repeat(0).take(encrypted.len()).collect(); - let decryption_succeed = { - ChaCha20Poly1305::new(&key, &nonce, &[]) - .decrypt(&encrypted, &mut decrypted, &tag) - }; + let mut decrypted: Vec = repeat(0).take(encrypted.len()).collect(); + let decryption_succeed = + { ChaCha20Poly1305::new(&key, &nonce, &[]).decrypt(&encrypted, &mut decrypted, &tag) }; if decryption_succeed { unsafe { write_data(&decrypted, output_ptr) }; From bf644a10c22e232ca4d0b16cb9308a12cfc62a48 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Fri, 18 Jan 2019 17:22:20 +0300 Subject: [PATCH 24/33] Implemented `redemption_private_to_address` to `lib.rs` for old API --- wallet-wasm/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index c17465d..14edcd4 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -25,6 +25,7 @@ use self::cardano::wallet::{ scheme::{SelectionPolicy, Wallet}, }; use self::cardano::{coin, fee, tx, txutils, util::hex}; +use self::cardano::{redeem, txbuild}; use self::cardano::util::try_from_slice::TryFromSlice; @@ -1254,3 +1255,40 @@ pub extern "C" fn decrypt_with_password( -1 } } + +#[no_mangle] +pub extern "C" fn redemption_private_to_address( + private_ptr: *const c_uchar, + protocol_magic: u32, + out: *mut c_uchar, +) -> u32 { + let priv_key = unsafe { + let slice: &[u8] = std::slice::from_raw_parts(private_ptr, redeem::PRIVATEKEY_SIZE); + redeem::PrivateKey::from_slice(slice).unwrap() + }; + let pub_key = priv_key.public(); + let addr_type = address::AddrType::ATRedeem; + let addr_data = address::SpendingData::RedeemASD(pub_key.clone()); + let magic = cardano::config::ProtocolMagic::new(protocol_magic); + let addr_attr = address::Attributes::new_bootstrap_era(None,magic.into()); + let address = address::ExtendedAddr::new(addr_type, addr_data, addr_attr)); + let address_bytes = cbor!(address).unwrap(); + unsafe { write_data(&address_bytes, out) } + return address_bytes.len() as u32; +} + +#[derive(Serialize, Deserialize, Debug)] +struct WalletRedeemInput { + redemption_key: redeem::PrivateKey, + input: TxIn, + output: TxOut, +} + +#[no_mangle] +pub extern "C" fn xwallet_redeem( + input_ptr: *const c_uchar, + input_sz: usize, + output_ptr: *mut c_uchar, +) -> i32 { + +} From 65214605dc600175ad6848991b47ab3019ae3dfd Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Fri, 18 Jan 2019 18:01:22 +0300 Subject: [PATCH 25/33] Implemented `xwallet_redeem` in old API `lib.rs` --- wallet-wasm/src/lib.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index 14edcd4..b3e0e08 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -1279,16 +1279,44 @@ pub extern "C" fn redemption_private_to_address( #[derive(Serialize, Deserialize, Debug)] struct WalletRedeemInput { + protocol_magic: u32, redemption_key: redeem::PrivateKey, input: TxIn, output: TxOut, } +#[derive(Serialize, Deserialize, Debug)] +struct WalletRedeemOutput { + cbor_encoded_tx: Vec, +} + #[no_mangle] pub extern "C" fn xwallet_redeem( input_ptr: *const c_uchar, input_sz: usize, output_ptr: *mut c_uchar, ) -> i32 { - + let data: WalletRedeemInput = input_json!(output_ptr, input_ptr, input_sz); + let &mut txbuilder = txbuild::TxBuilder::new(); + txbuilder.add_input(&data.input.convert(), data.output.value.0); + txbuilder.add_output_value(&data.output.convert()); + let tx: result::Result = txbuilder.make_tx() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); + let txaux: tx::TxAux = jrpc_try!(output_ptr, tx.and_then(|tx| { + let magic = cardano::config::ProtocolMagic::new(data.protocol_magic); + let witness = tx::TxInWitness::redeem(magic, &data.redemption_key, &tx.0.id()); + let &mut finalized = txbuild::TxFinalized::new(tx); + let witness_result: result::Result<(), JsValue> = finalized + .add_witness(witness) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); + return witness_result.and_then(|| { + return finalized + .make_txaux() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); + }); + })); + let cbor = jrpc_try!(output_ptr, cbor!(&txaux)); + jrpc_ok!(output_ptr, WalletRedeemOutput { + cbor_encoded_tx: cbor + }) } From 1ae6d33359441bf7245fa340a7813890edf6d2af Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Fri, 18 Jan 2019 18:25:42 +0300 Subject: [PATCH 26/33] Fixed some code in redemption functions in old API --- wallet-wasm/src/lib.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index b3e0e08..b68805b 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -1279,7 +1279,7 @@ pub extern "C" fn redemption_private_to_address( #[derive(Serialize, Deserialize, Debug)] struct WalletRedeemInput { - protocol_magic: u32, + protocol_magic: cardano::config::ProtocolMagic, redemption_key: redeem::PrivateKey, input: TxIn, output: TxOut, @@ -1300,21 +1300,24 @@ pub extern "C" fn xwallet_redeem( let &mut txbuilder = txbuild::TxBuilder::new(); txbuilder.add_input(&data.input.convert(), data.output.value.0); txbuilder.add_output_value(&data.output.convert()); - let tx: result::Result = txbuilder.make_tx() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); - let txaux: tx::TxAux = jrpc_try!(output_ptr, tx.and_then(|tx| { - let magic = cardano::config::ProtocolMagic::new(data.protocol_magic); - let witness = tx::TxInWitness::redeem(magic, &data.redemption_key, &tx.0.id()); - let &mut finalized = txbuild::TxFinalized::new(tx); - let witness_result: result::Result<(), JsValue> = finalized - .add_witness(witness) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); - return witness_result.and_then(|| { - return finalized - .make_txaux() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})); - }); - })); + let tx: tx::Tx = jrpc_try!( + output_ptr, + txbuilder.make_tx() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + ); + let witness = tx::TxInWitness::redeem( + data.protocol_magic, &data.redemption_key, &tx.0.id()); + let &mut finalized = txbuild::TxFinalized::new(tx); + jrpc_try!( + output_ptr, + finalized.add_witness(witness) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + ); + let txaux: tx::TxAux = jrpc_try!( + output_ptr, + finalized.make_txaux() + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + ); let cbor = jrpc_try!(output_ptr, cbor!(&txaux)); jrpc_ok!(output_ptr, WalletRedeemOutput { cbor_encoded_tx: cbor From 42d26d222a3363420e853d5ccfe6ff9e4a3a5cd3 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Fri, 18 Jan 2019 20:37:56 +0300 Subject: [PATCH 27/33] Implemented `Redemption.js` connectors for new redemption functions in old API --- js/Redemption.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ js/index.js | 2 ++ 2 files changed, 62 insertions(+) create mode 100644 js/Redemption.js diff --git a/js/Redemption.js b/js/Redemption.js new file mode 100644 index 0000000..9389e2c --- /dev/null +++ b/js/Redemption.js @@ -0,0 +1,60 @@ +import iconv from 'iconv-lite'; +import RustModule from './RustModule'; +import { newArray, newArray0, copyArray } from './utils/arrays'; +import { apply } from './utils/functions'; + +export const REDEMPTION_PRIVATE_KEY_SIZE = 32; +const MAX_OUTPUT_SIZE = 4096; + +/** + * @param module - the WASM module that is used for crypto operations + * @param redemptionKey - the private redemption key, needs to be {@link REDEMPTION_PRIVATE_KEY_SIZE} + * @param magic - protocol magic integer + * @returns {*} - returns false if the seed is not of the valid length, or returns the redemption address + */ +export const redemptionKeyToAddress = (module, redemptionKey, magic) => { + if (redemptionKey.length !== REDEMPTION_PRIVATE_KEY_SIZE) { + return false; + } + const bufkey = newArray(module, seed); + const bufaddr = newArray0(module, 1024); + const rs = module.redemption_private_to_address(bufkey, magic, bufaddr); + let result = copyArray(module, bufaddr, rs); + module.dealloc(bufkey); + module.dealloc(bufaddr); + return result; +}; + +/** + * @param module - the WASM module that is used for crypto operations + * @param redemptionKey - the private redemption key, needs to be {@link REDEMPTION_PRIVATE_KEY_SIZE} + * @param input - single input as: { id, index } + * @param output - single output as: { address, value } + * @param magic - protocol magic integer + * @returns {*} - returns false if the seed is not of the valid length, or returns the response as: { cbor_encoded_tx } + */ +export const createRedemptionTransaction = (module, redemptionKey, input, output, magic) => { + if (redemptionKey.length !== REDEMPTION_PRIVATE_KEY_SIZE) { + return false; + } + const input_obj = { protocol_magic: magic, redemption_key: redemptionKey, input, output }; + const input_str = JSON.stringify(input_obj); + const input_array = iconv.encode(input_str, 'utf8'); + + const bufinput = newArray(module, input_array); + const bufoutput = newArray0(module, MAX_OUTPUT_SIZE); + + let rsz = module.xwallet_redeem(bufinput, input_array.length, bufoutput); + let output_array = copyArray(module, bufoutput, rsz); + + module.dealloc(bufoutput); + module.dealloc(bufinput); + + let output_str = iconv.decode(Buffer.from(output_array), 'utf8'); + return JSON.parse(output_str); +}; + +export default { + redemptionKeyToAddress: apply(redemptionKeyToAddress, RustModule), + createRedemptionTransaction: apply(createRedemptionTransaction, RustModule), +}; diff --git a/js/index.js b/js/index.js index 9efdb4e..eb011a4 100644 --- a/js/index.js +++ b/js/index.js @@ -6,6 +6,7 @@ import Payload from './Payload.js'; import Tx from './Tx.js'; import Config from './Config.js'; import Wallet from './Wallet.js'; +import Redemption from './Redemption.js'; import RandomAddressChecker from './RandomAddressChecker'; import PasswordProtect from './PasswordProtect'; @@ -19,6 +20,7 @@ module.exports = { RandomAddressChecker, HdWallet, Wallet, + Redemption, Config, PasswordProtect, }; From 94606adcf71c8bbca8473cdb3975cec5929694a9 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Tue, 22 Jan 2019 03:21:34 +0300 Subject: [PATCH 28/33] Implemented js-tests for new redemption connectors, and fixed errors --- js/Redemption.js | 4 ++- js/tests/redemption.js | 82 ++++++++++++++++++++++++++++++++++++++++++ package.json | 5 ++- wallet-wasm/src/lib.rs | 39 ++++++++++++++------ 4 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 js/tests/redemption.js diff --git a/js/Redemption.js b/js/Redemption.js index 9389e2c..eac1fc4 100644 --- a/js/Redemption.js +++ b/js/Redemption.js @@ -16,7 +16,7 @@ export const redemptionKeyToAddress = (module, redemptionKey, magic) => { if (redemptionKey.length !== REDEMPTION_PRIVATE_KEY_SIZE) { return false; } - const bufkey = newArray(module, seed); + const bufkey = newArray(module, redemptionKey); const bufaddr = newArray0(module, 1024); const rs = module.redemption_private_to_address(bufkey, magic, bufaddr); let result = copyArray(module, bufaddr, rs); @@ -37,6 +37,8 @@ export const createRedemptionTransaction = (module, redemptionKey, input, output if (redemptionKey.length !== REDEMPTION_PRIVATE_KEY_SIZE) { return false; } + redemptionKey = [...Buffer.from(redemptionKey)]; + input.id = Buffer.from(input.id).toString('hex'); const input_obj = { protocol_magic: magic, redemption_key: redemptionKey, input, output }; const input_str = JSON.stringify(input_obj); const input_array = iconv.encode(input_str, 'utf8'); diff --git a/js/tests/redemption.js b/js/tests/redemption.js new file mode 100644 index 0000000..c5fc816 --- /dev/null +++ b/js/tests/redemption.js @@ -0,0 +1,82 @@ +const expect = require('chai').expect; +const CardanoCrypto = require('../../dist/index.js'); +const bs58 = require('bs58'); +const cbor = require('cbor'); +const crc = require('crc'); + +const TEST_VECTORS = [ + { + redemptionKey: Buffer.from('qXQWDxI3JrlFRtC4SeQjeGzLbVXWBomYPbNO1Vfm1T4=', 'base64'), + expectedAddress: 'Ae2tdPwUPEZ1xZTLczMGYL5PhADi1nbFmESqS9vUuLkyUe1isQ77TRUE9NS', + txId: new Uint8Array([0xaa,0xd7,0x8a,0x13,0xb5,0x0a,0x01,0x4a,0x24,0x63,0x3c,0x7d,0x44,0xfd,0x8f,0x8d,0x18,0xf6,0x7b,0xbb,0x3f,0xa9,0xcb,0xce,0xdf,0x83,0x4a,0xc8,0x99,0x75,0x9d,0xcd]), + txOutIndex: 1, + coinValue: 12345678 + } +]; + +let mkTest = (i) => { + const { redemptionKey, expectedAddress, txId, txOutIndex, coinValue } = TEST_VECTORS[i]; + const cfg = CardanoCrypto.Config.defaultConfig(); + + describe('Test ' + i, function() { + before(async () => { + await CardanoCrypto.loadRustModule() + }); + + it('generates valid address', function() { + const a = CardanoCrypto.Redemption.redemptionKeyToAddress(redemptionKey, cfg.protocol_magic); + const [tagged, checksum] = cbor.decode(Buffer.from(a)); + expect(crc.crc32(tagged.value)).equal(checksum); + }); + + it('creates address matching expected', function() { + const a = CardanoCrypto.Redemption.redemptionKeyToAddress(redemptionKey, cfg.protocol_magic); + expect(bs58.encode(Buffer.from(a))).equal(expectedAddress) + }); + + it('generates valid transaction', function () { + const address = CardanoCrypto.Redemption.redemptionKeyToAddress(redemptionKey, cfg.protocol_magic); + const input = { id: txId, index: txOutIndex }; + const output = { address: bs58.encode(Buffer.from(address)), value: JSON.stringify(coinValue) }; + const { result: { cbor_encoded_tx } } = CardanoCrypto.Redemption.createRedemptionTransaction(redemptionKey, input, output, cfg.protocol_magic); + + // destruct result transaction + const [[resultInputs, resultOutputs, attributes], resultWitnesses] = cbor.decode(Buffer.from(cbor_encoded_tx)); + + // validate inputs + expect(resultInputs.length).equal(1); + expect(resultInputs[0].length).equal(2); + const [[intputType, inputTagged]] = resultInputs; + expect(intputType).equal(0); + const [inputId, inputIndex] = cbor.decode(inputTagged.value); + expect(inputIndex).equal(txOutIndex); + expect(inputId).deep.equal(txId); + + // validate outputs + expect(resultInputs.length).equal(1); + expect(resultInputs[0].length).equal(2); + const [[outputAddress, outputValue]] = resultOutputs; + expect(cbor.encode(outputAddress)).deep.equal(address); + expect(outputValue).equal(coinValue); + + // validate witness + expect(resultWitnesses.length).equal(1); + expect(resultWitnesses[0].length).equal(2); + const [[witnessType, witnessTagged]] = resultWitnesses; + expect(witnessType).equal(2); + const [witnessPub, witnessSign] = cbor.decode(witnessTagged.value); + + // TODO: expecting fake witness data - fix after implementing signing in Rust + expect(witnessPub.toString('hex')) + .equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + expect(witnessSign.toString('hex')) + .equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + }); + }); +}; + +describe('Test redemption', function() { + for (let i = 0; i < TEST_VECTORS.length; i++) { + mkTest(i); + } +}); diff --git a/package.json b/package.json index aec49cd..82d4369 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,10 @@ "mocha": "5.0.2", "nodemon": "1.17.1", "wasm-loader": "1.3.0", - "webpack": "^3.11.0" + "webpack": "^3.11.0", + "bs58": "4.0.1", + "cbor": "4.1.4", + "crc": "3.8.0" }, "peerDependencies": { "bip39": "2.3.0" diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index b68805b..0882573 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -1269,9 +1269,9 @@ pub extern "C" fn redemption_private_to_address( let pub_key = priv_key.public(); let addr_type = address::AddrType::ATRedeem; let addr_data = address::SpendingData::RedeemASD(pub_key.clone()); - let magic = cardano::config::ProtocolMagic::new(protocol_magic); + let magic = cardano::config::ProtocolMagic::from(protocol_magic); let addr_attr = address::Attributes::new_bootstrap_era(None,magic.into()); - let address = address::ExtendedAddr::new(addr_type, addr_data, addr_attr)); + let address = address::ExtendedAddr::new(addr_type, addr_data, addr_attr); let address_bytes = cbor!(address).unwrap(); unsafe { write_data(&address_bytes, out) } return address_bytes.len() as u32; @@ -1280,7 +1280,7 @@ pub extern "C" fn redemption_private_to_address( #[derive(Serialize, Deserialize, Debug)] struct WalletRedeemInput { protocol_magic: cardano::config::ProtocolMagic, - redemption_key: redeem::PrivateKey, + redemption_key: [u8; redeem::PRIVATEKEY_SIZE], // hex input: TxIn, output: TxOut, } @@ -1297,29 +1297,48 @@ pub extern "C" fn xwallet_redeem( output_ptr: *mut c_uchar, ) -> i32 { let data: WalletRedeemInput = input_json!(output_ptr, input_ptr, input_sz); - let &mut txbuilder = txbuild::TxBuilder::new(); + let mut txbuilder = txbuild::TxBuilder::new(); txbuilder.add_input(&data.input.convert(), data.output.value.0); txbuilder.add_output_value(&data.output.convert()); let tx: tx::Tx = jrpc_try!( output_ptr, txbuilder.make_tx() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) ); - let witness = tx::TxInWitness::redeem( - data.protocol_magic, &data.redemption_key, &tx.0.id()); - let &mut finalized = txbuild::TxFinalized::new(tx); + print!("Tx: {}", tx); + let redemption_key = jrpc_try!( + output_ptr, + redeem::PrivateKey::from_slice(&data.redemption_key) + ); + print!("Key: {}", redemption_key); + let witness = jrpc_try!( + output_ptr, + create_redemption_witness(data.protocol_magic, &redemption_key, &tx.id()) + ); + let mut finalized = txbuild::TxFinalized::new(tx); jrpc_try!( output_ptr, finalized.add_witness(witness) - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) ); let txaux: tx::TxAux = jrpc_try!( output_ptr, finalized.make_txaux() - .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) ); let cbor = jrpc_try!(output_ptr, cbor!(&txaux)); jrpc_ok!(output_ptr, WalletRedeemOutput { cbor_encoded_tx: cbor }) } + +fn create_redemption_witness( + protocol_magic: cardano::config::ProtocolMagic, + key: &redeem::PrivateKey, + txid: &tx::TxId, +) -> redeem::Result { + // TODO: actual implementation + let s32 = (0..64).map(|_| "f").collect::(); + let s64 = (0..128).map(|_| "f").collect::(); + let pk = redeem::PublicKey::from_hex(&s32); + let sg = redeem::Signature::from_hex(&s64); + return pk.and_then(|k| sg.map(|s| (k, s))) + .map(|(k,s)| tx::TxInWitness::RedeemWitness(k, s)); +} \ No newline at end of file From a5f4a2e558c815c1d3392e968e442f44c4f18076 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Tue, 22 Jan 2019 03:30:02 +0300 Subject: [PATCH 29/33] Simplified `redemption_private_to_address` function --- wallet-wasm/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index 0882573..1abd4e1 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -1267,11 +1267,8 @@ pub extern "C" fn redemption_private_to_address( redeem::PrivateKey::from_slice(slice).unwrap() }; let pub_key = priv_key.public(); - let addr_type = address::AddrType::ATRedeem; - let addr_data = address::SpendingData::RedeemASD(pub_key.clone()); let magic = cardano::config::ProtocolMagic::from(protocol_magic); - let addr_attr = address::Attributes::new_bootstrap_era(None,magic.into()); - let address = address::ExtendedAddr::new(addr_type, addr_data, addr_attr); + let (_, address) = tx::redeem_pubkey_to_txid(&pub_key, magic); let address_bytes = cbor!(address).unwrap(); unsafe { write_data(&address_bytes, out) } return address_bytes.len() as u32; From 2578dc149efa9e7a68e6b1f53650304d5d37a897 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Wed, 23 Jan 2019 23:20:42 +0300 Subject: [PATCH 30/33] Replaced mock signature by actual witness creation (and updated rust dependency revision) --- js/tests/redemption.js | 8 +++++--- rust | 2 +- wallet-wasm/src/lib.rs | 22 +++------------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/js/tests/redemption.js b/js/tests/redemption.js index c5fc816..545c468 100644 --- a/js/tests/redemption.js +++ b/js/tests/redemption.js @@ -8,6 +8,8 @@ const TEST_VECTORS = [ { redemptionKey: Buffer.from('qXQWDxI3JrlFRtC4SeQjeGzLbVXWBomYPbNO1Vfm1T4=', 'base64'), expectedAddress: 'Ae2tdPwUPEZ1xZTLczMGYL5PhADi1nbFmESqS9vUuLkyUe1isQ77TRUE9NS', + expectedPublicKey: 'faf8ed5be127c8ea22e3a0f1b9335f910aaa672e96c41d7cbdcaed897a450667', + expectedSignature: '80ed81b9f3683684fdb7c286eb7cfa63580a239ed6bf67f71643d290de2206855171003c7f33cad04e9fa28ee5d5c18c4e5f0d788ae2a63fa492ba7b59995c03', txId: new Uint8Array([0xaa,0xd7,0x8a,0x13,0xb5,0x0a,0x01,0x4a,0x24,0x63,0x3c,0x7d,0x44,0xfd,0x8f,0x8d,0x18,0xf6,0x7b,0xbb,0x3f,0xa9,0xcb,0xce,0xdf,0x83,0x4a,0xc8,0x99,0x75,0x9d,0xcd]), txOutIndex: 1, coinValue: 12345678 @@ -15,7 +17,7 @@ const TEST_VECTORS = [ ]; let mkTest = (i) => { - const { redemptionKey, expectedAddress, txId, txOutIndex, coinValue } = TEST_VECTORS[i]; + const { redemptionKey, expectedAddress, expectedPublicKey, expectedSignature, txId, txOutIndex, coinValue } = TEST_VECTORS[i]; const cfg = CardanoCrypto.Config.defaultConfig(); describe('Test ' + i, function() { @@ -68,9 +70,9 @@ let mkTest = (i) => { // TODO: expecting fake witness data - fix after implementing signing in Rust expect(witnessPub.toString('hex')) - .equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + .equal(expectedPublicKey); expect(witnessSign.toString('hex')) - .equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + .equal(expectedSignature); }); }); }; diff --git a/rust b/rust index e869c9d..467727c 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit e869c9d7d231114526f68196e53ffea6c131160d +Subproject commit 467727c5fee7921704475f74ca9f96a1df28cbaa diff --git a/wallet-wasm/src/lib.rs b/wallet-wasm/src/lib.rs index 1abd4e1..7ca69b7 100644 --- a/wallet-wasm/src/lib.rs +++ b/wallet-wasm/src/lib.rs @@ -483,7 +483,7 @@ pub extern "C" fn wallet_tx_sign( let mut deserialiser = cbor_event::de::Deserializer::from(tx_bytes.as_slice()); let tx: tx::Tx = deserialiser.deserialize_complete().unwrap(); - let txinwitness = tx::TxInWitness::new(cfg.protocol_magic, &xprv, &tx.id()); + let txinwitness = tx::TxInWitness::new_extended_pk(cfg.protocol_magic, &xprv, &tx.id()); let signature = match txinwitness { tx::TxInWitness::PkWitness(_, sig) => sig, @@ -1307,10 +1307,8 @@ pub extern "C" fn xwallet_redeem( redeem::PrivateKey::from_slice(&data.redemption_key) ); print!("Key: {}", redemption_key); - let witness = jrpc_try!( - output_ptr, - create_redemption_witness(data.protocol_magic, &redemption_key, &tx.id()) - ); + let witness = tx::TxInWitness::new_redeem_pk( + data.protocol_magic, &redemption_key, &tx.id()); let mut finalized = txbuild::TxFinalized::new(tx); jrpc_try!( output_ptr, @@ -1325,17 +1323,3 @@ pub extern "C" fn xwallet_redeem( cbor_encoded_tx: cbor }) } - -fn create_redemption_witness( - protocol_magic: cardano::config::ProtocolMagic, - key: &redeem::PrivateKey, - txid: &tx::TxId, -) -> redeem::Result { - // TODO: actual implementation - let s32 = (0..64).map(|_| "f").collect::(); - let s64 = (0..128).map(|_| "f").collect::(); - let pk = redeem::PublicKey::from_hex(&s32); - let sg = redeem::Signature::from_hex(&s64); - return pk.and_then(|k| sg.map(|s| (k, s))) - .map(|(k,s)| tx::TxInWitness::RedeemWitness(k, s)); -} \ No newline at end of file From 7c490fd2392fee0a7da35bb4d2a4892d2dbc6862 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Wed, 23 Jan 2019 23:31:38 +0300 Subject: [PATCH 31/33] Removed fixed TODO comment --- js/tests/redemption.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/tests/redemption.js b/js/tests/redemption.js index 545c468..e25ea53 100644 --- a/js/tests/redemption.js +++ b/js/tests/redemption.js @@ -67,8 +67,6 @@ let mkTest = (i) => { const [[witnessType, witnessTagged]] = resultWitnesses; expect(witnessType).equal(2); const [witnessPub, witnessSign] = cbor.decode(witnessTagged.value); - - // TODO: expecting fake witness data - fix after implementing signing in Rust expect(witnessPub.toString('hex')) .equal(expectedPublicKey); expect(witnessSign.toString('hex')) From dabf15519574a5573cbb1fddfb763ee1786bfc5c Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Fri, 25 Jan 2019 01:35:39 +0300 Subject: [PATCH 32/33] Updated rust dependency to include derivation addressing fix --- rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust b/rust index 467727c..9bef10d 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit 467727c5fee7921704475f74ca9f96a1df28cbaa +Subproject commit 9bef10d1bbd1321d98aa6b30ba030631806ad153 From 3dcd62d6f10d7281e08a236f3ca5dff92e04e90a Mon Sep 17 00:00:00 2001 From: Nicolas Di Prima Date: Mon, 28 Jan 2019 11:19:26 +0000 Subject: [PATCH 33/33] update deps --- package-lock.json | 86 ++++++++++++++++++++++++++++++++++++++++++ wallet-wasm/Cargo.lock | 2 + wallet-wasm/Cargo.toml | 2 +- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 6b0c564..7774c4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1130,6 +1130,15 @@ } } }, + "base-x": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", + "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", @@ -1142,6 +1151,12 @@ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", "dev": true }, + "bignumber.js": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.0.2.tgz", + "integrity": "sha512-EiuvFrnbv0jFixEQ9f58jo7X0qI2lNGIr/MxntmVzQc5JUweDSh8y8hbTCAomFtqwUPIOWcLXP0VEOSZTG7FFw==", + "dev": true + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", @@ -1337,6 +1352,15 @@ "electron-to-chromium": "1.3.36" } }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dev": true, + "requires": { + "base-x": "3.0.5" + } + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -1401,6 +1425,26 @@ "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, + "cbor": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-4.1.4.tgz", + "integrity": "sha512-SqNWyQnnYtKAPLA7lupvuGKrEgoF2rR/7I9rXdmW/9uxtmKdltthHTf8hfLLN1SIkoAFwz/jb6+VZuaHv3Lv6Q==", + "dev": true, + "requires": { + "bignumber.js": "8.0.2", + "commander": "2.19.0", + "json-text-sequence": "0.1.1", + "nofilter": "1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } + } + }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", @@ -1694,6 +1738,27 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "requires": { + "buffer": "5.2.1" + }, + "dependencies": { + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "1.3.0", + "ieee754": "1.1.11" + } + } + } + }, "create-ecdh": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.1.tgz", @@ -1836,6 +1901,12 @@ "isobject": "3.0.1" } }, + "delimit-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/delimit-stream/-/delimit-stream-0.1.0.tgz", + "integrity": "sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs=", + "dev": true + }, "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", @@ -4033,6 +4104,15 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, + "json-text-sequence": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/json-text-sequence/-/json-text-sequence-0.1.1.tgz", + "integrity": "sha1-py8hfcSvxGKf/1/rME3BvVGi89I=", + "dev": true, + "requires": { + "delimit-stream": "0.1.0" + } + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -4509,6 +4589,12 @@ } } }, + "nofilter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.1.tgz", + "integrity": "sha512-p2J1rODXWkqM5NAT7TMUmKGAcnUreT4uSTUar+vdrquOXV5vn8oHW2hBU0LeQSh6nwz4xkxcpRE89UGk+eQUcw==", + "dev": true + }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", diff --git a/wallet-wasm/Cargo.lock b/wallet-wasm/Cargo.lock index 9734ef0..44daa70 100644 --- a/wallet-wasm/Cargo.lock +++ b/wallet-wasm/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "cardano" version = "0.1.1" diff --git a/wallet-wasm/Cargo.toml b/wallet-wasm/Cargo.toml index fb67fe1..0882b92 100644 --- a/wallet-wasm/Cargo.toml +++ b/wallet-wasm/Cargo.toml @@ -20,7 +20,7 @@ path = "../rust/cardano" features = [ "generic-serialization" ] [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [profile.release] debug = false