Skip to content

Commit

Permalink
WASM implementation of adapters' traits on WASM types (#1506)
Browse files Browse the repository at this point in the history
* Move identity_wasm package into new folder bindings/wasm/identity_wasm

* Reorganize tsconfig and typedoc files

* Extend build scripts to handle multiple artifacts and add new bindings/wasm README.md

* Update identity_wasm/package.json to use shared build scripts

* Initial version of the identity_iota_interaction crate

* MoveType has been moved from identity_iota_core to identity_iota_interaction
* The identity_iota_interaction README has been updated to use the latest draft for folder and crate names

* Provide identity_iota_interaction crate types as `use identity_iota::iota_interaction::...`

* Initial version of iota_interactions_ts

* Prepare identity_iota_core/rebased for iota_interaction usage

* Minor README fixes

* Updated identity_interaction_rust MoveCalls to the latest identity_iota_core/rebase version

* First compilable but not successfully testable version of identity_iota_core for the redesigned crate structure

* Compiling identity_iota_core for wasm32 will result in one compiler error
  due to missing wasm-bindings for ProgrammableTransaction
  (TODO in identity_iota_core/src/rebased/transaction.rs)
* identity_iota_core and examples are compilable for non wasm32 platforms
  * Remaining compiler warnings should be investigated
* identity_iota_core e2e tests are compilable but will fail due to serialization/deserialization issues.
  For example:
    IotaTransactionBlockResponse bcs deserialization failed: NotSupported("deserialize_any")
    thread 'identity::adding_controller_works' panicked at /home/christof/Develop/iotaledger/identity.rs/identity_iota_core/src/rebased/transaction.rs:84:14:
    IotaTransactionBlockResponse bcs deserialization failed: NotSupported("deserialize_any")
    stack backtrace:
      0: rust_begin_unwind
      1: core::panicking::panic_fmt
      2: core::result::unwrap_failed
      3: <identity_iota_core::rebased::transaction::TransactionOutput<T> as core::convert::From<identity_iota_core::rebased::transaction::TransactionOutputInternal<T>>>::from
      4: <T as identity_iota_core::rebased::transaction::Transaction>::execute_with_opt_gas::{{closure}}
      5: identity_iota_core::rebased::transaction::Transaction::execute::{{closure}}
      6: e2e::identity::adding_controller_works::{{closure}}
      7: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
      8: tokio::runtime::runtime::Runtime::block_on
      9: e2e::identity::adding_controller_works
      10: core::ops::function::FnOnce::call_once
    note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

* add stubs for `ProgrammableTransaction` type chain

* re-add changes from `feat/add-iota_client_ts_sdk-impls` as closely as possible

* ts move calls

* Use cloned native IotaTransactionBlockResponse instead of a BCS deserialization

BCS deserialization is not available for IotaTransactionBlockResponse because the BCS format is not self describing and several struct fields of IotaTransactionBlockResponse are optionally skipped during serialization. JSON deserialization will probably be available but using a cloned instance to convert a TransactionOutputInternal into a TransactionOutput will have a much better efficiency.

* Fix build warnings and fix MoveType implementation for IotaVerifiableCredential

All tests running successfully for the first time now

* Fix & allow clippy errors

* Fix clippy error writing `&Vec` instead of `&[_]` involves a new object where a slice will do
* allow error clippy::too_many_arguments

* Fix even more clippy errors

* Elide superfluous explicit lifetimes

* Use rustfmt ignore to exclude identity_iota_interaction/src/sdk_types from format checks

* Fix rustfmt issues for identity_iota_core/src/iota_interaction_rust and identity_iota_interaction

* Fix rustfmt issues for bindings/wasm/iota_interaction_ts and identity_iota_core

* Update all paths for bindings/wasm/identity_wasm in al GH workflows files and add clippy + format steps for bindings/wasm/iota_interaction_ts

* Rollback removing bindings/wasm/iota_interaction_ts from the workspace members list

* Fix format issue for step "wasm fmt check identity_wasm"

* Fix issue "identity_iota_core not compilable using --no-default-features"

* Remove iota_interaction_ts related steps from github workflows as this crate is processed as workspace member

* Fix several dprint issues

* scratch

* Add iota_interaction_ts build products as 'excludes' to the dprint config

* Fixing paths to the bindings/wasm/identity_wasm crate in several READMEs and gitignore files

* fix dprint issue missing empty line at end  of file

* Update licence_template file to allow optional "Modifications", "(c)" words and specific contributor copyrights

* Add missing copyright license comments

* Narrowed the dockerignore filter down from "bindings/wasm" to "bindings/wasm/identity_wasm" because bindings/wasm/iota_interaction_ts is needed to build the docker image

* implement TsMoveCalls

* bindings/wasm projects identity_wasm and iota_interaction_ts can be build now and example bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts can be started

* dprint refomated

* assets move calls

* avoid unsafe

* clippy

* Removed IntentFn adapters and fixed several issues reulting from last merged PR - identity_wasm rust toolchain set to "1.84.0"

* implement missing bindings

* make iota_interaction_ts a workspace member again

* TransactionBuilder adaptor

* TransactionBlockResponseAdapter

* fix merge issues

* fmt

* Update package.json

Removed superfluous @iota/iota-sdk dependency from iota_interaction_ts package

* fix formatting and re-add TS ProgrammableTransaction

* identity_wasm fmt

---------

Co-authored-by: chrisgitiota <christof.gerritsma@iota.org>
Co-authored-by: Sebastian Wolfram <wulfraem@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent c3d5f09 commit 7c4c46d
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 34 deletions.
10 changes: 6 additions & 4 deletions bindings/wasm/build/node.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
const path = require("path");
const fs = require("fs");
const {lintAll} = require("./lints");
const { lintAll } = require("./lints");
const generatePackage = require("./utils/generatePackage");

const artifact = process.argv[2];

const RELEASE_FOLDER = path.join(__dirname, "..", artifact, "node");
const entryFilePathNode = path.join(RELEASE_FOLDER, `${artifact}.js`);
const entryFileNode = fs.readFileSync(entryFilePathNode).toString();
console.log(`[build/node.js] Processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,)
console.log(`[build/node.js] Processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`);

lintAll(entryFileNode);

Expand All @@ -29,7 +29,9 @@ fs.writeFileSync(
entryFilePathNode,
changedFileNode,
);
console.log(`[build/node.js] Added node-fetch polyfill to entryFile '${entryFilePathNode}'. Starting generatePackage().`,)
console.log(
`[build/node.js] Added node-fetch polyfill to entryFile '${entryFilePathNode}'. Starting generatePackage().`,
);

// Generate `package.json`.
const newPackage = generatePackage({
Expand All @@ -38,4 +40,4 @@ const newPackage = generatePackage({
artifact,
});
fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2));
console.log(`[build/node.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,)
console.log(`[build/node.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`);
10 changes: 5 additions & 5 deletions bindings/wasm/build/web.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const path = require("path");
const fs = require("fs");
const fse = require("fs-extra");
const {lintAll} = require("./lints");
const { lintAll } = require("./lints");
const generatePackage = require("./utils/generatePackage");

const artifact = process.argv[2];

const RELEASE_FOLDER = path.join(__dirname, "..", artifact, "web");
const entryFilePath = path.join(RELEASE_FOLDER, `${artifact}.js`);
const entryFile = fs.readFileSync(entryFilePath).toString();
console.log(`[build/web.js] Processing entryFile '${entryFilePath}' for artifact '${artifact}'`,)
console.log(`[build/web.js] Processing entryFile '${entryFilePath}' for artifact '${artifact}'`);

lintAll(entryFile);

Expand All @@ -29,7 +29,7 @@ fs.writeFileSync(
entryFilePath,
changedFile,
);
console.log(`[build/web.js] Commented out webpack workaround for '${entryFilePath}'.`,)
console.log(`[build/web.js] Commented out webpack workaround for '${entryFilePath}'.`);

const entryFilePathTs = path.join(RELEASE_FOLDER, `${artifact}.d.ts`);
const entryFileTs = fs.readFileSync(entryFilePathTs).toString();
Expand All @@ -47,7 +47,7 @@ fs.writeFileSync(
entryFilePathTs,
changedFileTs,
);
console.log(`[build/web.js] Created init function for '${entryFilePathTs}'. Starting generatePackage().`,)
console.log(`[build/web.js] Created init function for '${entryFilePathTs}'. Starting generatePackage().`);

// Generate `package.json`.
const newPackage = generatePackage({
Expand All @@ -56,4 +56,4 @@ const newPackage = generatePackage({
artifact,
});
fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2));
console.log(`[build/web.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,)
console.log(`[build/web.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`);
2 changes: 1 addition & 1 deletion bindings/wasm/iota_interaction_ts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ js-sys = { version = "0.3.61" }
secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.6.5"
serde_json = { version = "1.0", default-features = false }
serde_json.workspace = true
thiserror.workspace = true
tsify = "0.4.5"
wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] }
Expand Down
4 changes: 4 additions & 0 deletions bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export class IotaTransactionBlockResponseAdapter {
? this.response.effects.created
: null;
}

get_response(): IotaTransactionBlockResponse {
return this.response;
}
}

async function signTransactionData(
Expand Down
1 change: 0 additions & 1 deletion bindings/wasm/iota_interaction_ts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 26 additions & 14 deletions bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright 2020-2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::net::AddrParseError;

use identity_iota_interaction::rpc_types::IotaExecutionStatus;
use identity_iota_interaction::rpc_types::OwnedObjectRef;
use identity_iota_interaction::types::base_types::IotaAddress;
use identity_iota_interaction::types::base_types::ObjectID;
Expand All @@ -9,8 +12,10 @@ use identity_iota_interaction::types::base_types::SequenceNumber;
use identity_iota_interaction::types::execution_status::CommandArgumentError;
use identity_iota_interaction::types::execution_status::ExecutionStatus;
use identity_iota_interaction::types::object::Owner;
use identity_iota_interaction::IotaTransactionBlockResponseT;
use identity_iota_interaction::ProgrammableTransactionBcs;
use js_sys::Promise;
use js_sys::Uint8Array;
use serde::Deserialize;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsCast;
Expand All @@ -21,6 +26,7 @@ use crate::bindings::WasmIotaClient;
use crate::common::into_sdk_type;
use crate::console_log;
use crate::error::TsSdkError;
use crate::error::WasmError;

// TODO: fix/add
// not available anymore
Expand Down Expand Up @@ -58,9 +64,6 @@ const TS_SDK_TYPES: &'static str = r#"
executeTransaction,
IotaTransactionBlockResponseAdapter,
} from "./iota_client_helpers"
// TODO: decide if we use this or replace it with an adapter written in TypeScript type if needed
type ProgrammableTransaction = ReturnType<typeof bcs.ProgrammableTransaction.parse>;
"#;

#[wasm_bindgen(module = "@iota/iota.js/client")]
Expand All @@ -71,6 +74,12 @@ extern "C" {
#[wasm_bindgen(typescript_type = "Transaction")]
pub type WasmTransactionBuilder;

#[wasm_bindgen(js_name = "from", static_method_of = WasmTransactionBuilder, catch)]
pub fn from_bcs_bytes(bytes: &[u8]) -> Result<WasmTransactionBuilder, JsValue>;

#[wasm_bindgen(method, structural, catch)]
pub async fn build(this: &WasmTransactionBuilder) -> Result<Uint8Array, JsValue>;

#[wasm_bindgen(typescript_type = "TransactionArgument")]
pub type WasmTransactionArgument;

Expand Down Expand Up @@ -238,6 +247,9 @@ extern "C" {
#[wasm_bindgen(method)]
fn effects_created_inner(this: &IotaTransactionBlockResponseAdapter) -> Option<Vec<WasmOwnedObjectRef>>;

#[wasm_bindgen(method, js_name = "get_response")]
fn response(this: &IotaTransactionBlockResponseAdapter) -> WasmIotaTransactionBlockResponse;

#[wasm_bindgen(js_name = executeTransaction)]
fn execute_transaction_inner(
iotaClient: &WasmIotaClient, // --> TypeScript: IotaClient
Expand All @@ -249,12 +261,6 @@ extern "C" {
) -> PromiseIotaTransactionBlockResponseAdapter;
}

#[wasm_bindgen] // no module here, as imported via custom section above
extern "C" {
#[wasm_bindgen(typescript_type = "ProgrammableTransaction")]
pub type WasmProgrammableTransaction;
}

#[derive(Deserialize)]
struct WasmExecutionStatusAdapter {
status: ExecutionStatus,
Expand Down Expand Up @@ -306,9 +312,15 @@ pub async fn execute_transaction(
}

#[derive(Deserialize)]
// TODO: add manual deserialization later on (must be deserializable, but WasmPT is not)
// pub struct ProgrammableTransaction(WasmProgrammableTransaction);
pub struct ProgrammableTransaction(());
#[serde(try_from = "Vec<u8>")]
pub struct ProgrammableTransaction(WasmTransactionBuilder);

// TODO: fill in required functions and data handling here
impl ProgrammableTransaction {}
impl TryFrom<Vec<u8>> for ProgrammableTransaction {
type Error = TsSdkError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
WasmTransactionBuilder::from_bcs_bytes(&value)
.map(Self)
.map_err(WasmError::from)
.map_err(TsSdkError::from)
}
}
2 changes: 0 additions & 2 deletions bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ use identity_iota_interaction::rpc_types::OwnedObjectRef;
use identity_iota_interaction::types::base_types::IotaAddress;
use identity_iota_interaction::types::base_types::ObjectID;
use identity_iota_interaction::types::base_types::ObjectRef;
use identity_iota_interaction::types::base_types::SequenceNumber;
use identity_iota_interaction::types::transaction::Argument;
use identity_iota_interaction::types::TypeTag;
use identity_iota_interaction::BorrowIntentFnInternalT;
use identity_iota_interaction::ControllerIntentFnInternalT;
Expand Down
3 changes: 1 addition & 2 deletions bindings/wasm/iota_interaction_ts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ cfg_if::cfg_if! {

#[allow(unused_imports)] pub use error::TsSdkError as AdapterError;
#[allow(unused_imports)] pub use bindings::IotaTransactionBlockResponseAdapter as AdapterNativeResponse;
#[allow(unused_imports)] pub use bindings::ProgrammableTransaction;

#[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper;

#[allow(unused_imports)] pub use bindings::ProgrammableTransaction;
}
}
2 changes: 0 additions & 2 deletions bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ use identity_iota_interaction::ident_str;
use identity_iota_interaction::rpc_types::OwnedObjectRef;
use identity_iota_interaction::types::base_types::ObjectID;
use identity_iota_interaction::types::base_types::ObjectRef;
use identity_iota_interaction::types::transaction::ObjectArg;
use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID;
use identity_iota_interaction::MigrationMoveCalls;
use identity_iota_interaction::ProgrammableTransactionBcs;
use js_sys::Uint8Array;
Expand Down
10 changes: 7 additions & 3 deletions bindings/wasm/iota_interaction_ts/src/transaction_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::ops::DerefMut;

use crate::bindings::WasmTransactionBuilder;
use crate::error::TsSdkError;
use crate::error::WasmError;
use identity_iota_interaction::ProgrammableTransactionBcs;
use identity_iota_interaction::TransactionBuilderT;

Expand All @@ -26,15 +27,18 @@ impl TransactionBuilderT for TransactionBuilderTsSdk {
type NativeTxBuilder = NativeTsTransactionBuilderBindingWrapper;

fn finish(self) -> Result<ProgrammableTransactionBcs, TsSdkError> {
unimplemented!();
futures::executor::block_on(self.builder.build())
.map(|js_arr| js_arr.to_vec())
.map_err(WasmError::from)
.map_err(Self::Error::from)
}

fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder {
todo!()
&mut self.builder
}

fn into_native_tx_builder(self) -> Self::NativeTxBuilder {
todo!()
self.builder
}
}

Expand Down

0 comments on commit 7c4c46d

Please sign in to comment.