Skip to content

Commit e37bcab

Browse files
authored
Adds Ability to Create Account Component from Package in Web Client (0xMiden#1469)
1 parent d01a3d2 commit e37bcab

File tree

14 files changed

+291
-52
lines changed

14 files changed

+291
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* Added `getMapEntries` method to `AccountStorage` in web client for iterating storage map entries ([#1323](https://github.com/0xMiden/miden-client/pull/1323)).
1717
* Added `Address` addition and removal for accounts ([#1367](https://github.com/0xMiden/miden-client/pull/1367)).
1818
* Refactored code into their own files and added `ProvenTransaction` and `TransactionStoreUpdate` bindings for the WebClient ([#1408](https://github.com/0xMiden/miden-client/pull/1408)).
19-
* Added `NoteFile` type, used for exporting and importing `Notes` ([#1378](https://github.com/0xMiden/miden-client/pull/1383)).
19+
* Added `NoteFile` type, used for exporting and importing `Notes`([#1378](https://github.com/0xMiden/miden-client/pull/1383)).
2020
* Build `IndexedDB` code from a `build.rs` instead of pushing artifacts to the repo ([#1409](https://github.com/0xMiden/miden-client/pull/1409)).
2121
* Implemented missing RPC endpoints: `/SyncStorageMaps`, `/SyncAccountVault` & `/SyncTransactions` ([#1362](https://github.com/0xMiden/miden-client/pull/1362)).
2222
* Updated `submit_proven_transaction()` to include `TransactionInputs` for validator ([#1421](https://github.com/0xMiden/miden-client/pull/1421)).
@@ -25,6 +25,7 @@
2525
* Started allowing for note ID prefixes in CLI `notes --send` ([#1433](https://github.com/0xMiden/miden-client/pull/1433)).
2626
* Refactored note scripts to be pre-loaded into the store instead of providing them through advice inputs ([#1426](https://github.com/0xMiden/miden-client/pull/1426)).
2727
* [BREAKING] Refactored client transaction APIs and the new `TransactionResult` type ([#1407](https://github.com/0xMiden/miden-client/pull/1407)).
28+
* Added ability to create `AccountComponent` from a `Package` and `StorageSlot` array in the Web Client ([#1469](https://github.com/0xMiden/miden-client/pull/1469)).
2829

2930
### Changes
3031

crates/idxdb-store/build.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
use std::process::Command;
22

3-
fn main() -> miette::Result<(), String> {
4-
println!("cargo::rerun-if-changed=src");
5-
6-
// Install dependencies for the TS project files with yarn
7-
let status = Command::new("yarn").current_dir("src").status().map_err(|err| {
8-
format!("could not install ts dependencies -- have you installed yarn? got error: {err}")
9-
})?;
3+
#[cfg(windows)]
4+
fn run_yarn(args: &[&str]) -> Result<(), String> {
5+
let status = Command::new("cmd")
6+
.args(["/C", "yarn"])
7+
.args(args)
8+
.current_dir("src")
9+
.status()
10+
.map_err(|err| format!("could not run yarn via cmd: {err}"))?;
1011
if !status.success() {
11-
return Err(format!("could not install ts dependencies: yarn exited with status {status}"));
12+
return Err(format!("yarn exited with status {status}"));
1213
}
14+
Ok(())
15+
}
1316

14-
// Build the TS files into JS and store the artifacts under src/js
17+
#[cfg(not(windows))]
18+
fn run_yarn(args: &[&str]) -> Result<(), String> {
1519
let status = Command::new("yarn")
16-
.args(["build"])
20+
.args(args)
1721
.current_dir("src")
1822
.status()
19-
.map_err(|err| format!("failed to run build command for typescript: {err}"))?;
23+
.map_err(|err| format!("could not run yarn: {err}"))?;
2024
if !status.success() {
21-
return Err(format!("failed to build typescript: yarn build exited with status {status}"));
25+
return Err(format!("yarn exited with status {status}"));
2226
}
27+
Ok(())
28+
}
29+
30+
fn main() -> miette::Result<(), String> {
31+
println!("cargo::rerun-if-changed=src");
32+
33+
// Install deps
34+
run_yarn(&[]).map_err(|e| format!("could not install ts dependencies: {e}"))?;
35+
36+
// Build TS
37+
run_yarn(&["build"]).map_err(|e| format!("failed to build typescript: {e}"))?;
2338

2439
Ok(())
2540
}

crates/web-client/js/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export {
5555
NoteType,
5656
OutputNote,
5757
OutputNotesArray,
58+
Package,
5859
PublicKey,
5960
Rpo256,
6061
RpcClient,

crates/web-client/src/models/account_component.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ use miden_client::account::StorageSlot as NativeStorageSlot;
22
use miden_client::account::component::AccountComponent as NativeAccountComponent;
33
use miden_client::auth::{AuthRpoFalcon512 as NativeRpoFalcon512, PublicKeyCommitment};
44
use miden_client::crypto::rpo_falcon512::SecretKey as NativeSecretKey;
5+
use miden_client::vm::Package as NativePackage;
56
use miden_core::mast::MastNodeExt;
67
use wasm_bindgen::prelude::*;
78

89
use crate::js_error_with_context;
10+
use crate::models::miden_arrays::StorageSlotArray;
11+
use crate::models::package::Package;
912
use crate::models::script_builder::ScriptBuilder;
1013
use crate::models::secret_key::SecretKey;
1114
use crate::models::storage_slot::StorageSlot;
@@ -73,6 +76,26 @@ impl AccountComponent {
7376
.into();
7477
AccountComponent(native_auth_component)
7578
}
79+
80+
#[wasm_bindgen(js_name = "fromPackage")]
81+
pub fn from_package(
82+
package: &Package,
83+
storage_slots: &StorageSlotArray,
84+
) -> Result<AccountComponent, JsValue> {
85+
let native_package: NativePackage = package.into();
86+
let native_library = native_package.unwrap_library().as_ref().clone();
87+
let native_slots: Vec<NativeStorageSlot> = storage_slots
88+
.__inner
89+
.iter()
90+
.map(|storage_slot| storage_slot.clone().into())
91+
.collect();
92+
93+
NativeAccountComponent::new(native_library, native_slots)
94+
.map(AccountComponent)
95+
.map_err(|e| {
96+
js_error_with_context(e, "Failed to create account component from package")
97+
})
98+
}
7699
}
77100

78101
// CONVERSIONS

crates/web-client/src/models/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub mod note_tag;
7777
pub mod note_type;
7878
pub mod output_note;
7979
pub mod output_notes;
80+
pub mod package;
8081
pub mod partial_note;
8182
pub mod proven_transaction;
8283
pub mod provers;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use miden_client::vm::Package as NativePackage;
2+
use wasm_bindgen::prelude::*;
3+
use wasm_bindgen_futures::js_sys::Uint8Array;
4+
5+
use crate::utils::{deserialize_from_uint8array, serialize_to_uint8array};
6+
7+
#[derive(Clone)]
8+
#[wasm_bindgen]
9+
pub struct Package(NativePackage);
10+
11+
#[wasm_bindgen]
12+
impl Package {
13+
pub fn serialize(&self) -> Uint8Array {
14+
serialize_to_uint8array(&self.0)
15+
}
16+
17+
pub fn deserialize(bytes: &Uint8Array) -> Result<Package, JsValue> {
18+
deserialize_from_uint8array::<NativePackage>(bytes).map(Package)
19+
}
20+
}
21+
22+
// CONVERSIONS
23+
// ================================================================================================
24+
25+
impl From<NativePackage> for Package {
26+
fn from(native_package: NativePackage) -> Self {
27+
Package(native_package)
28+
}
29+
}
30+
31+
impl From<&NativePackage> for Package {
32+
fn from(native_package: &NativePackage) -> Self {
33+
Package(native_package.clone())
34+
}
35+
}
36+
37+
impl From<Package> for NativePackage {
38+
fn from(package: Package) -> Self {
39+
package.0
40+
}
41+
}
42+
43+
impl From<&Package> for NativePackage {
44+
fn from(package: &Package) -> Self {
45+
package.0.clone()
46+
}
47+
}

crates/web-client/src/utils/test_utils.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
use alloc::sync::Arc;
2+
3+
use miden_client::Serializable;
14
use miden_client::account::AccountId as NativeAccountId;
5+
use miden_client::assembly::Assembler as NativeAssembler;
26
use miden_client::testing::account_id::ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE;
7+
use miden_client::vm::{
8+
MastArtifact as NativeMastArtifact,
9+
Package as NativePackage,
10+
PackageManifest as NativePackageManifest,
11+
};
312
use wasm_bindgen::prelude::*;
13+
use wasm_bindgen_futures::js_sys::Uint8Array;
414

515
use crate::models::account_id::AccountId;
616

@@ -15,4 +25,32 @@ impl TestUtils {
1525
ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE.try_into().unwrap();
1626
native_account_id.into()
1727
}
28+
29+
#[wasm_bindgen(js_name = "createMockSerializedPackage")]
30+
pub fn create_mock_serialized_package() -> Uint8Array {
31+
pub const CODE: &str = "
32+
export.foo
33+
push.1.2 mul
34+
end
35+
36+
export.bar
37+
push.1.2 add
38+
end
39+
";
40+
41+
let library = NativeAssembler::default().assemble_library([CODE]).unwrap();
42+
43+
let package_without_metadata = NativePackage {
44+
name: "test_package_no_metadata".to_string(),
45+
mast: NativeMastArtifact::Library(Arc::new(library)),
46+
manifest: NativePackageManifest::new(None),
47+
sections: vec![], // No metadata section
48+
version: Default::default(),
49+
description: None,
50+
};
51+
52+
let bytes: Vec<u8> = package_without_metadata.to_bytes();
53+
54+
Uint8Array::from(bytes.as_slice())
55+
}
1856
}

crates/web-client/test/global.test.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
NoteType,
4545
OutputNote,
4646
OutputNotesArray,
47+
Package,
4748
PublicKey,
4849
Rpo256,
4950
RpcClient,
@@ -132,6 +133,7 @@ declare global {
132133
NoteType: typeof NoteType;
133134
OutputNote: typeof OutputNote;
134135
OutputNotesArray: typeof OutputNotesArray;
136+
Package: typeof Package;
135137
PublicKey: typeof PublicKey;
136138
Rpo256: typeof Rpo256;
137139
SecretKey: typeof SecretKey;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Page } from "@playwright/test";
2+
import test from "./playwright.global.setup";
3+
4+
export const deserializePackageFromBytes = async (
5+
testingPage: Page
6+
): Promise<void> => {
7+
await testingPage.evaluate(async () => {
8+
const testPackageBytes = window.TestUtils.createMockSerializedPackage();
9+
window.Package.deserialize(testPackageBytes);
10+
});
11+
};
12+
13+
export const createAccountComponentFromPackage = async (
14+
testingPage: Page
15+
): Promise<void> => {
16+
return await testingPage.evaluate(async () => {
17+
const testPackageBytes = window.TestUtils.createMockSerializedPackage();
18+
const deserializedPackage = window.Package.deserialize(testPackageBytes);
19+
let emptyStorageSlot = window.StorageSlot.emptyValue();
20+
let storageMap = new window.StorageMap();
21+
let storageSlotMap = window.StorageSlot.map(storageMap);
22+
23+
window.AccountComponent.fromPackage(
24+
deserializedPackage,
25+
new window.MidenArrays.StorageSlotArray([
26+
emptyStorageSlot,
27+
storageSlotMap,
28+
])
29+
);
30+
});
31+
};
32+
33+
test.describe("package tests", () => {
34+
test("successfully deserializes a package from bytes", async ({ page }) => {
35+
await deserializePackageFromBytes(page);
36+
});
37+
38+
test("creates an account component from a package and storage slot array", async ({
39+
page,
40+
}) => {
41+
await createAccountComponentFromPackage(page);
42+
});
43+
});

0 commit comments

Comments
 (0)