-
Notifications
You must be signed in to change notification settings - Fork 321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improvements and fixes to wasm transaction planner and wasm view server #2973
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use base64::DecodeError; | ||
use hex::FromHexError; | ||
use penumbra_tct::error::{InsertBlockError, InsertEpochError, InsertError}; | ||
use serde_wasm_bindgen::Error; | ||
use std::convert::Infallible; | ||
use thiserror::Error; | ||
use wasm_bindgen::{JsError, JsValue}; | ||
use web_sys::DomException; | ||
|
||
pub type WasmResult<T> = Result<T, WasmError>; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum WasmError { | ||
#[error("{0}")] | ||
Anyhow(#[from] anyhow::Error), | ||
|
||
#[error("{0}")] | ||
DecodeError(#[from] DecodeError), | ||
|
||
#[error("{0}")] | ||
Dom(#[from] DomError), | ||
|
||
#[error("{0}")] | ||
FromHexError(#[from] FromHexError), | ||
|
||
#[error("{0}")] | ||
Infallible(#[from] Infallible), | ||
|
||
#[error("{0}")] | ||
InsertBlockError(#[from] InsertBlockError), | ||
|
||
#[error("{0}")] | ||
InsertEpochError(#[from] InsertEpochError), | ||
|
||
#[error("{0}")] | ||
InsertError(#[from] InsertError), | ||
|
||
#[error("{0}")] | ||
Wasm(#[from] serde_wasm_bindgen::Error), | ||
} | ||
|
||
impl From<WasmError> for serde_wasm_bindgen::Error { | ||
fn from(wasm_err: WasmError) -> Self { | ||
Error::new(wasm_err.to_string()) | ||
} | ||
} | ||
|
||
impl From<WasmError> for JsValue { | ||
fn from(error: WasmError) -> Self { | ||
JsError::from(error).into() | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct DomError(DomException); | ||
|
||
impl std::fmt::Display for DomError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
write!(f, "DOM Exception: {:?}", self.0) | ||
} | ||
} | ||
|
||
impl std::error::Error for DomError {} | ||
|
||
impl From<DomException> for WasmError { | ||
fn from(dom_exception: DomException) -> Self { | ||
WasmError::Dom(DomError(dom_exception)) | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think this file has some helper functions that are small enough to be moved into the main function. It's similar feedback as the other. Will have to move There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
use crate::error::WasmResult; | ||
use penumbra_keys::keys::{SeedPhrase, SpendKey}; | ||
use penumbra_keys::{Address, FullViewingKey}; | ||
use penumbra_proto::{core::crypto::v1alpha1 as pb, serializers::bech32str, DomainType}; | ||
use rand_core::OsRng; | ||
use std::str::FromStr; | ||
use wasm_bindgen::prelude::*; | ||
|
||
/// generate a spend key from a seed phrase | ||
/// Arguments: | ||
/// seed_phrase: `string` | ||
/// Returns: `bech32 string` | ||
#[wasm_bindgen] | ||
pub fn generate_spend_key(seed_phrase: &str) -> WasmResult<JsValue> { | ||
let seed = SeedPhrase::from_str(seed_phrase)?; | ||
let spend_key = SpendKey::from_seed_phrase_bip39(seed, 0); | ||
|
||
let proto = spend_key.to_proto(); | ||
|
||
let spend_key_str = bech32str::encode( | ||
&proto.inner, | ||
bech32str::spend_key::BECH32_PREFIX, | ||
bech32str::Bech32m, | ||
); | ||
|
||
Ok(JsValue::from_str(&spend_key_str)) | ||
} | ||
|
||
/// get full viewing key from spend key | ||
/// Arguments: | ||
/// spend_key_str: `bech32 string` | ||
/// Returns: `bech32 string` | ||
#[wasm_bindgen] | ||
pub fn get_full_viewing_key(spend_key: &str) -> WasmResult<JsValue> { | ||
let spend_key = SpendKey::from_str(spend_key)?; | ||
|
||
let fvk: &FullViewingKey = spend_key.full_viewing_key(); | ||
|
||
let proto = fvk.to_proto(); | ||
|
||
let fvk_bech32 = bech32str::encode( | ||
&proto.inner, | ||
bech32str::full_viewing_key::BECH32_PREFIX, | ||
bech32str::Bech32m, | ||
); | ||
Ok(JsValue::from_str(&fvk_bech32)) | ||
} | ||
|
||
/// get address by index using FVK | ||
/// Arguments: | ||
/// full_viewing_key: `bech32 string` | ||
/// index: `u32` | ||
/// Returns: `pb::Address` | ||
#[wasm_bindgen] | ||
pub fn get_address_by_index(full_viewing_key: &str, index: u32) -> WasmResult<JsValue> { | ||
let fvk = FullViewingKey::from_str(full_viewing_key)?; | ||
let (address, _dtk) = fvk.incoming().payment_address(index.into()); | ||
let proto = address.to_proto(); | ||
let result = serde_wasm_bindgen::to_value(&proto)?; | ||
Ok(result) | ||
} | ||
|
||
/// get ephemeral (randomizer) address using FVK | ||
/// The derivation tree is like "spend key / address index / ephemeral address" so we must also pass index as an argument | ||
/// Arguments: | ||
/// full_viewing_key: `bech32 string` | ||
/// index: `u32` | ||
/// Returns: `pb::Address` | ||
#[wasm_bindgen] | ||
pub fn get_ephemeral_address(full_viewing_key: &str, index: u32) -> WasmResult<JsValue> { | ||
let fvk = FullViewingKey::from_str(full_viewing_key)?; | ||
let (address, _dtk) = fvk.ephemeral_address(OsRng, index.into()); | ||
let proto = address.to_proto(); | ||
let result = serde_wasm_bindgen::to_value(&proto)?; | ||
Ok(result) | ||
} | ||
|
||
/// Check if the address is FVK controlled | ||
/// Arguments: | ||
/// full_viewing_key: `bech32 String` | ||
/// address: `bech32 String` | ||
/// Returns: `Option<pb::AddressIndex>` | ||
#[wasm_bindgen] | ||
pub fn is_controlled_address(full_viewing_key: &str, address: &str) -> WasmResult<JsValue> { | ||
let fvk = FullViewingKey::from_str(full_viewing_key)?; | ||
let index: Option<pb::AddressIndex> = fvk | ||
.address_index(&Address::from_str(address)?) | ||
.map(Into::into); | ||
let result = serde_wasm_bindgen::to_value(&index)?; | ||
Ok(result) | ||
} | ||
|
||
/// Get canonical short form address by index | ||
/// This feature is probably redundant and will be removed from wasm in the future | ||
/// Arguments: | ||
/// full_viewing_key: `bech32 string` | ||
/// index: `u32` | ||
/// Returns: `String` | ||
#[wasm_bindgen] | ||
pub fn get_short_address_by_index(full_viewing_key: &str, index: u32) -> WasmResult<JsValue> { | ||
let fvk = FullViewingKey::from_str(full_viewing_key)?; | ||
|
||
let (address, _dtk) = fvk.incoming().payment_address(index.into()); | ||
let short_address = address.display_short_form(); | ||
Ok(JsValue::from_str(&short_address)) | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thank you for making |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,131 +1,15 @@ | ||
#![deny(clippy::unwrap_used)] | ||
#![allow(dead_code)] | ||
extern crate core; | ||
|
||
mod error; | ||
mod keys; | ||
mod note_record; | ||
mod planner; | ||
mod storage; | ||
mod swap_record; | ||
mod tx; | ||
mod utils; | ||
mod view_server; | ||
use penumbra_proto::{core::crypto::v1alpha1 as pb, serializers::bech32str, DomainType}; | ||
mod wasm_planner; | ||
|
||
use penumbra_keys::{Address, FullViewingKey}; | ||
use std::convert::TryFrom; | ||
use std::str::FromStr; | ||
|
||
use penumbra_keys::keys::{SeedPhrase, SpendKey}; | ||
use wasm_bindgen::prelude::*; | ||
|
||
use penumbra_transaction::Transaction; | ||
|
||
pub use tx::send_plan; | ||
pub use view_server::ViewServer; | ||
|
||
#[wasm_bindgen] | ||
pub fn generate_spend_key(seed_phrase: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
let seed = | ||
SeedPhrase::from_str(seed_phrase).expect("the provided string is a valid seed phrase"); | ||
let spend_key = SpendKey::from_seed_phrase_bip39(seed, 0); | ||
|
||
let proto = spend_key.to_proto(); | ||
let spend_key_str = &bech32str::encode( | ||
&proto.inner, | ||
bech32str::spend_key::BECH32_PREFIX, | ||
bech32str::Bech32m, | ||
); | ||
|
||
serde_wasm_bindgen::to_value(&spend_key_str).expect("able to serialize spend key") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn get_full_viewing_key(spend_key_str: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
let spend_key = | ||
SpendKey::from_str(spend_key_str).expect("the provided string is a valid spend key"); | ||
|
||
let fvk: &FullViewingKey = spend_key.full_viewing_key(); | ||
|
||
let proto = pb::FullViewingKey::from(fvk.to_proto()); | ||
|
||
let fvk_str = &bech32str::encode( | ||
&proto.inner, | ||
bech32str::full_viewing_key::BECH32_PREFIX, | ||
bech32str::Bech32m, | ||
); | ||
serde_wasm_bindgen::to_value(&fvk_str).expect("able to serialize full viewing key") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn get_address_by_index(full_viewing_key: &str, index: u32) -> JsValue { | ||
utils::set_panic_hook(); | ||
let fvk = FullViewingKey::from_str(full_viewing_key.as_ref()) | ||
.expect("the provided string is a valid FullViewingKey"); | ||
|
||
let (address, _dtk) = fvk.incoming().payment_address(index.into()); | ||
|
||
let proto = address.to_proto(); | ||
let address_str = &bech32str::encode( | ||
&proto.inner, | ||
bech32str::address::BECH32_PREFIX, | ||
bech32str::Bech32m, | ||
); | ||
|
||
serde_wasm_bindgen::to_value(&address_str).expect("able to serialize address") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn base64_to_bech32(prefix: &str, base64_str: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
|
||
let bech32 = &bech32str::encode( | ||
&base64::Engine::decode(&base64::engine::general_purpose::STANDARD, base64_str) | ||
.expect("the provided string is a valid base64 string"), | ||
prefix, | ||
bech32str::Bech32m, | ||
); | ||
serde_wasm_bindgen::to_value(bech32).expect("able to serialize bech32 string") | ||
} | ||
#[wasm_bindgen] | ||
pub fn is_controlled_address(full_viewing_key: &str, address: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
let fvk = FullViewingKey::from_str(full_viewing_key.as_ref()) | ||
.expect("the provided string is a valid FullViewingKey"); | ||
|
||
let index = fvk.address_index(&Address::from_str(address.as_ref()).expect("valid address")); | ||
|
||
serde_wasm_bindgen::to_value(&index).expect("able to serialize address index") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn get_short_address_by_index(full_viewing_key: &str, index: u32) -> JsValue { | ||
utils::set_panic_hook(); | ||
let fvk = FullViewingKey::from_str(full_viewing_key.as_ref()) | ||
.expect("The provided string is not a valid FullViewingKey"); | ||
|
||
let (address, _dtk) = fvk.incoming().payment_address(index.into()); | ||
let short_address = address.display_short_form(); | ||
serde_wasm_bindgen::to_value(&short_address).expect("able to serialize address") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn decode_transaction(tx_bytes: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
let tx_vec: Vec<u8> = | ||
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, tx_bytes) | ||
.expect("the provided tx string is a valid base64 string"); | ||
let transaction: Transaction = | ||
Transaction::try_from(tx_vec).expect("the provided tx string is a valid transaction"); | ||
serde_wasm_bindgen::to_value(&transaction).expect("able to serialize transaction") | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn decode_nct_root(tx_bytes: &str) -> JsValue { | ||
utils::set_panic_hook(); | ||
let tx_vec: Vec<u8> = | ||
hex::decode(tx_bytes).expect("the provided tx string is a valid hex string"); | ||
let root = penumbra_tct::Root::decode(tx_vec.as_slice()) | ||
.expect("the provided tx string is a valid nct root"); | ||
serde_wasm_bindgen::to_value(&root).expect("able to serialize nct root") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job on these! Can you the errors below in alphabetical order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅