Skip to content

Commit

Permalink
WASM: add bindings for the Sapling keys
Browse files Browse the repository at this point in the history
  • Loading branch information
andiflabs committed Nov 26, 2024
1 parent 6bdd3ec commit 971ad1e
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 14 deletions.
6 changes: 6 additions & 0 deletions ironfish-rust-wasm/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

mod proof_generation_key;
mod public_address;
mod sapling_key;
mod view_keys;

pub use proof_generation_key::ProofGenerationKey;
pub use public_address::PublicAddress;
pub use sapling_key::SaplingKey;
pub use view_keys::{IncomingViewKey, OutgoingViewKey, ViewKey};
36 changes: 36 additions & 0 deletions ironfish-rust-wasm/src/keys/proof_generation_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::{
errors::IronfishError,
primitives::{Fr, SubgroupPoint},
wasm_bindgen_wrapper,
};
use wasm_bindgen::prelude::*;

wasm_bindgen_wrapper! {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ProofGenerationKey(ironfish::keys::ProofGenerationKey);
}

#[wasm_bindgen]
impl ProofGenerationKey {
#[wasm_bindgen(constructor)]
pub fn deserialize(bytes: &[u8]) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::ProofGenerationKey::read(bytes)?))
}

#[wasm_bindgen]
pub fn serialize(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}

#[wasm_bindgen(js_name = fromParts)]
pub fn from_parts(ak: SubgroupPoint, nsk: Fr) -> Self {
Self(ironfish::keys::ProofGenerationKey::new(
ak.into(),
nsk.into(),
))
}
}
105 changes: 105 additions & 0 deletions ironfish-rust-wasm/src/keys/sapling_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::{
errors::IronfishError,
keys::{IncomingViewKey, OutgoingViewKey, ProofGenerationKey, PublicAddress, ViewKey},
wasm_bindgen_wrapper,
};
use wasm_bindgen::prelude::*;

wasm_bindgen_wrapper! {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SaplingKey(ironfish::keys::SaplingKey);
}

#[wasm_bindgen]
impl SaplingKey {
#[wasm_bindgen(constructor)]
pub fn deserialize(bytes: &[u8]) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::SaplingKey::read(bytes)?))
}

#[wasm_bindgen]
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::new();
self.0
.write(&mut buf)
.expect("failed to serialize sapling key");
buf
}

#[wasm_bindgen]
pub fn random() -> Self {
Self(ironfish::keys::SaplingKey::generate_key())
}

#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(hex: &str) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::SaplingKey::from_hex(hex)?))
}

#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.0.hex_spending_key()
}

// TODO: to/fromWords

#[wasm_bindgen(getter, js_name = publicAddress)]
pub fn public_address(&self) -> PublicAddress {
self.0.public_address().into()
}

#[wasm_bindgen(getter, js_name = spendingKey)]
pub fn spending_key(&self) -> Vec<u8> {
self.0.spending_key().to_vec()
}

#[wasm_bindgen(getter, js_name = incomingViewKey)]
pub fn incoming_view_key(&self) -> IncomingViewKey {
self.0.incoming_view_key().to_owned().into()
}

#[wasm_bindgen(getter, js_name = outgoingViewKey)]
pub fn outgoing_view_key(&self) -> OutgoingViewKey {
self.0.outgoing_view_key().to_owned().into()
}

#[wasm_bindgen(getter, js_name = viewKey)]
pub fn view_key(&self) -> ViewKey {
self.0.view_key().to_owned().into()
}

#[wasm_bindgen(getter, js_name = proofGenerationKey)]
pub fn proof_generation_key(&self) -> ProofGenerationKey {
self.0.sapling_proof_generation_key().into()
}
}

#[cfg(test)]
mod tests {
use crate::keys::{IncomingViewKey, OutgoingViewKey, ProofGenerationKey, SaplingKey, ViewKey};
use wasm_bindgen_test::wasm_bindgen_test;

macro_rules! assert_serde_ok {
( $type:ty, $key:expr ) => {
assert_eq!(
$key,
<$type>::deserialize($key.serialize().as_slice()).expect("deserialization failed")
)
};
}

#[test]
#[wasm_bindgen_test]
fn serialization_roundtrip() {
let key = SaplingKey::random();
assert_serde_ok!(SaplingKey, key);
assert_serde_ok!(IncomingViewKey, key.incoming_view_key());
assert_serde_ok!(OutgoingViewKey, key.outgoing_view_key());
assert_serde_ok!(ViewKey, key.view_key());
assert_serde_ok!(ProofGenerationKey, key.proof_generation_key());
}
}
119 changes: 119 additions & 0 deletions ironfish-rust-wasm/src/keys/view_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::{
errors::IronfishError, keys::PublicAddress, primitives::PublicKey, wasm_bindgen_wrapper,
};
use wasm_bindgen::prelude::*;

wasm_bindgen_wrapper! {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct IncomingViewKey(ironfish::keys::IncomingViewKey);
}

#[wasm_bindgen]
impl IncomingViewKey {
#[wasm_bindgen(constructor)]
pub fn deserialize(bytes: &[u8]) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::IncomingViewKey::read(bytes)?))
}

#[wasm_bindgen]
pub fn serialize(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}

#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(hex: &str) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::IncomingViewKey::from_hex(hex)?))
}

#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.0.hex_key()
}

// TODO: to/fromWords

#[wasm_bindgen(getter, js_name = publicAddress)]
pub fn public_address(&self) -> PublicAddress {
self.0.public_address().into()
}
}

wasm_bindgen_wrapper! {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct OutgoingViewKey(ironfish::keys::OutgoingViewKey);
}

#[wasm_bindgen]
impl OutgoingViewKey {
#[wasm_bindgen(constructor)]
pub fn deserialize(bytes: &[u8]) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::OutgoingViewKey::read(bytes)?))
}

#[wasm_bindgen]
pub fn serialize(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}

#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(hex: &str) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::OutgoingViewKey::from_hex(hex)?))
}

#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.0.hex_key()
}

// TODO: to/fromWords
}

wasm_bindgen_wrapper! {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ViewKey(ironfish::keys::ViewKey);
}

#[wasm_bindgen]
impl ViewKey {
#[wasm_bindgen(constructor)]
pub fn deserialize(bytes: &[u8]) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::ViewKey::read(bytes)?))
}

#[wasm_bindgen]
pub fn serialize(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}

#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(hex: &str) -> Result<Self, IronfishError> {
Ok(Self(ironfish::keys::ViewKey::from_hex(hex)?))
}

#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.0.hex_key()
}

#[wasm_bindgen(getter, js_name = publicAddress)]
pub fn public_address(&self) -> Result<PublicAddress, IronfishError> {
self.0
.public_address()
.map(|a| a.into())
.map_err(|e| e.into())
}

#[wasm_bindgen(getter, js_name = authorizingKey)]
pub fn authorizing_key(&self) -> PublicKey {
self.0.authorizing_key.into()
}

#[wasm_bindgen(getter, js_name = nullifierDerivingKey)]
pub fn nullifier_deriving_key(&self) -> PublicKey {
self.0.nullifier_deriving_key.into()
}
}
12 changes: 12 additions & 0 deletions ironfish-rust-wasm/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ impl PublicKey {
}
}

impl From<ironfish_jubjub::ExtendedPoint> for PublicKey {
fn from(p: ironfish_jubjub::ExtendedPoint) -> Self {
Self(redjubjub::PublicKey(p))
}
}

impl From<ironfish_jubjub::SubgroupPoint> for PublicKey {
fn from(p: ironfish_jubjub::SubgroupPoint) -> Self {
Self(redjubjub::PublicKey(p.into()))
}
}

wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct Signature(redjubjub::Signature);
Expand Down
13 changes: 10 additions & 3 deletions ironfish-rust/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use ironfish_zkp::constants::{
pub use ironfish_zkp::ProofGenerationKey;
use rand::prelude::*;

use std::io;
use std::{fmt, io};

mod ephemeral;
pub use ephemeral::EphemeralKeyPair;
Expand All @@ -41,7 +41,7 @@ pub const SPEND_KEY_SIZE: usize = 32;
/// While the key parts are all represented as 256 bit keys to the outside
/// world, inside the API they map to Edwards points or scalar values
/// on the JubJub curve.
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct SaplingKey {
/// The private (secret) key from which all the other key parts are derived.
/// The expanded form of this key is required before a note can be spent.
Expand Down Expand Up @@ -116,7 +116,7 @@ impl SaplingKey {
}

/// Load a new key from a Read implementation (e.g: socket, file)
pub fn read<R: io::Read>(reader: &mut R) -> Result<Self, IronfishError> {
pub fn read<R: io::Read>(mut reader: R) -> Result<Self, IronfishError> {
let mut spending_key = [0; SPEND_KEY_SIZE];
reader.read_exact(&mut spending_key)?;
Self::new(spending_key)
Expand Down Expand Up @@ -266,3 +266,10 @@ impl SaplingKey {
Ok(scalar)
}
}

impl fmt::Debug for SaplingKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Hide all private keys
f.debug_struct("SaplingKey").finish_non_exhaustive()
}
}
Loading

0 comments on commit 971ad1e

Please sign in to comment.