Skip to content

Commit

Permalink
wasm: build action tests (#1875)
Browse files Browse the repository at this point in the history
* migrate build methods and remove legacy mock idbdatabase

* use mockDb

* changeset

* linting

* utils

* add types

* formatting

* clippy

* gabe's feedback
  • Loading branch information
TalDerei authored Nov 18, 2024
1 parent 4574541 commit fa39e46
Show file tree
Hide file tree
Showing 11 changed files with 514 additions and 661 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-vans-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/wasm': minor
---

migrate wasm unit test for action building to use mockDb
3 changes: 3 additions & 0 deletions packages/types/src/indexed-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,7 @@ export const IDB_TABLES: Tables = {
validator_infos: 'VALIDATOR_INFOS',
transactions: 'TRANSACTIONS',
full_sync_height: 'FULL_SYNC_HEIGHT',
tree_commitments: 'TREE_COMMITMENTS',
tree_last_position: 'TREE_LAST_POSITION',
tree_last_forgotten: 'TREE_LAST_FORGOTTEN',
};
108 changes: 100 additions & 8 deletions packages/wasm/crate/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use crate::error::WasmResult;
use crate::utils;
use penumbra_keys::FullViewingKey;
use penumbra_proto::DomainType;
use penumbra_transaction::{
plan::{ActionPlan, TransactionPlan},
WitnessData,
Action, AuthorizationData, Transaction, WitnessData,
};
use wasm_bindgen::prelude::wasm_bindgen;

use crate::error::WasmResult;
use crate::utils;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};

/// Builds a planned [`Action`] specified by
/// the [`ActionPlan`] in a [`TransactionPlan`].
/// Arguments:
/// transaction_plan: `TransactionPlan`
/// action_plan: `ActionPlan`
/// full_viewing_key: `byte representation inner FullViewingKey`
/// full_viewing_key: `FullViewingKey`
/// witness_data: `WitnessData``
/// Returns: `Action`
#[wasm_bindgen]
Expand All @@ -28,10 +27,103 @@ pub fn build_action(
let transaction_plan = TransactionPlan::decode(transaction_plan)?;
let witness = WitnessData::decode(witness_data)?;
let action_plan = ActionPlan::decode(action_plan)?;
let full_viewing_key: FullViewingKey = FullViewingKey::decode(full_viewing_key)?;
let full_viewing_key = FullViewingKey::decode(full_viewing_key)?;

let action = build_action_inner(transaction_plan, action_plan, full_viewing_key, witness)?;

Ok(action.encode_to_vec())
}

pub fn build_action_inner(
transaction_plan: TransactionPlan,
action_plan: ActionPlan,
full_viewing_key: FullViewingKey,
witness: WitnessData,
) -> WasmResult<Action> {
let memo_key = transaction_plan.memo.map(|memo_plan| memo_plan.key);

let action = ActionPlan::build_unauth(action_plan, &full_viewing_key, &witness, memo_key)?;
Ok(action.encode_to_vec())

Ok(action)
}

/// Build serial tx –
/// building a transaction may take some time,
/// depending on CPU performance and number of actions
/// in the transaction plan.
/// Arguments:
/// full_viewing_key: `FullViewingKey`
/// transaction_plan: `TransactionPlan`
/// witness_data: `WitnessData`
/// auth_data: `AuthorizationData`
/// Returns: `Transaction`
#[wasm_bindgen]
pub fn build_serial(
full_viewing_key: &[u8],
transaction_plan: &[u8],
witness_data: &[u8],
auth_data: &[u8],
) -> WasmResult<Vec<u8>> {
utils::set_panic_hook();

let plan = TransactionPlan::decode(transaction_plan)?;
let witness = WitnessData::decode(witness_data)?;
let auth = AuthorizationData::decode(auth_data)?;
let fvk = FullViewingKey::decode(full_viewing_key)?;

let tx: Transaction = build_serial_inner(fvk, plan, witness, auth)?;

Ok(tx.encode_to_vec())
}

pub fn build_serial_inner(
fvk: FullViewingKey,
plan: TransactionPlan,
witness: WitnessData,
auth: AuthorizationData,
) -> WasmResult<Transaction> {
let tx: Transaction = plan.build(&fvk, &witness, &auth)?;

Ok(tx)
}

/// Build parallel tx –
/// building a transaction may take some time,
/// depending on CPU performance and number of
/// actions in the transaction plan.
/// Arguments:
/// actions: `Vec<Actions>`
/// transaction_plan: `TransactionPlan`
/// witness_data: `WitnessData`
/// auth_data: `AuthorizationData`
/// Returns: `Transaction`
#[wasm_bindgen]
pub fn build_parallel(
actions: JsValue,
transaction_plan: &[u8],
witness_data: &[u8],
auth_data: &[u8],
) -> WasmResult<Vec<u8>> {
utils::set_panic_hook();

let plan = TransactionPlan::decode(transaction_plan)?;
let witness = WitnessData::decode(witness_data)?;
let auth = AuthorizationData::decode(auth_data)?;
let actions: Vec<Action> = serde_wasm_bindgen::from_value(actions)?;

let tx = build_parallel_inner(actions, plan, witness, auth)?;

Ok(tx.encode_to_vec())
}

pub fn build_parallel_inner(
actions: Vec<Action>,
plan: TransactionPlan,
witness: WitnessData,
auth: AuthorizationData,
) -> WasmResult<Transaction> {
let transaction = plan.clone().build_unauth_with_actions(actions, &witness)?;
let tx = plan.apply_auth_data(&auth, transaction)?;

Ok(tx)
}
54 changes: 2 additions & 52 deletions packages/wasm/crate/src/database/indexed_db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::future::IntoFuture;

use indexed_db_futures::idb_object_store::IdbObjectStoreParameters;
use indexed_db_futures::prelude::{IdbOpenDbRequestLike, OpenDbRequest};
use indexed_db_futures::{IdbDatabase, IdbKeyPath, IdbQuerySource, IdbVersionChangeEvent};
use indexed_db_futures::prelude::OpenDbRequest;
use indexed_db_futures::{IdbDatabase, IdbQuerySource};
use serde::de::DeserializeOwned;
use serde::Serialize;
use wasm_bindgen::JsValue;
Expand All @@ -16,59 +15,10 @@ pub async fn open_idb_database(constants: &DbConstants) -> WasmResult<IdbDatabas
#[allow(unused_mut)]
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&constants.name, constants.version)?;

// Conditionally mock sample `IdbDatabase` database for testing purposes
#[cfg(feature = "mock-database")]
let db_req = mock_test_database(db_req).into_future().await;

let db = db_req.into_future().await?;
Ok(db)
}

// Previous method of testing that requires features in prod code to seed indexed db for tests
// TODO: Swap out with new MockDb utility for testing
#[allow(dead_code)]
async fn mock_test_database(mut db_req: OpenDbRequest) -> OpenDbRequest {
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Check if the object store exists; create it if it doesn't
if evt.db().name() == "penumbra-db-wasm-test" {
let note_key: JsValue = serde_wasm_bindgen::to_value("noteCommitment.inner")?;
let note_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(note_key)))
.to_owned();
let note_object_store = evt
.db()
.create_object_store_with_params("SPENDABLE_NOTES", &note_object_store_params)?;

let nullifier_key: JsValue = serde_wasm_bindgen::to_value("nullifier.inner")?;
let params = web_sys::IdbIndexParameters::new();
params.set_unique(false);
note_object_store.create_index_with_params(
"nullifier",
&IdbKeyPath::new(nullifier_key),
&params,
)?;
evt.db().create_object_store("TREE_LAST_POSITION")?;
evt.db().create_object_store("TREE_LAST_FORGOTTEN")?;

let commitment_key: JsValue = serde_wasm_bindgen::to_value("commitment.inner")?;
let commitment_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(commitment_key)))
.to_owned();
evt.db().create_object_store_with_params(
"TREE_COMMITMENTS",
&commitment_object_store_params,
)?;
evt.db().create_object_store("TREE_HASHES")?;
evt.db().create_object_store("FMD_PARAMETERS")?;
evt.db().create_object_store("APP_PARAMETERS")?;
evt.db().create_object_store("GAS_PRICES")?;
}
Ok(())
}));

db_req
}

impl Database for IdbDatabase {
async fn get<T, K>(&self, table: &str, key: K) -> WasmResult<Option<T>>
where
Expand Down
3 changes: 3 additions & 0 deletions packages/wasm/crate/src/database/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub fn get_mock_tables() -> Tables {
full_sync_height: "full_sync_height".to_string(),
auctions: "auctions".to_string(),
auction_outstanding_reserves: "auction_outstanding_reserves".to_string(),
tree_commitments: "tree_commitments".to_string(),
tree_last_position: "tree_last_position".to_string(),
tree_last_forgotten: "tree_last_forgotten".to_string(),
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/wasm/crate/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub struct Tables {
pub full_sync_height: String,
pub auctions: String,
pub auction_outstanding_reserves: String,
pub tree_commitments: String,
pub tree_last_position: String,
pub tree_last_forgotten: String,
}

pub async fn init_idb_storage(constants: DbConstants) -> WasmResult<Storage<IdbDatabase>> {
Expand Down
58 changes: 0 additions & 58 deletions packages/wasm/crate/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,64 +112,6 @@ pub fn witness(transaction_plan: &[u8], stored_tree: JsValue) -> WasmResult<Vec<
Ok(witness_data.encode_to_vec())
}

/// Build serial tx
/// Building a transaction may take some time,
/// depending on CPU performance and number of actions in transaction_plan
/// Arguments:
/// full_viewing_key: `byte representation inner FullViewingKey`
/// transaction_plan: `pb::TransactionPlan`
/// witness_data: `pb::WitnessData`
/// auth_data: `pb::AuthorizationData`
/// Returns: `pb::Transaction`
#[wasm_bindgen]
pub fn build(
full_viewing_key: &[u8],
transaction_plan: &[u8],
witness_data: &[u8],
auth_data: &[u8],
) -> WasmResult<Vec<u8>> {
utils::set_panic_hook();

let plan = TransactionPlan::decode(transaction_plan)?;
let witness = WitnessData::decode(witness_data)?;
let auth = AuthorizationData::decode(auth_data)?;
let fvk: FullViewingKey = FullViewingKey::decode(full_viewing_key)?;

let tx: Transaction = plan.build(&fvk, &witness, &auth)?;

Ok(tx.encode_to_vec())
}

/// Build parallel tx
/// Building a transaction may take some time,
/// depending on CPU performance and number of actions in transaction_plan
/// Arguments:
/// actions: `Vec<Actions>`
/// transaction_plan: `pb::TransactionPlan`
/// witness_data: `pb::WitnessData`
/// auth_data: `pb::AuthorizationData`
/// Returns: `pb::Transaction`
#[wasm_bindgen]
pub fn build_parallel(
actions: JsValue,
transaction_plan: &[u8],
witness_data: &[u8],
auth_data: &[u8],
) -> WasmResult<Vec<u8>> {
utils::set_panic_hook();

let plan = TransactionPlan::decode(transaction_plan)?;
let witness = WitnessData::decode(witness_data)?;
let auth = AuthorizationData::decode(auth_data)?;
let actions: Vec<Action> = serde_wasm_bindgen::from_value(actions)?;

let transaction = plan.clone().build_unauth_with_actions(actions, &witness)?;

let tx = plan.apply_auth_data(&auth, transaction)?;

Ok(tx.encode_to_vec())
}

#[wasm_bindgen(getter_with_clone)]
pub struct TxpAndTxvBytes {
pub txp: Vec<u8>,
Expand Down
Loading

0 comments on commit fa39e46

Please sign in to comment.