From 8fa2d1c510895405527494ae30ef0f7dd59e045d Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Mon, 27 Jan 2025 12:51:13 +0100 Subject: [PATCH 01/63] add wip state for identity client --- bindings/wasm/identity_wasm/Cargo.toml | 3 +- .../examples/src/0_basic/-1_test_api_call.ts | 369 +++++++++++------- .../examples/src/0_basic/0_create_did.ts | 107 ++--- .../examples/src/0_basic/1_update_did.ts | 64 +-- .../examples/src/0_basic/2_resolve_did.ts | 40 +- .../wasm/identity_wasm/examples/src/main.ts | 6 +- .../identity_wasm/examples/src/utils_alpha.ts | 150 +++++++ bindings/wasm/identity_wasm/package-lock.json | 6 +- .../src/kinesis/client_dummy/identity.rs | 2 +- .../kinesis/client_dummy/identity_client.rs | 158 -------- .../client_dummy/identity_client_builder.rs | 82 ---- .../src/kinesis/client_dummy/mod.rs | 13 +- .../identity_wasm/src/kinesis/identity.rs | 126 +++--- .../wasm/identity_wasm/src/kinesis/mod.rs | 4 +- .../src/kinesis/wasm_identity_client.rs | 221 +++++------ .../kinesis/wasm_identity_client_read_only.rs | 120 ++++++ .../wasm/identity_wasm/src/storage/mod.rs | 6 +- .../src/storage/storage_signer_owned.rs | 93 +++++ .../src/storage/wasm_storage_signer.rs | 17 +- .../src/storage/wasm_storage_signer_inner.rs | 93 +++++ .../src/rebased/client/read_only.rs | 18 +- .../src/rebased/iota/types/mod.rs | 2 +- .../src/rebased/migration/identity.rs | 2 +- .../src/rebased/migration/multicontroller.rs | 2 +- identity_iota_core/src/rebased/transaction.rs | 4 +- 25 files changed, 970 insertions(+), 738 deletions(-) create mode 100644 bindings/wasm/identity_wasm/examples/src/utils_alpha.ts delete mode 100644 bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs delete mode 100644 bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs create mode 100644 bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs create mode 100644 bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs create mode 100644 bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 4b5db73196..522615e5c1 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -26,7 +26,7 @@ iota-sdk = { version = "1.1.5", default-features = false, features = ["serde", " js-sys = { version = "0.3.61" } json-proof-token = "0.3.4" proc_typescript = { version = "0.1.0", path = "./proc_typescript" } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, branch = "main" } +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 } @@ -36,6 +36,7 @@ tokio = { version = "=1.39.2", default-features = false, features = ["sync"] } tsify = "0.4.5" wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } +anyhow = "1.0.95" [dependencies.identity_iota] path = "../../../identity_iota" diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index 060e88c604..301614b8c1 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -11,19 +11,23 @@ import { JwsAlgorithm, KeyIdMemStore, KinesisIdentityClient, + KinesisIdentityClientReadOnly, Multicontroller, + OnChainIdentity, ProposalAction, Storage, + StorageSigner, // StorageSigner, } from "@iota/identity-wasm/node"; import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; import { bcs } from "@iota/iota.js/bcs"; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { IotaClient as KinesisClient, QueryEventsParams } from "@iota/iota.js/client"; import { getFaucetHost, requestIotaFromFaucetV0, requestIotaFromFaucetV1 } from "@iota/iota.js/faucet"; import { Ed25519Keypair, Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; import { Transaction } from "@iota/iota.js/transactions"; import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; +import { event } from "cypress/types/jquery"; // this was is the implemented in `src/kinesis/wasm_identity_client.rs` // and then imported here as `convertToAddress` from `@iota/identity-wasm/node` (see above) @@ -54,7 +58,8 @@ export const DEFAULT_GAS_BUDGET = 10000000; const NETWORK_NAME = "local"; const NETWORK_NAME_FAUCET = "localnet"; const NETWORK_URL = "http://127.0.0.1:9000"; -const IDENTITY_IOTA_PACKAGE_ID = "0x7e0ccc737a8def97f37fe9f70267a14bc0fe0871c12f8742fac5e3baf58eb45b"; +// for now fixed, better impl in utils already +const IDENTITY_IOTA_PACKAGE_ID = "0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d"; async function initializeClients() { const kinesis_client = new KinesisClient({ url: NETWORK_URL }); @@ -99,76 +104,154 @@ async function initializeClients() { let pub_key = key_pair.getPublicKey(); console.log(`Created Ed25519Keypair with PublicKey ${pub_key.toBase64()} and address ${pub_key.toIotaAddress()}`); - // test builder and create instance for other tests - let identityClient = KinesisIdentityClient - .builder() - .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) - .senderPublicKey(pub_key.toRawBytes()) - .senderAddress(pub_key.toIotaAddress()) - .iotaClient(kinesis_client) - .networkName(NETWORK_NAME) - .build(); - - await requestIotaFromFaucetV0({ - host: getFaucetHost(NETWORK_NAME_FAUCET), - recipient: identityClient.senderAddress(), + // // test builder and create instance for other tests + // let identityClient = KinesisIdentityClient + // .builder() + // .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) + // .senderPublicKey(pub_key.toRawBytes()) + // .senderAddress(pub_key.toIotaAddress()) + // .iotaClient(kinesis_client) + // .networkName(NETWORK_NAME) + // .build(); + + // delete later if not required anymore + // try to find package beforehand + // "MoveEventType":"0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d::migration_registry::MigrationRegistryCreated" + // "Sender": "0xd40005ab355d8342fa6b94e9638a1040483d70430720d28e9b425283d011c0a8" + // const eventsQuery: QueryEventsParams = { + // "query": { + // "MoveEventType":"0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d::migration_registry::MigrationRegistryCreated" + // }, + // "limit":1, + // "order":"ascending" + // }; + // const eventsResult = await kinesis_client.queryEvents(eventsQuery); + // console.dir(eventsResult); + + const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesis_client, IDENTITY_IOTA_PACKAGE_ID); + console.dir(identityClientReadOnly); + + // create new storage + const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); + + // generate new key + let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); + if (typeof publicKeyJwk === "undefined") { + throw new Error("failed to derive public JWK from generated JWK"); + } + let keyId = generate.keyId(); + console.dir({ + keyId, + publicKeyJwk: publicKeyJwk, }); - const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); - if (balance.totalBalance === "0") { - throw new Error("Balance is still 0"); - } else { - console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); - } + // create signer from storage + let signer = new StorageSigner(storage, keyId, publicKeyJwk); + const identityClient = await KinesisIdentityClient.create(identityClientReadOnly, signer); + + // await requestIotaFromFaucetV0({ + // host: getFaucetHost(NETWORK_NAME_FAUCET), + // recipient: identityClient.senderAddress(), + // }); + + // const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + // if (balance.totalBalance === "0") { + // throw new Error("Balance is still 0"); + // } else { + // console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); + // } return { kinesis_client, identityClient, key_pair }; } + +async function testIdentityClientReadOnly() { + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + const identityClient = await KinesisIdentityClientReadOnly.createWithPkgId(kinesisClient, IDENTITY_IOTA_PACKAGE_ID); + + console.log("\n-------------- Start testIdentityClientReadOnly -------------------------------"); + console.log(`networkName: ${identityClient.network()}`); + console.log(`packageId ${identityClient.packageId()}`); + console.log(`migrationRegistry ${identityClient.migrationRegistryId()}`); + console.log(`resolveDid ${await identityClient.resolveDid( + IotaDID.fromAliasId( + "0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc", + identityClient.network(), + ) + )}`); + const identity = await identityClient.getIdentity("0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc"); + console.log(`identity.id ${identity.toFullFledged()?.id()}`); +} + + async function testIdentityClient( identityClient: KinesisIdentityClient, kinesis_client: KinesisClient, key_pair: Ed25519Keypair, ): Promise { console.log("\n-------------- Start testIdentityClient -------------------------------"); - console.log(`chainIdentifier: ${await identityClient.getChainIdentifier()}`); console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); console.log(`senderAddress: ${identityClient.senderAddress()}`); - console.log(`networkName: ${identityClient.networkName()}`); + console.log(`networkName: ${identityClient.network()}`); + console.log(`packageId ${identityClient.packageId()}`); + console.log(`migrationRegistry ${identityClient.migrationRegistryId()}`); + console.log(`resolveDid ${await identityClient.resolveDid( + IotaDID.fromAliasId( + "0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc", + identityClient.network(), + ) + )}`); + + const newDoc = new IotaDocument(identityClient.network()); + console.dir(newDoc); + const builder = identityClient.createIdentity(newDoc) + console.dir(builder); + const tx = builder.finish(); + console.dir(tx); + const ex = tx.execute(identityClient); + console.dir(ex); - try { - console.log("\n---------------- executeDummyTransaction ------------------------"); - let coins = await kinesis_client.getCoins({ - owner: identityClient.senderAddress(), - coinType: IOTA_TYPE_ARG, - }); - const tx = new Transaction(); - const coin_0 = coins.data[0]; - const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ - bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), - ]); - tx.transferObjects([coin], identityClient.senderAddress()); - tx.setSenderIfNotSet(key_pair.getPublicKey().toIotaAddress()); - const signatureWithBytes = await tx.sign({ signer: key_pair, client: kinesis_client }); - - const response = await identityClient.executeDummyTransaction( - signatureWithBytes.bytes, - [signatureWithBytes.signature], - ); - console.dir(response); - - // The above transaction execution is equivalent to the following snippet using the TS SDK iota client - const response2 = await kinesis_client.executeTransactionBlock({ - transactionBlock: signatureWithBytes.bytes, - signature: signatureWithBytes.signature, - }); - console.log(`TX result: ${response2}`); - } catch (ex) { - console.log(`\nTest execute_dummy_transaction() - Error: ${(ex as Error).message}`); - } + // try { + // console.log("\n---------------- executeDummyTransaction ------------------------"); + // let coins = await kinesis_client.getCoins({ + // owner: identityClient.senderAddress(), + // coinType: IOTA_TYPE_ARG, + // }); + // const tx = new Transaction(); + // const coin_0 = coins.data[0]; + // const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ + // bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), + // ]); + // tx.transferObjects([coin], identityClient.senderAddress()); + // tx.setSenderIfNotSet(key_pair.getPublicKey().toIotaAddress()); + // const signatureWithBytes = await tx.sign({ signer: key_pair, client: kinesis_client }); + + // const response = await identityClient.executeDummyTransaction( + // signatureWithBytes.bytes, + // [signatureWithBytes.signature], + // ); + // console.dir(response); + + // // The above transaction execution is equivalent to the following snippet using the TS SDK iota client + // const response2 = await kinesis_client.executeTransactionBlock({ + // transactionBlock: signatureWithBytes.bytes, + // signature: signatureWithBytes.signature, + // }); + // console.log(`TX result: ${response2}`); + // } catch (ex) { + // console.log(`\nTest execute_dummy_transaction() - Error: ${(ex as Error).message}`); + // } try { console.log("\n---------------- getIdentity ------------------------"); - await identityClient.getIdentity("foobar"); + // const testValue = await identityClient.getIdentity("foobar"); + const testValue = await identityClient.getIdentity("0xd9a0f8139076bfbdc245d402c655b4e93cdf5b4184294da2bbbf7ae3d8ec97a4"); + console.dir(testValue); + const fufle = testValue.toFullFledged() as OnChainIdentity; + console.dir(fufle.id()); + console.dir(fufle.isShared()); + } catch (ex) { console.log(`Test getIdentity() - Error: ${(ex as Error).message}`); } @@ -224,81 +307,81 @@ function testMultiController(): void { console.dir(multiController.threshold()); } -async function testProposals(identityClient: KinesisIdentityClient): Promise { - let action: ProposalAction = "Deactivate"; - console.dir(action); - - action = { UpdateDocument: new IotaDocument("foobar") }; - console.dir(action); - console.dir(action.UpdateDocument); - console.dir(action.UpdateDocument.id()); - console.dir(action.UpdateDocument.toJSON()); - - let identity = await identityClient - .createIdentity(Uint8Array.from([1, 2, 3])) - .threshold(BigInt(1)) - .gasBudget(BigInt(1)) - .controllers([ - new ControllerAndVotingPower("one", BigInt(1)), - new ControllerAndVotingPower("two", BigInt(2)), - ]) - .finish(identityClient, "dummySigner"); - console.dir(identity); - console.dir(identity.isShared()); - console.dir(identity.proposals()); - const deactivateProposal = await identity - .deactivateDid() - .expirationEpoch(BigInt(1)) - .gasBudget(BigInt(1)) - .key("key") - .finish(identityClient, "dummySigner"); - console.dir(deactivateProposal); - - // proposals consume the identity instance, so we need a new one - identity = await identityClient - .createIdentity(Uint8Array.from([1, 2, 3])) - .threshold(BigInt(1)) - .gasBudget(BigInt(1)) - .controllers([ - new ControllerAndVotingPower("one", BigInt(1)), - new ControllerAndVotingPower("two", BigInt(2)), - ]) - .finish(identityClient, "dummySigner"); - - const updateProposal = await identity - .updateDidDocument(new IotaDocument("foobar")) - .expirationEpoch(BigInt(1)) - .gasBudget(BigInt(1)) - .key("key") - .finish(identityClient, "dummySigner"); - console.dir(updateProposal); -} - -// async function signerTest(): Promise { -// // create new storage -// const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); -// -// // generate new key -// let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); -// let publicKeyJwk = generate.jwk().toPublic(); -// if (typeof publicKeyJwk === "undefined") { -// throw new Error("failed to derive public JWK from generated JWK"); -// } -// let keyId = generate.keyId(); -// console.dir({ -// keyId, -// publicKeyJwk: publicKeyJwk, -// }); -// -// // create signer from storage -// let signer = new StorageSigner(storage, keyId, publicKeyJwk); -// console.log({ keyIdFromSigner: signer.keyId() }); -// -// // sign test -// let signed = await signer.sign(new Uint8Array([0, 1, 2, 4])); -// console.dir({ signed }); +// async function testProposals(identityClient: KinesisIdentityClient): Promise { +// let action: ProposalAction = "Deactivate"; +// console.dir(action); + +// action = { UpdateDocument: new IotaDocument("foobar") }; +// console.dir(action); +// console.dir(action.UpdateDocument); +// console.dir(action.UpdateDocument.id()); +// console.dir(action.UpdateDocument.toJSON()); + +// let identity = await identityClient +// .createIdentity(Uint8Array.from([1, 2, 3])) +// .threshold(BigInt(1)) +// .gasBudget(BigInt(1)) +// .controllers([ +// new ControllerAndVotingPower("one", BigInt(1)), +// new ControllerAndVotingPower("two", BigInt(2)), +// ]) +// .finish(identityClient, "dummySigner"); +// console.dir(identity); +// console.dir(identity.isShared()); +// console.dir(identity.proposals()); +// const deactivateProposal = await identity +// .deactivateDid() +// .expirationEpoch(BigInt(1)) +// .gasBudget(BigInt(1)) +// .key("key") +// .finish(identityClient, "dummySigner"); +// console.dir(deactivateProposal); + +// // proposals consume the identity instance, so we need a new one +// identity = await identityClient +// .createIdentity(Uint8Array.from([1, 2, 3])) +// .threshold(BigInt(1)) +// .gasBudget(BigInt(1)) +// .controllers([ +// new ControllerAndVotingPower("one", BigInt(1)), +// new ControllerAndVotingPower("two", BigInt(2)), +// ]) +// .finish(identityClient, "dummySigner"); + +// const updateProposal = await identity +// .updateDidDocument(new IotaDocument("foobar")) +// .expirationEpoch(BigInt(1)) +// .gasBudget(BigInt(1)) +// .key("key") +// .finish(identityClient, "dummySigner"); +// console.dir(updateProposal); // } +async function signerTest(): Promise { + // create new storage + const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); + + // generate new key + let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); + if (typeof publicKeyJwk === "undefined") { + throw new Error("failed to derive public JWK from generated JWK"); + } + let keyId = generate.keyId(); + console.dir({ + keyId, + publicKeyJwk: publicKeyJwk, + }); + + // create signer from storage + let signer = new StorageSigner(storage, keyId, publicKeyJwk); + console.log({ keyIdFromSigner: signer.keyId() }); + + // sign test + let signed = await signer.sign(new Uint8Array([0, 1, 2, 4])); + console.dir({ signed }); +} + // async function testExecuteTransaction(kinesis_client: KinesisClient) { // console.log("---------------- testing executeTransaction ------------------------"); // @@ -351,30 +434,38 @@ async function testProposals(identityClient: KinesisIdentityClient): Promise { const { kinesis_client, identityClient, key_pair } = await initializeClients(); - type ProgrammableTransaction = ReturnType; - let tt: ProgrammableTransaction = bcs.ProgrammableTransaction.parse(new Uint8Array([])); - console.dir(tt); + // type ProgrammableTransaction = ReturnType; + // let tt: ProgrammableTransaction = bcs.ProgrammableTransaction.parse(new Uint8Array([])); + // console.dir(tt); + try { - await testIdentityClient(identityClient, kinesis_client, key_pair); + await testIdentityClientReadOnly(); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`identity client binding test failed: ${suffix}`); } try { - testMultiController(); + await testIdentityClient(identityClient, kinesis_client, key_pair); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`multi controller binding test failed: ${suffix}`); + console.error(`identity client binding test failed: ${suffix}`); } - try { - await testProposals(identityClient); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`proposals binding test failed: ${suffix}`); - } + // try { + // testMultiController(); + // } catch (err) { + // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + // console.error(`multi controller binding test failed: ${suffix}`); + // } + + // try { + // await testProposals(identityClient); + // } catch (err) { + // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + // console.error(`proposals binding test failed: ${suffix}`); + // } // try { // await signerTest(); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index aacc6d5338..61158a0388 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -2,82 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 import { - IotaDID, - IotaDocument, - IotaIdentityClient, - JwkMemStore, - JwsAlgorithm, - KeyIdMemStore, - MethodScope, - Storage, -} from "@iota/identity-wasm/node"; -import { AliasOutput, Client, MnemonicSecretManager, SecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; - -/** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ -export async function createIdentity(): Promise<{ - didClient: IotaIdentityClient; - secretManager: SecretManager; - walletAddressBech32: string; - did: IotaDID; -}> { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp: string = await didClient.getNetworkHrp(); - - const mnemonicSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Generate a random mnemonic for our wallet. - const secretManager: SecretManager = new SecretManager(mnemonicSecretManager); - - const walletAddressBech32 = (await secretManager.generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: networkHrp, - }))[0]; - console.log("Wallet address Bech32:", walletAddressBech32); - - // Request funds for the wallet, if needed - only works on development networks. - await ensureAddressHasFunds(client, walletAddressBech32); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - - // Insert a new Ed25519 verification method in the DID document. - await document.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - "#key-1", - MethodScope.VerificationMethod(), - ); - - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - const address = Utils.parseBech32Address(walletAddressBech32); - const aliasOutput: AliasOutput = await didClient.newDidOutput(address, document); - console.log("Alias Output:", JSON.stringify(aliasOutput, null, 2)); - - // Publish the Alias Output and get the published DID document. - const published = await didClient.publishDidOutput(mnemonicSecretManager, aliasOutput); - console.log("Published DID document:", JSON.stringify(published, null, 2)); - - return { - didClient, - secretManager, - walletAddressBech32, - did: published.id(), - }; -} + createDidDocument, + getClientAndCreateAccount, + getMemstorage, +} from '../utils_alpha'; + +// old API: +// export async function createIdentityOld(): Promise<{ +// didClient: IotaIdentityClient; +// secretManager: SecretManager; +// walletAddressBech32: string; +// did: IotaDID; +// }> { /* ... */ } + +/** Demonstrate how to create a DID Document and publish it. */ +export async function createIdentity(): Promise { + // create new client to interact with chain and get funded account with keys + const storage = getMemstorage(); + const identityClient = await getClientAndCreateAccount(storage); + + // create new DID document and publish it + const [document] = await createDidDocument(identityClient, storage); + console.log(`Published DID document: ${JSON.stringify(document, null, 2)}`); + + // check if we can resolve it via client + const resolved = await identityClient.resolveDid(document.id()); + console.log(`Resolved DID document: ${JSON.stringify(resolved, null, 2)}`); + } + \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index be635b0dbd..c27d889b63 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -2,46 +2,35 @@ // SPDX-License-Identifier: Apache-2.0 import { - IotaDocument, - IotaIdentityClient, JwkMemStore, JwsAlgorithm, - KeyIdMemStore, MethodRelationship, MethodScope, Service, - Storage, Timestamp, VerificationMethod, } from "@iota/identity-wasm/node"; -import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; + +import { + createDidDocument, + getClientAndCreateAccount, + getMemstorage, + TEST_GAS_BUDGET, +} from "../utils_alpha"; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document, fragment } = await createDid( - client, - secretManager, - storage, - ); - const did = document.id(); + // create new client to interact with chain and get funded account with keys + const storage = getMemstorage(); + const identityClient = await getClientAndCreateAccount(storage); + + // create new DID document and publish it + let [document, vmFragment1] = await createDidDocument(identityClient, storage); + let did = document.id(); // Resolve the latest state of the document. // Technically this is equivalent to the document above. - document = await didClient.resolveDid(did); + document = await identityClient.resolveDid(did); // Insert a new Ed25519 verification method in the DID document. await document.generateMethod( @@ -55,6 +44,7 @@ export async function updateIdentity() { // Attach a new method relationship to the inserted method. document.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); + // Add a new Service. const service: Service = new Service({ id: did.join("#linked-domain"), @@ -65,24 +55,10 @@ export async function updateIdentity() { document.setMetadataUpdated(Timestamp.nowUTC()); // Remove a verification method. - let originalMethod = document.resolveMethod(fragment) as VerificationMethod; + let originalMethod = document.resolveMethod(vmFragment1) as VerificationMethod; await document.purgeMethod(storage, originalMethod?.id()); - // Resolve the latest output and update it with the given document. - let aliasOutput: AliasOutput = await didClient.updateDidOutput(document); - - // Because the size of the DID document increased, we have to increase the allocated storage deposit. - // This increases the deposit amount to the new minimum. - const rentStructure: IRent = await didClient.getRentStructure(); - - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - // Publish the output. - const updated: IotaDocument = await didClient.publishDidOutput(secretManager, aliasOutput); - console.log("Updated DID document:", JSON.stringify(updated, null, 2)); + let updated = identityClient + .publishDidDocumentUpdate(document.clone(), TEST_GAS_BUDGET); + console.log(`Updated DID document result: ${JSON.stringify(updated, null, 2)}`); } diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index ce8ea7c3e1..2792f7ad05 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -1,6 +1,11 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// TODO: +// - [ ] clarify if we need/want a resolver example +// - [ ] clarify if we need/want the AliasOutput -> ObjectID example + + import { CoreDocument, DIDJwk, @@ -12,40 +17,29 @@ import { Resolver, Storage, } from "@iota/identity-wasm/node"; -import { AliasOutput, Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; +import { AliasOutput, } from "@iota/sdk-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; +import { createDidDocument, getClientAndCreateAccount, getMemstorage } from "../utils_alpha"; const DID_JWK: string = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ export async function resolveIdentity() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - const did = document.id(); + // create new client to interact with chain and get funded account with keys + const storage = getMemstorage(); + const identityClient = await getClientAndCreateAccount(storage); + + // create new DID document and publish it + let [document] = await createDidDocument(identityClient, storage); + let did = document.id(); // Resolve the associated Alias Output and extract the DID document from it. - const resolved: IotaDocument = await didClient.resolveDid(did); + const resolved: IotaDocument = await identityClient.resolveDid(did); console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); - // We can also resolve the Alias Output directly. - const aliasOutput: AliasOutput = await didClient.resolveDidOutput(did); + // We can also resolve the Object ID reictly + const aliasOutput: AliasOutput = await identityClient.resolveDidOutput(did); console.log("The Alias Output holds " + aliasOutput.getAmount() + " tokens"); // did:jwk can be resolved as well. diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts index 343f45f1aa..b1e6ab528c 100644 --- a/bindings/wasm/identity_wasm/examples/src/main.ts +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { testApiCall } from "./0_basic/-1_test_api_call"; -// import { createIdentity } from "./0_basic/0_create_did"; +import { createIdentity } from "./0_basic/0_create_did"; // import { updateIdentity } from "./0_basic/1_update_did"; // import { resolveIdentity } from "./0_basic/2_resolve_did"; // import { deactivateIdentity } from "./0_basic/3_deactivate_did"; @@ -31,8 +31,8 @@ async function main() { switch (argument) { case "-1_test_api_call": return await testApiCall(); - // case "0_create_did": - // return await createIdentity(); + case "0_create_did": + return await createIdentity(); // case "1_update_did": // return await updateIdentity(); // case "2_resolve_did": diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts new file mode 100644 index 0000000000..d2ad0b58a1 --- /dev/null +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -0,0 +1,150 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + IotaDocument, + JwkMemStore, + JwsAlgorithm, + KeyIdMemStore, + KinesisIdentityClient, + KinesisIdentityClientReadOnly, + MethodScope, + Storage, +} from "@iota/identity-wasm/node"; +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; +import { Ed25519Keypair } from "@iota/iota.js/keypairs/ed25519"; + +export const TEST_GAS_BUDGET = 50_000_000; + +const IOTA_LOCAL_NETWORK_URL = "http://127.0.0.1:9000"; +const NETWORK_NAME = "local"; +const NETWORK_NAME_FAUCET = "localnet" + +const { + API_ENDPOINT, + IDENTITY_IOTA_PACKAGE_ID, +} = process.env; + +export function getMemstorage(): Storage { + return new Storage(new JwkMemStore(), new KeyIdMemStore()); +} + +export async function getClientAndCreateAccount(storage: Storage): Promise { + let api_endpoint = API_ENDPOINT || IOTA_LOCAL_NETWORK_URL; + if (!IDENTITY_IOTA_PACKAGE_ID) { + throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); + } + + const kinesisClient = new KinesisClient({ url: api_endpoint }); + + // generate new key + // TODO: make random key + console.log("---------------- Preparing IdentityClient ------------------------"); + const VALID_SECP256K1_SECRET_KEY = [ + 59, + 148, + 11, + 85, + 134, + 130, + 61, + 253, + 2, + 174, + 59, + 70, + 27, + 180, + 51, + 107, + 94, + 203, + 174, + 253, + 102, + 39, + 170, + 146, + 46, + 252, + 4, + 143, + 236, + 12, + 136, + 28, + ]; + const secretKey = new Uint8Array(VALID_SECP256K1_SECRET_KEY); + let keyPair = Ed25519Keypair.fromSecretKey(secretKey); + let pubKey = keyPair.getPublicKey(); + console.log(`Created Ed25519Keypair with PublicKey ${pubKey.toBase64()} and address ${pubKey.toIotaAddress()}`); + + // test builder and create instance for other tests + // let identityClient = KinesisIdentityClient + // .builder() + // .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) + // .senderPublicKey(pubKey.toRawBytes()) + // .senderAddress(pubKey.toIotaAddress()) + // .iotaClient(kinesisClient) + // .networkName(NETWORK_NAME) + // .build(); + const identityClientReadOnly = KinesisIdentityClientReadOnly.create(kinesisClient); + console.dir(identityClientReadOnly); + const identityClient = null as any; + + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: identityClient.senderAddress(), + }); + + const balance = await kinesisClient.getBalance({ owner: identityClient.senderAddress() }); + if (balance.totalBalance === "0") { + throw new Error("Balance is still 0"); + } else { + console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); + } + + return identityClient; +} + +export async function createDidDocument( + identity_client: KinesisIdentityClient, + storage: Storage, +): Promise<[IotaDocument, String]> { + // Create a new DID document with a placeholder DID. + const unpublished = new IotaDocument(identity_client.network()); + + throw new Error('createDidDocument not fully implemented'); + + // // Insert a new Ed25519 verification method in the DID document. + // await unpublished.generateMethod( + // storage, + // JwkMemStore.ed25519KeyType(), + // JwsAlgorithm.EdDSA, + // "#key-1", + // MethodScope.VerificationMethod(), + // ); + + // let verification_method_fragment = await unpublished + // .generate_method( + // storage, + // JwkMemStore.ed25519KeyType(), + // JwsAlgorithm.EdDSA, + // "#key-1", // TODO: `None` in Rust? oO + // MethodScope.VerificationMethod(), + // ); + + // // TODO: add publish + // // (Dummy interface) + // // await identityClient.publishDidDocument(document, BigInt(12345), "dummy signer"); + // // (Rust) + // // let document = identity_client + // // .publish_did_document(unpublished) + // // .execute_with_gas(TEST_GAS_BUDGET, identity_client) + // // .await? + // // .output; + + // return [document, verification_method_fragment]; + } + \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index ca0998324a..268de2f1a6 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -11640,9 +11640,9 @@ "requires": {} }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true }, "typical": { diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs index d0be5ec0cd..a129919404 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs @@ -14,11 +14,11 @@ use serde::Deserialize; use serde::Serialize; use super::DummySigner; -use super::IdentityClient; use super::Multicontroller; use super::Proposal; use identity_iota::iota::rebased::Error; use identity_iota::iota::IotaDocument; +use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota_interaction::rpc_types::IotaObjectData; use identity_iota::iota_interaction::rpc_types::OwnedObjectRef; use identity_iota::iota_interaction::types::base_types::IotaAddress; diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs deleted file mode 100644 index 7e69fcb597..0000000000 --- a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// This file has been moved here from identity_iota_core/src/client_dummy. -// The file will be removed after the TS-Client-SDK is integrated. -// The file provides a POC for the wasm-bindgen glue code needed to -// implement the TS-Client-SDK integration. - -use std::str::FromStr; - -use identity_iota::iota::rebased::rebased_err; -use identity_iota::iota::rebased::Error; -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use identity_iota::iota::NetworkName; -use identity_iota::iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; -use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::types::base_types::ObjectID; -use identity_iota::iota_interaction::IotaClientTrait; -use identity_iota::iota_interaction::IotaTransactionBlockResponseT; -use identity_iota::iota_interaction::SignatureBcs; -use identity_iota::iota_interaction::TransactionDataBcs; - -use super::DummySigner; -use super::Identity; -use super::IdentityBuilder; -use super::IdentityClientBuilder; -use iota_interaction_ts::bindings::IotaTransactionBlockResponseAdapter; -use iota_interaction_ts::error::TsSdkError; - -// `IdentityClient` is a dummy placeholder to prepare wasm bindings for the actual one -// as long as it is not compilable to wasm - -pub struct IdentityClient { - client: T, - network_name: NetworkName, - identity_iota_package_id: IotaAddress, - sender_address: IotaAddress, - sender_public_key: Vec, -} - -// builder related functions -impl IdentityClient -where - T: IotaClientTrait, -{ - pub fn builder() -> IdentityClientBuilder { - IdentityClientBuilder::::default() - } - - pub(crate) fn from_builder(builder: IdentityClientBuilder) -> Result { - let network_name = NetworkName::try_from(builder.network_name.unwrap_or("dummy".to_string())).unwrap(); - let sender_address = builder.sender_address.unwrap_or(IotaAddress::default()); - Ok(Self { - client: builder.iota_client.unwrap(), - network_name, - identity_iota_package_id: IotaAddress::from( - builder.identity_iota_package_id.unwrap_or( - ObjectID::from_str("0x0101010101010101010101010101010101010101010101010101010101010101") - .map_err(|e| Error::InvalidArgument(e.to_string()))?, - ), - ), - sender_public_key: builder.sender_public_key.unwrap_or(vec![1u8, 2u8, 3u8, 4u8]), - sender_address, - }) - } -} - -// mock functions for wasm integration -impl IdentityClient -where - T: IotaClientTrait, -{ - pub fn sender_public_key(&self) -> Result<&[u8], Error> { - Ok(self.sender_public_key.as_ref()) - } - - pub fn sender_address(&self) -> Result { - Ok(self.sender_address.clone()) - } - - pub fn network_name(&self) -> &NetworkName { - &self.network_name - } - - pub fn create_identity(&self, _iota_document: &[u8]) -> IdentityBuilder { - IdentityBuilder::new( - &[], - ObjectID::from_str("foobar").expect("foobar can not be parsed into ObjectId"), - ) - } - - pub async fn get_identity(&self, _object_id: ObjectID) -> Result { - unimplemented!("get_identity"); - } - - pub async fn execute_dummy_transaction( - &self, - tx_data_bcs: TransactionDataBcs, - signatures: Vec, - ) -> Result< - Box>, - Error, - > { - let tx_response = self - .client - .quorum_driver_api() - .execute_transaction_block( - &tx_data_bcs, - &signatures, - Some(IotaTransactionBlockResponseOptions::new().with_effects()), - None, - ) - .await?; - Ok(tx_response) - } - - pub async fn resolve_did(&self, _did: &IotaDID) -> Result { - unimplemented!("resolve_did"); - } - - pub async fn publish_did_document( - &self, - _document: IotaDocument, - _gas_budget: u64, - _signer: &DummySigner, - ) -> Result { - unimplemented!("publish_did_document"); - } - - pub async fn publish_did_document_update( - &self, - _document: IotaDocument, - _gas_budget: u64, - _signer: &DummySigner, - ) -> Result { - unimplemented!("publish_did_document_update"); - } - - pub async fn deactivate_did_output( - &self, - _did: &IotaDID, - _gas_budget: u64, - _signer: &DummySigner, - ) -> Result<(), Error> { - unimplemented!("deactivate_did_output"); - } -} - -// test function(s) for wasm calling test -impl IdentityClient -where - T: IotaClientTrait, -{ - pub async fn get_chain_identifier(&self) -> Result { - self.client.read_api().get_chain_identifier().await.map_err(rebased_err) - } -} diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs deleted file mode 100644 index 6a3f1e3889..0000000000 --- a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// This file has been moved here from identity_iota_core/src/client_dummy. -// The file will be removed after the TS-Client-SDK is integrated. -// The file provides a POC for the wasm-bindgen glue code needed to -// implement the TS-Client-SDK integration. - -use super::IdentityClient; -use identity_iota::iota::rebased::Error; -use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::types::base_types::ObjectID; -use identity_iota::iota_interaction::IotaClientTrait; -use iota_interaction_ts::error::TsSdkError; - -pub struct IdentityClientBuilder { - pub(crate) identity_iota_package_id: Option, - pub(crate) sender_public_key: Option>, - pub(crate) sender_address: Option, - pub(crate) iota_client: Option, - pub(crate) network_name: Option, -} - -impl IdentityClientBuilder -where - T: IotaClientTrait, -{ - /// Sets the `identity_iota_package_id` value. - #[must_use] - pub fn identity_iota_package_id(mut self, value: ObjectID) -> Self { - self.identity_iota_package_id = Some(value); - self - } - - /// Sets the `sender_public_key` value. - #[must_use] - pub fn sender_public_key(mut self, value: &[u8]) -> Self { - self.sender_public_key = Some(value.into()); - self - } - - /// Sets the `sender_address` value. - #[must_use] - pub fn sender_address(mut self, value: &IotaAddress) -> Self { - self.sender_address = Some(value.clone()); - self - } - - /// Sets the `iota_client` value. - #[must_use] - pub fn iota_client(mut self, value: T) -> Self { - self.iota_client = Some(value); - self - } - - /// Sets the `network_name` value. - #[must_use] - pub fn network_name(mut self, value: &str) -> Self { - self.network_name = Some(value.to_string()); - self - } - - /// Returns a new `KinesisIdentityClientDummyBuilder` based on the `KinesisIdentityClientDummyBuilder` configuration. - pub fn build(self) -> Result, Error> { - IdentityClient::from_builder(self) - } -} - -impl Default for IdentityClientBuilder -where - T: IotaClientTrait, -{ - fn default() -> Self { - Self { - identity_iota_package_id: None, - sender_public_key: None, - sender_address: None, - iota_client: None, - network_name: None, - } - } -} diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs index 9b66b4a40b..cc7b992a9d 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs @@ -2,16 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 mod identity; -mod identity_client; -mod identity_client_builder; mod multicontroller; -pub use identity::*; -pub use identity_client::*; -pub use identity_client_builder::*; -pub use multicontroller::*; +pub(crate) use identity::*; +pub(crate) use multicontroller::*; // dummy types, have to be replaced with actual types later on -pub type DummySigner = str; -pub type Hashable = Vec; -pub type Identity = (); +pub(crate) type DummySigner = str; +pub(crate) type Hashable = Vec; diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/identity.rs index 08904ae4af..daf522c984 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/identity.rs @@ -1,7 +1,13 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::iota::rebased::migration::CreateIdentityTx; +use identity_iota::iota::rebased::migration::IdentityBuilder; +use identity_iota::iota::rebased::migration::OnChainIdentity; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; use identity_iota::iota::IotaDocument; +use iota_interaction_ts::AdapterNativeResponse; use tsify::Tsify; use wasm_bindgen::prelude::*; @@ -14,8 +20,6 @@ use crate::error::Result; use crate::iota::WasmIotaDocument; use super::client_dummy::DummySigner; -use super::client_dummy::IdentityBuilder; -use super::client_dummy::OnChainIdentity; use super::client_dummy::ProposalAction; use super::client_dummy::ProposalBuilder; use super::types::into_sdk_type; @@ -43,6 +47,11 @@ pub struct WasmOnChainIdentity(pub(crate) OnChainIdentity); #[wasm_bindgen(js_class = OnChainIdentity)] impl WasmOnChainIdentity { + #[wasm_bindgen(js_name = id)] + pub fn id(&self) -> String { + self.0.id().to_string() + } + #[wasm_bindgen(js_name = isShared)] pub fn is_shared(&self) -> bool { self.0.is_shared() @@ -55,17 +64,19 @@ impl WasmOnChainIdentity { #[wasm_bindgen(js_name = updateDidDocument)] pub fn update_did_document(self, updated_doc: WasmIotaDocument) -> Result { - let doc: IotaDocument = updated_doc - .0 - .try_read() - .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - .clone(); - Ok(WasmProposalBuilder(self.0.update_did_document::(doc))) + unimplemented!("WasmOnChainIdentity::update_did_document"); + // let doc: IotaDocument = updated_doc + // .0 + // .try_read() + // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + // .clone(); + // Ok(WasmProposalBuilder(self.0.update_did_document::(doc))) } #[wasm_bindgen(js_name = deactivateDid)] pub fn deactivate_did(self) -> WasmProposalBuilder { - WasmProposalBuilder(self.0.deactivate_did::()) + unimplemented!("WasmOnChainIdentity::deactivate_did"); + // WasmProposalBuilder(self.0.deactivate_did::()) } #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below @@ -75,16 +86,17 @@ impl WasmOnChainIdentity { last_version: Option, page_size: Option, ) -> Result { - let rs_history = self - .0 - .get_history( - &client.0, - last_version.map(|lv| into_sdk_type(lv).unwrap()).as_ref(), - page_size, - ) - .await - .map_err(wasm_error)?; - serde_wasm_bindgen::to_value(&rs_history).map_err(wasm_error) + unimplemented!("WasmOnChainIdentity::get_history"); + // let rs_history = self + // .0 + // .get_history( + // &client.0, + // last_version.map(|lv| into_sdk_type(lv).unwrap()).as_ref(), + // page_size, + // ) + // .await + // .map_err(wasm_error)?; + // serde_wasm_bindgen::to_value(&rs_history).map_err(wasm_error) } } @@ -125,10 +137,10 @@ pub struct WasmProposalBuilder(pub(crate) ProposalBuilder); #[wasm_bindgen(js_class = ProposalBuilder)] impl WasmProposalBuilder { - #[wasm_bindgen(constructor)] - pub fn new(identity: WasmOnChainIdentity, action: WasmProposalAction) -> Self { - Self(ProposalBuilder::new(identity.0, action.into())) - } + // #[wasm_bindgen(constructor)] + // pub fn new(identity: WasmOnChainIdentity, action: WasmProposalAction) -> Self { + // Self(ProposalBuilder::new(identity.0, action.into())) + // } #[wasm_bindgen(js_name = expirationEpoch)] pub fn expiration_epoch(self, exp: u64) -> Self { @@ -145,12 +157,13 @@ impl WasmProposalBuilder { } pub async fn finish(self, client: &WasmKinesisIdentityClient, signer: &DummySigner) -> Result> { - self - .0 - .finish(&client.0, signer) - .await - .map(|option| option.map(WasmProposal)) - .map_err(wasm_error) + unimplemented!("WasmProposalBuilder::finish"); + // self + // .0 + // .finish(&client.0, signer) + // .await + // .map(|option| option.map(WasmProposal)) + // .map_err(wasm_error) } } @@ -160,13 +173,11 @@ pub struct WasmIdentityBuilder(pub(crate) IdentityBuilder); #[wasm_bindgen(js_class = IdentityBuilder)] impl WasmIdentityBuilder { #[wasm_bindgen(constructor)] - pub fn new(did_doc: &[u8], _package_id: WasmObjectID) -> Self { - Self(IdentityBuilder::new( - did_doc, - "foobar" - .parse() - .expect("foobar won't be parsable into an ObjectID, TODO: Replace foobar with correct ObjectID"), - )) + pub fn new(did_doc: &WasmIotaDocument) -> WasmIdentityBuilder { + let document: IotaDocument = did_doc.0.try_read().unwrap().clone(); + WasmIdentityBuilder( + IdentityBuilder::new(document) + ) } pub fn controller(self, address: WasmIotaAddress, voting_power: u64) -> Self { @@ -184,11 +195,6 @@ impl WasmIdentityBuilder { Self(self.0.threshold(threshold)) } - #[wasm_bindgen(js_name = gasBudget)] - pub fn gas_budget(self, gas_budget: u64) -> Self { - Self(self.0.gas_budget(gas_budget)) - } - pub fn controllers(self, controllers: Vec) -> Self { Self( self.0.controllers( @@ -207,12 +213,36 @@ impl WasmIdentityBuilder { ) } - pub async fn finish(self, client: &WasmKinesisIdentityClient, signer: &DummySigner) -> Result { - self - .0 - .finish(&client.0, signer) - .await - .map(WasmOnChainIdentity) - .map_err(wasm_error) + #[wasm_bindgen(js_name = finish)] + pub fn finish(self) -> WasmCreateIdentityTx { + WasmCreateIdentityTx(self.0.finish()) } } + +#[wasm_bindgen(js_name = CreateIdentityTx)] +pub struct WasmCreateIdentityTx(pub(crate) CreateIdentityTx); + +#[wasm_bindgen(js_class = CreateIdentityTx)] +impl WasmCreateIdentityTx { + #[wasm_bindgen(js_name = execute)] + pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { + let output = self.0.execute(&client.0).await.unwrap(); + Ok(WasmTransactionOutputInternalOnChainIdentity(output)) + } +} + +#[wasm_bindgen(js_name = TransactionOutputInternalOnChainIdentity)] +pub struct WasmTransactionOutputInternalOnChainIdentity(pub(crate) TransactionOutputInternal); + +#[wasm_bindgen(js_class = TransactionOutputInternalOnChainIdentity)] +impl WasmTransactionOutputInternalOnChainIdentity { + #[wasm_bindgen(getter)] + pub fn output(&self) -> WasmOnChainIdentity { + WasmOnChainIdentity(self.0.output.clone()) + } + + #[wasm_bindgen(getter)] + pub fn response(&self) -> AdapterNativeResponse { + self.0.response.clone_native_response() + } +} \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/kinesis/mod.rs b/bindings/wasm/identity_wasm/src/kinesis/mod.rs index 6d00540c6b..f54b699e42 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/mod.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/mod.rs @@ -6,10 +6,10 @@ mod identity; mod multicontroller; mod types; mod wasm_identity_client; -mod wasm_identity_client_builder; +mod wasm_identity_client_read_only; pub use identity::*; pub use multicontroller::*; pub use types::*; pub use wasm_identity_client::*; -pub use wasm_identity_client_builder::*; +pub use wasm_identity_client_read_only::*; diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index 8ff6b98dbc..f9c67735b7 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -6,6 +6,7 @@ use std::rc::Rc; use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; +use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota_interaction::types::base_types::IotaAddress; use identity_iota::iota_interaction::SignatureBcs; @@ -18,20 +19,20 @@ use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; use identity_iota::iota::rebased::Error; -use super::client_dummy::DummySigner; -use super::client_dummy::Identity; -use super::client_dummy::IdentityClient; +use super::IdentityContainer; +use super::WasmIdentityBuilder; +use super::WasmKinesisIdentityClientReadOnly; use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; +use crate::storage::StorageSignerOwned; +use crate::storage::WasmStorageSigner; use identity_iota::iota::IotaDocument; use wasm_bindgen::prelude::*; use super::types::WasmIotaAddress; use super::types::WasmObjectID; -use super::wasm_identity_client_builder::WasmKinesisIdentityClientBuilder; -use super::WasmIdentityBuilder; #[wasm_bindgen(getter_with_clone, inspectable, js_name = IotaTransactionBlockResponseEssence)] pub struct WasmIotaTransactionBlockResponseEssence { @@ -45,80 +46,57 @@ pub struct WasmIotaTransactionBlockResponseEssence { } #[wasm_bindgen(js_name = KinesisIdentityClient)] -pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); +pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); // builder related functions #[wasm_bindgen(js_class = KinesisIdentityClient)] impl WasmKinesisIdentityClient { - #[wasm_bindgen] - pub fn builder() -> WasmKinesisIdentityClientBuilder { - // WasmKinesisIdentityClientBuilder::default() - WasmKinesisIdentityClientBuilder(IdentityClient::::builder()) + #[wasm_bindgen(js_name = create)] + pub async fn new(client: WasmKinesisIdentityClientReadOnly, signer: WasmStorageSigner) -> Result { + let inner_client = IdentityClient::new(client.0, signer.0).await?; + Ok(WasmKinesisIdentityClient(inner_client)) } - // mock functions for wasm integration - #[wasm_bindgen(js_name = senderPublicKey)] - pub fn sender_public_key(&self) -> Result, JsError> { - self.0.sender_public_key().map(|v| v.to_vec()).map_err(|e| e.into()) + pub fn sender_public_key(&self) -> Vec { + self.0.sender_public_key().to_vec() } #[wasm_bindgen(js_name = senderAddress)] - pub fn sender_address(&self) -> Result { - self.0.sender_address().map(|a| a.to_string()).map_err(|e| e.into()) + pub fn sender_address(&self) -> WasmIotaAddress { + self.0.sender_address().to_string() } - #[wasm_bindgen(js_name = networkName)] - pub fn network_name(&self) -> String { - self.0.network_name().to_string() + #[wasm_bindgen(js_name = network)] + pub fn network(&self) -> String { + self.0.network().to_string() + } + + #[wasm_bindgen(js_name = migrationRegistryId)] + pub fn migration_registry_id(&self) -> String { + self.0.migration_registry_id().to_string() } #[wasm_bindgen(js_name = createIdentity)] - pub fn create_identity(&self, iota_document: &[u8]) -> WasmIdentityBuilder { - WasmIdentityBuilder(self.0.create_identity(iota_document)) + pub fn create_identity(&self, iota_document: &WasmIotaDocument) -> WasmIdentityBuilder { + WasmIdentityBuilder::new(iota_document) } + // #[wasm_bindgen(js_name = getIdentity)] + // pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { + // let identity = self.0.get_identity(object_id.parse()?).await.map_err(|e| e.into())?; + // Ok(WasmIdentity(identity)) + // } + #[wasm_bindgen(js_name = getIdentity)] - pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { - self.0.get_identity(object_id.parse()?).await.map_err(|e| e.into()) + pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { + let inner_value = self.0.get_identity(object_id.parse()?).await.unwrap(); + Ok(IdentityContainer(inner_value)) } - #[wasm_bindgen(js_name = executeDummyTransaction)] - pub async fn execute_dummy_transaction( - &self, - tx_data_bcs_str: String, - signatures_str: Vec, - ) -> Result { - let dummy = 1; - let tx_data_bcs = BaseEncoding::decode(tx_data_bcs_str.as_str(), Base::Base64Pad)?; - let signatures = signatures_str - .iter() - .map(|s| BaseEncoding::decode(s, Base::Base64Pad)) - .collect::, _>>()?; - - let response = self - .0 - .execute_dummy_transaction(tx_data_bcs, signatures) - .await - .map_err(>::into)?; - - let effects_execution_status: Option = response - .effects_execution_status() - .map(|status| serde_wasm_bindgen::to_value(&status).unwrap().into()); - - let effects_created: Option> = response.effects_created().map(|effects| { - effects - .into_iter() - .map(|efct| serde_wasm_bindgen::to_value(&efct).unwrap().into()) - .collect() - }); - - Ok(WasmIotaTransactionBlockResponseEssence { - effects_exist: response.effects_is_some(), - effects: response.to_string(), - effects_execution_status, - effects_created, - }) + #[wasm_bindgen(js_name = packageId)] + pub fn package_id(&self) -> Result { + Ok(self.0.package_id().to_string()) } #[wasm_bindgen(js_name = resolveDid)] @@ -131,73 +109,64 @@ impl WasmKinesisIdentityClient { Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } - #[wasm_bindgen(js_name = publishDidDocument)] - pub async fn publish_did_document( - &self, - document: &WasmIotaDocument, - gas_budget: u64, - signer: &DummySigner, - ) -> Result { - let doc: IotaDocument = document - .0 - .try_read() - .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - .clone(); - let document = self - .0 - .publish_did_document(doc, gas_budget, signer) - .await - .map_err(>::into)?; - - Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) - } - - #[wasm_bindgen(js_name = publishDidDocumentUpdate)] - pub async fn publish_did_document_update( - &self, - document: &WasmIotaDocument, - gas_budget: u64, - signer: &DummySigner, - ) -> Result { - let doc: IotaDocument = document - .0 - .try_read() - .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - .clone(); - let document = self - .0 - .publish_did_document_update(doc, gas_budget, signer) - .await - .map_err(>::into)?; - - Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) - } - - #[wasm_bindgen(js_name = deactivateDidOutput)] - pub async fn deactivate_did_output( - &self, - did: &WasmIotaDID, - gas_budget: u64, - signer: &DummySigner, - ) -> Result<(), JsError> { - self - .0 - .deactivate_did_output(&did.0, gas_budget, signer) - .await - .map_err(>::into)?; - - Ok(()) - } - - // test function(s) for wasm calling test - - // make test call - #[wasm_bindgen(js_name = getChainIdentifier)] - pub async fn get_chain_identifier(&self) -> Result { - IdentityClient::get_chain_identifier(&self.0) - .await - .map_err(|err| JsError::new(&format!("could not get balance; {err}"))) - } + // not included in any e2e test anymore, so let's skip it for now + + // #[wasm_bindgen(js_name = publishDidDocument)] + // pub async fn publish_did_document( + // &self, + // document: &WasmIotaDocument, + // ) -> Result<(), JsError> { + // let doc: IotaDocument = document + // .0 + // .try_read() + // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + // .clone(); + // let publish_tx = self + // .0 + // .publish_did_document(doc); + // // .await + // // .map_err(>::into)?; + + // // Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + // Ok(()) + // } + + // #[wasm_bindgen(js_name = publishDidDocumentUpdate)] + // pub async fn publish_did_document_update( + // &self, + // document: &WasmIotaDocument, + // gas_budget: u64, + // signer: &WasmStorageSigner, + // ) -> Result { + // let doc: IotaDocument = document + // .0 + // .try_read() + // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + // .clone(); + // let document = self + // .0 + // .publish_did_document_update(doc, gas_budget) + // .await + // .map_err(>::into)?; + + // Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + // } + + // #[wasm_bindgen(js_name = deactivateDidOutput)] + // pub async fn deactivate_did_output( + // &self, + // did: &WasmIotaDID, + // gas_budget: u64, + // signer: &WasmStorageSigner, + // ) -> Result<(), JsError> { + // self + // .0 + // .deactivate_did_output(&did.0, gas_budget) + // .await + // .map_err(>::into)?; + + // Ok(()) + // } } /// TODO: consider importing function from rebased later on, if possible diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs new file mode 100644 index 0000000000..964ec0d330 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -0,0 +1,120 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::rc::Rc; +use std::str::FromStr; + +use identity_iota::iota_interaction::types::base_types::ObjectID; +use identity_iota::iota::rebased::client::IdentityClientReadOnly; +use identity_iota::iota::rebased::migration::Identity; +use iota_interaction_ts::bindings::WasmIotaClient; +use wasm_bindgen::prelude::*; + +use super::types::WasmObjectID; +use super::WasmOnChainIdentity; +use crate::iota::IotaDocumentLock; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDocument; + +#[wasm_bindgen(js_name = Identity)] +pub struct IdentityContainer(pub(crate) Identity); +#[wasm_bindgen(js_class = Identity)] +impl IdentityContainer { + /// TODO: check if we can actually do this like this w/o consuming the container on the 1st try + #[wasm_bindgen(js_name = toFullFledged)] + pub fn to_full_fledged(self) -> Option { + match self.0 { + Identity::FullFledged(v) => Some(WasmOnChainIdentity(v)), + _ => None, + } + } + + // #[wasm_bindgen(js_name = toLegacy)] + // pub fn to_legacy(self) -> Option { + // match self.0 { + // Identity::Legacy (v) => Some(v), + // _ => None, + // } + // } +} + +#[wasm_bindgen(js_name = KinesisIdentityClientReadOnly)] +pub struct WasmKinesisIdentityClientReadOnly(pub(crate) IdentityClientReadOnly); + +// builder related functions +#[wasm_bindgen(js_class = KinesisIdentityClientReadOnly)] +impl WasmKinesisIdentityClientReadOnly { + #[wasm_bindgen(js_name = create)] + pub async fn new(iota_client: WasmIotaClient) -> Result { + let inner_client = IdentityClientReadOnly::new(iota_client).await?; + Ok(WasmKinesisIdentityClientReadOnly(inner_client)) + } + + #[wasm_bindgen(js_name = createWithPkgId)] + pub async fn new_new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: String) -> Result { + let inner_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; + Ok(WasmKinesisIdentityClientReadOnly(inner_client)) + } + + #[wasm_bindgen(js_name = packageId)] + pub fn package_id(&self) -> Result { + Ok(self.0.package_id().to_string()) + } + + #[wasm_bindgen(js_name = network)] + pub fn network(&self) -> String { + self.0.network().to_string() + } + + #[wasm_bindgen(js_name = migrationRegistryId)] + pub fn migration_registry_id(&self) -> String { + self.0.migration_registry_id().to_string() + } + + // TODO: implement later on + // < + + // pub async fn get_object_by_id(&self, id: ObjectID) -> Result where T: DeserializeOwned {} + + // pub async fn get_object_ref_by_id(&self, obj: ObjectID) -> Result, Error> {} + + // pub async fn find_owned_ref_for_address

( + // &self, + // address: IotaAddress, + // tag: StructTag, + // predicate: P, + // ) -> Result, Error> {} + + // > + + #[wasm_bindgen(js_name = resolveDid)] + pub async fn resolve_did(&self, did: &WasmIotaDID) -> Result { + let document = self + .0 + .resolve_did(&did.0) + .await + .map_err(>::into)?; + Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + } + + #[wasm_bindgen(js_name = getIdentity)] + pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { + let inner_value = self.0.get_identity(object_id.parse()?).await.unwrap(); + Ok(IdentityContainer(inner_value)) + } +} + +// TODO: consider importing function from rebased later on, if possible +// pub fn convert_to_address(sender_public_key: &[u8]) -> Result { +// let public_key = Ed25519PublicKey::from_bytes(sender_public_key) +// .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; + +// Ok(IotaAddress::from(&public_key)) +// } + +// #[wasm_bindgen(js_name = convertToAddress)] +// pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { +// convert_to_address(sender_public_key) +// .map(|v| v.to_string()) +// .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) +// } diff --git a/bindings/wasm/identity_wasm/src/storage/mod.rs b/bindings/wasm/identity_wasm/src/storage/mod.rs index edd502e3da..ce8fd2e8b1 100644 --- a/bindings/wasm/identity_wasm/src/storage/mod.rs +++ b/bindings/wasm/identity_wasm/src/storage/mod.rs @@ -11,7 +11,8 @@ mod method_digest; mod signature_options; mod wasm_storage; // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] -// mod wasm_storage_signer; +mod storage_signer_owned; +mod wasm_storage_signer; pub use jpt_timeframe_revocation_ext::*; pub use jwk_gen_output::*; @@ -22,4 +23,5 @@ pub use method_digest::*; pub use signature_options::*; pub use wasm_storage::*; // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] -// pub use wasm_storage_signer::*; +pub use storage_signer_owned::*; +pub use wasm_storage_signer::*; diff --git a/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs b/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs new file mode 100644 index 0000000000..4131311719 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs @@ -0,0 +1,93 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use identity_iota::iota_interaction::IotaKeySignature; +use identity_iota::verification::jwk::Jwk; +use identity_iota::verification::jwk::JwkParams; +use identity_iota::verification::jwu; +use secret_storage::Error as SecretStorageError; +use secret_storage::SignatureScheme; +use secret_storage::Signer; +use identity_iota::storage::JwkStorage; +use identity_iota::storage::KeyId; +use identity_iota::storage::KeyIdStorage; +use identity_iota::storage::KeyStorageErrorKind; +use identity_iota::storage::Storage; + +use crate::jose::WasmJwk; + +use super::WasmStorage; + +/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`. +/// `Storage` is used to sign. +pub struct StorageSignerOwned { + key_id: KeyId, + public_key: Jwk, + // storage: WasmStorage, +} + +// impl Clone for StorageSignerOwned { +// fn clone(&self) -> Self { +// StorageSignerOwned { +// key_id: self.key_id.clone(), +// public_key: self.public_key.clone(), +// storage: self.storage, +// } +// } +// } + +impl StorageSignerOwned { + /// Creates new `StorageSignerOwned` with reference to a `Storage` instance. + pub fn new(storage: WasmStorage, key_id: KeyId, public_key: Jwk) -> Self { + Self { + key_id, + public_key, + // storage, + } + } + + /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`]. + pub fn key_id(&self) -> &KeyId { + &self.key_id + } + + /// Returns this [`Signer`]'s public key as [`Jwk`]. + pub fn public_key(&self) -> &Jwk { + &self.public_key + } + + // /// Returns a reference to this [`Signer`]'s [`Storage`]. + // pub fn storage(&self) -> &WasmStorage { + // &self.storage + // } +} + +#[async_trait(?Send)] +impl Signer for StorageSignerOwned { + type KeyId = KeyId; + fn key_id(&self) -> &KeyId { + &self.key_id + } + async fn public_key(&self) -> Result<::PublicKey, SecretStorageError> { + match self.public_key.params() { + JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) + .map_err(|e| SecretStorageError::Other(anyhow::anyhow!("could not base64 decode key {}; {e}", self.key_id()))), + _ => todo!("add support for other key types"), + } + } + async fn sign(&self, data: &[u8]) -> Result<::Signature, SecretStorageError> { + // let key_storage = self + // .storage + // .key_storage(); + // // call trait's sign impl for `WasmJwkStorage` instead of `WasmJwkStorage`s own impl + // JwkStorage::sign(&key_storage, &self.key_id, data, &self.public_key) + // .await + // .map_err(|e| match e.kind() { + // KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), + // KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), + // _ => SecretStorageError::Other(anyhow::anyhow!(e)), + // }) + todo!("pointer test"); + } +} diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index 8c8899aaf3..7966ff3422 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -1,13 +1,8 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::borrow::Borrow; - use identity_iota::iota_interaction::IotaKeySignature; use identity_iota::storage::KeyId; -use identity_iota::storage::Storage; -use identity_iota::storage::StorageSigner; -use identity_iota::verification::jwk::Jwk; use identity_iota::verification::jwk::JwkParams; use identity_iota::verification::jwu; use secret_storage::SignatureScheme; @@ -17,19 +12,19 @@ use wasm_bindgen::prelude::*; use crate::error::wasm_error; use crate::error::Result; use crate::jose::WasmJwk; -use crate::storage::WasmJwkStorage; -use crate::storage::WasmKeyIdStorage; use crate::storage::WasmStorage; +use super::StorageSignerOwned; + #[wasm_bindgen(js_name = StorageSigner)] -pub struct WasmStorageSigner(pub(crate) StorageSigner); +pub struct WasmStorageSigner(pub(crate) StorageSignerOwned); #[wasm_bindgen(js_class = StorageSigner)] impl WasmStorageSigner { #[wasm_bindgen(constructor)] - pub fn new(storage: &WasmStorage, key_id: String, public_key: WasmJwk) -> Self { - let signer = StorageSigner::new_with_shared_storage( - storage.0.clone(), + pub fn new(storage: WasmStorage, key_id: String, public_key: WasmJwk) -> Self { + let signer = StorageSignerOwned::new( + storage, KeyId::new(&key_id), public_key.0, ); diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs new file mode 100644 index 0000000000..d759a6f007 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs @@ -0,0 +1,93 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use identity_iota_interaction::IotaKeySignature; +use identity_verification::jwk::Jwk; +use identity_verification::jwk::JwkParams; +use identity_verification::jwu; +use secret_storage::Error as SecretStorageError; +use secret_storage::SignatureScheme; +use secret_storage::Signer; + +use crate::JwkStorage; +use crate::KeyId; +use crate::KeyIdStorage; +use crate::KeyStorageErrorKind; +use crate::Storage; + +/// Basically the same as the original storage signer with the exception, that this one here is owned. +/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`. +/// `Storage` is used to sign. +pub struct StorageSignerOwned { + key_id: KeyId, + public_key: Jwk, + storage: Storage, +} + +impl Clone for StorageSignerOwned { + fn clone(&self) -> Self { + StorageSignerOwned { + key_id: self.key_id.clone(), + public_key: self.public_key.clone(), + storage: self.storage, + } + } +} + +impl<'a, K, I> StorageSignerOwned { + /// Creates new `StorageSignerOwned` with reference to a `Storage` instance. + pub fn new(storage: Storage, key_id: KeyId, public_key: Jwk) -> Self { + Self { + key_id, + public_key, + storage, + } + } + + /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`]. + pub fn key_id(&self) -> &KeyId { + &self.key_id + } + + /// Returns this [`Signer`]'s public key as [`Jwk`]. + pub fn public_key(&self) -> &Jwk { + &self.public_key + } + + /// Returns a reference to this [`Signer`]'s [`Storage`]. + pub fn storage(&self) -> &Storage { + &self.storage + } +} + +#[async_trait(?Send)] +impl Signer for StorageSignerOwned +where + K: JwkStorage + Sync, + I: KeyIdStorage + Sync, +{ + type KeyId = KeyId; + fn key_id(&self) -> &KeyId { + &self.key_id + } + async fn public_key(&self) -> core::result::Result<::PublicKey, SecretStorageError> { + match self.public_key.params() { + JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) + .map_err(|e| SecretStorageError::Other(anyhow::anyhow!("could not base64 decode key {}; {e}", self.key_id()))), + _ => todo!("add support for other key types"), + } + } + async fn sign(&self, data: &[u8]) -> core::result::Result<::Signature, SecretStorageError> { + self + .storage + .key_storage() + .sign(&self.key_id, data, &self.public_key) + .await + .map_err(|e| match e.kind() { + KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), + KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), + _ => SecretStorageError::Other(anyhow::anyhow!(e)), + }) + } +} diff --git a/identity_iota_core/src/rebased/client/read_only.rs b/identity_iota_core/src/rebased/client/read_only.rs index df2ee83083..7902d0d6b6 100644 --- a/identity_iota_core/src/rebased/client/read_only.rs +++ b/identity_iota_core/src/rebased/client/read_only.rs @@ -41,6 +41,11 @@ use serde::Deserialize; #[cfg(not(target_arch = "wasm32"))] use identity_iota_interaction::IotaClient; +#[cfg(target_arch = "wasm32")] +use iota_interaction_ts::bindings::WasmIotaClient; +#[cfg(target_arch = "wasm32")] +use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; + /// An [`IotaClient`] enriched with identity-related /// functionalities. #[derive(Clone)] @@ -79,7 +84,10 @@ impl IdentityClientReadOnly { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - //TODO: Define fn new() for wasm32 platforms + // TODO: Define fn new() for wasm32 platforms + pub async fn new(iota_client: WasmIotaClient) -> Result { + Self::new_internal(IotaClientAdapter::new(iota_client)?).await + } } else { /// Attempts to create a new [`IdentityClientReadOnly`] from a given [`IotaClient`]. /// @@ -118,7 +126,13 @@ impl IdentityClientReadOnly { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - //TODO: Define fn new() for wasm32 platforms + // TODO: Define fn new() for wasm32 platforms + pub async fn new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: ObjectID) -> Result { + Self::new_with_pkg_id_internal( + IotaClientAdapter::new(iota_client)?, + iota_identity_pkg_id + ).await + } } else { /// Attempts to create a new [`IdentityClientReadOnly`] from /// the given [`IotaClient`]. diff --git a/identity_iota_core/src/rebased/iota/types/mod.rs b/identity_iota_core/src/rebased/iota/types/mod.rs index 4c68b07d71..fdf352f8ff 100644 --- a/identity_iota_core/src/rebased/iota/types/mod.rs +++ b/identity_iota_core/src/rebased/iota/types/mod.rs @@ -9,7 +9,7 @@ pub(crate) use number::*; use serde::Deserialize; use serde::Serialize; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub(crate) struct Bag { pub id: UID, #[serde(deserialize_with = "serde_aux::field_attributes::deserialize_number_from_string")] diff --git a/identity_iota_core/src/rebased/migration/identity.rs b/identity_iota_core/src/rebased/migration/identity.rs index 7935413c82..3448aaa6dd 100644 --- a/identity_iota_core/src/rebased/migration/identity.rs +++ b/identity_iota_core/src/rebased/migration/identity.rs @@ -98,7 +98,7 @@ impl Identity { } /// An on-chain entity that wraps a DID Document. -#[derive(Debug, Serialize)] +#[derive(Clone, Debug, Serialize)] pub struct OnChainIdentity { id: UID, multi_controller: Multicontroller>, diff --git a/identity_iota_core/src/rebased/migration/multicontroller.rs b/identity_iota_core/src/rebased/migration/multicontroller.rs index a1d75ad71e..958c9bd75d 100644 --- a/identity_iota_core/src/rebased/migration/multicontroller.rs +++ b/identity_iota_core/src/rebased/migration/multicontroller.rs @@ -109,7 +109,7 @@ impl From> for IotaProposal { } /// Representation of `identity.rs`'s `multicontroller::Multicontroller` Move type. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(try_from = "IotaMulticontroller::")] pub struct Multicontroller { controlled_value: T, diff --git a/identity_iota_core/src/rebased/transaction.rs b/identity_iota_core/src/rebased/transaction.rs index 73910b0f97..2ba23e2793 100644 --- a/identity_iota_core/src/rebased/transaction.rs +++ b/identity_iota_core/src/rebased/transaction.rs @@ -69,7 +69,7 @@ pub trait Transaction: Sized { } } -pub(crate) struct TransactionOutputInternal { +pub struct TransactionOutputInternal { pub output: T, pub response: IotaTransactionBlockResponseAdaptedTraitObj, } @@ -95,7 +95,7 @@ impl From> for TransactionOutput { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub(crate) trait TransactionInternal: Sized { +pub trait TransactionInternal: Sized { type Output; async fn execute_with_opt_gas_internal + Sync>( From 8c314c9aa2ef2fdeabbc2780084189a5c9346d43 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 28 Jan 2025 14:32:13 +0100 Subject: [PATCH 02/63] signer: support keys other than ed25519 --- .../src/iota_client_ts_sdk.rs | 2 - examples/utils/utils.rs | 10 ++-- .../iota_client_rust_sdk.rs | 16 +++++-- .../src/rebased/client/full_client.rs | 30 +++--------- identity_iota_interaction/Cargo.toml | 1 + .../src/iota_client_trait.rs | 9 ++-- .../src/storage/storage_signer.rs | 47 +++++++++++++++++-- 7 files changed, 68 insertions(+), 47 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index f408665516..2cb7b28fd8 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -325,8 +325,6 @@ impl IotaClientTrait for IotaClientTsSdk { async fn execute_transaction>( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 50c7ebaec1..d3f0b562e7 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -12,8 +12,6 @@ use identity_iota::storage::Storage; use identity_iota::verification::jws::JwsAlgorithm; use identity_iota::verification::MethodScope; -use identity_iota::iota::rebased::client::convert_to_address; -use identity_iota::iota::rebased::client::get_sender_public_key; use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota::rebased::client::IdentityClientReadOnly; use identity_iota::iota::rebased::client::IotaKeySignature; @@ -24,6 +22,7 @@ use identity_storage::KeyIdStorage; use identity_storage::KeyType; use identity_storage::StorageSigner; use identity_stronghold::StrongholdStorage; +use iota_sdk::types::base_types::IotaAddress; use iota_sdk::IotaClientBuilder; use iota_sdk::IOTA_LOCAL_NETWORK_URL; use iota_sdk_legacy::client::secret::stronghold::StrongholdSecretManager; @@ -94,8 +93,9 @@ where .generate(KeyType::new("Ed25519"), JwsAlgorithm::EdDSA) .await?; let public_key_jwk = generate.jwk.to_public().expect("public components should be derivable"); - let public_key_bytes = get_sender_public_key(&public_key_jwk)?; - let sender_address = convert_to_address(&public_key_bytes)?; + let signer = StorageSigner::new(storage, generate.key_id, public_key_jwk); + let sender_address = IotaAddress::from(&Signer::public_key(&signer).await?); + request_funds(&sender_address).await?; let package_id = std::env::var("IOTA_IDENTITY_PKG_ID") .map_err(|e| { @@ -105,8 +105,6 @@ where let read_only_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, package_id).await?; - let signer = StorageSigner::new(storage, generate.key_id, public_key_jwk); - let identity_client = IdentityClient::new(read_only_client, signer).await?; Ok(identity_client) diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 95c766eaac..4dca8b6f72 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -332,17 +332,23 @@ impl IotaClientTrait for IotaClientRustSdk { }) } - async fn execute_transaction + Sync>( + async fn execute_transaction( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, - ) -> Result { + ) -> Result + where + S: Signer + Sync, + { let tx = bcs::from_bytes::(tx_bcs.as_slice())?; + let public_key = signer + .public_key() + .await + .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; + let address = IotaAddress::from(&public_key); let response = self - .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) + .sdk_execute_transaction(address, public_key.as_ref(), tx, gas_budget, signer) .await?; Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) } diff --git a/identity_iota_core/src/rebased/client/full_client.rs b/identity_iota_core/src/rebased/client/full_client.rs index 910f0e99fa..80d3cf03e8 100644 --- a/identity_iota_core/src/rebased/client/full_client.rs +++ b/identity_iota_core/src/rebased/client/full_client.rs @@ -6,8 +6,6 @@ use std::ops::Deref; use crate::IotaDID; use crate::IotaDocument; use async_trait::async_trait; -use fastcrypto::ed25519::Ed25519PublicKey; -use fastcrypto::traits::ToFromBytes; use identity_iota_interaction::move_types::language_storage::StructTag; use identity_iota_interaction::rpc_types::IotaObjectData; use identity_iota_interaction::rpc_types::IotaObjectDataFilter; @@ -15,6 +13,7 @@ use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::base_types::ObjectRef; use identity_verification::jwk::Jwk; +use iota_sdk::types::crypto::PublicKey; use secret_storage::Signer; use serde::de::DeserializeOwned; use serde::Serialize; @@ -78,10 +77,8 @@ impl From for String { pub struct IdentityClient { /// [`IdentityClientReadOnly`] instance, used for read-only operations. read_client: IdentityClientReadOnly, - /// The address of the client. - address: IotaAddress, /// The public key of the client. - public_key: Vec, + public_key: PublicKey, /// The signer of the client. signer: S, } @@ -103,11 +100,9 @@ where .public_key() .await .map_err(|e| Error::InvalidKey(e.to_string()))?; - let address = convert_to_address(&public_key)?; Ok(Self { public_key, - address, read_client: client, signer, }) @@ -126,13 +121,7 @@ where // IotaClientAdapter for readonly. self .read_client - .execute_transaction( - self.sender_address(), - self.sender_public_key(), - tx_bcs, - gas_budget, - self.signer(), - ) + .execute_transaction(tx_bcs, gas_budget, self.signer()) .await .map_err(rebased_err) } @@ -140,13 +129,14 @@ where impl IdentityClient { /// Returns the bytes of the sender's public key. - pub fn sender_public_key(&self) -> &[u8] { + pub fn sender_public_key(&self) -> &PublicKey { &self.public_key } /// Returns this [`IdentityClient`]'s sender address. + #[inline(always)] pub fn sender_address(&self) -> IotaAddress { - self.address + IotaAddress::from(&self.public_key) } /// Returns a reference to this [`IdentityClient`]'s [`Signer`]. @@ -263,14 +253,6 @@ pub fn get_sender_public_key(sender_public_jwk: &Jwk) -> Result, Error> .map_err(|err| Error::InvalidKey(format!("could not decode base64 public key; {err}"))) } -/// Utility function to convert a public key's bytes into an [`IotaAddress`]. -pub fn convert_to_address(sender_public_key: &[u8]) -> Result { - let public_key = Ed25519PublicKey::from_bytes(sender_public_key) - .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; - - Ok(IotaAddress::from(&public_key)) -} - /// Publishes a new DID Document on-chain. An [`crate::rebased::migration::OnChainIdentity`] will be created to contain /// the provided document. #[derive(Debug)] diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 56f8ca991d..82ebc71f48 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -45,6 +45,7 @@ strum.workspace = true thiserror.workspace = true tracing = { version = "0.1" } uint = { version = "0.9" } +phf = { version = "0.11.2", features = ["macros"] } [package.metadata.docs.rs] # To build locally: diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs index 69d177e55c..2a8cf6211c 100644 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -17,6 +17,7 @@ use crate::rpc_types::OwnedObjectRef; use crate::types::base_types::IotaAddress; use crate::types::base_types::ObjectID; use crate::types::base_types::SequenceNumber; +use crate::types::crypto::PublicKey; use crate::types::digests::TransactionDigest; use crate::types::dynamic_field::DynamicFieldName; use crate::types::event::EventID; @@ -36,12 +37,12 @@ use std::result::Result; use std::marker::Send; pub struct IotaKeySignature { - pub public_key: Vec, + pub public_key: PublicKey, pub signature: Vec, } impl SignatureScheme for IotaKeySignature { - type PublicKey = Vec; + type PublicKey = PublicKey; type Signature = Vec; } @@ -229,8 +230,6 @@ pub trait IotaClientTrait { #[cfg(not(feature = "send-sync-transaction"))] async fn execute_transaction>( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, @@ -241,8 +240,6 @@ pub trait IotaClientTrait { #[cfg(feature = "send-sync-transaction")] async fn execute_transaction + Sync>( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index ff76fa8c67..73a18f9f8f 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -1,10 +1,15 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use anyhow::anyhow; +use anyhow::Context as _; use async_trait::async_trait; +use identity_iota_interaction::types::crypto::PublicKey; +use identity_iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; use identity_iota_interaction::IotaKeySignature; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; +use identity_verification::jwk::JwkParamsEc; use identity_verification::jwu; use secret_storage::Error as SecretStorageError; use secret_storage::SignatureScheme; @@ -71,11 +76,45 @@ where fn key_id(&self) -> &KeyId { &self.key_id } - async fn public_key(&self) -> Result<::PublicKey, SecretStorageError> { + async fn public_key(&self) -> Result { match self.public_key.params() { - JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) - .map_err(|e| SecretStorageError::Other(anyhow::anyhow!("could not base64 decode key {}; {e}", self.key_id()))), - _ => todo!("add support for other key types"), + JwkParams::Okp(params) => { + if params.crv != "Ed25519" { + return Err(SecretStorageError::Other(anyhow!( + "unsupported key type {}", + params.crv + ))); + } + + jwu::decode_b64(¶ms.x) + .context("failed to base64 decode key") + .and_then(|pk_bytes| { + PublicKey::try_from_bytes(IotaSignatureScheme::ED25519, &pk_bytes).map_err(|e| anyhow!("{e}")) + }) + .map_err(SecretStorageError::Other) + } + JwkParams::Ec(JwkParamsEc { crv, x, y, .. }) => { + let pk_bytes = { + let mut decoded_x_bytes = jwu::decode_b64(x) + .map_err(|e| SecretStorageError::Other(anyhow!("failed to decode b64 x parameter: {e}")))?; + let mut decoded_y_bytes = jwu::decode_b64(y) + .map_err(|e| SecretStorageError::Other(anyhow!("failed to decode b64 y parameter: {e}")))?; + decoded_x_bytes.append(&mut decoded_y_bytes); + + decoded_x_bytes + }; + + if self.public_key.alg() == Some("ES256") || crv == "P-256" { + PublicKey::try_from_bytes(IotaSignatureScheme::Secp256r1, &pk_bytes) + .map_err(|e| SecretStorageError::Other(anyhow!("not a secp256r1 key: {e}"))) + } else if self.public_key.alg() == Some("ES256K") || crv == "K-256" { + PublicKey::try_from_bytes(IotaSignatureScheme::Secp256k1, &pk_bytes) + .map_err(|e| SecretStorageError::Other(anyhow!("not a secp256k1 key: {e}"))) + } else { + Err(SecretStorageError::Other(anyhow!("invalid EC key"))) + } + } + _ => Err(SecretStorageError::Other(anyhow!("unsupported key"))), } } async fn sign(&self, data: &[u8]) -> Result<::Signature, SecretStorageError> { From bcfd3af9fedcf62360e2e2ec8eaf614cf46966e6 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 28 Jan 2025 15:00:40 +0100 Subject: [PATCH 03/63] remove unused dependency --- identity_iota_interaction/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 82ebc71f48..56f8ca991d 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -45,7 +45,6 @@ strum.workspace = true thiserror.workspace = true tracing = { version = "0.1" } uint = { version = "0.9" } -phf = { version = "0.11.2", features = ["macros"] } [package.metadata.docs.rs] # To build locally: From 1a588d425b1ddb93bdda4e63b0f7795196fae1be Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 11:41:24 +0100 Subject: [PATCH 04/63] fix failing doc test --- identity_iota/Cargo.toml | 1 + identity_iota/README.md | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 63b4792683..29eebb337a 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -34,6 +34,7 @@ identity_iota = { version = "=1.4.0", path = "./", features = ["memstore"] } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } rand = "0.8.5" tokio = { version = "1.29.0", features = ["full"] } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } [features] default = ["revocation-bitmap", "iota-client", "send-sync", "resolver"] diff --git a/identity_iota/README.md b/identity_iota/README.md index bf59c9d988..39777d2ad3 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -97,6 +97,7 @@ edition = "2021" [dependencies] anyhow = "1.0.62" identity_iota = { git = "https://github.com/iotaledger/identity.rs.git", tag = "v1.6.0-alpha", features = ["memstore"] } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } rand = "0.8.5" tokio = { version = "1", features = ["full"] } @@ -119,8 +120,6 @@ timeout 360 cargo build || (echo "Process timed out after 360 seconds" && exit 1 ```rust,no_run use anyhow::Context; use identity_iota::iota::IotaDocument; -use identity_iota::iota::rebased::client::convert_to_address; -use identity_iota::iota::rebased::client::get_sender_public_key; use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota::rebased::client::IdentityClientReadOnly; use identity_iota::iota::rebased::transaction::Transaction; @@ -134,6 +133,8 @@ use identity_iota::storage::StorageSigner; use identity_iota::verification::jws::JwsAlgorithm; use identity_iota::verification::MethodScope; use iota_sdk::IotaClientBuilder; +use iota_sdk::types::base_types::IotaAddress; +use secret_storage::Signer; use tokio::io::AsyncReadExt; /// Demonstrates how to create a DID Document and publish it in a new identity. @@ -152,8 +153,11 @@ async fn main() -> anyhow::Result<()> { .generate(KeyType::new("Ed25519"), JwsAlgorithm::EdDSA) .await?; let public_key_jwk = generate.jwk.to_public().expect("public components should be derivable"); - let public_key_bytes = get_sender_public_key(&public_key_jwk)?; - let sender_address = convert_to_address(&public_key_bytes)?; + let signer = StorageSigner::new(&storage, generate.key_id, public_key_jwk); + let sender_address = { + let public_key = Signer::public_key(&signer).await?; + IotaAddress::from(&public_key) + }; let package_id = std::env::var("IOTA_IDENTITY_PKG_ID") .map_err(|e| { anyhow::anyhow!("env variable IOTA_IDENTITY_PKG_ID must be set in order to run the examples").context(e) @@ -162,7 +166,6 @@ async fn main() -> anyhow::Result<()> { // Create identity client with signing capabilities. let read_only_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, package_id).await?; - let signer = StorageSigner::new(&storage, generate.key_id, public_key_jwk); let identity_client = IdentityClient::new(read_only_client, signer).await?; println!("Your wallet address is: {}", sender_address); From 8885db3fe4bb4c793fda6e4f9852682e1d489e65 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 11:43:53 +0100 Subject: [PATCH 05/63] dprint --- identity_iota/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 29eebb337a..18c695ac5a 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -33,8 +33,8 @@ anyhow = "1.0.64" identity_iota = { version = "=1.4.0", path = "./", features = ["memstore"] } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } rand = "0.8.5" -tokio = { version = "1.29.0", features = ["full"] } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } +tokio = { version = "1.29.0", features = ["full"] } [features] default = ["revocation-bitmap", "iota-client", "send-sync", "resolver"] From 6e010ca94dfe1752e8f730a455de5e9d78c34e5b Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 15:36:01 +0100 Subject: [PATCH 06/63] StorageSigner receives TxData instead of raw bytes --- bindings/wasm/iota_interaction_ts/Cargo.toml | 2 +- examples/Cargo.toml | 2 +- identity_iota/Cargo.toml | 2 +- identity_iota/README.md | 2 +- identity_iota_core/Cargo.toml | 2 +- .../iota_client_rust_sdk.rs | 52 ++++--------------- identity_iota_interaction/Cargo.toml | 2 +- .../src/iota_client_trait.rs | 7 ++- identity_storage/Cargo.toml | 12 ++++- .../src/storage/storage_signer.rs | 34 ++++++++++-- 10 files changed, 60 insertions(+), 57 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 083061b61c..f59787eb98 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -25,7 +25,7 @@ console_error_panic_hook = { version = "0.1" } fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } futures = { version = "0.3" } js-sys = { version = "0.3.61" } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.2.0" } serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = "0.6.5" serde_json.workspace = true diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 02f3c6da18..b6a296ffe6 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -15,7 +15,7 @@ iota-sdk-legacy = { package = "iota-sdk", version = "1.0", default-features = fa json-proof-token.workspace = true rand = "0.8.5" sd-jwt-payload = { version = "0.2.1", default-features = false, features = ["sha"] } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.2.0" } serde_json = { version = "1.0", default-features = false } tokio = { version = "1.29", default-features = false, features = ["rt", "macros"] } diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 18c695ac5a..365382694f 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -33,7 +33,7 @@ anyhow = "1.0.64" identity_iota = { version = "=1.4.0", path = "./", features = ["memstore"] } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } rand = "0.8.5" -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.2.0" } tokio = { version = "1.29.0", features = ["full"] } [features] diff --git a/identity_iota/README.md b/identity_iota/README.md index 39777d2ad3..60f458c2c2 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -97,7 +97,7 @@ edition = "2021" [dependencies] anyhow = "1.0.62" identity_iota = { git = "https://github.com/iotaledger/identity.rs.git", tag = "v1.6.0-alpha", features = ["memstore"] } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.2.0" } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } rand = "0.8.5" tokio = { version = "1", features = ["full"] } diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index bb1156860d..9069b04b51 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -40,7 +40,7 @@ iota-crypto = { version = "0.23", optional = true } itertools = { version = "0.13.0", optional = true } phf = { version = "0.11.2", features = ["macros"] } rand = { version = "0.8.5", optional = true } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0", default-features = false, optional = true } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.2.0", default-features = false, optional = true } serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 4dca8b6f72..3606a7c69d 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -7,8 +7,6 @@ use std::marker::Send; use std::option::Option; use std::result::Result; -use fastcrypto::hash::Blake2b256; -use fastcrypto::traits::ToFromBytes; use secret_storage::Signer; use crate::rebased::Error; @@ -35,13 +33,10 @@ use identity_iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; use identity_iota_interaction::rpc_types::ObjectChange; use identity_iota_interaction::rpc_types::ObjectsPage; use identity_iota_interaction::rpc_types::OwnedObjectRef; -use identity_iota_interaction::shared_crypto::intent::Intent; -use identity_iota_interaction::shared_crypto::intent::IntentMessage; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::base_types::ObjectID; use identity_iota_interaction::types::base_types::SequenceNumber; use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::crypto::SignatureScheme; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; use identity_iota_interaction::types::event::EventID; @@ -342,14 +337,7 @@ impl IotaClientTrait for IotaClientRustSdk { S: Signer + Sync, { let tx = bcs::from_bytes::(tx_bcs.as_slice())?; - let public_key = signer - .public_key() - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - let address = IotaAddress::from(&public_key); - let response = self - .sdk_execute_transaction(address, public_key.as_ref(), tx, gas_budget, signer) - .await?; + let response = self.sdk_execute_transaction(tx, gas_budget, signer).await?; Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) } @@ -451,18 +439,21 @@ impl IotaClientRustSdk { async fn sdk_execute_transaction>( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx: ProgrammableTransaction, gas_budget: Option, signer: &S, ) -> Result { + let public_key = signer + .public_key() + .await + .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; + let sender_address = IotaAddress::from(&public_key); let gas_budget = match gas_budget { Some(gas) => gas, None => self.sdk_default_gas_budget(sender_address, &tx).await?, }; let tx_data = self.get_transaction_data(tx, gas_budget, sender_address).await?; - let signature = Self::sign_transaction_data(signer, &tx_data, sender_public_key).await?; + let signature = Self::sign_transaction_data(signer, &tx_data).await?; // execute tx let response = self @@ -554,34 +545,11 @@ impl IotaClientRustSdk { async fn sign_transaction_data>( signer: &S, tx_data: &TransactionData, - sender_public_key: &[u8], ) -> Result { - use fastcrypto::hash::HashFunction; - - let intent = Intent::iota_transaction(); - let intent_msg = IntentMessage::new(intent, tx_data); - let mut hasher = Blake2b256::default(); - let bcs_bytes = bcs::to_bytes(&intent_msg).map_err(|err| { - Error::TransactionSigningFailed(format!("could not serialize transaction message to bcs; {err}")) - })?; - hasher.update(bcs_bytes); - let digest = hasher.finalize().digest; - - let raw_signature = signer - .sign(&digest) + signer + .sign(tx_data) .await - .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}")))?; - - let binding = [ - [SignatureScheme::ED25519.flag()].as_slice(), - &raw_signature, - sender_public_key, - ] - .concat(); - let signature_bytes: &[u8] = binding.as_slice(); - - Signature::from_bytes(signature_bytes) - .map_err(|err| Error::TransactionSigningFailed(format!("could not parse signature to IOTA signature; {err}"))) + .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}"))) } async fn get_coin_for_transaction(&self, sender_address: IotaAddress) -> Result { diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 56f8ca991d..6e916f4637 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -15,7 +15,7 @@ description = "Trait definitions and a wasm32 compatible subset of code, copied anyhow = "1.0.75" async-trait = { version = "0.1.81", default-features = false } cfg-if = "1.0.0" -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.2.0" } serde.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs index 2a8cf6211c..7cc3c3501f 100644 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -18,10 +18,12 @@ use crate::types::base_types::IotaAddress; use crate::types::base_types::ObjectID; use crate::types::base_types::SequenceNumber; use crate::types::crypto::PublicKey; +use crate::types::crypto::Signature; use crate::types::digests::TransactionDigest; use crate::types::dynamic_field::DynamicFieldName; use crate::types::event::EventID; use crate::types::quorum_driver_types::ExecuteTransactionRequestType; +use crate::types::transaction::TransactionData; use crate::OptionalSend; use crate::ProgrammableTransactionBcs; use crate::SignatureBcs; @@ -38,12 +40,13 @@ use std::marker::Send; pub struct IotaKeySignature { pub public_key: PublicKey, - pub signature: Vec, + pub signature: Signature, } impl SignatureScheme for IotaKeySignature { type PublicKey = PublicKey; - type Signature = Vec; + type Signature = Signature; + type Input = TransactionData; } //******************************************************************** diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index 29ddb2ae99..720e2fe092 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -14,7 +14,9 @@ description = "Abstractions over storage for cryptographic keys used in DID Docu [dependencies] anyhow = { version = "1.0.82" } async-trait = { version = "0.1.64", default-features = false } +bcs = { version = "0.1.4", optional = true } bls12_381_plus = { workspace = true, optional = true } +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto", optional = true } futures = { version = "0.3.27", default-features = false, features = ["async-await"] } identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } identity_credential = { version = "=1.4.0", path = "../identity_credential", default-features = false, features = ["credential", "presentation"] } @@ -27,7 +29,7 @@ iota-crypto = { version = "0.23.2", default-features = false, features = ["ed255 json-proof-token = { workspace = true, optional = true } rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"], optional = true } seahash = { version = "4.1.0", default-features = false } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0", optional = true } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.2.0", optional = true } serde.workspace = true serde_json.workspace = true thiserror.workspace = true @@ -49,7 +51,13 @@ send-sync-storage = ["identity_iota_core?/send-sync-client-ext", "secret-storage # Implements the JwkStorageDocumentExt trait for IotaDocument iota-document = ["dep:identity_iota_core"] # enables support to sign via storage -storage-signer = ["identity_iota_core?/iota-client", "dep:secret-storage", "dep:identity_iota_interaction"] +storage-signer = [ + "identity_iota_core?/iota-client", + "dep:secret-storage", + "dep:identity_iota_interaction", + "dep:fastcrypto", + "dep:bcs", +] # Enables JSON Proof Token & BBS+ related features jpt-bbs-plus = [ "identity_credential/jpt-bbs-plus", diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index 73a18f9f8f..485f91dae7 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -4,15 +4,20 @@ use anyhow::anyhow; use anyhow::Context as _; use async_trait::async_trait; +use fastcrypto::hash::Blake2b256; +use fastcrypto::traits::ToFromBytes; +use identity_iota_interaction::shared_crypto::intent::Intent; +use identity_iota_interaction::shared_crypto::intent::IntentMessage; use identity_iota_interaction::types::crypto::PublicKey; +use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; +use identity_iota_interaction::types::transaction::TransactionData; use identity_iota_interaction::IotaKeySignature; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwk::JwkParamsEc; use identity_verification::jwu; use secret_storage::Error as SecretStorageError; -use secret_storage::SignatureScheme; use secret_storage::Signer; use crate::JwkStorage; @@ -73,9 +78,11 @@ where I: KeyIdStorage + Sync, { type KeyId = KeyId; + fn key_id(&self) -> &KeyId { &self.key_id } + async fn public_key(&self) -> Result { match self.public_key.params() { JwkParams::Okp(params) => { @@ -117,16 +124,33 @@ where _ => Err(SecretStorageError::Other(anyhow!("unsupported key"))), } } - async fn sign(&self, data: &[u8]) -> Result<::Signature, SecretStorageError> { - self + async fn sign(&self, data: &TransactionData) -> Result { + use fastcrypto::hash::HashFunction; + + let intent = Intent::iota_transaction(); + let intent_msg = IntentMessage::new(intent, data); + let mut hasher = Blake2b256::default(); + let bcs_bytes = bcs::to_bytes(&intent_msg) + .map_err(|e| SecretStorageError::Other(anyhow!("could not serialize transaction message to bcs; {e}")))?; + hasher.update(bcs_bytes); + let digest = hasher.finalize().digest; + + let signature_bytes = self .storage .key_storage() - .sign(&self.key_id, data, &self.public_key) + .sign(&self.key_id, &digest, &self.public_key) .await .map_err(|e| match e.kind() { KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), _ => SecretStorageError::Other(anyhow::anyhow!(e)), - }) + })?; + + let public_key = Signer::public_key(self).await?; + + let iota_signature_bytes = [[public_key.flag()].as_slice(), &signature_bytes, public_key.as_ref()].concat(); + + Signature::from_bytes(&iota_signature_bytes) + .map_err(|e| SecretStorageError::Other(anyhow!("failed to create valid IOTA signature: {e}"))) } } From 74a3c0076d9748b1bef77068a818366663de2930 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 17:11:07 +0100 Subject: [PATCH 07/63] Use IotaSignature as Signer output, let signer handle all signing logic --- .../iota_client_rust_sdk.rs | 2 +- identity_iota_interaction/Cargo.toml | 3 + .../src/iota_client_trait.rs | 3 +- .../src/sdk_types/iota_types/crypto.rs | 365 +++++++++++++++--- .../src/storage/storage_signer.rs | 13 +- 5 files changed, 326 insertions(+), 60 deletions(-) diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 3606a7c69d..3fbcf5064d 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -547,7 +547,7 @@ impl IotaClientRustSdk { tx_data: &TransactionData, ) -> Result { signer - .sign(tx_data) + .sign(&bcs::to_bytes(tx_data)?) .await .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}"))) } diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 6e916f4637..be8a28ad6d 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -45,6 +45,9 @@ strum.workspace = true thiserror.workspace = true tracing = { version = "0.1" } uint = { version = "0.9" } +derive_more = "0.99.18" +enum_dispatch = "0.3.13" +schemars = "0.8.21" [package.metadata.docs.rs] # To build locally: diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs index 7cc3c3501f..802c08176a 100644 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -23,7 +23,6 @@ use crate::types::digests::TransactionDigest; use crate::types::dynamic_field::DynamicFieldName; use crate::types::event::EventID; use crate::types::quorum_driver_types::ExecuteTransactionRequestType; -use crate::types::transaction::TransactionData; use crate::OptionalSend; use crate::ProgrammableTransactionBcs; use crate::SignatureBcs; @@ -46,7 +45,7 @@ pub struct IotaKeySignature { impl SignatureScheme for IotaKeySignature { type PublicKey = PublicKey; type Signature = Signature; - type Input = TransactionData; + type Input = TransactionDataBcs; } //******************************************************************** diff --git a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs index 9c98cfe7f2..d34c258299 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs @@ -1,37 +1,32 @@ // Copyright (c) Mysten Labs, Inc. // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::hash::Hash; +use enum_dispatch::enum_dispatch; use strum::EnumString; +use schemars::JsonSchema; +use derive_more::{AsRef, AsMut}; use fastcrypto::{ bls12381::min_sig::{ BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, - }, - ed25519::{ + }, ed25519::{ Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, Ed25519Signature - }, - hash::{Blake2b256, HashFunction}, - traits::{VerifyingKey, Authenticator}, - encoding::{Base64}, - secp256k1::{ - Secp256k1PublicKeyAsBytes - }, - secp256r1::{ - Secp256r1PublicKeyAsBytes - } + }, encoding::{Base64, Encoding}, error::FastCryptoError, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey} }; -use fastcrypto_zkp::{zk_login_utils::Bn254FrElement}; +use fastcrypto_zkp::zk_login_utils::Bn254FrElement; -use serde::Deserialize; +use serde::{Deserialize, Deserializer, Serializer}; use serde::Serialize; use serde_with::{serde_as, Bytes}; +use crate::shared_crypto::intent::IntentMessage; + use super::{ - iota_serde::{Readable}, - error::{IotaResult, IotaError}, + base_types::IotaAddress, error::{IotaError, IotaResult}, iota_serde::Readable }; // Authority Objects @@ -104,6 +99,48 @@ impl PublicKey { } } +pub trait IotaPublicKey: VerifyingKey { + const SIGNATURE_SCHEME: SignatureScheme; +} + +// This struct exists due to the limitations of the `enum_dispatch` library. +// +pub trait IotaSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { + type Sig: Authenticator; + type PubKey: VerifyingKey + IotaPublicKey; + type KeyPair: KeypairTraits; + + const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; + const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME; + + /// Returns the deserialized signature and deserialized pubkey. + fn get_verification_inputs(&self) -> IotaResult<(Self::Sig, Self::PubKey)> { + let pk = Self::PubKey::from_bytes(self.public_key_bytes()) + .map_err(|_| IotaError::KeyConversion("Invalid public key".to_string()))?; + + // deserialize the signature + let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { + IotaError::InvalidSignature { + error: "Fail to get pubkey and sig".to_string(), + } + })?; + + Ok((signature, pk)) + } + + fn new(kp: &Self::KeyPair, message: &[u8]) -> Self { + let sig = Signer::sign(kp, message); + + let mut signature_bytes: Vec = Vec::new(); + signature_bytes + .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); + signature_bytes.extend_from_slice(sig.as_ref()); + signature_bytes.extend_from_slice(kp.public().as_ref()); + Self::from_bytes(&signature_bytes[..]) + .expect("Serialized signature did not have expected size") + } +} + /// Defines the compressed version of the public key that we pass around /// in Iota #[serde_as] @@ -124,38 +161,6 @@ pub struct AuthorityPublicKeyBytes( pub [u8; AuthorityPublicKey::LENGTH], ); -// BLS Port -// - -impl IotaPublicKey for BLS12381PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::BLS12381; -} - -// Ed25519 Iota Signature port -// - -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct Ed25519IotaSignature( - #[serde_as(as = "Readable")] - [u8; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1], -); - -// Implementation useful for simplify testing when mock signature is needed -impl Default for Ed25519IotaSignature { - fn default() -> Self { - Self([0; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1]) - } -} - -impl IotaPublicKey for Ed25519PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; -} - -pub trait IotaPublicKey: VerifyingKey { - const SIGNATURE_SCHEME: SignatureScheme; -} - #[derive(Clone, Copy, Deserialize, Serialize, Debug, EnumString, strum::Display)] #[strum(serialize_all = "lowercase")] @@ -211,4 +216,266 @@ impl SignatureScheme { _ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())), } } -} \ No newline at end of file +} + +// Enums for signature scheme signatures +#[enum_dispatch] +#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)] +pub enum Signature { + Ed25519IotaSignature, + Secp256k1IotaSignature, + Secp256r1IotaSignature, +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let bytes = self.as_ref(); + + if serializer.is_human_readable() { + let s = Base64::encode(bytes); + serializer.serialize_str(&s) + } else { + serializer.serialize_bytes(bytes) + } + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + let bytes = if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))? + } else { + let data: Vec = Vec::deserialize(deserializer)?; + data + }; + + Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string())) + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + match self { + Signature::Ed25519IotaSignature(sig) => sig.as_ref(), + Signature::Secp256k1IotaSignature(sig) => sig.as_ref(), + Signature::Secp256r1IotaSignature(sig) => sig.as_ref(), + } + } +} +impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + match self { + Signature::Ed25519IotaSignature(sig) => sig.as_mut(), + Signature::Secp256k1IotaSignature(sig) => sig.as_mut(), + Signature::Secp256r1IotaSignature(sig) => sig.as_mut(), + } + } +} + +impl ToFromBytes for Signature { + fn from_bytes(bytes: &[u8]) -> Result { + match bytes.first() { + Some(x) => { + if x == &Ed25519IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else if x == &Secp256k1IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else if x == &Secp256r1IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else { + Err(FastCryptoError::InvalidInput) + } + } + _ => Err(FastCryptoError::InvalidInput), + } + } +} + +// Ed25519 Iota Signature port +// + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Ed25519IotaSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1], +); + +// Implementation useful for simplify testing when mock signature is needed +impl Default for Ed25519IotaSignature { + fn default() -> Self { + Self([0; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1]) + } +} + +impl ToFromBytes for Ed25519IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +impl IotaSignatureInner for Ed25519IotaSignature { + type Sig = Ed25519Signature; + type PubKey = Ed25519PublicKey; + type KeyPair = Ed25519KeyPair; + const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1; +} + +impl IotaPublicKey for Ed25519PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; +} + +// Secp256k1 Iota Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Secp256k1IotaSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1], +); + +impl ToFromBytes for Secp256k1IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +impl IotaSignatureInner for Secp256k1IotaSignature { + type Sig = Secp256k1Signature; + type PubKey = Secp256k1PublicKey; + type KeyPair = Secp256k1KeyPair; + const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1; +} + +impl IotaPublicKey for Secp256k1PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1; +} + +// Secp256r1 Iota Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Secp256r1IotaSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1], +); + +impl ToFromBytes for Secp256r1IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +impl IotaSignatureInner for Secp256r1IotaSignature { + type Sig = Secp256r1Signature; + type PubKey = Secp256r1PublicKey; + type KeyPair = Secp256r1KeyPair; + const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1; +} + +impl IotaPublicKey for Secp256r1PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1; +} + +#[enum_dispatch(Signature)] +pub trait IotaSignature: Sized + ToFromBytes { + fn signature_bytes(&self) -> &[u8]; + fn public_key_bytes(&self) -> &[u8]; + fn scheme(&self) -> SignatureScheme; + + fn verify_secure( + &self, + value: &IntentMessage, + author: IotaAddress, + scheme: SignatureScheme, + ) -> IotaResult<()> + where + T: Serialize; +} + +impl IotaSignature for S { + fn signature_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_ref()[1..1 + S::Sig::LENGTH] + } + + fn public_key_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_ref()[S::Sig::LENGTH + 1..] + } + + fn scheme(&self) -> SignatureScheme { + S::PubKey::SIGNATURE_SCHEME + } + + fn verify_secure( + &self, + value: &IntentMessage, + author: IotaAddress, + scheme: SignatureScheme, + ) -> Result<(), IotaError> + where + T: Serialize, + { + let mut hasher = DefaultHash::default(); + hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); + let digest = hasher.finalize().digest; + + let (sig, pk) = &self.get_verification_inputs()?; + match scheme { + SignatureScheme::ZkLoginAuthenticator => {} // Pass this check because zk login does + // not derive address from pubkey. + _ => { + let address = IotaAddress::from(pk); + if author != address { + return Err(IotaError::IncorrectSigner { + error: format!( + "Incorrect signer, expected {:?}, got {:?}", + author, address + ), + }); + } + } + } + + pk.verify(&digest, sig) + .map_err(|e| IotaError::InvalidSignature { + error: format!("Fail to verify user sig {}", e), + }) + } +} diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index 485f91dae7..dd254c9a00 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -7,12 +7,11 @@ use async_trait::async_trait; use fastcrypto::hash::Blake2b256; use fastcrypto::traits::ToFromBytes; use identity_iota_interaction::shared_crypto::intent::Intent; -use identity_iota_interaction::shared_crypto::intent::IntentMessage; use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; -use identity_iota_interaction::types::transaction::TransactionData; use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::TransactionDataBcs; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwk::JwkParamsEc; @@ -124,15 +123,13 @@ where _ => Err(SecretStorageError::Other(anyhow!("unsupported key"))), } } - async fn sign(&self, data: &TransactionData) -> Result { + async fn sign(&self, data: &TransactionDataBcs) -> Result { use fastcrypto::hash::HashFunction; - let intent = Intent::iota_transaction(); - let intent_msg = IntentMessage::new(intent, data); + let intent_bytes = Intent::iota_transaction().to_bytes(); let mut hasher = Blake2b256::default(); - let bcs_bytes = bcs::to_bytes(&intent_msg) - .map_err(|e| SecretStorageError::Other(anyhow!("could not serialize transaction message to bcs; {e}")))?; - hasher.update(bcs_bytes); + hasher.update(intent_bytes); + hasher.update(data); let digest = hasher.finalize().digest; let signature_bytes = self From f17718c8688bf10eb11f04ecc714b79b8ce3becc Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 28 Jan 2025 19:46:04 +0100 Subject: [PATCH 08/63] Keytool signer --- identity_iota_core/Cargo.toml | 6 ++++- .../iota_client_rust_sdk.rs | 26 +++++++++++++++++-- identity_iota_core/src/rebased/mod.rs | 4 +++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 9069b04b51..8999d0eb68 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -26,6 +26,7 @@ num-traits = { version = "0.2", default-features = false, features = ["std"] } once_cell = { version = "1.18", default-features = false, features = ["std"] } prefix-hex = { version = "0.7", default-features = false } ref-cast = { version = "1.0.14", default-features = false } +jsonpath-rust = { version = "0.5.1", optional = true } serde.workspace = true serde_json.workspace = true strum.workspace = true @@ -76,7 +77,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["iota-client", "revocation-bitmap", "send-sync"] +default = ["iota-client", "revocation-bitmap", "send-sync", "keytool"] # Enables the IOTA Client related components, and dependencies. iota-client = [ "dep:async-trait", @@ -107,5 +108,8 @@ send-sync-storage = ["secret-storage?/send-sync-storage"] # Enables `Send` + `Sync` bounds for IOTA client interaction traits. send-sync-client-ext = [] +# Enables integration with IOTA's Keytool. +keytool = ["dep:jsonpath-rust"] + [lints] workspace = true diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 3fbcf5064d..3a92c3dfd5 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -36,6 +36,7 @@ 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::SequenceNumber; +use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; @@ -337,7 +338,14 @@ impl IotaClientTrait for IotaClientRustSdk { S: Signer + Sync, { let tx = bcs::from_bytes::(tx_bcs.as_slice())?; - let response = self.sdk_execute_transaction(tx, gas_budget, signer).await?; + let public_key = signer + .public_key() + .await + .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; + let address = IotaAddress::from(&public_key); + let response = self + .sdk_execute_transaction(address, &public_key, tx, gas_budget, signer) + .await?; Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) } @@ -439,6 +447,8 @@ impl IotaClientRustSdk { async fn sdk_execute_transaction>( &self, + sender_address: IotaAddress, + sender_public_key: &PublicKey, tx: ProgrammableTransaction, gas_budget: Option, signer: &S, @@ -545,11 +555,23 @@ impl IotaClientRustSdk { async fn sign_transaction_data>( signer: &S, tx_data: &TransactionData, + sender_public_key: &PublicKey, ) -> Result { signer .sign(&bcs::to_bytes(tx_data)?) .await - .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}"))) + .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}")))?; + + let binding = [ + [sender_public_key.flag()].as_slice(), + &raw_signature, + sender_public_key.as_ref(), + ] + .concat(); + let signature_bytes: &[u8] = binding.as_slice(); + + Signature::from_bytes(signature_bytes) + .map_err(|err| Error::TransactionSigningFailed(format!("could not parse signature to IOTA signature; {err}"))) } async fn get_coin_for_transaction(&self, sender_address: IotaAddress) -> Result { diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index 9dfd1f9139..15d9995783 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -16,5 +16,9 @@ pub mod transaction; /// Contains utility functions. pub mod utils; +/// Integration with IOTA's Keytool. +#[cfg(feature = "keytool")] +pub mod keytool_signer; + pub use assets::*; pub use error::*; From 1449c337eea54a360b29c95764488e42664043da Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 18:13:01 +0100 Subject: [PATCH 09/63] Keytool signer --- identity_iota_core/Cargo.toml | 2 +- .../iota_client_rust_sdk.rs | 26 +--- .../src/rebased/keytool_signer.rs | 123 ++++++++++++++++++ 3 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 identity_iota_core/src/rebased/keytool_signer.rs diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 8999d0eb68..1cc0df112d 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -21,12 +21,12 @@ identity_credential = { version = "=1.4.0", path = "../identity_credential", def identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } +jsonpath-rust = { version = "0.5.1", optional = true } num-derive = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } once_cell = { version = "1.18", default-features = false, features = ["std"] } prefix-hex = { version = "0.7", default-features = false } ref-cast = { version = "1.0.14", default-features = false } -jsonpath-rust = { version = "0.5.1", optional = true } serde.workspace = true serde_json.workspace = true strum.workspace = true diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 3a92c3dfd5..3fbcf5064d 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -36,7 +36,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::SequenceNumber; -use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; @@ -338,14 +337,7 @@ impl IotaClientTrait for IotaClientRustSdk { S: Signer + Sync, { let tx = bcs::from_bytes::(tx_bcs.as_slice())?; - let public_key = signer - .public_key() - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - let address = IotaAddress::from(&public_key); - let response = self - .sdk_execute_transaction(address, &public_key, tx, gas_budget, signer) - .await?; + let response = self.sdk_execute_transaction(tx, gas_budget, signer).await?; Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) } @@ -447,8 +439,6 @@ impl IotaClientRustSdk { async fn sdk_execute_transaction>( &self, - sender_address: IotaAddress, - sender_public_key: &PublicKey, tx: ProgrammableTransaction, gas_budget: Option, signer: &S, @@ -555,23 +545,11 @@ impl IotaClientRustSdk { async fn sign_transaction_data>( signer: &S, tx_data: &TransactionData, - sender_public_key: &PublicKey, ) -> Result { signer .sign(&bcs::to_bytes(tx_data)?) .await - .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}")))?; - - let binding = [ - [sender_public_key.flag()].as_slice(), - &raw_signature, - sender_public_key.as_ref(), - ] - .concat(); - let signature_bytes: &[u8] = binding.as_slice(); - - Signature::from_bytes(signature_bytes) - .map_err(|err| Error::TransactionSigningFailed(format!("could not parse signature to IOTA signature; {err}"))) + .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}"))) } async fn get_coin_for_transaction(&self, sender_address: IotaAddress) -> Result { diff --git a/identity_iota_core/src/rebased/keytool_signer.rs b/identity_iota_core/src/rebased/keytool_signer.rs new file mode 100644 index 0000000000..b904ee290e --- /dev/null +++ b/identity_iota_core/src/rebased/keytool_signer.rs @@ -0,0 +1,123 @@ +use anyhow::anyhow; +use anyhow::Context as _; +use async_trait::async_trait; +use fastcrypto::encoding::Base64; +use fastcrypto::encoding::Encoding; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::crypto::PublicKey; +use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::TransactionDataBcs; +use jsonpath_rust::JsonPathQuery as _; +use secret_storage::Error as SecretStorageError; +use secret_storage::Signer; +use serde_json::Value; +use tokio::process::Command; + +use super::Error; + +/// IOTA Keytool [Signer] implementation. +#[derive(Debug)] +pub struct KeytoolSigner { + address: IotaAddress, +} + +impl KeytoolSigner { + /// Returns a [KeytoolSigner] that signs data using `address`'s private key. + pub fn new(address: IotaAddress) -> Self { + Self { address } + } + + /// Returns a new [KeytoolSigner] using the address that is returned by + /// invoking the shell command `$ iota client active-address`. + pub async fn new_active_address() -> Result { + let output = run_iota_cli_command("client active-address") + .await + .map_err(Error::AnyError)?; + + let address = String::from_utf8(output) + .context("command output is not valid utf8") + .and_then(|s| s.parse().context("command output is not a valid IOTA address")) + .map_err(Error::AnyError)?; + + Ok(Self { address }) + } + + /// Returns the [IotaAddress] used by this [KeytoolSigner]. + pub fn address(&self) -> IotaAddress { + self.address + } +} + +#[cfg_attr(feature = "send-sync", async_trait)] +#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))] +impl Signer for KeytoolSigner { + type KeyId = IotaAddress; + + fn key_id(&self) -> &Self::KeyId { + &self.address + } + + async fn public_key(&self) -> Result { + let query = format!( + "$[@.iotaAddress==\"{}\"][\"publicBase64Key\",\"keyScheme\"]", + self.address + ); + let (base64_pk, key_type) = run_iota_cli_command("keytool list") + .await + .and_then(|output_bytes| { + serde_json::from_slice::(&output_bytes).context("failed to parse command output to JSON") + }) + .and_then(|json_value| { + json_value + .path(&query) + .map_err(|e| anyhow!("failed to query JSON output: {e}")) + }) + .and_then(|key_attributes| { + serde_json::from_value::<(String, String)>(key_attributes).context("failed to parse key data tuple") + }) + .map_err(SecretStorageError::Other)?; + + let decoded_pk_bytes = Base64::decode(&base64_pk) + .context("invalid base64 data") + .map_err(SecretStorageError::Other)?; + let signature_scheme = key_type + .parse() + .map_err(|_| SecretStorageError::Other(anyhow!("invalid key type {key_type}")))?; + + PublicKey::try_from_bytes(signature_scheme, &decoded_pk_bytes) + .map_err(|e| SecretStorageError::Other(anyhow!("invalid key: {e}"))) + } + + async fn sign(&self, data: &TransactionDataBcs) -> Result { + let base64_data = Base64::encode(data); + let command = format!("keytool sign --address {} --data {base64_data} --json", self.address); + + run_iota_cli_command(&command) + .await + .and_then(|output_bytes| serde_json::from_slice::(&output_bytes).context("output is not JSON")) + .and_then(|json| { + json + .path("$.iotaSignature") + .map_err(|_| anyhow!("invalid JSON output: missing iotaSignature")) + }) + .and_then(|json_sig| serde_json::from_value::(json_sig).context("invalid IOTA signature")) + .map_err(SecretStorageError::Other) + } +} + +async fn run_iota_cli_command(args: &str) -> anyhow::Result> { + let output = Command::new("iota") + .args(args.split_ascii_whitespace()) + .output() + .await + .map_err(|e| Error::AnyError(anyhow!("failed to run command: {e}")))?; + + if !output.status.success() { + let err_msg = + String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; + return Err(anyhow!("failed to run \"iota client active-address\": {err_msg}")); + } + + Ok(output.stdout) +} From 93202d92c60cc636341ef1bc92f4aa25b85854df Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 20:08:11 +0100 Subject: [PATCH 10/63] Keytool signer tests --- .../src/rebased/keytool_signer.rs | 46 +++--- identity_iota_core/tests/e2e/client.rs | 54 +++++++ identity_iota_core/tests/e2e/common.rs | 151 ++++++++++-------- 3 files changed, 162 insertions(+), 89 deletions(-) diff --git a/identity_iota_core/src/rebased/keytool_signer.rs b/identity_iota_core/src/rebased/keytool_signer.rs index b904ee290e..41eadf214e 100644 --- a/identity_iota_core/src/rebased/keytool_signer.rs +++ b/identity_iota_core/src/rebased/keytool_signer.rs @@ -1,8 +1,12 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use anyhow::anyhow; use anyhow::Context as _; use async_trait::async_trait; use fastcrypto::encoding::Base64; use fastcrypto::encoding::Encoding; +use fastcrypto::traits::EncodeDecodeBase64; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; @@ -37,7 +41,7 @@ impl KeytoolSigner { let address = String::from_utf8(output) .context("command output is not valid utf8") - .and_then(|s| s.parse().context("command output is not a valid IOTA address")) + .and_then(|s| s.trim().parse().context("command output is not a valid IOTA address")) .map_err(Error::AnyError)?; Ok(Self { address }) @@ -59,34 +63,25 @@ impl Signer for KeytoolSigner { } async fn public_key(&self) -> Result { - let query = format!( - "$[@.iotaAddress==\"{}\"][\"publicBase64Key\",\"keyScheme\"]", - self.address - ); - let (base64_pk, key_type) = run_iota_cli_command("keytool list") + let query = format!("$[?(@.iotaAddress==\"{}\")].publicBase64Key", self.address); + let Value::String(base64_key_str) = run_iota_cli_command("keytool list --json") .await - .and_then(|output_bytes| { - serde_json::from_slice::(&output_bytes).context("failed to parse command output to JSON") + .and_then(|output_bytes| String::from_utf8(output_bytes).context("command output is not valid utf8")) + .and_then(|output_str| { + serde_json::from_str::(output_str.trim()).context("failed to parse command output to JSON") }) .and_then(|json_value| { json_value .path(&query) .map_err(|e| anyhow!("failed to query JSON output: {e}")) + .and_then(|mut results| results.get_mut(0).context("key not found").map(Value::take)) }) - .and_then(|key_attributes| { - serde_json::from_value::<(String, String)>(key_attributes).context("failed to parse key data tuple") - }) - .map_err(SecretStorageError::Other)?; - - let decoded_pk_bytes = Base64::decode(&base64_pk) - .context("invalid base64 data") - .map_err(SecretStorageError::Other)?; - let signature_scheme = key_type - .parse() - .map_err(|_| SecretStorageError::Other(anyhow!("invalid key type {key_type}")))?; + .map_err(SecretStorageError::Other)? + else { + return Err(SecretStorageError::Other(anyhow!("keytool key encoding error"))); + }; - PublicKey::try_from_bytes(signature_scheme, &decoded_pk_bytes) - .map_err(|e| SecretStorageError::Other(anyhow!("invalid key: {e}"))) + PublicKey::decode_base64(&base64_key_str).map_err(|e| SecretStorageError::Other(anyhow!("{e}"))) } async fn sign(&self, data: &TransactionDataBcs) -> Result { @@ -98,10 +93,13 @@ impl Signer for KeytoolSigner { .and_then(|output_bytes| serde_json::from_slice::(&output_bytes).context("output is not JSON")) .and_then(|json| { json - .path("$.iotaSignature") - .map_err(|_| anyhow!("invalid JSON output: missing iotaSignature")) + .get("iotaSignature") + .context("invalid JSON output: missing iotaSignature")? + .as_str() + .context("not a JSON string")? + .parse() + .map_err(|e| anyhow!("invalid IOTA signature: {e}")) }) - .and_then(|json_sig| serde_json::from_value::(json_sig).context("invalid IOTA signature")) .map_err(SecretStorageError::Other) } } diff --git a/identity_iota_core/tests/e2e/client.rs b/identity_iota_core/tests/e2e/client.rs index cb40cc0370..059304525e 100644 --- a/identity_iota_core/tests/e2e/client.rs +++ b/identity_iota_core/tests/e2e/client.rs @@ -4,9 +4,11 @@ use std::ops::Deref; use crate::common::get_client as get_test_client; +use crate::common::TestClient; use identity_iota_core::rebased::migration; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::IotaDocument; +use iota_sdk::types::crypto::SignatureScheme; #[tokio::test] async fn can_create_an_identity() -> anyhow::Result<()> { @@ -44,3 +46,55 @@ async fn can_resolve_a_new_identity() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test] +async fn client_with_keytool_signer_active_address_works() -> anyhow::Result<()> { + let test_client = TestClient::new().await?; + let _identity = test_client + .create_identity(IotaDocument::new(test_client.network())) + .finish() + .execute(&test_client) + .await? + .output; + + Ok(()) +} + +#[tokio::test] +async fn client_with_new_ed25519_keytool_signer_works() -> anyhow::Result<()> { + let test_client = TestClient::new_with_key_type(SignatureScheme::ED25519).await?; + let _identity = test_client + .create_identity(IotaDocument::new(test_client.network())) + .finish() + .execute(&test_client) + .await? + .output; + + Ok(()) +} + +#[tokio::test] +async fn client_with_new_secp256r1_keytool_signer_works() -> anyhow::Result<()> { + let test_client = TestClient::new_with_key_type(SignatureScheme::Secp256r1).await?; + let _identity = test_client + .create_identity(IotaDocument::new(test_client.network())) + .finish() + .execute(&test_client) + .await? + .output; + + Ok(()) +} + +#[tokio::test] +async fn client_with_new_secp256k1_keytool_signer_works() -> anyhow::Result<()> { + let test_client = TestClient::new_with_key_type(SignatureScheme::Secp256k1).await?; + let _identity = test_client + .create_identity(IotaDocument::new(test_client.network())) + .finish() + .execute(&test_client) + .await? + .output; + + Ok(()) +} \ No newline at end of file diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index 0edb20502c..7a943c924d 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -6,6 +6,7 @@ use anyhow::anyhow; use anyhow::Context; use identity_iota_core::rebased::client::IdentityClient; use identity_iota_core::rebased::client::IdentityClientReadOnly; +use identity_iota_core::rebased::keytool_signer::KeytoolSigner; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::rebased::utils::request_funds; use identity_iota_core::IotaDID; @@ -25,13 +26,13 @@ use identity_verification::VerificationMethod; use iota_sdk::rpc_types::IotaTransactionBlockEffectsAPI; use iota_sdk::types::base_types::IotaAddress; use iota_sdk::types::base_types::ObjectID; +use iota_sdk::types::crypto::SignatureScheme; use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; use iota_sdk::types::TypeTag; use iota_sdk::types::IOTA_FRAMEWORK_PACKAGE_ID; use iota_sdk::IotaClient; use iota_sdk::IotaClientBuilder; use iota_sdk::IOTA_LOCAL_NETWORK_URL; -use jsonpath_rust::JsonPathQuery; use lazy_static::lazy_static; use move_core_types::ident_str; use move_core_types::language_storage::StructTag; @@ -76,22 +77,7 @@ lazy_static! { } pub async fn get_client() -> anyhow::Result { - let api_endpoint = std::env::var("API_ENDPOINT").unwrap_or_else(|_| IOTA_LOCAL_NETWORK_URL.to_string()); - let client = IotaClientBuilder::default().build(&api_endpoint).await?; - let package_id = PACKAGE_ID.get_or_try_init(|| init(&client)).await.copied()?; - let address = get_active_address().await?; - - request_funds(&address).await?; - - let storage = Arc::new(Storage::new(JwkMemStore::new(), KeyIdMemstore::new())); - let identity_client = IdentityClientReadOnly::new_with_pkg_id(client, package_id).await?; - - Ok(TestClient { - client: identity_client, - package_id, - address, - storage, - }) + TestClient::new().await } async fn init(iota_client: &IotaClient) -> anyhow::Result { @@ -183,28 +169,51 @@ fn get_public_key_bytes(sender_public_jwk: &Jwk) -> Result, anyhow::Erro #[derive(Clone)] pub struct TestClient { - client: IdentityClientReadOnly, - package_id: ObjectID, - #[allow(dead_code)] - address: IotaAddress, + client: Arc>, storage: Arc, } impl Deref for TestClient { - type Target = IdentityClientReadOnly; + type Target = IdentityClient; fn deref(&self) -> &Self::Target { &self.client } } impl TestClient { + pub async fn new() -> anyhow::Result { + let active_address = get_active_address().await?; + Self::new_from_address(active_address).await + } + + pub async fn new_from_address(address: IotaAddress) -> anyhow::Result { + let api_endpoint = std::env::var("API_ENDPOINT").unwrap_or_else(|_| IOTA_LOCAL_NETWORK_URL.to_string()); + let client = IotaClientBuilder::default().build(&api_endpoint).await?; + let package_id = PACKAGE_ID.get_or_try_init(|| init(&client)).await.copied()?; + + request_funds(&address).await?; + + let storage = Arc::new(Storage::new(JwkMemStore::new(), KeyIdMemstore::new())); + let identity_client = IdentityClientReadOnly::new_with_pkg_id(client, package_id).await?; + let client = IdentityClient::new(identity_client, KeytoolSigner::new_active_address().await?).await?; + + Ok(TestClient { + client: Arc::new(client), + storage, + }) + } + + pub async fn new_with_key_type(key_type: SignatureScheme) -> anyhow::Result { + let address = make_address(key_type).await?; + Self::new_from_address(address).await + } // Sets the current address to the address controller by this client. async fn switch_address(&self) -> anyhow::Result<()> { let output = Command::new("iota") .arg("client") .arg("switch") .arg("--address") - .arg(self.address.to_string()) + .arg(self.client.sender_address().to_string()) .output() .await?; @@ -219,50 +228,11 @@ impl TestClient { } pub fn package_id(&self) -> ObjectID { - self.package_id + self.client.package_id() } - pub async fn new_address(&self) -> anyhow::Result { - let output = Command::new("iota") - .arg("client") - .arg("new-address") - .arg("ed25519") - .arg("--json") - .output() - .await?; - let new_address = { - let output_str = std::str::from_utf8(&output.stdout).unwrap(); - let start_of_json = output_str - .find('{') - .ok_or(anyhow!("No json in output: {}", output_str))?; - let json_result = serde_json::from_str::(output_str[start_of_json..].trim())?; - let address_json = json_result - .path("$.address") - .map_err(|e| anyhow!("failed to parse json output: {e}"))?; - serde_json::from_value::(address_json)? - }; - - request_funds(&new_address).await?; - - let mut new_client = self.clone(); - new_client.address = new_address; - Ok(new_client) - } - - pub async fn new_user_client(&self) -> anyhow::Result> { - let generate = self - .storage - .key_storage() - .generate(KeyType::new("Ed25519"), JwsAlgorithm::EdDSA) - .await?; - let public_key_jwk = generate.jwk.to_public().expect("public components should be derivable"); - let signer = StorageSigner::new(&self.storage, generate.key_id, public_key_jwk); - - let user_client = IdentityClient::new(self.client.clone(), signer).await?; - - request_funds(&user_client.sender_address()).await?; - - Ok(user_client) + pub fn signer(&self) -> &KeytoolSigner { + self.client.signer() } pub async fn store_key_id_for_verification_method( @@ -284,6 +254,22 @@ impl TestClient { Ok(()) } + + pub async fn new_user_client(&self) -> anyhow::Result> { + let generate = self + .storage + .key_storage() + .generate(KeyType::new("Ed25519"), JwsAlgorithm::EdDSA) + .await?; + let public_key_jwk = generate.jwk.to_public().expect("public components should be derivable"); + let signer = StorageSigner::new(&self.storage, generate.key_id, public_key_jwk); + + let user_client = IdentityClient::new((*self.client).clone(), signer).await?; + + request_funds(&user_client.sender_address()).await?; + + Ok(user_client) + } } pub async fn get_test_coin(recipient: IotaAddress, client: &IdentityClient) -> anyhow::Result @@ -311,3 +297,38 @@ where .map(|obj| obj.object_id()) .context("no coins were created") } + +pub async fn make_address(key_type: SignatureScheme) -> anyhow::Result { + if !matches!( + key_type, + SignatureScheme::ED25519 | SignatureScheme::Secp256k1 | SignatureScheme::Secp256r1 + ) { + anyhow::bail!("key type {key_type} is not supported"); + } + + let output = Command::new("iota") + .arg("client") + .arg("new-address") + .arg(key_type.to_string()) + .arg("--json") + .output() + .await?; + let new_address = { + let output_str = std::str::from_utf8(&output.stdout).unwrap(); + let start_of_json = output_str + .find('{') + .ok_or(anyhow!("No json in output: {}", output_str))?; + let json_result = serde_json::from_str::(output_str[start_of_json..].trim())?; + let address_str = json_result + .get("address") + .context("no address in JSON output")? + .as_str() + .context("address is not a JSON string")?; + + address_str.parse()? + }; + + request_funds(&new_address).await?; + + Ok(new_address) +} From ff3b3aec17bbf2a40708a5c531b6b6976390266f Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 20:13:45 +0100 Subject: [PATCH 11/63] fmt --- identity_iota_core/tests/e2e/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity_iota_core/tests/e2e/client.rs b/identity_iota_core/tests/e2e/client.rs index 059304525e..00a7e23027 100644 --- a/identity_iota_core/tests/e2e/client.rs +++ b/identity_iota_core/tests/e2e/client.rs @@ -97,4 +97,4 @@ async fn client_with_new_secp256k1_keytool_signer_works() -> anyhow::Result<()> .output; Ok(()) -} \ No newline at end of file +} From 326cf7149d180283055415e4ea302669aea302ff Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 29 Jan 2025 20:18:49 +0100 Subject: [PATCH 12/63] re-export KeytoolSigner --- identity_iota_core/src/rebased/mod.rs | 5 ++++- identity_iota_core/tests/e2e/common.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index 15d9995783..c6aa5b5c4d 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -18,7 +18,10 @@ pub mod utils; /// Integration with IOTA's Keytool. #[cfg(feature = "keytool")] -pub mod keytool_signer; +mod keytool_signer; + +#[cfg(feature = "keytool")] +pub use keytool_signer::*; pub use assets::*; pub use error::*; diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index 7a943c924d..df05bccd91 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -6,9 +6,9 @@ use anyhow::anyhow; use anyhow::Context; use identity_iota_core::rebased::client::IdentityClient; use identity_iota_core::rebased::client::IdentityClientReadOnly; -use identity_iota_core::rebased::keytool_signer::KeytoolSigner; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::rebased::utils::request_funds; +use identity_iota_core::rebased::KeytoolSigner; use identity_iota_core::IotaDID; use identity_iota_interaction::IotaKeySignature; use identity_jose::jwk::Jwk; From 27f0d99300cdc90515eafe8d88e5dd6cc05b2c0e Mon Sep 17 00:00:00 2001 From: umr1352 Date: Thu, 30 Jan 2025 14:03:51 +0100 Subject: [PATCH 13/63] KeytoolSignerBuilder --- .../src/rebased/keytool_signer.rs | 147 +++++++++++++----- identity_iota_core/tests/e2e/common.rs | 3 +- 2 files changed, 106 insertions(+), 44 deletions(-) diff --git a/identity_iota_core/src/rebased/keytool_signer.rs b/identity_iota_core/src/rebased/keytool_signer.rs index 41eadf214e..12f99a7770 100644 --- a/identity_iota_core/src/rebased/keytool_signer.rs +++ b/identity_iota_core/src/rebased/keytool_signer.rs @@ -1,6 +1,9 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::path::Path; +use std::path::PathBuf; + use anyhow::anyhow; use anyhow::Context as _; use async_trait::async_trait; @@ -20,37 +23,84 @@ use tokio::process::Command; use super::Error; +/// Builder structure to ease the creation of a [KeytoolSigner]. +#[derive(Debug, Default)] +pub struct KeytoolSignerBuilder { + address: Option, + iota_bin: Option, +} + +impl KeytoolSignerBuilder { + /// Returns a new [KeytoolSignerBuilder] with default configuration: + /// - use current active address; + /// - assumes `iota` binary to be in PATH; + pub fn new() -> Self { + Self::default() + } + + /// Sets the address the signer will use. + /// Defaults to current active address if no address is provided. + pub fn with_address(mut self, address: IotaAddress) -> Self { + self.address = Some(address); + self + } + + /// Sets the path to the `iota` binary to use. + /// Assumes `iota` to be in PATH if no value is provided. + pub fn iota_bin_location(mut self, path: impl AsRef) -> Self { + let path = path.as_ref().to_path_buf(); + self.iota_bin = Some(path); + + self + } + + /// Builds a new [KeytoolSigner] using the provided configuration. + pub async fn build(self) -> anyhow::Result { + let KeytoolSignerBuilder { address, iota_bin } = self; + let iota_bin = iota_bin.unwrap_or_else(|| "iota".into()); + let address = if let Some(address) = address { + address + } else { + get_active_address(&iota_bin).await? + }; + + let public_key = get_key(&iota_bin, address).await.context("cannot find key")?; + + Ok(KeytoolSigner { + public_key, + iota_bin, + address, + }) + } +} + /// IOTA Keytool [Signer] implementation. #[derive(Debug)] pub struct KeytoolSigner { + public_key: PublicKey, + iota_bin: PathBuf, address: IotaAddress, } impl KeytoolSigner { - /// Returns a [KeytoolSigner] that signs data using `address`'s private key. - pub fn new(address: IotaAddress) -> Self { - Self { address } - } - - /// Returns a new [KeytoolSigner] using the address that is returned by - /// invoking the shell command `$ iota client active-address`. - pub async fn new_active_address() -> Result { - let output = run_iota_cli_command("client active-address") - .await - .map_err(Error::AnyError)?; - - let address = String::from_utf8(output) - .context("command output is not valid utf8") - .and_then(|s| s.trim().parse().context("command output is not a valid IOTA address")) - .map_err(Error::AnyError)?; - - Ok(Self { address }) + /// Returns a [KeytoolSignerBuilder]. + pub fn builder() -> KeytoolSignerBuilder { + KeytoolSignerBuilder::default() } /// Returns the [IotaAddress] used by this [KeytoolSigner]. pub fn address(&self) -> IotaAddress { self.address } + + /// Returns the [PublicKey] used by this [KeytoolSigner]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + async fn run_iota_cli_command(&self, args: &str) -> anyhow::Result { + run_iota_cli_command_with_bin(&self.iota_bin, args).await + } } #[cfg_attr(feature = "send-sync", async_trait)] @@ -63,34 +113,16 @@ impl Signer for KeytoolSigner { } async fn public_key(&self) -> Result { - let query = format!("$[?(@.iotaAddress==\"{}\")].publicBase64Key", self.address); - let Value::String(base64_key_str) = run_iota_cli_command("keytool list --json") - .await - .and_then(|output_bytes| String::from_utf8(output_bytes).context("command output is not valid utf8")) - .and_then(|output_str| { - serde_json::from_str::(output_str.trim()).context("failed to parse command output to JSON") - }) - .and_then(|json_value| { - json_value - .path(&query) - .map_err(|e| anyhow!("failed to query JSON output: {e}")) - .and_then(|mut results| results.get_mut(0).context("key not found").map(Value::take)) - }) - .map_err(SecretStorageError::Other)? - else { - return Err(SecretStorageError::Other(anyhow!("keytool key encoding error"))); - }; - - PublicKey::decode_base64(&base64_key_str).map_err(|e| SecretStorageError::Other(anyhow!("{e}"))) + Ok(self.public_key.clone()) } async fn sign(&self, data: &TransactionDataBcs) -> Result { let base64_data = Base64::encode(data); - let command = format!("keytool sign --address {} --data {base64_data} --json", self.address); + let command = format!("keytool sign --address {} --data {base64_data}", self.address); - run_iota_cli_command(&command) + self + .run_iota_cli_command(&command) .await - .and_then(|output_bytes| serde_json::from_slice::(&output_bytes).context("output is not JSON")) .and_then(|json| { json .get("iotaSignature") @@ -104,9 +136,12 @@ impl Signer for KeytoolSigner { } } -async fn run_iota_cli_command(args: &str) -> anyhow::Result> { - let output = Command::new("iota") +async fn run_iota_cli_command_with_bin(iota_bin: impl AsRef, args: &str) -> anyhow::Result { + let iota_bin = iota_bin.as_ref(); + + let output = Command::new(iota_bin) .args(args.split_ascii_whitespace()) + .arg("--json") .output() .await .map_err(|e| Error::AnyError(anyhow!("failed to run command: {e}")))?; @@ -117,5 +152,31 @@ async fn run_iota_cli_command(args: &str) -> anyhow::Result> { return Err(anyhow!("failed to run \"iota client active-address\": {err_msg}")); } - Ok(output.stdout) + serde_json::from_slice(&output.stdout).context("invalid JSON object in command output") +} + +async fn get_active_address(iota_bin: impl AsRef) -> anyhow::Result { + run_iota_cli_command_with_bin(iota_bin, "client active-address") + .await + .and_then(|value| serde_json::from_value(value).context("failed to parse IotaAddress from output")) +} + +async fn get_key(iota_bin: impl AsRef, address: IotaAddress) -> anyhow::Result { + let query = format!("$[?(@.iotaAddress==\"{}\")].publicBase64Key", address); + + let base64_pk_json = run_iota_cli_command_with_bin(iota_bin, "keytool list") + .await + .and_then(|json_value| { + json_value + .path(&query) + .map_err(|e| anyhow!("failed to query JSON output: {e}"))? + .get_mut(0) + .context("key not found") + .map(Value::take) + })?; + let base64_pk = base64_pk_json + .as_str() + .context("invalid JSON public key representation")?; + + PublicKey::decode_base64(base64_pk).context("failed to decode base64 public key") } diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index df05bccd91..245b3d67cd 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -195,7 +195,8 @@ impl TestClient { let storage = Arc::new(Storage::new(JwkMemStore::new(), KeyIdMemstore::new())); let identity_client = IdentityClientReadOnly::new_with_pkg_id(client, package_id).await?; - let client = IdentityClient::new(identity_client, KeytoolSigner::new_active_address().await?).await?; + let signer = KeytoolSigner::builder().build().await?; + let client = IdentityClient::new(identity_client, signer).await?; Ok(TestClient { client: Arc::new(client), From d76b712653073b5bdcdac47688536de6f84934ee Mon Sep 17 00:00:00 2001 From: umr1352 Date: Mon, 3 Feb 2025 16:05:50 +0100 Subject: [PATCH 14/63] WASM KeytoolSigner --- bindings/wasm/iota_interaction_ts/Cargo.toml | 4 + .../wasm/iota_interaction_ts/package.json | 3 +- .../src/bindings/keytool_signer.rs | 89 +++++++++++++++++++ .../iota_interaction_ts/src/bindings/mod.rs | 4 + identity_iota_core/Cargo.toml | 7 +- identity_iota_core/src/rebased/mod.rs | 11 +-- identity_iota_interaction/Cargo.toml | 7 +- .../src}/keytool_signer.rs | 51 ++++++----- identity_iota_interaction/src/lib.rs | 4 + 9 files changed, 143 insertions(+), 37 deletions(-) create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs rename {identity_iota_core/src/rebased => identity_iota_interaction/src}/keytool_signer.rs (79%) diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index f59787eb98..046fa78d28 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -53,3 +53,7 @@ instant = { version = "0.1", default-features = false, features = ["wasm-bindgen # can be removed as soon as fix has been added to clippy # see https://github.com/rust-lang/rust-clippy/issues/12377 empty_docs = "allow" + +[features] +default = [] +keytool-signer = ["identity_iota_interaction/keytool-signer"] \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index 7e788f6757..c8d70f8fcf 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -11,9 +11,10 @@ }, "scripts": { "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", + "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool-signer", "bundle:nodejs": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", "bundle:web": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", - "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/iota_interaction_ts_bg.wasm -o node/iota_interaction_ts_bg.wasm", + "build:nodejs": "npm run build:src:node && npm run bundle:nodejs && wasm-opt -O node/iota_interaction_ts_bg.wasm -o node/iota_interaction_ts_bg.wasm", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/iota_interaction_ts_bg.wasm -o web/iota_interaction_ts_bg.wasm", "build:docs": "typedoc && npm run fix_docs", "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist iota_interaction_ts resolve", diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs new file mode 100644 index 0000000000..5b516eca09 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -0,0 +1,89 @@ +use std::path::PathBuf; +use std::str::FromStr; + +use crate::error::Result; +use anyhow::Context; +use async_trait::async_trait; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::crypto::PublicKey; +use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::KeytoolSigner; +use identity_iota_interaction::KeytoolSignerBuilder; +use identity_iota_interaction::TransactionDataBcs; +use secret_storage::Error as SecretStorageError; +use secret_storage::Signer; +use serde_json::Value; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen(typescript_custom_section)] +const NODE_EXEC_HANDLER: &str = r#" +const utils = require("util"); +const exec = utils.promisify(require("child_process").exec); + +async function exec_handler(cmd: string): Promise { + try { + const { stdout } = await exec(cmd); + return JSON.parse(stdout) as unknown; + } catch(e) { + console.error(e); + } +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(catch)] + async fn exec_handler(cmd: &str) -> Result; +} + +#[wasm_bindgen(js_name = KeytoolSigner)] +pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); + +#[wasm_bindgen(js_class = KeytoolSigner)] +impl WasmKeytoolSigner { + #[wasm_bindgen(constructor)] + pub async fn new(address: Option<&str>, iota_bin_location: Option<&str>) -> Result { + let address = address.map(IotaAddress::from_str).transpose()?; + + let builder = address + .map(|address| KeytoolSignerBuilder::new().with_address(address)) + .unwrap_or_default(); + let builder = if let Some(iota_bin_location) = iota_bin_locatin { + builder.iota_bin_location(iota_bin_location) + } else { + builder + }; + + builder.build().await.map(Self) + } + + #[wasm_bindgen] + pub fn address(&self) -> String { + self.0.address().to_string() + } +} + +#[async_trait(?Send)] +impl Signer for WasmKeytoolSigner { + type KeyId = IotaAddress; + + fn key_id(&self) -> &Self::KeyId { + self.0.key_id() + } + + async fn public_key(&self) -> Result { + self.0.public_key().await + } + + async fn sign(&self, data: &TransactionDataBcs) -> Result { + self.0.sign(data).await + } +} + +// This is used in KeytoolSigner implementation to issue CLI commands. +#[no_mangle] +async fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { + let output = exec_handler(cmd).await?; + serde_wasm_bindgen::from_value(output).context("failed to deserialize JSON object from command output") +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs index c04f573559..73eea9533e 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs @@ -4,7 +4,11 @@ mod types; mod wasm_iota_client; mod wasm_types; +#[cfg(feature = "keytool-signer")] +mod keytool_signer; pub use types::*; pub use wasm_iota_client::*; pub use wasm_types::*; +#[cfg(feature = "keytool-signer")] +pub use keytool_signer::*; diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 1cc0df112d..3ed5c14a78 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -66,7 +66,6 @@ proptest = { version = "1.0.0", default-features = false, features = ["std"] } # for feature iota-client tests identity_iota_core = { path = ".", features = ["iota-client"] } # enable for e2e tests identity_storage = { path = "../identity_storage", features = ["send-sync-storage", "storage-signer"] } -jsonpath-rust = "0.5.1" lazy_static = "1.5.0" serial_test = "3.1.1" @@ -77,7 +76,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["iota-client", "revocation-bitmap", "send-sync", "keytool"] +default = ["iota-client", "revocation-bitmap", "send-sync", "keytool-signer"] # Enables the IOTA Client related components, and dependencies. iota-client = [ "dep:async-trait", @@ -107,9 +106,7 @@ send-sync = ["send-sync-storage", "send-sync-client-ext"] send-sync-storage = ["secret-storage?/send-sync-storage"] # Enables `Send` + `Sync` bounds for IOTA client interaction traits. send-sync-client-ext = [] - -# Enables integration with IOTA's Keytool. -keytool = ["dep:jsonpath-rust"] +keytool-signer = ["identity_iota_interaction/keytool-signer", "iota-client"] [lints] workspace = true diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index c6aa5b5c4d..1abdc7cc74 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -16,12 +16,9 @@ pub mod transaction; /// Contains utility functions. pub mod utils; -/// Integration with IOTA's Keytool. -#[cfg(feature = "keytool")] -mod keytool_signer; - -#[cfg(feature = "keytool")] -pub use keytool_signer::*; - pub use assets::*; pub use error::*; + +/// Integration with IOTA's Keytool. +#[cfg(feature = "keytool-signer")] +pub use identity_iota_interaction::keytool_signer::*; diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index be8a28ad6d..7c1bb2e7a3 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -15,19 +15,22 @@ description = "Trait definitions and a wasm32 compatible subset of code, copied anyhow = "1.0.75" async-trait = { version = "0.1.81", default-features = false } cfg-if = "1.0.0" +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } +jsonpath-rust = { version = "0.5.1", optional = true } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.2.0" } serde.workspace = true +serde_json.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.8.1-rc" } +tokio = { version = "*", optional = true } shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.8.1-rc" } [target.'cfg(target_arch = "wasm32")'.dependencies] bcs = "0.1.4" eyre = { version = "0.6" } -fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto-zkp" } getrandom = { version = "0.2", default-features = false, features = ["js"] } hex = { version = "0.4" } @@ -38,7 +41,6 @@ num-bigint = { version = "0.4" } primitive-types = { version = "0.12", features = ["impl-serde"] } rand = "0.8.5" ref-cast = { version = "1.0" } -serde_json.workspace = true serde_repr = { version = "0.1" } serde_with = { version = "3.8", features = ["hex"] } strum.workspace = true @@ -58,6 +60,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["send-sync-transaction", "secret-storage/default"] send-sync-transaction = ["secret-storage/send-sync-storage"] +keytool-signer = ["dep:tokio", "dep:jsonpath-rust"] [lints] workspace = true diff --git a/identity_iota_core/src/rebased/keytool_signer.rs b/identity_iota_interaction/src/keytool_signer.rs similarity index 79% rename from identity_iota_core/src/rebased/keytool_signer.rs rename to identity_iota_interaction/src/keytool_signer.rs index 12f99a7770..22ae3ae513 100644 --- a/identity_iota_core/src/rebased/keytool_signer.rs +++ b/identity_iota_interaction/src/keytool_signer.rs @@ -4,24 +4,21 @@ use std::path::Path; use std::path::PathBuf; +use crate::types::base_types::IotaAddress; +use crate::types::crypto::PublicKey; +use crate::types::crypto::Signature; +use crate::IotaKeySignature; +use crate::TransactionDataBcs; use anyhow::anyhow; use anyhow::Context as _; use async_trait::async_trait; use fastcrypto::encoding::Base64; use fastcrypto::encoding::Encoding; use fastcrypto::traits::EncodeDecodeBase64; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::crypto::PublicKey; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::TransactionDataBcs; use jsonpath_rust::JsonPathQuery as _; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use serde_json::Value; -use tokio::process::Command; - -use super::Error; /// Builder structure to ease the creation of a [KeytoolSigner]. #[derive(Debug, Default)] @@ -103,8 +100,8 @@ impl KeytoolSigner { } } -#[cfg_attr(feature = "send-sync", async_trait)] -#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] impl Signer for KeytoolSigner { type KeyId = IotaAddress; @@ -139,20 +136,30 @@ impl Signer for KeytoolSigner { async fn run_iota_cli_command_with_bin(iota_bin: impl AsRef, args: &str) -> anyhow::Result { let iota_bin = iota_bin.as_ref(); - let output = Command::new(iota_bin) - .args(args.split_ascii_whitespace()) - .arg("--json") - .output() - .await - .map_err(|e| Error::AnyError(anyhow!("failed to run command: {e}")))?; + cfg_if::cfg_if! { + if #[cfg(not(target_arch = "wasm32"))] { + let output = tokio::process::Command::new(iota_bin) + .args(args.split_ascii_whitespace()) + .arg("--json") + .output() + .await + .map_err(|e| anyhow!("failed to run command: {e}"))?; - if !output.status.success() { - let err_msg = - String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; - return Err(anyhow!("failed to run \"iota client active-address\": {err_msg}")); - } + if !output.status.success() { + let err_msg = + String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; + return Err(anyhow!("failed to run \"iota client active-address\": {err_msg}")); + } - serde_json::from_slice(&output.stdout).context("invalid JSON object in command output") + serde_json::from_slice(&output.stdout).context("invalid JSON object in command output") + } else { + extern "Rust" { + async fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; + } + let cmd = format!("{iota_bin} {args} --json"); + unsafe { __wasm_exec_iota_cmd(&cmd).await } + } + } } async fn get_active_address(iota_bin: impl AsRef) -> anyhow::Result { diff --git a/identity_iota_interaction/src/lib.rs b/identity_iota_interaction/src/lib.rs index ef235d1aa9..e4e0b58319 100644 --- a/identity_iota_interaction/src/lib.rs +++ b/identity_iota_interaction/src/lib.rs @@ -7,12 +7,16 @@ mod iota_client_trait; mod iota_verifiable_credential; +#[cfg(feature = "keytool-signer")] +pub mod keytool_signer; mod move_call_traits; mod move_type; mod transaction_builder_trait; pub use iota_client_trait::*; pub use iota_verifiable_credential::*; +#[cfg(feature = "keytool-signer")] +pub use keytool_signer::*; pub use move_call_traits::*; pub use move_type::*; pub use transaction_builder_trait::*; From 05d6491794bbfc5939498c322227c86b6ef77cbf Mon Sep 17 00:00:00 2001 From: umr1352 Date: Mon, 3 Feb 2025 16:12:00 +0100 Subject: [PATCH 15/63] fmt --- bindings/wasm/iota_interaction_ts/Cargo.toml | 2 +- bindings/wasm/iota_interaction_ts/src/bindings/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 046fa78d28..434123b02d 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -56,4 +56,4 @@ empty_docs = "allow" [features] default = [] -keytool-signer = ["identity_iota_interaction/keytool-signer"] \ No newline at end of file +keytool-signer = ["identity_iota_interaction/keytool-signer"] diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs index 73eea9533e..750feed897 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs @@ -1,14 +1,14 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "keytool-signer")] +mod keytool_signer; mod types; mod wasm_iota_client; mod wasm_types; -#[cfg(feature = "keytool-signer")] -mod keytool_signer; +#[cfg(feature = "keytool-signer")] +pub use keytool_signer::*; pub use types::*; pub use wasm_iota_client::*; pub use wasm_types::*; -#[cfg(feature = "keytool-signer")] -pub use keytool_signer::*; From 44034739f0cc4f153f70f2b46d12454dcf554d04 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Mon, 3 Feb 2025 17:50:52 +0100 Subject: [PATCH 16/63] buildable wasm --- bindings/wasm/iota_interaction_ts/Cargo.toml | 4 +- .../src/bindings/keytool_signer.rs | 30 ++++++---- .../wasm/iota_interaction_ts/src/error.rs | 9 +++ .../src/keytool_signer.rs | 5 +- .../src/sdk_types/iota_types/crypto.rs | 57 ++++++++++++++++++- 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 434123b02d..2a7aa70f0d 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -31,7 +31,7 @@ serde-wasm-bindgen = "0.6.5" serde_json.workspace = true thiserror.workspace = true tsify = "0.4.5" -wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } zkryptium = "0.2.2" @@ -55,5 +55,5 @@ instant = { version = "0.1", default-features = false, features = ["wasm-bindgen empty_docs = "allow" [features] -default = [] +default = ["keytool-signer"] keytool-signer = ["identity_iota_interaction/keytool-signer"] diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index 5b516eca09..9964244e79 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -2,11 +2,12 @@ use std::path::PathBuf; use std::str::FromStr; use crate::error::Result; -use anyhow::Context; +use crate::error::WasmResult; use async_trait::async_trait; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::KeytoolSigner; use identity_iota_interaction::KeytoolSignerBuilder; use identity_iota_interaction::TransactionDataBcs; @@ -43,19 +44,23 @@ pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); #[wasm_bindgen(js_class = KeytoolSigner)] impl WasmKeytoolSigner { #[wasm_bindgen(constructor)] - pub async fn new(address: Option<&str>, iota_bin_location: Option<&str>) -> Result { - let address = address.map(IotaAddress::from_str).transpose()?; + pub async fn new(address: Option, iota_bin_location: Option) -> Result { + let address = address + .as_deref() + .map(IotaAddress::from_str) + .transpose() + .wasm_result()?; let builder = address .map(|address| KeytoolSignerBuilder::new().with_address(address)) .unwrap_or_default(); - let builder = if let Some(iota_bin_location) = iota_bin_locatin { + let builder = if let Some(iota_bin_location) = iota_bin_location { builder.iota_bin_location(iota_bin_location) } else { builder }; - builder.build().await.map(Self) + Ok(WasmKeytoolSigner(builder.build().await.wasm_result()?)) } #[wasm_bindgen] @@ -65,25 +70,26 @@ impl WasmKeytoolSigner { } #[async_trait(?Send)] -impl Signer for WasmKeytoolSigner { +impl Signer for WasmKeytoolSigner { type KeyId = IotaAddress; fn key_id(&self) -> &Self::KeyId { self.0.key_id() } - async fn public_key(&self) -> Result { - self.0.public_key().await + async fn public_key(&self) -> std::result::Result { + Ok(self.0.public_key().clone()) } - async fn sign(&self, data: &TransactionDataBcs) -> Result { + async fn sign(&self, data: &TransactionDataBcs) -> std::result::Result { self.0.sign(data).await } } // This is used in KeytoolSigner implementation to issue CLI commands. #[no_mangle] -async fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { - let output = exec_handler(cmd).await?; - serde_wasm_bindgen::from_value(output).context("failed to deserialize JSON object from command output") +fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { + let output = futures::executor::block_on(exec_handler(cmd)).map_err(|_| anyhow::anyhow!("exec failed"))?; + serde_wasm_bindgen::from_value(output) + .map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) } diff --git a/bindings/wasm/iota_interaction_ts/src/error.rs b/bindings/wasm/iota_interaction_ts/src/error.rs index dc30256a1f..63a2101c65 100644 --- a/bindings/wasm/iota_interaction_ts/src/error.rs +++ b/bindings/wasm/iota_interaction_ts/src/error.rs @@ -166,6 +166,15 @@ impl From for WasmError<'_> { } } +impl From for WasmError<'_> { + fn from(error: anyhow::Error) -> Self { + Self { + name: Cow::Borrowed("anyhow::Error"), + message: Cow::Owned(error.to_string()), + } + } +} + /// Consumes the struct and returns a Result<_, String>, leaving an `Ok` value untouched. pub fn stringify_js_error(result: Result) -> StdResult { result.map_err(|js_value| { diff --git a/identity_iota_interaction/src/keytool_signer.rs b/identity_iota_interaction/src/keytool_signer.rs index 22ae3ae513..99f53006f8 100644 --- a/identity_iota_interaction/src/keytool_signer.rs +++ b/identity_iota_interaction/src/keytool_signer.rs @@ -154,10 +154,11 @@ async fn run_iota_cli_command_with_bin(iota_bin: impl AsRef, args: &str) - serde_json::from_slice(&output.stdout).context("invalid JSON object in command output") } else { extern "Rust" { - async fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; + fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; } + let iota_bin = iota_bin.to_str().context("invalid IOTA bin path")?; let cmd = format!("{iota_bin} {args} --json"); - unsafe { __wasm_exec_iota_cmd(&cmd).await } + unsafe { __wasm_exec_iota_cmd(&cmd) } } } } diff --git a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs index d34c258299..623c36176c 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs @@ -2,6 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use std::hash::Hash; +use std::str::FromStr; use enum_dispatch::enum_dispatch; use strum::EnumString; @@ -9,13 +10,14 @@ use schemars::JsonSchema; use derive_more::{AsRef, AsMut}; use fastcrypto::{ + error::FastCryptoResult, bls12381::min_sig::{ BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, }, ed25519::{ Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, Ed25519Signature - }, encoding::{Base64, Encoding}, error::FastCryptoError, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey} + }, encoding::{Base64, Encoding}, error::FastCryptoError, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey, EncodeDecodeBase64} }; use fastcrypto_zkp::zk_login_utils::Bn254FrElement; @@ -87,7 +89,53 @@ impl AsRef<[u8]> for PublicKey { } } +impl EncodeDecodeBase64 for PublicKey { + fn encode_base64(&self) -> String { + let mut bytes: Vec = Vec::new(); + bytes.extend_from_slice(&[self.flag()]); + bytes.extend_from_slice(self.as_ref()); + Base64::encode(&bytes[..]) + } + + fn decode_base64(value: &str) -> FastCryptoResult { + let bytes = Base64::decode(value)?; + match bytes.first() { + Some(x) => { + if x == &SignatureScheme::ED25519.flag() { + let pk: Ed25519PublicKey = + Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Ed25519((&pk).into())) + } else if x == &SignatureScheme::Secp256k1.flag() { + let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Secp256k1((&pk).into())) + } else if x == &SignatureScheme::Secp256r1.flag() { + let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Secp256r1((&pk).into())) + } else if x == &SignatureScheme::PasskeyAuthenticator.flag() { + let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Passkey((&pk).into())) + } else { + Err(FastCryptoError::InvalidInput) + } + } + _ => Err(FastCryptoError::InvalidInput), + } + } +} + impl PublicKey { + pub fn flag(&self) -> u8 { + self.scheme().flag() + } + pub fn scheme(&self) -> SignatureScheme { match self { PublicKey::Ed25519(_) => SignatureScheme::ED25519, // Equals Ed25519IotaSignature::SCHEME @@ -300,6 +348,13 @@ impl ToFromBytes for Signature { } } +impl FromStr for Signature { + type Err = eyre::Report; + fn from_str(s: &str) -> Result { + Self::decode_base64(s).map_err(|e| eyre::eyre!("Fail to decode base64 {}", e.to_string())) + } +} + // Ed25519 Iota Signature port // From ee335dc383cf78123b558a48dd0b91b7ba7d17d5 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 4 Feb 2025 17:01:42 +0100 Subject: [PATCH 17/63] Make WasmKeytoolSigner work --- .../src/bindings/keytool_signer.rs | 34 ++++++++----------- bindings/wasm/iota_interaction_ts/src/lib.rs | 2 ++ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index 9964244e79..af2a8ba497 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -17,25 +17,18 @@ use serde_json::Value; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; -#[wasm_bindgen(typescript_custom_section)] -const NODE_EXEC_HANDLER: &str = r#" -const utils = require("util"); -const exec = utils.promisify(require("child_process").exec); - -async function exec_handler(cmd: string): Promise { - try { - const { stdout } = await exec(cmd); - return JSON.parse(stdout) as unknown; - } catch(e) { - console.error(e); - } +#[wasm_bindgen(module = buffer)] +extern "C" { + #[wasm_bindgen(typescript_type = Buffer)] + type NodeBuffer; + #[wasm_bindgen(method, js_name = toString)] + fn to_string(this: &NodeBuffer) -> String; } -"#; -#[wasm_bindgen] +#[wasm_bindgen(module = child_process)] extern "C" { - #[wasm_bindgen(catch)] - async fn exec_handler(cmd: &str) -> Result; + #[wasm_bindgen(js_name = execSync, catch)] + fn exec_cli_cmd(cmd: &str) -> Result; } #[wasm_bindgen(js_name = KeytoolSigner)] @@ -88,8 +81,9 @@ impl Signer for WasmKeytoolSigner { // This is used in KeytoolSigner implementation to issue CLI commands. #[no_mangle] -fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { - let output = futures::executor::block_on(exec_handler(cmd)).map_err(|_| anyhow::anyhow!("exec failed"))?; - serde_wasm_bindgen::from_value(output) - .map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) +pub extern "Rust" fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { + let output = exec_cli_cmd(cmd) + .map_err(|e| anyhow::anyhow!("exec failed: {e:?}"))? + .to_string(); + serde_json::from_str(&output).map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) } diff --git a/bindings/wasm/iota_interaction_ts/src/lib.rs b/bindings/wasm/iota_interaction_ts/src/lib.rs index 9a7c78a01b..5bb979f78c 100644 --- a/bindings/wasm/iota_interaction_ts/src/lib.rs +++ b/bindings/wasm/iota_interaction_ts/src/lib.rs @@ -50,6 +50,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; + #[cfg(feature = "keytool-signer")] + pub use bindings::WasmKeytoolSigner; #[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper; } From e717391c6cd3b318eac7b78ec2ec036e8d96cdc9 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 5 Feb 2025 11:56:54 +0100 Subject: [PATCH 18/63] ensures WASM keytool signer implements Signer TS interface --- .../src/bindings/keytool_signer.rs | 22 +++++++++++++++++++ identity_iota_interaction/Cargo.toml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index af2a8ba497..5694af0300 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -60,6 +60,28 @@ impl WasmKeytoolSigner { pub fn address(&self) -> String { self.0.address().to_string() } + + // These method definition are needed to make sure `KeytoolSigner` + // implements `Signer` interface. + + #[wasm_bindgen(js_name = keyId)] + pub fn key_id(&self) -> String { + self.address() + } + + #[wasm_bindgen(js_name = publicKey)] + pub async fn public_key(&self) -> Vec { + self.0.public_key().as_ref().to_owned() + } + + #[wasm_bindgen] + pub async fn sign(&self, data: Vec) -> Result> { + Signer::sign(self, &data) + .await + .map_err(|e| anyhow::Error::from(e)) + .map(|signature| signature.as_ref().to_owned()) + .wasm_result() + } } #[async_trait(?Send)] diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 7c1bb2e7a3..f2d30a6473 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -24,7 +24,7 @@ serde_json.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.8.1-rc" } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.8.1-rc" } -tokio = { version = "*", optional = true } +tokio = { version = "1", optional = true, default-features = false, features = ["process"] } shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.8.1-rc" } From bf91fa65e3851ac4cfa6be469fb18fe96760db54 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Wed, 5 Feb 2025 13:02:10 +0100 Subject: [PATCH 19/63] add signing and exection for exampe 0 (create did) --- Cargo.toml | 2 +- .../examples/src/0_basic/-1_test_api_call.ts | 425 +++++++----------- .../examples/src/0_basic/0_create_did.ts | 46 +- .../identity_wasm/examples/src/utils_alpha.ts | 141 ++---- bindings/wasm/identity_wasm/package-lock.json | 59 ++- bindings/wasm/identity_wasm/package.json | 3 +- .../src/kinesis/wasm_identity_client.rs | 92 ++-- .../kinesis/wasm_identity_client_read_only.rs | 20 +- .../wasm/identity_wasm/src/storage/mod.rs | 8 +- .../src/storage/storage_signer_owned.rs | 52 +-- .../src/storage/wasm_storage_signer.rs | 5 + .../src/storage/wasm_storage_signer_inner.rs | 93 ---- .../src/storage/wasm_transaction_signer.rs | 57 +++ .../lib/iota_client_helpers.ts | 76 +++- .../lib/move_calls/identity/create.ts | 13 +- .../src/bindings/wasm_iota_client.rs | 4 +- .../src/bindings/wasm_types.rs | 133 ++++-- .../wasm/iota_interaction_ts/src/error.rs | 4 + .../src/identity_move_calls.rs | 21 +- .../src/iota_client_ts_sdk.rs | 128 +++++- examples/1_advanced/10_zkp_revocation.rs | 2 +- examples/1_advanced/9_zkp.rs | 2 +- examples/utils/utils.rs | 2 +- identity_iota_core/Cargo.toml | 2 +- .../identity_move_calls.rs | 2 +- .../iota_client_rust_sdk.rs | 3 +- .../src/rebased/assets/asset.rs | 13 +- .../src/rebased/assets/public_available_vc.rs | 3 +- .../src/rebased/client/full_client.rs | 16 +- .../src/rebased/migration/alias.rs | 5 +- .../src/rebased/migration/identity.rs | 6 +- .../src/rebased/proposals/borrow.rs | 7 +- .../src/rebased/proposals/controller.rs | 7 +- .../src/rebased/proposals/mod.rs | 13 +- identity_iota_core/src/rebased/transaction.rs | 21 +- identity_iota_core/tests/e2e/common.rs | 2 +- .../src/iota_client_trait.rs | 9 +- .../src/move_call_traits.rs | 2 +- 38 files changed, 820 insertions(+), 679 deletions(-) delete mode 100644 bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs create mode 100644 bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs diff --git a/Cargo.toml b/Cargo.toml index 6f4ea1740b..e9870fa073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ result_large_err = "allow" [profile.release.package.iota_interaction_ts] opt-level = 's' -# Enabling debug for profile.release may lead to more helpfull loged call stacks. +# Enabling debug for profile.release may lead to more helpful logged call stacks. # TODO: Clarify if 'debug = true' facilitates error analysis via console logs. # If not, remove the next line # If yes, describe the helping effect in the comment above diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index 301614b8c1..e460f31726 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -2,64 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 import { - ControllerAndVotingPower, convertToAddress, IotaDID, IotaDocument, - Jwk, JwkMemStore, JwsAlgorithm, KeyIdMemStore, KinesisIdentityClient, KinesisIdentityClientReadOnly, Multicontroller, - OnChainIdentity, - ProposalAction, Storage, StorageSigner, - // StorageSigner, } from "@iota/identity-wasm/node"; import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; import { bcs } from "@iota/iota.js/bcs"; import { IotaClient as KinesisClient, QueryEventsParams } from "@iota/iota.js/client"; -import { getFaucetHost, requestIotaFromFaucetV0, requestIotaFromFaucetV1 } from "@iota/iota.js/faucet"; -import { Ed25519Keypair, Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; +import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; +import { Ed25519Keypair } from "@iota/iota.js/keypairs/ed25519"; import { Transaction } from "@iota/iota.js/transactions"; import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; -import { event } from "cypress/types/jquery"; - -// this was is the implemented in `src/kinesis/wasm_identity_client.rs` -// and then imported here as `convertToAddress` from `@iota/identity-wasm/node` (see above) -// -// use fastcrypto::ed25519::Ed25519PublicKey; -// use identity_iota::iota::iota_sdk_abstraction::types::base_types::IotaAddress; -// use identity_iota::iota::sui_name_tbd_error::Error as TbdError; -// -/// TODO: consider importing function from rebased later on, if possible -// pub fn convert_to_address(sender_public_key: &[u8]) -> Result { -// let public_key = Ed25519PublicKey::from_bytes(sender_public_key) -// .map_err(|err| TbdError::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; -// -// Ok(IotaAddress::from(&public_key)) -// } -// -// #[wasm_bindgen(js_name = convertToAddress)] -// pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { -// convert_to_address(sender_public_key) -// .map(|v| v.to_string()) -// .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) -// } -// -// Currently unclear where to put the this implementation atm. It was a helper for testing performing transactions, -// we might also be able to just drop it due to API restructuring. - -export const DEFAULT_GAS_BUDGET = 10000000; -const NETWORK_NAME = "local"; -const NETWORK_NAME_FAUCET = "localnet"; -const NETWORK_URL = "http://127.0.0.1:9000"; -// for now fixed, better impl in utils already -const IDENTITY_IOTA_PACKAGE_ID = "0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d"; +import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BUDGET } from "../utils_alpha"; async function initializeClients() { const kinesis_client = new KinesisClient({ url: NETWORK_URL }); @@ -104,32 +67,21 @@ async function initializeClients() { let pub_key = key_pair.getPublicKey(); console.log(`Created Ed25519Keypair with PublicKey ${pub_key.toBase64()} and address ${pub_key.toIotaAddress()}`); - // // test builder and create instance for other tests - // let identityClient = KinesisIdentityClient - // .builder() - // .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) - // .senderPublicKey(pub_key.toRawBytes()) - // .senderAddress(pub_key.toIotaAddress()) - // .iotaClient(kinesis_client) - // .networkName(NETWORK_NAME) - // .build(); - // delete later if not required anymore // try to find package beforehand // "MoveEventType":"0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d::migration_registry::MigrationRegistryCreated" // "Sender": "0xd40005ab355d8342fa6b94e9638a1040483d70430720d28e9b425283d011c0a8" - // const eventsQuery: QueryEventsParams = { - // "query": { - // "MoveEventType":"0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d::migration_registry::MigrationRegistryCreated" - // }, - // "limit":1, - // "order":"ascending" - // }; - // const eventsResult = await kinesis_client.queryEvents(eventsQuery); - // console.dir(eventsResult); + const eventsQuery: QueryEventsParams = { + "query": { + "MoveEventType":`${IDENTITY_IOTA_PACKAGE_ID}::migration_registry::MigrationRegistryCreated` + }, + "limit":1, + "order":"ascending" + }; + const eventsResult = await kinesis_client.queryEvents(eventsQuery); + console.dir(eventsResult); const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesis_client, IDENTITY_IOTA_PACKAGE_ID); - console.dir(identityClientReadOnly); // create new storage const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); @@ -141,26 +93,22 @@ async function initializeClients() { throw new Error("failed to derive public JWK from generated JWK"); } let keyId = generate.keyId(); - console.dir({ - keyId, - publicKeyJwk: publicKeyJwk, - }); // create signer from storage let signer = new StorageSigner(storage, keyId, publicKeyJwk); const identityClient = await KinesisIdentityClient.create(identityClientReadOnly, signer); - // await requestIotaFromFaucetV0({ - // host: getFaucetHost(NETWORK_NAME_FAUCET), - // recipient: identityClient.senderAddress(), - // }); + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: identityClient.senderAddress(), + }); - // const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); - // if (balance.totalBalance === "0") { - // throw new Error("Balance is still 0"); - // } else { - // console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); - // } + const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + if (balance.totalBalance === "0") { + throw new Error("Balance is still 0"); + } else { + console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); + } return { kinesis_client, identityClient, key_pair }; } @@ -188,7 +136,6 @@ async function testIdentityClientReadOnly() { async function testIdentityClient( identityClient: KinesisIdentityClient, kinesis_client: KinesisClient, - key_pair: Ed25519Keypair, ): Promise { console.log("\n-------------- Start testIdentityClient -------------------------------"); console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); @@ -203,54 +150,32 @@ async function testIdentityClient( ) )}`); + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: identityClient.senderAddress(), + }); + + const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + if (balance.totalBalance === "0") { + throw new Error("Balance is still 0"); + } else { + console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); + } + const newDoc = new IotaDocument(identityClient.network()); - console.dir(newDoc); - const builder = identityClient.createIdentity(newDoc) - console.dir(builder); - const tx = builder.finish(); - console.dir(tx); - const ex = tx.execute(identityClient); - console.dir(ex); - - // try { - // console.log("\n---------------- executeDummyTransaction ------------------------"); - // let coins = await kinesis_client.getCoins({ - // owner: identityClient.senderAddress(), - // coinType: IOTA_TYPE_ARG, - // }); - // const tx = new Transaction(); - // const coin_0 = coins.data[0]; - // const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ - // bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), - // ]); - // tx.transferObjects([coin], identityClient.senderAddress()); - // tx.setSenderIfNotSet(key_pair.getPublicKey().toIotaAddress()); - // const signatureWithBytes = await tx.sign({ signer: key_pair, client: kinesis_client }); - - // const response = await identityClient.executeDummyTransaction( - // signatureWithBytes.bytes, - // [signatureWithBytes.signature], - // ); - // console.dir(response); - - // // The above transaction execution is equivalent to the following snippet using the TS SDK iota client - // const response2 = await kinesis_client.executeTransactionBlock({ - // transactionBlock: signatureWithBytes.bytes, - // signature: signatureWithBytes.signature, - // }); - // console.log(`TX result: ${response2}`); - // } catch (ex) { - // console.log(`\nTest execute_dummy_transaction() - Error: ${(ex as Error).message}`); - // } + const { output: identity } = await identityClient + .createIdentity(newDoc) + .finish() + .execute(identityClient); + console.log(`created new identity with id "${identity.id()}"`); try { console.log("\n---------------- getIdentity ------------------------"); - // const testValue = await identityClient.getIdentity("foobar"); - const testValue = await identityClient.getIdentity("0xd9a0f8139076bfbdc245d402c655b4e93cdf5b4184294da2bbbf7ae3d8ec97a4"); - console.dir(testValue); - const fufle = testValue.toFullFledged() as OnChainIdentity; - console.dir(fufle.id()); - console.dir(fufle.isShared()); + const identity = await identityClient.getIdentity("0xd9a0f8139076bfbdc245d402c655b4e93cdf5b4184294da2bbbf7ae3d8ec97a4"); + console.dir(identity); + const onchainIdentity = identity.toFullFledged(); + console.dir(`resolved identities id is ${onchainIdentity?.id()}`); + console.dir(`resolved identity is shared: ${onchainIdentity?.isShared()}`); } catch (ex) { console.log(`Test getIdentity() - Error: ${(ex as Error).message}`); @@ -258,9 +183,9 @@ async function testIdentityClient( const did4resolveDid = IotaDID.parse("did:iota:0x0101010101010101010101010101010101010101010101010101010101010101"); try { - // console.log("\n---------------- resolveDid ------------------------"); - // not implemented - // await identityClient.resolveDid(did4resolveDid); + console.log("\n---------------- resolveDid ------------------------"); + // invalid DID + await identityClient.resolveDid(did4resolveDid); } catch (ex) { console.log(`Test resolveDid() - Error: ${(ex as Error).message}`); } @@ -307,55 +232,56 @@ function testMultiController(): void { console.dir(multiController.threshold()); } -// async function testProposals(identityClient: KinesisIdentityClient): Promise { -// let action: ProposalAction = "Deactivate"; -// console.dir(action); - -// action = { UpdateDocument: new IotaDocument("foobar") }; -// console.dir(action); -// console.dir(action.UpdateDocument); -// console.dir(action.UpdateDocument.id()); -// console.dir(action.UpdateDocument.toJSON()); - -// let identity = await identityClient -// .createIdentity(Uint8Array.from([1, 2, 3])) -// .threshold(BigInt(1)) -// .gasBudget(BigInt(1)) -// .controllers([ -// new ControllerAndVotingPower("one", BigInt(1)), -// new ControllerAndVotingPower("two", BigInt(2)), -// ]) -// .finish(identityClient, "dummySigner"); -// console.dir(identity); -// console.dir(identity.isShared()); -// console.dir(identity.proposals()); -// const deactivateProposal = await identity -// .deactivateDid() -// .expirationEpoch(BigInt(1)) -// .gasBudget(BigInt(1)) -// .key("key") -// .finish(identityClient, "dummySigner"); -// console.dir(deactivateProposal); - -// // proposals consume the identity instance, so we need a new one -// identity = await identityClient -// .createIdentity(Uint8Array.from([1, 2, 3])) -// .threshold(BigInt(1)) -// .gasBudget(BigInt(1)) -// .controllers([ -// new ControllerAndVotingPower("one", BigInt(1)), -// new ControllerAndVotingPower("two", BigInt(2)), -// ]) -// .finish(identityClient, "dummySigner"); - -// const updateProposal = await identity -// .updateDidDocument(new IotaDocument("foobar")) -// .expirationEpoch(BigInt(1)) -// .gasBudget(BigInt(1)) -// .key("key") -// .finish(identityClient, "dummySigner"); -// console.dir(updateProposal); -// } +async function testProposals(identityClient: KinesisIdentityClient): Promise { + console.log(`testProposals disabled after interface updates`); + // let action: ProposalAction = "Deactivate"; + // console.dir(action); + + // action = { UpdateDocument: new IotaDocument("foobar") }; + // console.dir(action); + // console.dir(action.UpdateDocument); + // console.dir(action.UpdateDocument.id()); + // console.dir(action.UpdateDocument.toJSON()); + + // let identity = await identityClient + // .createIdentity(Uint8Array.from([1, 2, 3])) + // .threshold(BigInt(1)) + // .gasBudget(BigInt(1)) + // .controllers([ + // new ControllerAndVotingPower("one", BigInt(1)), + // new ControllerAndVotingPower("two", BigInt(2)), + // ]) + // .finish(identityClient, "dummySigner"); + // console.dir(identity); + // console.dir(identity.isShared()); + // console.dir(identity.proposals()); + // const deactivateProposal = await identity + // .deactivateDid() + // .expirationEpoch(BigInt(1)) + // .gasBudget(BigInt(1)) + // .key("key") + // .finish(identityClient, "dummySigner"); + // console.dir(deactivateProposal); + + // // proposals consume the identity instance, so we need a new one + // identity = await identityClient + // .createIdentity(Uint8Array.from([1, 2, 3])) + // .threshold(BigInt(1)) + // .gasBudget(BigInt(1)) + // .controllers([ + // new ControllerAndVotingPower("one", BigInt(1)), + // new ControllerAndVotingPower("two", BigInt(2)), + // ]) + // .finish(identityClient, "dummySigner"); + + // const updateProposal = await identity + // .updateDidDocument(new IotaDocument("foobar")) + // .expirationEpoch(BigInt(1)) + // .gasBudget(BigInt(1)) + // .key("key") + // .finish(identityClient, "dummySigner"); + // console.dir(updateProposal); +} async function signerTest(): Promise { // create new storage @@ -382,62 +308,57 @@ async function signerTest(): Promise { console.dir({ signed }); } -// async function testExecuteTransaction(kinesis_client: KinesisClient) { -// console.log("---------------- testing executeTransaction ------------------------"); -// -// // create new storage -// const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); -// -// // generate new key -// let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); -// let publicKeyJwk = generate.jwk().toPublic(); -// if (typeof publicKeyJwk === "undefined") { -// throw new Error("failed to derive public JWK from generated JWK"); -// } -// -// // create signer from storage -// let signer = new StorageSigner(storage, generate.keyId(), publicKeyJwk); -// // get public key as bytes and create address -// let publicJwk = (signer as any).publicKeyRaw(); -// let address = convertToAddress(publicJwk); -// -// await requestIotaFromFaucetV0({ -// host: getFaucetHost(NETWORK_NAME_FAUCET), -// recipient: address, -// }); -// -// // try to craft tx with js api -// let coins = await kinesis_client.getCoins({ -// owner: address, -// coinType: IOTA_TYPE_ARG, -// }); -// const tx = new Transaction(); -// const coin_0 = coins.data[0]; -// const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ -// bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), -// ]); -// tx.transferObjects([coin], address); -// tx.setSenderIfNotSet(address); -// -// let response = await executeTransaction( -// kinesis_client, -// address, -// publicJwk, -// await tx.build({ client: kinesis_client }), -// signer, -// ); -// console.dir(response); -// console.dir(response?.response?.transaction?.data); -// } +async function testExecuteTransaction(kinesis_client: KinesisClient) { + console.log("---------------- testing executeTransaction ------------------------"); -/** Test API usage */ -export async function testApiCall(): Promise { - const { kinesis_client, identityClient, key_pair } = await initializeClients(); + // create new storage + const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - // type ProgrammableTransaction = ReturnType; - // let tt: ProgrammableTransaction = bcs.ProgrammableTransaction.parse(new Uint8Array([])); - // console.dir(tt); + // generate new key + let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); + if (typeof publicKeyJwk === "undefined") { + throw new Error("failed to derive public JWK from generated JWK"); + } + // create signer from storage + let signer = new StorageSigner(storage, generate.keyId(), publicKeyJwk); + // get public key as bytes and create address + let publicJwk = (signer as any).publicKeyRaw(); + let address = convertToAddress(publicJwk); + + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: address, + }); + + // try to craft tx with js api + let coins = await kinesis_client.getCoins({ + owner: address, + coinType: IOTA_TYPE_ARG, + }); + const tx = new Transaction(); + const coin_0 = coins.data[0]; + const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ + bcs.u64().serialize(TEST_GAS_BUDGET * 2), + ]); + tx.transferObjects([coin], address); + tx.setSenderIfNotSet(address); + + let response = await executeTransaction( + kinesis_client, + address, + publicJwk, + await tx.build({ client: kinesis_client }), + signer, + ); + console.dir(response); + console.dir(response?.response?.transaction?.data); +} + +/** Test API usage */ +export async function testApiCall(): Promise { + const { kinesis_client, identityClient } = await initializeClients(); try { await testIdentityClientReadOnly(); @@ -447,39 +368,39 @@ export async function testApiCall(): Promise { } try { - await testIdentityClient(identityClient, kinesis_client, key_pair); + await testIdentityClient(identityClient, kinesis_client); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`identity client binding test failed: ${suffix}`); } - // try { - // testMultiController(); - // } catch (err) { - // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - // console.error(`multi controller binding test failed: ${suffix}`); - // } - - // try { - // await testProposals(identityClient); - // } catch (err) { - // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - // console.error(`proposals binding test failed: ${suffix}`); - // } - - // try { - // await signerTest(); - // } catch (err) { - // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - // console.error(`signer binding test failed: ${suffix}`); - // } - - // try { - // await testExecuteTransaction(kinesis_client); - // } catch (err) { - // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - // console.error(`signer binding test failed: ${suffix}`); - // } + try { + testMultiController(); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`multi controller binding test failed: ${suffix}`); + } + + try { + await testProposals(identityClient); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`proposals binding test failed: ${suffix}`); + } + + try { + await signerTest(); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`signer binding test failed: ${suffix}`); + } + + try { + await testExecuteTransaction(kinesis_client); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`signer binding test failed: ${suffix}`); + } console.log("done"); } diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 61158a0388..00251c23b0 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -1,32 +1,50 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { IotaDID } from '@iota/identity-wasm/node'; +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; import { - createDidDocument, + createDocumentForNetwork, getClientAndCreateAccount, getMemstorage, + NETWORK_URL, } from '../utils_alpha'; -// old API: -// export async function createIdentityOld(): Promise<{ -// didClient: IotaIdentityClient; -// secretManager: SecretManager; -// walletAddressBech32: string; -// did: IotaDID; -// }> { /* ... */ } - /** Demonstrate how to create a DID Document and publish it. */ export async function createIdentity(): Promise { + // figure out a better way to organize those calls >.> + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + const network = await kinesisClient.getChainIdentifier(); + // create new client to interact with chain and get funded account with keys const storage = getMemstorage(); + // TODO: check if we can update storage implementation to a non-owning variant + // order is important here as wrapped storage will be set to a null pointer after passing it to a client + const [unpublished] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); - - // create new DID document and publish it - const [document] = await createDidDocument(identityClient, storage); - console.log(`Published DID document: ${JSON.stringify(document, null, 2)}`); + + console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); + let did: IotaDID; + + // TODO: decide upon wich style to use here + // so let's go with both for now, to show that both work + if (Math.random() > .5) { + console.log('Creating new identity fully via client flow'); + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + } else { + console.log('Publishing document to identity'); + const { output: published } = await identityClient + .publishDidDocument(unpublished) + .execute(identityClient); + did = published.id(); + } // check if we can resolve it via client - const resolved = await identityClient.resolveDid(document.id()); + const resolved = await identityClient.resolveDid(did); console.log(`Resolved DID document: ${JSON.stringify(resolved, null, 2)}`); } \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts index d2ad0b58a1..f4c4346f3e 100644 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -10,88 +10,58 @@ import { KinesisIdentityClientReadOnly, MethodScope, Storage, + StorageSigner, } from "@iota/identity-wasm/node"; import { IotaClient as KinesisClient } from "@iota/iota.js/client"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; -import { Ed25519Keypair } from "@iota/iota.js/keypairs/ed25519"; -export const TEST_GAS_BUDGET = 50_000_000; - -const IOTA_LOCAL_NETWORK_URL = "http://127.0.0.1:9000"; -const NETWORK_NAME = "local"; -const NETWORK_NAME_FAUCET = "localnet" - -const { - API_ENDPOINT, - IDENTITY_IOTA_PACKAGE_ID, -} = process.env; +export const IDENTITY_IOTA_PACKAGE_ID = + process.env.IDENTITY_IOTA_PACKAGE_ID || "0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d"; +export const NETWORK_NAME_FAUCET = "localnet"; +export const NETWORK_URL = + process.env.NETWORK_URL || "http://127.0.0.1:9000"; + export const TEST_GAS_BUDGET = 50_000_000; export function getMemstorage(): Storage { return new Storage(new JwkMemStore(), new KeyIdMemStore()); } +export async function createDocumentForNetwork(storage: Storage, network: string): Promise<[IotaDocument, string]> { + // Create a new DID document with a placeholder DID. + const unpublished = new IotaDocument(network); + + const verification_method_fragment = await unpublished.generateMethod( + storage, + JwkMemStore.ed25519KeyType(), + JwsAlgorithm.EdDSA, + "#key-1", + MethodScope.VerificationMethod(), + ); + + return [unpublished, verification_method_fragment]; +} + export async function getClientAndCreateAccount(storage: Storage): Promise { - let api_endpoint = API_ENDPOINT || IOTA_LOCAL_NETWORK_URL; if (!IDENTITY_IOTA_PACKAGE_ID) { throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); } - const kinesisClient = new KinesisClient({ url: api_endpoint }); + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + + const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId( + kinesisClient, IDENTITY_IOTA_PACKAGE_ID); // generate new key - // TODO: make random key - console.log("---------------- Preparing IdentityClient ------------------------"); - const VALID_SECP256K1_SECRET_KEY = [ - 59, - 148, - 11, - 85, - 134, - 130, - 61, - 253, - 2, - 174, - 59, - 70, - 27, - 180, - 51, - 107, - 94, - 203, - 174, - 253, - 102, - 39, - 170, - 146, - 46, - 252, - 4, - 143, - 236, - 12, - 136, - 28, - ]; - const secretKey = new Uint8Array(VALID_SECP256K1_SECRET_KEY); - let keyPair = Ed25519Keypair.fromSecretKey(secretKey); - let pubKey = keyPair.getPublicKey(); - console.log(`Created Ed25519Keypair with PublicKey ${pubKey.toBase64()} and address ${pubKey.toIotaAddress()}`); + let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); + if (typeof publicKeyJwk === "undefined") { + throw new Error("failed to derive public JWK from generated JWK"); + } + let keyId = generate.keyId(); - // test builder and create instance for other tests - // let identityClient = KinesisIdentityClient - // .builder() - // .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) - // .senderPublicKey(pubKey.toRawBytes()) - // .senderAddress(pubKey.toIotaAddress()) - // .iotaClient(kinesisClient) - // .networkName(NETWORK_NAME) - // .build(); - const identityClientReadOnly = KinesisIdentityClientReadOnly.create(kinesisClient); - console.dir(identityClientReadOnly); - const identityClient = null as any; + // create signer from storage + let signer = new StorageSigner(storage, keyId, publicKeyJwk); + const identityClient = await KinesisIdentityClient.create(identityClientReadOnly, signer); await requestIotaFromFaucetV0({ host: getFaucetHost(NETWORK_NAME_FAUCET), @@ -110,41 +80,12 @@ export async function getClientAndCreateAccount(storage: Storage): Promise { - // Create a new DID document with a placeholder DID. - const unpublished = new IotaDocument(identity_client.network()); - - throw new Error('createDidDocument not fully implemented'); - - // // Insert a new Ed25519 verification method in the DID document. - // await unpublished.generateMethod( - // storage, - // JwkMemStore.ed25519KeyType(), - // JwsAlgorithm.EdDSA, - // "#key-1", - // MethodScope.VerificationMethod(), - // ); - - // let verification_method_fragment = await unpublished - // .generate_method( - // storage, - // JwkMemStore.ed25519KeyType(), - // JwsAlgorithm.EdDSA, - // "#key-1", // TODO: `None` in Rust? oO - // MethodScope.VerificationMethod(), - // ); - - // // TODO: add publish - // // (Dummy interface) - // // await identityClient.publishDidDocument(document, BigInt(12345), "dummy signer"); - // // (Rust) - // // let document = identity_client - // // .publish_did_document(unpublished) - // // .execute_with_gas(TEST_GAS_BUDGET, identity_client) - // // .await? - // // .output; + unpublished: IotaDocument, +): Promise { + let tx = identity_client + .publishDidDocument(unpublished); + let txOutput = await tx.execute(identity_client); - // return [document, verification_method_fragment]; + return txOutput.output; } \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index 268de2f1a6..8489b18bbd 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -9,6 +9,7 @@ "version": "1.4.0", "license": "Apache-2.0", "dependencies": { + "@iota/iota-interaction-ts": "file:../iota_interaction_ts/node", "@iota/iota.js": "file:../../../../iota/sdk/typescript", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", @@ -93,6 +94,51 @@ "../../../../iotaledger/iota/sdk/typescript": { "extraneous": true }, + "../iota_interaction_ts": { + "name": "@iota/iota-interaction-ts", + "version": "1.4.0", + "license": "Apache-2.0", + "dependencies": { + "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@noble/ed25519": "^1.7.3", + "@noble/hashes": "^1.4.0", + "@types/node-fetch": "^2.6.2", + "base64-arraybuffer": "^1.0.2", + "node-fetch": "^2.6.7" + }, + "devDependencies": { + "@transmute/did-key-ed25519": "0.3.0-unstable.9", + "@types/mocha": "^9.1.0", + "@types/node": "^22.0.0", + "big-integer": "^1.6.51", + "copy-webpack-plugin": "^7.0.0", + "cypress": "^13.12.0", + "cypress-parallel": "^0.14.0", + "dprint": "^0.33.0", + "fs-extra": "^10.1.0", + "jsdoc-to-markdown": "^7.1.1", + "mocha": "^9.2.0", + "ts-mocha": "^9.0.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.1.0", + "txm": "^8.1.0", + "typedoc": "^0.24.6", + "typedoc-plugin-markdown": "^3.14.0", + "typescript": "=5.1", + "wasm-opt": "^1.3.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@iota/sdk-wasm": "^1.0.4" + } + }, + "../iota_interaction_ts/node": { + "name": "@iota/iota-interaction-ts", + "version": "1.4.0", + "license": "Apache-2.0" + }, "node_modules/@babel/parser": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", @@ -239,6 +285,10 @@ "@shikijs/vscode-textmate": "^10.0.1" } }, + "node_modules/@iota/iota-interaction-ts": { + "resolved": "../iota_interaction_ts/node", + "link": true + }, "node_modules/@iota/iota.js": { "resolved": "../../../../iota/sdk/typescript", "link": true @@ -7082,6 +7132,9 @@ "@shikijs/vscode-textmate": "^10.0.1" } }, + "@iota/iota-interaction-ts": { + "version": "file:../iota_interaction_ts/node" + }, "@iota/iota.js": { "version": "file:../../../../iota/sdk/typescript", "requires": { @@ -11640,9 +11693,9 @@ "requires": {} }, "typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "typical": { diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 5c09529114..55bdc95416 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -10,7 +10,7 @@ "example": "examples" }, "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", + "build:src": "cargo lbuild --lib --release --target wasm32-unknown-unknown", "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", @@ -79,6 +79,7 @@ }, "dependencies": { "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@iota/iota-interaction-ts": "file:../iota_interaction_ts/node", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index f9c67735b7..ff3eed9ca2 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -7,17 +7,16 @@ use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; use identity_iota::iota::rebased::client::IdentityClient; +use identity_iota::iota::rebased::client::PublishDidTx; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::SignatureBcs; - -use identity_iota::core::Base; -use identity_iota::core::BaseEncoding; use iota_interaction_ts::bindings::WasmExecutionStatus; use iota_interaction_ts::bindings::WasmOwnedObjectRef; -use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; use identity_iota::iota::rebased::Error; +use iota_interaction_ts::AdapterNativeResponse; use super::IdentityContainer; use super::WasmIdentityBuilder; @@ -26,8 +25,7 @@ use super::WasmKinesisIdentityClientReadOnly; use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; -use crate::storage::StorageSignerOwned; -use crate::storage::WasmStorageSigner; +use crate::storage::WasmTransactionSigner; use identity_iota::iota::IotaDocument; use wasm_bindgen::prelude::*; @@ -46,14 +44,13 @@ pub struct WasmIotaTransactionBlockResponseEssence { } #[wasm_bindgen(js_name = KinesisIdentityClient)] -pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); +pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); -// builder related functions #[wasm_bindgen(js_class = KinesisIdentityClient)] impl WasmKinesisIdentityClient { #[wasm_bindgen(js_name = create)] - pub async fn new(client: WasmKinesisIdentityClientReadOnly, signer: WasmStorageSigner) -> Result { - let inner_client = IdentityClient::new(client.0, signer.0).await?; + pub async fn new(client: WasmKinesisIdentityClientReadOnly, signer: WasmTransactionSigner) -> Result { + let inner_client = IdentityClient::new(client.0, signer).await?; Ok(WasmKinesisIdentityClient(inner_client)) } @@ -82,12 +79,6 @@ impl WasmKinesisIdentityClient { WasmIdentityBuilder::new(iota_document) } - // #[wasm_bindgen(js_name = getIdentity)] - // pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { - // let identity = self.0.get_identity(object_id.parse()?).await.map_err(|e| e.into())?; - // Ok(WasmIdentity(identity)) - // } - #[wasm_bindgen(js_name = getIdentity)] pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { let inner_value = self.0.get_identity(object_id.parse()?).await.unwrap(); @@ -109,28 +100,25 @@ impl WasmKinesisIdentityClient { Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } - // not included in any e2e test anymore, so let's skip it for now + #[wasm_bindgen(js_name = publishDidDocument)] + pub fn publish_did_document( + &self, + document: &WasmIotaDocument, + ) -> Result { + let doc: IotaDocument = document + .0 + .try_read() + .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + .clone(); - // #[wasm_bindgen(js_name = publishDidDocument)] - // pub async fn publish_did_document( - // &self, - // document: &WasmIotaDocument, - // ) -> Result<(), JsError> { - // let doc: IotaDocument = document - // .0 - // .try_read() - // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - // .clone(); - // let publish_tx = self - // .0 - // .publish_did_document(doc); - // // .await - // // .map_err(>::into)?; + Ok(WasmPublishDidTx(self + .0 + .publish_did_document(doc))) + } - // // Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) - // Ok(()) - // } + // not included in any e2e test anymore, so let's skip it for now + // TODO: re-add WasmKinesisIdentityClient::publish_did_document_update // #[wasm_bindgen(js_name = publishDidDocumentUpdate)] // pub async fn publish_did_document_update( // &self, @@ -152,6 +140,7 @@ impl WasmKinesisIdentityClient { // Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) // } + // TODO: re-add WasmKinesisIdentityClient::deactivate_did_output // #[wasm_bindgen(js_name = deactivateDidOutput)] // pub async fn deactivate_did_output( // &self, @@ -169,7 +158,7 @@ impl WasmKinesisIdentityClient { // } } -/// TODO: consider importing function from rebased later on, if possible +// TODO: consider importing function from rebased later on, if possible pub fn convert_to_address(sender_public_key: &[u8]) -> Result { let public_key = Ed25519PublicKey::from_bytes(sender_public_key) .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; @@ -183,3 +172,32 @@ pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result Result { + let output = self.0.execute(&client.0).await.unwrap(); + Ok(WasmTransactionOutputPublishDid(output)) + } +} + +#[wasm_bindgen(js_name = TransactionOutputInternalIotaDocument)] +pub struct WasmTransactionOutputPublishDid(pub(crate) TransactionOutputInternal); + +#[wasm_bindgen(js_class = TransactionOutputInternalIotaDocument)] +impl WasmTransactionOutputPublishDid { + #[wasm_bindgen(getter)] + pub fn output(&self) -> WasmIotaDocument { + self.0.output.clone().into() + } + + #[wasm_bindgen(getter)] + pub fn response(&self) -> AdapterNativeResponse { + self.0.response.clone_native_response() + } +} \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index 964ec0d330..48047752fd 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -21,9 +21,10 @@ pub struct IdentityContainer(pub(crate) Identity); #[wasm_bindgen(js_class = Identity)] impl IdentityContainer { /// TODO: check if we can actually do this like this w/o consuming the container on the 1st try + /// TODO: add support for unmigrated aliases #[wasm_bindgen(js_name = toFullFledged)] - pub fn to_full_fledged(self) -> Option { - match self.0 { + pub fn to_full_fledged(&self) -> Option { + match self.0.clone() { Identity::FullFledged(v) => Some(WasmOnChainIdentity(v)), _ => None, } @@ -103,18 +104,3 @@ impl WasmKinesisIdentityClientReadOnly { Ok(IdentityContainer(inner_value)) } } - -// TODO: consider importing function from rebased later on, if possible -// pub fn convert_to_address(sender_public_key: &[u8]) -> Result { -// let public_key = Ed25519PublicKey::from_bytes(sender_public_key) -// .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; - -// Ok(IotaAddress::from(&public_key)) -// } - -// #[wasm_bindgen(js_name = convertToAddress)] -// pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { -// convert_to_address(sender_public_key) -// .map(|v| v.to_string()) -// .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) -// } diff --git a/bindings/wasm/identity_wasm/src/storage/mod.rs b/bindings/wasm/identity_wasm/src/storage/mod.rs index ce8fd2e8b1..69fb983311 100644 --- a/bindings/wasm/identity_wasm/src/storage/mod.rs +++ b/bindings/wasm/identity_wasm/src/storage/mod.rs @@ -9,10 +9,10 @@ mod jwt_presentation_options; mod key_id_storage; mod method_digest; mod signature_options; -mod wasm_storage; -// Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] mod storage_signer_owned; +mod wasm_storage; mod wasm_storage_signer; +mod wasm_transaction_signer; pub use jpt_timeframe_revocation_ext::*; pub use jwk_gen_output::*; @@ -21,7 +21,7 @@ pub use jwt_presentation_options::*; pub use key_id_storage::*; pub use method_digest::*; pub use signature_options::*; -pub use wasm_storage::*; -// Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] pub use storage_signer_owned::*; +pub use wasm_storage::*; pub use wasm_storage_signer::*; +pub use wasm_transaction_signer::*; diff --git a/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs b/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs index 4131311719..29f450fbb1 100644 --- a/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs +++ b/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs @@ -11,11 +11,7 @@ use secret_storage::SignatureScheme; use secret_storage::Signer; use identity_iota::storage::JwkStorage; use identity_iota::storage::KeyId; -use identity_iota::storage::KeyIdStorage; use identity_iota::storage::KeyStorageErrorKind; -use identity_iota::storage::Storage; - -use crate::jose::WasmJwk; use super::WasmStorage; @@ -24,26 +20,16 @@ use super::WasmStorage; pub struct StorageSignerOwned { key_id: KeyId, public_key: Jwk, - // storage: WasmStorage, + storage: WasmStorage, } -// impl Clone for StorageSignerOwned { -// fn clone(&self) -> Self { -// StorageSignerOwned { -// key_id: self.key_id.clone(), -// public_key: self.public_key.clone(), -// storage: self.storage, -// } -// } -// } - impl StorageSignerOwned { /// Creates new `StorageSignerOwned` with reference to a `Storage` instance. pub fn new(storage: WasmStorage, key_id: KeyId, public_key: Jwk) -> Self { Self { key_id, public_key, - // storage, + storage, } } @@ -57,18 +43,20 @@ impl StorageSignerOwned { &self.public_key } - // /// Returns a reference to this [`Signer`]'s [`Storage`]. - // pub fn storage(&self) -> &WasmStorage { - // &self.storage - // } + /// Returns a reference to this [`Signer`]'s [`Storage`]. + pub fn storage(&self) -> &WasmStorage { + &self.storage + } } #[async_trait(?Send)] impl Signer for StorageSignerOwned { type KeyId = KeyId; + fn key_id(&self) -> &KeyId { &self.key_id } + async fn public_key(&self) -> Result<::PublicKey, SecretStorageError> { match self.public_key.params() { JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) @@ -76,18 +64,18 @@ impl Signer for StorageSignerOwned { _ => todo!("add support for other key types"), } } + async fn sign(&self, data: &[u8]) -> Result<::Signature, SecretStorageError> { - // let key_storage = self - // .storage - // .key_storage(); - // // call trait's sign impl for `WasmJwkStorage` instead of `WasmJwkStorage`s own impl - // JwkStorage::sign(&key_storage, &self.key_id, data, &self.public_key) - // .await - // .map_err(|e| match e.kind() { - // KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), - // KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), - // _ => SecretStorageError::Other(anyhow::anyhow!(e)), - // }) - todo!("pointer test"); + let key_storage = self + .storage + .key_storage(); + // call trait's sign impl for `WasmJwkStorage` instead of `WasmJwkStorage`s own impl + JwkStorage::sign(&key_storage, &self.key_id, data, &self.public_key) + .await + .map_err(|e| match e.kind() { + KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), + KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), + _ => SecretStorageError::Other(anyhow::anyhow!(e)), + }) } } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index 7966ff3422..3c90497601 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -54,4 +54,9 @@ impl WasmStorageSigner { _ => todo!("add support for other key types"), } } + + #[wasm_bindgen(js_name = publicKey)] + pub async fn public_key(&self) -> Result> { + self.public_key_raw() + } } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs deleted file mode 100644 index d759a6f007..0000000000 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer_inner.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use identity_iota_interaction::IotaKeySignature; -use identity_verification::jwk::Jwk; -use identity_verification::jwk::JwkParams; -use identity_verification::jwu; -use secret_storage::Error as SecretStorageError; -use secret_storage::SignatureScheme; -use secret_storage::Signer; - -use crate::JwkStorage; -use crate::KeyId; -use crate::KeyIdStorage; -use crate::KeyStorageErrorKind; -use crate::Storage; - -/// Basically the same as the original storage signer with the exception, that this one here is owned. -/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`. -/// `Storage` is used to sign. -pub struct StorageSignerOwned { - key_id: KeyId, - public_key: Jwk, - storage: Storage, -} - -impl Clone for StorageSignerOwned { - fn clone(&self) -> Self { - StorageSignerOwned { - key_id: self.key_id.clone(), - public_key: self.public_key.clone(), - storage: self.storage, - } - } -} - -impl<'a, K, I> StorageSignerOwned { - /// Creates new `StorageSignerOwned` with reference to a `Storage` instance. - pub fn new(storage: Storage, key_id: KeyId, public_key: Jwk) -> Self { - Self { - key_id, - public_key, - storage, - } - } - - /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`]. - pub fn key_id(&self) -> &KeyId { - &self.key_id - } - - /// Returns this [`Signer`]'s public key as [`Jwk`]. - pub fn public_key(&self) -> &Jwk { - &self.public_key - } - - /// Returns a reference to this [`Signer`]'s [`Storage`]. - pub fn storage(&self) -> &Storage { - &self.storage - } -} - -#[async_trait(?Send)] -impl Signer for StorageSignerOwned -where - K: JwkStorage + Sync, - I: KeyIdStorage + Sync, -{ - type KeyId = KeyId; - fn key_id(&self) -> &KeyId { - &self.key_id - } - async fn public_key(&self) -> core::result::Result<::PublicKey, SecretStorageError> { - match self.public_key.params() { - JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) - .map_err(|e| SecretStorageError::Other(anyhow::anyhow!("could not base64 decode key {}; {e}", self.key_id()))), - _ => todo!("add support for other key types"), - } - } - async fn sign(&self, data: &[u8]) -> core::result::Result<::Signature, SecretStorageError> { - self - .storage - .key_storage() - .sign(&self.key_id, data, &self.public_key) - .await - .map_err(|e| match e.kind() { - KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), - KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), - _ => SecretStorageError::Other(anyhow::anyhow!(e)), - }) - } -} diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs new file mode 100644 index 0000000000..f8dab34980 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -0,0 +1,57 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use wasm_bindgen::prelude::wasm_bindgen; +use identity_iota::iota::rebased::client::IotaKeySignature; +use secret_storage::Error as SecretStorageError; +use secret_storage::Signer; + +use crate::error::Result; + +#[wasm_bindgen(typescript_custom_section)] +const I_JWS_SIGNER: &str = r#" +interface TransactionSigner { + sign: (data: Uint8Array) => Promise; + publicKey: () => Promise; + keyId: () => string; +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "TransactionSigner")] + pub type WasmTransactionSigner; // we will receive this as `StorageSigner` from TS + + #[wasm_bindgen(js_name = "sign", structural, method, catch)] + pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; + + #[wasm_bindgen(js_name = "publicKey", structural, method, catch)] + pub async fn public_key(this: &WasmTransactionSigner) -> Result; + + // TODO: re-add WasmTransactionSigner::key_id + // #[wasm_bindgen(js_name = "keyId", structural, method)] + // // pub fn key_id(this: &WasmTransactionSigner) -> js_sys::JsString; + // pub fn key_id(this: &WasmTransactionSigner) -> String; +} + +#[async_trait(?Send)] +impl Signer for WasmTransactionSigner { + type KeyId = String; + + async fn sign(&self, data: &[u8]) -> std::result::Result, SecretStorageError> { + self.sign(data).await.map(|v| v.to_vec()).map_err(|err| SecretStorageError::Other( + anyhow::anyhow!(err.as_string().unwrap_or_else(|| "could not sign data".to_string())) + )) + } + + async fn public_key(&self) -> std::result::Result, SecretStorageError> { + self.public_key().await.map(|v| v.to_vec()).map_err(|err| SecretStorageError::Other( + anyhow::anyhow!(err.as_string().unwrap_or_else(|| "could not get public key".to_string())) + )) + } + + fn key_id(&self) -> &String { + todo!("WasmTransactionSigner::key_id"); + } +} \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index 9276a699b1..58985062d2 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -10,7 +10,7 @@ import { } from "@iota/iota.js/client"; import { messageWithIntent, toSerializedSignature } from "@iota/iota.js/cryptography"; import { Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; -import { TransactionDataBuilder } from "@iota/iota.js/transactions"; +import { GasData, TransactionDataBuilder } from "@iota/iota.js/transactions"; import { blake2b } from "@noble/hashes/blake2b"; export type Signer = { sign(data: Uint8Array): Promise }; @@ -49,14 +49,24 @@ export class IotaTransactionBlockResponseAdapter { } } +/** + * Builds message with `TransactionData` intent and returns hash of it. + * + * @param txBcs transaction data to hash + * @returns digest/hash of intent message for transaction data + */ +export function getTransactionDigest(txBcs: Uint8Array): Uint8Array { + const intent = "TransactionData"; + const intentMessage = messageWithIntent(intent, txBcs); + return blake2b(intentMessage, { dkLen: 32 }); +} + async function signTransactionData( txBcs: Uint8Array, senderPublicKey: Uint8Array, signer: { sign(data: Uint8Array): Promise }, ): Promise { - const intent = "TransactionData"; - const intentMessage = messageWithIntent(intent, txBcs); - const digest = blake2b(intentMessage, { dkLen: 32 }); + const digest = getTransactionDigest(txBcs); const signerSignature = await signer.sign(digest); const signature = toSerializedSignature({ signature: await signerSignature, @@ -70,13 +80,28 @@ async function signTransactionData( async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { const coins = await iotaClient.getCoins({ owner: senderAddress }); if (coins.data.length === 0) { - throw new Error("could not find coins for transaction"); + throw new Error(`could not find coins for transaction with sender ${senderAddress}`); } return coins.data[1]; } -async function addGasDataToTransaction( +/** + * Inserts these values into the transaction and replaces placeholder values. + * + * - sender (overwritten as we assume a placeholder to be used in prepared transaction) + * - gas budget (value determined automatically if not provided) + * - gas price (value determined automatically) + * - gas coin / payment object (fetched automatically) + * - gas owner (equals sender) + * + * @param iotaClient client instance + * @param senderAddress transaction sender (and the one paying for it) + * @param txBcs transaction data serialized to bcs, most probably having placeholder values + * @param gasBudget optional fixed gas budget, determined automatically with a dry run if not provided + * @returns updated transaction data + */ +export async function addGasDataToTransaction( iotaClient: IotaClient, senderAddress: string, txBcs: Uint8Array, @@ -85,7 +110,7 @@ async function addGasDataToTransaction( const gasPrice = await iotaClient.getReferenceGasPrice(); const gasCoin = await getCoinForTransaction(iotaClient, senderAddress); const txData = TransactionDataBuilder.fromBytes(txBcs); - const gasData = { + const gasData: GasData = { budget: gasBudget ? gasBudget.toString() : "50000000000", // 50_000_000_000 owner: senderAddress, payment: [{ @@ -95,12 +120,14 @@ async function addGasDataToTransaction( }], price: gasPrice.toString(), }; - let builtTx = txData.build({ - overrides: { - gasData, - sender: senderAddress, - }, - }); + const overrides = { + gasData, + sender: senderAddress, + }; + // TODO: check why `.build` with `overrides` doesn't override these values + txData.sender = overrides.sender; + txData.gasData = overrides.gasData; + let builtTx = txData.build({ overrides }); if (!gasBudget) { // no budget given, so we have to estimate gas usage @@ -120,13 +147,10 @@ async function addGasDataToTransaction( const maxCost = netUsed > computation ? netUsed : computation; const budget = overhead + maxCost; - gasData.budget = budget.toString(); - builtTx = txData.build({ - overrides: { - gasData, - sender: senderAddress, - }, - }); + overrides.gasData.budget = budget.toString(); + txData.gasData.budget = budget.toString(); + + builtTx = txData.build({ overrides }); } return builtTx; @@ -148,7 +172,7 @@ export async function executeTransaction( const response = await iotaClient.executeTransactionBlock({ transactionBlock: txWithGasData, signature, - options: { // `IotaTransactionBlockResponseOptions::full_content()` + options: { // equivalent of `IotaTransactionBlockResponseOptions::full_content()` showEffects: true, showInput: true, showRawInput: true, @@ -158,7 +182,6 @@ export async function executeTransaction( showRawEffects: false, }, }); - console.dir(response); if (response?.effects?.status.status === "failure") { throw new Error(`transaction returned an unexpected response; ${response?.effects?.status.error}`); @@ -166,3 +189,12 @@ export async function executeTransaction( return new IotaTransactionBlockResponseAdapter(response); } + +/** + * Helper function to pause execution. + * + * @param txBcs transaction data to hash + */ +export function sleep(durationMs: number) { + return new Promise(resolve => setTimeout(resolve, durationMs)); +} \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts index c81cde59a8..f45946e87b 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts @@ -4,7 +4,9 @@ import { Transaction } from "@iota/iota.js/transactions"; import { getClockRef } from "../utils"; -export function create(didDoc: Uint8Array, packageId: string): Promise { +// TODO: check going back to Uint8Array +// export async function create(didDoc: Uint8Array, packageId: string): Promise { +export async function create(didDoc: Uint8Array, packageId: string): Promise { const tx = new Transaction(); const didDocArg = tx.pure.vector("u8", didDoc); const clock = getClockRef(tx); @@ -14,7 +16,14 @@ export function create(didDoc: Uint8Array, packageId: string): Promise Result; - - #[wasm_bindgen(method, structural, catch)] - pub async fn build(this: &WasmTransactionBuilder) -> Result; - #[wasm_bindgen(typescript_type = "TransactionArgument")] pub type WasmTransactionArgument; @@ -171,6 +159,36 @@ extern "C" { pub type PromiseIotaTransactionBlockResponseAdapter; } +#[wasm_bindgen(module = "@iota/iota.js/transactions")] +extern "C" { + #[wasm_bindgen(typescript_type = "Transaction")] + pub type WasmTransactionBuilder; + + #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransactionBuilder, catch)] + pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; + + #[wasm_bindgen(method, structural, catch)] + pub async fn build(this: &WasmTransactionBuilder) -> Result; + + #[wasm_bindgen(js_name = "setSender", method, catch)] + pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = "setGasOwner", method, catch)] + pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = "setGasPrice", method, catch)] + pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = "setGasPayment", method, catch)] + pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = "setGasBudget", method, catch)] + pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = "getData", method, catch)] + pub fn get_data(this: &WasmTransactionBuilder) -> Result; +} + impl From for WasmObjectRef { fn from(value: ObjectRef) -> Self { let json_obj = serde_json::json!({ @@ -252,13 +270,74 @@ extern "C" { #[wasm_bindgen(js_name = executeTransaction)] fn execute_transaction_inner( - iotaClient: &WasmIotaClient, // --> TypeScript: IotaClient - sender_address: String, // --> TypeScript: string - sender_public_key: Vec, // --> TypeScript: Uint8Array - tx_bcs: Vec, // --> TypeScript: Uint8Array, - signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) - gas_budget: Option, // --> TypeScript: optional bigint + iota_client: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + sender_public_key: Vec, // --> TypeScript: Uint8Array + tx_bcs: Vec, // --> TypeScript: Uint8Array, + signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) + gas_budget: Option, // --> TypeScript: optional bigint ) -> PromiseIotaTransactionBlockResponseAdapter; + + #[wasm_bindgen(js_name = "getTransactionDigest")] + fn get_transaction_digest_inner(txBcs: Uint8Array) -> Uint8Array; + + #[wasm_bindgen(js_name = "addGasDataToTransaction")] + fn add_gas_data_to_transaction_inner( + iota_client: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + tx_bcs: Vec, // --> TypeScript: Uint8Array, + gas_budget: Option, // --> TypeScript: optional bigint +) -> PromiseUint8Array; + + #[wasm_bindgen(js_name = "sleep")] + fn sleep_inner(ms: i32) -> Promise; +} + +/// Builds message with `TransactionData` intent and returns hash of it. +pub(crate) fn get_transaction_digest(tx_data: &[u8]) -> Vec { + get_transaction_digest_inner(Uint8Array::from(tx_data)).to_vec() +} + +/// Inserts these values into the transaction and replaces placeholder values. +/// +/// - sender (overwritten as we assume a placeholder to be used in prepared transaction) +/// - gas budget (value determined automatically if not provided) +/// - gas price (value determined automatically) +/// - gas coin / payment object (fetched automatically) +/// - gas owner (equals sender) +/// +/// # Arguments +/// +/// * `iota_client` - client instance +/// * `sender_address` - transaction sender (and the one paying for it) +/// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values +/// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided +pub(crate) async fn add_gas_data_to_transaction( + iota_client: &WasmIotaClient, + sender_address: IotaAddress, + tx_bcs: Vec, + gas_budget: Option, +) -> Result, Error> { + let promise: Promise = Promise::resolve(&add_gas_data_to_transaction_inner( + iota_client, + sender_address.to_string(), + tx_bcs, + gas_budget, + )); + let value: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise) for `add_gas_data_to_transaction`: {:?}", e); + Error::FfiError(format!("{:?}", e)) + })?; + + Ok(Uint8Array::new(&value).to_vec()) +} + +/// Helper function to pause execution. +pub async fn sleep(duration_ms: i32) -> Result<(), JsValue> { + let promise = sleep_inner(duration_ms); + let js_fut = JsFuture::from(promise); + js_fut.await?; + Ok(()) } #[derive(Deserialize)] @@ -304,7 +383,7 @@ pub async fn execute_transaction( gas_budget, )); let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - console_log!("Error executing JsFuture::from(promise): {:?}", e); + console_log!("Error executing JsFuture::from(promise) for `execute_transaction`: {:?}", e); Error::FfiError(format!("{:?}", e)) })?; @@ -313,12 +392,12 @@ pub async fn execute_transaction( #[derive(Deserialize)] #[serde(try_from = "Vec")] -pub struct ProgrammableTransaction(WasmTransactionBuilder); - +pub struct ProgrammableTransaction(pub(crate) WasmTransactionBuilder); impl TryFrom> for ProgrammableTransaction { type Error = TsSdkError; fn try_from(value: Vec) -> Result { - WasmTransactionBuilder::from_bcs_bytes(&value) + let uint8array: Uint8Array = value.as_slice().into(); + WasmTransactionBuilder::from_bcs_bytes(uint8array) .map(Self) .map_err(WasmError::from) .map_err(TsSdkError::from) diff --git a/bindings/wasm/iota_interaction_ts/src/error.rs b/bindings/wasm/iota_interaction_ts/src/error.rs index dc30256a1f..a9b44b8f14 100644 --- a/bindings/wasm/iota_interaction_ts/src/error.rs +++ b/bindings/wasm/iota_interaction_ts/src/error.rs @@ -194,6 +194,10 @@ pub enum TsSdkError { WasmError(String, String), #[error("[TsSdkError] JsSysError: {0}")] JsSysError(String), + #[error("[TsSdkError] TransactionSerializationError: {0}")] + TransactionSerializationError(String), + #[error("[TsSdkError] TransactionSigningError: {0}")] + TransactionSigningError(String, String), } pub type TsSdkResult = core::result::Result; diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index a6317a97c5..fc1d285ef4 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -1,7 +1,10 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use js_sys::Array; +use js_sys::Promise; use js_sys::Uint8Array; +use wasm_bindgen_futures::JsFuture; use std::cell::Cell; use std::collections::HashSet; use wasm_bindgen::prelude::wasm_bindgen; @@ -13,6 +16,7 @@ use crate::bindings::WasmObjectRef; use crate::bindings::WasmSharedObjectRef; use crate::bindings::WasmTransactionArgument; use crate::bindings::WasmTransactionBuilder; +use crate::common::PromiseUint8Array; use crate::error::TsSdkError; use crate::error::WasmError; use crate::transaction_builder::TransactionBuilderTsSdk; @@ -43,10 +47,10 @@ extern "C" { pub(crate) type WasmTxArgumentMap; } -#[wasm_bindgen(module = "move_calls/identity")] +#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/identity")] extern "C" { #[wasm_bindgen(js_name = "create", catch)] - async fn identity_new(did: &[u8], package: &str) -> Result; + fn identity_new(did: &[u8], package: &str) -> Result; #[wasm_bindgen(js_name = "newWithControllers", catch)] async fn identity_new_with_controllers( @@ -473,12 +477,15 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { .map_err(TsSdkError::from) } - fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { + async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { let package = package_id.to_string(); - futures::executor::block_on(identity_new(did_doc, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) + let promise: Promise = Promise::resolve(&identity_new(did_doc, &package).unwrap()); + JsFuture::from(promise) + .await + .map(|v| JsValue::from(Array::from(&v))) + .map_err(WasmError::from) + .and_then(|v| v.into_serde().map_err(WasmError::from)) + .map_err(TsSdkError::from) } fn new_with_controllers( diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index f408665516..060c9df1d9 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -5,8 +5,13 @@ use std::boxed::Box; use std::option::Option; use std::result::Result; +use fastcrypto::hash::Blake2b256; +use identity_iota_interaction::shared_crypto::intent::Intent; +use identity_iota_interaction::shared_crypto::intent::IntentMessage; +use identity_iota_interaction::types::crypto::SignatureScheme; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; +use js_sys::Uint8Array; use secret_storage::Signer; use identity_iota_interaction::error::IotaRpcResult; @@ -31,7 +36,6 @@ use identity_iota_interaction::CoinReadTrait; use identity_iota_interaction::EventTrait; use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::IotaTransactionBlockResponseBcs; use identity_iota_interaction::IotaTransactionBlockResponseT; use identity_iota_interaction::ProgrammableTransactionBcs; use identity_iota_interaction::QuorumDriverTrait; @@ -39,10 +43,15 @@ use identity_iota_interaction::ReadTrait; use identity_iota_interaction::SignatureBcs; use identity_iota_interaction::TransactionDataBcs; +use crate::bindings::add_gas_data_to_transaction; +use crate::bindings::get_transaction_digest; +use crate::bindings::sleep; use crate::bindings::IotaTransactionBlockResponseAdapter; use crate::bindings::ManagedWasmIotaClient; use crate::bindings::WasmIotaClient; use crate::error::TsSdkError; +use crate::error::WasmError; +use crate::ProgrammableTransaction; #[allow(dead_code)] pub trait IotaTransactionBlockResponseAdaptedT: @@ -214,9 +223,9 @@ impl ReadTrait for ReadAdapter { async fn try_get_parsed_past_object( &self, - object_id: ObjectID, - version: SequenceNumber, - options: IotaObjectDataOptions, + _object_id: ObjectID, + _version: SequenceNumber, + _options: IotaObjectDataOptions, ) -> IotaRpcResult { // TODO: does not work anymore, find out, why we need to pass a different `SequenceNumber` now unimplemented!("try_get_parsed_past_object"); @@ -330,18 +339,21 @@ impl IotaClientTrait for IotaClientTsSdk { tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, - ) -> Result { - unimplemented!(); - // let wasm_response = execute_transaction( - // &self.iota_client.clone().0, - // sender_address, - // sender_public_key, - // tx_bcs, - // *signer.clone(), - // gas_budget, - // ) - // .await?; - // Ok(Box::new(IotaTransactionBlockResponseProvider::new(wasm_response))) + ) -> Result>, Self::Error> { + let tx: ProgrammableTransaction = tx_bcs.try_into()?; + let response = self + .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) + .await?; + + // wait a certain amount to time before continuing + // a follow up step was fetching an object created with this tx, which - for some reason - wasn't available yet + // TODO: check timing issues related to transactions finality here + sleep(500) + .await + .map_err(WasmError::from) + .map_err(TsSdkError::from)?; + + Ok(Box::new(response)) } async fn default_gas_budget( @@ -379,4 +391,88 @@ impl IotaClientTsSdk { iota_client: ManagedWasmIotaClient::new(iota_client), }) } + + /// Builds message with `TransactionData` intent, hashes it, and constructs full signature. + async fn get_transaction_signature_bytes>( + signer: &S, + tx_data: &Vec, + sender_public_key: &[u8], + ) -> Result, TsSdkError> { + let digest = get_transaction_digest(tx_data); + + let raw_signature = signer + .sign(&digest.to_vec()) + .await + .map_err(|err| + TsSdkError::TransactionSerializationError(format!("could not sign transaction message; {err}")) + )?; + + let signature_bytes = [ + [SignatureScheme::ED25519.flag()].as_slice(), + &raw_signature, + sender_public_key, + ] + .concat(); + + Ok(signature_bytes) + } + + /// Inserts these values into the transaction and replaces placeholder values. + /// + /// - sender (overwritten as we assume a placeholder to be used in prepared transaction) + /// - gas budget (value determined automatically if not provided) + /// - gas price (value determined automatically) + /// - gas coin / payment object (fetched automatically) + /// - gas owner (equals sender) + /// + /// # Arguments + /// + /// * `iota_client` - client instance + /// * `sender_address` - transaction sender (and the one paying for it) + /// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values + /// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided + async fn replace_transaction_placeholder_values( + &self, + tx: ProgrammableTransaction, + sender_address: IotaAddress, + gas_budget: Option, + ) -> Result, TsSdkError> { + let updated = add_gas_data_to_transaction( + &self.iota_client.0, + sender_address, + tx.0.build().await.unwrap().to_vec(), + gas_budget, + ).await.unwrap(); + + Ok(updated) + } + + // Submit tx to IOTA client, also: + // - ensures, `gas_budget` is set + // - signs tx + // - calls execute_transaction_block to submit tx (with signatures created here) + async fn sdk_execute_transaction>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx: ProgrammableTransaction, + gas_budget: Option, + signer: &S, + ) -> Result { + let final_tx = self.replace_transaction_placeholder_values(tx, sender_address, gas_budget).await?; + let signature = Self::get_transaction_signature_bytes(signer, &final_tx, sender_public_key).await?; + + let wasm_response = self + .quorum_driver_api() + .execute_transaction_block( + &final_tx, + &vec![signature], + Some(IotaTransactionBlockResponseOptions::full_content()), + Some(ExecuteTransactionRequestType::WaitForLocalExecution), + ) + .await.unwrap(); + let native = wasm_response.clone_native_response(); + + Ok(IotaTransactionBlockResponseProvider::new(native)) + } } diff --git a/examples/1_advanced/10_zkp_revocation.rs b/examples/1_advanced/10_zkp_revocation.rs index c9d5534243..31c6c684b7 100644 --- a/examples/1_advanced/10_zkp_revocation.rs +++ b/examples/1_advanced/10_zkp_revocation.rs @@ -77,7 +77,7 @@ async fn create_did( where K: identity_storage::JwkStorage + identity_storage::JwkStorageBbsPlusExt, I: identity_storage::KeyIdStorage, - S: Signer + Sync, + S: Signer + OptionalSync, { // Get the network name. let network_name: &NetworkName = identity_client.network(); diff --git a/examples/1_advanced/9_zkp.rs b/examples/1_advanced/9_zkp.rs index 98b43bce50..351e7ac517 100644 --- a/examples/1_advanced/9_zkp.rs +++ b/examples/1_advanced/9_zkp.rs @@ -51,7 +51,7 @@ pub async fn create_did( where K: identity_storage::JwkStorage + identity_storage::JwkStorageBbsPlusExt, I: identity_storage::KeyIdStorage, - S: Signer + Sync, + S: Signer + OptionalSync, { // Create a new DID document with a placeholder DID. let mut unpublished: IotaDocument = IotaDocument::new(identity_client.network()); diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 50c7ebaec1..229d19c4d9 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -43,7 +43,7 @@ pub async fn create_did_document( where K: identity_storage::JwkStorage, I: identity_storage::KeyIdStorage, - S: Signer + Sync, + S: Signer + OptionalSync, { // Create a new DID document with a placeholder DID. let mut unpublished: IotaDocument = IotaDocument::new(identity_client.network()); diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index bb1156860d..200db1e53b 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -54,7 +54,7 @@ tokio = { version = "1.29.0", default-features = false, optional = true, feature [target.'cfg(target_arch = "wasm32")'.dependencies] identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", default-features = false, optional = true } # Dependency iota_interaction_ts is always used on wasm32 platform. It is not controlled by the "iota-client" feature -# because ut's unclear how to implement this. wasm32 build will most probably always use the "iota-client" feature +# because it's unclear how to implement this. wasm32 build will most probably always use the "iota-client" feature # so this seems to be tolerable for now. iota_interaction_ts = { version = "=1.4.0", path = "../bindings/wasm/iota_interaction_ts" } diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs index 2f94a53596..d8c8fca5b9 100644 --- a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -580,7 +580,7 @@ impl IdentityMoveCalls for IdentityMoveCallsRustSdk { Ok(bcs::to_bytes(&ptb.finish())?) } - fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { + async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { let mut ptb = PrgrTxBuilder::new(); let doc_arg = utils::ptb_pure(&mut ptb, "did_doc", did_doc)?; let clock = utils::get_clock_ref(&mut ptb); diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 95c766eaac..fb2850702d 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -56,6 +56,7 @@ use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::IotaTransactionBlockResponseT; use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::OptionalSync; use identity_iota_interaction::QuorumDriverTrait; use identity_iota_interaction::ReadTrait; use identity_iota_interaction::SignatureBcs; @@ -332,7 +333,7 @@ impl IotaClientTrait for IotaClientRustSdk { }) } - async fn execute_transaction + Sync>( + async fn execute_transaction + OptionalSync>( &self, sender_address: IotaAddress, sender_public_key: &[u8], diff --git a/identity_iota_core/src/rebased/assets/asset.rs b/identity_iota_core/src/rebased/assets/asset.rs index 83901fa4be..a560c76105 100644 --- a/identity_iota_core/src/rebased/assets/asset.rs +++ b/identity_iota_core/src/rebased/assets/asset.rs @@ -27,6 +27,7 @@ use identity_iota_interaction::AssetMoveCalls; use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::MoveType; +use identity_iota_interaction::OptionalSync; use secret_storage::Signer; use serde::de::DeserializeOwned; use serde::Deserialize; @@ -389,7 +390,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let tx = AssetMoveCallsAdapter::update( self.asset.object_ref(client).await?, @@ -430,7 +431,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let asset_ref = self.0.object_ref(client).await?; let tx = AssetMoveCallsAdapter::delete::(asset_ref, client.package_id())?; @@ -457,7 +458,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let AuthenticatedAssetBuilder { inner, @@ -503,7 +504,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let tx = AssetMoveCallsAdapter::transfer::( self.asset.object_ref(client).await?, @@ -556,7 +557,7 @@ impl TransactionInternal for AcceptTransferTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { if self.0.done { return Err(Error::TransactionBuildingFailed( @@ -602,7 +603,7 @@ impl TransactionInternal for ConcludeTransferTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let cap = self.0.get_cap("SenderCap", client).await?; let (asset_ref, param_type) = self diff --git a/identity_iota_core/src/rebased/assets/public_available_vc.rs b/identity_iota_core/src/rebased/assets/public_available_vc.rs index e110b592cf..e09a605083 100644 --- a/identity_iota_core/src/rebased/assets/public_available_vc.rs +++ b/identity_iota_core/src/rebased/assets/public_available_vc.rs @@ -10,6 +10,7 @@ use identity_credential::credential::JwtCredential; use identity_iota_interaction::types::base_types::ObjectID; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::IotaVerifiableCredential; +use identity_iota_interaction::OptionalSync; use identity_jose::jwt::JwtHeader; use identity_jose::jwu; use itertools::Itertools; @@ -55,7 +56,7 @@ impl PublicAvailableVC { /// A new `PublicAvailableVC`. pub async fn new(jwt: Jwt, gas_budget: Option, client: &IdentityClient) -> Result where - S: Signer + Sync, + S: Signer + OptionalSync, { let jwt_bytes = String::from(jwt).into_bytes(); let credential = parse_jwt_credential(&jwt_bytes)?; diff --git a/identity_iota_core/src/rebased/client/full_client.rs b/identity_iota_core/src/rebased/client/full_client.rs index 910f0e99fa..c7973cc9ff 100644 --- a/identity_iota_core/src/rebased/client/full_client.rs +++ b/identity_iota_core/src/rebased/client/full_client.rs @@ -28,6 +28,7 @@ use crate::rebased::Error; use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::MoveType; +use identity_iota_interaction::OptionalSync; use identity_iota_interaction::ProgrammableTransactionBcs; use crate::rebased::transaction::TransactionOutputInternal; @@ -95,7 +96,7 @@ impl Deref for IdentityClient { impl IdentityClient where - S: Signer + Sync, + S: Signer, { /// Create a new [`IdentityClient`]. pub async fn new(client: IdentityClientReadOnly, signer: S) -> Result { @@ -112,7 +113,12 @@ where signer, }) } +} +impl IdentityClient +where + S: Signer + OptionalSync, +{ pub(crate) async fn execute_transaction( &self, tx_bcs: ProgrammableTransactionBcs, @@ -202,7 +208,7 @@ impl IdentityClient { impl IdentityClient where - S: Signer + Sync, + S: Signer + OptionalSync, { /// Returns [`Transaction`] [`PublishDidTx`] that - when executed - will publish a new DID Document on chain. pub fn publish_did_document(&self, document: IotaDocument) -> PublishDidTx { @@ -283,7 +289,7 @@ impl PublishDidTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let TransactionOutputInternal { output: identity, @@ -314,7 +320,7 @@ impl TransactionT for PublishDidTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { Ok( self @@ -331,7 +337,7 @@ impl TransactionT for PublishDidTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { self.execute_publish_did_tx_with_opt_gas(gas_budget, client).await } diff --git a/identity_iota_core/src/rebased/migration/alias.rs b/identity_iota_core/src/rebased/migration/alias.rs index 463286ab78..5226ed5542 100644 --- a/identity_iota_core/src/rebased/migration/alias.rs +++ b/identity_iota_core/src/rebased/migration/alias.rs @@ -24,6 +24,7 @@ use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::MigrationMoveCalls; use identity_iota_interaction::MoveType; +use identity_iota_interaction::OptionalSync; use identity_iota_interaction::ProgrammableTransactionBcs; use super::get_identity; @@ -31,7 +32,7 @@ use super::Identity; use super::OnChainIdentity; /// A legacy IOTA Stardust Output type, used to store DID Documents. -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct UnmigratedAlias { /// The ID of the Alias = hash of the Output ID that created the Alias Output in Stardust. /// This is the AliasID from Stardust. @@ -165,7 +166,7 @@ impl TransactionInternal for MigrateLegacyAliasTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let response = self.0.execute_with_opt_gas_internal(gas_budget, client).await?.response; // Make sure the tx was successful. diff --git a/identity_iota_core/src/rebased/migration/identity.rs b/identity_iota_core/src/rebased/migration/identity.rs index 3448aaa6dd..26317dc57b 100644 --- a/identity_iota_core/src/rebased/migration/identity.rs +++ b/identity_iota_core/src/rebased/migration/identity.rs @@ -33,6 +33,7 @@ use identity_iota_interaction::types::id::UID; use identity_iota_interaction::types::object::Owner; use identity_iota_interaction::types::TypeTag; use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::OptionalSync; use secret_storage::Signer; use serde; use serde::Deserialize; @@ -72,6 +73,7 @@ pub type IdentityData = ( ); /// An on-chain object holding a DID Document. +#[derive(Clone)] pub enum Identity { /// A legacy IOTA Stardust's Identity. Legacy(UnmigratedAlias), @@ -452,7 +454,7 @@ impl TransactionInternal for CreateIdentityTx { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let IdentityBuilder { did_doc, @@ -463,7 +465,7 @@ impl TransactionInternal for CreateIdentityTx { .pack(StateMetadataEncoding::default()) .map_err(|e| Error::DidDocSerialization(e.to_string()))?; let programmable_transaction = if controllers.is_empty() { - IdentityMoveCallsAdapter::new_identity(&did_doc, client.package_id())? + IdentityMoveCallsAdapter::new_identity(&did_doc, client.package_id()).await? } else { let threshold = match threshold { Some(t) => t, diff --git a/identity_iota_core/src/rebased/proposals/borrow.rs b/identity_iota_core/src/rebased/proposals/borrow.rs index 02cb94b97f..be61655026 100644 --- a/identity_iota_core/src/rebased/proposals/borrow.rs +++ b/identity_iota_core/src/rebased/proposals/borrow.rs @@ -24,6 +24,7 @@ use identity_iota_interaction::types::base_types::ObjectID; use identity_iota_interaction::types::transaction::Argument; use identity_iota_interaction::types::TypeTag; use identity_iota_interaction::MoveType; +use identity_iota_interaction::OptionalSync; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; @@ -156,7 +157,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -213,7 +214,7 @@ where _: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let borrow_action = self.into_action(); @@ -274,7 +275,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let Self { identity, diff --git a/identity_iota_core/src/rebased/proposals/controller.rs b/identity_iota_core/src/rebased/proposals/controller.rs index 0296ec8d24..90ddcf8940 100644 --- a/identity_iota_core/src/rebased/proposals/controller.rs +++ b/identity_iota_core/src/rebased/proposals/controller.rs @@ -25,6 +25,7 @@ use identity_iota_interaction::types::base_types::ObjectID; use identity_iota_interaction::types::transaction::Argument; use identity_iota_interaction::types::TypeTag; use identity_iota_interaction::MoveType; +use identity_iota_interaction::OptionalSync; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; @@ -161,7 +162,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -221,7 +222,7 @@ where _: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let controller_execution_action = self.into_action(); @@ -282,7 +283,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let Self { identity, diff --git a/identity_iota_core/src/rebased/proposals/mod.rs b/identity_iota_core/src/rebased/proposals/mod.rs index cfabad0b1b..836b834336 100644 --- a/identity_iota_core/src/rebased/proposals/mod.rs +++ b/identity_iota_core/src/rebased/proposals/mod.rs @@ -29,6 +29,7 @@ use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::IotaTransactionBlockResponseT; use identity_iota_interaction::OptionalSend; +use identity_iota_interaction::OptionalSync; use identity_iota_interaction::ProgrammableTransactionBcs; use crate::rebased::client::IdentityClientReadOnly; @@ -91,7 +92,7 @@ pub trait ProposalT: Sized { client: &IdentityClient, ) -> Result>, Error> where - S: Signer + Sync; + S: Signer + OptionalSync; /// Converts the [`Proposal`] into a transaction that can be executed. async fn into_tx<'i, S>( @@ -100,7 +101,7 @@ pub trait ProposalT: Sized { client: &IdentityClient, ) -> Result where - S: Signer + Sync; + S: Signer + OptionalSync; #[cfg(not(target_arch = "wasm32"))] /// Parses the transaction's effects and returns the output of the [`Proposal`]. @@ -169,7 +170,7 @@ impl<'i, A> ProposalBuilder<'i, A> { ) -> Result>> + use<'i, 'c, S, A>, Error> where Proposal: ProposalT, - S: Signer + Sync, + S: Signer + OptionalSync, A: 'c, 'i: 'c, { @@ -217,7 +218,7 @@ where client: &IdentityClient, ) -> Result>>, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let Self { identity, @@ -286,7 +287,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let Self { identity, tx, .. } = self; let tx_response = client.execute_transaction(tx, gas_budget).await?; @@ -330,7 +331,7 @@ where client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let Self { proposal, identity, .. } = self; let identity_ref = client.get_object_ref_by_id(identity.id()).await?.unwrap(); diff --git a/identity_iota_core/src/rebased/transaction.rs b/identity_iota_core/src/rebased/transaction.rs index 2ba23e2793..e121407979 100644 --- a/identity_iota_core/src/rebased/transaction.rs +++ b/identity_iota_core/src/rebased/transaction.rs @@ -15,6 +15,7 @@ use crate::rebased::client::IdentityClient; use crate::rebased::Error; use async_trait::async_trait; use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::OptionalSync; use identity_iota_interaction::ProgrammableTransactionBcs; use secret_storage::Signer; @@ -45,14 +46,14 @@ pub trait Transaction: Sized { /// Executes this operation using the given `client` and an optional `gas_budget`. /// If no value for `gas_budget` is provided, an estimated value will be used. - async fn execute_with_opt_gas + Sync>( + async fn execute_with_opt_gas + OptionalSync>( self, gas_budget: Option, client: &IdentityClient, ) -> Result, Error>; /// Executes this operation using `client`. - async fn execute + Sync>( + async fn execute + OptionalSync>( self, client: &IdentityClient, ) -> Result, Error> { @@ -60,7 +61,7 @@ pub trait Transaction: Sized { } /// Executes this operation using `client` and a well defined `gas_budget`. - async fn execute_with_gas + Sync>( + async fn execute_with_gas + OptionalSync>( self, gas_budget: u64, client: &IdentityClient, @@ -98,14 +99,14 @@ impl From> for TransactionOutput { pub trait TransactionInternal: Sized { type Output; - async fn execute_with_opt_gas_internal + Sync>( + async fn execute_with_opt_gas_internal + OptionalSync,>( self, gas_budget: Option, client: &IdentityClient, ) -> Result, Error>; #[cfg(target_arch = "wasm32")] - async fn execute + Sync>( + async fn execute>( self, client: &IdentityClient, ) -> Result, Error> { @@ -113,7 +114,7 @@ pub trait TransactionInternal: Sized { } #[cfg(target_arch = "wasm32")] - async fn execute_with_gas + Sync>( + async fn execute_with_gas + OptionalSync>( self, gas_budget: u64, client: &IdentityClient, @@ -127,7 +128,7 @@ pub trait TransactionInternal: Sized { impl + Send, O> Transaction for T { type Output = O; - async fn execute_with_opt_gas + Sync>( + async fn execute_with_opt_gas + OptionalSync>( self, gas_budget: Option, client: &IdentityClient, @@ -147,7 +148,7 @@ impl TransactionInternal for ProgrammableTransaction { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let tx_bcs = bcs::to_bytes(&self)?; let response = client.execute_transaction(tx_bcs, gas_budget).await?; @@ -165,7 +166,7 @@ impl TransactionInternal for ProgrammableTransaction { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { unimplemented!("TransactionInternal::execute_with_opt_gas_internal for ProgrammableTransaction"); } @@ -176,7 +177,7 @@ impl TransactionInternal for ProgrammableTransaction { impl TransactionInternal for ProgrammableTransactionBcs { type Output = (); - async fn execute_with_opt_gas_internal + Sync>( + async fn execute_with_opt_gas_internal + OptionalSync>( self, gas_budget: Option, client: &IdentityClient, diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index 0edb20502c..6150f55319 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -288,7 +288,7 @@ impl TestClient { pub async fn get_test_coin(recipient: IotaAddress, client: &IdentityClient) -> anyhow::Result where - S: Signer + Sync, + S: Signer + OptionalSync, { let mut ptb = ProgrammableTransactionBuilder::new(); let coin = ptb.programmable_move_call( diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs index 69d177e55c..ed812c4b64 100644 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -26,7 +26,7 @@ use crate::ProgrammableTransactionBcs; use crate::SignatureBcs; use crate::TransactionDataBcs; use async_trait::async_trait; -use secret_storage::SignatureScheme; +use secret_storage::SignatureScheme as SignatureSchemeSecretStorage; use secret_storage::Signer; use std::boxed::Box; use std::option::Option; @@ -35,12 +35,15 @@ use std::result::Result; #[cfg(not(target_arch = "wasm32"))] use std::marker::Send; +#[cfg(feature = "send-sync-transaction")] +use crate::OptionalSync; + pub struct IotaKeySignature { pub public_key: Vec, pub signature: Vec, } -impl SignatureScheme for IotaKeySignature { +impl SignatureSchemeSecretStorage for IotaKeySignature { type PublicKey = Vec; type Signature = Vec; } @@ -239,7 +242,7 @@ pub trait IotaClientTrait { Self::Error, >; #[cfg(feature = "send-sync-transaction")] - async fn execute_transaction + Sync>( + async fn execute_transaction + OptionalSync>( &self, sender_address: IotaAddress, sender_public_key: &[u8], diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_interaction/src/move_call_traits.rs index 8eb839ece0..939f4a1b1b 100644 --- a/identity_iota_interaction/src/move_call_traits.rs +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -172,7 +172,7 @@ pub trait IdentityMoveCalls { where F: ControllerIntentFnInternalT; - fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result; + async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result; fn new_with_controllers>( did_doc: &[u8], From 6cd3c40fdd6fe6e49bc965d71b1bf384cd8958ee Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Wed, 5 Feb 2025 15:56:38 +0100 Subject: [PATCH 20/63] fix smaller TODOs and unwraps --- bindings/wasm/identity_wasm/Cargo.toml | 2 +- .../examples/src/0_basic/-1_test_api_call.ts | 54 +++++++------------ .../identity_wasm/examples/src/utils_alpha.ts | 10 ++-- bindings/wasm/identity_wasm/package.json | 2 +- .../identity_wasm/src/kinesis/identity.rs | 10 ++-- .../src/kinesis/wasm_identity_client.rs | 6 ++- .../kinesis/wasm_identity_client_read_only.rs | 4 +- .../src/storage/wasm_transaction_signer.rs | 20 ++++--- .../lib/move_calls/identity/create.ts | 15 ++---- .../lib/move_calls/utils.ts | 21 +++++++- .../src/bindings/wasm_types.rs | 49 +++++++++-------- .../src/identity_move_calls.rs | 3 +- .../src/iota_client_ts_sdk.rs | 8 ++- .../src/rebased/client/read_only.rs | 13 ++++- identity_iota_core/tests/e2e/common.rs | 1 + 15 files changed, 122 insertions(+), 96 deletions(-) diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 522615e5c1..ce557aca9d 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -16,6 +16,7 @@ description = "Web Assembly bindings for the identity-rs crate." crate-type = ["cdylib", "rlib"] [dependencies] +anyhow = "1.0.95" async-trait = { version = "0.1", default-features = false } console_error_panic_hook = { version = "0.1" } fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } @@ -36,7 +37,6 @@ tokio = { version = "=1.39.2", default-features = false, features = ["sync"] } tsify = "0.4.5" wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } -anyhow = "1.0.95" [dependencies.identity_iota] path = "../../../identity_iota" diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index e460f31726..72adf75f12 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -25,7 +25,7 @@ import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BUDGET } from "../utils_alpha"; async function initializeClients() { - const kinesis_client = new KinesisClient({ url: NETWORK_URL }); + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); console.log("---------------- Preparing IdentityClient ------------------------"); const VALID_SECP256K1_SECRET_KEY = [ @@ -62,26 +62,12 @@ async function initializeClients() { 136, 28, ]; - const secret_key = new Uint8Array(VALID_SECP256K1_SECRET_KEY); - let key_pair = Ed25519Keypair.fromSecretKey(secret_key); - let pub_key = key_pair.getPublicKey(); - console.log(`Created Ed25519Keypair with PublicKey ${pub_key.toBase64()} and address ${pub_key.toIotaAddress()}`); - - // delete later if not required anymore - // try to find package beforehand - // "MoveEventType":"0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d::migration_registry::MigrationRegistryCreated" - // "Sender": "0xd40005ab355d8342fa6b94e9638a1040483d70430720d28e9b425283d011c0a8" - const eventsQuery: QueryEventsParams = { - "query": { - "MoveEventType":`${IDENTITY_IOTA_PACKAGE_ID}::migration_registry::MigrationRegistryCreated` - }, - "limit":1, - "order":"ascending" - }; - const eventsResult = await kinesis_client.queryEvents(eventsQuery); - console.dir(eventsResult); - - const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesis_client, IDENTITY_IOTA_PACKAGE_ID); + const secretKey = new Uint8Array(VALID_SECP256K1_SECRET_KEY); + let keyPair = Ed25519Keypair.fromSecretKey(secretKey); + let pubKey = keyPair.getPublicKey(); + console.log(`Created Ed25519Keypair with PublicKey ${pubKey.toBase64()} and address ${pubKey.toIotaAddress()}`); + + const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesisClient, IDENTITY_IOTA_PACKAGE_ID); // create new storage const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); @@ -103,14 +89,14 @@ async function initializeClients() { recipient: identityClient.senderAddress(), }); - const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + const balance = await kinesisClient.getBalance({ owner: identityClient.senderAddress() }); if (balance.totalBalance === "0") { throw new Error("Balance is still 0"); } else { console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); } - return { kinesis_client, identityClient, key_pair }; + return { kinesisClient, identityClient, keyPair }; } @@ -135,7 +121,7 @@ async function testIdentityClientReadOnly() { async function testIdentityClient( identityClient: KinesisIdentityClient, - kinesis_client: KinesisClient, + kinesisClient: KinesisClient, ): Promise { console.log("\n-------------- Start testIdentityClient -------------------------------"); console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); @@ -155,7 +141,7 @@ async function testIdentityClient( recipient: identityClient.senderAddress(), }); - const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + const balance = await kinesisClient.getBalance({ owner: identityClient.senderAddress() }); if (balance.totalBalance === "0") { throw new Error("Balance is still 0"); } else { @@ -308,7 +294,7 @@ async function signerTest(): Promise { console.dir({ signed }); } -async function testExecuteTransaction(kinesis_client: KinesisClient) { +async function testExecuteTransaction(kinesisClient: KinesisClient) { console.log("---------------- testing executeTransaction ------------------------"); // create new storage @@ -333,23 +319,23 @@ async function testExecuteTransaction(kinesis_client: KinesisClient) { }); // try to craft tx with js api - let coins = await kinesis_client.getCoins({ + let coins = await kinesisClient.getCoins({ owner: address, coinType: IOTA_TYPE_ARG, }); const tx = new Transaction(); - const coin_0 = coins.data[0]; - const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ + const coin0 = coins.data[0]; + const coin = tx.splitCoins(tx.object(coin0.coinObjectId), [ bcs.u64().serialize(TEST_GAS_BUDGET * 2), ]); tx.transferObjects([coin], address); tx.setSenderIfNotSet(address); let response = await executeTransaction( - kinesis_client, + kinesisClient, address, publicJwk, - await tx.build({ client: kinesis_client }), + await tx.build({ client: kinesisClient }), signer, ); console.dir(response); @@ -358,7 +344,7 @@ async function testExecuteTransaction(kinesis_client: KinesisClient) { /** Test API usage */ export async function testApiCall(): Promise { - const { kinesis_client, identityClient } = await initializeClients(); + const { kinesisClient, identityClient } = await initializeClients(); try { await testIdentityClientReadOnly(); @@ -368,7 +354,7 @@ export async function testApiCall(): Promise { } try { - await testIdentityClient(identityClient, kinesis_client); + await testIdentityClient(identityClient, kinesisClient); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`identity client binding test failed: ${suffix}`); @@ -396,7 +382,7 @@ export async function testApiCall(): Promise { } try { - await testExecuteTransaction(kinesis_client); + await testExecuteTransaction(kinesisClient); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`signer binding test failed: ${suffix}`); diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts index f4c4346f3e..412da71765 100644 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -30,7 +30,7 @@ export async function createDocumentForNetwork(storage: Storage, network: string // Create a new DID document with a placeholder DID. const unpublished = new IotaDocument(network); - const verification_method_fragment = await unpublished.generateMethod( + const verificationMethodFragment = await unpublished.generateMethod( storage, JwkMemStore.ed25519KeyType(), JwsAlgorithm.EdDSA, @@ -38,7 +38,7 @@ export async function createDocumentForNetwork(storage: Storage, network: string MethodScope.VerificationMethod(), ); - return [unpublished, verification_method_fragment]; + return [unpublished, verificationMethodFragment]; } export async function getClientAndCreateAccount(storage: Storage): Promise { @@ -79,12 +79,12 @@ export async function getClientAndCreateAccount(storage: Storage): Promise { - let tx = identity_client + let tx = identityClient .publishDidDocument(unpublished); - let txOutput = await tx.execute(identity_client); + let txOutput = await tx.execute(identityClient); return txOutput.output; } diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 55bdc95416..1c2e6439b0 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -10,7 +10,7 @@ "example": "examples" }, "scripts": { - "build:src": "cargo lbuild --lib --release --target wasm32-unknown-unknown", + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/identity.rs index daf522c984..b32ff1be0f 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/identity.rs @@ -173,10 +173,12 @@ pub struct WasmIdentityBuilder(pub(crate) IdentityBuilder); #[wasm_bindgen(js_class = IdentityBuilder)] impl WasmIdentityBuilder { #[wasm_bindgen(constructor)] - pub fn new(did_doc: &WasmIotaDocument) -> WasmIdentityBuilder { + pub fn new(did_doc: &WasmIotaDocument) -> Result { let document: IotaDocument = did_doc.0.try_read().unwrap().clone(); - WasmIdentityBuilder( - IdentityBuilder::new(document) + Ok( + WasmIdentityBuilder( + IdentityBuilder::new(document) + ) ) } @@ -226,7 +228,7 @@ pub struct WasmCreateIdentityTx(pub(crate) CreateIdentityTx); impl WasmCreateIdentityTx { #[wasm_bindgen(js_name = execute)] pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { - let output = self.0.execute(&client.0).await.unwrap(); + let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputInternalOnChainIdentity(output)) } } diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index ff3eed9ca2..db6718c6a6 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -22,6 +22,7 @@ use super::IdentityContainer; use super::WasmIdentityBuilder; use super::WasmKinesisIdentityClientReadOnly; +use crate::error::wasm_error; use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; @@ -75,8 +76,9 @@ impl WasmKinesisIdentityClient { } #[wasm_bindgen(js_name = createIdentity)] - pub fn create_identity(&self, iota_document: &WasmIotaDocument) -> WasmIdentityBuilder { + pub fn create_identity(&self, iota_document: &WasmIotaDocument) -> Result { WasmIdentityBuilder::new(iota_document) + .map_err(|err| JsError::new(&format!("failed to initialize new identity builder; {err:?}"))) } #[wasm_bindgen(js_name = getIdentity)] @@ -181,7 +183,7 @@ pub struct WasmPublishDidTx(pub(crate) PublishDidTx); impl WasmPublishDidTx { #[wasm_bindgen(js_name = execute)] pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { - let output = self.0.execute(&client.0).await.unwrap(); + let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputPublishDid(output)) } } diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index 48047752fd..25f34d8513 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -100,7 +100,9 @@ impl WasmKinesisIdentityClientReadOnly { #[wasm_bindgen(js_name = getIdentity)] pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { - let inner_value = self.0.get_identity(object_id.parse()?).await.unwrap(); + let inner_value = self.0.get_identity(object_id.parse()?) + .await + .map_err(|err| JsError::new(&format!("failed to resolve identity by object id; {err:?}")))?; Ok(IdentityContainer(inner_value)) } } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index f8dab34980..8e66af1661 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -40,15 +40,23 @@ impl Signer for WasmTransactionSigner { type KeyId = String; async fn sign(&self, data: &[u8]) -> std::result::Result, SecretStorageError> { - self.sign(data).await.map(|v| v.to_vec()).map_err(|err| SecretStorageError::Other( - anyhow::anyhow!(err.as_string().unwrap_or_else(|| "could not sign data".to_string())) - )) + self.sign(data).await.map(|v| v.to_vec()).map_err(|err| { + let details = err.as_string() + .map(|v| format!("; {}", v)) + .unwrap_or_default(); + let message = format!("could not sign data{details}"); + SecretStorageError::Other(anyhow::anyhow!(message)) + }) } async fn public_key(&self) -> std::result::Result, SecretStorageError> { - self.public_key().await.map(|v| v.to_vec()).map_err(|err| SecretStorageError::Other( - anyhow::anyhow!(err.as_string().unwrap_or_else(|| "could not get public key".to_string())) - )) + self.public_key().await.map(|v| v.to_vec()).map_err(|err| { + let details = err.as_string() + .map(|v| format!("; {}", v)) + .unwrap_or_default(); + let message = format!("could not get public key{details}"); + SecretStorageError::KeyNotFound(message) + }) } fn key_id(&self) -> &String { diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts index f45946e87b..a72fbf5aaf 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts @@ -2,11 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { Transaction } from "@iota/iota.js/transactions"; -import { getClockRef } from "../utils"; +import { getClockRef, insertPlaceholders } from "../utils"; -// TODO: check going back to Uint8Array -// export async function create(didDoc: Uint8Array, packageId: string): Promise { -export async function create(didDoc: Uint8Array, packageId: string): Promise { +export async function create(didDoc: Uint8Array, packageId: string): Promise { const tx = new Transaction(); const didDocArg = tx.pure.vector("u8", didDoc); const clock = getClockRef(tx); @@ -16,14 +14,9 @@ export async function create(didDoc: Uint8Array, packageId: string): Promise Result; - #[wasm_bindgen(js_name = "setSender", method, catch)] - pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + // TODO: decide if we need the following functions: "yagni" or not? + + // #[wasm_bindgen(js_name = "setSender", method, catch)] + // pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - #[wasm_bindgen(js_name = "setGasOwner", method, catch)] - pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + // #[wasm_bindgen(js_name = "setGasOwner", method, catch)] + // pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - #[wasm_bindgen(js_name = "setGasPrice", method, catch)] - pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; + // #[wasm_bindgen(js_name = "setGasPrice", method, catch)] + // pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; - #[wasm_bindgen(js_name = "setGasPayment", method, catch)] - pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; + // #[wasm_bindgen(js_name = "setGasPayment", method, catch)] + // pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; - #[wasm_bindgen(js_name = "setGasBudget", method, catch)] - pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; + // #[wasm_bindgen(js_name = "setGasBudget", method, catch)] + // pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; - #[wasm_bindgen(js_name = "getData", method, catch)] - pub fn get_data(this: &WasmTransactionBuilder) -> Result; + // #[wasm_bindgen(js_name = "getData", method, catch)] + // pub fn get_data(this: &WasmTransactionBuilder) -> Result; } impl From for WasmObjectRef { @@ -317,7 +312,7 @@ pub(crate) async fn add_gas_data_to_transaction( sender_address: IotaAddress, tx_bcs: Vec, gas_budget: Option, -) -> Result, Error> { +) -> Result, TsSdkError> { let promise: Promise = Promise::resolve(&add_gas_data_to_transaction_inner( iota_client, sender_address.to_string(), @@ -325,8 +320,10 @@ pub(crate) async fn add_gas_data_to_transaction( gas_budget, )); let value: JsValue = JsFuture::from(promise).await.map_err(|e| { - console_log!("Error executing JsFuture::from(promise) for `add_gas_data_to_transaction`: {:?}", e); - Error::FfiError(format!("{:?}", e)) + let message = "Error executing JsFuture::from(promise) for `add_gas_data_to_transaction`"; + let details = format!("{e:?}"); + console_log!("{message}; {details}"); + TsSdkError::WasmError(message.to_string(), details.to_string()) })?; Ok(Uint8Array::new(&value).to_vec()) @@ -373,7 +370,7 @@ pub async fn execute_transaction( tx_bcs: ProgrammableTransactionBcs, // --> Binding: Vec signer: WasmStorageSigner, // --> Binding: WasmStorageSigner gas_budget: Option, // --> Binding: Option, -) -> Result { +) -> Result { let promise: Promise = Promise::resolve(&execute_transaction_inner( iota_client, sender_address.to_string(), @@ -383,8 +380,10 @@ pub async fn execute_transaction( gas_budget, )); let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - console_log!("Error executing JsFuture::from(promise) for `execute_transaction`: {:?}", e); - Error::FfiError(format!("{:?}", e)) + let message = "Error executing JsFuture::from(promise) for `execute_transaction`"; + let details = format!("{e:?}"); + console_log!("{message}; {details}"); + TsSdkError::WasmError(message.to_string(), details.to_string()) })?; Ok(IotaTransactionBlockResponseAdapter::new(result.into())) diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index fc1d285ef4..b4c1ef4146 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -479,7 +479,8 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { let package = package_id.to_string(); - let promise: Promise = Promise::resolve(&identity_new(did_doc, &package).unwrap()); + let array_promise = identity_new(did_doc, &package).map_err(WasmError::from)?; + let promise: Promise = Promise::resolve(&array_promise); JsFuture::from(promise) .await .map(|v| JsValue::from(Array::from(&v))) diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index 060c9df1d9..16e0e822c9 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -437,12 +437,16 @@ impl IotaClientTsSdk { sender_address: IotaAddress, gas_budget: Option, ) -> Result, TsSdkError> { + let tx_bcs = tx.0.build() + .await + .map_err(WasmError::from)? + .to_vec(); let updated = add_gas_data_to_transaction( &self.iota_client.0, sender_address, - tx.0.build().await.unwrap().to_vec(), + tx_bcs, gas_budget, - ).await.unwrap(); + ).await?; Ok(updated) } diff --git a/identity_iota_core/src/rebased/client/read_only.rs b/identity_iota_core/src/rebased/client/read_only.rs index 7902d0d6b6..5464415c52 100644 --- a/identity_iota_core/src/rebased/client/read_only.rs +++ b/identity_iota_core/src/rebased/client/read_only.rs @@ -84,7 +84,15 @@ impl IdentityClientReadOnly { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - // TODO: Define fn new() for wasm32 platforms + /// Attempts to create a new [`IdentityClientReadOnly`] from a given [`IotaClient`]. + /// + /// # Failures + /// This function fails if the provided `iota_client` is connected to an unrecognized + /// network. + /// + /// # Notes + /// When trying to connect to a local or unofficial network prefer using + /// [`IdentityClientReadOnly::new_with_pkg_id`]. pub async fn new(iota_client: WasmIotaClient) -> Result { Self::new_internal(IotaClientAdapter::new(iota_client)?).await } @@ -126,7 +134,8 @@ impl IdentityClientReadOnly { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - // TODO: Define fn new() for wasm32 platforms + /// Attempts to create a new [`IdentityClientReadOnly`] from + /// the given [`IotaClient`]. pub async fn new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: ObjectID) -> Result { Self::new_with_pkg_id_internal( IotaClientAdapter::new(iota_client)?, diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index 6150f55319..df2f43a8c3 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -10,6 +10,7 @@ use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::rebased::utils::request_funds; use identity_iota_core::IotaDID; use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::OptionalSync; use identity_jose::jwk::Jwk; use identity_jose::jws::JwsAlgorithm; use identity_storage::JwkMemStore; From c5b952f6c2daf2519cd3355fff21655bd1fcb02f Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Thu, 6 Feb 2025 13:20:24 +0100 Subject: [PATCH 21/63] add update did example impl --- .../examples/src/0_basic/-1_test_api_call.ts | 2 +- .../examples/src/0_basic/0_create_did.ts | 4 +- .../examples/src/0_basic/1_update_did.ts | 70 +++++++++++-------- .../wasm/identity_wasm/examples/src/main.ts | 6 +- .../identity_wasm/examples/src/utils_alpha.ts | 2 +- .../src/kinesis/wasm_identity_client.rs | 40 +++++------ .../lib/move_calls/identity/update.ts | 9 ++- .../src/bindings/wasm_types.rs | 5 +- .../src/identity_move_calls.rs | 33 ++++++--- .../identity_move_calls.rs | 2 +- .../src/rebased/proposals/update_did_doc.rs | 1 + .../src/move_call_traits.rs | 2 +- 12 files changed, 107 insertions(+), 69 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index 72adf75f12..be5eb3d527 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -326,7 +326,7 @@ async function testExecuteTransaction(kinesisClient: KinesisClient) { const tx = new Transaction(); const coin0 = coins.data[0]; const coin = tx.splitCoins(tx.object(coin0.coinObjectId), [ - bcs.u64().serialize(TEST_GAS_BUDGET * 2), + bcs.u64().serialize(TEST_GAS_BUDGET * BigInt(2)), ]); tx.transferObjects([coin], address); tx.setSenderIfNotSet(address); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 00251c23b0..bf266a67ea 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -12,15 +12,15 @@ import { /** Demonstrate how to create a DID Document and publish it. */ export async function createIdentity(): Promise { - // figure out a better way to organize those calls >.> + // create new client to connect to IOTA network const kinesisClient = new KinesisClient({ url: NETWORK_URL }); const network = await kinesisClient.getChainIdentifier(); - // create new client to interact with chain and get funded account with keys const storage = getMemstorage(); // TODO: check if we can update storage implementation to a non-owning variant // order is important here as wrapped storage will be set to a null pointer after passing it to a client const [unpublished] = await createDocumentForNetwork(storage, network); + // create new client that offers identity related functions const identityClient = await getClientAndCreateAccount(storage); console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index c27d889b63..57817a2bb4 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { + IotaDID, JwkMemStore, JwsAlgorithm, MethodRelationship, @@ -10,40 +11,55 @@ import { Timestamp, VerificationMethod, } from "@iota/identity-wasm/node"; - +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; import { - createDidDocument, + createDocumentForNetwork, getClientAndCreateAccount, getMemstorage, + NETWORK_URL, TEST_GAS_BUDGET, -} from "../utils_alpha"; +} from '../utils_alpha'; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { - // create new client to interact with chain and get funded account with keys + // create new clients and create new account + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + const network = await kinesisClient.getChainIdentifier(); const storage = getMemstorage(); + const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); - - // create new DID document and publish it - let [document, vmFragment1] = await createDidDocument(identityClient, storage); - let did = document.id(); + + // create new identity for this account and publish document for it + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); // Resolve the latest state of the document. // Technically this is equivalent to the document above. - document = await identityClient.resolveDid(did); + const resolved = await identityClient.resolveDid(did); - // Insert a new Ed25519 verification method in the DID document. - await document.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - "#key-2", - MethodScope.VerificationMethod(), - ); + if ((storage as any).__wbg_ptr === 0) { + console.log('cannot re-use storage, skipping generating new method'); + } else { + console.log('can re-use storage, generating new method'); + // Insert a new Ed25519 verification method in the DID document. + await resolved.generateMethod( + storage, + JwkMemStore.ed25519KeyType(), + JwsAlgorithm.EdDSA, + "#key-2", + MethodScope.VerificationMethod(), + ); - // Attach a new method relationship to the inserted method. - document.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); + // Attach a new method relationship to the inserted method. + resolved.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); + // Remove a verification method. + let originalMethod = resolved.resolveMethod(vmFragment1) as VerificationMethod; + await resolved.purgeMethod(storage, originalMethod?.id()); + } // Add a new Service. const service: Service = new Service({ @@ -51,14 +67,12 @@ export async function updateIdentity() { type: "LinkedDomains", serviceEndpoint: "https://iota.org/", }); - document.insertService(service); - document.setMetadataUpdated(Timestamp.nowUTC()); - - // Remove a verification method. - let originalMethod = document.resolveMethod(vmFragment1) as VerificationMethod; - await document.purgeMethod(storage, originalMethod?.id()); + resolved.insertService(service); + resolved.setMetadataUpdated(Timestamp.nowUTC()); - let updated = identityClient - .publishDidDocumentUpdate(document.clone(), TEST_GAS_BUDGET); - console.log(`Updated DID document result: ${JSON.stringify(updated, null, 2)}`); + let updated = await identityClient + .publishDidDocumentUpdate(resolved.clone(), TEST_GAS_BUDGET); + // and resolve again to make sure we're looking at the onchain information + const resolvedAgain = await identityClient.resolveDid(did); + console.log(`Updated DID document result: ${JSON.stringify(resolvedAgain, null, 2)}`); } diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts index b1e6ab528c..36d2dc60f0 100644 --- a/bindings/wasm/identity_wasm/examples/src/main.ts +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -3,7 +3,7 @@ import { testApiCall } from "./0_basic/-1_test_api_call"; import { createIdentity } from "./0_basic/0_create_did"; -// import { updateIdentity } from "./0_basic/1_update_did"; +import { updateIdentity } from "./0_basic/1_update_did"; // import { resolveIdentity } from "./0_basic/2_resolve_did"; // import { deactivateIdentity } from "./0_basic/3_deactivate_did"; // import { deleteIdentity } from "./0_basic/4_delete_did"; @@ -33,8 +33,8 @@ async function main() { return await testApiCall(); case "0_create_did": return await createIdentity(); - // case "1_update_did": - // return await updateIdentity(); + case "1_update_did": + return await updateIdentity(); // case "2_resolve_did": // return await resolveIdentity(); // case "3_deactivate_did": diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts index 412da71765..e5bc1d65ba 100644 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -20,7 +20,7 @@ export const IDENTITY_IOTA_PACKAGE_ID = export const NETWORK_NAME_FAUCET = "localnet"; export const NETWORK_URL = process.env.NETWORK_URL || "http://127.0.0.1:9000"; - export const TEST_GAS_BUDGET = 50_000_000; +export const TEST_GAS_BUDGET = BigInt(50_000_000); export function getMemstorage(): Storage { return new Storage(new JwkMemStore(), new KeyIdMemStore()); diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index db6718c6a6..0342fa6f04 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -118,29 +118,27 @@ impl WasmKinesisIdentityClient { .publish_did_document(doc))) } - // not included in any e2e test anymore, so let's skip it for now + #[wasm_bindgen(js_name = publishDidDocumentUpdate)] + pub async fn publish_did_document_update( + &self, + document: &WasmIotaDocument, + gas_budget: u64, + ) -> Result { + let doc: IotaDocument = document + .0 + .try_read() + .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + .clone(); + let document = self + .0 + .publish_did_document_update(doc, gas_budget) + .await + .map_err(>::into)?; - // TODO: re-add WasmKinesisIdentityClient::publish_did_document_update - // #[wasm_bindgen(js_name = publishDidDocumentUpdate)] - // pub async fn publish_did_document_update( - // &self, - // document: &WasmIotaDocument, - // gas_budget: u64, - // signer: &WasmStorageSigner, - // ) -> Result { - // let doc: IotaDocument = document - // .0 - // .try_read() - // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - // .clone(); - // let document = self - // .0 - // .publish_did_document_update(doc, gas_budget) - // .await - // .map_err(>::into)?; + Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + } - // Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) - // } + // not included in any e2e test anymore, so let's skip it for now // TODO: re-add WasmKinesisIdentityClient::deactivate_did_output // #[wasm_bindgen(js_name = deactivateDidOutput)] diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts index 76b3558013..a72983d4f6 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts @@ -3,7 +3,12 @@ import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; -import { getClockRef, getControllerDelegation, putBackDelegationToken } from "../utils"; +import { + getClockRef, + getControllerDelegation, + insertPlaceholders, + putBackDelegationToken, +} from "../utils"; export function proposeUpdate( identity: SharedObjectRef, @@ -27,6 +32,8 @@ export function proposeUpdate( putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + insertPlaceholders(tx); + return tx.build(); } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index faa3c06db6..ce031883a2 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -12,6 +12,7 @@ use identity_iota_interaction::types::object::Owner; use identity_iota_interaction::ProgrammableTransactionBcs; use js_sys::Promise; use js_sys::Uint8Array; +use serde::Serialize; use serde::Deserialize; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; @@ -192,7 +193,7 @@ impl From for WasmObjectRef { "digest": value.2, }); - serde_wasm_bindgen::to_value(&json_obj) + json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS ObjectRef's interface. .unchecked_into() @@ -207,7 +208,7 @@ impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { "mutable": value.2, }); - serde_wasm_bindgen::to_value(&json_obj) + json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS SharedObjectRef's interface. .unchecked_into() diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index b4c1ef4146..4ecae377b8 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -120,13 +120,13 @@ extern "C" { ) -> Result; #[wasm_bindgen(js_name = "proposeUpdate", catch)] - async fn propose_update( + fn propose_update( identity: WasmSharedObjectRef, capability: WasmObjectRef, did_doc: &[u8], package: &str, expiration: Option, - ) -> Result; + ) -> Result; #[wasm_bindgen(js_name = "executeUpdate", catch)] async fn execute_update( @@ -479,6 +479,7 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { let package = package_id.to_string(); + let array_promise = identity_new(did_doc, &package).map_err(WasmError::from)?; let promise: Promise = Promise::resolve(&array_promise); JsFuture::from(promise) @@ -629,7 +630,7 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { .map_err(TsSdkError::from) } - fn propose_update( + async fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, did_doc: impl AsRef<[u8]>, @@ -637,20 +638,36 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { package_id: ObjectID, ) -> Result { let identity = identity.try_into()?; + // panic!("controller cap {capability:?}"); let controller_cap = capability.into(); let did_doc = did_doc.as_ref(); let package_id = package_id.to_string(); - futures::executor::block_on(propose_update( + let array_promise = propose_update( identity, controller_cap, did_doc, &package_id, expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) + ).map_err(WasmError::from)?; + let promise: Promise = Promise::resolve(&array_promise); + JsFuture::from(promise) + .await + .map(|v| JsValue::from(Array::from(&v))) + .map_err(WasmError::from) + .and_then(|v| v.into_serde().map_err(WasmError::from)) + .map_err(TsSdkError::from) + + // futures::executor::block_on(propose_update( + // identity, + // controller_cap, + // did_doc, + // &package_id, + // expiration, + // )) + // .map(|js_arr| js_arr.to_vec()) + // .map_err(WasmError::from) + // .map_err(TsSdkError::from) } fn execute_update( diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs index d8c8fca5b9..f73ded4e9c 100644 --- a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -796,7 +796,7 @@ impl IdentityMoveCalls for IdentityMoveCallsRustSdk { Ok(bcs::to_bytes(&ptb.finish())?) } - fn propose_update( + async fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, did_doc: impl AsRef<[u8]>, diff --git a/identity_iota_core/src/rebased/proposals/update_did_doc.rs b/identity_iota_core/src/rebased/proposals/update_did_doc.rs index 3e6ed7fd93..9da6da0000 100644 --- a/identity_iota_core/src/rebased/proposals/update_did_doc.rs +++ b/identity_iota_core/src/rebased/proposals/update_did_doc.rs @@ -82,6 +82,7 @@ impl ProposalT for Proposal { expiration, client.package_id(), ) + .await .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(CreateProposalTx { diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_interaction/src/move_call_traits.rs index 939f4a1b1b..3085a512d7 100644 --- a/identity_iota_interaction/src/move_call_traits.rs +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -218,7 +218,7 @@ pub trait IdentityMoveCalls { package: ObjectID, ) -> Result; - fn propose_update( + async fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, did_doc: impl AsRef<[u8]>, From 6e23af121feaafa2a40978c6daf35e4c910f2bbf Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 09:49:59 +0100 Subject: [PATCH 22/63] add example to deactivate a did --- .../examples/src/0_basic/3_deactivate_did.ts | 89 +++++++------------ .../wasm/identity_wasm/examples/src/main.ts | 6 +- .../src/kinesis/wasm_identity_client.rs | 32 +++---- .../lib/move_calls/identity/deactivate.ts | 4 +- .../src/identity_move_calls.rs | 36 ++++---- examples/utils/utils.rs | 1 + .../identity_move_calls.rs | 2 +- .../src/rebased/proposals/deactivate_did.rs | 1 + .../src/move_call_traits.rs | 2 +- 9 files changed, 75 insertions(+), 98 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 93fef4926a..0dde675de0 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -1,78 +1,57 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDocument, IotaIdentityClient, JwkMemStore, KeyIdMemStore, Storage } from "@iota/identity-wasm/node"; -import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaDID } from "@iota/identity-wasm/node"; +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { + createDocumentForNetwork, + getClientAndCreateAccount, + getMemstorage, + NETWORK_URL, + TEST_GAS_BUDGET, +} from '../utils_alpha'; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - const did = document.id(); - - // Resolve the latest state of the DID document, so we can reactivate it later. + // create new clients and create new account + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + const network = await kinesisClient.getChainIdentifier(); + const storage = getMemstorage(); + const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); + const identityClient = await getClientAndCreateAccount(storage); + + // create new identity for this account and publish document for it + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + + // Resolve the latest state of the document. // Technically this is equivalent to the document above. - document = await didClient.resolveDid(did); + const resolved = await identityClient.resolveDid(did); + console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); // Deactivate the DID by publishing an empty document. - // This process can be reversed since the Alias Output is not destroyed. - // Deactivation may only be performed by the state controller of the Alias Output. - let deactivatedOutput: AliasOutput = await didClient.deactivateDidOutput(did); - - // Optional: reduce and reclaim the storage deposit, sending the tokens to the state controller. - const rentStructure: IRent = await didClient.getRentStructure(); - - deactivatedOutput = await client.buildAliasOutput({ - ...deactivatedOutput, - amount: Utils.computeStorageDeposit(deactivatedOutput, rentStructure), - aliasId: deactivatedOutput.getAliasId(), - unlockConditions: deactivatedOutput.getUnlockConditions(), - }); - - // Publish the deactivated DID document. - await didClient.publishDidOutput(secretManager, deactivatedOutput); + await identityClient.deactivateDidOutput(did, TEST_GAS_BUDGET); // Resolving a deactivated DID returns an empty DID document // with its `deactivated` metadata field set to `true`. - let deactivated: IotaDocument = await didClient.resolveDid(did); + let deactivated = await identityClient.resolveDid(did); console.log("Deactivated DID document:", JSON.stringify(deactivated, null, 2)); if (deactivated.metadataDeactivated() !== true) { throw new Error("Failed to deactivate DID document"); } // Re-activate the DID by publishing a valid DID document. - let reactivatedOutput: AliasOutput = await didClient.updateDidOutput(document); - - // Increase the storage deposit to the minimum again, if it was reclaimed during deactivation. - reactivatedOutput = await client.buildAliasOutput({ - ...reactivatedOutput, - amount: Utils.computeStorageDeposit(reactivatedOutput, rentStructure), - aliasId: reactivatedOutput.getAliasId(), - unlockConditions: reactivatedOutput.getUnlockConditions(), - }); - - await didClient.publishDidOutput(secretManager, reactivatedOutput); + console.log("Publishing this:", JSON.stringify(resolved, null, 2)); + await identityClient + .publishDidDocumentUpdate(resolved, TEST_GAS_BUDGET); // Resolve the reactivated DID document. - let reactivated: IotaDocument = await didClient.resolveDid(did); - if (reactivated.metadataDeactivated() === true) { + let resolvedReactivated = await identityClient.resolveDid(did); + console.log("Reactivated DID document:", JSON.stringify(resolvedReactivated, null, 2)); + if (resolvedReactivated.metadataDeactivated() === true) { throw new Error("Failed to reactivate DID document"); } } diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts index 36d2dc60f0..a7e4d817ee 100644 --- a/bindings/wasm/identity_wasm/examples/src/main.ts +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -5,7 +5,7 @@ import { testApiCall } from "./0_basic/-1_test_api_call"; import { createIdentity } from "./0_basic/0_create_did"; import { updateIdentity } from "./0_basic/1_update_did"; // import { resolveIdentity } from "./0_basic/2_resolve_did"; -// import { deactivateIdentity } from "./0_basic/3_deactivate_did"; +import { deactivateIdentity } from "./0_basic/3_deactivate_did"; // import { deleteIdentity } from "./0_basic/4_delete_did"; // import { createVC } from "./0_basic/5_create_vc"; // import { createVP } from "./0_basic/6_create_vp"; @@ -37,8 +37,8 @@ async function main() { return await updateIdentity(); // case "2_resolve_did": // return await resolveIdentity(); - // case "3_deactivate_did": - // return await deactivateIdentity(); + case "3_deactivate_did": + return await deactivateIdentity(); // case "4_delete_did": // return await deleteIdentity(); // case "5_create_vc": diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index 0342fa6f04..bddb4ee6b8 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -138,24 +138,20 @@ impl WasmKinesisIdentityClient { Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } - // not included in any e2e test anymore, so let's skip it for now - - // TODO: re-add WasmKinesisIdentityClient::deactivate_did_output - // #[wasm_bindgen(js_name = deactivateDidOutput)] - // pub async fn deactivate_did_output( - // &self, - // did: &WasmIotaDID, - // gas_budget: u64, - // signer: &WasmStorageSigner, - // ) -> Result<(), JsError> { - // self - // .0 - // .deactivate_did_output(&did.0, gas_budget) - // .await - // .map_err(>::into)?; - - // Ok(()) - // } + #[wasm_bindgen(js_name = deactivateDidOutput)] + pub async fn deactivate_did_output( + &self, + did: &WasmIotaDID, + gas_budget: u64, + ) -> Result<(), JsError> { + self + .0 + .deactivate_did_output(&did.0, gas_budget) + .await + .map_err(>::into)?; + + Ok(()) + } } // TODO: consider importing function from rebased later on, if possible diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts index af37bfb200..5bc4f9cdd0 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts @@ -3,7 +3,7 @@ import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; -import { getClockRef, getControllerDelegation, putBackDelegationToken } from "../utils"; +import { getClockRef, getControllerDelegation, insertPlaceholders, putBackDelegationToken } from "../utils"; export function proposeDeactivation( identity: SharedObjectRef, @@ -25,6 +25,8 @@ export function proposeDeactivation( putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + insertPlaceholders(tx); + return tx.build(); } diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index 4ecae377b8..5b7b3a1670 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -70,12 +70,12 @@ extern "C" { ) -> Result; #[wasm_bindgen(js_name = "proposeDeactivation", catch)] - async fn propose_deactivation( + fn propose_deactivation( identity: WasmSharedObjectRef, capability: WasmObjectRef, package: &str, expiration: Option, - ) -> Result; + ) -> Result; #[wasm_bindgen(js_name = "executeDeactivation", catch)] async fn execute_deactivation( @@ -512,7 +512,7 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { .map_err(TsSdkError::from) } - fn propose_deactivation( + async fn propose_deactivation( identity: OwnedObjectRef, capability: ObjectRef, expiration: Option, @@ -522,10 +522,20 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { let capability = capability.into(); let package = package_id.to_string(); - futures::executor::block_on(propose_deactivation(identity, capability, &package, expiration)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) + let array_promise = propose_deactivation( + identity, + capability, + &package, + expiration, + ) + .map_err(WasmError::from)?; + let promise: Promise = Promise::resolve(&array_promise); + JsFuture::from(promise) + .await + .map(|v| JsValue::from(Array::from(&v))) + .map_err(WasmError::from) + .and_then(|v| v.into_serde().map_err(WasmError::from)) + .map_err(TsSdkError::from) } fn execute_deactivation( @@ -638,7 +648,6 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { package_id: ObjectID, ) -> Result { let identity = identity.try_into()?; - // panic!("controller cap {capability:?}"); let controller_cap = capability.into(); let did_doc = did_doc.as_ref(); let package_id = package_id.to_string(); @@ -657,17 +666,6 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { .map_err(WasmError::from) .and_then(|v| v.into_serde().map_err(WasmError::from)) .map_err(TsSdkError::from) - - // futures::executor::block_on(propose_update( - // identity, - // controller_cap, - // did_doc, - // &package_id, - // expiration, - // )) - // .map(|js_arr| js_arr.to_vec()) - // .map_err(WasmError::from) - // .map_err(TsSdkError::from) } fn execute_update( diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index 229d19c4d9..d60f8b06ef 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use anyhow::Context; use identity_iota::iota::IotaDocument; +use identity_iota::iota_interaction::OptionalSync; use identity_iota::storage::JwkDocumentExt; use identity_iota::storage::JwkMemStore; use identity_iota::storage::KeyIdMemstore; diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs index f73ded4e9c..432fbdeab7 100644 --- a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -650,7 +650,7 @@ impl IdentityMoveCalls for IdentityMoveCallsRustSdk { Ok(bcs::to_bytes(&ptb.finish())?) } - fn propose_deactivation( + async fn propose_deactivation( identity: OwnedObjectRef, capability: ObjectRef, expiration: Option, diff --git a/identity_iota_core/src/rebased/proposals/deactivate_did.rs b/identity_iota_core/src/rebased/proposals/deactivate_did.rs index f72ddd043d..67f9336baf 100644 --- a/identity_iota_core/src/rebased/proposals/deactivate_did.rs +++ b/identity_iota_core/src/rebased/proposals/deactivate_did.rs @@ -75,6 +75,7 @@ impl ProposalT for Proposal { let chained_execution = sender_vp >= identity.threshold(); let tx = IdentityMoveCallsAdapter::propose_deactivation(identity_ref, controller_cap_ref, expiration, client.package_id()) + .await .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(CreateProposalTx { diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_interaction/src/move_call_traits.rs index 3085a512d7..1b8541f7cb 100644 --- a/identity_iota_interaction/src/move_call_traits.rs +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -181,7 +181,7 @@ pub trait IdentityMoveCalls { package_id: ObjectID, ) -> Result; - fn propose_deactivation( + async fn propose_deactivation( identity: OwnedObjectRef, capability: ObjectRef, expiration: Option, From e5f751ef9f82c42592be834cbcdd40d4535f3df7 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 14:06:54 +0100 Subject: [PATCH 23/63] apply review suggestions - remove unused code from -1 example --- .../examples/src/0_basic/-1_test_api_call.ts | 134 +----------------- 1 file changed, 3 insertions(+), 131 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index be5eb3d527..ee51fa6a02 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -17,56 +17,15 @@ import { import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; import { bcs } from "@iota/iota.js/bcs"; -import { IotaClient as KinesisClient, QueryEventsParams } from "@iota/iota.js/client"; +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; -import { Ed25519Keypair } from "@iota/iota.js/keypairs/ed25519"; import { Transaction } from "@iota/iota.js/transactions"; import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BUDGET } from "../utils_alpha"; async function initializeClients() { - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - console.log("---------------- Preparing IdentityClient ------------------------"); - const VALID_SECP256K1_SECRET_KEY = [ - 59, - 148, - 11, - 85, - 134, - 130, - 61, - 253, - 2, - 174, - 59, - 70, - 27, - 180, - 51, - 107, - 94, - 203, - 174, - 253, - 102, - 39, - 170, - 146, - 46, - 252, - 4, - 143, - 236, - 12, - 136, - 28, - ]; - const secretKey = new Uint8Array(VALID_SECP256K1_SECRET_KEY); - let keyPair = Ed25519Keypair.fromSecretKey(secretKey); - let pubKey = keyPair.getPublicKey(); - console.log(`Created Ed25519Keypair with PublicKey ${pubKey.toBase64()} and address ${pubKey.toIotaAddress()}`); - + const kinesisClient = new KinesisClient({ url: NETWORK_URL }); const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesisClient, IDENTITY_IOTA_PACKAGE_ID); // create new storage @@ -96,7 +55,7 @@ async function initializeClients() { console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); } - return { kinesisClient, identityClient, keyPair }; + return { kinesisClient, identityClient }; } @@ -175,35 +134,6 @@ async function testIdentityClient( } catch (ex) { console.log(`Test resolveDid() - Error: ${(ex as Error).message}`); } - - const document1 = new IotaDocument("foobar"); - try { - // console.log("\n---------------- publishDidDocument ------------------------"); - // not implemented - // await identityClient.publishDidDocument(document1, BigInt(12345), "dummy signer"); - } catch (ex) { - console.log(`Test publishDidDocument() - Error: ${(ex as Error).message}`); - } - - const document2 = new IotaDocument("foobar"); - try { - // not implemented - // console.log("\n---------------- publishDidDocumentUpdate ------------------------"); - // await identityClient.publishDidDocumentUpdate(document2, BigInt(12345), "dummy signer"); - } catch (ex) { - console.log(`Test publishDidDocumentUpdate() - Error: ${(ex as Error).message}`); - } - - const did4deactivateDidOutput = IotaDID.parse( - "did:iota:0x0101010101010101010101010101010101010101010101010101010101010101", - ); - try { - // not implemented - // console.log("\n---------------- deactivateDidOutput ------------------------"); - // await identityClient.deactivateDidOutput(did4deactivateDidOutput, BigInt(12345), "dummy signer"); - } catch (ex) { - console.log(`Test deactivateDidOutput() - Error: ${(ex as Error).message}`); - } } function testMultiController(): void { @@ -218,57 +148,6 @@ function testMultiController(): void { console.dir(multiController.threshold()); } -async function testProposals(identityClient: KinesisIdentityClient): Promise { - console.log(`testProposals disabled after interface updates`); - // let action: ProposalAction = "Deactivate"; - // console.dir(action); - - // action = { UpdateDocument: new IotaDocument("foobar") }; - // console.dir(action); - // console.dir(action.UpdateDocument); - // console.dir(action.UpdateDocument.id()); - // console.dir(action.UpdateDocument.toJSON()); - - // let identity = await identityClient - // .createIdentity(Uint8Array.from([1, 2, 3])) - // .threshold(BigInt(1)) - // .gasBudget(BigInt(1)) - // .controllers([ - // new ControllerAndVotingPower("one", BigInt(1)), - // new ControllerAndVotingPower("two", BigInt(2)), - // ]) - // .finish(identityClient, "dummySigner"); - // console.dir(identity); - // console.dir(identity.isShared()); - // console.dir(identity.proposals()); - // const deactivateProposal = await identity - // .deactivateDid() - // .expirationEpoch(BigInt(1)) - // .gasBudget(BigInt(1)) - // .key("key") - // .finish(identityClient, "dummySigner"); - // console.dir(deactivateProposal); - - // // proposals consume the identity instance, so we need a new one - // identity = await identityClient - // .createIdentity(Uint8Array.from([1, 2, 3])) - // .threshold(BigInt(1)) - // .gasBudget(BigInt(1)) - // .controllers([ - // new ControllerAndVotingPower("one", BigInt(1)), - // new ControllerAndVotingPower("two", BigInt(2)), - // ]) - // .finish(identityClient, "dummySigner"); - - // const updateProposal = await identity - // .updateDidDocument(new IotaDocument("foobar")) - // .expirationEpoch(BigInt(1)) - // .gasBudget(BigInt(1)) - // .key("key") - // .finish(identityClient, "dummySigner"); - // console.dir(updateProposal); -} - async function signerTest(): Promise { // create new storage const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); @@ -367,13 +246,6 @@ export async function testApiCall(): Promise { console.error(`multi controller binding test failed: ${suffix}`); } - try { - await testProposals(identityClient); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`proposals binding test failed: ${suffix}`); - } - try { await signerTest(); } catch (err) { From 7634aab996cbce270c742afcea21f20932a8135a Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 14:17:01 +0100 Subject: [PATCH 24/63] update IOTA sdk dependency to `@iota/iota.js` - update name from `@iota/iota.js` to `@iota/iota-sdk` - switch to published version --- .../examples/src/0_basic/-1_test_api_call.ts | 10 +- .../examples/src/0_basic/0_create_did.ts | 2 +- .../examples/src/0_basic/1_update_did.ts | 2 +- .../examples/src/0_basic/3_deactivate_did.ts | 2 +- .../identity_wasm/examples/src/utils_alpha.ts | 4 +- bindings/wasm/identity_wasm/package-lock.json | 458 ++++++++++++++---- bindings/wasm/identity_wasm/package.json | 2 +- .../lib/iota_client_helpers.ts | 8 +- .../lib/move_calls/asset/create.ts | 2 +- .../lib/move_calls/asset/delete.ts | 2 +- .../lib/move_calls/asset/transfer.ts | 4 +- .../lib/move_calls/asset/update.ts | 2 +- .../lib/move_calls/identity/borrow_asset.ts | 6 +- .../lib/move_calls/identity/config.ts | 4 +- .../identity/controller_execution.ts | 4 +- .../lib/move_calls/identity/create.ts | 2 +- .../lib/move_calls/identity/deactivate.ts | 4 +- .../lib/move_calls/identity/proposal.ts | 4 +- .../lib/move_calls/identity/send_asset.ts | 4 +- .../lib/move_calls/identity/update.ts | 4 +- .../lib/move_calls/identity/upgrade.ts | 4 +- .../lib/move_calls/migration.ts | 4 +- .../lib/move_calls/utils.ts | 4 +- .../iota_interaction_ts/package-lock.json | 122 ++--- .../wasm/iota_interaction_ts/package.json | 2 +- .../src/bindings/wasm_iota_client.rs | 4 +- .../src/bindings/wasm_types.rs | 8 +- 27 files changed, 446 insertions(+), 232 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index ee51fa6a02..04fad6f9cb 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -16,11 +16,11 @@ import { } from "@iota/identity-wasm/node"; import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; -import { bcs } from "@iota/iota.js/bcs"; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; -import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; -import { Transaction } from "@iota/iota.js/transactions"; -import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; +import { bcs } from "@iota/iota-sdk/bcs"; +import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { IOTA_TYPE_ARG } from "@iota/iota-sdk/utils"; import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BUDGET } from "../utils_alpha"; async function initializeClients() { diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index bf266a67ea..5abbd5fa19 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { IotaDID } from '@iota/identity-wasm/node'; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index 57817a2bb4..e72f000106 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -11,7 +11,7 @@ import { Timestamp, VerificationMethod, } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 0dde675de0..0bfb077291 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { IotaDID } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts index e5bc1d65ba..cd9bd45b10 100644 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -12,8 +12,8 @@ import { Storage, StorageSigner, } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota.js/client"; -import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota.js/faucet"; +import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; export const IDENTITY_IOTA_PACKAGE_ID = process.env.IDENTITY_IOTA_PACKAGE_ID || "0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d"; diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index 8489b18bbd..fb0a83775f 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@iota/iota-interaction-ts": "file:../iota_interaction_ts/node", - "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@iota/iota-sdk": "^0.5.0", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", @@ -45,61 +45,11 @@ "@iota/sdk-wasm": "^1.0.4" } }, - "../../../../iota/sdk/typescript": { - "name": "@iota/iota-sdk", - "version": "0.3.0", - "license": "Apache-2.0", - "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "@iota/bcs": "workspace:*", - "@noble/curves": "^1.4.2", - "@noble/hashes": "^1.4.0", - "@scure/bip32": "^1.4.0", - "@scure/bip39": "^1.3.0", - "@suchipi/femver": "^1.0.0", - "bech32": "^2.0.0", - "gql.tada": "^1.8.2", - "graphql": "^16.9.0", - "tweetnacl": "^1.0.3", - "valibot": "^0.36.0" - }, - "devDependencies": { - "@0no-co/graphqlsp": "^1.12.11", - "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typed-document-node": "^5.0.9", - "@graphql-codegen/typescript": "4.0.9", - "@graphql-codegen/typescript-operations": "^4.2.3", - "@iarna/toml": "^2.2.5", - "@iota/build-scripts": "workspace:^", - "@types/node": "^20.14.10", - "@types/tmp": "^0.2.6", - "@types/ws": "^8.5.10", - "cross-env": "^7.0.3", - "dotenv": "^16.4.5", - "graphql-config": "^5.0.3", - "msw": "^2.3.1", - "tmp": "^0.2.3", - "ts-retry-promise": "^0.8.1", - "typescript": "^5.5.3", - "vite": "^5.3.3", - "vitest": "^2.0.1", - "wait-on": "^7.2.0", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=20" - } - }, - "../../../../iotaledger/iota/sdk/typescript": { - "extraneous": true - }, "../iota_interaction_ts": { "name": "@iota/iota-interaction-ts", "version": "1.4.0", "license": "Apache-2.0", "dependencies": { - "@iota/iota.js": "file:../../../../iota/sdk/typescript", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", @@ -139,6 +89,34 @@ "version": "1.4.0", "license": "Apache-2.0" }, + "node_modules/@0no-co/graphql.web": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", + "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", + "license": "MIT", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, + "node_modules/@0no-co/graphqlsp": { + "version": "1.12.16", + "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.12.16.tgz", + "integrity": "sha512-B5pyYVH93Etv7xjT6IfB7QtMBdaaC07yjbhN6v8H7KgFStMkPvi+oWYBTibMFRMY89qwc9H8YixXg8SXDVgYWw==", + "license": "MIT", + "dependencies": { + "@gql.tada/internal": "^1.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + } + }, "node_modules/@babel/parser": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", @@ -285,13 +263,95 @@ "@shikijs/vscode-textmate": "^10.0.1" } }, + "node_modules/@gql.tada/cli-utils": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.6.3.tgz", + "integrity": "sha512-jFFSY8OxYeBxdKi58UzeMXG1tdm4FVjXa8WHIi66Gzu9JWtCE6mqom3a8xkmSw+mVaybFW5EN2WXf1WztJVNyQ==", + "license": "MIT", + "dependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/internal": "1.0.8", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/svelte-support": "1.0.1", + "@gql.tada/vue-support": "1.0.1", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "@gql.tada/svelte-support": { + "optional": true + }, + "@gql.tada/vue-support": { + "optional": true + } + } + }, + "node_modules/@gql.tada/internal": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.8.tgz", + "integrity": "sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.5" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@iota/bcs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-0.2.1.tgz", + "integrity": "sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==", + "license": "Apache-2.0", + "dependencies": { + "bs58": "^6.0.0" + } + }, "node_modules/@iota/iota-interaction-ts": { "resolved": "../iota_interaction_ts/node", "link": true }, - "node_modules/@iota/iota.js": { - "resolved": "../../../../iota/sdk/typescript", - "link": true + "node_modules/@iota/iota-sdk": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.5.0.tgz", + "integrity": "sha512-ZFg4C5EuHV55fHITKOO6Mg1dLqgojZqJsDsR3SRt8W9Ofzbjt8shlM2uLNRwDpiM7GzTb4UUFcKXpOt//5gEmQ==", + "license": "Apache-2.0", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "@iota/bcs": "0.2.1", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@scure/bip32": "^1.4.0", + "@scure/bip39": "^1.3.0", + "@suchipi/femver": "^1.0.0", + "bech32": "^2.0.0", + "gql.tada": "^1.8.2", + "graphql": "^16.9.0", + "tweetnacl": "^1.0.3", + "valibot": "^0.36.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@iota/iota-sdk/node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" }, "node_modules/@iota/sdk-wasm": { "version": "1.1.3", @@ -402,6 +462,21 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/ed25519": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", @@ -414,9 +489,9 @@ ] }, "node_modules/@noble/hashes": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", - "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -460,6 +535,42 @@ "node": ">= 8" } }, + "node_modules/@scure/base": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@shikijs/engine-oniguruma": { "version": "1.27.2", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.27.2.tgz", @@ -574,6 +685,12 @@ "@stablelib/wipe": "^1.0.1" } }, + "node_modules/@suchipi/femver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", + "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==", + "license": "MIT" + }, "node_modules/@transmute/did-context": { "version": "0.6.1-unstable.37", "resolved": "https://registry.npmjs.org/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz", @@ -1359,6 +1476,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base-x": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", + "license": "MIT" + }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -1396,6 +1519,12 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", + "license": "MIT" + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -1497,6 +1626,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -3146,6 +3284,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gql.tada": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.8.10.tgz", + "integrity": "sha512-FrvSxgz838FYVPgZHGOSgbpOjhR+yq44rCzww3oOPJYi0OvBJjAgCiP6LEokZIYND2fUTXzQAyLgcvgw1yNP5A==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.5", + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/cli-utils": "1.6.3", + "@gql.tada/internal": "1.0.8" + }, + "bin": { + "gql-tada": "bin/cli.js", + "gql.tada": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3153,6 +3310,15 @@ "dev": true, "license": "ISC" }, + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -6497,7 +6663,6 @@ "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -6678,6 +6843,12 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/valibot": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", + "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==", + "license": "MIT" + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -7010,6 +7181,21 @@ } }, "dependencies": { + "@0no-co/graphql.web": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", + "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", + "requires": {} + }, + "@0no-co/graphqlsp": { + "version": "1.12.16", + "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.12.16.tgz", + "integrity": "sha512-B5pyYVH93Etv7xjT6IfB7QtMBdaaC07yjbhN6v8H7KgFStMkPvi+oWYBTibMFRMY89qwc9H8YixXg8SXDVgYWw==", + "requires": { + "@gql.tada/internal": "^1.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + } + }, "@babel/parser": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", @@ -7132,46 +7318,65 @@ "@shikijs/vscode-textmate": "^10.0.1" } }, + "@gql.tada/cli-utils": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.6.3.tgz", + "integrity": "sha512-jFFSY8OxYeBxdKi58UzeMXG1tdm4FVjXa8WHIi66Gzu9JWtCE6mqom3a8xkmSw+mVaybFW5EN2WXf1WztJVNyQ==", + "requires": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/internal": "1.0.8", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + } + }, + "@gql.tada/internal": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.8.tgz", + "integrity": "sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==", + "requires": { + "@0no-co/graphql.web": "^1.0.5" + } + }, + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "requires": {} + }, + "@iota/bcs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-0.2.1.tgz", + "integrity": "sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==", + "requires": { + "bs58": "^6.0.0" + } + }, "@iota/iota-interaction-ts": { "version": "file:../iota_interaction_ts/node" }, - "@iota/iota.js": { - "version": "file:../../../../iota/sdk/typescript", + "@iota/iota-sdk": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.5.0.tgz", + "integrity": "sha512-ZFg4C5EuHV55fHITKOO6Mg1dLqgojZqJsDsR3SRt8W9Ofzbjt8shlM2uLNRwDpiM7GzTb4UUFcKXpOt//5gEmQ==", "requires": { - "@0no-co/graphqlsp": "^1.12.11", - "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typed-document-node": "^5.0.9", - "@graphql-codegen/typescript": "4.0.9", - "@graphql-codegen/typescript-operations": "^4.2.3", "@graphql-typed-document-node/core": "^3.2.0", - "@iarna/toml": "^2.2.5", - "@iota/bcs": "workspace:*", - "@iota/build-scripts": "workspace:^", + "@iota/bcs": "0.2.1", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@scure/bip32": "^1.4.0", "@scure/bip39": "^1.3.0", "@suchipi/femver": "^1.0.0", - "@types/node": "^20.14.10", - "@types/tmp": "^0.2.6", - "@types/ws": "^8.5.10", "bech32": "^2.0.0", - "cross-env": "^7.0.3", - "dotenv": "^16.4.5", "gql.tada": "^1.8.2", "graphql": "^16.9.0", - "graphql-config": "^5.0.3", - "msw": "^2.3.1", - "tmp": "^0.2.3", - "ts-retry-promise": "^0.8.1", "tweetnacl": "^1.0.3", - "typescript": "^5.5.3", - "valibot": "^0.36.0", - "vite": "^5.3.3", - "vitest": "^2.0.1", - "wait-on": "^7.2.0", - "ws": "^8.18.0" + "valibot": "^0.36.0" + }, + "dependencies": { + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + } } }, "@iota/sdk-wasm": { @@ -7267,15 +7472,23 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "requires": { + "@noble/hashes": "1.7.1" + } + }, "@noble/ed25519": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==" }, "@noble/hashes": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", - "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -7303,6 +7516,30 @@ "fastq": "^1.6.0" } }, + "@scure/base": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==" + }, + "@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "requires": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + } + }, + "@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "requires": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + } + }, "@shikijs/engine-oniguruma": { "version": "1.27.2", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.27.2.tgz", @@ -7414,6 +7651,11 @@ "@stablelib/wipe": "^1.0.1" } }, + "@suchipi/femver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", + "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==" + }, "@transmute/did-context": { "version": "0.6.1-unstable.37", "resolved": "https://registry.npmjs.org/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz", @@ -8066,6 +8308,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base-x": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==" + }, "base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -8086,6 +8333,11 @@ "tweetnacl": "^0.14.3" } }, + "bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, "big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -8154,6 +8406,14 @@ "update-browserslist-db": "^1.1.1" } }, + "bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "requires": { + "base-x": "^5.0.0" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -9372,12 +9632,28 @@ "slash": "^3.0.0" } }, + "gql.tada": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.8.10.tgz", + "integrity": "sha512-FrvSxgz838FYVPgZHGOSgbpOjhR+yq44rCzww3oOPJYi0OvBJjAgCiP6LEokZIYND2fUTXzQAyLgcvgw1yNP5A==", + "requires": { + "@0no-co/graphql.web": "^1.0.5", + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/cli-utils": "1.6.3", + "@gql.tada/internal": "1.0.8" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==" + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -11695,8 +11971,7 @@ "typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" }, "typical": { "version": "2.6.1", @@ -11818,6 +12093,11 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "valibot": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", + "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 1c2e6439b0..312eccd77c 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -78,7 +78,7 @@ "wasm-opt": "^1.3.0" }, "dependencies": { - "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@iota/iota-sdk": "^0.5.0", "@iota/iota-interaction-ts": "file:../iota_interaction_ts/node", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index 58985062d2..a53f75ef9b 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -7,10 +7,10 @@ import { IotaClient, IotaTransactionBlockResponse, OwnedObjectRef, -} from "@iota/iota.js/client"; -import { messageWithIntent, toSerializedSignature } from "@iota/iota.js/cryptography"; -import { Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; -import { GasData, TransactionDataBuilder } from "@iota/iota.js/transactions"; +} from "@iota/iota-sdk/client"; +import { messageWithIntent, toSerializedSignature } from "@iota/iota-sdk/cryptography"; +import { Ed25519PublicKey } from "@iota/iota-sdk/keypairs/ed25519"; +import { GasData, TransactionDataBuilder } from "@iota/iota-sdk/transactions"; import { blake2b } from "@noble/hashes/blake2b"; export type Signer = { sign(data: Uint8Array): Promise }; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts index 1aecf07f6a..9aee87fa2c 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Transaction } from "@iota/iota.js/transactions"; +import { Transaction } from "@iota/iota-sdk/transactions"; export function create( inner_bytes: Uint8Array, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts index 088388a9c4..04b4a10f99 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; export function remove( asset: ObjectRef, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts index 2301be9331..5ab2b7ad7d 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; export function transfer( asset: ObjectRef, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts index 60b0de6f98..c80f52f9b3 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; export function update( asset: ObjectRef, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts index 8fff37c9ec..774eb6eeec 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts @@ -1,9 +1,9 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { IotaObjectData } from "@iota/iota.js/dist/cjs/client"; -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { IotaObjectData } from "@iota/iota-sdk/dist/cjs/client"; +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; import { getControllerDelegation, putBackDelegationToken } from "../utils"; export function proposeBorrow( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts index c870586e4a..666b0022eb 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getControllerDelegation, putBackDelegationToken } from "../utils"; export function proposeConfigChange( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts index 59bd0f9cea..332daa33a4 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; import { getControllerDelegation, putBackDelegationToken } from "../utils"; export function proposeControllerExecution( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts index a72fbf5aaf..49f46f57d0 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Transaction } from "@iota/iota.js/transactions"; +import { Transaction } from "@iota/iota-sdk/transactions"; import { getClockRef, insertPlaceholders } from "../utils"; export async function create(didDoc: Uint8Array, packageId: string): Promise { diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts index 5bc4f9cdd0..4625849d9c 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/deactivate.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getClockRef, getControllerDelegation, insertPlaceholders, putBackDelegationToken } from "../utils"; export function proposeDeactivation( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts index 6c3ba09603..6a683ced37 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getControllerDelegation, putBackDelegationToken } from "../utils"; export function approve( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts index 8063641b36..2688a0d4fb 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getControllerDelegation, putBackDelegationToken } from "../utils"; export function proposeSend( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts index a72983d4f6..5761aba667 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getClockRef, getControllerDelegation, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts index 4097c19a82..cc8622bfec 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; export function proposeUpgrade( identity: SharedObjectRef, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts index eb0c7df3b3..e29cd620fa 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts @@ -1,8 +1,8 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { SharedObjectRef } from "@iota/iota.js/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota.js/transactions"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; import { getClockRef } from "./utils"; export function migrateDidOutput( diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts index b99d31b660..1bfa5cab3b 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts @@ -1,5 +1,5 @@ -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota.js/transactions"; -import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota.js/utils"; +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; +import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; const PLACEHOLDER_SENDER = '0x00000000000000090807060504030201'; const PLACEHOLDER_GAS_BUDGET = 9; diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json index 42bb5221c4..81ecc84c08 100644 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -9,8 +9,7 @@ "version": "1.4.0", "license": "Apache-2.0", "dependencies": { - "@iota/iota-sdk": "^0.4.0", - "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@iota/iota-sdk": "^0.5.0", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", @@ -45,59 +44,10 @@ "@iota/sdk-wasm": "^1.0.4" } }, - "../../../../iota/sdk/typescript": { - "name": "@iota/iota-sdk", - "version": "0.3.1", - "license": "Apache-2.0", - "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "@iota/bcs": "workspace:*", - "@noble/curves": "^1.4.2", - "@noble/hashes": "^1.4.0", - "@scure/bip32": "^1.4.0", - "@scure/bip39": "^1.3.0", - "@suchipi/femver": "^1.0.0", - "bech32": "^2.0.0", - "gql.tada": "^1.8.2", - "graphql": "^16.9.0", - "tweetnacl": "^1.0.3", - "valibot": "^0.36.0" - }, - "devDependencies": { - "@0no-co/graphqlsp": "^1.12.11", - "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typed-document-node": "^5.0.9", - "@graphql-codegen/typescript": "4.0.9", - "@graphql-codegen/typescript-operations": "^4.2.3", - "@iarna/toml": "^2.2.5", - "@iota/build-scripts": "workspace:^", - "@types/node": "^20.14.10", - "@types/tmp": "^0.2.6", - "@types/ws": "^8.5.10", - "cross-env": "^7.0.3", - "dotenv": "^16.4.5", - "graphql-config": "^5.0.3", - "msw": "^2.3.1", - "tmp": "^0.2.3", - "ts-retry-promise": "^0.8.1", - "typescript": "^5.5.3", - "vite": "^5.3.3", - "vitest": "^2.0.1", - "wait-on": "^7.2.0", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=20" - } - }, - "../../../../iotaledger/iota/sdk/typescript": { - "extraneous": true - }, "node_modules/@0no-co/graphql.web": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.12.tgz", - "integrity": "sha512-BTDjjsV/zSPy5fqItwm+KWUfh9CSe9tTtR6rCB72ddtkAxdcHbi4Ir4r/L1Et4lyxmL+i7Rb3m9sjLLi9tYrzA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", + "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", "license": "MIT", "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" @@ -362,9 +312,9 @@ } }, "node_modules/@iota/iota-sdk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.4.0.tgz", - "integrity": "sha512-uhHcQGN3G/gJi62aOKmvgiloaNAC61fEsV4GKAKVVv5kUP60p6Q3S5xQL3OoYf6kiAUUhaFoAr4keiEQDZp5aw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.5.0.tgz", + "integrity": "sha512-ZFg4C5EuHV55fHITKOO6Mg1dLqgojZqJsDsR3SRt8W9Ofzbjt8shlM2uLNRwDpiM7GzTb4UUFcKXpOt//5gEmQ==", "license": "Apache-2.0", "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", @@ -390,10 +340,6 @@ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "license": "Unlicense" }, - "node_modules/@iota/iota.js": { - "resolved": "../../../../iota/sdk/typescript", - "link": true - }, "node_modules/@iota/sdk-wasm": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", @@ -520,12 +466,12 @@ } }, "node_modules/@noble/curves": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", - "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.6.0" + "@noble/hashes": "1.7.1" }, "engines": { "node": "^14.21.3 || >=16" @@ -534,18 +480,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", - "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@noble/ed25519": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", @@ -559,9 +493,9 @@ "license": "MIT" }, "node_modules/@noble/hashes": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", - "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -609,36 +543,36 @@ } }, "node_modules/@scure/base": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz", - "integrity": "sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.0.tgz", - "integrity": "sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", "license": "MIT", "dependencies": { - "@noble/curves": "~1.7.0", - "@noble/hashes": "~1.6.0", - "@scure/base": "~1.2.1" + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.0.tgz", - "integrity": "sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", "license": "MIT", "dependencies": { - "@noble/hashes": "~1.6.0", - "@scure/base": "~1.2.1" + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" }, "funding": { "url": "https://paulmillr.com/funding/" diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index 7e788f6757..ba9d46a7e2 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -73,7 +73,7 @@ "wasm-opt": "^1.3.0" }, "dependencies": { - "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@iota/iota-sdk": "^0.5.0", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs index bcf37597fb..bbdf10ee3e 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -66,10 +66,10 @@ use crate::error::TsSdkError; // TODO: check why this isn't done by `module` macro attribute for `WasmKinesisClient` #[wasm_bindgen(typescript_custom_section)] const IOTA_CLIENT_TYPE: &'static str = r#" - import { IotaClient } from "@iota/iota.js/client"; + import { IotaClient } from "@iota/iota-sdk/client"; "#; -#[wasm_bindgen(module = "@iota/iota.js/client")] +#[wasm_bindgen(module = "@iota/iota-sdk/client")] extern "C" { #[wasm_bindgen(typescript_type = "IotaClient")] #[derive(Clone)] diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index ce031883a2..2a795a2ae2 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -49,15 +49,15 @@ const TS_SDK_TYPES: &'static str = r#" PaginatedObjectsResponse, QueryEventsParams, TryGetPastObjectParams, - } from "@iota/iota.js/client"; - import { bcs } from "@iota/iota.js/bcs"; + } from "@iota/iota-sdk/client"; + import { bcs } from "@iota/iota-sdk/bcs"; import { executeTransaction, IotaTransactionBlockResponseAdapter, } from "./iota_client_helpers" "#; -#[wasm_bindgen(module = "@iota/iota.js/client")] +#[wasm_bindgen(module = "@iota/iota-sdk/client")] extern "C" { #[wasm_bindgen(typescript_type = "Promise")] pub type PromiseBalance; @@ -153,7 +153,7 @@ extern "C" { pub type PromiseIotaTransactionBlockResponseAdapter; } -#[wasm_bindgen(module = "@iota/iota.js/transactions")] +#[wasm_bindgen(module = "@iota/iota-sdk/transactions")] extern "C" { #[wasm_bindgen(typescript_type = "Transaction")] pub type WasmTransactionBuilder; From b965dc283d383a2bdc90ec62fe436836df5bb135 Mon Sep 17 00:00:00 2001 From: wulfraem Date: Fri, 7 Feb 2025 14:40:09 +0100 Subject: [PATCH 25/63] Apply suggestions from code review - remove unnecessary `js_name` renaming Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> --- bindings/wasm/identity_wasm/src/kinesis/identity.rs | 6 +++--- .../src/kinesis/wasm_identity_client_read_only.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/identity.rs index b32ff1be0f..67001577b9 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/identity.rs @@ -47,7 +47,7 @@ pub struct WasmOnChainIdentity(pub(crate) OnChainIdentity); #[wasm_bindgen(js_class = OnChainIdentity)] impl WasmOnChainIdentity { - #[wasm_bindgen(js_name = id)] + #[wasm_bindgen] pub fn id(&self) -> String { self.0.id().to_string() } @@ -215,7 +215,7 @@ impl WasmIdentityBuilder { ) } - #[wasm_bindgen(js_name = finish)] + #[wasm_bindgen] pub fn finish(self) -> WasmCreateIdentityTx { WasmCreateIdentityTx(self.0.finish()) } @@ -226,7 +226,7 @@ pub struct WasmCreateIdentityTx(pub(crate) CreateIdentityTx); #[wasm_bindgen(js_class = CreateIdentityTx)] impl WasmCreateIdentityTx { - #[wasm_bindgen(js_name = execute)] + #[wasm_bindgen] pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputInternalOnChainIdentity(output)) diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index 25f34d8513..bd5ca8cf7d 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -62,7 +62,7 @@ impl WasmKinesisIdentityClientReadOnly { Ok(self.0.package_id().to_string()) } - #[wasm_bindgen(js_name = network)] + #[wasm_bindgen] pub fn network(&self) -> String { self.0.network().to_string() } From 4ee56ba9d7d97e0b0e230195193aa35410571784 Mon Sep 17 00:00:00 2001 From: wulfraem Date: Fri, 7 Feb 2025 15:37:43 +0100 Subject: [PATCH 26/63] Apply suggestions from code review Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> --- .../wasm/identity_wasm/src/storage/wasm_transaction_signer.rs | 4 ++-- .../wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs | 2 +- bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index 8e66af1661..9cc6366397 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -10,7 +10,7 @@ use secret_storage::Signer; use crate::error::Result; #[wasm_bindgen(typescript_custom_section)] -const I_JWS_SIGNER: &str = r#" +const I_TX_SIGNER: &str = r#" interface TransactionSigner { sign: (data: Uint8Array) => Promise; publicKey: () => Promise; @@ -21,7 +21,7 @@ interface TransactionSigner { #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "TransactionSigner")] - pub type WasmTransactionSigner; // we will receive this as `StorageSigner` from TS + pub type WasmTransactionSigner; #[wasm_bindgen(js_name = "sign", structural, method, catch)] pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs index bbdf10ee3e..4198155652 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -60,7 +60,7 @@ use crate::error::TsSdkError; // the interface of the TS Iota client to rust code. // The typescript declarations imported in the following typescript_custom_section -// can be use as arguments for rust functions via the typescript_type annotation. +// can be used as arguments for rust functions via the typescript_type annotation. // In other words: The typescript_type "IotaClient" is imported here to be bound // to the WasmIotaClient functions below. // TODO: check why this isn't done by `module` macro attribute for `WasmKinesisClient` diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index 2a795a2ae2..9782d98923 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -30,7 +30,7 @@ use crate::error::WasmError; type WasmStorageSigner = (); #[wasm_bindgen(typescript_custom_section)] -const TS_SDK_TYPES: &'static str = r#" +const TS_SDK_TYPES: &str = r#" import { Balance, ExecuteTransactionBlockParams, From 5ad547ef46a562cab0b4bac61fff6899f925e574 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 14:56:29 +0100 Subject: [PATCH 27/63] apply review suggestion - remove unnecessary result wrapping --- .../wasm/identity_wasm/src/kinesis/wasm_identity_client.rs | 4 ++-- .../src/kinesis/wasm_identity_client_read_only.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index bddb4ee6b8..428703aecb 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -88,8 +88,8 @@ impl WasmKinesisIdentityClient { } #[wasm_bindgen(js_name = packageId)] - pub fn package_id(&self) -> Result { - Ok(self.0.package_id().to_string()) + pub fn package_id(&self) -> String { + self.0.package_id().to_string() } #[wasm_bindgen(js_name = resolveDid)] diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index bd5ca8cf7d..38e55a8a91 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -58,8 +58,8 @@ impl WasmKinesisIdentityClientReadOnly { } #[wasm_bindgen(js_name = packageId)] - pub fn package_id(&self) -> Result { - Ok(self.0.package_id().to_string()) + pub fn package_id(&self) -> String { + self.0.package_id().to_string() } #[wasm_bindgen] From 8f8c8ccdaa583718a769565b22389ba695d06e93 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 15:29:44 +0100 Subject: [PATCH 28/63] apply review suggestions - more readable error conversion (`JsError::from`) --- bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs | 2 +- .../identity_wasm/src/kinesis/wasm_identity_client_read_only.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index 428703aecb..e740508526 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -98,7 +98,7 @@ impl WasmKinesisIdentityClient { .0 .resolve_did(&did.0) .await - .map_err(>::into)?; + .map_err(JsError::from)?; Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index 38e55a8a91..9c542bd234 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -94,7 +94,7 @@ impl WasmKinesisIdentityClientReadOnly { .0 .resolve_did(&did.0) .await - .map_err(>::into)?; + .map_err(JsError::from)?; Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } From 6c23d948eea613f7b7b60dbef35c08d68e4e940f Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 7 Feb 2025 15:36:35 +0100 Subject: [PATCH 29/63] apply reivew suggestions - wasm binding header cleanup --- .../identity_wasm/src/storage/wasm_transaction_signer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index 9cc6366397..59fc27ea87 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -23,10 +23,9 @@ extern "C" { #[wasm_bindgen(typescript_type = "TransactionSigner")] pub type WasmTransactionSigner; - #[wasm_bindgen(js_name = "sign", structural, method, catch)] + #[wasm_bindgen(method, catch)] pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; - - #[wasm_bindgen(js_name = "publicKey", structural, method, catch)] + #[wasm_bindgen(js_name = "publicKey", method, catch)] pub async fn public_key(this: &WasmTransactionSigner) -> Result; // TODO: re-add WasmTransactionSigner::key_id From e30c980752b97a6ae8fc177ff7129232bbf18cf7 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Mon, 10 Feb 2025 08:51:17 +0100 Subject: [PATCH 30/63] remove unused error variant --- bindings/wasm/iota_interaction_ts/src/error.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/error.rs b/bindings/wasm/iota_interaction_ts/src/error.rs index a9b44b8f14..0e58e6b4cd 100644 --- a/bindings/wasm/iota_interaction_ts/src/error.rs +++ b/bindings/wasm/iota_interaction_ts/src/error.rs @@ -196,8 +196,6 @@ pub enum TsSdkError { JsSysError(String), #[error("[TsSdkError] TransactionSerializationError: {0}")] TransactionSerializationError(String), - #[error("[TsSdkError] TransactionSigningError: {0}")] - TransactionSigningError(String, String), } pub type TsSdkResult = core::result::Result; From 84c92fc0dac38bf863dc113c6fad459ff97c7cd8 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Tue, 11 Feb 2025 09:51:32 +0100 Subject: [PATCH 31/63] add helper function for Uint8 array to pt bcs conversion --- .../iota_interaction_ts/src/common/types.rs | 17 ++++++ .../src/identity_move_calls.rs | 52 +++++-------------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/common/types.rs b/bindings/wasm/iota_interaction_ts/src/common/types.rs index a3197dafe6..263dbe048e 100644 --- a/bindings/wasm/iota_interaction_ts/src/common/types.rs +++ b/bindings/wasm/iota_interaction_ts/src/common/types.rs @@ -2,9 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 use identity_core::common::Object; +use identity_iota_interaction::ProgrammableTransactionBcs; +use js_sys::Promise; +use js_sys::Uint8Array; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; +use wasm_bindgen_futures::JsFuture; +use crate::error::TsSdkError; +use crate::error::TsSdkResult; +use crate::error::WasmError; use crate::error::WasmResult; #[wasm_bindgen] @@ -83,3 +90,13 @@ impl Default for MapStringAny { js_sys::Map::new().unchecked_into() } } + +impl PromiseUint8Array { + pub async fn to_transaction_bcs(&self) -> TsSdkResult{ + let promise: Promise = Promise::resolve(self); + JsFuture::from(promise).await + .map(|v| Uint8Array::from(v).to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } +} \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index 5b7b3a1670..b8b44f980c 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -4,12 +4,12 @@ use js_sys::Array; use js_sys::Promise; use js_sys::Uint8Array; -use wasm_bindgen_futures::JsFuture; use std::cell::Cell; use std::collections::HashSet; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; use crate::bindings::WasmIotaObjectData; use crate::bindings::WasmObjectRef; @@ -479,15 +479,11 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { async fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { let package = package_id.to_string(); - - let array_promise = identity_new(did_doc, &package).map_err(WasmError::from)?; - let promise: Promise = Promise::resolve(&array_promise); - JsFuture::from(promise) - .await - .map(|v| JsValue::from(Array::from(&v))) - .map_err(WasmError::from) - .and_then(|v| v.into_serde().map_err(WasmError::from)) - .map_err(TsSdkError::from) + + identity_new(did_doc, &package) + .map_err(WasmError::from)? + .to_transaction_bcs() + .await } fn new_with_controllers( @@ -522,20 +518,10 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { let capability = capability.into(); let package = package_id.to_string(); - let array_promise = propose_deactivation( - identity, - capability, - &package, - expiration, - ) - .map_err(WasmError::from)?; - let promise: Promise = Promise::resolve(&array_promise); - JsFuture::from(promise) - .await - .map(|v| JsValue::from(Array::from(&v))) - .map_err(WasmError::from) - .and_then(|v| v.into_serde().map_err(WasmError::from)) - .map_err(TsSdkError::from) + propose_deactivation(identity, capability, &package, expiration) + .map_err(WasmError::from)? + .to_transaction_bcs() + .await } fn execute_deactivation( @@ -652,20 +638,10 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { let did_doc = did_doc.as_ref(); let package_id = package_id.to_string(); - let array_promise = propose_update( - identity, - controller_cap, - did_doc, - &package_id, - expiration, - ).map_err(WasmError::from)?; - let promise: Promise = Promise::resolve(&array_promise); - JsFuture::from(promise) - .await - .map(|v| JsValue::from(Array::from(&v))) - .map_err(WasmError::from) - .and_then(|v| v.into_serde().map_err(WasmError::from)) - .map_err(TsSdkError::from) + propose_update(identity, controller_cap, did_doc, &package_id, expiration) + .map_err(WasmError::from)? + .to_transaction_bcs() + .await } fn execute_update( From dfe8d4190a856a29cc8aaa40f47d889bc38a1207 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Tue, 11 Feb 2025 10:09:15 +0100 Subject: [PATCH 32/63] add comment and formatting --- .../wasm/iota_interaction_ts/src/common/types.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/common/types.rs b/bindings/wasm/iota_interaction_ts/src/common/types.rs index 263dbe048e..9d596d53e3 100644 --- a/bindings/wasm/iota_interaction_ts/src/common/types.rs +++ b/bindings/wasm/iota_interaction_ts/src/common/types.rs @@ -92,11 +92,13 @@ impl Default for MapStringAny { } impl PromiseUint8Array { - pub async fn to_transaction_bcs(&self) -> TsSdkResult{ + /// Helper function to convert Uint8 arrays from contract calls to the internal `ProgrammableTransactionBcs` type. + pub async fn to_transaction_bcs(&self) -> TsSdkResult { let promise: Promise = Promise::resolve(self); - JsFuture::from(promise).await - .map(|v| Uint8Array::from(v).to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) + JsFuture::from(promise) + .await + .map(|v| Uint8Array::from(v).to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) } -} \ No newline at end of file +} From aa6c35b17839df910d31a24a9cfdaf58932b1942 Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:27:20 +0100 Subject: [PATCH 33/63] StorageSigner (#1523) --- .../src/kinesis/client_dummy/identity.rs | 2 +- .../identity_wasm/src/kinesis/identity.rs | 13 ++- .../src/kinesis/wasm_identity_client.rs | 30 +++---- .../kinesis/wasm_identity_client_read_only.rs | 20 ++--- .../wasm/identity_wasm/src/storage/mod.rs | 2 - .../src/storage/storage_signer_owned.rs | 81 ------------------- .../identity_wasm/src/storage/wasm_storage.rs | 1 + .../src/storage/wasm_storage_signer.rs | 56 ++++++------- .../src/storage/wasm_transaction_signer.rs | 74 ++++++++--------- .../src/storage/storage_signer.rs | 5 +- 10 files changed, 95 insertions(+), 189 deletions(-) delete mode 100644 bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs index a129919404..ce65f3a663 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs @@ -16,9 +16,9 @@ use serde::Serialize; use super::DummySigner; use super::Multicontroller; use super::Proposal; +use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota::rebased::Error; use identity_iota::iota::IotaDocument; -use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota_interaction::rpc_types::IotaObjectData; use identity_iota::iota_interaction::rpc_types::OwnedObjectRef; use identity_iota::iota_interaction::types::base_types::IotaAddress; diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/identity.rs index 67001577b9..23a3ab6a08 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/identity.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/identity.rs @@ -175,11 +175,7 @@ impl WasmIdentityBuilder { #[wasm_bindgen(constructor)] pub fn new(did_doc: &WasmIotaDocument) -> Result { let document: IotaDocument = did_doc.0.try_read().unwrap().clone(); - Ok( - WasmIdentityBuilder( - IdentityBuilder::new(document) - ) - ) + Ok(WasmIdentityBuilder(IdentityBuilder::new(document))) } pub fn controller(self, address: WasmIotaAddress, voting_power: u64) -> Self { @@ -227,7 +223,10 @@ pub struct WasmCreateIdentityTx(pub(crate) CreateIdentityTx); #[wasm_bindgen(js_class = CreateIdentityTx)] impl WasmCreateIdentityTx { #[wasm_bindgen] - pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { + pub async fn execute( + self, + client: &WasmKinesisIdentityClient, + ) -> Result { let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputInternalOnChainIdentity(output)) } @@ -247,4 +246,4 @@ impl WasmTransactionOutputInternalOnChainIdentity { pub fn response(&self) -> AdapterNativeResponse { self.0.response.clone_native_response() } -} \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs index e740508526..be7a98b214 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -50,7 +50,10 @@ pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient Result { + pub async fn new( + client: WasmKinesisIdentityClientReadOnly, + signer: WasmTransactionSigner, + ) -> Result { let inner_client = IdentityClient::new(client.0, signer).await?; Ok(WasmKinesisIdentityClient(inner_client)) } @@ -69,7 +72,7 @@ impl WasmKinesisIdentityClient { pub fn network(&self) -> String { self.0.network().to_string() } - + #[wasm_bindgen(js_name = migrationRegistryId)] pub fn migration_registry_id(&self) -> String { self.0.migration_registry_id().to_string() @@ -94,28 +97,19 @@ impl WasmKinesisIdentityClient { #[wasm_bindgen(js_name = resolveDid)] pub async fn resolve_did(&self, did: &WasmIotaDID) -> Result { - let document = self - .0 - .resolve_did(&did.0) - .await - .map_err(JsError::from)?; + let document = self.0.resolve_did(&did.0).await.map_err(JsError::from)?; Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } #[wasm_bindgen(js_name = publishDidDocument)] - pub fn publish_did_document( - &self, - document: &WasmIotaDocument, - ) -> Result { + pub fn publish_did_document(&self, document: &WasmIotaDocument) -> Result { let doc: IotaDocument = document .0 .try_read() .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? .clone(); - Ok(WasmPublishDidTx(self - .0 - .publish_did_document(doc))) + Ok(WasmPublishDidTx(self.0.publish_did_document(doc))) } #[wasm_bindgen(js_name = publishDidDocumentUpdate)] @@ -139,11 +133,7 @@ impl WasmKinesisIdentityClient { } #[wasm_bindgen(js_name = deactivateDidOutput)] - pub async fn deactivate_did_output( - &self, - did: &WasmIotaDID, - gas_budget: u64, - ) -> Result<(), JsError> { + pub async fn deactivate_did_output(&self, did: &WasmIotaDID, gas_budget: u64) -> Result<(), JsError> { self .0 .deactivate_did_output(&did.0, gas_budget) @@ -196,4 +186,4 @@ impl WasmTransactionOutputPublishDid { pub fn response(&self) -> AdapterNativeResponse { self.0.response.clone_native_response() } -} \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs index 9c542bd234..b59f6d6a33 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs @@ -4,9 +4,9 @@ use std::rc::Rc; use std::str::FromStr; -use identity_iota::iota_interaction::types::base_types::ObjectID; use identity_iota::iota::rebased::client::IdentityClientReadOnly; use identity_iota::iota::rebased::migration::Identity; +use identity_iota::iota_interaction::types::base_types::ObjectID; use iota_interaction_ts::bindings::WasmIotaClient; use wasm_bindgen::prelude::*; @@ -52,8 +52,12 @@ impl WasmKinesisIdentityClientReadOnly { } #[wasm_bindgen(js_name = createWithPkgId)] - pub async fn new_new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: String) -> Result { - let inner_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; + pub async fn new_new_with_pkg_id( + iota_client: WasmIotaClient, + iota_identity_pkg_id: String, + ) -> Result { + let inner_client = + IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; Ok(WasmKinesisIdentityClientReadOnly(inner_client)) } @@ -90,17 +94,15 @@ impl WasmKinesisIdentityClientReadOnly { #[wasm_bindgen(js_name = resolveDid)] pub async fn resolve_did(&self, did: &WasmIotaDID) -> Result { - let document = self - .0 - .resolve_did(&did.0) - .await - .map_err(JsError::from)?; + let document = self.0.resolve_did(&did.0).await.map_err(JsError::from)?; Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) } #[wasm_bindgen(js_name = getIdentity)] pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { - let inner_value = self.0.get_identity(object_id.parse()?) + let inner_value = self + .0 + .get_identity(object_id.parse()?) .await .map_err(|err| JsError::new(&format!("failed to resolve identity by object id; {err:?}")))?; Ok(IdentityContainer(inner_value)) diff --git a/bindings/wasm/identity_wasm/src/storage/mod.rs b/bindings/wasm/identity_wasm/src/storage/mod.rs index 69fb983311..cb97eec532 100644 --- a/bindings/wasm/identity_wasm/src/storage/mod.rs +++ b/bindings/wasm/identity_wasm/src/storage/mod.rs @@ -9,7 +9,6 @@ mod jwt_presentation_options; mod key_id_storage; mod method_digest; mod signature_options; -mod storage_signer_owned; mod wasm_storage; mod wasm_storage_signer; mod wasm_transaction_signer; @@ -21,7 +20,6 @@ pub use jwt_presentation_options::*; pub use key_id_storage::*; pub use method_digest::*; pub use signature_options::*; -pub use storage_signer_owned::*; pub use wasm_storage::*; pub use wasm_storage_signer::*; pub use wasm_transaction_signer::*; diff --git a/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs b/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs deleted file mode 100644 index 29f450fbb1..0000000000 --- a/bindings/wasm/identity_wasm/src/storage/storage_signer_owned.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use identity_iota::iota_interaction::IotaKeySignature; -use identity_iota::verification::jwk::Jwk; -use identity_iota::verification::jwk::JwkParams; -use identity_iota::verification::jwu; -use secret_storage::Error as SecretStorageError; -use secret_storage::SignatureScheme; -use secret_storage::Signer; -use identity_iota::storage::JwkStorage; -use identity_iota::storage::KeyId; -use identity_iota::storage::KeyStorageErrorKind; - -use super::WasmStorage; - -/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`. -/// `Storage` is used to sign. -pub struct StorageSignerOwned { - key_id: KeyId, - public_key: Jwk, - storage: WasmStorage, -} - -impl StorageSignerOwned { - /// Creates new `StorageSignerOwned` with reference to a `Storage` instance. - pub fn new(storage: WasmStorage, key_id: KeyId, public_key: Jwk) -> Self { - Self { - key_id, - public_key, - storage, - } - } - - /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`]. - pub fn key_id(&self) -> &KeyId { - &self.key_id - } - - /// Returns this [`Signer`]'s public key as [`Jwk`]. - pub fn public_key(&self) -> &Jwk { - &self.public_key - } - - /// Returns a reference to this [`Signer`]'s [`Storage`]. - pub fn storage(&self) -> &WasmStorage { - &self.storage - } -} - -#[async_trait(?Send)] -impl Signer for StorageSignerOwned { - type KeyId = KeyId; - - fn key_id(&self) -> &KeyId { - &self.key_id - } - - async fn public_key(&self) -> Result<::PublicKey, SecretStorageError> { - match self.public_key.params() { - JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) - .map_err(|e| SecretStorageError::Other(anyhow::anyhow!("could not base64 decode key {}; {e}", self.key_id()))), - _ => todo!("add support for other key types"), - } - } - - async fn sign(&self, data: &[u8]) -> Result<::Signature, SecretStorageError> { - let key_storage = self - .storage - .key_storage(); - // call trait's sign impl for `WasmJwkStorage` instead of `WasmJwkStorage`s own impl - JwkStorage::sign(&key_storage, &self.key_id, data, &self.public_key) - .await - .map_err(|e| match e.kind() { - KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()), - KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()), - _ => SecretStorageError::Other(anyhow::anyhow!(e)), - }) - } -} diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs index 7e75091855..9a88bee98b 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs @@ -13,6 +13,7 @@ pub(crate) type WasmStorageInner = Storage; /// A type wrapping a `JwkStorage` and `KeyIdStorage` that should always be used together when /// working with storage backed DID documents. #[wasm_bindgen(js_name = Storage)] +#[derive(Clone)] pub struct WasmStorage(pub(crate) Arc); #[wasm_bindgen(js_class = Storage)] diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index 3c90497601..2bdabc2e8e 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -1,62 +1,62 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::iota_interaction::IotaKeySignature; use identity_iota::storage::KeyId; +use identity_iota::storage::StorageSigner; use identity_iota::verification::jwk::JwkParams; use identity_iota::verification::jwu; -use secret_storage::SignatureScheme; use secret_storage::Signer; use wasm_bindgen::prelude::*; -use crate::error::wasm_error; use crate::error::Result; +use crate::error::WasmResult; use crate::jose::WasmJwk; +use crate::storage::WasmJwkStorage; +use crate::storage::WasmKeyIdStorage; use crate::storage::WasmStorage; -use super::StorageSignerOwned; - #[wasm_bindgen(js_name = StorageSigner)] -pub struct WasmStorageSigner(pub(crate) StorageSignerOwned); +pub struct WasmStorageSigner { + storage: WasmStorage, + key_id: KeyId, + public_key: WasmJwk, +} + +impl WasmStorageSigner { + fn signer(&self) -> StorageSigner<'_, WasmJwkStorage, WasmKeyIdStorage> { + StorageSigner::new(&self.storage.0, self.key_id.clone(), self.public_key.0.clone()) + } +} #[wasm_bindgen(js_class = StorageSigner)] impl WasmStorageSigner { #[wasm_bindgen(constructor)] - pub fn new(storage: WasmStorage, key_id: String, public_key: WasmJwk) -> Self { - let signer = StorageSignerOwned::new( - storage, - KeyId::new(&key_id), - public_key.0, - ); - Self(signer) + pub fn new(storage: &WasmStorage, key_id: String, public_key: WasmJwk) -> Self { + Self { + storage: storage.clone(), + key_id: KeyId::new(key_id), + public_key, + } } #[wasm_bindgen(js_name = keyId)] pub fn key_id(&self) -> String { - self.0.key_id().to_string() + self.key_id.to_string() } #[wasm_bindgen(js_name = sign)] - pub async fn sign( - &self, - data: &[u8], - ) -> Result<::Signature> { - self.0.sign(data).await.map_err(wasm_error) + pub async fn sign(&self, data: &[u8]) -> Result> { + self.signer().sign(data).await.wasm_result() } - #[wasm_bindgen(js_name = publicKeyRaw)] - pub fn public_key_raw(&self) -> Result<::PublicKey> { - let jwk = self.0.public_key().clone(); + #[wasm_bindgen(js_name = publicKey)] + pub async fn public_key(&self) -> Result> { + let jwk = &self.public_key.0; match jwk.params() { JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) - .map_err(|e| JsValue::from_str(&format!("could not base64 decode key {}; {e}", self.key_id()))), + .map_err(|e| JsValue::from_str(&format!("could not base64 decode key {}; {e}", &self.key_id))), _ => todo!("add support for other key types"), } } - - #[wasm_bindgen(js_name = publicKey)] - pub async fn public_key(&self) -> Result> { - self.public_key_raw() - } } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index 59fc27ea87..1774ca0723 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use wasm_bindgen::prelude::wasm_bindgen; use identity_iota::iota::rebased::client::IotaKeySignature; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; +use wasm_bindgen::prelude::wasm_bindgen; use crate::error::Result; @@ -20,45 +20,41 @@ interface TransactionSigner { #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "TransactionSigner")] - pub type WasmTransactionSigner; - - #[wasm_bindgen(method, catch)] - pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; - #[wasm_bindgen(js_name = "publicKey", method, catch)] - pub async fn public_key(this: &WasmTransactionSigner) -> Result; - - // TODO: re-add WasmTransactionSigner::key_id - // #[wasm_bindgen(js_name = "keyId", structural, method)] - // // pub fn key_id(this: &WasmTransactionSigner) -> js_sys::JsString; - // pub fn key_id(this: &WasmTransactionSigner) -> String; + #[wasm_bindgen(typescript_type = "TransactionSigner")] + pub type WasmTransactionSigner; + + #[wasm_bindgen(method, catch)] + pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; + #[wasm_bindgen(js_name = "publicKey", method, catch)] + pub async fn public_key(this: &WasmTransactionSigner) -> Result; + + // TODO: re-add WasmTransactionSigner::key_id + // #[wasm_bindgen(js_name = "keyId", structural, method)] + // // pub fn key_id(this: &WasmTransactionSigner) -> js_sys::JsString; + // pub fn key_id(this: &WasmTransactionSigner) -> String; } #[async_trait(?Send)] impl Signer for WasmTransactionSigner { - type KeyId = String; - - async fn sign(&self, data: &[u8]) -> std::result::Result, SecretStorageError> { - self.sign(data).await.map(|v| v.to_vec()).map_err(|err| { - let details = err.as_string() - .map(|v| format!("; {}", v)) - .unwrap_or_default(); - let message = format!("could not sign data{details}"); - SecretStorageError::Other(anyhow::anyhow!(message)) - }) - } - - async fn public_key(&self) -> std::result::Result, SecretStorageError> { - self.public_key().await.map(|v| v.to_vec()).map_err(|err| { - let details = err.as_string() - .map(|v| format!("; {}", v)) - .unwrap_or_default(); - let message = format!("could not get public key{details}"); - SecretStorageError::KeyNotFound(message) - }) - } - - fn key_id(&self) -> &String { - todo!("WasmTransactionSigner::key_id"); - } -} \ No newline at end of file + type KeyId = String; + + async fn sign(&self, data: &[u8]) -> std::result::Result, SecretStorageError> { + self.sign(data).await.map(|v| v.to_vec()).map_err(|err| { + let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); + let message = format!("could not sign data{details}"); + SecretStorageError::Other(anyhow::anyhow!(message)) + }) + } + + async fn public_key(&self) -> std::result::Result, SecretStorageError> { + self.public_key().await.map(|v| v.to_vec()).map_err(|err| { + let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); + let message = format!("could not get public key{details}"); + SecretStorageError::KeyNotFound(message) + }) + } + + fn key_id(&self) -> &String { + todo!("WasmTransactionSigner::key_id"); + } +} diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index ff76fa8c67..53042947a9 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::OptionalSync; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwu; @@ -64,8 +65,8 @@ impl<'a, K, I> StorageSigner<'a, K, I> { #[cfg_attr(feature = "send-sync-storage", async_trait)] impl Signer for StorageSigner<'_, K, I> where - K: JwkStorage + Sync, - I: KeyIdStorage + Sync, + K: JwkStorage + OptionalSync, + I: KeyIdStorage + OptionalSync, { type KeyId = KeyId; fn key_id(&self) -> &KeyId { From 634d5be5860e91d19d1adb5e27b25c502c1633fc Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:49:47 +0100 Subject: [PATCH 34/63] Remove any mention of "Kinesis" from identity_wasm (#1524) * Kinesis renaming * Update bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts Co-authored-by: wulfraem * Update bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts Co-authored-by: wulfraem * Update bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts Co-authored-by: wulfraem * Update bindings/wasm/identity_wasm/src/lib.rs Co-authored-by: wulfraem --------- Co-authored-by: wulfraem --- .../examples/src/0_basic/-1_test_api_call.ts | 40 +++++++++---------- .../examples/src/0_basic/0_create_did.ts | 6 +-- .../examples/src/0_basic/1_update_did.ts | 6 +-- .../examples/src/0_basic/3_deactivate_did.ts | 6 +-- .../identity_wasm/examples/src/utils_alpha.ts | 20 +++++----- bindings/wasm/identity_wasm/src/error.rs | 4 +- bindings/wasm/identity_wasm/src/lib.rs | 3 +- .../client_dummy/identity.rs | 0 .../{kinesis => rebased}/client_dummy/mod.rs | 0 .../client_dummy/multicontroller.rs | 0 .../src/{kinesis => rebased}/identity.rs | 11 ++--- .../src/{kinesis => rebased}/mod.rs | 0 .../{kinesis => rebased}/multicontroller.rs | 0 .../src/{kinesis => rebased}/types.rs | 2 +- .../wasm_identity_client.rs | 19 ++++----- .../wasm_identity_client_builder.rs | 14 +++---- .../wasm_identity_client_read_only.rs | 22 +++++----- .../src/bindings/wasm_iota_client.rs | 2 +- 18 files changed, 72 insertions(+), 83 deletions(-) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/client_dummy/identity.rs (100%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/client_dummy/mod.rs (100%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/client_dummy/multicontroller.rs (100%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/identity.rs (95%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/mod.rs (100%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/multicontroller.rs (100%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/types.rs (94%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/wasm_identity_client.rs (91%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/wasm_identity_client_builder.rs (79%) rename bindings/wasm/identity_wasm/src/{kinesis => rebased}/wasm_identity_client_read_only.rs (79%) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts index 04fad6f9cb..ed85309315 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -8,8 +8,8 @@ import { JwkMemStore, JwsAlgorithm, KeyIdMemStore, - KinesisIdentityClient, - KinesisIdentityClientReadOnly, + IdentityClient, + IdentityClientReadOnly, Multicontroller, Storage, StorageSigner, @@ -17,7 +17,7 @@ import { import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; import { bcs } from "@iota/iota-sdk/bcs"; -import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { IotaClient } from "@iota/iota-sdk/client"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; import { Transaction } from "@iota/iota-sdk/transactions"; import { IOTA_TYPE_ARG } from "@iota/iota-sdk/utils"; @@ -25,8 +25,8 @@ import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BU async function initializeClients() { console.log("---------------- Preparing IdentityClient ------------------------"); - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId(kinesisClient, IDENTITY_IOTA_PACKAGE_ID); + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); // create new storage const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); @@ -41,27 +41,27 @@ async function initializeClients() { // create signer from storage let signer = new StorageSigner(storage, keyId, publicKeyJwk); - const identityClient = await KinesisIdentityClient.create(identityClientReadOnly, signer); + const identityClient = await IdentityClient.create(identityClientReadOnly, signer); await requestIotaFromFaucetV0({ host: getFaucetHost(NETWORK_NAME_FAUCET), recipient: identityClient.senderAddress(), }); - const balance = await kinesisClient.getBalance({ owner: identityClient.senderAddress() }); + const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); if (balance.totalBalance === "0") { throw new Error("Balance is still 0"); } else { console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); } - return { kinesisClient, identityClient }; + return { iotaClient, identityClient }; } async function testIdentityClientReadOnly() { - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - const identityClient = await KinesisIdentityClientReadOnly.createWithPkgId(kinesisClient, IDENTITY_IOTA_PACKAGE_ID); + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const identityClient = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); console.log("\n-------------- Start testIdentityClientReadOnly -------------------------------"); console.log(`networkName: ${identityClient.network()}`); @@ -79,8 +79,8 @@ async function testIdentityClientReadOnly() { async function testIdentityClient( - identityClient: KinesisIdentityClient, - kinesisClient: KinesisClient, + identityClient: IdentityClient, + iotaClient: IotaClient, ): Promise { console.log("\n-------------- Start testIdentityClient -------------------------------"); console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); @@ -100,7 +100,7 @@ async function testIdentityClient( recipient: identityClient.senderAddress(), }); - const balance = await kinesisClient.getBalance({ owner: identityClient.senderAddress() }); + const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); if (balance.totalBalance === "0") { throw new Error("Balance is still 0"); } else { @@ -173,7 +173,7 @@ async function signerTest(): Promise { console.dir({ signed }); } -async function testExecuteTransaction(kinesisClient: KinesisClient) { +async function testExecuteTransaction(iotaClient: IotaClient) { console.log("---------------- testing executeTransaction ------------------------"); // create new storage @@ -198,7 +198,7 @@ async function testExecuteTransaction(kinesisClient: KinesisClient) { }); // try to craft tx with js api - let coins = await kinesisClient.getCoins({ + let coins = await iotaClient.getCoins({ owner: address, coinType: IOTA_TYPE_ARG, }); @@ -211,10 +211,10 @@ async function testExecuteTransaction(kinesisClient: KinesisClient) { tx.setSenderIfNotSet(address); let response = await executeTransaction( - kinesisClient, + iotaClient, address, publicJwk, - await tx.build({ client: kinesisClient }), + await tx.build({ client: iotaClient }), signer, ); console.dir(response); @@ -223,7 +223,7 @@ async function testExecuteTransaction(kinesisClient: KinesisClient) { /** Test API usage */ export async function testApiCall(): Promise { - const { kinesisClient, identityClient } = await initializeClients(); + const { iotaClient, identityClient } = await initializeClients(); try { await testIdentityClientReadOnly(); @@ -233,7 +233,7 @@ export async function testApiCall(): Promise { } try { - await testIdentityClient(identityClient, kinesisClient); + await testIdentityClient(identityClient, iotaClient); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`identity client binding test failed: ${suffix}`); @@ -254,7 +254,7 @@ export async function testApiCall(): Promise { } try { - await testExecuteTransaction(kinesisClient); + await testExecuteTransaction(iotaClient); } catch (err) { const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; console.error(`signer binding test failed: ${suffix}`); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 5abbd5fa19..98469b99ae 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { IotaDID } from '@iota/identity-wasm/node'; -import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, @@ -13,8 +13,8 @@ import { /** Demonstrate how to create a DID Document and publish it. */ export async function createIdentity(): Promise { // create new client to connect to IOTA network - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - const network = await kinesisClient.getChainIdentifier(); + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); // TODO: check if we can update storage implementation to a non-owning variant diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index e72f000106..dde2091c06 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -11,7 +11,7 @@ import { Timestamp, VerificationMethod, } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, @@ -23,8 +23,8 @@ import { /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { // create new clients and create new account - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - const network = await kinesisClient.getChainIdentifier(); + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 0bfb077291..823d371e40 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { IotaDID } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getClientAndCreateAccount, @@ -14,8 +14,8 @@ import { /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { // create new clients and create new account - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); - const network = await kinesisClient.getChainIdentifier(); + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts index cd9bd45b10..ba28c9a66c 100644 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts @@ -6,13 +6,13 @@ import { JwkMemStore, JwsAlgorithm, KeyIdMemStore, - KinesisIdentityClient, - KinesisIdentityClientReadOnly, + IdentityClient, + IdentityClientReadOnly, MethodScope, Storage, StorageSigner, } from "@iota/identity-wasm/node"; -import { IotaClient as KinesisClient } from "@iota/iota-sdk/client"; +import { IotaClient } from "@iota/iota-sdk/client"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; export const IDENTITY_IOTA_PACKAGE_ID = @@ -41,15 +41,15 @@ export async function createDocumentForNetwork(storage: Storage, network: string return [unpublished, verificationMethodFragment]; } -export async function getClientAndCreateAccount(storage: Storage): Promise { +export async function getClientAndCreateAccount(storage: Storage): Promise { if (!IDENTITY_IOTA_PACKAGE_ID) { throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); } - const kinesisClient = new KinesisClient({ url: NETWORK_URL }); + const iotaClient = new IotaClient({ url: NETWORK_URL }); - const identityClientReadOnly = await KinesisIdentityClientReadOnly.createWithPkgId( - kinesisClient, IDENTITY_IOTA_PACKAGE_ID); + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( + iotaClient, IDENTITY_IOTA_PACKAGE_ID); // generate new key let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); @@ -61,14 +61,14 @@ export async function getClientAndCreateAccount(storage: Storage): Promise { let tx = identityClient diff --git a/bindings/wasm/identity_wasm/src/error.rs b/bindings/wasm/identity_wasm/src/error.rs index 114e81856b..a5a07247ae 100644 --- a/bindings/wasm/identity_wasm/src/error.rs +++ b/bindings/wasm/identity_wasm/src/error.rs @@ -313,7 +313,7 @@ impl JsValueResult { self.stringify_error().map_err(identity_iota::iota::Error::JsError) } - pub fn to_kinesis_client_error(self) -> StdResult { + pub fn to_iota_client_error(self) -> StdResult { self .stringify_error() .map_err(|e| identity_iota::iota::rebased::Error::FfiError(e.to_string())) @@ -361,7 +361,7 @@ impl serde::Deserialize<'a>> From for KeyIdStorageResu impl serde::Deserialize<'a>> From for StdResult { fn from(result: JsValueResult) -> Self { - result.to_kinesis_client_error().and_then(|js_value| { + result.to_iota_client_error().and_then(|js_value| { js_value .into_serde() .map_err(|e| identity_iota::iota::rebased::Error::FfiError(e.to_string())) diff --git a/bindings/wasm/identity_wasm/src/lib.rs b/bindings/wasm/identity_wasm/src/lib.rs index 2373fb9819..a8116d0183 100644 --- a/bindings/wasm/identity_wasm/src/lib.rs +++ b/bindings/wasm/identity_wasm/src/lib.rs @@ -33,8 +33,7 @@ pub mod verification; #[cfg(feature = "dummy-client")] // Currently it's unclear if this module will be removed or can be used for integration or unit tests. -// TODO manage the final location of the kinesis TS module -pub(crate) mod kinesis; +pub(crate) mod rebased; #[cfg(feature = "dummy-client")] // Remove this module when working on [Issue #1445 Replace mocked Identity client with real Identity client] pub(crate) mod obsolete; diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs similarity index 100% rename from bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs rename to bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs similarity index 100% rename from bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs rename to bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs similarity index 100% rename from bindings/wasm/identity_wasm/src/kinesis/client_dummy/multicontroller.rs rename to bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs similarity index 95% rename from bindings/wasm/identity_wasm/src/kinesis/identity.rs rename to bindings/wasm/identity_wasm/src/rebased/identity.rs index 23a3ab6a08..4e2c5a6478 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -25,7 +25,7 @@ use super::client_dummy::ProposalBuilder; use super::types::into_sdk_type; use super::types::WasmIotaAddress; use super::types::WasmObjectID; -use super::WasmKinesisIdentityClient; +use super::WasmIdentityClient; use super::WasmProposal; /// Helper type for `WasmIdentityBuilder::controllers`. @@ -82,7 +82,7 @@ impl WasmOnChainIdentity { #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below pub async fn get_history( &self, - client: WasmKinesisIdentityClient, + client: WasmIdentityClient, last_version: Option, page_size: Option, ) -> Result { @@ -156,7 +156,7 @@ impl WasmProposalBuilder { Self(self.0.gas_budget(amount)) } - pub async fn finish(self, client: &WasmKinesisIdentityClient, signer: &DummySigner) -> Result> { + pub async fn finish(self, client: &WasmIdentityClient, signer: &DummySigner) -> Result> { unimplemented!("WasmProposalBuilder::finish"); // self // .0 @@ -223,10 +223,7 @@ pub struct WasmCreateIdentityTx(pub(crate) CreateIdentityTx); #[wasm_bindgen(js_class = CreateIdentityTx)] impl WasmCreateIdentityTx { #[wasm_bindgen] - pub async fn execute( - self, - client: &WasmKinesisIdentityClient, - ) -> Result { + pub async fn execute(self, client: &WasmIdentityClient) -> Result { let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputInternalOnChainIdentity(output)) } diff --git a/bindings/wasm/identity_wasm/src/kinesis/mod.rs b/bindings/wasm/identity_wasm/src/rebased/mod.rs similarity index 100% rename from bindings/wasm/identity_wasm/src/kinesis/mod.rs rename to bindings/wasm/identity_wasm/src/rebased/mod.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs similarity index 100% rename from bindings/wasm/identity_wasm/src/kinesis/multicontroller.rs rename to bindings/wasm/identity_wasm/src/rebased/multicontroller.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/types.rs b/bindings/wasm/identity_wasm/src/rebased/types.rs similarity index 94% rename from bindings/wasm/identity_wasm/src/kinesis/types.rs rename to bindings/wasm/identity_wasm/src/rebased/types.rs index 7ef4ac6e97..531e16177e 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/types.rs +++ b/bindings/wasm/identity_wasm/src/rebased/types.rs @@ -18,7 +18,7 @@ pub fn into_sdk_type>(wasm_type_instance: // TODO: Replace all console_log! usages by proper Error management and Result types. // Use console_log! only for debug purposes console_log!( - "[identity_wasm::kinesis::types - fn into_sdk_type]\n js_value: {:?}\n Error: {:?}", + "[identity_wasm::rebased::types - fn into_sdk_type]\n js_value: {:?}\n Error: {:?}", js_value, e ); diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs similarity index 91% rename from bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs rename to bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index be7a98b214..9699d0b971 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -20,7 +20,7 @@ use iota_interaction_ts::AdapterNativeResponse; use super::IdentityContainer; use super::WasmIdentityBuilder; -use super::WasmKinesisIdentityClientReadOnly; +use super::WasmIdentityClientReadOnly; use crate::error::wasm_error; use crate::iota::IotaDocumentLock; @@ -44,18 +44,15 @@ pub struct WasmIotaTransactionBlockResponseEssence { pub effects_created: Option>, } -#[wasm_bindgen(js_name = KinesisIdentityClient)] -pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); +#[wasm_bindgen(js_name = IdentityClient)] +pub struct WasmIdentityClient(pub(crate) IdentityClient); -#[wasm_bindgen(js_class = KinesisIdentityClient)] -impl WasmKinesisIdentityClient { +#[wasm_bindgen(js_class = IdentityClient)] +impl WasmIdentityClient { #[wasm_bindgen(js_name = create)] - pub async fn new( - client: WasmKinesisIdentityClientReadOnly, - signer: WasmTransactionSigner, - ) -> Result { + pub async fn new(client: WasmIdentityClientReadOnly, signer: WasmTransactionSigner) -> Result { let inner_client = IdentityClient::new(client.0, signer).await?; - Ok(WasmKinesisIdentityClient(inner_client)) + Ok(WasmIdentityClient(inner_client)) } #[wasm_bindgen(js_name = senderPublicKey)] @@ -166,7 +163,7 @@ pub struct WasmPublishDidTx(pub(crate) PublishDidTx); #[wasm_bindgen(js_class = PublishDidTx)] impl WasmPublishDidTx { #[wasm_bindgen(js_name = execute)] - pub async fn execute(self, client: &WasmKinesisIdentityClient) -> Result { + pub async fn execute(self, client: &WasmIdentityClient) -> Result { let output = self.0.execute(&client.0).await.map_err(wasm_error)?; Ok(WasmTransactionOutputPublishDid(output)) } diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_builder.rs similarity index 79% rename from bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs rename to bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_builder.rs index c799a4b10e..7217dd3e4a 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_builder.rs @@ -16,14 +16,14 @@ use crate::error::Result; use super::types::WasmObjectID; use super::WasmIotaAddress; -use super::WasmKinesisIdentityClient; +use super::WasmIdentityClient; #[derive(Default)] -#[wasm_bindgen(js_name = KinesisIdentityClientBuilder)] -pub struct WasmKinesisIdentityClientBuilder(pub(crate) IdentityClientBuilder); +#[wasm_bindgen(js_name = IdentityClientBuilder)] +pub struct WasmIdentityClientBuilder(pub(crate) IdentityClientBuilder); -#[wasm_bindgen(js_class = KinesisIdentityClientBuilder)] -impl WasmKinesisIdentityClientBuilder { +#[wasm_bindgen(js_class = IdentityClientBuilder)] +impl WasmIdentityClientBuilder { #[wasm_bindgen(js_name = identityIotaPackageId)] pub fn identity_iota_package_id(self, value: WasmObjectID) -> Self { Self( @@ -63,7 +63,7 @@ impl WasmKinesisIdentityClientBuilder { Self(self.0.network_name(value)) } - pub fn build(self) -> Result { - Ok(WasmKinesisIdentityClient(self.0.build().map_err(wasm_error)?)) + pub fn build(self) -> Result { + Ok(WasmIdentityClient(self.0.build().map_err(wasm_error)?)) } } diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs similarity index 79% rename from bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs rename to bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs index b59f6d6a33..76b6ff07aa 100644 --- a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs @@ -39,26 +39,22 @@ impl IdentityContainer { // } } -#[wasm_bindgen(js_name = KinesisIdentityClientReadOnly)] -pub struct WasmKinesisIdentityClientReadOnly(pub(crate) IdentityClientReadOnly); +#[wasm_bindgen(js_name = IdentityClientReadOnly)] +pub struct WasmIdentityClientReadOnly(pub(crate) IdentityClientReadOnly); // builder related functions -#[wasm_bindgen(js_class = KinesisIdentityClientReadOnly)] -impl WasmKinesisIdentityClientReadOnly { +#[wasm_bindgen(js_class = IdentityClientReadOnly)] +impl WasmIdentityClientReadOnly { #[wasm_bindgen(js_name = create)] - pub async fn new(iota_client: WasmIotaClient) -> Result { + pub async fn new(iota_client: WasmIotaClient) -> Result { let inner_client = IdentityClientReadOnly::new(iota_client).await?; - Ok(WasmKinesisIdentityClientReadOnly(inner_client)) + Ok(WasmIdentityClientReadOnly(inner_client)) } #[wasm_bindgen(js_name = createWithPkgId)] - pub async fn new_new_with_pkg_id( - iota_client: WasmIotaClient, - iota_identity_pkg_id: String, - ) -> Result { - let inner_client = - IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; - Ok(WasmKinesisIdentityClientReadOnly(inner_client)) + pub async fn new_new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: String) -> Result { + let inner_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; + Ok(WasmIdentityClientReadOnly(inner_client)) } #[wasm_bindgen(js_name = packageId)] diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs index 4198155652..f94ee3ff6d 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -63,7 +63,7 @@ use crate::error::TsSdkError; // can be used as arguments for rust functions via the typescript_type annotation. // In other words: The typescript_type "IotaClient" is imported here to be bound // to the WasmIotaClient functions below. -// TODO: check why this isn't done by `module` macro attribute for `WasmKinesisClient` +// TODO: check why this isn't done by `module` macro attribute for `WasmIotaClient` #[wasm_bindgen(typescript_custom_section)] const IOTA_CLIENT_TYPE: &'static str = r#" import { IotaClient } from "@iota/iota-sdk/client"; From d679051823889f9367f5301ff77a48a7c74308fe Mon Sep 17 00:00:00 2001 From: wulfraem Date: Wed, 19 Feb 2025 08:04:29 +0100 Subject: [PATCH 35/63] Re-add Resolver API for WASM builds (#1526) * add 2_resolve_did example * add custom resolver example * add include custom resolution example in main * fix outdated wording in examples * update examples to use a more natural order of calls, remove unused variables * update resolver logic - resolver can be created with read-only and with write capable clients - `IotaDocument` now serialize to full serialized `IotaDocument` intead of just the `CoreDocument` part when calling `.to_json()`/`.toJSON()` - this affects the documents resolved via `Resolver` as well - `Resolver` class now accepts generic type parameter to specify the type of the resolved documents * fix missing imports in examples * update `actions/cache` version see https://github.blog/changelog/2024-12-05-notice-of-upcoming-releases-and-breaking-changes-for-github-actions/#actions-cache-v1-v2-and-actions-toolkit-cache-package-closing-down * Update bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> * fix typo found in review --------- Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> --- .github/actions/rust/rust-setup/action.yml | 6 +- bindings/wasm/identity_wasm/Cargo.toml | 4 - .../examples/src/0_basic/0_create_did.ts | 7 +- .../examples/src/0_basic/1_update_did.ts | 2 +- .../examples/src/0_basic/2_resolve_did.ts | 76 +++++++++++++------ .../examples/src/0_basic/3_deactivate_did.ts | 2 +- .../src/1_advanced/4_custom_resolution.ts | 73 ++++++++++-------- .../wasm/identity_wasm/examples/src/main.ts | 12 +-- bindings/wasm/identity_wasm/lib/index.ts | 3 + bindings/wasm/identity_wasm/lib/resolver.ts | 37 +++++++++ bindings/wasm/identity_wasm/src/error.rs | 2 - .../identity_wasm/src/iota/iota_document.rs | 4 +- .../wasm/identity_wasm/src/resolver/mod.rs | 3 +- .../src/resolver/resolver_config.rs | 8 +- .../resolver/wasm_did_resolution_handler.rs | 45 +++++++++++ .../src/resolver/wasm_resolver.rs | 25 +++--- examples/1_advanced/10_zkp_revocation.rs | 1 + examples/1_advanced/9_zkp.rs | 1 + identity_iota/Cargo.toml | 2 +- identity_iota/src/lib.rs | 6 +- identity_resolver/src/resolution/resolver.rs | 2 +- 21 files changed, 220 insertions(+), 101 deletions(-) create mode 100644 bindings/wasm/identity_wasm/lib/resolver.ts create mode 100644 bindings/wasm/identity_wasm/src/resolver/wasm_did_resolution_handler.rs diff --git a/.github/actions/rust/rust-setup/action.yml b/.github/actions/rust/rust-setup/action.yml index 5f783a98cc..b8e968addf 100644 --- a/.github/actions/rust/rust-setup/action.yml +++ b/.github/actions/rust/rust-setup/action.yml @@ -90,7 +90,7 @@ runs: rustup show - name: Cache cargo - uses: actions/cache@v2.1.7 + uses: actions/cache@v4 if: inputs.cargo-cache-enabled == 'true' with: # https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci @@ -115,7 +115,7 @@ runs: shell: bash - name: Cache build target - uses: actions/cache@v2.1.7 + uses: actions/cache@v4 if: inputs.target-cache-enabled == 'true' with: path: ${{ inputs.target-cache-path }} @@ -127,7 +127,7 @@ runs: ${{ inputs.os }}-target- - name: Cache sccache - uses: actions/cache@v2.1.7 + uses: actions/cache@v4 if: inputs.sccache-enabled == 'true' with: path: ${{ inputs.sccache-path }} diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index ce557aca9d..144e6312f2 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -76,7 +76,3 @@ empty_docs = "allow" [features] default = ["dummy-client"] dummy-client = ["dep:iota_interaction_ts"] -# As identity_iota::resolver is currently not available for wasm32 this temporary flag is used -# to switch of all code depending on identity_iota::resolver -# TODO: Remove this feature flag after identity_iota::resolver is available for wasm32 platforms -wasm-resolver = [] diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 98469b99ae..597ee81ffa 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -16,13 +16,12 @@ export async function createIdentity(): Promise { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); - const storage = getMemstorage(); - // TODO: check if we can update storage implementation to a non-owning variant - // order is important here as wrapped storage will be set to a null pointer after passing it to a client - const [unpublished] = await createDocumentForNetwork(storage, network); // create new client that offers identity related functions + const storage = getMemstorage(); const identityClient = await getClientAndCreateAccount(storage); + // create new unpublished document + const [unpublished] = await createDocumentForNetwork(storage, network); console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); let did: IotaDID; diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index dde2091c06..4664e2ff13 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -26,8 +26,8 @@ export async function updateIdentity() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); + const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); // create new identity for this account and publish document for it const { output: identity } = await identityClient diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index 2792f7ad05..fa98ac9ef5 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -1,56 +1,84 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// TODO: -// - [ ] clarify if we need/want a resolver example -// - [ ] clarify if we need/want the AliasOutput -> ObjectID example - - import { CoreDocument, DIDJwk, + IdentityClientReadOnly, + IotaDID, IotaDocument, - IotaIdentityClient, IToCoreDocument, - JwkMemStore, - KeyIdMemStore, Resolver, - Storage, } from "@iota/identity-wasm/node"; -import { AliasOutput, } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; -import { createDidDocument, getClientAndCreateAccount, getMemstorage } from "../utils_alpha"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getClientAndCreateAccount, + getMemstorage, + IDENTITY_IOTA_PACKAGE_ID, + NETWORK_URL, +} from '../utils_alpha'; const DID_JWK: string = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ export async function resolveIdentity() { - // create new client to interact with chain and get funded account with keys + // create new clients and create new account + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); const identityClient = await getClientAndCreateAccount(storage); - - // create new DID document and publish it - let [document] = await createDidDocument(identityClient, storage); - let did = document.id(); + const [unpublished] = await createDocumentForNetwork(storage, network); + + // create new identity for this account and publish document for it + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); // Resolve the associated Alias Output and extract the DID document from it. - const resolved: IotaDocument = await identityClient.resolveDid(did); + const resolved = await identityClient.resolveDid(did); console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); - // We can also resolve the Object ID reictly - const aliasOutput: AliasOutput = await identityClient.resolveDidOutput(did); - console.log("The Alias Output holds " + aliasOutput.getAmount() + " tokens"); + // We can resolve the Object ID directly + const resolvedIdentity = await identityClient.getIdentity(identity.id()); + console.dir(resolvedIdentity); + console.log(`Resolved identity has object ID ${resolvedIdentity.toFullFledged()?.id()}`); + + // Or we can resolve it via the `Resolver` api: - // did:jwk can be resolved as well. + // While at it, define a custom resolver for jwk DIDs as well. const handlers = new Map Promise>(); handlers.set("jwk", didJwkHandler); - const resolver = new Resolver({ handlers }); + + // Create new `Resolver` instance with the client with write capabilities we already have at hand + const resolver = new Resolver({ client: identityClient, handlers }); + + // and resolve identity DID with it. + const resolverResolved = await resolver.resolve(did.toString()); + console.log(`resolverResolved ${did.toString()} resolves to:\n ${JSON.stringify(resolverResolved, null, 2)}`); + + // We can also resolve via the custom resolver defined before: const did_jwk_resolved_doc = await resolver.resolve(DID_JWK); console.log(`DID ${DID_JWK} resolves to:\n ${JSON.stringify(did_jwk_resolved_doc, null, 2)}`); + + // We can also create a resolver with a read-only client + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); + // In this case we will only be resolving `IotaDocument` instances, as we don't pass a `handler` configuration. + // Therefore we can limit the type of the resolved documents to `IotaDocument` when creating the new resolver as well. + const resolverWithReadOnlyClient = new Resolver({ client: identityClientReadOnly }); + + // And resolve as before. + const resolvedViaReadOnly = await resolverWithReadOnlyClient.resolve(did.toString()); + console.log(`resolverWithReadOnlyClient ${did.toString()} resolves to:\n ${JSON.stringify(resolvedViaReadOnly, null, 2)}`); + + // As our `Resolver` instance will only return `IotaDocument` instances, we can directly work with them, e.g. + console.log(`${did.toString()}'s metadata is ${resolvedViaReadOnly.metadata()}`); } const didJwkHandler = async (did: string) => { let did_jwk = DIDJwk.parse(did); return CoreDocument.expandDIDJwk(did_jwk); -}; +}; \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 823d371e40..4e70d336a1 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -17,8 +17,8 @@ export async function deactivateIdentity() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); const identityClient = await getClientAndCreateAccount(storage); + const [unpublished] = await createDocumentForNetwork(storage, network); // create new identity for this account and publish document for it const { output: identity } = await identityClient diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts index e0cf9a7bda..3380647b24 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts @@ -1,44 +1,64 @@ import { CoreDocument, + IotaDID, IotaDocument, - IotaIdentityClient, - JwkMemStore, - KeyIdMemStore, Resolver, - Storage, } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getClientAndCreateAccount, + getMemstorage, + NETWORK_URL, +} from '../utils_alpha'; // Use this external package to avoid implementing the entire did:key method in this example. import * as ed25519 from "@transmute/did-key-ed25519"; +type KeyDocument = { customProperty: String } & CoreDocument; + +function isKeyDocument(doc: object): doc is KeyDocument { + return 'customProperty' in doc; +} + /** Demonstrates how to set up a resolver using custom handlers. */ export async function customResolution() { // Set up a handler for resolving Ed25519 did:key - const keyHandler = async function(didKey: string): Promise { + const keyHandler = async function(didKey: string): Promise { let document = await ed25519.resolve( didKey, { accept: "application/did+ld+json" }, ); - return CoreDocument.fromJSON(document.didDocument); + + // for demo purposes we'll just inject the custom property into a core document + // instead of creating a proper instance + let coreDocument = CoreDocument.fromJSON(document.didDocument); + (coreDocument as unknown as KeyDocument).customProperty = "foobar"; + return coreDocument as unknown as KeyDocument; }; - // Create a new Client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); + // create new clients and create new account + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + const storage = getMemstorage(); + const identityClient = await getClientAndCreateAccount(storage); + const [unpublished] = await createDocumentForNetwork(storage, network); + + // create new identity for this account and publish document for it, DID of it will be resolved later on + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); // Construct a Resolver capable of resolving the did:key and iota methods. - let handlerMap: Map Promise> = new Map(); + let handlerMap: Map Promise> = new Map(); handlerMap.set("key", keyHandler); - const resolver = new Resolver( + const resolver = new Resolver( { - client: didClient, + client: identityClient, handlers: handlerMap, }, ); @@ -46,30 +66,17 @@ export async function customResolution() { // A valid Ed25519 did:key value taken from https://w3c-ccg.github.io/did-method-key/#example-1-a-simple-ed25519-did-key-value. const didKey = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity for us to resolve (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - const did = document.id(); - // Resolve didKey into a DID document. const didKeyDoc = await resolver.resolve(didKey); - // Resolve the DID we created on the IOTA ledger. + // Resolve the DID we created on the IOTA network. const didIotaDoc = await resolver.resolve(did.toString()); // Check that the types of the resolved documents match our expectations: - if (didKeyDoc instanceof CoreDocument) { + if (isKeyDocument(didKeyDoc)) { console.log("Resolved DID Key document:", JSON.stringify(didKeyDoc, null, 2)); + console.log(`Resolved DID Key document has a custom property with the value '${didKeyDoc.customProperty}'`); } else { throw new Error( "the resolved document type should match the output type of keyHandler", diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts index a7e4d817ee..dac40d5fbd 100644 --- a/bindings/wasm/identity_wasm/examples/src/main.ts +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -4,7 +4,7 @@ import { testApiCall } from "./0_basic/-1_test_api_call"; import { createIdentity } from "./0_basic/0_create_did"; import { updateIdentity } from "./0_basic/1_update_did"; -// import { resolveIdentity } from "./0_basic/2_resolve_did"; +import { resolveIdentity } from "./0_basic/2_resolve_did"; import { deactivateIdentity } from "./0_basic/3_deactivate_did"; // import { deleteIdentity } from "./0_basic/4_delete_did"; // import { createVC } from "./0_basic/5_create_vc"; @@ -14,7 +14,7 @@ import { deactivateIdentity } from "./0_basic/3_deactivate_did"; // import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; // import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; // import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; -// import { customResolution } from "./1_advanced/4_custom_resolution"; +import { customResolution } from "./1_advanced/4_custom_resolution"; // import { domainLinkage } from "./1_advanced/5_domain_linkage"; // import { sdJwt } from "./1_advanced/6_sd_jwt"; // import { statusList2021 } from "./1_advanced/7_status_list_2021"; @@ -35,8 +35,8 @@ async function main() { return await createIdentity(); case "1_update_did": return await updateIdentity(); - // case "2_resolve_did": - // return await resolveIdentity(); + case "2_resolve_did": + return await resolveIdentity(); case "3_deactivate_did": return await deactivateIdentity(); // case "4_delete_did": @@ -55,8 +55,8 @@ async function main() { // return await nftOwnsDid(); // case "3_did_issues_tokens": // return await didIssuesTokens(); - // case "4_custom_resolution": - // return await customResolution(); + case "4_custom_resolution": + return await customResolution(); // case "5_domain_linkage": // return await domainLinkage(); // case "6_sd_jwt": diff --git a/bindings/wasm/identity_wasm/lib/index.ts b/bindings/wasm/identity_wasm/lib/index.ts index e9ab2f00b5..7a0a246940 100644 --- a/bindings/wasm/identity_wasm/lib/index.ts +++ b/bindings/wasm/identity_wasm/lib/index.ts @@ -9,3 +9,6 @@ export * from "./jwk_storage"; export * from "./key_id_storage"; export * from "~identity_wasm"; + +// keep this export last to override the original `Resolver` from `identity_wasm` in the exports +export { Resolver } from "./resolver"; diff --git a/bindings/wasm/identity_wasm/lib/resolver.ts b/bindings/wasm/identity_wasm/lib/resolver.ts new file mode 100644 index 0000000000..611fc278e2 --- /dev/null +++ b/bindings/wasm/identity_wasm/lib/resolver.ts @@ -0,0 +1,37 @@ +// Copyright 2021-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { CoreDocument, IToCoreDocument, Resolver as ResolverInner } from "~identity_wasm"; + +// `Resolver` type below acts the same as the "normal" resolver from `~identity_wasm` +// with the difference being that the `Resolver` here allows to pass generic type params to +// the constructor to specify the types expected to be returned by the `resolve` function. + +/** +* Convenience type for resolving DID documents from different DID methods. +* +* DID documents resolved with `resolve` will have the type specified as generic type parameter T. +* With the default being `CoreDocument | IToCoreDocument`. +* +* Also provides methods for resolving DID Documents associated with +* verifiable {@link Credential}s and {@link Presentation}s. +* +* # Configuration +* +* The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. +*/ +export class Resolver extends ResolverInner { + /** + * Fetches the DID Document of the given DID. + * + * ### Errors + * + * Errors if the resolver has not been configured to handle the method + * corresponding to the given DID or the resolution process itself fails. + * @param {string} did + * @returns {Promise} + */ + async resolve(did: string): Promise { + return super.resolve(did) as unknown as T; + } + } \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/error.rs b/bindings/wasm/identity_wasm/src/error.rs index a5a07247ae..9e9604682d 100644 --- a/bindings/wasm/identity_wasm/src/error.rs +++ b/bindings/wasm/identity_wasm/src/error.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::credential::CompoundJwtPresentationValidationError; -#[cfg(feature = "wasm-resolver")] use identity_iota::resolver; use identity_iota::storage::key_id_storage::KeyIdStorageError; use identity_iota::storage::key_id_storage::KeyIdStorageErrorKind; @@ -159,7 +158,6 @@ impl<'a, E: std::error::Error> Display for ErrorMessage<'a, E> { } } -#[cfg(feature = "wasm-resolver")] impl From for WasmError<'_> { fn from(error: resolver::Error) -> Self { Self { diff --git a/bindings/wasm/identity_wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs index 3716bb580d..41cd17cc7b 100644 --- a/bindings/wasm/identity_wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -648,7 +648,9 @@ impl WasmIotaDocument { /// Serializes to a plain JS representation. #[wasm_bindgen(js_name = toJSON)] pub fn to_json(&self) -> Result { - JsValue::from_serde(&self.0.try_read()?.as_ref()).wasm_result() + let read_guard = self.0.try_read()?; + let iota_document: &IotaDocument = &read_guard; + JsValue::from_serde(&iota_document).wasm_result() } /// Deserializes an instance from a plain JS representation. diff --git a/bindings/wasm/identity_wasm/src/resolver/mod.rs b/bindings/wasm/identity_wasm/src/resolver/mod.rs index beb0467d3a..636bc6b85d 100644 --- a/bindings/wasm/identity_wasm/src/resolver/mod.rs +++ b/bindings/wasm/identity_wasm/src/resolver/mod.rs @@ -3,7 +3,8 @@ mod resolver_config; mod resolver_types; -#[cfg(feature = "wasm-resolver")] +mod wasm_did_resolution_handler; mod wasm_resolver; pub use resolver_types::*; +pub use wasm_did_resolution_handler::WasmDidResolutionHandler; diff --git a/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs b/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs index aba402d8c7..4094060030 100644 --- a/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs +++ b/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::*; -use crate::iota::WasmIotaIdentityClient; +use super::WasmDidResolutionHandler; #[wasm_bindgen] extern "C" { @@ -14,7 +14,7 @@ extern "C" { pub type ResolverConfig; #[wasm_bindgen(method, getter)] - pub(crate) fn client(this: &ResolverConfig) -> Option; + pub(crate) fn client(this: &ResolverConfig) -> Option; #[wasm_bindgen(method, getter)] pub(crate) fn handlers(this: &ResolverConfig) -> Option; @@ -33,9 +33,9 @@ const TS_RESOLVER_CONFIG: &'static str = r#" */ export type ResolverConfig = { /** - * Client for resolving DIDs of the iota method. + * Client for resolving DIDs of the iota method, usually an {@link IdentityClient} or an {@link IdentityClientReadOnly} */ - client?: IIotaIdentityClient, + client?: WasmDidResolutionHandler, /** * Handlers for resolving DIDs from arbitrary DID methods. diff --git a/bindings/wasm/identity_wasm/src/resolver/wasm_did_resolution_handler.rs b/bindings/wasm/identity_wasm/src/resolver/wasm_did_resolution_handler.rs new file mode 100644 index 0000000000..c5413afa26 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/resolver/wasm_did_resolution_handler.rs @@ -0,0 +1,45 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::iota::DidResolutionHandler; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use js_sys::Promise; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; + +use crate::error::JsValueResult; +use crate::iota::PromiseIotaDocument; +use crate::iota::WasmIotaDID; + +#[wasm_bindgen(typescript_custom_section)] +const WASM_DID_RESOLUTION_HANDLER: &str = r#" +interface WasmDidResolutionHandler { + resolveDid: (did: IotaDID) => Promise; +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "WasmDidResolutionHandler")] + pub type WasmDidResolutionHandler; + + #[wasm_bindgen(js_name = "resolveDid", method)] + pub fn resolve_did(this: &WasmDidResolutionHandler, did: WasmIotaDID) -> PromiseIotaDocument; +} + +#[async_trait::async_trait(?Send)] +impl DidResolutionHandler for WasmDidResolutionHandler { + async fn resolve_did(&self, did: &IotaDID) -> Result { + let promise: Promise = Promise::resolve(&self.resolve_did(WasmIotaDID(did.clone()))); + let result: JsValueResult = JsFuture::from(promise).await.into(); + let js_value: JsValue = result.to_iota_core_error()?; + + js_value.into_serde().map_err(|err| { + identity_iota::iota::Error::JsError(format!( + "failed to parse resolved DID document to `IotaDocument`: {err}" + )) + }) + } +} diff --git a/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs b/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs index da680e0877..f5683caba0 100644 --- a/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs +++ b/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs @@ -6,6 +6,7 @@ use std::rc::Rc; use identity_iota::did::CoreDID; use identity_iota::did::DID; +use identity_iota::iota::DidResolutionHandler; use identity_iota::iota::IotaDID; use identity_iota::resolver::SingleThreadedResolver; use js_sys::Array; @@ -18,14 +19,12 @@ use wasm_bindgen_futures::JsFuture; use crate::common::ArrayString; use crate::error::JsValueResult; use crate::error::WasmError; -use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; -use crate::iota::WasmIotaIdentityClient; -use crate::obsolete::IotaIdentityClientExt; use crate::resolver::resolver_config::MapResolutionHandler; use crate::resolver::resolver_config::ResolverConfig; use crate::resolver::PromiseArrayIToCoreDocument; +use crate::resolver::WasmDidResolutionHandler; use super::resolver_types::PromiseIToCoreDocument; use crate::error::Result; @@ -35,6 +34,7 @@ use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; type JsDocumentResolver = SingleThreadedResolver; + /// Convenience type for resolving DID documents from different DID methods. /// /// Also provides methods for resolving DID Documents associated with @@ -59,7 +59,7 @@ impl WasmResolver { let mut attached_iota_method = false; let resolution_handlers: Option = config.handlers(); - let client: Option = config.client(); + let client: Option = config.client(); if let Some(handlers) = resolution_handlers { let map: &Map = handlers.dyn_ref::().ok_or_else(|| { @@ -82,11 +82,11 @@ impl WasmResolver { ))?; } - let rc_client: Rc = Rc::new(wasm_client); + let rc_client: Rc = Rc::new(wasm_client); // Take CoreDID (instead of IotaDID) to avoid inconsistent error messages between the // cases when the iota handler is attached by passing a client or directly as a handler. let handler = move |did: CoreDID| { - let rc_client_clone: Rc = rc_client.clone(); + let rc_client_clone: Rc = rc_client.clone(); async move { let iota_did: IotaDID = IotaDID::parse(did).map_err(identity_iota::iota::Error::DIDSyntaxError)?; Self::client_as_handler(rc_client_clone.as_ref(), iota_did.into()).await @@ -98,13 +98,14 @@ impl WasmResolver { Ok(Self(Rc::new(resolver))) } - pub(crate) async fn client_as_handler( - client: &WasmIotaIdentityClient, + pub(crate) async fn client_as_handler( + client: &H, did: WasmIotaDID, - ) -> std::result::Result { - Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new( - client.resolve_did(&did.0).await?, - )))) + ) -> std::result::Result + where + H: DidResolutionHandler, + { + Ok(WasmIotaDocument::from(client.resolve_did(&did.0).await?)) } /// attempts to extract (method, handler) pairs from the entries of a map and attaches them to the resolver. diff --git a/examples/1_advanced/10_zkp_revocation.rs b/examples/1_advanced/10_zkp_revocation.rs index 31c6c684b7..d966917e0e 100644 --- a/examples/1_advanced/10_zkp_revocation.rs +++ b/examples/1_advanced/10_zkp_revocation.rs @@ -51,6 +51,7 @@ use identity_iota::iota::rebased::transaction::Transaction; use identity_iota::iota::rebased::transaction::TransactionOutput; use identity_iota::iota::IotaDocument; use identity_iota::iota::NetworkName; +use identity_iota::iota_interaction::OptionalSync; use identity_iota::resolver::Resolver; use identity_iota::storage::JwkDocumentExt; use identity_iota::storage::JwkMemStore; diff --git a/examples/1_advanced/9_zkp.rs b/examples/1_advanced/9_zkp.rs index 351e7ac517..75b9b65cd7 100644 --- a/examples/1_advanced/9_zkp.rs +++ b/examples/1_advanced/9_zkp.rs @@ -25,6 +25,7 @@ use identity_iota::credential::SelectiveDisclosurePresentation; use identity_iota::credential::Subject; use identity_iota::did::CoreDID; use identity_iota::did::DID; +use identity_iota::iota_interaction::OptionalSync; use identity_iota::iota::rebased::transaction::TransactionOutput; use identity_iota::iota::IotaDocument; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 63b4792683..1a48950561 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -17,12 +17,12 @@ identity_credential = { version = "=1.4.0", path = "../identity_credential", fea identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } identity_iota_core = { version = "=1.4.0", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=1.4.0", path = "../identity_resolver", default-features = false, optional = true } identity_storage = { version = "=1.4.0", path = "../identity_storage", default-features = false, features = ["iota-document"] } identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction" } -identity_resolver = { version = "=1.4.0", path = "../identity_resolver", default-features = false, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", default-features = false } diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 05dad2bc0c..325fc6dccc 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -78,15 +78,15 @@ pub mod prelude { pub use identity_iota_core::IotaDocument; #[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] - #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "resolver", not(target_arch = "wasm32")))))] pub use identity_iota_core::DidResolutionHandler; - #[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] + #[cfg(feature = "resolver")] #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub use identity_resolver::Resolver; } -#[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] +#[cfg(feature = "resolver")] #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub mod resolver { //! DID resolution utilities diff --git a/identity_resolver/src/resolution/resolver.rs b/identity_resolver/src/resolution/resolver.rs index 50ca40b2e7..4519c45d39 100644 --- a/identity_resolver/src/resolution/resolver.rs +++ b/identity_resolver/src/resolution/resolver.rs @@ -264,7 +264,7 @@ impl + 'static> Resolver> { } } -#[cfg(feature = "iota")] +#[cfg(all(feature = "iota", not(target_arch = "wasm32")))] mod iota_handler { use crate::ErrorCause; From bc9c441f8d7f2474ae40929421eeb7d6687bd6ee Mon Sep 17 00:00:00 2001 From: wulfraem Date: Wed, 19 Feb 2025 17:36:57 +0100 Subject: [PATCH 36/63] Update and re-enable WASM examples (#1528) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add 2_resolve_did example * add custom resolver example * add include custom resolution example in main * fix outdated wording in examples * update examples to use a more natural order of calls, remove unused variables * update and re-add wasm examples as used in rust code * replace `util` helper with updated variant for rebased * update resolver logic - resolver can be created with read-only and with write capable clients - `IotaDocument` now serialize to full serialized `IotaDocument` intead of just the `CoreDocument` part when calling `.to_json()`/`.toJSON()` - this affects the documents resolved via `Resolver` as well - `Resolver` class now accepts generic type parameter to specify the type of the resolved documents * fix missing imports in examples * update `actions/cache` version see https://github.blog/changelog/2024-12-05-notice-of-upcoming-releases-and-breaking-changes-for-github-actions/#actions-cache-v1-v2-and-actions-toolkit-cache-package-closing-down * Update bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> * fix typo found in review * update resolver in examples to use read-only identity client * align example/test helper functions * remove api tests from WASM examples - most parts covered by them should be covered by actual examples by now * update readme for WASM examples * Apply suggestions from code review Co-authored-by: Eike Haß * reactivate tests for WASM build * update example in readme, use testnet identity package as example * update sccache action to also run on any ubuntu version * add dedicated working directory to `npm ci` step for wasm * update workflows to use local reusable workflows * update wasm-bindgen-cli in ci to use fixed version * remove unused dependency in web build file * disable wasm unit and browser tests for now * disable wasm tests in ci for now * disable firefox and chrome tests as well --------- Co-authored-by: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Co-authored-by: Eike Haß --- .../rust/sccache/setup-sccache/action.yml | 5 +- .github/workflows/build-and-test.yml | 16 +- .github/workflows/clippy.yml | 2 + .../rust-automatic-release-and-publish.yml | 3 +- .github/workflows/rust-create-hotfix-pr.yml | 3 +- .github/workflows/rust-create-release-pr.yml | 6 +- .github/workflows/shared-build-wasm.yml | 3 + .github/workflows/upload-docs.yml | 3 +- .../wasm-automatic-release-and-publish.yml | 7 +- .github/workflows/wasm-create-hotfix-pr.yml | 3 +- .github/workflows/wasm-create-release-pr.yml | 6 +- .github/workflows/wasm-publish-to-npm.yml | 4 +- bindings/wasm/build/web.js | 3 +- bindings/wasm/identity_wasm/README.md | 157 ++++++----- .../wasm/identity_wasm/examples/README.md | 37 ++- .../examples/src/0_basic/-1_test_api_call.ts | 264 ------------------ .../examples/src/0_basic/0_create_did.ts | 15 +- .../examples/src/0_basic/1_update_did.ts | 37 ++- .../examples/src/0_basic/2_resolve_did.ts | 8 +- .../examples/src/0_basic/3_deactivate_did.ts | 8 +- .../examples/src/0_basic/4_delete_did.ts | 47 ---- .../examples/src/0_basic/5_create_vc.ts | 56 ++-- .../examples/src/0_basic/6_create_vp.ts | 76 +++-- .../examples/src/0_basic/7_revoke_vc.ts | 132 +++------ .../src/1_advanced/0_did_controls_did.ts | 159 ----------- .../src/1_advanced/1_did_issues_nft.ts | 167 ----------- .../examples/src/1_advanced/2_nft_owns_did.ts | 164 ----------- .../src/1_advanced/3_did_issues_tokens.ts | 216 -------------- .../src/1_advanced/4_custom_resolution.ts | 10 +- .../src/1_advanced/5_domain_linkage.ts | 76 ++--- .../examples/src/1_advanced/6_sd_jwt.ts | 67 ++--- .../src/1_advanced/7_status_list_2021.ts | 71 ++--- .../examples/src/1_advanced/8_zkp.ts | 106 ++----- .../src/1_advanced/9_zkp_revocation.ts | 145 +++------- .../wasm/identity_wasm/examples/src/main.ts | 66 ++--- .../examples/src/tests/0_did_controls_did.ts | 8 - .../examples/src/tests/1_did_issues_nft.ts | 8 - .../examples/src/tests/2_nft_owns_did.ts | 8 - .../examples/src/tests/3_did_issues_tokens.ts | 8 - .../examples/src/tests/4_delete_did.ts | 8 - .../wasm/identity_wasm/examples/src/util.ts | 172 ++++-------- .../identity_wasm/examples/src/utils_alpha.ts | 91 ------ .../identity_wasm/src/rebased/identity.rs | 6 + .../wasm/identity_wasm/tests/txm_readme.js | 2 +- examples/0_basic/0_create_did.rs | 4 +- examples/0_basic/1_update_did.rs | 4 +- examples/0_basic/2_resolve_did.rs | 4 +- examples/0_basic/3_deactivate_did.rs | 4 +- examples/0_basic/5_create_vc.rs | 6 +- examples/0_basic/6_create_vp.rs | 8 +- examples/0_basic/7_revoke_vc.rs | 6 +- examples/0_basic/8_legacy_stronghold.rs | 4 +- examples/1_advanced/10_zkp_revocation.rs | 6 +- .../11_linked_verifiable_presentation.rs | 4 +- examples/1_advanced/4_identity_history.rs | 4 +- examples/1_advanced/5_custom_resolution.rs | 4 +- examples/1_advanced/6_domain_linkage.rs | 4 +- examples/1_advanced/7_sd_jwt.rs | 6 +- examples/1_advanced/8_status_list_2021.rs | 6 +- examples/1_advanced/9_zkp.rs | 4 +- examples/utils/utils.rs | 2 +- identity_iota_core/tests/e2e/asset.rs | 14 +- identity_iota_core/tests/e2e/client.rs | 6 +- identity_iota_core/tests/e2e/common.rs | 2 +- identity_iota_core/tests/e2e/identity.rs | 18 +- identity_iota_core/tests/e2e/migration.rs | 4 +- 66 files changed, 591 insertions(+), 1992 deletions(-) delete mode 100644 bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts delete mode 100644 bindings/wasm/identity_wasm/examples/src/utils_alpha.ts diff --git a/.github/actions/rust/sccache/setup-sccache/action.yml b/.github/actions/rust/sccache/setup-sccache/action.yml index b6cb313073..f6392ded7e 100644 --- a/.github/actions/rust/sccache/setup-sccache/action.yml +++ b/.github/actions/rust/sccache/setup-sccache/action.yml @@ -15,8 +15,8 @@ runs: brew update --auto-update brew install sccache - - name: Install sccache (ubuntu-24.04) - if: inputs.os == 'ubuntu-24.04' + - name: Install sccache (ubuntu) + if: ${{ startsWith(inputs.os, 'ubuntu-') }} shell: bash run: | SCCACHE_DOWNLOAD_LINK=https://github.com/mozilla/sccache/releases/download @@ -42,5 +42,6 @@ runs: - name: Start sccache shell: bash run: | + echo "starting sccache on ${{ inputs.os }}" sccache --start-server sccache -s diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5c8d15a1fc..2822469655 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -183,16 +183,16 @@ jobs: build-wasm: needs: check-for-run-condition - # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} - if: ${{ false }} # disable for now, as the wasm bindings are not yet ready - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-wasm.yml@main + if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + uses: './.github/workflows/shared-build-wasm.yml' with: + run-unit-tests: false output-artifact-name: identity-wasm-bindings-build test-wasm: needs: build-wasm - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + if: ${{ false }} # disable for now runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -228,7 +228,8 @@ jobs: test-wasm-firefox: needs: build-wasm - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + if: ${{ false }} # disable for now runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -272,7 +273,8 @@ jobs: test-wasm-chrome: needs: build-wasm - if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} + if: ${{ false }} # disable for now runs-on: ubuntu-24.04 strategy: fail-fast: false diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 72a2376c15..373da56f47 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -34,6 +34,8 @@ jobs: # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli uses: jetli/wasm-bindgen-action@24ba6f9fff570246106ac3f80f35185600c3f6c9 + with: + version: '0.2.93' - name: core clippy check uses: actions-rs-plus/clippy-check@b09a9c37c9df7db8b1a5d52e8fe8e0b6e3d574c4 diff --git a/.github/workflows/rust-automatic-release-and-publish.yml b/.github/workflows/rust-automatic-release-and-publish.yml index 12825d9149..a7e9a22953 100644 --- a/.github/workflows/rust-automatic-release-and-publish.yml +++ b/.github/workflows/rust-automatic-release-and-publish.yml @@ -10,8 +10,7 @@ on: jobs: call-create-release-workflow: if: github.event.pull_request.merged == true - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-release.yml@main + uses: './.github/workflows/shared-release.yml' with: changelog-config-path: ./.github/.github_changelog_generator pre-release-tag-regex: ^v[0-9]+\.[0-9]+\.[0-9]+-(?\w+)\.\d+$ diff --git a/.github/workflows/rust-create-hotfix-pr.yml b/.github/workflows/rust-create-hotfix-pr.yml index 13ed90557c..c2f9cdd2c9 100644 --- a/.github/workflows/rust-create-hotfix-pr.yml +++ b/.github/workflows/rust-create-hotfix-pr.yml @@ -9,8 +9,7 @@ on: jobs: create-hotfix-pr: - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-hotfix-pr.yml@main + uses: './.github/workflows/shared-create-hotfix-pr.yml' with: branch: ${{ github.event.inputs.branch }} branch-regex: ^support\/v[0-9]+\.[0-9]+$ diff --git a/.github/workflows/rust-create-release-pr.yml b/.github/workflows/rust-create-release-pr.yml index 2db35ee710..644ea64e28 100644 --- a/.github/workflows/rust-create-release-pr.yml +++ b/.github/workflows/rust-create-release-pr.yml @@ -20,8 +20,7 @@ on: jobs: create-dev-release-pr: if: github.event.inputs.release-type != 'main' - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-dev-release-pr.yml@main + uses: './.github/workflows/shared-create-dev-release-pr.yml' with: tag-prefix: v tag-postfix: -${{ github.event.inputs.release-type }}. @@ -34,8 +33,7 @@ jobs: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} create-main-release-pr: if: github.event.inputs.release-type == 'main' - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-main-release-pr.yml@main + uses: './.github/workflows/shared-create-main-release-pr.yml' with: tag-prefix: v tag-base: ${{ github.event.inputs.version }} diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 25be1b761a..6931b065e0 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -55,6 +55,8 @@ jobs: # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli uses: jetli/wasm-bindgen-action@24ba6f9fff570246106ac3f80f35185600c3f6c9 + with: + version: '0.2.93' - name: Setup sccache uses: './.github/actions/rust/sccache/setup-sccache' @@ -68,6 +70,7 @@ jobs: - name: Install JS dependencies run: npm ci + working-directory: bindings/wasm/identity_wasm - name: Build WASM bindings run: npm run build diff --git a/.github/workflows/upload-docs.yml b/.github/workflows/upload-docs.yml index 5ce1429767..983efc0ae9 100644 --- a/.github/workflows/upload-docs.yml +++ b/.github/workflows/upload-docs.yml @@ -14,8 +14,7 @@ permissions: jobs: build-wasm: - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-wasm.yml@main + uses: './.github/workflows/shared-build-wasm.yml' with: run-unit-tests: false ref: ${{ inputs.ref }} diff --git a/.github/workflows/wasm-automatic-release-and-publish.yml b/.github/workflows/wasm-automatic-release-and-publish.yml index 873e8b4fa3..d2172d1ee7 100644 --- a/.github/workflows/wasm-automatic-release-and-publish.yml +++ b/.github/workflows/wasm-automatic-release-and-publish.yml @@ -11,8 +11,7 @@ on: jobs: call-create-release-workflow: if: github.event.pull_request.merged == true - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-release.yml@main + uses: './.github/workflows/shared-release.yml' with: changelog-config-path: ./bindings/wasm/identity_wasm/.github_changelog_generator pre-release-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+-(?\w+)\.\d+$ @@ -25,9 +24,9 @@ jobs: build-wasm: needs: call-create-release-workflow if: ${{ needs.call-create-release-workflow.outputs.is-release }} - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-wasm.yml@main + uses: './.github/workflows/shared-build-wasm.yml' with: + run-unit-tests: false output-artifact-name: identity-wasm-bindings-build release-wasm: diff --git a/.github/workflows/wasm-create-hotfix-pr.yml b/.github/workflows/wasm-create-hotfix-pr.yml index 4d3f03ebcc..f9d7b3897c 100644 --- a/.github/workflows/wasm-create-hotfix-pr.yml +++ b/.github/workflows/wasm-create-hotfix-pr.yml @@ -9,8 +9,7 @@ on: jobs: create-hotfix-pr: - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-hotfix-pr.yml@main + uses: './.github/workflows/shared-create-hotfix-pr.yml' with: branch: ${{ github.event.inputs.branch }} branch-regex: ^support\/wasm-v[0-9]+\.[0-9]+$ diff --git a/.github/workflows/wasm-create-release-pr.yml b/.github/workflows/wasm-create-release-pr.yml index e6ea8c6619..d91c8b31bc 100644 --- a/.github/workflows/wasm-create-release-pr.yml +++ b/.github/workflows/wasm-create-release-pr.yml @@ -20,8 +20,7 @@ on: jobs: create-dev-release-pr: if: github.event.inputs.release-type != 'main' - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-dev-release-pr.yml@main + uses: './.github/workflows/shared-create-dev-release-pr.yml' with: tag-prefix: wasm-v tag-postfix: -${{ github.event.inputs.release-type }}. @@ -37,8 +36,7 @@ jobs: create-main-release-pr: if: github.event.inputs.release-type == 'main' - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-create-main-release-pr.yml@main + uses: './.github/workflows/shared-create-main-release-pr.yml' with: tag-prefix: wasm-v tag-base: ${{ github.event.inputs.version }} diff --git a/.github/workflows/wasm-publish-to-npm.yml b/.github/workflows/wasm-publish-to-npm.yml index a1d8b8762b..7b50b5c9d1 100644 --- a/.github/workflows/wasm-publish-to-npm.yml +++ b/.github/workflows/wasm-publish-to-npm.yml @@ -18,9 +18,9 @@ on: jobs: build-wasm: - # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 - uses: iotaledger/identity.rs/.github/workflows/shared-build-wasm.yml@main + uses: './.github/workflows/shared-build-wasm.yml' with: + run-unit-tests: false ref: ${{ github.event.inputs.branch }} output-artifact-name: identity-wasm-bindings-build diff --git a/bindings/wasm/build/web.js b/bindings/wasm/build/web.js index 5adfa74651..cdc787e0bd 100644 --- a/bindings/wasm/build/web.js +++ b/bindings/wasm/build/web.js @@ -1,6 +1,5 @@ const path = require("path"); const fs = require("fs"); -const fse = require("fs-extra"); const { lintAll } = require("./lints"); const generatePackage = require("./utils/generatePackage"); @@ -56,4 +55,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 '${entryFilePathTs}' for artifact '${artifact}'`); diff --git a/bindings/wasm/identity_wasm/README.md b/bindings/wasm/identity_wasm/README.md index 9299d29a15..3c9afd37a4 100644 --- a/bindings/wasm/identity_wasm/README.md +++ b/bindings/wasm/identity_wasm/README.md @@ -65,68 +65,65 @@ cat | sed -e "s#require('@iota/identity-wasm/node')#require('./node')#" | timeou ```typescript -const { - Jwk, - JwkType, - EdCurve, - MethodScope, - IotaDocument, - VerificationMethod, - Service, - MethodRelationship, - IotaIdentityClient, -} = require('@iota/identity-wasm/node'); -const { Client } = require('@iota/sdk-wasm/node'); +import { + EdCurve, + IdentityClientReadOnly, + IotaDocument, + Jwk, + JwkType, + MethodRelationship, + MethodScope, + Service, + VerificationMethod, +} from '@iota/identity-wasm/node'; +import { IotaClient } from "@iota/iota-sdk/client"; const EXAMPLE_JWK = new Jwk({ - kty: JwkType.Okp, - crv: EdCurve.Ed25519, - x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + kty: JwkType.Okp, + crv: EdCurve.Ed25519, + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", }); // The endpoint of the IOTA node to use. -const API_ENDPOINT = "http://localhost"; +const NETWORK_URL = "https://api.testnet.iota.cafe"; /** Demonstrate how to create a DID Document. */ -async function main() { - // Create a new client with the given network endpoint. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - const didClient = new IotaIdentityClient(client); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp = await didClient.getNetworkHrp(); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - - // Insert a new Ed25519 verification method in the DID document. - const method = VerificationMethod.newFromJwk( - document.id(), - EXAMPLE_JWK, - "#key-1" - ); - document.insertMethod(method, MethodScope.VerificationMethod()); - - // Attach a new method relationship to the existing method. - document.attachMethodRelationship( - document.id().join("#key-1"), - MethodRelationship.Authentication - ); - - // Add a new Service. - const service = new Service({ - id: document.id().join("#linked-domain"), - type: "LinkedDomains", - serviceEndpoint: "https://iota.org/", - }); - document.insertService(service); - - console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); +export async function main() { + // Create a new client with the given network endpoint. + const iotaClient = new IotaClient({ url: NETWORK_URL }); + + const identityClient = await IdentityClientReadOnly.create(iotaClient); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp = identityClient.network(); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + const method = VerificationMethod.newFromJwk( + document.id(), + EXAMPLE_JWK, + "#key-1" + ); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship( + document.id().join("#key-1"), + MethodRelationship.Authentication + ); + + // Add a new Service. + const service = new Service({ + id: document.id().join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/", + }); + document.insertService(service); + + console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); } main(); @@ -136,29 +133,35 @@ which prints ``` Created document { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", - "verificationMethod": [ - { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", - "controller": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "crv": "Ed25519", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" + "doc": { + "id": "did:iota:testnet:0x0000000000000000000000000000000000000000000000000000000000000000", + "verificationMethod": [ + { + "id": "did:iota:testnet:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", + "controller": "did:iota:testnet:0x0000000000000000000000000000000000000000000000000000000000000000", + "type": "JsonWebKey2020", + "publicKeyJwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" + } } - } - ], - "authentication": [ - "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" - ], - "service": [ - { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - } - ] + ], + "authentication": [ + "did:iota:testnet:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" + ], + "service": [ + { + "id": "did:iota:testnet:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2025-02-19T12:47:28Z", + "updated": "2025-02-19T12:47:28Z" + } } ``` diff --git a/bindings/wasm/identity_wasm/examples/README.md b/bindings/wasm/identity_wasm/examples/README.md index f467c89c07..e3ce021442 100644 --- a/bindings/wasm/identity_wasm/examples/README.md +++ b/bindings/wasm/identity_wasm/examples/README.md @@ -6,6 +6,28 @@ The following code examples demonstrate how to use the IOTA Identity Wasm bindin The examples are written in TypeScript and can be run with Node.js. +### Prerequisites + +Examples can be run against +- a local IOTA node +- or an existing network, e.g. the IOTA testnet + +When setting up the local node, you'll also need to publish an identity package as described in [Getting Started](../../../../README.md#getting-started), the `IDENTITY_IOTA_PACKAGE_ID`. You'll need this ID to be able to run the examples against the local node. + +In case of running the examples against an existing network, this network needs to have a faucet to fund your accounts (the IOTA testnet (`https://api.testnet.iota.cafe`) supports this), and you need to specify this via `NETWORK_URL`. + +The examples require you to have the node you want to use in the iota clients "envs" (`iota client env`) configuration. If this node is configured as `localnet`, you don't have to provide it when running the examples, if not, provide its name as `NETWORK_NAME_FAUCET`. The table below assumes - in case you're running a local node - you have it configured as `localnet` in your IOTA clients "env" setting. + +### Environment variables + +Summarizing the last point, you'll need one or more of the following environment variables: + +| Name | Required for local node | Required for testnet | Required for other node | Comment | +| ------------------------ | :---------------------: | :------------------: | :---------------------: | :------------------: | +| IDENTITY_IOTA_PACKAGE_ID | x | | x | | +| NETWORK_URL | | x | x | | +| NETWORK_NAME_FAUCET | | x | x | see assumption above | + ### Node.js Install the dependencies: @@ -20,16 +42,16 @@ Build the bindings: npm run build ``` -Then, run an example using: +Then, run an example using the following command, environment variables depend on you setup, see [Environment variables](#environment-variables). ```bash -npm run example:node -- +IDENTITY_IOTA_PACKAGE_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- ``` -For instance, to run the `0_create_did` example execute: +For instance, to run the `0_create_did` example with the following (environment variables depend on you setup, see [Environment variables](#environment-variables)): ```bash -npm run example:node -- 0_create_did +IDENTITY_IOTA_PACKAGE_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- 0_create_did ``` ## Basic Examples @@ -42,7 +64,6 @@ The following basic CRUD (Create, Read, Update, Delete) examples are available: | [1_update_did](src/0_basic/1_update_did.ts) | Demonstrates how to update a DID document in an existing Alias Output. | | [2_resolve_did](src/0_basic/2_resolve_did.ts) | Demonstrates how to resolve an existing DID in an Alias Output. | | [3_deactivate_did](src/0_basic/3_deactivate_did.ts) | Demonstrates how to deactivate a DID in an Alias Output. | -| [4_delete_did](src/0_basic/4_delete_did.ts) | Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. | | [5_create_vc](src/0_basic/5_create_vc.ts) | Demonstrates how to create and verify verifiable credentials. | | [6_create_vp](src/0_basic/6_create_vp.ts) | Demonstrates how to create and verify verifiable presentations. | | [7_revoke_vc](src/0_basic/7_revoke_vc.ts) | Demonstrates how to revoke a verifiable credential. | @@ -53,14 +74,12 @@ The following advanced examples are available: | Name | Information | |:-------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------| -| [0_did_controls_did](src/1_advanced/0_did_controls_did.ts) | Demonstrates how an identity can control another identity. | -| [1_did_issues_nft](src/1_advanced/1_did_issues_nft.ts) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. | -| [2_nft_owns_did](src/1_advanced/2_nft_owns_did.ts) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | -| [3_did_issues_tokens](src/1_advanced/3_did_issues_tokens.ts) | Demonstrates how an identity can issue and control a Token Foundry and its tokens. | | [4_custom_resolution](src/1_advanced/4_custom_resolution.ts) | Demonstrates how to set up a resolver using custom handlers. | | [5_domain_linkage](src/1_advanced/5_domain_linkage.ts) | Demonstrates how to link a domain and a DID and verify the linkage. | | [6_sd_jwt](src/1_advanced/6_sd_jwt.ts) | Demonstrates how to create a selective disclosure verifiable credential | | [7_domain_linkage](src/1_advanced/7_status_list_2021.ts) | Demonstrates how to revoke a credential using `StatusList2021`. | +| [8_zkp](./1_advanced/8_zkp.ts) | Demonstrates how to create an Anonymous Credential with BBS+. | +| [9_zkp_revocation](./1_advanced/9_zkp_revocation.ts) | Demonstrates how to revoke a credential. | ## Browser diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts deleted file mode 100644 index ed85309315..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - convertToAddress, - IotaDID, - IotaDocument, - JwkMemStore, - JwsAlgorithm, - KeyIdMemStore, - IdentityClient, - IdentityClientReadOnly, - Multicontroller, - Storage, - StorageSigner, -} from "@iota/identity-wasm/node"; - -import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; -import { bcs } from "@iota/iota-sdk/bcs"; -import { IotaClient } from "@iota/iota-sdk/client"; -import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; -import { Transaction } from "@iota/iota-sdk/transactions"; -import { IOTA_TYPE_ARG } from "@iota/iota-sdk/utils"; -import { IDENTITY_IOTA_PACKAGE_ID, NETWORK_NAME_FAUCET, NETWORK_URL, TEST_GAS_BUDGET } from "../utils_alpha"; - -async function initializeClients() { - console.log("---------------- Preparing IdentityClient ------------------------"); - const iotaClient = new IotaClient({ url: NETWORK_URL }); - const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); - - // create new storage - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - - // generate new key - let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); - let publicKeyJwk = generate.jwk().toPublic(); - if (typeof publicKeyJwk === "undefined") { - throw new Error("failed to derive public JWK from generated JWK"); - } - let keyId = generate.keyId(); - - // create signer from storage - let signer = new StorageSigner(storage, keyId, publicKeyJwk); - const identityClient = await IdentityClient.create(identityClientReadOnly, signer); - - await requestIotaFromFaucetV0({ - host: getFaucetHost(NETWORK_NAME_FAUCET), - recipient: identityClient.senderAddress(), - }); - - const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); - if (balance.totalBalance === "0") { - throw new Error("Balance is still 0"); - } else { - console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); - } - - return { iotaClient, identityClient }; -} - - -async function testIdentityClientReadOnly() { - const iotaClient = new IotaClient({ url: NETWORK_URL }); - const identityClient = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); - - console.log("\n-------------- Start testIdentityClientReadOnly -------------------------------"); - console.log(`networkName: ${identityClient.network()}`); - console.log(`packageId ${identityClient.packageId()}`); - console.log(`migrationRegistry ${identityClient.migrationRegistryId()}`); - console.log(`resolveDid ${await identityClient.resolveDid( - IotaDID.fromAliasId( - "0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc", - identityClient.network(), - ) - )}`); - const identity = await identityClient.getIdentity("0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc"); - console.log(`identity.id ${identity.toFullFledged()?.id()}`); -} - - -async function testIdentityClient( - identityClient: IdentityClient, - iotaClient: IotaClient, -): Promise { - console.log("\n-------------- Start testIdentityClient -------------------------------"); - console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); - console.log(`senderAddress: ${identityClient.senderAddress()}`); - console.log(`networkName: ${identityClient.network()}`); - console.log(`packageId ${identityClient.packageId()}`); - console.log(`migrationRegistry ${identityClient.migrationRegistryId()}`); - console.log(`resolveDid ${await identityClient.resolveDid( - IotaDID.fromAliasId( - "0x2604b185e0956e5f61549839c2eb5b83274a697ba548ac8d4e474def91a039cc", - identityClient.network(), - ) - )}`); - - await requestIotaFromFaucetV0({ - host: getFaucetHost(NETWORK_NAME_FAUCET), - recipient: identityClient.senderAddress(), - }); - - const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); - if (balance.totalBalance === "0") { - throw new Error("Balance is still 0"); - } else { - console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); - } - - const newDoc = new IotaDocument(identityClient.network()); - const { output: identity } = await identityClient - .createIdentity(newDoc) - .finish() - .execute(identityClient); - console.log(`created new identity with id "${identity.id()}"`); - - try { - console.log("\n---------------- getIdentity ------------------------"); - const identity = await identityClient.getIdentity("0xd9a0f8139076bfbdc245d402c655b4e93cdf5b4184294da2bbbf7ae3d8ec97a4"); - console.dir(identity); - const onchainIdentity = identity.toFullFledged(); - console.dir(`resolved identities id is ${onchainIdentity?.id()}`); - console.dir(`resolved identity is shared: ${onchainIdentity?.isShared()}`); - - } catch (ex) { - console.log(`Test getIdentity() - Error: ${(ex as Error).message}`); - } - - const did4resolveDid = IotaDID.parse("did:iota:0x0101010101010101010101010101010101010101010101010101010101010101"); - try { - console.log("\n---------------- resolveDid ------------------------"); - // invalid DID - await identityClient.resolveDid(did4resolveDid); - } catch (ex) { - console.log(`Test resolveDid() - Error: ${(ex as Error).message}`); - } -} - -function testMultiController(): void { - let multiController = new Multicontroller(); - - const testCapId = "123"; - console.dir(multiController.controlledValue()); - console.dir(multiController.controllerVotingPower(testCapId)); - console.dir(multiController.hasMember(testCapId)); - console.dir(multiController.intoInner()); - console.dir(multiController.proposals()); - console.dir(multiController.threshold()); -} - -async function signerTest(): Promise { - // create new storage - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - - // generate new key - let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); - let publicKeyJwk = generate.jwk().toPublic(); - if (typeof publicKeyJwk === "undefined") { - throw new Error("failed to derive public JWK from generated JWK"); - } - let keyId = generate.keyId(); - console.dir({ - keyId, - publicKeyJwk: publicKeyJwk, - }); - - // create signer from storage - let signer = new StorageSigner(storage, keyId, publicKeyJwk); - console.log({ keyIdFromSigner: signer.keyId() }); - - // sign test - let signed = await signer.sign(new Uint8Array([0, 1, 2, 4])); - console.dir({ signed }); -} - -async function testExecuteTransaction(iotaClient: IotaClient) { - console.log("---------------- testing executeTransaction ------------------------"); - - // create new storage - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - - // generate new key - let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); - let publicKeyJwk = generate.jwk().toPublic(); - if (typeof publicKeyJwk === "undefined") { - throw new Error("failed to derive public JWK from generated JWK"); - } - - // create signer from storage - let signer = new StorageSigner(storage, generate.keyId(), publicKeyJwk); - // get public key as bytes and create address - let publicJwk = (signer as any).publicKeyRaw(); - let address = convertToAddress(publicJwk); - - await requestIotaFromFaucetV0({ - host: getFaucetHost(NETWORK_NAME_FAUCET), - recipient: address, - }); - - // try to craft tx with js api - let coins = await iotaClient.getCoins({ - owner: address, - coinType: IOTA_TYPE_ARG, - }); - const tx = new Transaction(); - const coin0 = coins.data[0]; - const coin = tx.splitCoins(tx.object(coin0.coinObjectId), [ - bcs.u64().serialize(TEST_GAS_BUDGET * BigInt(2)), - ]); - tx.transferObjects([coin], address); - tx.setSenderIfNotSet(address); - - let response = await executeTransaction( - iotaClient, - address, - publicJwk, - await tx.build({ client: iotaClient }), - signer, - ); - console.dir(response); - console.dir(response?.response?.transaction?.data); -} - -/** Test API usage */ -export async function testApiCall(): Promise { - const { iotaClient, identityClient } = await initializeClients(); - - try { - await testIdentityClientReadOnly(); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`identity client binding test failed: ${suffix}`); - } - - try { - await testIdentityClient(identityClient, iotaClient); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`identity client binding test failed: ${suffix}`); - } - - try { - testMultiController(); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`multi controller binding test failed: ${suffix}`); - } - - try { - await signerTest(); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`signer binding test failed: ${suffix}`); - } - - try { - await testExecuteTransaction(iotaClient); - } catch (err) { - const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; - console.error(`signer binding test failed: ${suffix}`); - } - - console.log("done"); -} diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 597ee81ffa..0cb07329cb 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -5,24 +5,24 @@ import { IotaDID } from '@iota/identity-wasm/node'; import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, - getClientAndCreateAccount, + getFundedClient, getMemstorage, NETWORK_URL, -} from '../utils_alpha'; +} from '../util'; /** Demonstrate how to create a DID Document and publish it. */ -export async function createIdentity(): Promise { +export async function createIdentity(): Promise { // create new client to connect to IOTA network const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); // create new client that offers identity related functions const storage = getMemstorage(); - const identityClient = await getClientAndCreateAccount(storage); + const identityClient = await getFundedClient(storage); // create new unpublished document const [unpublished] = await createDocumentForNetwork(storage, network); - console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); + console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); let did: IotaDID; // TODO: decide upon wich style to use here @@ -41,9 +41,8 @@ export async function createIdentity(): Promise { .execute(identityClient); did = published.id(); } - + // check if we can resolve it via client const resolved = await identityClient.resolveDid(did); console.log(`Resolved DID document: ${JSON.stringify(resolved, null, 2)}`); - } - \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index 4664e2ff13..adac672c2f 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -14,11 +14,11 @@ import { import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, - getClientAndCreateAccount, + getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET, -} from '../utils_alpha'; +} from '../util'; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { @@ -26,7 +26,7 @@ export async function updateIdentity() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const identityClient = await getClientAndCreateAccount(storage); + const identityClient = await getFundedClient(storage); const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); // create new identity for this account and publish document for it @@ -40,26 +40,21 @@ export async function updateIdentity() { // Technically this is equivalent to the document above. const resolved = await identityClient.resolveDid(did); - if ((storage as any).__wbg_ptr === 0) { - console.log('cannot re-use storage, skipping generating new method'); - } else { - console.log('can re-use storage, generating new method'); - // Insert a new Ed25519 verification method in the DID document. - await resolved.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - "#key-2", - MethodScope.VerificationMethod(), - ); + // Insert a new Ed25519 verification method in the DID document. + await resolved.generateMethod( + storage, + JwkMemStore.ed25519KeyType(), + JwsAlgorithm.EdDSA, + "#key-2", + MethodScope.VerificationMethod(), + ); - // Attach a new method relationship to the inserted method. - resolved.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); + // Attach a new method relationship to the inserted method. + resolved.attachMethodRelationship(did.join("#key-2"), MethodRelationship.Authentication); - // Remove a verification method. - let originalMethod = resolved.resolveMethod(vmFragment1) as VerificationMethod; - await resolved.purgeMethod(storage, originalMethod?.id()); - } + // Remove a verification method. + let originalMethod = resolved.resolveMethod(vmFragment1) as VerificationMethod; + await resolved.purgeMethod(storage, originalMethod?.id()); // Add a new Service. const service: Service = new Service({ diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index fa98ac9ef5..6fb65a2df0 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -13,11 +13,11 @@ import { import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, - getClientAndCreateAccount, + getFundedClient, getMemstorage, IDENTITY_IOTA_PACKAGE_ID, NETWORK_URL, -} from '../utils_alpha'; +} from '../util'; const DID_JWK: string = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9"; @@ -28,7 +28,7 @@ export async function resolveIdentity() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const identityClient = await getClientAndCreateAccount(storage); + const identityClient = await getFundedClient(storage); const [unpublished] = await createDocumentForNetwork(storage, network); // create new identity for this account and publish document for it @@ -45,7 +45,7 @@ export async function resolveIdentity() { // We can resolve the Object ID directly const resolvedIdentity = await identityClient.getIdentity(identity.id()); console.dir(resolvedIdentity); - console.log(`Resolved identity has object ID ${resolvedIdentity.toFullFledged()?.id()}`); + console.log(`Identity client resolved identity has object ID ${resolvedIdentity.toFullFledged()?.id()}`); // Or we can resolve it via the `Resolver` api: diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 4e70d336a1..debb53eb8b 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -5,11 +5,11 @@ import { IotaDID } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, - getClientAndCreateAccount, + getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET, -} from '../utils_alpha'; +} from '../util'; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { @@ -17,7 +17,7 @@ export async function deactivateIdentity() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const identityClient = await getClientAndCreateAccount(storage); + const identityClient = await getFundedClient(storage); const [unpublished] = await createDocumentForNetwork(storage, network); // create new identity for this account and publish document for it @@ -46,7 +46,7 @@ export async function deactivateIdentity() { // Re-activate the DID by publishing a valid DID document. console.log("Publishing this:", JSON.stringify(resolved, null, 2)); await identityClient - .publishDidDocumentUpdate(resolved, TEST_GAS_BUDGET); + .publishDidDocumentUpdate(resolved, TEST_GAS_BUDGET); // Resolve the reactivated DID document. let resolvedReactivated = await identityClient.resolveDid(did); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts deleted file mode 100644 index c85bdb8e96..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { IotaIdentityClient, JwkMemStore, KeyIdMemStore, Storage } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; - -/** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ -export async function deleteIdentity() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - // const { address, document } = await createDid(client, secretManager); - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { address, document } = await createDid( - client, - secretManager, - storage, - ); - const did = document.id(); - - // Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed. - // This operation is *not* reversible. - // Deletion can only be done by the governor of the Alias Output. - const destinationAddress = address; - await didClient.deleteDidOutput(secretManager, destinationAddress, did); - - // Attempting to resolve a deleted DID results in a `NotFound` error. - let deleted = false; - try { - await didClient.resolveDid(did); - } catch (err) { - deleted = true; - } - if (!deleted) { - throw new Error("failed to delete DID"); - } -} diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts index 1b711e51ad..56a6db5b26 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts @@ -5,15 +5,17 @@ import { Credential, EdDSAJwsVerifier, FailFast, - JwkMemStore, JwsSignatureOptions, JwtCredentialValidationOptions, JwtCredentialValidator, - KeyIdMemStore, - Storage, } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + NETWORK_URL, +} from '../util'; /** * This example shows how to create a Verifiable Credential and validate it. @@ -22,31 +24,29 @@ import { API_ENDPOINT, createDid } from "../util"; * This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whomever they please. */ export async function createVC() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + const issuerDocument = issuerIdentity.didDocument(); - // Create an identity for the issuer with one verification method `key-1`. - const issuerStorage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - secretManager, - issuerStorage, - ); - - // Create an identity for the holder, in this case also the subject. - const aliceStorage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document: aliceDocument } = await createDid( - client, - secretManager, - aliceStorage, - ); + // Create an identity for the holder, and publish DID document for it, in this case also the subject. + const aliceStorage = getMemstorage(); + const aliceClient = await getFundedClient(aliceStorage); + const [unpublishedAliceDocument] = await createDocumentForNetwork(aliceStorage, network); + const { output: aliceIdentity } = await aliceClient + .createIdentity(unpublishedAliceDocument) + .finish() + .execute(aliceClient); + const aliceDocument = aliceIdentity.didDocument(); // Create a credential subject indicating the degree earned by Alice, linked to their DID. const subject = { diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts index f78f19dd1b..9fd1a74975 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts @@ -7,8 +7,8 @@ import { Duration, EdDSAJwsVerifier, FailFast, - IotaIdentityClient, - JwkMemStore, + IdentityClientReadOnly, + IotaDocument, JwsSignatureOptions, JwsVerificationOptions, Jwt, @@ -17,15 +17,19 @@ import { JwtPresentationOptions, JwtPresentationValidationOptions, JwtPresentationValidator, - KeyIdMemStore, Presentation, Resolver, - Storage, SubjectHolderRelationship, Timestamp, } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + IDENTITY_IOTA_PACKAGE_ID, + NETWORK_URL, +} from '../util'; /** * This example shows how to create a Verifiable Presentation and validate it. @@ -37,39 +41,29 @@ export async function createVP() { // Step 1: Create identities for the issuer and the holder. // =========================================================================== - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Creates a new wallet and identity (see "0_create_did" example). - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, - issuerStorage, - ); - - // Create an identity for the holder, in this case also the subject. - const aliceSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const aliceStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: aliceDocument, fragment: aliceFragment } = await createDid( - client, - aliceSecretManager, - aliceStorage, - ); + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + + // create issuer account, create identity, and publish DID document for it + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + const issuerDocument = issuerIdentity.didDocument(); + + // create holder account, create identity, and publish DID document for it + const aliceStorage = getMemstorage(); + const aliceClient = await getFundedClient(aliceStorage); + const [unpublishedAliceDocument, aliceFragment] = await createDocumentForNetwork(aliceStorage, network); + const { output: aliceIdentity } = await aliceClient + .createIdentity(unpublishedAliceDocument) + .finish() + .execute(aliceClient); + const aliceDocument = aliceIdentity.didDocument(); // =========================================================================== // Step 2: Issuer creates and signs a Verifiable Credential. @@ -169,8 +163,8 @@ export async function createVP() { }, ); - const resolver = new Resolver({ - client: didClient, + const resolver = new Resolver({ + client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID), }); // Resolve the presentation holder. const presentationHolderDID: CoreDID = JwtPresentationValidator.extractHolder(presentationJwt); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts index 63b00ee4d7..4b40b1c163 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts @@ -3,27 +3,30 @@ import { Credential, - EdCurve, FailFast, + IdentityClientReadOnly, IJwsVerifier, IotaDocument, - IotaIdentityClient, Jwk, - JwkMemStore, JwsAlgorithm, JwsSignatureOptions, JwtCredentialValidationOptions, JwtCredentialValidator, - KeyIdMemStore, Resolver, RevocationBitmap, Service, - Storage, VerificationMethod, verifyEd25519, } from "@iota/identity-wasm/node"; -import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + IDENTITY_IOTA_PACKAGE_ID, + NETWORK_URL, + TEST_GAS_BUDGET, +} from '../util'; /** * This example shows how to revoke a verifiable credential. @@ -38,43 +41,29 @@ export async function revokeVC() { // Create a Verifiable Credential. // =========================================================================== - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for the issuer. - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create an identity for the issuer with one verification method `key-1`. - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, - issuerStorage, - ); - - // Generate a random mnemonic for Alice. - const aliceSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create an identity for the holder, in this case also the subject. - const aliceStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: aliceDocument } = await createDid( - client, - aliceSecretManager, - aliceStorage, - ); + // Create new client to connect to IOTA network. + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + let issuerDocument = issuerIdentity.didDocument(); + + // create holder account, create identity, and publish DID document for it. + const aliceStorage = getMemstorage(); + const aliceClient = await getFundedClient(aliceStorage); + const [unpublishedAliceDocument, aliceFragment] = await createDocumentForNetwork(aliceStorage, network); + const { output: aliceIdentity } = await aliceClient + .createIdentity(unpublishedAliceDocument) + .finish() + .execute(aliceClient); + const aliceDocument = aliceIdentity.didDocument(); // Create a new empty revocation bitmap. No credential is revoked yet. const revocationBitmap = new RevocationBitmap(); @@ -84,25 +73,10 @@ export async function revokeVC() { const service: Service = revocationBitmap.toService(serviceId); issuerDocument.insertService(service); - // Resolve the latest output and update it with the given document. - let aliasOutput: AliasOutput = await didClient.updateDidOutput( + // Publish the updated document. + let updatedDocument: IotaDocument = await issuerClient.publishDidDocumentUpdate( issuerDocument, - ); - - // Because the size of the DID document increased, we have to increase the allocated storage deposit. - // This increases the deposit amount to the new minimum. - let rentStructure: IRent = await didClient.getRentStructure(); - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - // Publish the document. - issuerDocument = await didClient.publishDidOutput( - issuerSecretManager, - aliasOutput, + TEST_GAS_BUDGET, ); // Create a credential subject indicating the degree earned by Alice, linked to their DID. @@ -156,18 +130,9 @@ export async function revokeVC() { issuerDocument.revokeCredentials("my-revocation-service", CREDENTIAL_INDEX); // Publish the changes. - aliasOutput = await didClient.updateDidOutput(issuerDocument); - rentStructure = await didClient.getRentStructure(); - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - const update2: IotaDocument = await didClient.publishDidOutput( - issuerSecretManager, - aliasOutput, + let update2: IotaDocument = await issuerClient.publishDidDocumentUpdate( + issuerDocument, + TEST_GAS_BUDGET, ); // Credential verification now fails. @@ -195,22 +160,15 @@ export async function revokeVC() { await issuerDocument.purgeMethod(issuerStorage, originalMethod.id()); // Publish the changes. - aliasOutput = await didClient.updateDidOutput(issuerDocument); - rentStructure = await didClient.getRentStructure(); - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - issuerDocument = await didClient.publishDidOutput( - issuerSecretManager, - aliasOutput, + issuerDocument = await issuerClient.publishDidDocumentUpdate( + issuerDocument, + TEST_GAS_BUDGET, ); // We expect the verifiable credential to be revoked. - const resolver = new Resolver({ client: didClient }); + const resolver = new Resolver({ + client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID), + }); try { // Resolve the issuer's updated DID Document to ensure the key was revoked successfully. const resolvedIssuerDoc = await resolver.resolve( diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts deleted file mode 100644 index 0eeb9f214e..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - IotaDID, - IotaDocument, - IotaIdentityClient, - JwkMemStore, - JwsAlgorithm, - KeyIdMemStore, - MethodScope, - Storage, -} from "@iota/identity-wasm/node"; -import { - Address, - AliasAddress, - AliasOutput, - Client, - IRent, - IssuerFeature, - MnemonicSecretManager, - StateControllerAddressUnlockCondition, - UnlockConditionType, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; - -/** Demonstrates how an identity can control another identity. - -For this example, we consider the case where a parent company's DID controls the DID of a subsidiary. */ -export async function didControlsDid() { - // ======================================================== - // Create the company's and subsidiary's Alias Output DIDs. - // ======================================================== - - // Create a new Client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Creates a new wallet and identity (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - let companyDid = document.id(); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Construct a new DID document for the subsidiary. - var subsidiaryDocument: IotaDocument = new IotaDocument(networkName); - - // Create the Alias Address of the company. - const companyAliasAddress: Address = new AliasAddress(companyDid.toAliasId()); - - // Create a DID for the subsidiary that is controlled by the parent company's DID. - // This means the subsidiary's Alias Output can only be updated or destroyed by - // the state controller or governor of the company's Alias Output respectively. - var subsidiaryAlias: AliasOutput = await didClient.newDidOutput( - companyAliasAddress, - subsidiaryDocument, - rentStructure, - ); - - // Optionally, we can mark the company as the issuer of the subsidiary DID. - // This allows to verify trust relationships between DIDs, as a resolver can - // verify that the subsidiary DID was created by the parent company. - subsidiaryAlias = await client.buildAliasOutput({ - ...subsidiaryAlias, - immutableFeatures: [new IssuerFeature(companyAliasAddress)], - aliasId: subsidiaryAlias.getAliasId(), - unlockConditions: subsidiaryAlias.getUnlockConditions(), - }); - - // Adding the issuer feature means we have to recalculate the required storage deposit. - subsidiaryAlias = await client.buildAliasOutput({ - ...subsidiaryAlias, - amount: Utils.computeStorageDeposit(subsidiaryAlias, rentStructure), - aliasId: subsidiaryAlias.getAliasId(), - unlockConditions: subsidiaryAlias.getUnlockConditions(), - }); - - // Publish the subsidiary's DID. - subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAlias); - - // ===================================== - // Update the subsidiary's Alias Output. - // ===================================== - - // Add a verification method to the subsidiary. - // This only serves as an example for updating the subsidiary DID. - await subsidiaryDocument.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - "#key-2", - MethodScope.VerificationMethod(), - ); - - // Update the subsidiary's Alias Output with the updated document - // and increase the storage deposit. - let subsidiaryAliasUpdate: AliasOutput = await didClient.updateDidOutput(subsidiaryDocument); - subsidiaryAliasUpdate = await client.buildAliasOutput({ - ...subsidiaryAliasUpdate, - amount: Utils.computeStorageDeposit(subsidiaryAliasUpdate, rentStructure), - aliasId: subsidiaryAliasUpdate.getAliasId(), - unlockConditions: subsidiaryAliasUpdate.getUnlockConditions(), - }); - - // Publish the updated subsidiary's DID. - // - // This works because `secret_manager` can unlock the company's Alias Output, - // which is required in order to update the subsidiary's Alias Output. - subsidiaryDocument = await didClient.publishDidOutput(secretManager, subsidiaryAliasUpdate); - - // =================================================================== - // Determine the controlling company's DID given the subsidiary's DID. - // =================================================================== - - // Resolve the subsidiary's Alias Output. - const subsidiaryOutput: AliasOutput = await didClient.resolveDidOutput(subsidiaryDocument.id()); - - // Extract the company's Alias Id from the state controller unlock condition. - // - // If instead we wanted to determine the original creator of the DID, - // we could inspect the issuer feature. This feature needs to be set when creating the DID. - // - // Non-null assertion is safe to use since every Alias Output has a state controller unlock condition. - // Cast to StateControllerAddressUnlockCondition is safe as we check the type in find. - const stateControllerUnlockCondition: StateControllerAddressUnlockCondition = subsidiaryOutput.getUnlockConditions() - .find( - unlockCondition => unlockCondition.getType() == UnlockConditionType.StateControllerAddress, - )! as StateControllerAddressUnlockCondition; - - // Cast to IAliasAddress is safe because we set an Alias Address earlier. - const companyAliasId: string = (stateControllerUnlockCondition.getAddress() as AliasAddress).getAliasId(); - - // Reconstruct the company's DID from the Alias Id and the network. - companyDid = IotaDID.fromAliasId(companyAliasId, networkName); - - // Resolve the company's DID document. - const companyDocument: IotaDocument = await didClient.resolveDid(companyDid); - - console.log("Company ", JSON.stringify(companyDocument, null, 2)); - console.log("Subsidiary ", JSON.stringify(subsidiaryDocument, null, 2)); -} diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts deleted file mode 100644 index bf70963e5d..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - IotaDID, - IotaDocument, - IotaIdentityClient, - JwkMemStore, - KeyIdMemStore, - Storage, -} from "@iota/identity-wasm/node"; -import { - Address, - AddressType, - AddressUnlockCondition, - AliasAddress, - Client, - FeatureType, - IRent, - IssuerFeature, - MetadataFeature, - MnemonicSecretManager, - NftOutput, - Output, - OutputResponse, - OutputType, - Payload, - PayloadType, - RegularTransactionEssence, - TransactionEssenceType, - TransactionPayload, - utf8ToHex, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; - -/** Demonstrates how an identity can issue and own NFTs, -and how observers can verify the issuer of the NFT. - -For this example, we consider the case where a manufacturer issues -a digital product passport (DPP) as an NFT. */ -export async function didIssuesNft() { - // ============================================== - // Create the manufacturer's DID and the DPP NFT. - // ============================================== - - // Create a new Client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create a new DID for the manufacturer. (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - let manufacturerDid = document.id(); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Create the Alias Address of the manufacturer. - const manufacturerAliasAddress: Address = new AliasAddress( - manufacturerDid.toAliasId(), - ); - - // Create a Digital Product Passport NFT issued by the manufacturer. - let productPassportNft: NftOutput = await client.buildNftOutput({ - nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", - immutableFeatures: [ - // Set the manufacturer as the immutable issuer. - new IssuerFeature(manufacturerAliasAddress), - // A proper DPP would hold its metadata here. - new MetadataFeature(utf8ToHex("Digital Product Passport Metadata")), - ], - unlockConditions: [ - // The NFT will initially be owned by the manufacturer. - new AddressUnlockCondition(manufacturerAliasAddress), - ], - }); - - // Set the appropriate storage deposit. - productPassportNft = await client.buildNftOutput({ - ...productPassportNft, - amount: Utils.computeStorageDeposit(productPassportNft, rentStructure), - nftId: productPassportNft.getNftId(), - unlockConditions: productPassportNft.getUnlockConditions(), - }); - - // Publish the NFT. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [productPassportNft] }); - await client.retryUntilIncluded(blockId); - - // ======================================================== - // Resolve the Digital Product Passport NFT and its issuer. - // ======================================================== - - // Extract the identifier of the NFT from the published block. - // Non-null assertion is safe because we published a block with a payload. - let nftId: string = computeNftOutputId(block.payload!); - - // Fetch the NFT Output. - const nftOutputId: string = await client.nftOutputId(nftId); - const outputResponse: OutputResponse = await client.getOutput(nftOutputId); - const output: Output = outputResponse.output; - - // Extract the issuer of the NFT. - let manufacturerAliasId: string; - if (output.getType() === OutputType.Nft && (output as NftOutput).getImmutableFeatures()) { - // Cast is fine as we checked the type. - const nftOutput: NftOutput = output as NftOutput; - // Non-null assertion is fine as we checked the immutable features are present. - const issuerFeature: IssuerFeature = nftOutput.getImmutableFeatures()!.find(feature => - feature.getType() === FeatureType.Issuer - ) as IssuerFeature; - if (issuerFeature && issuerFeature.getIssuer().getType() === AddressType.Alias) { - manufacturerAliasId = (issuerFeature.getIssuer() as AliasAddress).getAliasId(); - } else { - throw new Error("expected to find issuer feature with an alias address"); - } - } else { - throw new Error("expected NFT output with immutable features"); - } - - // Reconstruct the manufacturer's DID from the Alias Id. - manufacturerDid = IotaDID.fromAliasId(manufacturerAliasId, networkName); - - // Resolve the issuer of the NFT. - const manufacturerDocument: IotaDocument = await didClient.resolveDid(manufacturerDid); - - console.log("The issuer of the Digital Product Passport NFT is:", JSON.stringify(manufacturerDocument, null, 2)); -} - -function computeNftOutputId(payload: Payload): string { - if (payload.getType() === PayloadType.Transaction) { - const transactionPayload: TransactionPayload = payload as TransactionPayload; - const transactionId = Utils.transactionId(transactionPayload); - - if (transactionPayload.essence.getType() === TransactionEssenceType.Regular) { - const regularTxPayload = transactionPayload.essence as RegularTransactionEssence; - const outputs = regularTxPayload.outputs; - for (const index in outputs) { - if (outputs[index].getType() === OutputType.Nft) { - const outputId: string = Utils.computeOutputId(transactionId, parseInt(index)); - return Utils.computeNftId(outputId); - } - } - throw new Error("no NFT output in transaction essence"); - } else { - throw new Error("expected transaction essence"); - } - } else { - throw new Error("expected transaction payload"); - } -} diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts deleted file mode 100644 index 1c4aee6abc..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; -import { - Address, - AddressType, - AddressUnlockCondition, - AliasOutput, - Client, - IRent, - MnemonicSecretManager, - NftAddress, - NftOutput, - Output, - OutputResponse, - OutputType, - Payload, - PayloadType, - RegularTransactionEssence, - SecretManager, - StateControllerAddressUnlockCondition, - TransactionEssenceType, - TransactionPayload, - UnlockConditionType, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; - -/** Demonstrates how an identity can be owned by NFTs, -and how observers can verify that relationship. - -For this example, we consider the case where a car's NFT owns -the DID of the car, so that transferring the NFT also transfers DID ownership. */ -export async function nftOwnsDid() { - // ============================= - // Create the car's NFT and DID. - // ============================= - - // Create a new Client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // Create a new address that will own the NFT. - const addressBech32 = (await new SecretManager(secretManager).generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: networkName, - }))[0]; - const address = Utils.parseBech32Address(addressBech32); - - // Get funds for testing from the faucet. - await ensureAddressHasFunds(client, addressBech32); - - // Create the car NFT with an Ed25519 address as the unlock condition. - let carNft: NftOutput = await client.buildNftOutput({ - nftId: "0x0000000000000000000000000000000000000000000000000000000000000000", - unlockConditions: [ - // The NFT will initially be owned by the Ed25519 address. - new AddressUnlockCondition(address), - ], - }); - - // Set the appropriate storage deposit. - carNft = await client.buildNftOutput({ - ...carNft, - amount: Utils.computeStorageDeposit(carNft, rentStructure), - nftId: carNft.getNftId(), - unlockConditions: carNft.getUnlockConditions(), - }); - - // Publish the NFT. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { outputs: [carNft] }); - await client.retryUntilIncluded(blockId); - - // Extract the identifier of the NFT from the published block. - // Non-null assertion is safe because we published a block with a payload. - var carNftId: string = computeNftOutputId(block.payload!); - - // Create the address of the NFT. - const nftAddress: Address = new NftAddress(carNftId); - - // Construct a DID document for the car. - var carDocument: IotaDocument = new IotaDocument(networkName); - - // Create a new DID for the car that is owned by the car NFT. - var carDidAliasOutput: AliasOutput = await didClient.newDidOutput(nftAddress, carDocument, rentStructure); - - // Publish the car DID. - carDocument = await didClient.publishDidOutput(secretManager, carDidAliasOutput); - - // ============================================ - // Determine the car's NFT given the car's DID. - // ============================================ - - // Resolve the Alias Output of the DID. - carDidAliasOutput = await didClient.resolveDidOutput(carDocument.id()); - - // Extract the NFT Id from the state controller unlock condition. - const stateControllerUnlockCondition: StateControllerAddressUnlockCondition = carDidAliasOutput - .getUnlockConditions() - .find(feature => - feature.getType() === UnlockConditionType.StateControllerAddress - ) as StateControllerAddressUnlockCondition; - if (stateControllerUnlockCondition.getAddress().getType() === AddressType.Nft) { - carNftId = (stateControllerUnlockCondition.getAddress() as NftAddress).getNftId(); - } else { - throw new Error("expected nft address unlock condition"); - } - - // Fetch the NFT Output of the car. - const nftOutputId: string = await client.nftOutputId(carNftId); - const outputResponse: OutputResponse = await client.getOutput(nftOutputId); - const output: Output = outputResponse.output; - - if (output.getType() === OutputType.Nft) { - carNft = output as NftOutput; - } else { - throw new Error("expected nft output type"); - } - - console.log("The car's DID is:", JSON.stringify(carDocument, null, 2)); - console.log("The car's NFT is:", JSON.stringify(carNft, null, 2)); -} - -function computeNftOutputId(payload: Payload): string { - if (payload.getType() === PayloadType.Transaction) { - const transactionPayload: TransactionPayload = payload as TransactionPayload; - const transactionId = Utils.transactionId(transactionPayload); - - if (transactionPayload.essence.getType() === TransactionEssenceType.Regular) { - const regularTxPayload = transactionPayload.essence as RegularTransactionEssence; - const outputs = regularTxPayload.outputs; - for (const index in outputs) { - if (outputs[index].getType() === OutputType.Nft) { - const outputId: string = Utils.computeOutputId(transactionId, parseInt(index)); - return Utils.computeNftId(outputId); - } - } - throw new Error("no NFT output in transaction essence"); - } else { - throw new Error("expected transaction essence"); - } - } else { - throw new Error("expected transaction payload"); - } -} diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts deleted file mode 100644 index ae55925790..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - IotaDID, - IotaDocument, - IotaIdentityClient, - JwkMemStore, - KeyIdMemStore, - Storage, -} from "@iota/identity-wasm/node"; -import { - AddressUnlockCondition, - AliasAddress, - AliasOutput, - BasicOutput, - Client, - ExpirationUnlockCondition, - FoundryOutput, - ImmutableAliasAddressUnlockCondition, - IRent, - MnemonicSecretManager, - Output, - OutputResponse, - OutputType, - SecretManager, - SimpleTokenScheme, - UnlockConditionType, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; - -/** Demonstrates how an identity can issue and control a Token Foundry and its tokens. - -For this example, we consider the case where an authority issues carbon credits -that can be used to pay for carbon emissions or traded on a marketplace. */ -export async function didIssuesTokens() { - // =========================================== - // Create the authority's DID and the foundry. - // =========================================== - - // Create a new Client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create a new DID for the authority. (see "0_create_did" example). - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - let { document } = await createDid( - client, - secretManager, - storage, - ); - let authorityDid = document.id(); - - // Get the current byte costs. - const rentStructure: IRent = await didClient.getRentStructure(); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkName: string = await didClient.getNetworkHrp(); - - // We want to update the foundry counter of the authority's Alias Output, so we create an - // updated version of the output. We pass in the previous document, - // because we don't want to modify it in this update. - var authorityDocument: IotaDocument = await didClient.resolveDid(authorityDid); - var authorityAliasOutput: AliasOutput = await didClient.updateDidOutput(authorityDocument); - - // We will add one foundry to this Alias Output. - authorityAliasOutput = await client.buildAliasOutput({ - ...authorityAliasOutput, - foundryCounter: authorityAliasOutput.getFoundryCounter() + 1, - aliasId: authorityAliasOutput.getAliasId(), - unlockConditions: authorityAliasOutput.getUnlockConditions(), - }); - - // Create a token foundry that represents carbon credits. - const tokenScheme: SimpleTokenScheme = new SimpleTokenScheme( - BigInt(500_000), - BigInt(0), - BigInt(1_000_000), - ); - - // Create the identifier of the token, which is partially derived from the Alias Address. - const tokenId: string = Utils.computeTokenId(authorityDid.toAliasId(), 1, tokenScheme.getType()); - - // Create a token foundry that represents carbon credits. - var carbonCreditsFoundry: FoundryOutput = await client.buildFoundryOutput({ - tokenScheme, - serialNumber: 1, - // Initially, all carbon credits are owned by the foundry. - nativeTokens: [ - { - id: tokenId, - amount: BigInt(500_000), - }, - ], - // The authority is set as the immutable owner. - unlockConditions: [ - new ImmutableAliasAddressUnlockCondition( - new AliasAddress(authorityDid.toAliasId()), - ), - ], - }); - - // Set the appropriate storage deposit. - carbonCreditsFoundry = await client.buildFoundryOutput({ - ...carbonCreditsFoundry, - amount: Utils.computeStorageDeposit(carbonCreditsFoundry, rentStructure), - tokenScheme, - serialNumber: carbonCreditsFoundry.getSerialNumber(), - unlockConditions: carbonCreditsFoundry.getUnlockConditions(), - }); - - // Publish the foundry. - const [blockId, block] = await client.buildAndPostBlock(secretManager, { - outputs: [authorityAliasOutput, carbonCreditsFoundry], - }); - await client.retryUntilIncluded(blockId); - - // =================================== - // Resolve foundry and its issuer DID. - // =================================== - - // Get the latest output that contains the foundry. - const carbonCreditsFoundryId: string = tokenId; - const outputId: string = await client.foundryOutputId(carbonCreditsFoundryId); - const outputResponse: OutputResponse = await client.getOutput(outputId); - const output: Output = outputResponse.output; - - if (output.getType() === OutputType.Foundry) { - carbonCreditsFoundry = output as FoundryOutput; - } else { - throw new Error("expected foundry output"); - } - - // Get the Alias Id of the authority that issued the carbon credits foundry. - // Non-null assertion is safe as each founry output needs to have an immutable alias unlock condition. - const aliasUnlockCondition: ImmutableAliasAddressUnlockCondition = carbonCreditsFoundry.getUnlockConditions().find( - unlockCondition => unlockCondition.getType() === UnlockConditionType.ImmutableAliasAddress, - )! as ImmutableAliasAddressUnlockCondition; - - // We know the immutable alias unlock condition contains an alias address. - const authorityAliasId: string = (aliasUnlockCondition.getAddress() as AliasAddress).getAliasId(); - - // Reconstruct the DID of the authority. - authorityDid = IotaDID.fromAliasId(authorityAliasId, networkName); - - // Resolve the authority's DID document. - authorityDocument = await didClient.resolveDid(authorityDid); - - console.log("The authority's DID is:", JSON.stringify(authorityDocument, null, 2)); - - // ========================================================= - // Transfer 1000 carbon credits to the address of a company. - // ========================================================= - - // Create a new address that represents the company. - const companyAddressBech32: string = (await new SecretManager(secretManager).generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 1, - end: 2, - }, - }))[0]; - const companyAddress = Utils.parseBech32Address(companyAddressBech32); - - // Create a timestamp 24 hours from now. - const tomorrow: number = Math.floor(Date.now() / 1000) + (60 * 60 * 24); - - // Create a basic output containing our carbon credits that we'll send to the company's address. - const basicOutput: BasicOutput = await client.buildBasicOutput({ - nativeTokens: [ - { - amount: BigInt(1000), - id: tokenId, - }, - ], - // Allow the company to claim the credits within 24 hours by using an expiration unlock condition. - unlockConditions: [ - new AddressUnlockCondition(companyAddress), - new ExpirationUnlockCondition( - new AliasAddress(authorityAliasId), - tomorrow, - ), - ], - }); - - // Reduce the carbon credits in the foundry by the amount that is sent to the company. - carbonCreditsFoundry = await client.buildFoundryOutput({ - ...carbonCreditsFoundry, - nativeTokens: [ - { - amount: BigInt(499_000), - id: tokenId, - }, - ], - tokenScheme, - serialNumber: carbonCreditsFoundry.getSerialNumber(), - unlockConditions: carbonCreditsFoundry.getUnlockConditions(), - }); - - // Publish the Basic Output and the updated foundry. - const [blockId2, block2] = await client.buildAndPostBlock(secretManager, { - outputs: [basicOutput, carbonCreditsFoundry], - }); - await client.retryUntilIncluded(blockId2); - - console.log("Sent carbon credits to", companyAddressBech32); -} diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts index 3380647b24..8eeded3119 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts @@ -7,10 +7,10 @@ import { import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, - getClientAndCreateAccount, + getFundedClient, getMemstorage, NETWORK_URL, -} from '../utils_alpha'; +} from '../util'; // Use this external package to avoid implementing the entire did:key method in this example. import * as ed25519 from "@transmute/did-key-ed25519"; @@ -32,7 +32,7 @@ export async function customResolution() { ); // for demo purposes we'll just inject the custom property into a core document - // instead of creating a proper instance + // to create a new KeyDocument instance let coreDocument = CoreDocument.fromJSON(document.didDocument); (coreDocument as unknown as KeyDocument).customProperty = "foobar"; return coreDocument as unknown as KeyDocument; @@ -42,9 +42,9 @@ export async function customResolution() { const iotaClient = new IotaClient({ url: NETWORK_URL }); const network = await iotaClient.getChainIdentifier(); const storage = getMemstorage(); - const identityClient = await getClientAndCreateAccount(storage); + const identityClient = await getFundedClient(storage); const [unpublished] = await createDocumentForNetwork(storage, network); - + // create new identity for this account and publish document for it, DID of it will be resolved later on const { output: identity } = await identityClient .createIdentity(unpublished) diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts index a2af8f83fb..8b8f616100 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts @@ -10,39 +10,39 @@ import { EdDSAJwsVerifier, IotaDID, IotaDocument, - IotaIdentityClient, - JwkMemStore, JwsSignatureOptions, JwtCredentialValidationOptions, JwtDomainLinkageValidator, - KeyIdMemStore, LinkedDomainService, - Storage, Timestamp, } from "@iota/identity-wasm/node"; -import { AliasOutput, Client, IRent, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + NETWORK_URL, + TEST_GAS_BUDGET, +} from '../util'; /** * Demonstrates how to link a domain and a DID and verify the linkage. */ export async function domainLinkage() { - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - const didClient = new IotaIdentityClient(client); - - // Generate a random mnemonic for our wallet. - const secretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); - - // Creates a new wallet and identity (see "0_create_did" example). - let { document, fragment } = await createDid(client, secretManager, storage); - const did: IotaDID = document.id(); + // create new clients and create new account + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + const storage = getMemstorage(); + const identityClient = await getFundedClient(storage); + const [unpublished, vmFragment1] = await createDocumentForNetwork(storage, network); + + // create new identity for this account and publish document for it + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + const document = identity.didDocument();; + const did = document.id(); // ===================================================== // Create Linked Domain service @@ -59,7 +59,8 @@ export async function domainLinkage() { domains: [domainFoo, domainBar], }); document.insertService(linkedDomainService.toService()); - let updatedDidDocument = await publishDocument(didClient, secretManager, document); + let updatedDidDocument = await await identityClient + .publishDidDocumentUpdate(document, TEST_GAS_BUDGET); console.log("Updated DID document:", JSON.stringify(updatedDidDocument, null, 2)); // ===================================================== @@ -81,7 +82,7 @@ export async function domainLinkage() { // Sign the credential. const credentialJwt = await document.createCredentialJwt( storage, - fragment, + vmFragment1, domainLinkageCredential, new JwsSignatureOptions(), ); @@ -120,7 +121,7 @@ export async function domainLinkage() { // Retrieve the issuers of the Domain Linkage Credentials which correspond to the possibly linked DIDs. // Note that in this example only the first entry in the credential is validated. let issuers: Array = fetchedConfigurationResource.issuers(); - const issuerDocument: IotaDocument = await didClient.resolveDid(IotaDID.parse(issuers[0].toString())); + const issuerDocument: IotaDocument = await identityClient.resolveDid(IotaDID.parse(issuers[0].toString())); // Validate the linkage between the Domain Linkage Credential in the configuration and the provided issuer DID. // Validation succeeds when no error is thrown. @@ -135,7 +136,7 @@ export async function domainLinkage() { // → Case 2: starting from a DID // ===================================================== - const didDocument: IotaDocument = await didClient.resolveDid(did); + const didDocument: IotaDocument = await identityClient.resolveDid(did); // Get the Linked Domain Services from the DID Document. let linkedDomainServices: LinkedDomainService[] = didDocument @@ -167,26 +168,3 @@ export async function domainLinkage() { console.log("Successfully validated Domain Linkage!"); } - -async function publishDocument( - client: IotaIdentityClient, - secretManager: MnemonicSecretManager, - document: IotaDocument, -): Promise { - // Resolve the latest output and update it with the given document. - let aliasOutput: AliasOutput = await client.updateDidOutput(document); - - // Because the size of the DID document increased, we have to increase the allocated storage deposit. - // This increases the deposit amount to the new minimum. - const rentStructure: IRent = await client.getRentStructure(); - aliasOutput = await client.client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rentStructure), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - - // Publish the output. - const updated: IotaDocument = await client.publishDidOutput(secretManager, aliasOutput); - return updated; -} diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts index e389118f8c..87807530d8 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts @@ -6,63 +6,56 @@ import { DecodedJwtCredential, EdDSAJwsVerifier, FailFast, - JwkMemStore, JwsSignatureOptions, JwsVerificationOptions, JwtCredentialValidationOptions, KeyBindingJwtClaims, KeyBindingJWTValidationOptions, - KeyIdMemStore, SdJwt, SdJwtCredentialValidator, SdObjectEncoder, - Storage, Timestamp, } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + NETWORK_URL, +} from '../util'; /** - * Demonstrates how to create a selective disclosure verifiable credential and validate it - * using the [Selective Disclosure for JWTs (SD-JWT)](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html) specification. + * Demonstrates how to create a selective disclosure verifiable credential and validate it * using the [Selective Disclosure for JWTs (SD-JWT)](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html) specification. */ export async function sdJwt() { // =========================================================================== // Step 1: Create identities for the issuer and the holder. // =========================================================================== - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); // Creates a new wallet and identity (see "0_create_did" example). - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, - issuerStorage, - ); - - // Create an identity for the holder, in this case also the subject. - const aliceSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const aliceStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: aliceDocument, fragment: aliceFragment } = await createDid( - client, - aliceSecretManager, - aliceStorage, - ); + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + const issuerDocument = issuerIdentity.didDocument(); + + // Create an identity for the holder, and publish DID document for it, in this case also the subject. + const aliceStorage = getMemstorage(); + const aliceClient = await getFundedClient(aliceStorage); + const [unpublishedAliceDocument, aliceFragment] = await createDocumentForNetwork(aliceStorage, network); + const { output: aliceIdentity } = await aliceClient + .createIdentity(unpublishedAliceDocument) + .finish() + .execute(aliceClient); + const aliceDocument = aliceIdentity.didDocument(); // =========================================================================== // Step 2: Issuer creates and signs a selectively disclosable JWT verifiable credential. diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts index 4e70d8fa19..5db62743bb 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts @@ -5,63 +5,52 @@ import { Credential, EdDSAJwsVerifier, FailFast, - JwkMemStore, JwsSignatureOptions, JwtCredentialValidationOptions, JwtCredentialValidator, - KeyIdMemStore, StatusCheck, StatusList2021, StatusList2021Credential, StatusList2021CredentialBuilder, StatusList2021Entry, StatusPurpose, - Storage, } from "@iota/identity-wasm/node"; -import { Client, MnemonicSecretManager, Utils } from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, createDid } from "../util"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { + createDocumentForNetwork, + getFundedClient, + getMemstorage, + NETWORK_URL, +} from '../util'; export async function statusList2021() { // =========================================================================== // Create a Verifiable Credential. // =========================================================================== - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - // Generate a random mnemonic for the issuer. - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create an identity for the issuer with one verification method `key-1`. - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, - issuerStorage, - ); - - // Generate a random mnemonic for Alice. - const aliceSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - - // Create an identity for the holder, in this case also the subject. - const aliceStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: aliceDocument } = await createDid( - client, - aliceSecretManager, - aliceStorage, - ); + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + const issuerDocument = issuerIdentity.didDocument(); + + // Create an identity for the holder, and publish DID document for it, in this case also the subject. + const aliceStorage = getMemstorage(); + const aliceClient = await getFundedClient(aliceStorage); + const [unpublishedAliceDocument] = await createDocumentForNetwork(aliceStorage, network); + const { output: aliceIdentity } = await aliceClient + .createIdentity(unpublishedAliceDocument) + .finish() + .execute(aliceClient); + const aliceDocument = aliceIdentity.didDocument(); // Create a new empty status list. No credentials have been revoked yet. const statusList = new StatusList2021(); diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts index 55d0c82fca..54473de9ac 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts @@ -1,106 +1,53 @@ import { Credential, FailFast, + IdentityClientReadOnly, IotaDID, IotaDocument, - IotaIdentityClient, JptCredentialValidationOptions, JptCredentialValidator, JptCredentialValidatorUtils, JptPresentationValidationOptions, JptPresentationValidator, JptPresentationValidatorUtils, - JwkMemStore, JwpCredentialOptions, JwpPresentationOptions, - KeyIdMemStore, MethodScope, ProofAlgorithm, SelectiveDisclosurePresentation, - Storage, } from "@iota/identity-wasm/node"; +import { IotaClient } from "@iota/iota-sdk/client"; import { - type Address, - AliasOutput, - Client, - MnemonicSecretManager, - SecretManager, - SecretManagerType, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; - -/** Creates a DID Document and publishes it in a new Alias Output. - -Its functionality is equivalent to the "create DID" example -and exists for convenient calling from the other examples. */ -export async function createDid(client: Client, secretManager: SecretManagerType, storage: Storage): Promise<{ - address: Address; - document: IotaDocument; - fragment: string; -}> { - const didClient = new IotaIdentityClient(client); - const networkHrp: string = await didClient.getNetworkHrp(); - - const secretManagerInstance = new SecretManager(secretManager); - const walletAddressBech32 = (await secretManagerInstance.generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: networkHrp, - }))[0]; - - console.log("Wallet address Bech32:", walletAddressBech32); - - await ensureAddressHasFunds(client, walletAddressBech32); - - const address: Address = Utils.parseBech32Address(walletAddressBech32); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - - const fragment = await document.generateMethodJwp( - storage, - ProofAlgorithm.BLS12381_SHA256, - undefined, - MethodScope.VerificationMethod(), - ); - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - const aliasOutput: AliasOutput = await didClient.newDidOutput(address, document); - - // Publish the Alias Output and get the published DID document. - const published = await didClient.publishDidOutput(secretManager, aliasOutput); + getFundedClient, + getMemstorage, + IDENTITY_IOTA_PACKAGE_ID, + NETWORK_URL, +} from '../util'; - return { address, document: published, fragment }; -} export async function zkp() { // =========================================================================== // Step 1: Create identity for the issuer. // =========================================================================== - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); - // Creates a new wallet and identity (see "0_create_did" example). - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const unpublishedIssuerDocument = new IotaDocument(network); + const issuerFragment = await unpublishedIssuerDocument.generateMethodJwp( issuerStorage, + ProofAlgorithm.BLS12381_SHA256, + undefined, + MethodScope.VerificationMethod(), ); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + const issuerDocument = issuerIdentity.didDocument(); // =========================================================================== // Step 2: Issuer creates and signs a Verifiable Credential with BBS algorithm. @@ -148,11 +95,12 @@ export async function zkp() { // ============================================================================================ // Step 4: Holder resolve Issuer's DID, retrieve Issuer's document and validate the Credential // ============================================================================================ - const identityClient = new IotaIdentityClient(client); + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( + iotaClient, IDENTITY_IOTA_PACKAGE_ID); // Holder resolves issuer's DID. let issuerDid = IotaDID.parse(JptCredentialValidatorUtils.extractIssuerFromIssuedJpt(credentialJpt).toString()); - let issuerDoc = await identityClient.resolveDid(issuerDid); + let issuerDoc = await identityClientReadOnly.resolveDid(issuerDid); // Holder validates the credential and retrieve the JwpIssued, needed to construct the JwpPresented let decodedCredential = JptCredentialValidator.validate( @@ -212,7 +160,7 @@ export async function zkp() { const issuerDidV = IotaDID.parse( JptPresentationValidatorUtils.extractIssuerFromPresentedJpt(presentationJpt).toString(), ); - const issuerDocV = await identityClient.resolveDid(issuerDidV); + const issuerDocV = await identityClientReadOnly.resolveDid(issuerDidV); const presentationValidationOptions = new JptPresentationValidationOptions({ nonce: challenge }); const decodedPresentedCredential = JptPresentationValidator.validate( diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts index e8c3d586a1..c1de5f57ae 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts @@ -2,19 +2,15 @@ import { Credential, Duration, FailFast, - IotaDID, IotaDocument, - IotaIdentityClient, JptCredentialValidationOptions, JptCredentialValidator, JptCredentialValidatorUtils, JptPresentationValidationOptions, JptPresentationValidator, JptPresentationValidatorUtils, - JwkMemStore, JwpCredentialOptions, JwpPresentationOptions, - KeyIdMemStore, MethodScope, ProofAlgorithm, RevocationBitmap, @@ -22,104 +18,53 @@ import { SelectiveDisclosurePresentation, Status, StatusCheck, - Storage, Timestamp, } from "@iota/identity-wasm/node"; +import { IotaClient } from "@iota/iota-sdk/client"; import { - type Address, - AliasOutput, - Client, - MnemonicSecretManager, - SecretManager, - SecretManagerType, - Utils, -} from "@iota/sdk-wasm/node"; -import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; + createDocumentForNetwork, + getFundedClient, + getMemstorage, + NETWORK_URL, + TEST_GAS_BUDGET, +} from '../util'; -/** Creates a DID Document and publishes it in a new Alias Output. - -Its functionality is equivalent to the "create DID" example -and exists for convenient calling from the other examples. */ -export async function createDid(client: Client, secretManager: SecretManagerType, storage: Storage): Promise<{ - address: Address; - document: IotaDocument; - fragment: string; -}> { - const didClient = new IotaIdentityClient(client); - const networkHrp: string = await didClient.getNetworkHrp(); - - const secretManagerInstance = new SecretManager(secretManager); - const walletAddressBech32 = (await secretManagerInstance.generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: networkHrp, - }))[0]; - - console.log("Wallet address Bech32:", walletAddressBech32); - - await ensureAddressHasFunds(client, walletAddressBech32); - - const address: Address = Utils.parseBech32Address(walletAddressBech32); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - - const fragment = await document.generateMethodJwp( - storage, +export async function zkp_revocation() { + // create new client to connect to IOTA network + const iotaClient = new IotaClient({ url: NETWORK_URL }); + const network = await iotaClient.getChainIdentifier(); + + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. + const issuerStorage = getMemstorage(); + const issuerClient = await getFundedClient(issuerStorage); + const unpublishedIssuerDocument = new IotaDocument(network); + const issuerFragment = await unpublishedIssuerDocument.generateMethodJwp( + issuerStorage, ProofAlgorithm.BLS12381_SHA256, undefined, MethodScope.VerificationMethod(), ); const revocationBitmap = new RevocationBitmap(); - const serviceId = document.id().toUrl().join("#my-revocation-service"); + // add service for revocation + const serviceId = unpublishedIssuerDocument.id().toUrl().join("#my-revocation-service"); const service = revocationBitmap.toService(serviceId); + unpublishedIssuerDocument.insertService(service); + const { output: issuerIdentity } = await issuerClient + .createIdentity(unpublishedIssuerDocument) + .finish() + .execute(issuerClient); + let issuerDocument = issuerIdentity.didDocument(); + + // Create an identity for the holder, and publish DID document for it, in this case also the subject. + const holderStorage = getMemstorage(); + const holderClient = await getFundedClient(holderStorage); + const [unpublishedholderDocument] = await createDocumentForNetwork(holderStorage, network); + const { output: holderIdentity } = await holderClient + .createIdentity(unpublishedholderDocument) + .finish() + .execute(holderClient); + const holderDocument = holderIdentity.didDocument(); - document.insertService(service); - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - const aliasOutput: AliasOutput = await didClient.newDidOutput(address, document); - - // Publish the Alias Output and get the published DID document. - const published = await didClient.publishDidOutput(secretManager, aliasOutput); - - return { address, document: published, fragment }; -} -export async function zkp_revocation() { - // Create a new client to interact with the IOTA ledger. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - // Creates a new wallet and identity (see "0_create_did" example). - const issuerSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const issuerStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: issuerDocument, fragment: issuerFragment } = await createDid( - client, - issuerSecretManager, - issuerStorage, - ); - const holderSecretManager: MnemonicSecretManager = { - mnemonic: Utils.generateMnemonic(), - }; - const holderStorage: Storage = new Storage( - new JwkMemStore(), - new KeyIdMemStore(), - ); - let { document: holderDocument, fragment: holderFragment } = await createDid( - client, - holderSecretManager, - holderStorage, - ); // ========================================================================================= // Step 1: Create a new RevocationTimeframeStatus containing the current validityTimeframe // ======================================================================================= @@ -132,9 +77,9 @@ export async function zkp_revocation() { Timestamp.nowUTC(), ); - // Create a credential subject indicating the degree earned by Alice. + // Create a credential subject indicating the degree earned by holder. const subject = { - name: "Alice", + name: "holder", mainCourses: ["Object-oriented Programming", "Mathematics"], degree: { type: "BachelorDegree", @@ -244,7 +189,7 @@ export async function zkp_revocation() { StatusCheck.Strict, ); } catch (_) { - console.log("successfully expired!"); + console.log("Successfully expired!"); } // =========================================================================== @@ -253,20 +198,10 @@ export async function zkp_revocation() { console.log("Issuer decides to revoke the Credential"); - const identityClient = new IotaIdentityClient(client); - // Update the RevocationBitmap service in the issuer's DID Document. // This revokes the credential's unique index. issuerDocument.revokeCredentials("my-revocation-service", 5); - let aliasOutput = await identityClient.updateDidOutput(issuerDocument); - const rent = await identityClient.getRentStructure(); - aliasOutput = await client.buildAliasOutput({ - ...aliasOutput, - amount: Utils.computeStorageDeposit(aliasOutput, rent), - aliasId: aliasOutput.getAliasId(), - unlockConditions: aliasOutput.getUnlockConditions(), - }); - issuerDocument = await identityClient.publishDidOutput(issuerSecretManager, aliasOutput); + issuerDocument = await issuerClient.publishDidDocumentUpdate(issuerDocument, TEST_GAS_BUDGET); // Holder checks if his credential has been revoked by the Issuer try { diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts index dac40d5fbd..7a2f4c2b5e 100644 --- a/bindings/wasm/identity_wasm/examples/src/main.ts +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -1,25 +1,19 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { testApiCall } from "./0_basic/-1_test_api_call"; import { createIdentity } from "./0_basic/0_create_did"; import { updateIdentity } from "./0_basic/1_update_did"; import { resolveIdentity } from "./0_basic/2_resolve_did"; import { deactivateIdentity } from "./0_basic/3_deactivate_did"; -// import { deleteIdentity } from "./0_basic/4_delete_did"; -// import { createVC } from "./0_basic/5_create_vc"; -// import { createVP } from "./0_basic/6_create_vp"; -// import { revokeVC } from "./0_basic/7_revoke_vc"; -// import { didControlsDid } from "./1_advanced/0_did_controls_did"; -// import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; -// import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; -// import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; +import { createVC } from "./0_basic/5_create_vc"; +import { createVP } from "./0_basic/6_create_vp"; +import { revokeVC } from "./0_basic/7_revoke_vc"; import { customResolution } from "./1_advanced/4_custom_resolution"; -// import { domainLinkage } from "./1_advanced/5_domain_linkage"; -// import { sdJwt } from "./1_advanced/6_sd_jwt"; -// import { statusList2021 } from "./1_advanced/7_status_list_2021"; -// import { zkp } from "./1_advanced/8_zkp"; -// import { zkp_revocation } from "./1_advanced/9_zkp_revocation"; +import { domainLinkage } from "./1_advanced/5_domain_linkage"; +import { sdJwt } from "./1_advanced/6_sd_jwt"; +import { statusList2021 } from "./1_advanced/7_status_list_2021"; +import { zkp } from "./1_advanced/8_zkp"; +import { zkp_revocation } from "./1_advanced/9_zkp_revocation"; async function main() { // Extract example name. @@ -29,8 +23,6 @@ async function main() { const argument = process.argv[2].toLowerCase(); switch (argument) { - case "-1_test_api_call": - return await testApiCall(); case "0_create_did": return await createIdentity(); case "1_update_did": @@ -39,34 +31,24 @@ async function main() { return await resolveIdentity(); case "3_deactivate_did": return await deactivateIdentity(); - // case "4_delete_did": - // return await deleteIdentity(); - // case "5_create_vc": - // return await createVC(); - // case "6_create_vp": - // return await createVP(); - // case "7_revoke_vc": - // return await revokeVC(); - // case "0_did_controls_did": - // return await didControlsDid(); - // case "1_did_issues_nft": - // return await didIssuesNft(); - // case "2_nft_owns_did": - // return await nftOwnsDid(); - // case "3_did_issues_tokens": - // return await didIssuesTokens(); + case "5_create_vc": + return await createVC(); + case "6_create_vp": + return await createVP(); + case "7_revoke_vc": + return await revokeVC(); case "4_custom_resolution": return await customResolution(); - // case "5_domain_linkage": - // return await domainLinkage(); - // case "6_sd_jwt": - // return await sdJwt(); - // case "7_status_list_2021": - // return await statusList2021(); - // case "8_zkp": - // return await zkp(); - // case "9_zkp_revocation": - // return await zkp_revocation(); + case "5_domain_linkage": + return await domainLinkage(); + case "6_sd_jwt": + return await sdJwt(); + case "7_status_list_2021": + return await statusList2021(); + case "8_zkp": + return await zkp(); + case "9_zkp_revocation": + return await zkp_revocation(); default: throw "Unknown example name: '" + argument + "'"; } diff --git a/bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts deleted file mode 100644 index 1ec2bb4012..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { didControlsDid } from "../1_advanced/0_did_controls_did"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Did controls Did", async () => { - await didControlsDid(); - }); -}); diff --git a/bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts b/bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts deleted file mode 100644 index 90660df3ae..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { didIssuesNft } from "../1_advanced/1_did_issues_nft"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Did issues Nft", async () => { - await didIssuesNft(); - }); -}); diff --git a/bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts deleted file mode 100644 index f148462eb3..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { nftOwnsDid } from "../1_advanced/2_nft_owns_did"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Nft owns Did", async () => { - await nftOwnsDid(); - }); -}); diff --git a/bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts b/bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts deleted file mode 100644 index a3dc41e1d9..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { didIssuesTokens } from "../1_advanced/3_did_issues_tokens"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Did issues tokens", async () => { - await didIssuesTokens(); - }); -}); diff --git a/bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts deleted file mode 100644 index e22e6e27e2..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { deleteIdentity } from "../0_basic/4_delete_did"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Delete Identity", async () => { - await deleteIdentity(); - }); -}); diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index 3fc2be116e..00acf200f5 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -1,139 +1,89 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + import { IotaDocument, - IotaIdentityClient, JwkMemStore, JwsAlgorithm, + KeyIdMemStore, + IdentityClient, + IdentityClientReadOnly, MethodScope, Storage, + StorageSigner, } from "@iota/identity-wasm/node"; -import { - type Address, - AliasOutput, - type Client, - IOutputsResponse, - SecretManager, - SecretManagerType, - Utils, -} from "@iota/sdk-wasm/node"; - -export const API_ENDPOINT = "http://localhost"; -export const FAUCET_ENDPOINT = "http://localhost/faucet/api/enqueue"; - -/** Creates a DID Document and publishes it in a new Alias Output. - -Its functionality is equivalent to the "create DID" example -and exists for convenient calling from the other examples. */ -export async function createDid(client: Client, secretManager: SecretManagerType, storage: Storage): Promise<{ - address: Address; - document: IotaDocument; - fragment: string; -}> { - const didClient = new IotaIdentityClient(client); - const networkHrp: string = await didClient.getNetworkHrp(); - - const secretManagerInstance = new SecretManager(secretManager); - const walletAddressBech32 = (await secretManagerInstance.generateEd25519Addresses({ - accountIndex: 0, - range: { - start: 0, - end: 1, - }, - bech32Hrp: networkHrp, - }))[0]; - - console.log("Wallet address Bech32:", walletAddressBech32); - - await ensureAddressHasFunds(client, walletAddressBech32); - - const address: Address = Utils.parseBech32Address(walletAddressBech32); +import { IotaClient } from "@iota/iota-sdk/client"; +import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; + +export const IDENTITY_IOTA_PACKAGE_ID = + process.env.IDENTITY_IOTA_PACKAGE_ID || "0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818"; +export const NETWORK_NAME_FAUCET = process.env.NETWORK_NAME_FAUCET || "localnet"; +export const NETWORK_URL = + process.env.NETWORK_URL || "http://127.0.0.1:9000"; +export const TEST_GAS_BUDGET = BigInt(50_000_000); + +export function getMemstorage(): Storage { + return new Storage(new JwkMemStore(), new KeyIdMemStore()); +} +export async function createDocumentForNetwork(storage: Storage, network: string): Promise<[IotaDocument, string]> { // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); + const unpublished = new IotaDocument(network); - const fragment = await document.generateMethod( + const verificationMethodFragment = await unpublished.generateMethod( storage, JwkMemStore.ed25519KeyType(), JwsAlgorithm.EdDSA, - "#jwk", - MethodScope.AssertionMethod(), + "#key-1", + MethodScope.VerificationMethod(), ); - // Construct an Alias Output containing the DID document, with the wallet address - // set as both the state controller and governor. - const aliasOutput: AliasOutput = await didClient.newDidOutput(address, document); - - // Publish the Alias Output and get the published DID document. - const published = await didClient.publishDidOutput(secretManager, aliasOutput); - - return { address, document: published, fragment }; + return [unpublished, verificationMethodFragment]; } -/** Request funds from the faucet API, if needed, and wait for them to show in the wallet. */ -export async function ensureAddressHasFunds(client: Client, addressBech32: string) { - let balance = await getAddressBalance(client, addressBech32); - if (balance > BigInt(0)) { - return; +export async function getFundedClient(storage: Storage): Promise { + if (!IDENTITY_IOTA_PACKAGE_ID) { + throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); } - await requestFundsFromFaucet(addressBech32); + const iotaClient = new IotaClient({ url: NETWORK_URL }); - for (let i = 0; i < 9; i++) { - // Wait for the funds to reflect. - await new Promise(f => setTimeout(f, 5000)); + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( + iotaClient, IDENTITY_IOTA_PACKAGE_ID); - let balance = await getAddressBalance(client, addressBech32); - if (balance > BigInt(0)) { - break; - } + // generate new key + let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); + if (typeof publicKeyJwk === "undefined") { + throw new Error("failed to derive public JWK from generated JWK"); } -} - -/** Returns the balance of the given Bech32-encoded address. */ -async function getAddressBalance(client: Client, addressBech32: string): Promise { - const outputIds: IOutputsResponse = await client.basicOutputIds([ - { address: addressBech32 }, - { hasExpiration: false }, - { hasTimelock: false }, - { hasStorageDepositReturn: false }, - ]); - const outputs = await client.getOutputs(outputIds.items); - - let totalAmount = BigInt(0); - for (const output of outputs) { - totalAmount += output.output.getAmount(); + let keyId = generate.keyId(); + + // create signer from storage + let signer = new StorageSigner(storage, keyId, publicKeyJwk); + const identityClient = await IdentityClient.create(identityClientReadOnly, signer); + + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: identityClient.senderAddress(), + }); + + const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); + if (balance.totalBalance === "0") { + throw new Error("Balance is still 0"); + } else { + console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); } - return totalAmount; + return identityClient; } -/** Request tokens from the faucet API. */ -async function requestFundsFromFaucet(addressBech32: string) { - const requestObj = JSON.stringify({ address: addressBech32 }); - let errorMessage, data; - try { - const response = await fetch(FAUCET_ENDPOINT, { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: requestObj, - }); - if (response.status === 202) { - errorMessage = "OK"; - } else if (response.status === 429) { - errorMessage = "too many requests, please try again later."; - } else { - data = await response.json(); - // @ts-ignore - errorMessage = data.error.message; - } - } catch (error) { - errorMessage = error; - } +export async function createDidDocument( + identityClient: IdentityClient, + unpublished: IotaDocument, +): Promise { + let tx = identityClient.publishDidDocument(unpublished); + let txOutput = await tx.execute(identityClient); - if (errorMessage != "OK") { - throw new Error(`failed to get funds from faucet: ${errorMessage}`); - } + return txOutput.output; } diff --git a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts b/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts deleted file mode 100644 index ba28c9a66c..0000000000 --- a/bindings/wasm/identity_wasm/examples/src/utils_alpha.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - IotaDocument, - JwkMemStore, - JwsAlgorithm, - KeyIdMemStore, - IdentityClient, - IdentityClientReadOnly, - MethodScope, - Storage, - StorageSigner, -} from "@iota/identity-wasm/node"; -import { IotaClient } from "@iota/iota-sdk/client"; -import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; - -export const IDENTITY_IOTA_PACKAGE_ID = - process.env.IDENTITY_IOTA_PACKAGE_ID || "0xac854096fcbfadcdd8cc8e4b6242d1b35607ef5324bfe54ba7a4be69fa6db36d"; -export const NETWORK_NAME_FAUCET = "localnet"; -export const NETWORK_URL = - process.env.NETWORK_URL || "http://127.0.0.1:9000"; -export const TEST_GAS_BUDGET = BigInt(50_000_000); - -export function getMemstorage(): Storage { - return new Storage(new JwkMemStore(), new KeyIdMemStore()); -} - -export async function createDocumentForNetwork(storage: Storage, network: string): Promise<[IotaDocument, string]> { - // Create a new DID document with a placeholder DID. - const unpublished = new IotaDocument(network); - - const verificationMethodFragment = await unpublished.generateMethod( - storage, - JwkMemStore.ed25519KeyType(), - JwsAlgorithm.EdDSA, - "#key-1", - MethodScope.VerificationMethod(), - ); - - return [unpublished, verificationMethodFragment]; -} - -export async function getClientAndCreateAccount(storage: Storage): Promise { - if (!IDENTITY_IOTA_PACKAGE_ID) { - throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); - } - - const iotaClient = new IotaClient({ url: NETWORK_URL }); - - const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( - iotaClient, IDENTITY_IOTA_PACKAGE_ID); - - // generate new key - let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); - let publicKeyJwk = generate.jwk().toPublic(); - if (typeof publicKeyJwk === "undefined") { - throw new Error("failed to derive public JWK from generated JWK"); - } - let keyId = generate.keyId(); - - // create signer from storage - let signer = new StorageSigner(storage, keyId, publicKeyJwk); - const identityClient = await IdentityClient.create(identityClientReadOnly, signer); - - await requestIotaFromFaucetV0({ - host: getFaucetHost(NETWORK_NAME_FAUCET), - recipient: identityClient.senderAddress(), - }); - - const balance = await iotaClient.getBalance({ owner: identityClient.senderAddress() }); - if (balance.totalBalance === "0") { - throw new Error("Balance is still 0"); - } else { - console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); - } - - return identityClient; -} - -export async function createDidDocument( - identityClient: IdentityClient, - unpublished: IotaDocument, -): Promise { - let tx = identityClient - .publishDidDocument(unpublished); - let txOutput = await tx.execute(identityClient); - - return txOutput.output; - } - \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/rebased/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs index 4e2c5a6478..b73506ee4f 100644 --- a/bindings/wasm/identity_wasm/src/rebased/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -52,6 +52,12 @@ impl WasmOnChainIdentity { self.0.id().to_string() } + #[wasm_bindgen(js_name = didDocument)] + pub fn did_document(&self) -> WasmIotaDocument { + let inner_doc: IotaDocument = self.0.as_ref().clone().into(); + WasmIotaDocument::from(inner_doc) + } + #[wasm_bindgen(js_name = isShared)] pub fn is_shared(&self) -> bool { self.0.is_shared() diff --git a/bindings/wasm/identity_wasm/tests/txm_readme.js b/bindings/wasm/identity_wasm/tests/txm_readme.js index 2a388c2dba..e912404751 100644 --- a/bindings/wasm/identity_wasm/tests/txm_readme.js +++ b/bindings/wasm/identity_wasm/tests/txm_readme.js @@ -1,7 +1,7 @@ const assert = require("assert"); const spawn = require("child_process").spawn; -describe("Test TXM", () => { +describe.skip("Test TXM", () => { before((done) => { let process = spawn("txm", ["README.md"]); process.stdout.on("data", function(data) { diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs index 980febfda3..35a61a52aa 100644 --- a/examples/0_basic/0_create_did.rs +++ b/examples/0_basic/0_create_did.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; @@ -17,7 +17,7 @@ use examples::get_memstorage; async fn main() -> anyhow::Result<()> { // create new client to interact with chain and get funded account with keys let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, _) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/0_basic/1_update_did.rs b/examples/0_basic/1_update_did.rs index 945aa639e2..e15d04a117 100644 --- a/examples/0_basic/1_update_did.rs +++ b/examples/0_basic/1_update_did.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; use identity_iota::core::json; @@ -24,7 +24,7 @@ use identity_iota::verification::MethodScope; async fn main() -> anyhow::Result<()> { // create new client to interact with chain and get funded account with keys let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, vm_fragment_1) = create_did_document(&identity_client, &storage).await?; let did: IotaDID = document.id().clone(); diff --git a/examples/0_basic/2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs index fc0188edc3..233132b20d 100644 --- a/examples/0_basic/2_resolve_did.rs +++ b/examples/0_basic/2_resolve_did.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use identity_iota::iota::IotaDocument; @@ -13,7 +13,7 @@ use identity_iota::prelude::Resolver; async fn main() -> anyhow::Result<()> { // create new client to interact with chain and get funded account with keys let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, _) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/0_basic/3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs index a8dd772077..4d3b12c601 100644 --- a/examples/0_basic/3_deactivate_did.rs +++ b/examples/0_basic/3_deactivate_did.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; use identity_iota::iota::IotaDID; @@ -13,7 +13,7 @@ use identity_iota::iota::IotaDocument; async fn main() -> anyhow::Result<()> { // create new client to interact with chain and get funded account with keys let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, _) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/0_basic/5_create_vc.rs b/examples/0_basic/5_create_vc.rs index e6ed4610ac..65ed7255cd 100644 --- a/examples/0_basic/5_create_vc.rs +++ b/examples/0_basic/5_create_vc.rs @@ -10,7 +10,7 @@ //! cargo run --release --example 5_create_vc use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use identity_eddsa_verifier::EdDSAJwsVerifier; use identity_iota::core::Object; @@ -35,12 +35,12 @@ use identity_iota::did::DID; async fn main() -> anyhow::Result<()> { // create new issuer account with did document let issuer_storage = get_memstorage()?; - let issuer_identity_client = get_client_and_create_account(&issuer_storage).await?; + let issuer_identity_client = get_funded_client(&issuer_storage).await?; let (issuer_document, issuer_vm_fragment) = create_did_document(&issuer_identity_client, &issuer_storage).await?; // create new holder account with did document let holder_storage = get_memstorage()?; - let holder_identity_client = get_client_and_create_account(&holder_storage).await?; + let holder_identity_client = get_funded_client(&holder_storage).await?; let (holder_document, _) = create_did_document(&holder_identity_client, &holder_storage).await?; // Create a credential subject indicating the degree earned by Alice. diff --git a/examples/0_basic/6_create_vp.rs b/examples/0_basic/6_create_vp.rs index 916d63e99d..3f9f2b49fe 100644 --- a/examples/0_basic/6_create_vp.rs +++ b/examples/0_basic/6_create_vp.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use identity_eddsa_verifier::EdDSAJwsVerifier; use identity_iota::core::Object; @@ -53,18 +53,18 @@ async fn main() -> anyhow::Result<()> { // create new issuer account with did document let issuer_storage = get_memstorage()?; - let issuer_identity_client = get_client_and_create_account(&issuer_storage).await?; + let issuer_identity_client = get_funded_client(&issuer_storage).await?; let (issuer_document, issuer_vm_fragment) = create_did_document(&issuer_identity_client, &issuer_storage).await?; // create new holder account with did document let holder_storage = get_memstorage()?; - let holder_identity_client = get_client_and_create_account(&holder_storage).await?; + let holder_identity_client = get_funded_client(&holder_storage).await?; let (holder_document, holder_vm_fragment) = create_did_document(&holder_identity_client, &holder_storage).await?; // create new client for verifier // new client actually not necessary, but shows, that client is independent from issuer and holder let verifier_storage = &get_memstorage()?; - let verifier_client = get_client_and_create_account(verifier_storage).await?; + let verifier_client = get_funded_client(verifier_storage).await?; // =========================================================================== // Step 2: Issuer creates and signs a Verifiable Credential. diff --git a/examples/0_basic/7_revoke_vc.rs b/examples/0_basic/7_revoke_vc.rs index c713f70ca9..e22c5f61be 100644 --- a/examples/0_basic/7_revoke_vc.rs +++ b/examples/0_basic/7_revoke_vc.rs @@ -12,7 +12,7 @@ use anyhow::anyhow; use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; use identity_eddsa_verifier::EdDSAJwsVerifier; @@ -51,12 +51,12 @@ async fn main() -> anyhow::Result<()> { // create new issuer account with did document let issuer_storage = get_memstorage()?; - let issuer_identity_client = get_client_and_create_account(&issuer_storage).await?; + let issuer_identity_client = get_funded_client(&issuer_storage).await?; let (mut issuer_document, issuer_vm_fragment) = create_did_document(&issuer_identity_client, &issuer_storage).await?; // create new holder account with did document let holder_storage = get_memstorage()?; - let holder_identity_client = get_client_and_create_account(&holder_storage).await?; + let holder_identity_client = get_funded_client(&holder_storage).await?; let (holder_document, _) = create_did_document(&holder_identity_client, &holder_storage).await?; // Create a new empty revocation bitmap. No credential is revoked yet. diff --git a/examples/0_basic/8_legacy_stronghold.rs b/examples/0_basic/8_legacy_stronghold.rs index a2261a2973..86776a87a4 100644 --- a/examples/0_basic/8_legacy_stronghold.rs +++ b/examples/0_basic/8_legacy_stronghold.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_stronghold_storage; use examples::random_stronghold_path; use identity_eddsa_verifier::EdDSAJwsVerifier; @@ -25,7 +25,7 @@ async fn main() -> anyhow::Result<()> { let storage = get_stronghold_storage(Some(path.clone()))?; // use stronghold storage to create new client to interact with chain and get funded account with keys - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create and publish document with stronghold storage let (document, vm_fragment) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/1_advanced/10_zkp_revocation.rs b/examples/1_advanced/10_zkp_revocation.rs index d966917e0e..807db667ab 100644 --- a/examples/1_advanced/10_zkp_revocation.rs +++ b/examples/1_advanced/10_zkp_revocation.rs @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung, Fondazione Links // SPDX-License-Identifier: Apache-2.0 -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::MemStorage; use examples::TEST_GAS_BUDGET; @@ -130,9 +130,9 @@ async fn main() -> anyhow::Result<()> { let storage_holder: MemStorage = MemStorage::new(JwkMemStore::new(), KeyIdMemstore::new()); - let issuer_identity_client = get_client_and_create_account(&storage_issuer).await?; + let issuer_identity_client = get_funded_client(&storage_issuer).await?; - let holder_identity_client = get_client_and_create_account(&storage_holder).await?; + let holder_identity_client = get_funded_client(&storage_holder).await?; let (mut issuer_document, fragment_issuer): (IotaDocument, String) = create_did( &issuer_identity_client, diff --git a/examples/1_advanced/11_linked_verifiable_presentation.rs b/examples/1_advanced/11_linked_verifiable_presentation.rs index 648b085bf6..97b50e8bcf 100644 --- a/examples/1_advanced/11_linked_verifiable_presentation.rs +++ b/examples/1_advanced/11_linked_verifiable_presentation.rs @@ -4,7 +4,7 @@ use anyhow::Context; use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::MemStorage; use examples::TEST_GAS_BUDGET; @@ -43,7 +43,7 @@ async fn main() -> anyhow::Result<()> { let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (mut did_document, fragment) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/1_advanced/4_identity_history.rs b/examples/1_advanced/4_identity_history.rs index 6450b3ded6..87f5efd690 100644 --- a/examples/1_advanced/4_identity_history.rs +++ b/examples/1_advanced/4_identity_history.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; use identity_iota::core::json; @@ -24,7 +24,7 @@ async fn main() -> anyhow::Result<()> { // Create a new client to interact with the IOTA ledger. // NOTE: a permanode is required to fetch older output histories. let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, vm_fragment_1) = create_did_document(&identity_client, &storage).await?; let did: IotaDID = document.id().clone(); diff --git a/examples/1_advanced/5_custom_resolution.rs b/examples/1_advanced/5_custom_resolution.rs index 3dfcdf211f..c466744bd8 100644 --- a/examples/1_advanced/5_custom_resolution.rs +++ b/examples/1_advanced/5_custom_resolution.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use identity_iota::core::FromJson; use identity_iota::core::ToJson; @@ -20,7 +20,7 @@ use identity_iota::resolver::Resolver; async fn main() -> anyhow::Result<()> { // create new client to interact with chain and get funded account with keys let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // create new DID document and publish it let (document, _) = create_did_document(&identity_client, &storage).await?; diff --git a/examples/1_advanced/6_domain_linkage.rs b/examples/1_advanced/6_domain_linkage.rs index b7a29f2275..ca7e69ccd7 100644 --- a/examples/1_advanced/6_domain_linkage.rs +++ b/examples/1_advanced/6_domain_linkage.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; use identity_eddsa_verifier::EdDSAJwsVerifier; @@ -34,7 +34,7 @@ use identity_iota::storage::JwsSignatureOptions; async fn main() -> anyhow::Result<()> { // Create new client to interact with chain and get funded account with keys. let storage = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage).await?; + let identity_client = get_funded_client(&storage).await?; // Create a DID for the entity that will issue the Domain Linkage Credential. let (mut document, vm_fragment_1) = create_did_document(&identity_client, &storage).await?; let did: IotaDID = document.id().clone(); diff --git a/examples/1_advanced/7_sd_jwt.rs b/examples/1_advanced/7_sd_jwt.rs index 6ced57c846..0f3199d727 100644 --- a/examples/1_advanced/7_sd_jwt.rs +++ b/examples/1_advanced/7_sd_jwt.rs @@ -7,7 +7,7 @@ //! cargo run --release --example 7_sd_jwt use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::pretty_print_json; use identity_eddsa_verifier::EdDSAJwsVerifier; @@ -42,12 +42,12 @@ async fn main() -> anyhow::Result<()> { // Create an identity for the issuer with one verification method `key-1`. let issuer_storage = get_memstorage()?; - let issuer_identity_client = get_client_and_create_account(&issuer_storage).await?; + let issuer_identity_client = get_funded_client(&issuer_storage).await?; let (issuer_document, issuer_vm_fragment) = create_did_document(&issuer_identity_client, &issuer_storage).await?; // Create an identity for the holder, in this case also the subject. let holder_storage = get_memstorage()?; - let holder_identity_client = get_client_and_create_account(&holder_storage).await?; + let holder_identity_client = get_funded_client(&holder_storage).await?; let (holder_document, holder_vm_fragment) = create_did_document(&holder_identity_client, &holder_storage).await?; // =========================================================================== diff --git a/examples/1_advanced/8_status_list_2021.rs b/examples/1_advanced/8_status_list_2021.rs index 3a87cb24d2..d32a5bb3bb 100644 --- a/examples/1_advanced/8_status_list_2021.rs +++ b/examples/1_advanced/8_status_list_2021.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use examples::create_did_document; -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use identity_eddsa_verifier::EdDSAJwsVerifier; use identity_iota::core::FromJson; @@ -40,12 +40,12 @@ async fn main() -> anyhow::Result<()> { // create new issuer account with did document let issuer_storage = get_memstorage()?; - let issuer_identity_client = get_client_and_create_account(&issuer_storage).await?; + let issuer_identity_client = get_funded_client(&issuer_storage).await?; let (issuer_document, issuer_vm_fragment) = create_did_document(&issuer_identity_client, &issuer_storage).await?; // create new holder account with did document let holder_storage = get_memstorage()?; - let holder_identity_client = get_client_and_create_account(&holder_storage).await?; + let holder_identity_client = get_funded_client(&holder_storage).await?; let (holder_document, _) = create_did_document(&holder_identity_client, &holder_storage).await?; // Create a new empty status list. No credentials have been revoked yet. diff --git a/examples/1_advanced/9_zkp.rs b/examples/1_advanced/9_zkp.rs index 75b9b65cd7..9d03b44f37 100644 --- a/examples/1_advanced/9_zkp.rs +++ b/examples/1_advanced/9_zkp.rs @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung, Fondazione Links // SPDX-License-Identifier: Apache-2.0 -use examples::get_client_and_create_account; +use examples::get_funded_client; use examples::get_memstorage; use examples::TEST_GAS_BUDGET; @@ -78,7 +78,7 @@ async fn main() -> anyhow::Result<()> { let storage_issuer = get_memstorage()?; - let identity_client = get_client_and_create_account(&storage_issuer).await?; + let identity_client = get_funded_client(&storage_issuer).await?; let (issuer_document, fragment_issuer): (IotaDocument, String) = create_did( &identity_client, diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs index d60f8b06ef..02026b5d93 100644 --- a/examples/utils/utils.rs +++ b/examples/utils/utils.rs @@ -76,7 +76,7 @@ pub fn random_stronghold_path() -> PathBuf { file.to_owned() } -pub async fn get_client_and_create_account( +pub async fn get_funded_client( storage: &Storage, ) -> Result>, anyhow::Error> where diff --git a/identity_iota_core/tests/e2e/asset.rs b/identity_iota_core/tests/e2e/asset.rs index ddab27ba2a..1312ff3b71 100644 --- a/identity_iota_core/tests/e2e/asset.rs +++ b/identity_iota_core/tests/e2e/asset.rs @@ -3,7 +3,7 @@ use std::str::FromStr; -use crate::common::get_client as get_test_client; +use crate::common::get_funded_test_client; use crate::common::TEST_GAS_BUDGET; use identity_core::common::Object; use identity_core::common::Timestamp; @@ -31,7 +31,7 @@ use move_core_types::language_storage::StructTag; #[tokio::test] async fn creating_authenticated_asset_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let asset = alice_client @@ -47,7 +47,7 @@ async fn creating_authenticated_asset_works() -> anyhow::Result<()> { #[tokio::test] async fn transferring_asset_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let bob_client = test_client.new_user_client().await?; @@ -107,7 +107,7 @@ async fn transferring_asset_works() -> anyhow::Result<()> { #[tokio::test] async fn accepting_the_transfer_of_an_asset_requires_capability() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let bob_client = test_client.new_user_client().await?; let caty_client = test_client.new_user_client().await?; @@ -140,7 +140,7 @@ async fn accepting_the_transfer_of_an_asset_requires_capability() -> anyhow::Res #[tokio::test] async fn modifying_mutable_asset_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let mut asset = alice_client @@ -159,7 +159,7 @@ async fn modifying_mutable_asset_works() -> anyhow::Result<()> { #[tokio::test] async fn deleting_asset_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let asset = alice_client @@ -187,7 +187,7 @@ async fn deleting_asset_works() -> anyhow::Result<()> { #[tokio::test] async fn hosting_vc_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let newly_created_identity = identity_client diff --git a/identity_iota_core/tests/e2e/client.rs b/identity_iota_core/tests/e2e/client.rs index cb40cc0370..29afecddf3 100644 --- a/identity_iota_core/tests/e2e/client.rs +++ b/identity_iota_core/tests/e2e/client.rs @@ -3,14 +3,14 @@ use std::ops::Deref; -use crate::common::get_client as get_test_client; +use crate::common::get_funded_test_client; use identity_iota_core::rebased::migration; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::IotaDocument; #[tokio::test] async fn can_create_an_identity() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let identity = identity_client @@ -28,7 +28,7 @@ async fn can_create_an_identity() -> anyhow::Result<()> { #[tokio::test] async fn can_resolve_a_new_identity() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let new_identity = identity_client diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index df2f43a8c3..a4e6dad306 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -76,7 +76,7 @@ lazy_static! { pub static ref TEST_COIN_TYPE: StructTag = "0x2::coin::Coin".parse().unwrap(); } -pub async fn get_client() -> anyhow::Result { +pub async fn get_funded_test_client() -> anyhow::Result { let api_endpoint = std::env::var("API_ENDPOINT").unwrap_or_else(|_| IOTA_LOCAL_NETWORK_URL.to_string()); let client = IotaClientBuilder::default().build(&api_endpoint).await?; let package_id = PACKAGE_ID.get_or_try_init(|| init(&client)).await.copied()?; diff --git a/identity_iota_core/tests/e2e/identity.rs b/identity_iota_core/tests/e2e/identity.rs index 7ad719dd8b..545444c19d 100644 --- a/identity_iota_core/tests/e2e/identity.rs +++ b/identity_iota_core/tests/e2e/identity.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use crate::common; -use crate::common::get_client as get_test_client; +use crate::common::get_funded_test_client; use crate::common::get_key_data; use crate::common::TEST_COIN_TYPE; use crate::common::TEST_GAS_BUDGET; @@ -29,7 +29,7 @@ use move_core_types::language_storage::StructTag; #[tokio::test] async fn identity_deactivation_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut identity = identity_client @@ -53,7 +53,7 @@ async fn identity_deactivation_works() -> anyhow::Result<()> { #[tokio::test] async fn updating_onchain_identity_did_doc_with_single_controller_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut newly_created_identity = identity_client @@ -89,7 +89,7 @@ async fn updating_onchain_identity_did_doc_with_single_controller_works() -> any #[tokio::test] async fn approving_proposal_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let bob_client = test_client.new_user_client().await?; @@ -136,7 +136,7 @@ async fn approving_proposal_works() -> anyhow::Result<()> { #[tokio::test] async fn adding_controller_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let alice_client = test_client.new_user_client().await?; let bob_client = test_client.new_user_client().await?; @@ -171,7 +171,7 @@ async fn adding_controller_works() -> anyhow::Result<()> { #[tokio::test] async fn can_get_historical_identity_data() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut newly_created_identity = identity_client @@ -264,7 +264,7 @@ async fn can_get_historical_identity_data() -> anyhow::Result<()> { #[tokio::test] async fn send_proposal_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut identity = identity_client @@ -309,7 +309,7 @@ async fn send_proposal_works() -> anyhow::Result<()> { #[tokio::test] async fn borrow_proposal_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut identity = identity_client @@ -351,7 +351,7 @@ async fn borrow_proposal_works() -> anyhow::Result<()> { #[tokio::test] async fn controller_execution_works() -> anyhow::Result<()> { - let test_client = get_test_client().await?; + let test_client = get_funded_test_client().await?; let identity_client = test_client.new_user_client().await?; let mut identity = identity_client diff --git a/identity_iota_core/tests/e2e/migration.rs b/identity_iota_core/tests/e2e/migration.rs index 7bdcb4210d..39597203e5 100644 --- a/identity_iota_core/tests/e2e/migration.rs +++ b/identity_iota_core/tests/e2e/migration.rs @@ -1,13 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::common::get_client; +use crate::common::get_funded_test_client; use identity_iota_core::rebased::migration; use iota_sdk::types::base_types::ObjectID; #[tokio::test] async fn migration_registry_is_found() -> anyhow::Result<()> { - let client = get_client().await?; + let client = get_funded_test_client().await?; let random_alias_id = ObjectID::random(); let doc = migration::lookup(&client, random_alias_id).await?; From f7ffc941cd7cc992259a47282c2411c917d244b6 Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:36:53 +0100 Subject: [PATCH 37/63] WASM Proposals (#1529) * update_did and deactivate_did wasm * DeactivateDid proposal * WASM Update DID Document proposal * WASM send assets proposal * fix WASM Send proposal * update TS example to use proposals * config change proposal * use deactivate_did proposal in examples * rename IDENTITY_IOTA_PACKAGE_ID to IDENTITY_IOTA_PKG_ID * use proposal API and fix WasmOnChainIdentity::didDocument * fix pkg name * Update identity_iota_core/src/rebased/proposals/config_change.rs Co-authored-by: wulfraem * remove unused type --------- Co-authored-by: wulfraem --- .../wasm/identity_wasm/examples/README.md | 8 +- .../examples/src/0_basic/0_create_did.ts | 15 +- .../examples/src/0_basic/1_update_did.ts | 19 +- .../examples/src/0_basic/2_resolve_did.ts | 16 +- .../examples/src/0_basic/3_deactivate_did.ts | 21 +- .../examples/src/0_basic/5_create_vc.ts | 7 +- .../examples/src/0_basic/6_create_vp.ts | 10 +- .../examples/src/0_basic/7_revoke_vc.ts | 32 +- .../src/1_advanced/4_custom_resolution.ts | 16 +- .../src/1_advanced/5_domain_linkage.ts | 15 +- .../examples/src/1_advanced/6_sd_jwt.ts | 7 +- .../src/1_advanced/7_status_list_2021.ts | 7 +- .../examples/src/1_advanced/8_zkp.ts | 11 +- .../src/1_advanced/9_zkp_revocation.ts | 11 +- .../wasm/identity_wasm/examples/src/util.ts | 19 +- bindings/wasm/identity_wasm/lib/resolver.ts | 46 +-- .../identity_wasm/src/rebased/identity.rs | 121 +++---- .../wasm/identity_wasm/src/rebased/mod.rs | 1 + .../src/rebased/multicontroller.rs | 6 +- .../src/rebased/proposals/config_change.rs | 330 ++++++++++++++++++ .../src/rebased/proposals/deactivate_did.rs | 252 +++++++++++++ .../src/rebased/proposals/mod.rs | 103 ++++++ .../src/rebased/proposals/send.rs | 293 ++++++++++++++++ .../src/rebased/proposals/update_did.rs | 289 +++++++++++++++ .../src/rebased/wasm_identity_client.rs | 8 + .../rebased/wasm_identity_client_read_only.rs | 2 +- .../scripts/publish_identity_package.sh | 2 +- .../src/rebased/migration/multicontroller.rs | 10 + .../src/rebased/proposals/config_change.rs | 39 +++ .../src/rebased/proposals/send.rs | 6 + .../src/rebased/proposals/update_did_doc.rs | 5 + .../src/state_metadata/document.rs | 5 + 32 files changed, 1506 insertions(+), 226 deletions(-) create mode 100644 bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs create mode 100644 bindings/wasm/identity_wasm/src/rebased/proposals/deactivate_did.rs create mode 100644 bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs create mode 100644 bindings/wasm/identity_wasm/src/rebased/proposals/send.rs create mode 100644 bindings/wasm/identity_wasm/src/rebased/proposals/update_did.rs diff --git a/bindings/wasm/identity_wasm/examples/README.md b/bindings/wasm/identity_wasm/examples/README.md index e3ce021442..57bbe7b9f6 100644 --- a/bindings/wasm/identity_wasm/examples/README.md +++ b/bindings/wasm/identity_wasm/examples/README.md @@ -12,7 +12,7 @@ Examples can be run against - a local IOTA node - or an existing network, e.g. the IOTA testnet -When setting up the local node, you'll also need to publish an identity package as described in [Getting Started](../../../../README.md#getting-started), the `IDENTITY_IOTA_PACKAGE_ID`. You'll need this ID to be able to run the examples against the local node. +When setting up the local node, you'll also need to publish an identity package as described in [Getting Started](../../../../README.md#getting-started), the `IOTA_IDENTITY_PKG_ID`. You'll need this ID to be able to run the examples against the local node. In case of running the examples against an existing network, this network needs to have a faucet to fund your accounts (the IOTA testnet (`https://api.testnet.iota.cafe`) supports this), and you need to specify this via `NETWORK_URL`. @@ -24,7 +24,7 @@ Summarizing the last point, you'll need one or more of the following environment | Name | Required for local node | Required for testnet | Required for other node | Comment | | ------------------------ | :---------------------: | :------------------: | :---------------------: | :------------------: | -| IDENTITY_IOTA_PACKAGE_ID | x | | x | | +| IOTA_IDENTITY_PKG_ID | x | | x | | | NETWORK_URL | | x | x | | | NETWORK_NAME_FAUCET | | x | x | see assumption above | @@ -45,13 +45,13 @@ npm run build Then, run an example using the following command, environment variables depend on you setup, see [Environment variables](#environment-variables). ```bash -IDENTITY_IOTA_PACKAGE_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- +IOTA_IDENTITY_PKG_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- ``` For instance, to run the `0_create_did` example with the following (environment variables depend on you setup, see [Environment variables](#environment-variables)): ```bash -IDENTITY_IOTA_PACKAGE_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- 0_create_did +IOTA_IDENTITY_PKG_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818 npm run example:node -- 0_create_did ``` ## Basic Examples diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 0cb07329cb..3b62bf3876 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -1,14 +1,9 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDID } from '@iota/identity-wasm/node'; +import { IotaDID } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; /** Demonstrate how to create a DID Document and publish it. */ export async function createIdentity(): Promise { @@ -20,7 +15,7 @@ export async function createIdentity(): Promise { const storage = getMemstorage(); const identityClient = await getFundedClient(storage); - // create new unpublished document + // create new unpublished document const [unpublished] = await createDocumentForNetwork(storage, network); console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); let did: IotaDID; @@ -28,14 +23,14 @@ export async function createIdentity(): Promise { // TODO: decide upon wich style to use here // so let's go with both for now, to show that both work if (Math.random() > .5) { - console.log('Creating new identity fully via client flow'); + console.log("Creating new identity fully via client flow"); const { output: identity } = await identityClient .createIdentity(unpublished) .finish() .execute(identityClient); did = IotaDID.fromAliasId(identity.id(), identityClient.network()); } else { - console.log('Publishing document to identity'); + console.log("Publishing document to identity"); const { output: published } = await identityClient .publishDidDocument(unpublished) .execute(identityClient); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index adac672c2f..4afcc39ed3 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -12,13 +12,7 @@ import { VerificationMethod, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, - TEST_GAS_BUDGET, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; /** Demonstrates how to update a DID document in an existing Alias Output. */ export async function updateIdentity() { @@ -63,10 +57,15 @@ export async function updateIdentity() { serviceEndpoint: "https://iota.org/", }); resolved.insertService(service); - resolved.setMetadataUpdated(Timestamp.nowUTC()); - let updated = await identityClient - .publishDidDocumentUpdate(resolved.clone(), TEST_GAS_BUDGET); + let maybePendingProposal = await identity + .updateDidDocument(resolved.clone()) + .withGasBudget(TEST_GAS_BUDGET) + .execute(identityClient) + .then(result => result.output); + + console.assert(maybePendingProposal === undefined, "the proposal should have been executed right away!"); + // and resolve again to make sure we're looking at the onchain information const resolvedAgain = await identityClient.resolveDid(did); console.log(`Updated DID document result: ${JSON.stringify(resolvedAgain, null, 2)}`); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index 6fb65a2df0..c48ac2ceeb 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -11,13 +11,7 @@ import { Resolver, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - IDENTITY_IOTA_PACKAGE_ID, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, IOTA_IDENTITY_PKG_ID, NETWORK_URL } from "../util"; const DID_JWK: string = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9"; @@ -65,14 +59,16 @@ export async function resolveIdentity() { console.log(`DID ${DID_JWK} resolves to:\n ${JSON.stringify(did_jwk_resolved_doc, null, 2)}`); // We can also create a resolver with a read-only client - const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID); + const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId(iotaClient, IOTA_IDENTITY_PKG_ID); // In this case we will only be resolving `IotaDocument` instances, as we don't pass a `handler` configuration. // Therefore we can limit the type of the resolved documents to `IotaDocument` when creating the new resolver as well. const resolverWithReadOnlyClient = new Resolver({ client: identityClientReadOnly }); // And resolve as before. const resolvedViaReadOnly = await resolverWithReadOnlyClient.resolve(did.toString()); - console.log(`resolverWithReadOnlyClient ${did.toString()} resolves to:\n ${JSON.stringify(resolvedViaReadOnly, null, 2)}`); + console.log( + `resolverWithReadOnlyClient ${did.toString()} resolves to:\n ${JSON.stringify(resolvedViaReadOnly, null, 2)}`, + ); // As our `Resolver` instance will only return `IotaDocument` instances, we can directly work with them, e.g. console.log(`${did.toString()}'s metadata is ${resolvedViaReadOnly.metadata()}`); @@ -81,4 +77,4 @@ export async function resolveIdentity() { const didJwkHandler = async (did: string) => { let did_jwk = DIDJwk.parse(did); return CoreDocument.expandDIDJwk(did_jwk); -}; \ No newline at end of file +}; diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index debb53eb8b..d91316f389 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -3,13 +3,7 @@ import { IotaDID } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, - TEST_GAS_BUDGET, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; /** Demonstrates how to deactivate a DID in an Alias Output. */ export async function deactivateIdentity() { @@ -32,8 +26,11 @@ export async function deactivateIdentity() { const resolved = await identityClient.resolveDid(did); console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); - // Deactivate the DID by publishing an empty document. - await identityClient.deactivateDidOutput(did, TEST_GAS_BUDGET); + // Deactivate the DID. + await identity + .deactivateDid() + .withGasBudget(TEST_GAS_BUDGET) + .execute(identityClient); // Resolving a deactivated DID returns an empty DID document // with its `deactivated` metadata field set to `true`. @@ -45,8 +42,10 @@ export async function deactivateIdentity() { // Re-activate the DID by publishing a valid DID document. console.log("Publishing this:", JSON.stringify(resolved, null, 2)); - await identityClient - .publishDidDocumentUpdate(resolved, TEST_GAS_BUDGET); + await identity + .updateDidDocument(resolved) + .withGasBudget(TEST_GAS_BUDGET) + .execute(identityClient); // Resolve the reactivated DID document. let resolvedReactivated = await identityClient.resolveDid(did); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts index 56a6db5b26..681def72e3 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts @@ -10,12 +10,7 @@ import { JwtCredentialValidator, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; /** * This example shows how to create a Verifiable Credential and validate it. diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts index 9fd1a74975..66fdcd73a2 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts @@ -23,13 +23,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - IDENTITY_IOTA_PACKAGE_ID, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, IOTA_IDENTITY_PKG_ID, NETWORK_URL } from "../util"; /** * This example shows how to create a Verifiable Presentation and validate it. @@ -164,7 +158,7 @@ export async function createVP() { ); const resolver = new Resolver({ - client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID), + client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IOTA_IDENTITY_PKG_ID), }); // Resolve the presentation holder. const presentationHolderDID: CoreDID = JwtPresentationValidator.extractHolder(presentationJwt); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts index 4b40b1c163..c650c078f4 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts @@ -23,10 +23,10 @@ import { createDocumentForNetwork, getFundedClient, getMemstorage, - IDENTITY_IOTA_PACKAGE_ID, + IOTA_IDENTITY_PKG_ID, NETWORK_URL, TEST_GAS_BUDGET, -} from '../util'; +} from "../util"; /** * This example shows how to revoke a verifiable credential. @@ -74,10 +74,10 @@ export async function revokeVC() { issuerDocument.insertService(service); // Publish the updated document. - let updatedDocument: IotaDocument = await issuerClient.publishDidDocumentUpdate( - issuerDocument, - TEST_GAS_BUDGET, - ); + await issuerIdentity + .updateDidDocument(issuerDocument) + .withGasBudget(TEST_GAS_BUDGET) + .execute(issuerClient); // Create a credential subject indicating the degree earned by Alice, linked to their DID. const subject = { @@ -130,10 +130,10 @@ export async function revokeVC() { issuerDocument.revokeCredentials("my-revocation-service", CREDENTIAL_INDEX); // Publish the changes. - let update2: IotaDocument = await issuerClient.publishDidDocumentUpdate( - issuerDocument, - TEST_GAS_BUDGET, - ); + await issuerIdentity + .updateDidDocument(issuerDocument) + .withGasBudget(TEST_GAS_BUDGET) + .execute(issuerClient); // Credential verification now fails. try { @@ -160,14 +160,16 @@ export async function revokeVC() { await issuerDocument.purgeMethod(issuerStorage, originalMethod.id()); // Publish the changes. - issuerDocument = await issuerClient.publishDidDocumentUpdate( - issuerDocument, - TEST_GAS_BUDGET, - ); + await issuerIdentity + .updateDidDocument(issuerDocument) + .withGasBudget(TEST_GAS_BUDGET) + .execute(issuerClient); + + issuerDocument = issuerIdentity.didDocument(); // We expect the verifiable credential to be revoked. const resolver = new Resolver({ - client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IDENTITY_IOTA_PACKAGE_ID), + client: await IdentityClientReadOnly.createWithPkgId(iotaClient, IOTA_IDENTITY_PKG_ID), }); try { // Resolve the issuer's updated DID Document to ensure the key was revoked successfully. diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts index 8eeded3119..26aaf4b634 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts @@ -1,16 +1,6 @@ -import { - CoreDocument, - IotaDID, - IotaDocument, - Resolver, -} from "@iota/identity-wasm/node"; +import { CoreDocument, IotaDID, IotaDocument, Resolver } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; // Use this external package to avoid implementing the entire did:key method in this example. import * as ed25519 from "@transmute/did-key-ed25519"; @@ -18,7 +8,7 @@ import * as ed25519 from "@transmute/did-key-ed25519"; type KeyDocument = { customProperty: String } & CoreDocument; function isKeyDocument(doc: object): doc is KeyDocument { - return 'customProperty' in doc; + return "customProperty" in doc; } /** Demonstrates how to set up a resolver using custom handlers. diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts index 8b8f616100..6b928690eb 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts @@ -17,13 +17,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, - TEST_GAS_BUDGET, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; /** * Demonstrates how to link a domain and a DID and verify the linkage. @@ -41,7 +35,7 @@ export async function domainLinkage() { .createIdentity(unpublished) .finish() .execute(identityClient); - const document = identity.didDocument();; + const document = identity.didDocument(); const did = document.id(); // ===================================================== @@ -59,8 +53,9 @@ export async function domainLinkage() { domains: [domainFoo, domainBar], }); document.insertService(linkedDomainService.toService()); - let updatedDidDocument = await await identityClient - .publishDidDocumentUpdate(document, TEST_GAS_BUDGET); + await identity.updateDidDocument(document).execute(identityClient); + + let updatedDidDocument = identity.didDocument(); console.log("Updated DID document:", JSON.stringify(updatedDidDocument, null, 2)); // ===================================================== diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts index 87807530d8..84d5bf4e52 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts @@ -17,12 +17,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; /** * Demonstrates how to create a selective disclosure verifiable credential and validate it * using the [Selective Disclosure for JWTs (SD-JWT)](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html) specification. diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts index 5db62743bb..a36bea5443 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts @@ -16,12 +16,7 @@ import { StatusPurpose, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; export async function statusList2021() { // =========================================================================== diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts index 54473de9ac..b216ebce0f 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts @@ -17,12 +17,7 @@ import { SelectiveDisclosurePresentation, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - getFundedClient, - getMemstorage, - IDENTITY_IOTA_PACKAGE_ID, - NETWORK_URL, -} from '../util'; +import { getFundedClient, getMemstorage, IOTA_IDENTITY_PKG_ID, NETWORK_URL } from "../util"; export async function zkp() { // =========================================================================== @@ -96,7 +91,9 @@ export async function zkp() { // Step 4: Holder resolve Issuer's DID, retrieve Issuer's document and validate the Credential // ============================================================================================ const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( - iotaClient, IDENTITY_IOTA_PACKAGE_ID); + iotaClient, + IOTA_IDENTITY_PKG_ID, + ); // Holder resolves issuer's DID. let issuerDid = IotaDID.parse(JptCredentialValidatorUtils.extractIssuerFromIssuedJpt(credentialJpt).toString()); diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts index c1de5f57ae..b9bb2a768c 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts @@ -21,13 +21,7 @@ import { Timestamp, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { - createDocumentForNetwork, - getFundedClient, - getMemstorage, - NETWORK_URL, - TEST_GAS_BUDGET, -} from '../util'; +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; export async function zkp_revocation() { // create new client to connect to IOTA network @@ -201,7 +195,8 @@ export async function zkp_revocation() { // Update the RevocationBitmap service in the issuer's DID Document. // This revokes the credential's unique index. issuerDocument.revokeCredentials("my-revocation-service", 5); - issuerDocument = await issuerClient.publishDidDocumentUpdate(issuerDocument, TEST_GAS_BUDGET); + await issuerIdentity.updateDidDocument(issuerDocument).execute(issuerClient); + issuerDocument = issuerIdentity.didDocument(); // Holder checks if his credential has been revoked by the Issuer try { diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index 00acf200f5..3929853684 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { + IdentityClient, + IdentityClientReadOnly, IotaDocument, JwkMemStore, JwsAlgorithm, KeyIdMemStore, - IdentityClient, - IdentityClientReadOnly, MethodScope, Storage, StorageSigner, @@ -15,11 +15,10 @@ import { import { IotaClient } from "@iota/iota-sdk/client"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; -export const IDENTITY_IOTA_PACKAGE_ID = - process.env.IDENTITY_IOTA_PACKAGE_ID || "0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818"; +export const IOTA_IDENTITY_PKG_ID = process.env.IOTA_IDENTITY_PKG_ID + || "0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818"; export const NETWORK_NAME_FAUCET = process.env.NETWORK_NAME_FAUCET || "localnet"; -export const NETWORK_URL = - process.env.NETWORK_URL || "http://127.0.0.1:9000"; +export const NETWORK_URL = process.env.NETWORK_URL || "http://127.0.0.1:9000"; export const TEST_GAS_BUDGET = BigInt(50_000_000); export function getMemstorage(): Storage { @@ -42,14 +41,16 @@ export async function createDocumentForNetwork(storage: Storage, network: string } export async function getFundedClient(storage: Storage): Promise { - if (!IDENTITY_IOTA_PACKAGE_ID) { - throw new Error(`IDENTITY_IOTA_PACKAGE_ID env variable must be provided to run the examples`); + if (!IOTA_IDENTITY_PKG_ID) { + throw new Error(`IOTA_IDENTITY_PKG_ID env variable must be provided to run the examples`); } const iotaClient = new IotaClient({ url: NETWORK_URL }); const identityClientReadOnly = await IdentityClientReadOnly.createWithPkgId( - iotaClient, IDENTITY_IOTA_PACKAGE_ID); + iotaClient, + IOTA_IDENTITY_PKG_ID, + ); // generate new key let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); diff --git a/bindings/wasm/identity_wasm/lib/resolver.ts b/bindings/wasm/identity_wasm/lib/resolver.ts index 611fc278e2..93c9665f94 100644 --- a/bindings/wasm/identity_wasm/lib/resolver.ts +++ b/bindings/wasm/identity_wasm/lib/resolver.ts @@ -8,30 +8,30 @@ import { CoreDocument, IToCoreDocument, Resolver as ResolverInner } from "~ident // the constructor to specify the types expected to be returned by the `resolve` function. /** -* Convenience type for resolving DID documents from different DID methods. -* -* DID documents resolved with `resolve` will have the type specified as generic type parameter T. -* With the default being `CoreDocument | IToCoreDocument`. -* -* Also provides methods for resolving DID Documents associated with -* verifiable {@link Credential}s and {@link Presentation}s. -* -* # Configuration -* -* The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. -*/ + * Convenience type for resolving DID documents from different DID methods. + * + * DID documents resolved with `resolve` will have the type specified as generic type parameter T. + * With the default being `CoreDocument | IToCoreDocument`. + * + * Also provides methods for resolving DID Documents associated with + * verifiable {@link Credential}s and {@link Presentation}s. + * + * # Configuration + * + * The resolver will only be able to resolve DID documents for methods it has been configured for in the constructor. + */ export class Resolver extends ResolverInner { /** - * Fetches the DID Document of the given DID. - * - * ### Errors - * - * Errors if the resolver has not been configured to handle the method - * corresponding to the given DID or the resolution process itself fails. - * @param {string} did - * @returns {Promise} - */ + * Fetches the DID Document of the given DID. + * + * ### Errors + * + * Errors if the resolver has not been configured to handle the method + * corresponding to the given DID or the resolution process itself fails. + * @param {string} did + * @returns {Promise} + */ async resolve(did: string): Promise { - return super.resolve(did) as unknown as T; + return super.resolve(did) as unknown as T; } - } \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/rebased/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs index b73506ee4f..30f1a3ed44 100644 --- a/bindings/wasm/identity_wasm/src/rebased/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -1,6 +1,8 @@ -// Copyright 2020-2023 IOTA Stiftung +// Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::rc::Rc; + use identity_iota::iota::rebased::migration::CreateIdentityTx; use identity_iota::iota::rebased::migration::IdentityBuilder; use identity_iota::iota::rebased::migration::OnChainIdentity; @@ -8,25 +10,29 @@ use identity_iota::iota::rebased::transaction::TransactionInternal; use identity_iota::iota::rebased::transaction::TransactionOutputInternal; use identity_iota::iota::IotaDocument; use iota_interaction_ts::AdapterNativeResponse; +use tokio::sync::RwLock; use tsify::Tsify; use wasm_bindgen::prelude::*; use iota_interaction_ts::bindings::WasmIotaObjectData; -use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; - use crate::error::wasm_error; use crate::error::Result; +use crate::error::WasmResult; use crate::iota::WasmIotaDocument; -use super::client_dummy::DummySigner; use super::client_dummy::ProposalAction; -use super::client_dummy::ProposalBuilder; -use super::types::into_sdk_type; +// use super::client_dummy::DummySigner; +// use super::client_dummy::ProposalAction; +// use super::client_dummy::ProposalBuilder; +use super::proposals::StringCouple; +use super::proposals::WasmConfigChange; +use super::proposals::WasmCreateConfigChangeProposalTx; +use super::proposals::WasmCreateDeactivateDidProposalTx; +use super::proposals::WasmCreateSendProposalTx; +use super::proposals::WasmCreateUpdateDidProposalTx; use super::types::WasmIotaAddress; -use super::types::WasmObjectID; use super::WasmIdentityClient; -use super::WasmProposal; /// Helper type for `WasmIdentityBuilder::controllers`. /// Has getters to support `Clone` for serialization @@ -43,46 +49,68 @@ impl ControllerAndVotingPower { } #[wasm_bindgen(js_name = OnChainIdentity)] -pub struct WasmOnChainIdentity(pub(crate) OnChainIdentity); +#[derive(Clone)] +pub struct WasmOnChainIdentity(pub(crate) Rc>); #[wasm_bindgen(js_class = OnChainIdentity)] impl WasmOnChainIdentity { + pub(crate) fn new(identity: OnChainIdentity) -> Self { + Self(Rc::new(RwLock::new(identity))) + } + #[wasm_bindgen] - pub fn id(&self) -> String { - self.0.id().to_string() + pub fn id(&self) -> Result { + Ok(self.0.try_read().wasm_result()?.id().to_string()) } #[wasm_bindgen(js_name = didDocument)] - pub fn did_document(&self) -> WasmIotaDocument { - let inner_doc: IotaDocument = self.0.as_ref().clone().into(); - WasmIotaDocument::from(inner_doc) + pub fn did_document(&self) -> Result { + let inner_doc = self.0.try_read().wasm_result()?.did_document().clone(); + Ok(WasmIotaDocument::from(inner_doc)) } #[wasm_bindgen(js_name = isShared)] - pub fn is_shared(&self) -> bool { - self.0.is_shared() + pub fn is_shared(&self) -> Result { + Ok(self.0.try_read().wasm_result()?.is_shared()) } #[wasm_bindgen(skip_typescript)] // ts type in custom section below pub fn proposals(&self) -> Result { - serde_wasm_bindgen::to_value(&self.0.proposals()).map_err(wasm_error) + let lock = self.0.try_read().wasm_result()?; + let proposals = lock.proposals(); + serde_wasm_bindgen::to_value(proposals).map_err(wasm_error) } #[wasm_bindgen(js_name = updateDidDocument)] - pub fn update_did_document(self, updated_doc: WasmIotaDocument) -> Result { - unimplemented!("WasmOnChainIdentity::update_did_document"); - // let doc: IotaDocument = updated_doc - // .0 - // .try_read() - // .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? - // .clone(); - // Ok(WasmProposalBuilder(self.0.update_did_document::(doc))) + pub fn update_did_document( + &self, + updated_doc: WasmIotaDocument, + expiration_epoch: Option, + ) -> WasmCreateUpdateDidProposalTx { + WasmCreateUpdateDidProposalTx::new(self, updated_doc, expiration_epoch) } #[wasm_bindgen(js_name = deactivateDid)] - pub fn deactivate_did(self) -> WasmProposalBuilder { - unimplemented!("WasmOnChainIdentity::deactivate_did"); - // WasmProposalBuilder(self.0.deactivate_did::()) + pub fn deactivate_did(&self, expiration_epoch: Option) -> WasmCreateDeactivateDidProposalTx { + WasmCreateDeactivateDidProposalTx::new(self, expiration_epoch) + } + + #[wasm_bindgen(js_name = updateConfig)] + pub fn update_config( + &self, + config: WasmConfigChange, + expiration_epoch: Option, + ) -> WasmCreateConfigChangeProposalTx { + WasmCreateConfigChangeProposalTx::new(self, config, expiration_epoch) + } + + #[wasm_bindgen(js_name = sendAssets)] + pub fn send_assets( + &self, + transfer_map: Vec, + expiration_epoch: Option, + ) -> WasmCreateSendProposalTx { + WasmCreateSendProposalTx::new(self, transfer_map, expiration_epoch) } #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below @@ -138,41 +166,6 @@ impl From for ProposalAction { // #[declare] // pub type ProposalAction = WasmProposalAction; -#[wasm_bindgen(js_name = ProposalBuilder)] -pub struct WasmProposalBuilder(pub(crate) ProposalBuilder); - -#[wasm_bindgen(js_class = ProposalBuilder)] -impl WasmProposalBuilder { - // #[wasm_bindgen(constructor)] - // pub fn new(identity: WasmOnChainIdentity, action: WasmProposalAction) -> Self { - // Self(ProposalBuilder::new(identity.0, action.into())) - // } - - #[wasm_bindgen(js_name = expirationEpoch)] - pub fn expiration_epoch(self, exp: u64) -> Self { - Self(self.0.expiration_epoch(exp)) - } - - pub fn key(self, key: String) -> Self { - Self(self.0.key(key)) - } - - #[wasm_bindgen(js_name = gasBudget)] - pub fn gas_budget(self, amount: u64) -> Self { - Self(self.0.gas_budget(amount)) - } - - pub async fn finish(self, client: &WasmIdentityClient, signer: &DummySigner) -> Result> { - unimplemented!("WasmProposalBuilder::finish"); - // self - // .0 - // .finish(&client.0, signer) - // .await - // .map(|option| option.map(WasmProposal)) - // .map_err(wasm_error) - } -} - #[wasm_bindgen(js_name = IdentityBuilder)] pub struct WasmIdentityBuilder(pub(crate) IdentityBuilder); @@ -242,7 +235,7 @@ pub struct WasmTransactionOutputInternalOnChainIdentity(pub(crate) TransactionOu impl WasmTransactionOutputInternalOnChainIdentity { #[wasm_bindgen(getter)] pub fn output(&self) -> WasmOnChainIdentity { - WasmOnChainIdentity(self.0.output.clone()) + WasmOnChainIdentity(Rc::new(RwLock::new(self.0.output.clone()))) } #[wasm_bindgen(getter)] diff --git a/bindings/wasm/identity_wasm/src/rebased/mod.rs b/bindings/wasm/identity_wasm/src/rebased/mod.rs index f54b699e42..ffa168c0e3 100644 --- a/bindings/wasm/identity_wasm/src/rebased/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/mod.rs @@ -7,6 +7,7 @@ mod multicontroller; mod types; mod wasm_identity_client; mod wasm_identity_client_read_only; +mod proposals; pub use identity::*; pub use multicontroller::*; diff --git a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs index a74a290103..4f751b45a3 100644 --- a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs +++ b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs @@ -2,18 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::core::Object; +use identity_iota::iota::rebased::migration::Proposal; +use identity_iota::iota::rebased::proposals::{DeactivateDid, UpdateDidDocument}; use super::client_dummy::Multicontroller; -use super::client_dummy::Proposal; use wasm_bindgen::prelude::*; use crate::common::MapStringAny; use super::types::WasmObjectID; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[wasm_bindgen(js_name = Proposal)] -pub struct WasmProposal(pub(crate) Proposal); #[wasm_bindgen(js_name = Multicontroller)] pub struct WasmMulticontroller(pub(crate) Multicontroller>); diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs new file mode 100644 index 0000000000..f60462273c --- /dev/null +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs @@ -0,0 +1,330 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::collections::HashSet; +use std::rc::Rc; + +use identity_iota::iota::rebased::migration::Proposal; +use identity_iota::iota::rebased::proposals::ConfigChange; +use identity_iota::iota::rebased::proposals::ProposalResult; +use identity_iota::iota::rebased::proposals::ProposalT; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; +use iota_interaction_ts::AdapterNativeResponse; +use tokio::sync::RwLock; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::prelude::JsCast; +use wasm_bindgen::JsValue; + +use super::MapStringNumber; +use super::StringSet; +use crate::error::Result; +use crate::error::WasmResult; +use crate::rebased::WasmIdentityClient; +use crate::rebased::WasmOnChainIdentity; + +#[wasm_bindgen(js_name = ConfigChange, inspectable, getter_with_clone)] +pub struct WasmConfigChange { + pub threshold: Option, + #[wasm_bindgen(js_name = controllersToAdd)] + pub controllers_to_add: Option, + #[wasm_bindgen(js_name = controllersToRemove)] + pub controllers_to_remove: Option, + #[wasm_bindgen(js_name = controllersToUpdate)] + pub controllers_to_update: Option, +} + +impl TryFrom for WasmConfigChange { + type Error = JsValue; + fn try_from(value: ConfigChange) -> std::result::Result { + let threshold = value.threshold(); + let controllers_to_add = if value.controllers_to_add().is_empty() { + None + } else { + Some(value.controllers_to_add().try_into()?) + }; + let controllers_to_remove = if value.controllers_to_remove().is_empty() { + None + } else { + Some(value.controllers_to_remove().try_into()?) + }; + let controllers_to_update = if value.controllers_to_update().is_empty() { + None + } else { + Some(value.controllers_to_update().try_into()?) + }; + + Ok(Self { + threshold, + controllers_to_add, + controllers_to_remove, + controllers_to_update, + }) + } +} + +#[wasm_bindgen(js_name = ConfigChangeProposal)] +#[derive(Clone)] +pub struct WasmConfigChangeProposal(pub(crate) Rc>>); + +#[wasm_bindgen(js_class = ConfigChangeProposal)] +impl WasmConfigChangeProposal { + fn new(proposal: Proposal) -> Self { + Self(Rc::new(RwLock::new(proposal))) + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> Result { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.id().to_string()) + } + + #[wasm_bindgen(getter)] + pub fn action(&self) -> Result { + self + .0 + .try_read() + .wasm_result() + .and_then(|proposal| proposal.action().clone().try_into()) + } + + #[wasm_bindgen(getter)] + pub fn expiration_epoch(&self) -> Result> { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.expiration_epoch()) + } + + #[wasm_bindgen(getter)] + pub fn votes(&self) -> Result { + self.0.try_read().wasm_result().map(|proposal| proposal.votes()) + } + + #[wasm_bindgen(getter)] + pub fn voters(&self) -> Result { + let js_set = self + .0 + .try_read() + .wasm_result()? + .voters() + .iter() + .map(ToString::to_string) + .map(js_sys::JsString::from) + .fold(js_sys::Set::default(), |set, value| { + set.add(&value); + set + }) + .unchecked_into(); + + Ok(js_set) + } + + #[wasm_bindgen] + pub fn approve(&self, identity: &WasmOnChainIdentity) -> WasmApproveConfigChangeProposalTx { + WasmApproveConfigChangeProposalTx::new(self, identity) + } + + #[wasm_bindgen(js_name = intoTx)] + pub fn into_tx(self, identity: &WasmOnChainIdentity) -> WasmExecuteConfigChangeProposalTx { + WasmExecuteConfigChangeProposalTx::new(self, identity) + } +} + +#[wasm_bindgen(js_name = ApproveConfigChangeProposalTx)] +pub struct WasmApproveConfigChangeProposalTx { + proposal: WasmConfigChangeProposal, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ApproveConfigChangeProposalTx)] +impl WasmApproveConfigChangeProposalTx { + fn new(proposal: &WasmConfigChangeProposal, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal: proposal.clone(), + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let identity_ref = self.identity.0.read().await; + self + .proposal + .0 + .write() + .await + .approve(&identity_ref) + .execute_with_opt_gas_internal(self.gas_budget, &client.0) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = ExecuteConfigChangeProposalTx)] +pub struct WasmExecuteConfigChangeProposalTx { + proposal: WasmConfigChangeProposal, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ExecuteConfigChangeProposalTx)] +impl WasmExecuteConfigChangeProposalTx { + fn new(proposal: WasmConfigChangeProposal, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal, + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let proposal = Rc::into_inner(self.proposal.0) + .ok_or_else(|| js_sys::Error::new("cannot consume proposal; try to drop all other references to it"))? + .into_inner(); + + proposal + .into_tx(&mut identity_ref, client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, client) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = CreateConfigChangeProposalTxOutput, inspectable, getter_with_clone)] +pub struct WasmCreateConfigChangeProposalTxOutput { + pub output: Option, + pub response: AdapterNativeResponse, +} + +impl From>>> + for WasmCreateConfigChangeProposalTxOutput +{ + fn from(tx_output: TransactionOutputInternal>>) -> Self { + let output = match tx_output.output { + ProposalResult::Pending(proposal) => Some(WasmConfigChangeProposal::new(proposal)), + ProposalResult::Executed(_) => None, + }; + let response = tx_output.response.clone_native_response(); + Self { output, response } + } +} + +#[wasm_bindgen(js_name = CreateConfigChangeProposalTx)] +pub struct WasmCreateConfigChangeProposalTx { + identity: WasmOnChainIdentity, + threshold: Option, + controllers_to_add: Option, + controllers_to_remove: Option, + controllers_to_update: Option, + expiration_epoch: Option, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = CreateConfigChangeProposalTx)] +impl WasmCreateConfigChangeProposalTx { + pub(crate) fn new(identity: &WasmOnChainIdentity, config: WasmConfigChange, expiration_epoch: Option) -> Self { + let WasmConfigChange { + controllers_to_add, + controllers_to_remove, + controllers_to_update, + threshold, + } = config; + Self { + identity: identity.clone(), + threshold, + controllers_to_add, + controllers_to_remove, + controllers_to_update, + expiration_epoch, + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let controllers_to_add = self + .controllers_to_add + .map(HashMap::try_from) + .transpose()? + .unwrap_or_default(); + let controllers_to_remove = self + .controllers_to_remove + .map(HashSet::try_from) + .transpose()? + .unwrap_or_default(); + let controllers_to_update = self + .controllers_to_update + .map(HashMap::try_from) + .transpose()? + .unwrap_or_default(); + let builder = identity_ref + .update_config() + .add_multiple_controllers(controllers_to_add) + .remove_multiple_controllers(controllers_to_remove) + .update_multiple_controllers(controllers_to_update); + // identity_ref.deactivate_did(); + let builder = if let Some(exp) = self.expiration_epoch { + builder.expiration_epoch(exp) + } else { + builder + }; + + let tx_output = builder + .finish(&client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, &client) + .await + .wasm_result()?; + + Ok(tx_output.into()) + } +} diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/deactivate_did.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/deactivate_did.rs new file mode 100644 index 0000000000..905650ffa7 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/deactivate_did.rs @@ -0,0 +1,252 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::rc::Rc; + +use identity_iota::iota::rebased::migration::Proposal; +use identity_iota::iota::rebased::proposals::DeactivateDid; +use identity_iota::iota::rebased::proposals::ProposalResult; +use identity_iota::iota::rebased::proposals::ProposalT; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; +use iota_interaction_ts::AdapterNativeResponse; +use tokio::sync::RwLock; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::prelude::JsCast; + +use super::StringSet; +use crate::error::Result; +use crate::error::WasmResult; +use crate::rebased::WasmIdentityClient; +use crate::rebased::WasmOnChainIdentity; + +#[wasm_bindgen(js_name = DeactivateDid)] +#[derive(Clone, Copy, Default)] +pub struct WasmDeactivateDid; + +#[wasm_bindgen(js_name = DeactivateDidProposal)] +#[derive(Clone)] +pub struct WasmProposalDeactivateDid(pub(crate) Rc>>); + +#[wasm_bindgen(js_class = DeactivateDidProposal)] +impl WasmProposalDeactivateDid { + fn new(proposal: Proposal) -> Self { + Self(Rc::new(RwLock::new(proposal))) + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> Result { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.id().to_string()) + } + + #[wasm_bindgen(getter)] + pub fn action(&self) -> WasmDeactivateDid { + WasmDeactivateDid + } + + #[wasm_bindgen(getter)] + pub fn expiration_epoch(&self) -> Result> { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.expiration_epoch()) + } + + #[wasm_bindgen(getter)] + pub fn votes(&self) -> Result { + self.0.try_read().wasm_result().map(|proposal| proposal.votes()) + } + + #[wasm_bindgen(getter)] + pub fn voters(&self) -> Result { + let js_set = self + .0 + .try_read() + .wasm_result()? + .voters() + .iter() + .map(ToString::to_string) + .map(js_sys::JsString::from) + .fold(js_sys::Set::default(), |set, value| { + set.add(&value); + set + }) + .unchecked_into(); + + Ok(js_set) + } + + #[wasm_bindgen] + pub fn approve(&self, identity: &WasmOnChainIdentity) -> WasmApproveDeativateDidProposalTx { + WasmApproveDeativateDidProposalTx::new(self, identity) + } + + #[wasm_bindgen(js_name = intoTx)] + pub fn into_tx(self, identity: &WasmOnChainIdentity) -> WasmExecuteDeactivateDidProposalTx { + WasmExecuteDeactivateDidProposalTx::new(self, identity) + } +} + +#[wasm_bindgen(js_name = ApproveDeativateDidProposalTx)] +pub struct WasmApproveDeativateDidProposalTx { + proposal: WasmProposalDeactivateDid, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ApproveDeativateDidProposalTx)] +impl WasmApproveDeativateDidProposalTx { + fn new(proposal: &WasmProposalDeactivateDid, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal: proposal.clone(), + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let identity_ref = self.identity.0.read().await; + self + .proposal + .0 + .write() + .await + .approve(&identity_ref) + .execute_with_opt_gas_internal(self.gas_budget, &client.0) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = ExecuteDeactivateDidProposalTx)] +pub struct WasmExecuteDeactivateDidProposalTx { + proposal: WasmProposalDeactivateDid, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ExecuteDeactivateDidProposalTx)] +impl WasmExecuteDeactivateDidProposalTx { + fn new(proposal: WasmProposalDeactivateDid, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal, + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let proposal = Rc::into_inner(self.proposal.0) + .ok_or_else(|| js_sys::Error::new("cannot consume proposal; try to drop all other references to it"))? + .into_inner(); + + proposal + .into_tx(&mut identity_ref, client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, client) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = CreateDeactivateDidProposalTxOutput, inspectable, getter_with_clone)] +pub struct WasmCreateDeactivateDidProposalTxOutput { + pub output: Option, + pub response: AdapterNativeResponse, +} + +impl From>>> + for WasmCreateDeactivateDidProposalTxOutput +{ + fn from(tx_output: TransactionOutputInternal>>) -> Self { + let output = match tx_output.output { + ProposalResult::Pending(proposal) => Some(WasmProposalDeactivateDid::new(proposal)), + ProposalResult::Executed(_) => None, + }; + let response = tx_output.response.clone_native_response(); + Self { output, response } + } +} + +#[wasm_bindgen(js_name = CreateDeactivateDidProposalTx)] +pub struct WasmCreateDeactivateDidProposalTx { + identity: WasmOnChainIdentity, + expiration_epoch: Option, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = CreateDeactivateDidProposalTx)] +impl WasmCreateDeactivateDidProposalTx { + pub(crate) fn new(identity: &WasmOnChainIdentity, expiration_epoch: Option) -> Self { + Self { + identity: identity.clone(), + expiration_epoch, + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let builder = identity_ref.deactivate_did(); + let builder = if let Some(exp) = self.expiration_epoch { + builder.expiration_epoch(exp) + } else { + builder + }; + + let tx_output = builder + .finish(&client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, &client) + .await + .wasm_result()?; + + Ok(tx_output.into()) + } +} diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs new file mode 100644 index 0000000000..86d8638511 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs @@ -0,0 +1,103 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod deactivate_did; +mod update_did; +mod send; +mod config_change; + +pub use deactivate_did::*; +pub use update_did::*; +pub use send::*; +pub use config_change::*; + +use std::collections::HashMap; +use std::collections::HashSet; + +use identity_iota::iota_interaction::types::base_types::ObjectID; +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use wasm_bindgen::JsCast as _; +use js_sys::Reflect; +use js_sys::JsString; + +#[wasm_bindgen] +extern "C" { + #[derive(Clone)] + #[wasm_bindgen(typescript_type = "Set")] + pub type StringSet; + + #[wasm_bindgen(typescript_type = "[string, string]")] + pub type StringCouple; + + #[derive(Clone)] + #[wasm_bindgen(typescript_type = "Map")] + pub type MapStringNumber; +} + +impl From for (String, String) { + fn from(value: StringCouple) -> Self { + let first = Reflect::get_u32(&value, 0) + .expect("[string, string] has property 0") + .unchecked_into::() + .into(); + let second = Reflect::get_u32(&value, 1) + .expect("[string, string] has property 1") + .unchecked_into::() + .into(); + + (first, second) + } +} + +impl From<(String, String)> for StringCouple { + fn from(value: (String, String)) -> Self { + serde_wasm_bindgen::to_value(&value).expect("a string couple can be serialized to JS").unchecked_into() + } +} + +impl TryFrom for HashMap { + type Error = JsValue; + fn try_from(value: MapStringNumber) -> Result { + Ok(serde_wasm_bindgen::from_value(value.into())?) + } +} + +impl TryFrom<&'_ HashMap> for MapStringNumber { + type Error = JsValue; + fn try_from(value: &'_ HashMap) -> Result { + let js_value = serde_wasm_bindgen::to_value(value)?; + Ok(js_value.dyn_into()?) + } +} + +impl TryFrom for HashMap { + type Error = JsValue; + fn try_from(value: MapStringNumber) -> Result { + Ok(serde_wasm_bindgen::from_value(value.into())?) + } +} + +impl TryFrom<&'_ HashMap> for MapStringNumber { + type Error = JsValue; + fn try_from(value: &'_ HashMap) -> Result { + let js_value = serde_wasm_bindgen::to_value(value)?; + Ok(js_value.dyn_into()?) + } +} + +impl TryFrom for HashSet { + type Error = JsValue; + fn try_from(value: StringSet) -> Result { + Ok(serde_wasm_bindgen::from_value(value.into())?) + } +} + +impl TryFrom<&'_ HashSet> for StringSet { + type Error = JsValue; + fn try_from(value: &'_ HashSet) -> Result { + let js_value = serde_wasm_bindgen::to_value(value)?; + Ok(js_value.dyn_into()?) + } +} \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/send.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/send.rs new file mode 100644 index 0000000000..4afcac9209 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/send.rs @@ -0,0 +1,293 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::rc::Rc; + +use identity_iota::iota::rebased::migration::Proposal; +use identity_iota::iota::rebased::proposals::ProposalResult; +use identity_iota::iota::rebased::proposals::ProposalT; +use identity_iota::iota::rebased::proposals::SendAction; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; +use iota_interaction_ts::AdapterNativeResponse; +use tokio::sync::RwLock; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::prelude::JsCast; + +use super::StringCouple; +use super::StringSet; +use crate::error::Result; +use crate::error::WasmResult; +use crate::rebased::WasmIdentityClient; +use crate::rebased::WasmOnChainIdentity; + +#[wasm_bindgen(js_name = SendAction)] +#[derive(Clone)] +pub struct WasmSendAction(pub(crate) SendAction); + +#[wasm_bindgen(js_class = SendAction)] +impl WasmSendAction { + #[wasm_bindgen(getter, js_name = objectRecipientMap)] + pub fn object_recipient_map(&self) -> Vec { + self + .0 + .as_ref() + .iter() + .map(|(obj, rec)| (obj.to_string(), rec.to_string()).into()) + .collect() + } +} + +#[wasm_bindgen(js_name = SendProposal)] +#[derive(Clone)] +pub struct WasmProposalSend(pub(crate) Rc>>); + +#[wasm_bindgen(js_class = SendProposal)] +impl WasmProposalSend { + fn new(proposal: Proposal) -> Self { + Self(Rc::new(RwLock::new(proposal))) + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> Result { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.id().to_string()) + } + + #[wasm_bindgen(getter)] + pub fn action(&self) -> Result { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| WasmSendAction(proposal.action().clone())) + } + + #[wasm_bindgen(getter)] + pub fn expiration_epoch(&self) -> Result> { + self + .0 + .try_read() + .wasm_result() + .map(|proposal| proposal.expiration_epoch()) + } + + #[wasm_bindgen(getter)] + pub fn votes(&self) -> Result { + self.0.try_read().wasm_result().map(|proposal| proposal.votes()) + } + + #[wasm_bindgen(getter)] + pub fn voters(&self) -> Result { + let js_set = self + .0 + .try_read() + .wasm_result()? + .voters() + .iter() + .map(ToString::to_string) + .map(js_sys::JsString::from) + .fold(js_sys::Set::default(), |set, value| { + set.add(&value); + set + }) + .unchecked_into(); + + Ok(js_set) + } + + #[wasm_bindgen] + pub fn approve(&self, identity: &WasmOnChainIdentity) -> WasmApproveSendProposalTx { + WasmApproveSendProposalTx::new(self, identity) + } + + #[wasm_bindgen(js_name = intoTx)] + pub fn into_tx(self, identity: &WasmOnChainIdentity) -> WasmExecuteSendProposalTx { + WasmExecuteSendProposalTx::new(self, identity) + } +} + +#[wasm_bindgen(js_name = ApproveSendProposalTx)] +pub struct WasmApproveSendProposalTx { + proposal: WasmProposalSend, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ApproveSendProposalTx)] +impl WasmApproveSendProposalTx { + fn new(proposal: &WasmProposalSend, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal: proposal.clone(), + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let identity_ref = self.identity.0.read().await; + self + .proposal + .0 + .write() + .await + .approve(&identity_ref) + .execute_with_opt_gas_internal(self.gas_budget, &client.0) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = ExecuteSendProposalTx)] +pub struct WasmExecuteSendProposalTx { + proposal: WasmProposalSend, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ExecuteSendProposalTx)] +impl WasmExecuteSendProposalTx { + fn new(proposal: WasmProposalSend, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal, + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let proposal = Rc::into_inner(self.proposal.0) + .ok_or_else(|| js_sys::Error::new("cannot consume proposal; try to drop all other references to it"))? + .into_inner(); + + proposal + .into_tx(&mut identity_ref, client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, client) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = CreateSendProposalTxOutput, inspectable, getter_with_clone)] +pub struct WasmCreateSendProposalTxOutput { + pub output: Option, + pub response: AdapterNativeResponse, +} + +impl From>>> for WasmCreateSendProposalTxOutput { + fn from(tx_output: TransactionOutputInternal>>) -> Self { + let output = match tx_output.output { + ProposalResult::Pending(proposal) => Some(WasmProposalSend::new(proposal)), + ProposalResult::Executed(_) => None, + }; + let response = tx_output.response.clone_native_response(); + Self { output, response } + } +} + +#[wasm_bindgen(js_name = CreateSendProposalTx)] +pub struct WasmCreateSendProposalTx { + identity: WasmOnChainIdentity, + object_recipient_map: Vec, + expiration_epoch: Option, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = CreateSendProposalTx)] +impl WasmCreateSendProposalTx { + pub(crate) fn new( + identity: &WasmOnChainIdentity, + object_recipient_map: Vec, + expiration_epoch: Option, + ) -> Self { + Self { + identity: identity.clone(), + object_recipient_map, + expiration_epoch, + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let builder = self + .object_recipient_map + .into_iter() + .map(Into::into) + .map(|(obj_id_str, address_str)| { + let obj_id = obj_id_str + .parse() + .map_err(|_| js_sys::TypeError::new("invalid object ID"))?; + let address = address_str + .parse() + .map_err(|_| js_sys::TypeError::new("invalid IOTA address"))?; + + Result::Ok((obj_id, address)) + }) + .try_fold(identity_ref.send_assets(), |builder, maybe_obj_address| { + let (obj_id, address) = maybe_obj_address?; + Result::Ok(builder.object(obj_id, address)) + })?; + + // identity_ref.deactivate_did(); + let builder = if let Some(exp) = self.expiration_epoch { + builder.expiration_epoch(exp) + } else { + builder + }; + + let tx_output = builder + .finish(&client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, &client) + .await + .wasm_result()?; + + Ok(tx_output.into()) + } +} diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/update_did.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/update_did.rs new file mode 100644 index 0000000000..bed2d4ed9d --- /dev/null +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/update_did.rs @@ -0,0 +1,289 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::rc::Rc; + +use identity_iota::iota::rebased::migration::Proposal; +use identity_iota::iota::rebased::proposals::ProposalResult; +use identity_iota::iota::rebased::proposals::ProposalT; +use identity_iota::iota::rebased::proposals::UpdateDidDocument; +use identity_iota::iota::rebased::transaction::TransactionInternal; +use identity_iota::iota::rebased::transaction::TransactionOutputInternal; +use identity_iota::iota::StateMetadataDocument; +use iota_interaction_ts::AdapterNativeResponse; +use tokio::sync::RwLock; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::prelude::JsCast; + +use super::StringSet; +use crate::error::Result; +use crate::error::WasmResult; +use crate::iota::WasmIotaDocument; +use crate::rebased::WasmIdentityClient; +use crate::rebased::WasmOnChainIdentity; + +#[wasm_bindgen(js_name = UpdateDid)] +pub struct WasmUpdateDid(pub(crate) WasmIotaDocument); + +#[wasm_bindgen(js_class = UpdateDid)] +impl WasmUpdateDid { + #[wasm_bindgen(getter, js_name = didDocument)] + pub fn did_document(&self) -> Result { + self.0.deep_clone() + } +} + +impl Clone for WasmUpdateDid { + fn clone(&self) -> Self { + Self(self.0.shallow_clone()) + } +} + +#[wasm_bindgen(js_name = UpdateDidProposal)] +#[derive(Clone)] +pub struct WasmProposalUpdateDid { + inner_proposal: Rc>>, + action: WasmUpdateDid, +} + +#[wasm_bindgen(js_class = UpdatedDidProposal)] +impl WasmProposalUpdateDid { + fn new(proposal: Proposal) -> Self { + let updated_iota_document = StateMetadataDocument::unpack(proposal.action().did_document_bytes()) + .expect("valid encoded IOTA DID document") + .into_iota_document_with_placeholders(); + let action = WasmUpdateDid(updated_iota_document.into()); + + Self { + inner_proposal: Rc::new(RwLock::new(proposal)), + action, + } + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> Result { + self + .inner_proposal + .try_read() + .wasm_result() + .map(|proposal| proposal.id().to_string()) + } + + #[wasm_bindgen(getter)] + pub fn action(&self) -> WasmUpdateDid { + self.action.clone() + } + + #[wasm_bindgen(getter)] + pub fn expiration_epoch(&self) -> Result> { + self + .inner_proposal + .try_read() + .wasm_result() + .map(|proposal| proposal.expiration_epoch()) + } + + #[wasm_bindgen(getter)] + pub fn votes(&self) -> Result { + self + .inner_proposal + .try_read() + .wasm_result() + .map(|proposal| proposal.votes()) + } + + #[wasm_bindgen(getter)] + pub fn voters(&self) -> Result { + let js_set = self + .inner_proposal + .try_read() + .wasm_result()? + .voters() + .iter() + .map(ToString::to_string) + .map(js_sys::JsString::from) + .fold(js_sys::Set::default(), |set, value| { + set.add(&value); + set + }) + .unchecked_into(); + + Ok(js_set) + } + + #[wasm_bindgen] + pub fn approve(&self, identity: &WasmOnChainIdentity) -> WasmApproveUpdateDidDocumentProposalTx { + WasmApproveUpdateDidDocumentProposalTx::new(self, identity) + } + + #[wasm_bindgen(js_name = intoTx)] + pub fn into_tx(self, identity: &WasmOnChainIdentity) -> WasmExecuteUpdateDidDocumentProposalTx { + WasmExecuteUpdateDidDocumentProposalTx::new(self, identity) + } +} + +#[wasm_bindgen(js_name = ApproveUpdateDidDocumentProposalTx)] +pub struct WasmApproveUpdateDidDocumentProposalTx { + proposal: WasmProposalUpdateDid, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ApproveUpdateDidDocumentProposalTx)] +impl WasmApproveUpdateDidDocumentProposalTx { + fn new(proposal: &WasmProposalUpdateDid, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal: proposal.clone(), + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let identity_ref = self.identity.0.read().await; + self + .proposal + .inner_proposal + .write() + .await + .approve(&identity_ref) + .execute_with_opt_gas_internal(self.gas_budget, &client.0) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = ExecuteUpdateDidProposalTx)] +pub struct WasmExecuteUpdateDidDocumentProposalTx { + proposal: WasmProposalUpdateDid, + identity: WasmOnChainIdentity, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = ExecuteUpdateDidProposalTx)] +impl WasmExecuteUpdateDidDocumentProposalTx { + fn new(proposal: WasmProposalUpdateDid, identity: &WasmOnChainIdentity) -> Self { + Self { + proposal, + identity: identity.clone(), + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let mut identity_ref = self.identity.0.write().await; + let proposal = Rc::into_inner(self.proposal.inner_proposal) + .ok_or_else(|| js_sys::Error::new("cannot consume proposal; try to drop all other references to it"))? + .into_inner(); + + proposal + .into_tx(&mut identity_ref, client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, client) + .await + .wasm_result() + .map(|tx_output| tx_output.response.clone_native_response()) + } +} + +#[wasm_bindgen(js_name = CreateUpdateDidProposalTxOutput, inspectable, getter_with_clone)] +pub struct WasmCreateUpdateDidProposalTxOutput { + pub output: Option, + pub response: AdapterNativeResponse, +} + +impl From>>> + for WasmCreateUpdateDidProposalTxOutput +{ + fn from(tx_output: TransactionOutputInternal>>) -> Self { + let output = match tx_output.output { + ProposalResult::Pending(proposal) => Some(WasmProposalUpdateDid::new(proposal)), + ProposalResult::Executed(_) => None, + }; + let response = tx_output.response.clone_native_response(); + Self { output, response } + } +} + +#[wasm_bindgen(js_name = CreateUpdateDidProposalTx)] +pub struct WasmCreateUpdateDidProposalTx { + identity: WasmOnChainIdentity, + updated_did_doc: WasmIotaDocument, + expiration_epoch: Option, + gas_budget: Option, +} + +#[wasm_bindgen(js_class = CreateUpdateDidProposalTx)] +impl WasmCreateUpdateDidProposalTx { + pub(crate) fn new( + identity: &WasmOnChainIdentity, + updated_did_doc: WasmIotaDocument, + expiration_epoch: Option, + ) -> Self { + Self { + identity: identity.clone(), + updated_did_doc, + expiration_epoch, + gas_budget: None, + } + } + + #[wasm_bindgen(js_name = withGasBudget)] + pub fn with_gas_budget(mut self, budget: u64) -> Self { + self.gas_budget = Some(budget); + self + } + + #[wasm_bindgen(setter, js_name = gasBudget)] + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + #[wasm_bindgen] + pub async fn execute(self, client: &WasmIdentityClient) -> Result { + let updated_did_doc = self.updated_did_doc.0.read().await.clone(); + let mut identity_ref = self.identity.0.write().await; + let builder = identity_ref.update_did_document(updated_did_doc); + let builder = if let Some(exp) = self.expiration_epoch { + builder.expiration_epoch(exp) + } else { + builder + }; + + let tx_output = builder + .finish(&client) + .await + .wasm_result()? + .execute_with_opt_gas_internal(self.gas_budget, &client) + .await + .wasm_result()?; + + Ok(tx_output.into()) + } +} diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index 9699d0b971..082702bb90 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::rc::Rc; +use std::ops::Deref; use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; @@ -47,6 +48,13 @@ pub struct WasmIotaTransactionBlockResponseEssence { #[wasm_bindgen(js_name = IdentityClient)] pub struct WasmIdentityClient(pub(crate) IdentityClient); +impl Deref for WasmIdentityClient { + type Target = IdentityClient; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[wasm_bindgen(js_class = IdentityClient)] impl WasmIdentityClient { #[wasm_bindgen(js_name = create)] diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs index 76b6ff07aa..6f7760ae70 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs @@ -25,7 +25,7 @@ impl IdentityContainer { #[wasm_bindgen(js_name = toFullFledged)] pub fn to_full_fledged(&self) -> Option { match self.0.clone() { - Identity::FullFledged(v) => Some(WasmOnChainIdentity(v)), + Identity::FullFledged(v) => Some(WasmOnChainIdentity::new(v)), _ => None, } } diff --git a/identity_iota_core/scripts/publish_identity_package.sh b/identity_iota_core/scripts/publish_identity_package.sh index 620d39289f..c3b7fb1941 100755 --- a/identity_iota_core/scripts/publish_identity_package.sh +++ b/identity_iota_core/scripts/publish_identity_package.sh @@ -9,4 +9,4 @@ package_dir=$script_dir/../packages/iota_identity # echo "publishing package from $package_dir" package_id=$(iota client publish --with-unpublished-dependencies --skip-dependency-verification --silence-warnings --json --gas-budget 500000000 $package_dir | jq --raw-output '.objectChanges[] | select(.type | contains("published")) | .packageId') export IOTA_IDENTITY_PKG_ID=$package_id -echo "${IOTA_IDENTITY_PKG_ID}" \ No newline at end of file +echo "${IOTA_IDENTITY_PKG_ID}" diff --git a/identity_iota_core/src/rebased/migration/multicontroller.rs b/identity_iota_core/src/rebased/migration/multicontroller.rs index 958c9bd75d..5002fb67d8 100644 --- a/identity_iota_core/src/rebased/migration/multicontroller.rs +++ b/identity_iota_core/src/rebased/migration/multicontroller.rs @@ -53,6 +53,16 @@ impl Proposal { pub fn into_action(self) -> T { self.action } + + /// Returns the set of voters' IDs. + pub fn voters(&self) -> &HashSet { + &self.voters + } + + /// Returns the epoch ID for this proposal's expiration. + pub fn expiration_epoch(&self) -> Option { + self.expiration_epoch + } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/identity_iota_core/src/rebased/proposals/config_change.rs b/identity_iota_core/src/rebased/proposals/config_change.rs index 57f76ab5f9..5e322f8673 100644 --- a/identity_iota_core/src/rebased/proposals/config_change.rs +++ b/identity_iota_core/src/rebased/proposals/config_change.rs @@ -94,6 +94,25 @@ impl ProposalBuilder<'_, ConfigChange> { self.deref_mut().remove_multiple_controllers(controllers); self } + + /// Sets a new voting power for a controller. + pub fn update_controller(mut self, controller_id: ObjectID, voting_power: u64) -> Self { + self.action.controllers_voting_power.insert(controller_id, voting_power); + self + } + + /// Updates many controllers' voting power. + pub fn update_multiple_controllers(mut self, controllers: I) -> Self + where + I: IntoIterator, + { + let controllers_to_update = &mut self.action.controllers_voting_power; + for (id, vp) in controllers { + controllers_to_update.insert(id, vp); + } + + self + } } impl ConfigChange { @@ -107,6 +126,26 @@ impl ConfigChange { self.threshold = Some(new_threshold); } + /// Returns the value for the new threshold. + pub fn threshold(&self) -> Option { + self.threshold + } + + /// Returns the controllers that will be added, as the map [IotaAddress] -> [u64]. + pub fn controllers_to_add(&self) -> &HashMap { + &self.controllers_to_add + } + + /// Returns the set of controllers that will be removed. + pub fn controllers_to_remove(&self) -> &HashSet { + &self.controllers_to_remove + } + + /// Returns the controllers that will be updated as the map [IotaAddress] -> [u64]. + pub fn controllers_to_update(&self) -> &HashMap { + &self.controllers_voting_power + } + /// Adds a controller. pub fn add_controller(&mut self, address: IotaAddress, voting_power: u64) { self.controllers_to_add.insert(address, voting_power); diff --git a/identity_iota_core/src/rebased/proposals/send.rs b/identity_iota_core/src/rebased/proposals/send.rs index cdebc4d9fa..58499c8f74 100644 --- a/identity_iota_core/src/rebased/proposals/send.rs +++ b/identity_iota_core/src/rebased/proposals/send.rs @@ -61,6 +61,12 @@ impl SendAction { } } +impl AsRef<[(ObjectID, IotaAddress)]> for SendAction { + fn as_ref(&self) -> &[(ObjectID, IotaAddress)] { + &self.0 + } +} + impl ProposalBuilder<'_, SendAction> { /// Adds one object to the list of objects to send. pub fn object(mut self, object_id: ObjectID, recipient: IotaAddress) -> Self { diff --git a/identity_iota_core/src/rebased/proposals/update_did_doc.rs b/identity_iota_core/src/rebased/proposals/update_did_doc.rs index 9da6da0000..44332f20ba 100644 --- a/identity_iota_core/src/rebased/proposals/update_did_doc.rs +++ b/identity_iota_core/src/rebased/proposals/update_did_doc.rs @@ -48,6 +48,11 @@ impl UpdateDidDocument { pub fn new(document: IotaDocument) -> Self { Self(document.pack().expect("a valid IotaDocument is packable")) } + + /// Returns the serialized DID document bytes. + pub fn did_document_bytes(&self) -> &[u8] { + &self.0 + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 5919e8a2d0..511528038f 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -72,6 +72,11 @@ impl StateMetadataDocument { Ok(IotaDocument { document, metadata }) } + /// Returns the corresponding [`IotaDocument`] with DID replaced by DID placeholder `did:0:0`. + pub fn into_iota_document_with_placeholders(self) -> IotaDocument { + IotaDocument { document: self.document, metadata: self.metadata } + } + /// Pack a [`StateMetadataDocument`] into bytes, suitable for storing in an identity, /// according to the given `encoding`. pub fn pack(mut self, encoding: StateMetadataEncoding) -> Result> { From 86468edf5089161d6545c26d7677c3240f47032e Mon Sep 17 00:00:00 2001 From: wulfraem Date: Thu, 20 Feb 2025 18:46:13 +0100 Subject: [PATCH 38/63] Add waiting for transaction block availability to TS variant of `execute_transaction` (#1539) * add `wait_for_transaction` to ts impl for `execute_transaction` * add missing impl for new digest function to rust trait impl --- .../lib/iota_client_helpers.ts | 4 ++ .../src/bindings/wasm_iota_client.rs | 52 +++++++++++++++++++ .../src/bindings/wasm_types.rs | 15 ++++++ .../src/iota_client_ts_sdk.rs | 22 +++++--- .../iota_client_rust_sdk.rs | 4 ++ .../src/iota_client_trait.rs | 3 ++ .../src/sdk_types/generated_types.rs | 35 +++++++++++++ 7 files changed, 128 insertions(+), 7 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index a53f75ef9b..8f1473edee 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -47,6 +47,10 @@ export class IotaTransactionBlockResponseAdapter { get_response(): IotaTransactionBlockResponse { return this.response; } + + get_digest(): string { + return this.response.digest; + } } /** diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs index f94ee3ff6d..41c37b26a7 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -12,6 +12,7 @@ use identity_iota_interaction::generated_types::GetTransactionBlockParams; use identity_iota_interaction::generated_types::QueryEventsParams; use identity_iota_interaction::generated_types::SortOrder; use identity_iota_interaction::generated_types::TryGetPastObjectParams; +use identity_iota_interaction::generated_types::WaitForTransactionParams; use identity_iota_interaction::rpc_types::CoinPage; use identity_iota_interaction::rpc_types::EventFilter; use identity_iota_interaction::rpc_types::EventPage; @@ -31,12 +32,14 @@ use identity_iota_interaction::types::quorum_driver_types::ExecuteTransactionReq use identity_iota_interaction::SignatureBcs; use identity_iota_interaction::TransactionDataBcs; use js_sys::Promise; +use serde::Serialize; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use super::wasm_types::IotaTransactionBlockResponseAdapter; use super::wasm_types::PromiseIotaTransactionBlockResponse; use super::wasm_types::WasmExecuteTransactionBlockParams; +use super::WasmWaitForTransactionParams; use crate::bindings::PromiseIotaObjectResponse; use crate::bindings::PromiseObjectRead; @@ -55,6 +58,7 @@ use crate::common::PromiseBigint; use crate::console_log; use crate::error::into_ts_sdk_result; use crate::error::TsSdkError; +use crate::error::WasmResult; // This file contains the wasm-bindgen 'glue code' providing // the interface of the TS Iota client to rust code. @@ -114,6 +118,12 @@ extern "C" { #[wasm_bindgen(method, js_name = getCoins)] pub fn get_coins(this: &WasmIotaClient, input: &WasmGetCoinsParams) -> PromisePaginatedCoins; + + #[wasm_bindgen(method, js_name = waitForTransaction)] + pub fn wait_for_transaction( + this: &WasmIotaClient, + input: &WasmWaitForTransactionParams, + ) -> PromiseIotaTransactionBlockResponse; } // Helper struct used to convert TYPESCRIPT types to RUST types @@ -372,4 +382,46 @@ impl ManagedWasmIotaClient { Ok(result.into_serde()?) } + + /// Wait for a transaction block result to be available over the API. + /// This can be used in conjunction with `execute_transaction_block` to wait for the transaction to + /// be available via the API. + /// This currently polls the `getTransactionBlock` API to check for the transaction. + /// + /// # Arguments + /// + /// * `digest` - The digest of the queried transaction. + /// * `options` - Options for specifying the content to be returned. + /// * `timeout` - The amount of time to wait for a transaction block. Defaults to one minute. + /// * `poll_interval` - The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. + pub async fn wait_for_transaction( + &self, + digest: TransactionDigest, + options: Option, + timeout: Option, + poll_interval: Option, + ) -> IotaRpcResult { + let params_object = WaitForTransactionParams::new(digest.to_string(), options, timeout, poll_interval); + let params: WasmWaitForTransactionParams = serde_json::to_value(¶ms_object) + .map_err(|e| { + console_log!("Error serializing WaitForTransactionParams to Value: {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + }) + .and_then(|v| { + v.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .map_err(|e| { + console_log!("Error serializing Value to WasmWaitForTransactionParams: {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + }) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::wait_for_transaction(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(IotaTransactionBlockResponseAdapter::new(result.into())) + } } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index 9782d98923..7a909bd921 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -1,11 +1,13 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; 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::digests::TransactionDigest; use identity_iota_interaction::types::execution_status::CommandArgumentError; use identity_iota_interaction::types::execution_status::ExecutionStatus; use identity_iota_interaction::types::object::Owner; @@ -39,6 +41,7 @@ const TS_SDK_TYPES: &str = r#" GetObjectParams, GetOwnedObjectsParams, GetTransactionBlockParams, + IotaClient, IotaObjectData, IotaObjectResponse, IotaTransactionBlockResponse, @@ -151,6 +154,10 @@ extern "C" { #[wasm_bindgen(typescript_type = "Promise")] #[derive(Clone)] pub type PromiseIotaTransactionBlockResponseAdapter; + + #[wasm_bindgen(typescript_type = "Parameters")] + #[derive(Clone, Debug)] + pub type WasmWaitForTransactionParams; } #[wasm_bindgen(module = "@iota/iota-sdk/transactions")] @@ -261,6 +268,9 @@ extern "C" { #[wasm_bindgen(method)] fn effects_created_inner(this: &IotaTransactionBlockResponseAdapter) -> Option>; + #[wasm_bindgen(method, js_name = "get_digest")] + fn digest_inner(this: &IotaTransactionBlockResponseAdapter) -> String; + #[wasm_bindgen(method, js_name = "get_response")] fn response(this: &IotaTransactionBlockResponseAdapter) -> WasmIotaTransactionBlockResponse; @@ -362,6 +372,11 @@ impl IotaTransactionBlockResponseAdapter { .collect() }) } + + pub fn digest(&self) -> Result { + TransactionDigest::from_str(&self.digest_inner()) + .map_err(|err| TsSdkError::WasmError("Failed to parse transaction block digest".to_string(), err.to_string())) + } } pub async fn execute_transaction( diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index 16e0e822c9..e3094aadac 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -166,6 +166,10 @@ impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { fn clone_native_response(&self) -> Self::NativeResponse { self.response.clone() } + + fn digest(&self) -> Result { + self.response.digest() + } } pub struct ReadAdapter { @@ -345,13 +349,17 @@ impl IotaClientTrait for IotaClientTsSdk { .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) .await?; - // wait a certain amount to time before continuing - // a follow up step was fetching an object created with this tx, which - for some reason - wasn't available yet - // TODO: check timing issues related to transactions finality here - sleep(500) - .await - .map_err(WasmError::from) - .map_err(TsSdkError::from)?; + // wait until new transaction block is available + self + .iota_client + .wait_for_transaction( + response.digest()?, + Some(IotaTransactionBlockResponseOptions::new()), + None, + None, + ) + .await + .unwrap(); Ok(Box::new(response)) } diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index fb2850702d..4d52ca62e2 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -161,6 +161,10 @@ impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { fn clone_native_response(&self) -> Self::NativeResponse { self.response.clone() } + + fn digest(&self) -> Result { + Ok(self.response.digest) + } } pub(crate) struct QuorumDriverAdapter<'a> { diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs index ed812c4b64..4955b9fe6b 100644 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -103,6 +103,9 @@ pub trait IotaTransactionBlockResponseT: OptionalSend { /// Returns a clone of the wrapped platform specific client sdk response fn clone_native_response(&self) -> Self::NativeResponse; + + // Returns digest for transaction block. + fn digest(&self) -> Result; } #[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] diff --git a/identity_iota_interaction/src/sdk_types/generated_types.rs b/identity_iota_interaction/src/sdk_types/generated_types.rs index 1a0ab84f66..2478fa4768 100644 --- a/identity_iota_interaction/src/sdk_types/generated_types.rs +++ b/identity_iota_interaction/src/sdk_types/generated_types.rs @@ -132,6 +132,7 @@ pub struct GetTransactionBlockParams { /// the digest of the queried transaction digest: String, /// options for specifying the content to be returned + #[serde(skip_serializing_if = "Option::is_none")] options: Option, } @@ -228,3 +229,37 @@ impl GetCoinsParams { } } } + +/// Params for `wait_for_transaction` / `wait_for_transaction`. +/// +/// Be careful when serializing with `serde_wasm_bindgen::to_value`, as `#[serde(flatten)]` +/// will turn the object into a `Map` instead of a plain object in Js. +/// Prefer serializing with `serde_wasm_bindgen::Serializer::json_compatible` or perform custom serialization. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WaitForTransactionParams { + /// Block digest and options for content that should be returned. + #[serde(flatten)] + get_transaction_block_params: GetTransactionBlockParams, + /// The amount of time to wait for a transaction block. Defaults to one minute. + #[serde(skip_serializing_if = "Option::is_none")] + timeout: Option, + /// The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. + #[serde(skip_serializing_if = "Option::is_none")] + poll_interval: Option, +} + +impl WaitForTransactionParams { + pub fn new( + digest: String, + options: Option, + timeout: Option, + poll_interval: Option, + ) -> Self { + WaitForTransactionParams { + get_transaction_block_params: GetTransactionBlockParams::new(digest, options), + timeout, + poll_interval, + } + } +} From b329aa9bcf559149d5d04f6c2b569e4b6dec8a25 Mon Sep 17 00:00:00 2001 From: wulfraem Date: Thu, 20 Feb 2025 18:47:10 +0100 Subject: [PATCH 39/63] Fix document consuming in WASM when publishing updates (#1541) * add `wait_for_transaction` to ts impl for `execute_transaction` * fix consuming of document arg when publishing document updates in wasm --- bindings/wasm/identity_wasm/src/iota/iota_document.rs | 1 + bindings/wasm/identity_wasm/src/rebased/identity.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs index 41cd17cc7b..76ab563bec 100644 --- a/bindings/wasm/identity_wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -112,6 +112,7 @@ impl IotaDocumentLock { /// Note: All methods that involve reading from this class may potentially raise an error /// if the object is being concurrently modified. #[wasm_bindgen(js_name = IotaDocument, inspectable)] +#[derive(Clone)] pub struct WasmIotaDocument(pub(crate) Rc); #[wasm_bindgen(js_class = IotaDocument)] diff --git a/bindings/wasm/identity_wasm/src/rebased/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs index 30f1a3ed44..f1568ab7f1 100644 --- a/bindings/wasm/identity_wasm/src/rebased/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -84,10 +84,10 @@ impl WasmOnChainIdentity { #[wasm_bindgen(js_name = updateDidDocument)] pub fn update_did_document( &self, - updated_doc: WasmIotaDocument, + updated_doc: &WasmIotaDocument, expiration_epoch: Option, ) -> WasmCreateUpdateDidProposalTx { - WasmCreateUpdateDidProposalTx::new(self, updated_doc, expiration_epoch) + WasmCreateUpdateDidProposalTx::new(self, updated_doc.clone(), expiration_epoch) } #[wasm_bindgen(js_name = deactivateDid)] From ba0f4b7be5d581f8d5f8addf66cbb4084be320e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 21 Feb 2025 11:06:18 +0100 Subject: [PATCH 40/63] Reenable Wasm tests (#1536) --- .github/workflows/build-and-test.yml | 33 ++++++++++++------- .github/workflows/shared-build-wasm.yml | 17 ++++++++-- .../{identity_wasm => }/.cargo/config.toml | 0 bindings/wasm/identity_wasm/package-lock.json | 20 +++++------ bindings/wasm/identity_wasm/package.json | 15 +++++---- bindings/wasm/iota_interaction_ts/Cargo.toml | 13 ++++---- .../iota_interaction_ts/lib/tsconfig.web.json | 1 + .../iota_interaction_ts/package-lock.json | 1 + .../wasm/iota_interaction_ts/package.json | 11 ++++--- 9 files changed, 67 insertions(+), 44 deletions(-) rename bindings/wasm/{identity_wasm => }/.cargo/config.toml (100%) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 2822469655..431fa3de2c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -12,6 +12,7 @@ on: - 'support/**' paths: - '.github/workflows/build-and-test.yml' + - '.github/workflows/shared-build-wasm.yml' - '.github/actions/**' - '**.rs' - '**.toml' @@ -140,16 +141,13 @@ jobs: # publish the package and set the IOTA_IDENTITY_PKG_ID env variable run: | iota move test - working-directory: ./identity_iota_core/packages/iota_identity + working-directory: identity_iota_core/packages/iota_identity - name: publish IotaIdentity package if: matrix.os != 'windows-latest' # publish the package and set the IOTA_IDENTITY_PKG_ID env variable - run: | - json_output=$(iota client publish --skip-dependency-verification --with-unpublished-dependencies --json --gas-budget 500000000 .) - package_id=$(echo $json_output | jq --raw-output '.objectChanges[] | select(.type | contains("published")) | .packageId') - echo "IOTA_IDENTITY_PKG_ID=$package_id" >> "$GITHUB_ENV" - working-directory: ./identity_iota_core/packages/iota_identity + run: echo "IOTA_IDENTITY_PKG_ID=$(./publish_identity_package.sh)" >> "$GITHUB_ENV" + working-directory: identity_iota_core/scripts/ - name: Run tests excluding `custom_time` feature if: matrix.os != 'windows-latest' @@ -191,8 +189,7 @@ jobs: test-wasm: needs: build-wasm - # if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} - if: ${{ false }} # disable for now + if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' }} runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -217,11 +214,25 @@ jobs: uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm/identity_wasm + path: bindings/wasm/ - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' + - name: publish IotaIdentity package + if: matrix.os != 'windows-latest' + # publish the package and set the IOTA_IDENTITY_PKG_ID env variable + run: echo "IOTA_IDENTITY_PKG_ID=$(./publish_identity_package.sh)" >> "$GITHUB_ENV" + working-directory: identity_iota_core/scripts/ + + - name: Install JS dependencies + run: npm ci + working-directory: bindings/wasm/identity_wasm + + - name: Install JS dependencies # This is problematic: @iota/iota-sdk seems to not get used from the identity_wasm package, that is why reinstall deps here + run: npm ci + working-directory: bindings/wasm/iota_interaction_ts + - name: Run Wasm examples run: npm run test:readme && npm run test:node working-directory: bindings/wasm/identity_wasm @@ -254,7 +265,7 @@ jobs: uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm/identity_wasm + path: bindings/wasm/ - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' @@ -299,7 +310,7 @@ jobs: uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm/identity_wasm + path: bindings/wasm/ - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 6931b065e0..9f64117e7b 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -23,7 +23,7 @@ jobs: build-wasm: defaults: run: - working-directory: bindings/wasm/identity_wasm + working-directory: bindings/wasm/ shell: bash runs-on: ubuntu-latest strategy: @@ -46,11 +46,12 @@ jobs: with: os: ${{ runner.os }} job: ${{ github.job }} + target: wasm32-unknown-unknown cargo-cache-enabled: true target-cache-enabled: true sccache-enabled: true sccache-path: ${{ matrix.sccache-path }} - target-cache-path: bindings/wasm/identity_wasm/target + target-cache-path: bindings/wasm/target # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli @@ -68,16 +69,26 @@ jobs: with: node-version: 16.x + - name: Install JS dependencies + run: npm ci + working-directory: bindings/wasm/iota_interaction_ts + + - name: Build IOTA interaction bindings + run: npm run build + working-directory: bindings/wasm/iota_interaction_ts + - name: Install JS dependencies run: npm ci working-directory: bindings/wasm/identity_wasm - name: Build WASM bindings run: npm run build + working-directory: bindings/wasm/identity_wasm - name: Run Node unit tests if: ${{ inputs.run-unit-tests }} run: npm run test:unit:node + working-directory: bindings/wasm/identity_wasm - name: Stop sccache uses: './.github/actions/rust/sccache/stop-sccache' @@ -93,5 +104,7 @@ jobs: bindings/wasm/identity_wasm/web bindings/wasm/identity_wasm/examples/dist bindings/wasm/identity_wasm/docs + bindings/wasm/iota_interaction_ts/node + bindings/wasm/iota_interaction_ts/web if-no-files-found: error retention-days: 1 diff --git a/bindings/wasm/identity_wasm/.cargo/config.toml b/bindings/wasm/.cargo/config.toml similarity index 100% rename from bindings/wasm/identity_wasm/.cargo/config.toml rename to bindings/wasm/.cargo/config.toml diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index fb0a83775f..0c17e87b45 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -35,13 +35,14 @@ "txm": "^8.1.0", "typedoc": "^0.27.6", "typedoc-plugin-markdown": "^4.4.1", - "typescript": "=5.3.3", + "typescript": "^5.7.3", "wasm-opt": "^1.3.0" }, "engines": { "node": ">=16" }, "peerDependencies": { + "@iota/iota-sdk": "^0.5.0", "@iota/sdk-wasm": "^1.0.4" } }, @@ -357,7 +358,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", - "license": "Apache-2.0", "peer": true, "dependencies": { "class-transformer": "^0.5.1", @@ -1889,7 +1889,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT", "peer": true }, "node_modules/clean-stack": { @@ -5509,7 +5508,6 @@ "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0", "peer": true }, "node_modules/remark-parse": { @@ -6230,7 +6228,6 @@ "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", "deprecated": "no longer maintained", - "license": "(Unlicense OR Apache-2.0)", "peer": true }, "node_modules/throttleit": { @@ -6660,10 +6657,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "license": "Apache-2.0", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11969,9 +11965,9 @@ "requires": {} }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==" }, "typical": { "version": "2.6.1", diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 312eccd77c..70078821f4 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -10,16 +10,16 @@ "example": "examples" }, "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", - "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", - "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", + "bundle:nodejs": "wasm-bindgen ../target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", + "bundle:web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "typedoc && npm run fix_docs", "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist identity_wasm resolve", "build": "npm run build:web && npm run build:nodejs && npm run build:docs", "example:node": "ts-node --project tsconfig.node.json -r tsconfig-paths/register ./examples/src/main.ts", - "test": "npm run test:unit:node && npm run test:readme && npm run test:node && test:browser:parallel", + "test": "npm run test:unit:node && npm run test:readme && npm run test:node && npm run test:browser:parallel", "test:node": "ts-mocha -r tsconfig-paths/register -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/e2e -a '\"--quiet\"'", "test:browser:parallel:firefox": "cypress-parallel -s test:browser:firefox -t 4 -d cypress/e2e -a '\"--quiet\"'", @@ -58,8 +58,8 @@ ], "devDependencies": { "@transmute/did-key-ed25519": "0.3.0-unstable.9", - "@types/node": "^22.0.0", "@types/mocha": "^9.1.0", + "@types/node": "^22.0.0", "big-integer": "^1.6.51", "copy-webpack-plugin": "^7.0.0", "cypress": "^13.12.0", @@ -74,12 +74,12 @@ "txm": "^8.1.0", "typedoc": "^0.27.6", "typedoc-plugin-markdown": "^4.4.1", - "typescript": "=5.3.3", + "typescript": "^5.7.3", "wasm-opt": "^1.3.0" }, "dependencies": { - "@iota/iota-sdk": "^0.5.0", "@iota/iota-interaction-ts": "file:../iota_interaction_ts/node", + "@iota/iota-sdk": "^0.5.0", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", @@ -87,6 +87,7 @@ "node-fetch": "^2.6.7" }, "peerDependencies": { + "@iota/iota-sdk": "^0.5.0", "@iota/sdk-wasm": "^1.0.4" }, "engines": { diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 083061b61c..b0b8bbdd70 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -1,15 +1,14 @@ [package] name = "iota_interaction_ts" version = "1.4.0" -authors.workspace = true -edition.workspace = true -homepage.workspace = true +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" keywords = ["iota", "tangle", "identity", "wasm"] -license.workspace = true +license = "Apache-2.0" publish = false -readme = "./README.md" -repository.workspace = true -rust-version.workspace = true +readme = "README.md" +resolver = "2" description = "identity_iota_interaction Adapters using Web Assembly bindings." [lib] diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json index 0d87cb28af..203545f4f0 100644 --- a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "target": "ES2020", "baseUrl": "./", "paths": { "~identity_wasm": [ diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json index 81ecc84c08..2c98769876 100644 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -41,6 +41,7 @@ "node": ">=16" }, "peerDependencies": { + "@iota/iota-sdk": "^0.5.0", "@iota/sdk-wasm": "^1.0.4" } }, diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index ba9d46a7e2..de0c61e079 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -10,14 +10,14 @@ "example": "examples" }, "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", - "bundle:nodejs": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", - "bundle:web": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", + "bundle:nodejs": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", + "bundle:web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/iota_interaction_ts_bg.wasm -o node/iota_interaction_ts_bg.wasm", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/iota_interaction_ts_bg.wasm -o web/iota_interaction_ts_bg.wasm", "build:docs": "typedoc && npm run fix_docs", "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist iota_interaction_ts resolve", - "build": "npm run build:web && npm run build:nodejs && npm run build:docs", + "build": "npm run build:web && npm run build:nodejs", "example:node": "ts-node --project tsconfig.node.json -r tsconfig-paths/register ./examples/src/main.ts", "test": "npm run test:unit:node && npm run test:readme && npm run test:node && test:browser:parallel", "test:node": "ts-mocha -r tsconfig-paths/register -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", @@ -81,7 +81,8 @@ "node-fetch": "^2.6.7" }, "peerDependencies": { - "@iota/sdk-wasm": "^1.0.4" + "@iota/sdk-wasm": "^1.0.4", + "@iota/iota-sdk": "^0.5.0" }, "engines": { "node": ">=16" From fe9c5ccbbcfad019155ccfe38cd01b541e7c751f Mon Sep 17 00:00:00 2001 From: wulfraem Date: Fri, 21 Feb 2025 14:54:39 +0100 Subject: [PATCH 41/63] update how DIDs are fetched from created identity (#1542) --- .../wasm/identity_wasm/examples/src/0_basic/0_create_did.ts | 2 +- .../wasm/identity_wasm/examples/src/0_basic/1_update_did.ts | 3 +-- .../wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts | 3 +-- .../identity_wasm/examples/src/0_basic/3_deactivate_did.ts | 3 +-- .../examples/src/1_advanced/4_custom_resolution.ts | 4 ++-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 3b62bf3876..880c3cb9ca 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -28,7 +28,7 @@ export async function createIdentity(): Promise { .createIdentity(unpublished) .finish() .execute(identityClient); - did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + did = identity.didDocument().id(); } else { console.log("Publishing document to identity"); const { output: published } = await identityClient diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index 4afcc39ed3..4b79a061f9 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { - IotaDID, JwkMemStore, JwsAlgorithm, MethodRelationship, @@ -28,7 +27,7 @@ export async function updateIdentity() { .createIdentity(unpublished) .finish() .execute(identityClient); - const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + const did = identity.didDocument().id(); // Resolve the latest state of the document. // Technically this is equivalent to the document above. diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index c48ac2ceeb..22a90deb95 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -5,7 +5,6 @@ import { CoreDocument, DIDJwk, IdentityClientReadOnly, - IotaDID, IotaDocument, IToCoreDocument, Resolver, @@ -30,7 +29,7 @@ export async function resolveIdentity() { .createIdentity(unpublished) .finish() .execute(identityClient); - const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + const did = identity.didDocument().id(); // Resolve the associated Alias Output and extract the DID document from it. const resolved = await identityClient.resolveDid(did); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index d91316f389..6592f2d39c 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -1,7 +1,6 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaDID } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; @@ -19,7 +18,7 @@ export async function deactivateIdentity() { .createIdentity(unpublished) .finish() .execute(identityClient); - const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + const did = identity.didDocument().id(); // Resolve the latest state of the document. // Technically this is equivalent to the document above. diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts index 26aaf4b634..d77d03eb46 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts @@ -1,4 +1,4 @@ -import { CoreDocument, IotaDID, IotaDocument, Resolver } from "@iota/identity-wasm/node"; +import { CoreDocument, IotaDocument, Resolver } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; @@ -40,7 +40,7 @@ export async function customResolution() { .createIdentity(unpublished) .finish() .execute(identityClient); - const did = IotaDID.fromAliasId(identity.id(), identityClient.network()); + const did = identity.didDocument().id(); // Construct a Resolver capable of resolving the did:key and iota methods. let handlerMap: Map Promise> = new Map(); From 512cb6b0e281ca23289a43d28fb5102611398611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 21 Feb 2025 16:22:09 +0100 Subject: [PATCH 42/63] Remove stardust artifacts (#1543) * remove stardust sdk and client * remove stardust sdk from interactions * update package-lock * remove stardust related deps, update comments * fix outdated comment --------- Co-authored-by: Sebastian Wolfram --- bindings/wasm/identity_wasm/README.md | 18 +- .../identity_wasm/cypress/support/setup.js | 4 +- .../wasm/identity_wasm/examples/README.md | 26 +-- .../examples/src/0_basic/1_update_did.ts | 2 +- .../examples/src/0_basic/2_resolve_did.ts | 4 +- .../examples/src/0_basic/3_deactivate_did.ts | 2 +- .../identity_wasm/examples/tsconfig.web.json | 3 - bindings/wasm/identity_wasm/lib/index.ts | 1 - .../identity_wasm/lib/iota_identity_client.ts | 189 --------------- bindings/wasm/identity_wasm/lib/tsconfig.json | 4 - .../wasm/identity_wasm/lib/tsconfig.web.json | 4 - bindings/wasm/identity_wasm/package-lock.json | 155 +++---------- bindings/wasm/identity_wasm/package.json | 3 +- .../identity_wasm/src/iota/identity_client.rs | 93 -------- .../src/iota/identity_client_ext.rs | 183 --------------- .../identity_wasm/src/iota/iota_document.rs | 82 +------ bindings/wasm/identity_wasm/src/iota/mod.rs | 5 +- bindings/wasm/identity_wasm/src/lib.rs | 4 - .../src/obsolete/identity_client.rs | 219 ------------------ .../wasm/identity_wasm/src/obsolete/mod.rs | 7 - .../iota_interaction_ts/lib/tsconfig.json | 4 - .../iota_interaction_ts/lib/tsconfig.web.json | 4 - .../iota_interaction_ts/package-lock.json | 78 ++----- .../wasm/iota_interaction_ts/package.json | 1 - 24 files changed, 79 insertions(+), 1016 deletions(-) delete mode 100644 bindings/wasm/identity_wasm/lib/iota_identity_client.ts delete mode 100644 bindings/wasm/identity_wasm/src/iota/identity_client.rs delete mode 100644 bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs delete mode 100644 bindings/wasm/identity_wasm/src/obsolete/identity_client.rs delete mode 100644 bindings/wasm/identity_wasm/src/obsolete/mod.rs diff --git a/bindings/wasm/identity_wasm/README.md b/bindings/wasm/identity_wasm/README.md index 3c9afd37a4..eb3cdac003 100644 --- a/bindings/wasm/identity_wasm/README.md +++ b/bindings/wasm/identity_wasm/README.md @@ -98,7 +98,7 @@ export async function main() { const networkHrp = identityClient.network(); // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. + // The DID will be derived from the ObjectId of the identity after publishing. const document = new IotaDocument(networkHrp); // Insert a new Ed25519 verification method in the DID document. @@ -191,11 +191,6 @@ import copy from "rollup-plugin-copy"; // Add the copy plugin to the `plugins` array of your rollup config: copy({ targets: [ - { - src: "node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm", - dest: "public", - rename: "iota_sdk_wasm_bg.wasm", - }, { src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", dest: "public", @@ -221,10 +216,6 @@ const CopyWebPlugin= require('copy-webpack-plugin'); new CopyWebPlugin({ patterns: [ - { - from: 'node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm', - to: 'iota_sdk_wasm_bg.wasm' - }, { from: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', to: 'identity_wasm_bg.wasm' @@ -236,7 +227,6 @@ new CopyWebPlugin({ ### Web Usage ```typescript -import init, { Client } from "@iota/sdk-wasm/web"; import * as identity from "@iota/identity-wasm/web"; // The endpoint of the IOTA node to use. @@ -262,7 +252,7 @@ async function createDocument() { const networkHrp = await didClient.getNetworkHrp(); // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. + // The DID will be derived from the ObjectId of the identity after publishing. const document = new identity.IotaDocument(networkHrp); // Insert a new Ed25519 verification method in the DID document. @@ -290,8 +280,7 @@ async function createDocument() { console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); } -init() - .then(() => identity.init()) +identity.init() .then(() => { await createDocument(); }); @@ -299,7 +288,6 @@ init() // or (async () => { - await init(); await identity.init(); await createDocument(); diff --git a/bindings/wasm/identity_wasm/cypress/support/setup.js b/bindings/wasm/identity_wasm/cypress/support/setup.js index 6f66f43751..6a3c5722b1 100644 --- a/bindings/wasm/identity_wasm/cypress/support/setup.js +++ b/bindings/wasm/identity_wasm/cypress/support/setup.js @@ -1,8 +1,6 @@ -import init from "@iota/sdk-wasm/web"; import * as identity from "../../web"; export const setup = async (func) => { - await init("../../../node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm") - .then(async () => await identity.init("../../../web/identity_wasm_bg.wasm")) + await identity.init("../../../web/identity_wasm_bg.wasm") .then(func); }; diff --git a/bindings/wasm/identity_wasm/examples/README.md b/bindings/wasm/identity_wasm/examples/README.md index 57bbe7b9f6..06d0cd731e 100644 --- a/bindings/wasm/identity_wasm/examples/README.md +++ b/bindings/wasm/identity_wasm/examples/README.md @@ -22,11 +22,11 @@ The examples require you to have the node you want to use in the iota clients "e Summarizing the last point, you'll need one or more of the following environment variables: -| Name | Required for local node | Required for testnet | Required for other node | Comment | -| ------------------------ | :---------------------: | :------------------: | :---------------------: | :------------------: | +| Name | Required for local node | Required for testnet | Required for other node | Comment | +| -------------------- | :---------------------: | :------------------: | :---------------------: | :------------------: | | IOTA_IDENTITY_PKG_ID | x | | x | | -| NETWORK_URL | | x | x | | -| NETWORK_NAME_FAUCET | | x | x | see assumption above | +| NETWORK_URL | | x | x | | +| NETWORK_NAME_FAUCET | | x | x | see assumption above | ### Node.js @@ -58,15 +58,15 @@ IOTA_IDENTITY_PKG_ID=0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a5025 The following basic CRUD (Create, Read, Update, Delete) examples are available: -| Name | Information | -|:----------------------------------------------------|:-------------------------------------------------------------------------------------| -| [0_create_did](src/0_basic/0_create_did.ts) | Demonstrates how to create a DID Document and publish it in a new Alias Output. | -| [1_update_did](src/0_basic/1_update_did.ts) | Demonstrates how to update a DID document in an existing Alias Output. | -| [2_resolve_did](src/0_basic/2_resolve_did.ts) | Demonstrates how to resolve an existing DID in an Alias Output. | -| [3_deactivate_did](src/0_basic/3_deactivate_did.ts) | Demonstrates how to deactivate a DID in an Alias Output. | -| [5_create_vc](src/0_basic/5_create_vc.ts) | Demonstrates how to create and verify verifiable credentials. | -| [6_create_vp](src/0_basic/6_create_vp.ts) | Demonstrates how to create and verify verifiable presentations. | -| [7_revoke_vc](src/0_basic/7_revoke_vc.ts) | Demonstrates how to revoke a verifiable credential. | +| Name | Information | +| :-------------------------------------------------- | :-------------------------------------------------------------------------- | +| [0_create_did](src/0_basic/0_create_did.ts) | Demonstrates how to create a DID Document and publish it in a new identity. | +| [1_update_did](src/0_basic/1_update_did.ts) | Demonstrates how to update a DID document in an existing identity. | +| [2_resolve_did](src/0_basic/2_resolve_did.ts) | Demonstrates how to resolve an existing DID in an identity. | +| [3_deactivate_did](src/0_basic/3_deactivate_did.ts) | Demonstrates how to deactivate a DID in an identity. | +| [5_create_vc](src/0_basic/5_create_vc.ts) | Demonstrates how to create and verify verifiable credentials. | +| [6_create_vp](src/0_basic/6_create_vp.ts) | Demonstrates how to create and verify verifiable presentations. | +| [7_revoke_vc](src/0_basic/7_revoke_vc.ts) | Demonstrates how to revoke a verifiable credential. | ## Advanced Examples diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts index 4b79a061f9..63c7b51fb3 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts @@ -13,7 +13,7 @@ import { import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; -/** Demonstrates how to update a DID document in an existing Alias Output. */ +/** Demonstrates how to update a DID document in an existing identity. */ export async function updateIdentity() { // create new clients and create new account const iotaClient = new IotaClient({ url: NETWORK_URL }); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts index 22a90deb95..fbffebbb5f 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts @@ -15,7 +15,7 @@ import { createDocumentForNetwork, getFundedClient, getMemstorage, IOTA_IDENTITY const DID_JWK: string = "did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9"; -/** Demonstrates how to resolve an existing DID in an Alias Output. */ +/** Demonstrates how to resolve an existing DID in an identity. */ export async function resolveIdentity() { // create new clients and create new account const iotaClient = new IotaClient({ url: NETWORK_URL }); @@ -31,7 +31,7 @@ export async function resolveIdentity() { .execute(identityClient); const did = identity.didDocument().id(); - // Resolve the associated Alias Output and extract the DID document from it. + // Resolve the associated identity and extract the DID document from it. const resolved = await identityClient.resolveDid(did); console.log("Resolved DID document:", JSON.stringify(resolved, null, 2)); diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts index 6592f2d39c..f31fb460f3 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts @@ -4,7 +4,7 @@ import { IotaClient } from "@iota/iota-sdk/client"; import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL, TEST_GAS_BUDGET } from "../util"; -/** Demonstrates how to deactivate a DID in an Alias Output. */ +/** Demonstrates how to deactivate a DID of an identity. */ export async function deactivateIdentity() { // create new clients and create new account const iotaClient = new IotaClient({ url: NETWORK_URL }); diff --git a/bindings/wasm/identity_wasm/examples/tsconfig.web.json b/bindings/wasm/identity_wasm/examples/tsconfig.web.json index 09b504a14a..4d96c67e9d 100644 --- a/bindings/wasm/identity_wasm/examples/tsconfig.web.json +++ b/bindings/wasm/identity_wasm/examples/tsconfig.web.json @@ -13,9 +13,6 @@ "@iota/identity-wasm/node": [ "../web" ], - "@iota/sdk-wasm/node": [ - "@iota/sdk-wasm/web" - ] } }, "exclude": [ diff --git a/bindings/wasm/identity_wasm/lib/index.ts b/bindings/wasm/identity_wasm/lib/index.ts index 7a0a246940..2deb0c3aca 100644 --- a/bindings/wasm/identity_wasm/lib/index.ts +++ b/bindings/wasm/identity_wasm/lib/index.ts @@ -3,7 +3,6 @@ import "./append_functions.js"; -export * from "./iota_identity_client.js"; export * from "./jose"; export * from "./jwk_storage"; export * from "./key_id_storage"; diff --git a/bindings/wasm/identity_wasm/lib/iota_identity_client.ts b/bindings/wasm/identity_wasm/lib/iota_identity_client.ts deleted file mode 100644 index 668d7d7dca..0000000000 --- a/bindings/wasm/identity_wasm/lib/iota_identity_client.ts +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { IIotaIdentityClient, IotaDID, IotaDocument, IotaIdentityClientExt } from "~identity_wasm"; - -import { - Address, - AddressUnlockCondition, - AliasOutput, - AliasOutputBuilderParams, - Client, - INodeInfoProtocol, - INodeInfoWrapper, - IRent, - OutputResponse, - OutputType, - SecretManagerType, - UTXOInput, -} from "~sdk-wasm"; - -/** Provides operations for IOTA DID Documents with Alias Outputs. */ -export class IotaIdentityClient implements IIotaIdentityClient { - client: Client; - - constructor(client: Client) { - this.client = client; - } - - async getNetworkHrp() { - return await this.client.getBech32Hrp(); - } - - async getAliasOutput(aliasId: string) { - // Lookup latest OutputId from the indexer plugin. - const outputId = await this.client.aliasOutputId(aliasId); - - // Fetch AliasOutput. - const outputResponse: OutputResponse = await this.client.getOutput(outputId); - const output = outputResponse.output; - if (output.getType() != OutputType.Alias) { - throw new Error("AliasId '" + aliasId + "' returned incorrect output type '" + output.getType() + "'"); - } - // Coerce to tuple instead of an array. - // Cast of output is fine as we checked the type earlier. - const ret: [string, AliasOutput] = [outputId, output as AliasOutput]; - return ret; - } - - async getRentStructure(): Promise { - const info: INodeInfoWrapper = await this.client.getInfo(); - return info.nodeInfo.protocol.rentStructure; - } - - async getTokenSupply(): Promise { - return await this.client.getTokenSupply(); - } - - async getProtocolParameters(): Promise { - const protocolParameters: INodeInfoProtocol = await this.client.getProtocolParameters(); - return protocolParameters; - } - - /** Create a DID with a new Alias Output containing the given `document`. - * - * The `address` will be set as the state controller and governor unlock conditions. - * The minimum required token deposit amount will be set according to the given - * `rent_structure`, which will be fetched from the node if not provided. - * The returned Alias Output can be further customized before publication, if desired. - * - * NOTE: this does *not* publish the Alias Output. - */ - async newDidOutput(address: Address, document: IotaDocument, rentStructure?: IRent): Promise { - const aliasOutputParams: AliasOutputBuilderParams = await IotaIdentityClientExt.newDidOutput( - this, - address, - document, - rentStructure, - ); - return await this.client.buildAliasOutput(aliasOutputParams); - } - - /** Fetches the associated Alias Output and updates it with `document` in its state metadata. - * The storage deposit on the output is left unchanged. If the size of the document increased, - * the amount should be increased manually. - * - * NOTE: this does *not* publish the updated Alias Output. - */ - async updateDidOutput(document: IotaDocument): Promise { - const aliasOutputParams: AliasOutputBuilderParams = await IotaIdentityClientExt.updateDidOutput(this, document); - return await this.client.buildAliasOutput(aliasOutputParams); - } - - /** Removes the DID document from the state metadata of its Alias Output, - * effectively deactivating it. The storage deposit on the output is left unchanged, - * and should be reallocated manually. - * - * Deactivating does not destroy the output. Hence, it can be re-activated by publishing - * an update containing a DID document. - * - * NOTE: this does *not* publish the updated Alias Output. - */ - async deactivateDidOutput(did: IotaDID): Promise { - const aliasOutputParams: AliasOutputBuilderParams = await IotaIdentityClientExt.deactivateDidOutput(this, did); - return await this.client.buildAliasOutput(aliasOutputParams); - } - - /** Resolve a {@link IotaDocument}. Returns an empty, deactivated document if the state - * metadata of the Alias Output is empty. - */ - async resolveDid(did: IotaDID): Promise { - return await IotaIdentityClientExt.resolveDid(this, did); - } - - /** Fetches the Alias Output associated with the given DID. */ - async resolveDidOutput(did: IotaDID): Promise { - const aliasOutputParams: AliasOutputBuilderParams = await IotaIdentityClientExt.resolveDidOutput(this, did); - return await this.client.buildAliasOutput(aliasOutputParams); - } - - /** Publish the given `aliasOutput` with the provided `secretManager`, and returns - * the DID document extracted from the published block. - * - * Note that only the state controller of an Alias Output is allowed to update its state. - * This will attempt to move tokens to or from the state controller address to match - * the storage deposit amount specified on `aliasOutput`. - * - * This method modifies the on-ledger state. - */ - async publishDidOutput(secretManager: SecretManagerType, aliasOutput: AliasOutput): Promise { - const networkHrp = await this.getNetworkHrp(); - // Publish block. - const [blockId, block] = await this.client.buildAndPostBlock(secretManager, { - outputs: [aliasOutput], - }); - await this.client.retryUntilIncluded(blockId); - - // Rewrite the following code when working on [Issue #1445 Replace mocked Identity client with real Identity client] - // - // // Extract document with computed AliasId. - // const documents = IotaDocument.unpackFromBlock(networkHrp, block); - // if (documents.length < 1) { - // throw new Error("publishDidOutput: no DID document in transaction payload"); - // } - // return documents[0]; - return IotaDocument.newWithId(IotaDID.parse("Foo Bar")); - } - - /** Destroy the Alias Output containing the given `did`, sending its tokens to a new Basic Output - * unlockable by the given address. - * - * Note that only the governor of an Alias Output is allowed to destroy it. - * - * ### WARNING - * - * This destroys the Alias Output and DID document, rendering them permanently unrecoverable. - */ - async deleteDidOutput(secretManager: SecretManagerType, address: Address, did: IotaDID) { - const networkHrp = await this.getNetworkHrp(); - if (networkHrp !== did.network()) { - throw new Error( - "deleteDidOutput: DID network mismatch, client expected `" + networkHrp + "`, DID network is `" - + did.network() + "`", - ); - } - - const aliasId: string = did.tag(); - const [outputId, aliasOutput] = await this.getAliasOutput(aliasId); - const aliasInput: UTXOInput = UTXOInput.fromOutputId(outputId); - - // Send funds to the address. - const basicOutput = await this.client.buildBasicOutput({ - amount: aliasOutput.getAmount(), - nativeTokens: aliasOutput.getNativeTokens(), - unlockConditions: [ - new AddressUnlockCondition(address), - ], - }); - - // Publish block. - const [blockId, _block] = await this.client.buildAndPostBlock(secretManager, { - inputs: [aliasInput], - outputs: [basicOutput], - burn: { - aliases: [aliasId], - }, - }); - await this.client.retryUntilIncluded(blockId); - } -} diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.json b/bindings/wasm/identity_wasm/lib/tsconfig.json index 08004d04ef..fc3c169b56 100644 --- a/bindings/wasm/identity_wasm/lib/tsconfig.json +++ b/bindings/wasm/identity_wasm/lib/tsconfig.json @@ -7,10 +7,6 @@ "../node/identity_wasm", "./identity_wasm.js" ], - "~sdk-wasm": [ - "../node_modules/@iota/sdk-wasm/node", - "@iota/sdk-wasm/node" - ], "../lib": [ "." ] diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.web.json b/bindings/wasm/identity_wasm/lib/tsconfig.web.json index 0d87cb28af..2c750a2848 100644 --- a/bindings/wasm/identity_wasm/lib/tsconfig.web.json +++ b/bindings/wasm/identity_wasm/lib/tsconfig.web.json @@ -7,10 +7,6 @@ "../web/identity_wasm", "./identity_wasm.js" ], - "~sdk-wasm": [ - "../node_modules/@iota/sdk-wasm/web", - "@iota/sdk-wasm/web" - ], "../lib": [ "." ] diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index 0c17e87b45..3d4053629a 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -42,51 +42,10 @@ "node": ">=16" }, "peerDependencies": { - "@iota/iota-sdk": "^0.5.0", - "@iota/sdk-wasm": "^1.0.4" - } - }, - "../iota_interaction_ts": { - "name": "@iota/iota-interaction-ts", - "version": "1.4.0", - "license": "Apache-2.0", - "dependencies": { - "@noble/ed25519": "^1.7.3", - "@noble/hashes": "^1.4.0", - "@types/node-fetch": "^2.6.2", - "base64-arraybuffer": "^1.0.2", - "node-fetch": "^2.6.7" - }, - "devDependencies": { - "@transmute/did-key-ed25519": "0.3.0-unstable.9", - "@types/mocha": "^9.1.0", - "@types/node": "^22.0.0", - "big-integer": "^1.6.51", - "copy-webpack-plugin": "^7.0.0", - "cypress": "^13.12.0", - "cypress-parallel": "^0.14.0", - "dprint": "^0.33.0", - "fs-extra": "^10.1.0", - "jsdoc-to-markdown": "^7.1.1", - "mocha": "^9.2.0", - "ts-mocha": "^9.0.2", - "ts-node": "^10.9.2", - "tsconfig-paths": "^4.1.0", - "txm": "^8.1.0", - "typedoc": "^0.24.6", - "typedoc-plugin-markdown": "^3.14.0", - "typescript": "=5.1", - "wasm-opt": "^1.3.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@iota/sdk-wasm": "^1.0.4" + "@iota/iota-sdk": "^0.5.0" } }, "../iota_interaction_ts/node": { - "name": "@iota/iota-interaction-ts", "version": "1.4.0", "license": "Apache-2.0" }, @@ -354,26 +313,6 @@ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "license": "Unlicense" }, - "node_modules/@iota/sdk-wasm": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", - "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", - "peer": true, - "dependencies": { - "class-transformer": "^0.5.1", - "node-fetch": "^2.6.7", - "qs": "^6.9.7", - "reflect-metadata": "^0.1.13", - "semver": "^7.5.2", - "text-encoding": "^0.7.0" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -1710,6 +1649,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -1885,12 +1825,6 @@ "node": ">=8" } }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "peer": true - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3129,6 +3063,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -3141,7 +3076,8 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -3156,6 +3092,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -3352,6 +3289,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -3372,6 +3310,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -3383,6 +3322,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -4186,6 +4126,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -5103,6 +5044,7 @@ "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5343,6 +5285,7 @@ "version": "6.10.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -5504,12 +5447,6 @@ "node": ">=0.10.0" } }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "peer": true - }, "node_modules/remark-parse": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", @@ -5697,6 +5634,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -5765,6 +5703,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -6223,13 +6162,6 @@ "node": ">=4" } }, - "node_modules/text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained", - "peer": true - }, "node_modules/throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", @@ -7096,7 +7028,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "2.7.0", @@ -7375,21 +7308,6 @@ } } }, - "@iota/sdk-wasm": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", - "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", - "peer": true, - "requires": { - "class-transformer": "^0.5.1", - "fsevents": "^2.3.2", - "node-fetch": "^2.6.7", - "qs": "^6.9.7", - "reflect-metadata": "^0.1.13", - "semver": "^7.5.2", - "text-encoding": "^0.7.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -8461,6 +8379,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -8568,12 +8487,6 @@ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true }, - "class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "peer": true - }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -9518,12 +9431,14 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "optional": true }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "get-caller-file": { "version": "2.0.5", @@ -9535,6 +9450,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -9673,6 +9589,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -9686,12 +9603,14 @@ "has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true }, "he": { "version": "1.2.0", @@ -10261,6 +10180,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -10845,7 +10765,8 @@ "object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true }, "object-to-spawn-args": { "version": "2.0.1", @@ -11015,6 +10936,7 @@ "version": "6.10.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "dev": true, "requires": { "side-channel": "^1.0.4" } @@ -11130,12 +11052,6 @@ } } }, - "reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "peer": true - }, "remark-parse": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", @@ -11266,6 +11182,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -11319,6 +11236,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -11658,12 +11576,6 @@ } } }, - "text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "peer": true - }, "throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", @@ -12284,7 +12196,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "2.7.0", diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 70078821f4..c80d0ae7b5 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -87,8 +87,7 @@ "node-fetch": "^2.6.7" }, "peerDependencies": { - "@iota/iota-sdk": "^0.5.0", - "@iota/sdk-wasm": "^1.0.4" + "@iota/iota-sdk": "^0.5.0" }, "engines": { "node": ">=16" diff --git a/bindings/wasm/identity_wasm/src/iota/identity_client.rs b/bindings/wasm/identity_wasm/src/iota/identity_client.rs deleted file mode 100644 index 8061852039..0000000000 --- a/bindings/wasm/identity_wasm/src/iota/identity_client.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Formatter; - -use iota_sdk::types::block::output::dto::AliasOutputDto; -use iota_sdk::types::block::output::AliasId; -use iota_sdk::types::block::output::AliasOutput; -use iota_sdk::types::block::output::OutputId; -use iota_sdk::types::block::protocol::ProtocolParameters; -use iota_sdk::types::TryFromDto; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::JsFuture; - -use crate::error::JsValueResult; -use crate::obsolete::IotaIdentityClient; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IIotaIdentityClient")] - pub type WasmIotaIdentityClient; - - #[allow(non_snake_case)] - #[wasm_bindgen(method, js_name = getAliasOutput)] - pub fn get_alias_output(this: &WasmIotaIdentityClient, aliasId: String) -> JsValue; - - #[wasm_bindgen(method, js_name = getProtocolParameters)] - pub fn get_protocol_parameters(this: &WasmIotaIdentityClient) -> JsValue; -} - -impl Debug for WasmIotaIdentityClient { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str("WasmIotaIdentityClient") - } -} - -#[async_trait::async_trait(?Send)] -impl IotaIdentityClient for WasmIotaIdentityClient { - async fn get_alias_output(&self, id: AliasId) -> Result<(OutputId, AliasOutput), identity_iota::iota::Error> { - let promise: Promise = Promise::resolve(&WasmIotaIdentityClient::get_alias_output(self, id.to_string())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let tuple: js_sys::Array = js_sys::Array::from(&result.to_iota_core_error()?); - - let mut iter: js_sys::ArrayIter<'_> = tuple.iter(); - - let output_id: OutputId = iter - .next() - .ok_or_else(|| identity_iota::iota::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| { - identity_iota::iota::Error::JsError(format!("get_alias_output failed to deserialize OutputId: {err}")) - })?; - let alias_dto: AliasOutputDto = iter - .next() - .ok_or_else(|| identity_iota::iota::Error::JsError("get_alias_output expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| { - identity_iota::iota::Error::JsError(format!("get_alias_output failed to deserialize AliasOutputDto: {err}")) - })?; - - let alias_output = AliasOutput::try_from_dto(alias_dto).map_err(|err| { - identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {err}")) - })?; - Ok((output_id, alias_output)) - } - - async fn get_protocol_parameters(&self) -> Result { - let promise: Promise = Promise::resolve(&WasmIotaIdentityClient::get_protocol_parameters(self)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let protocol_parameters: ProtocolParameters = result.to_iota_core_error().and_then(|parameters| { - parameters - .into_serde() - .map_err(|err| identity_iota::iota::Error::JsError(format!("could not obtain protocol parameters: {err}"))) - })?; - - Ok(protocol_parameters) - } -} - -#[wasm_bindgen(typescript_custom_section)] -const I_IOTA_IDENTITY_CLIENT: &'static str = r#" -import type { AliasOutput } from '~sdk-wasm'; -/** Helper interface necessary for `IotaIdentityClientExt`. */ -interface IIotaIdentityClient { - - /** Resolve an Alias identifier, returning its latest `OutputId` and `AliasOutput`. */ - getAliasOutput(aliasId: string): Promise<[string, AliasOutput]>; - - /** Returns the protocol parameters. */ - getProtocolParameters(): Promise; -}"#; diff --git a/bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs b/bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs deleted file mode 100644 index 2a9fbccb6b..0000000000 --- a/bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use iota_sdk::types::block::address::dto::AddressDto; -use iota_sdk::types::block::address::Address; -use iota_sdk::types::block::output::dto::AliasOutputDto; -use iota_sdk::types::block::output::AliasOutput; -use iota_sdk::types::block::output::RentStructure; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::error::Result; -use crate::error::WasmResult; -use crate::iota::identity_client::WasmIotaIdentityClient; -use crate::obsolete::IotaIdentityClientExt; - -use crate::iota::WasmIotaDID; -use crate::iota::WasmIotaDocument; - -// `IAliasOutput`, `AddressTypes`, and `IRent` are external interfaces. -// See the custom TypeScript section in `identity_client.rs` for the first import statement. -#[wasm_bindgen(typescript_custom_section)] -const TYPESCRIPT_IMPORTS: &'static str = - r#"import type { AliasOutputBuilderParams, Address, IRent } from '~sdk-wasm';"#; -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseAliasOutputBuilderParams; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseIotaDocument; - - #[wasm_bindgen(typescript_type = "Address")] - pub type WasmAddress; - - #[wasm_bindgen(typescript_type = "AliasOutputBuilderParams")] - pub type WasmAliasOutput; - - #[wasm_bindgen(typescript_type = "IRent")] - pub type IRent; -} - -/// An extension interface that provides helper functions for publication -/// and resolution of DID documents in Alias Outputs. -#[wasm_bindgen(js_name = IotaIdentityClientExt)] -pub struct WasmIotaIdentityClientExt; - -#[wasm_bindgen(js_class = IotaIdentityClientExt)] -impl WasmIotaIdentityClientExt { - /// Create a DID with a new Alias Output containing the given `document`. - /// - /// The `address` will be set as the state controller and governor unlock conditions. - /// The minimum required token deposit amount will be set according to the given - /// `rent_structure`, which will be fetched from the node if not provided. - /// The returned Alias Output can be further customised before publication, if desired. - /// - /// NOTE: this does *not* publish the Alias Output. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = newDidOutput)] - pub fn new_did_output( - client: WasmIotaIdentityClient, - address: WasmAddress, - document: &WasmIotaDocument, - rentStructure: Option, - ) -> Result { - let address_dto: AddressDto = address.into_serde().wasm_result()?; - let address: Address = Address::try_from(address_dto.clone()) - .map_err(|err| { - identity_iota::iota::Error::JsError(format!("newDidOutput failed to decode Address: {err}: {address_dto:?}")) - }) - .wasm_result()?; - let doc: IotaDocument = document.0.try_read()?.clone(); - - let promise: Promise = future_to_promise(async move { - let rent_structure: Option = rentStructure - .map(|rent| rent.into_serde::()) - .transpose() - .wasm_result()?; - - let output: AliasOutput = IotaIdentityClientExt::new_did_output(&client, address, doc, rent_structure) - .await - .wasm_result()?; - // Use DTO for correct serialization. - let dto: AliasOutputDto = AliasOutputDto::from(&output); - JsValue::from_serde(&dto).wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches the associated Alias Output and updates it with `document` in its state metadata. - /// The storage deposit on the output is left unchanged. If the size of the document increased, - /// the amount should be increased manually. - /// - /// NOTE: this does *not* publish the updated Alias Output. - #[wasm_bindgen(js_name = updateDidOutput)] - pub fn update_did_output( - client: WasmIotaIdentityClient, - document: &WasmIotaDocument, - ) -> Result { - let document: IotaDocument = document.0.try_read()?.clone(); - let promise: Promise = future_to_promise(async move { - let output: AliasOutput = IotaIdentityClientExt::update_did_output(&client, document) - .await - .wasm_result()?; - // Use DTO for correct serialization. - let dto: AliasOutputDto = AliasOutputDto::from(&output); - JsValue::from_serde(&dto).wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Removes the DID document from the state metadata of its Alias Output, - /// effectively deactivating it. The storage deposit on the output is left unchanged, - /// and should be reallocated manually. - /// - /// Deactivating does not destroy the output. Hence, it can be re-activated by publishing - /// an update containing a DID document. - /// - /// NOTE: this does *not* publish the updated Alias Output. - #[wasm_bindgen(js_name = deactivateDidOutput)] - pub fn deactivate_did_output( - client: WasmIotaIdentityClient, - did: &WasmIotaDID, - ) -> Result { - let did: IotaDID = did.0.clone(); - let promise: Promise = future_to_promise(async move { - let output: AliasOutput = IotaIdentityClientExt::deactivate_did_output(&client, &did) - .await - .wasm_result()?; - // Use DTO for correct serialization. - let dto: AliasOutputDto = AliasOutputDto::from(&output); - JsValue::from_serde(&dto).wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Resolve a {@link IotaDocument}. Returns an empty, deactivated document if the state metadata - /// of the Alias Output is empty. - #[wasm_bindgen(js_name = resolveDid)] - pub fn resolve_did(client: WasmIotaIdentityClient, did: &WasmIotaDID) -> Result { - let did: IotaDID = did.0.clone(); - let promise: Promise = future_to_promise(async move { - IotaIdentityClientExt::resolve_did(&client, &did) - .await - .map(WasmIotaDocument::from) - .map(Into::into) - .wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } - - /// Fetches the `IAliasOutput` associated with the given DID. - #[wasm_bindgen(js_name = resolveDidOutput)] - pub fn resolve_did_output( - client: WasmIotaIdentityClient, - did: &WasmIotaDID, - ) -> Result { - let did: IotaDID = did.0.clone(); - let promise: Promise = future_to_promise(async move { - let output: AliasOutput = IotaIdentityClientExt::resolve_did_output(&client, &did) - .await - .wasm_result()?; - // Use DTO for correct serialization. - let dto: AliasOutputDto = AliasOutputDto::from(&output); - JsValue::from_serde(&dto).wasm_result() - }); - - // WARNING: this does not validate the return type. Check carefully. - Ok(promise.unchecked_into::()) - } -} diff --git a/bindings/wasm/identity_wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs index 76ab563bec..44c586a858 100644 --- a/bindings/wasm/identity_wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -418,14 +418,14 @@ impl WasmIotaDocument { // Publishing // =========================================================================== - /// Serializes the document for inclusion in an Alias Output's state metadata + /// Serializes the document for inclusion in an identity's metadata /// with the default {@link StateMetadataEncoding}. #[wasm_bindgen] pub fn pack(&self) -> Result> { self.0.try_read()?.clone().pack().wasm_result() } - /// Serializes the document for inclusion in an Alias Output's state metadata. + /// Serializes the document for inclusion in an identity's metadata. #[wasm_bindgen(js_name = packWithEncoding)] pub fn pack_with_encoding(&self, encoding: WasmStateMetadataEncoding) -> Result> { self @@ -436,70 +436,6 @@ impl WasmIotaDocument { .wasm_result() } - // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] - // - // /// Deserializes the document from an Alias Output. - // /// - // /// If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` - // /// if `stateMetadata` is empty. - // /// - // /// The `tokenSupply` must be equal to the token supply of the network the DID is associated with. - // /// - // /// NOTE: `did` is required since it is omitted from the serialized DID Document and - // /// cannot be inferred from the state metadata. It also indicates the network, which is not - // /// encoded in the `AliasId` alone. - // #[allow(non_snake_case)] - // #[wasm_bindgen(js_name = unpackFromOutput)] - // pub fn unpack_from_output( - // did: &WasmIotaDID, - // aliasOutput: WasmAliasOutput, - // allowEmpty: bool, - // ) -> Result { - // let alias_dto: AliasOutputDto = aliasOutput.into_serde().wasm_result()?; - // let alias_output: AliasOutput = AliasOutput::try_from_dto(alias_dto) - // .map_err(|err| { - // identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {err}")) - // }) - // .wasm_result()?; - // IotaDocument::unpack_from_output(&did.0, &alias_output, allowEmpty) - // .map(WasmIotaDocument::from) - // .wasm_result() - // } - - // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] - // - // /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload - // /// outputs, if any. - // /// - // /// Errors if any Alias Output does not contain a valid or empty DID Document. - // #[allow(non_snake_case)] - // #[wasm_bindgen(js_name = unpackFromBlock)] - // pub fn unpack_from_block(network: String, block: &WasmBlock) -> Result { - // let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - // let block_dto: iota_sdk::types::block::BlockDto = block - // .into_serde() - // .map_err(|err| { - // identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {err}")) - // }) - // .wasm_result()?; - // - // let block: iota_sdk::types::block::Block = iota_sdk::types::block::Block::try_from_dto(block_dto) - // .map_err(|err| identity_iota::iota::Error::JsError( - // format!("unpackFromBlock failed to convert BlockDto: {err}") - // )) - // .wasm_result()?; - // - // Ok( - // IotaDocument::unpack_from_block(&network_name, &block) - // .wasm_result()? - // .into_iter() - // .map(WasmIotaDocument::from) - // .map(JsValue::from) - // .collect::() - // .unchecked_into::(), - // ) - // } - // =========================================================================== // Metadata // =========================================================================== @@ -1017,17 +953,9 @@ extern "C" { #[wasm_bindgen(typescript_type = "IotaDID[]")] pub type ArrayIotaDID; + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseIotaDocument; + #[wasm_bindgen(typescript_type = "IotaDocument[]")] pub type ArrayIotaDocument; - - // External interface from `@iota/sdk-wasm`, must be deserialized via BlockDto. - #[wasm_bindgen(typescript_type = "Block")] - pub type WasmBlock; - - // External interface from `@iota/sdk-wasm`, must be deserialized via ProtocolParameters. - #[wasm_bindgen(typescript_type = "INodeInfoProtocol")] - pub type INodeInfoProtocol; } - -#[wasm_bindgen(typescript_custom_section)] -const TYPESCRIPT_IMPORTS: &'static str = r#"import type { Block, INodeInfoProtocol } from '~sdk-wasm';"#; diff --git a/bindings/wasm/identity_wasm/src/iota/mod.rs b/bindings/wasm/identity_wasm/src/iota/mod.rs index fa68380d80..a8f882a79c 100644 --- a/bindings/wasm/identity_wasm/src/iota/mod.rs +++ b/bindings/wasm/identity_wasm/src/iota/mod.rs @@ -1,16 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub(crate) use identity_client::WasmIotaIdentityClient; -pub use identity_client_ext::PromiseIotaDocument; pub use iota_did::WasmIotaDID; pub(crate) use iota_document::IotaDocumentLock; +pub use iota_document::PromiseIotaDocument; pub use iota_document::WasmIotaDocument; pub use iota_document_metadata::WasmIotaDocumentMetadata; pub use iota_metadata_encoding::WasmStateMetadataEncoding; -mod identity_client; -mod identity_client_ext; mod iota_did; mod iota_document; mod iota_document_metadata; diff --git a/bindings/wasm/identity_wasm/src/lib.rs b/bindings/wasm/identity_wasm/src/lib.rs index a8116d0183..45657d7b9a 100644 --- a/bindings/wasm/identity_wasm/src/lib.rs +++ b/bindings/wasm/identity_wasm/src/lib.rs @@ -34,10 +34,6 @@ pub mod verification; #[cfg(feature = "dummy-client")] // Currently it's unclear if this module will be removed or can be used for integration or unit tests. pub(crate) mod rebased; -#[cfg(feature = "dummy-client")] -// Remove this module when working on [Issue #1445 Replace mocked Identity client with real Identity client] -pub(crate) mod obsolete; - /// Initializes the console error panic hook for better error messages #[wasm_bindgen(start)] pub fn start() -> Result<(), JsValue> { diff --git a/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs b/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs deleted file mode 100644 index 5a442c81f4..0000000000 --- a/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(feature = "test")] -use iota_sdk::client::Client; - -use identity_iota::iota::Error; -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use identity_iota::iota::NetworkName; -use identity_iota::iota::Result; -use iota_sdk::types::block::address::Address; -use iota_sdk::types::block::output::feature::SenderFeature; -use iota_sdk::types::block::output::unlock_condition::GovernorAddressUnlockCondition; -use iota_sdk::types::block::output::unlock_condition::StateControllerAddressUnlockCondition; -use iota_sdk::types::block::output::AliasId; -use iota_sdk::types::block::output::AliasOutput; -use iota_sdk::types::block::output::AliasOutputBuilder; -use iota_sdk::types::block::output::Feature; -use iota_sdk::types::block::output::OutputId; -use iota_sdk::types::block::output::RentStructure; -use iota_sdk::types::block::output::UnlockCondition; -use iota_sdk::types::block::protocol::ProtocolParameters; - -/// Helper functions necessary for the [`IotaIdentityClientExt`] trait. -#[async_trait::async_trait(?Send)] -pub trait IotaIdentityClient { - /// Resolve an Alias identifier, returning its latest [`OutputId`] and [`AliasOutput`]. - async fn get_alias_output(&self, alias_id: AliasId) -> Result<(OutputId, AliasOutput)>; - /// Get the protocol parameters of the node we are trying to connect to. - async fn get_protocol_parameters(&self) -> Result; -} - -/// An extension trait that provides helper functions for publication -/// and resolution of DID documents in Alias Outputs. -/// -/// This trait is not intended to be implemented directly, a blanket implementation is -/// provided for [`IotaIdentityClient`] implementers. -#[async_trait::async_trait(?Send)] -pub trait IotaIdentityClientExt: IotaIdentityClient { - /// Create a DID with a new Alias Output containing the given `document`. - /// - /// The `address` will be set as the state controller and governor unlock conditions. - /// The minimum required token deposit amount will be set according to the given - /// `rent_structure`, which will be fetched from the node if not provided. - /// The returned Alias Output can be further customised before publication, if desired. - /// - /// NOTE: This does *not* publish the Alias Output. - /// - /// # Errors - /// - /// - [`Error::DIDUpdateError`] when retrieving the `RentStructure` fails. - /// - [`Error::AliasOutputBuildError`] when building the Alias Output fails. - async fn new_did_output( - &self, - address: Address, - document: IotaDocument, - rent_structure: Option, - ) -> Result { - let rent_structure: RentStructure = if let Some(rent) = rent_structure { - rent - } else { - self.get_rent_structure().await? - }; - - AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null()) - .with_state_index(0) - .with_foundry_counter(0) - .with_state_metadata(document.pack()?) - .add_feature(Feature::Sender(SenderFeature::new(address))) - .add_unlock_condition(UnlockCondition::StateControllerAddress( - StateControllerAddressUnlockCondition::new(address), - )) - .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( - address, - ))) - .finish() - .map_err(|e| Error::DIDResolutionError(e.to_string())) - } - - /// Fetches the associated Alias Output and updates it with `document` in its state metadata. - /// The storage deposit on the output is left unchanged. If the size of the document increased, - /// the amount should be increased manually. - /// - /// NOTE: This does *not* publish the updated Alias Output. - /// - /// # Errors - /// - /// Returns `Err` when failing to resolve the DID contained in `document`. - async fn update_did_output(&self, document: IotaDocument) -> Result { - // let id: AliasId = AliasId::from(document.id()); - // let (_, alias_output) = self.get_alias_output(id).await?; - // - // let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) - // .with_state_index(alias_output.state_index() + 1) - // .with_state_metadata(document.pack()?); - // - // if alias_output.alias_id().is_null() { - // alias_output_builder = alias_output_builder.with_alias_id(id); - // } - // - // alias_output_builder.finish().map_err(|e| Error::DIDResolutionError(e.to_string())) - unimplemented!(); - } - - /// Removes the DID document from the state metadata of its Alias Output, - /// effectively deactivating it. The storage deposit on the output is left unchanged, - /// and should be reallocated manually. - /// - /// Deactivating does not destroy the output. Hence, it can be re-activated by publishing - /// an update containing a DID document. - /// - /// NOTE: this does *not* publish the updated Alias Output. - /// - /// # Errors - /// - /// Returns `Err` when failing to resolve the `did`. - async fn deactivate_did_output(&self, did: &IotaDID) -> Result { - // let alias_id: AliasId = AliasId::from(did); - // let (_, alias_output) = self.get_alias_output(alias_id).await?; - // - // let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) - // .with_state_index(alias_output.state_index() + 1) - // .with_state_metadata(Vec::new()); - // - // if alias_output.alias_id().is_null() { - // alias_output_builder = alias_output_builder.with_alias_id(alias_id); - // } - // - // alias_output_builder.finish().map_err(|e| Error::DIDResolutionError(e.to_string())) - unimplemented!(); - } - - /// Resolve a [`IotaDocument`]. Returns an empty, deactivated document if the state metadata - /// of the Alias Output is empty. - /// - /// # Errors - /// - /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. - /// - [`NotFound`](iota_sdk::client::Error::NoOutput) if the associated Alias Output was not found. - async fn resolve_did(&self, did: &IotaDID) -> Result { - // validate_network(self, did).await?; - // - // let id: AliasId = AliasId::from(did); - // let (_, alias_output) = self.get_alias_output(id).await?; - // IotaDocument::unpack_from_output(did, &alias_output, true) - unimplemented!(); - } - - /// Fetches the [`AliasOutput`] associated with the given DID. - /// - /// # Errors - /// - /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. - /// - [`NotFound`](iota_sdk::client::Error::NoOutput) if the associated Alias Output was not found. - async fn resolve_did_output(&self, did: &IotaDID) -> Result { - // validate_network(self, did).await?; - // - // let id: AliasId = AliasId::from(did); - // self.get_alias_output(id).await.map(|(_, alias_output)| alias_output) - unimplemented!(); - } - - /// Returns the network name of the client, which is the - /// Bech32 human-readable part (HRP) of the network. - /// - /// E.g. "iota", "atoi", "smr", "rms". - async fn network_name(&self) -> Result { - self.get_network_hrp().await.and_then(NetworkName::try_from) - } - - /// Return the rent structure of the network, indicating the byte costs for outputs. - async fn get_rent_structure(&self) -> Result { - self - .get_protocol_parameters() - .await - .map(|parameters| *parameters.rent_structure()) - } - - /// Gets the token supply of the node we're connecting to. - async fn get_token_supply(&self) -> Result { - self - .get_protocol_parameters() - .await - .map(|parameters| parameters.token_supply()) - } - - /// Return the Bech32 human-readable part (HRP) of the network. - /// - /// E.g. "iota", "atoi", "smr", "rms". - async fn get_network_hrp(&self) -> Result { - self - .get_protocol_parameters() - .await - .map(|parameters| parameters.bech32_hrp().to_string()) - } -} - -#[cfg(not(feature = "test"))] -impl IotaIdentityClientExt for T where T: IotaIdentityClient {} -#[cfg(feature = "test")] -impl IotaIdentityClientExt for Client {} - -pub(super) async fn validate_network(client: &T, did: &IotaDID) -> Result<()> -where - T: IotaIdentityClient + ?Sized, -{ - let network_hrp: String = client - .get_protocol_parameters() - .await - .map(|parameters| parameters.bech32_hrp().to_string())?; - if did.network_str() != network_hrp.as_str() { - return Err(Error::NetworkMismatch { - expected: did.network_str().to_owned(), - actual: network_hrp, - }); - }; - Ok(()) -} diff --git a/bindings/wasm/identity_wasm/src/obsolete/mod.rs b/bindings/wasm/identity_wasm/src/obsolete/mod.rs deleted file mode 100644 index cef8d4bc70..0000000000 --- a/bindings/wasm/identity_wasm/src/obsolete/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub(crate) mod identity_client; - -pub(crate) use identity_client::IotaIdentityClient; -pub(crate) use identity_client::IotaIdentityClientExt; diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json index 08004d04ef..fc3c169b56 100644 --- a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json @@ -7,10 +7,6 @@ "../node/identity_wasm", "./identity_wasm.js" ], - "~sdk-wasm": [ - "../node_modules/@iota/sdk-wasm/node", - "@iota/sdk-wasm/node" - ], "../lib": [ "." ] diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json index 203545f4f0..27b8c4906c 100644 --- a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json @@ -8,10 +8,6 @@ "../web/identity_wasm", "./identity_wasm.js" ], - "~sdk-wasm": [ - "../node_modules/@iota/sdk-wasm/web", - "@iota/sdk-wasm/web" - ], "../lib": [ "." ] diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json index 2c98769876..13375b0396 100644 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -41,8 +41,7 @@ "node": ">=16" }, "peerDependencies": { - "@iota/iota-sdk": "^0.5.0", - "@iota/sdk-wasm": "^1.0.4" + "@iota/iota-sdk": "^0.5.0" } }, "node_modules/@0no-co/graphql.web": { @@ -341,27 +340,6 @@ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "license": "Unlicense" }, - "node_modules/@iota/sdk-wasm": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", - "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "class-transformer": "^0.5.1", - "node-fetch": "^2.6.7", - "qs": "^6.9.7", - "reflect-metadata": "^0.1.13", - "semver": "^7.5.2", - "text-encoding": "^0.7.0" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -1728,6 +1706,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -1921,13 +1900,6 @@ "node": ">=8" } }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT", - "peer": true - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -2490,6 +2462,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -2682,6 +2655,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" @@ -2694,6 +2668,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3160,6 +3135,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3174,6 +3150,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3193,6 +3170,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3338,6 +3316,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3427,6 +3406,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -3439,6 +3419,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz", "integrity": "sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7" @@ -3454,6 +3435,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3466,6 +3448,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5344,6 +5327,7 @@ "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5599,22 +5583,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", - "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5732,13 +5700,6 @@ "node": ">=0.10.0" } }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0", - "peer": true - }, "node_modules/remark-parse": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", @@ -5922,6 +5883,7 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5951,6 +5913,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -6024,6 +5987,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -6443,14 +6407,6 @@ "node": ">=4" } }, - "node_modules/text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained", - "license": "(Unlicense OR Apache-2.0)", - "peer": true - }, "node_modules/throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index de0c61e079..2d94cf81a4 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -81,7 +81,6 @@ "node-fetch": "^2.6.7" }, "peerDependencies": { - "@iota/sdk-wasm": "^1.0.4", "@iota/iota-sdk": "^0.5.0" }, "engines": { From 2ee5e5a7370cb7a5d3fea50cf9f3a2da7be7e95f Mon Sep 17 00:00:00 2001 From: umr1352 Date: Fri, 21 Feb 2025 17:02:11 +0100 Subject: [PATCH 43/63] wasm keytoolsigner --- bindings/wasm/identity_wasm/Cargo.toml | 2 +- .../wasm/identity_wasm/examples/src/util.ts | 2 + .../src/rebased/wasm_identity_client.rs | 20 +-- .../src/storage/wasm_storage_signer.rs | 20 +-- .../src/storage/wasm_transaction_signer.rs | 23 ++-- .../lib/iota_client_helpers.ts | 44 ++----- .../src/bindings/keytool_signer.rs | 36 ++--- .../src/bindings/wasm_types.rs | 124 ++++++++++++++---- .../src/iota_client_ts_sdk.rs | 82 +++++------- bindings/wasm/iota_interaction_ts/src/lib.rs | 5 + .../src/rebased/client/full_client.rs | 2 +- .../src/sdk_types/iota_types/crypto.rs | 21 +++ 12 files changed, 208 insertions(+), 173 deletions(-) diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 144e6312f2..4d56de184c 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -27,7 +27,7 @@ iota-sdk = { version = "1.1.5", default-features = false, features = ["serde", " js-sys = { version = "0.3.61" } json-proof-token = "0.3.4" proc_typescript = { version = "0.1.0", path = "./proc_typescript" } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.2.0" } serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = "0.6.5" serde_json = { version = "1.0", default-features = false } diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index 3929853684..cd48ac63b8 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -8,6 +8,7 @@ import { JwkMemStore, JwsAlgorithm, KeyIdMemStore, + KeytoolSigner, MethodScope, Storage, StorageSigner, @@ -54,6 +55,7 @@ export async function getFundedClient(storage: Storage): Promise // generate new key let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); + let publicKeyJwk = generate.jwk().toPublic(); if (typeof publicKeyJwk === "undefined") { throw new Error("failed to derive public JWK from generated JWK"); diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index 082702bb90..ee60e4bbb0 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -15,6 +15,7 @@ use identity_iota::iota_interaction::types::base_types::IotaAddress; use iota_interaction_ts::bindings::WasmExecutionStatus; use iota_interaction_ts::bindings::WasmOwnedObjectRef; +use iota_interaction_ts::WasmPublicKey; use identity_iota::iota::rebased::Error; use iota_interaction_ts::AdapterNativeResponse; @@ -64,8 +65,8 @@ impl WasmIdentityClient { } #[wasm_bindgen(js_name = senderPublicKey)] - pub fn sender_public_key(&self) -> Vec { - self.0.sender_public_key().to_vec() + pub fn sender_public_key(&self) -> Result { + self.0.sender_public_key().try_into() } #[wasm_bindgen(js_name = senderAddress)] @@ -149,21 +150,6 @@ impl WasmIdentityClient { } } -// TODO: consider importing function from rebased later on, if possible -pub fn convert_to_address(sender_public_key: &[u8]) -> Result { - let public_key = Ed25519PublicKey::from_bytes(sender_public_key) - .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; - - Ok(IotaAddress::from(&public_key)) -} - -#[wasm_bindgen(js_name = convertToAddress)] -pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { - convert_to_address(sender_public_key) - .map(|v| v.to_string()) - .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) -} - // TODO: rethink how to organize the following types and impls #[wasm_bindgen(js_name = PublishDidTx)] pub struct WasmPublishDidTx(pub(crate) PublishDidTx); diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index 2bdabc2e8e..2adbd1b4f9 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -5,6 +5,8 @@ use identity_iota::storage::KeyId; use identity_iota::storage::StorageSigner; use identity_iota::verification::jwk::JwkParams; use identity_iota::verification::jwu; +use iota_interaction_ts::WasmIotaSignature; +use iota_interaction_ts::WasmPublicKey; use secret_storage::Signer; use wasm_bindgen::prelude::*; @@ -45,18 +47,16 @@ impl WasmStorageSigner { } #[wasm_bindgen(js_name = sign)] - pub async fn sign(&self, data: &[u8]) -> Result> { - self.signer().sign(data).await.wasm_result() + pub async fn sign(&self, data: Vec) -> Result { + let sig = self.signer().sign(&data).await.wasm_result()?; + sig.try_into() } #[wasm_bindgen(js_name = publicKey)] - pub async fn public_key(&self) -> Result> { - let jwk = &self.public_key.0; - - match jwk.params() { - JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) - .map_err(|e| JsValue::from_str(&format!("could not base64 decode key {}; {e}", &self.key_id))), - _ => todo!("add support for other key types"), - } + pub async fn public_key(&self) -> Result { + Signer::public_key(&self.signer()) + .await + .wasm_result() + .and_then(|pk| WasmPublicKey::try_from(&pk)) } } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index 1774ca0723..89765fbb70 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -3,6 +3,10 @@ use async_trait::async_trait; use identity_iota::iota::rebased::client::IotaKeySignature; +use identity_iota::iota_interaction::types::crypto::PublicKey; +use identity_iota::iota_interaction::types::crypto::Signature; +use iota_interaction_ts::WasmIotaSignature; +use iota_interaction_ts::WasmPublicKey; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use wasm_bindgen::prelude::wasm_bindgen; @@ -11,9 +15,12 @@ use crate::error::Result; #[wasm_bindgen(typescript_custom_section)] const I_TX_SIGNER: &str = r#" +import { PublicKey } from "@iota/iota-sdk/cryptography"; +import { Signature } from "@iota/iota-sdk/client"; + interface TransactionSigner { - sign: (data: Uint8Array) => Promise; - publicKey: () => Promise; + sign: (data: Uint8Array) => Promise; + publicKey: () => Promise; keyId: () => string; } "#; @@ -24,9 +31,9 @@ extern "C" { pub type WasmTransactionSigner; #[wasm_bindgen(method, catch)] - pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; + pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; #[wasm_bindgen(js_name = "publicKey", method, catch)] - pub async fn public_key(this: &WasmTransactionSigner) -> Result; + pub async fn public_key(this: &WasmTransactionSigner) -> Result; // TODO: re-add WasmTransactionSigner::key_id // #[wasm_bindgen(js_name = "keyId", structural, method)] @@ -38,16 +45,16 @@ extern "C" { impl Signer for WasmTransactionSigner { type KeyId = String; - async fn sign(&self, data: &[u8]) -> std::result::Result, SecretStorageError> { - self.sign(data).await.map(|v| v.to_vec()).map_err(|err| { + async fn sign(&self, data: &Vec) -> std::result::Result { + self.sign(data).await.and_then(|v| v.try_into()).map_err(|err| { let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); let message = format!("could not sign data{details}"); SecretStorageError::Other(anyhow::anyhow!(message)) }) } - async fn public_key(&self) -> std::result::Result, SecretStorageError> { - self.public_key().await.map(|v| v.to_vec()).map_err(|err| { + async fn public_key(&self) -> std::result::Result { + self.public_key().await.and_then(|v| v.try_into()).map_err(|err| { let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); let message = format!("could not get public key{details}"); SecretStorageError::KeyNotFound(message) diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index a53f75ef9b..3c9e7ab8bb 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -7,13 +7,14 @@ import { IotaClient, IotaTransactionBlockResponse, OwnedObjectRef, + Signature, } from "@iota/iota-sdk/client"; -import { messageWithIntent, toSerializedSignature } from "@iota/iota-sdk/cryptography"; +import { messageWithIntent, PublicKey, toSerializedSignature } from "@iota/iota-sdk/cryptography"; import { Ed25519PublicKey } from "@iota/iota-sdk/keypairs/ed25519"; import { GasData, TransactionDataBuilder } from "@iota/iota-sdk/transactions"; import { blake2b } from "@noble/hashes/blake2b"; -export type Signer = { sign(data: Uint8Array): Promise }; +export type Signer = { sign(data: Uint8Array): Promise }; export class IotaTransactionBlockResponseAdapter { response: IotaTransactionBlockResponse; @@ -49,34 +50,6 @@ export class IotaTransactionBlockResponseAdapter { } } -/** - * Builds message with `TransactionData` intent and returns hash of it. - * - * @param txBcs transaction data to hash - * @returns digest/hash of intent message for transaction data - */ -export function getTransactionDigest(txBcs: Uint8Array): Uint8Array { - const intent = "TransactionData"; - const intentMessage = messageWithIntent(intent, txBcs); - return blake2b(intentMessage, { dkLen: 32 }); -} - -async function signTransactionData( - txBcs: Uint8Array, - senderPublicKey: Uint8Array, - signer: { sign(data: Uint8Array): Promise }, -): Promise { - const digest = getTransactionDigest(txBcs); - const signerSignature = await signer.sign(digest); - const signature = toSerializedSignature({ - signature: await signerSignature, - signatureScheme: "ED25519", - publicKey: new Ed25519PublicKey(senderPublicKey), - }); - - return signature; -} - async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { const coins = await iotaClient.getCoins({ owner: senderAddress }); if (coins.data.length === 0) { @@ -160,18 +133,19 @@ export async function addGasDataToTransaction( export async function executeTransaction( iotaClient: IotaClient, senderAddress: string, - senderPublicKey: Uint8Array, txBcs: Uint8Array, signer: Signer, gasBudget?: bigint, ): Promise { const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); - const signature = await signTransactionData(txWithGasData, senderPublicKey, signer); - console.log(signature); + const signature = await signer.sign(txWithGasData) as any; + const base64signature = (signature.Ed25519IotaSignature + || signature.Secp256r1IotaSignature + || signature.Secp256k1IotaSignature) as string; const response = await iotaClient.executeTransactionBlock({ transactionBlock: txWithGasData, - signature, + signature: base64signature, options: { // equivalent of `IotaTransactionBlockResponseOptions::full_content()` showEffects: true, showInput: true, @@ -192,8 +166,6 @@ export async function executeTransaction( /** * Helper function to pause execution. - * - * @param txBcs transaction data to hash */ export function sleep(durationMs: number) { return new Promise(resolve => setTimeout(resolve, durationMs)); diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index 5694af0300..3372ef7554 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -15,8 +15,12 @@ use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use serde_json::Value; use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsError; use wasm_bindgen::JsValue; +use super::WasmIotaSignature; +use super::WasmPublicKey; + #[wasm_bindgen(module = buffer)] extern "C" { #[wasm_bindgen(typescript_type = Buffer)] @@ -70,34 +74,18 @@ impl WasmKeytoolSigner { } #[wasm_bindgen(js_name = publicKey)] - pub async fn public_key(&self) -> Vec { - self.0.public_key().as_ref().to_owned() + pub async fn public_key(&self) -> Result { + self.0.public_key().try_into() } #[wasm_bindgen] - pub async fn sign(&self, data: Vec) -> Result> { - Signer::sign(self, &data) + pub async fn sign(&self, data: Vec) -> Result { + self + .0 + .sign(&data) .await - .map_err(|e| anyhow::Error::from(e)) - .map(|signature| signature.as_ref().to_owned()) - .wasm_result() - } -} - -#[async_trait(?Send)] -impl Signer for WasmKeytoolSigner { - type KeyId = IotaAddress; - - fn key_id(&self) -> &Self::KeyId { - self.0.key_id() - } - - async fn public_key(&self) -> std::result::Result { - Ok(self.0.public_key().clone()) - } - - async fn sign(&self, data: &TransactionDataBcs) -> std::result::Result { - self.0.sign(data).await + .map_err(|e| JsError::new(&e.to_string()).into()) + .and_then(|sig| sig.try_into()) } } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index 9782d98923..b37699445f 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -1,21 +1,25 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use fastcrypto::traits::EncodeDecodeBase64; 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::crypto::PublicKey; +use identity_iota_interaction::types::crypto::Signature; 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::ProgrammableTransactionBcs; use js_sys::Promise; use js_sys::Uint8Array; -use serde::Serialize; use serde::Deserialize; +use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; +use wasm_bindgen::JsError; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; @@ -25,6 +29,7 @@ use crate::common::PromiseUint8Array; use crate::console_log; use crate::error::TsSdkError; use crate::error::WasmError; +use crate::error::WasmResult; // TODO: fix/add signer or remove functions relying on it type WasmStorageSigner = (); @@ -151,6 +156,24 @@ extern "C" { #[wasm_bindgen(typescript_type = "Promise")] #[derive(Clone)] pub type PromiseIotaTransactionBlockResponseAdapter; + + #[wasm_bindgen(typescript_type = "Signature")] + pub type WasmIotaSignature; +} + +impl TryFrom for WasmIotaSignature { + type Error = JsValue; + fn try_from(sig: Signature) -> Result { + let js_value = serde_wasm_bindgen::to_value(&sig)?; + js_value.dyn_into() + } +} + +impl TryFrom for Signature { + type Error = JsValue; + fn try_from(sig: WasmIotaSignature) -> Result { + serde_wasm_bindgen::from_value(sig.into()).wasm_result() + } } #[wasm_bindgen(module = "@iota/iota-sdk/transactions")] @@ -185,6 +208,62 @@ extern "C" { // pub fn get_data(this: &WasmTransactionBuilder) -> Result; } +#[wasm_bindgen(module = "@iota/iota-sdk/cryptography")] +extern "C" { + #[wasm_bindgen(typescript_type = PublicKey)] + pub type WasmPublicKey; + + #[wasm_bindgen(js_name = toIotaPublicKey, method)] + pub fn to_iota_public_key(this: &WasmPublicKey) -> String; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/ed25519")] +extern "C" { + pub type Ed25519PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_ed25519_pk(bytes: &[u8]) -> Result; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256r1")] +extern "C" { + pub type Secp256r1PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_secp256r1_pk(bytes: &[u8]) -> Result; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256k1")] +extern "C" { + pub type Secp256k1PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_secp256k1_pk(bytes: &[u8]) -> Result; +} + +impl TryFrom<&'_ PublicKey> for WasmPublicKey { + type Error = JsValue; + fn try_from(pk: &PublicKey) -> Result { + let pk_bytes = pk.as_ref(); + let wasm_pk = match pk { + PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.unchecked_into(), + PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.unchecked_into(), + PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.unchecked_into(), + _ => return Err(JsError::new("unsupported PublicKey type").into()), + }; + + Ok(wasm_pk) + } +} + +impl TryFrom for PublicKey { + type Error = JsValue; + fn try_from(pk: WasmPublicKey) -> Result { + let base64_pk = pk.to_iota_public_key(); + PublicKey::decode_base64(&base64_pk).map_err(|e| JsError::new(&e.to_string()).into()) + } +} + impl From for WasmObjectRef { fn from(value: ObjectRef) -> Self { let json_obj = serde_json::json!({ @@ -193,7 +272,8 @@ impl From for WasmObjectRef { "digest": value.2, }); - json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS ObjectRef's interface. .unchecked_into() @@ -208,7 +288,8 @@ impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { "mutable": value.2, }); - json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS SharedObjectRef's interface. .unchecked_into() @@ -268,51 +349,42 @@ extern "C" { fn execute_transaction_inner( iota_client: &WasmIotaClient, // --> TypeScript: IotaClient sender_address: String, // --> TypeScript: string - sender_public_key: Vec, // --> TypeScript: Uint8Array tx_bcs: Vec, // --> TypeScript: Uint8Array, signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) gas_budget: Option, // --> TypeScript: optional bigint ) -> PromiseIotaTransactionBlockResponseAdapter; - #[wasm_bindgen(js_name = "getTransactionDigest")] - fn get_transaction_digest_inner(txBcs: Uint8Array) -> Uint8Array; - #[wasm_bindgen(js_name = "addGasDataToTransaction")] fn add_gas_data_to_transaction_inner( - iota_client: &WasmIotaClient, // --> TypeScript: IotaClient - sender_address: String, // --> TypeScript: string - tx_bcs: Vec, // --> TypeScript: Uint8Array, - gas_budget: Option, // --> TypeScript: optional bigint -) -> PromiseUint8Array; + iota_client: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + tx_bcs: Vec, // --> TypeScript: Uint8Array, + gas_budget: Option, // --> TypeScript: optional bigint + ) -> PromiseUint8Array; #[wasm_bindgen(js_name = "sleep")] fn sleep_inner(ms: i32) -> Promise; } -/// Builds message with `TransactionData` intent and returns hash of it. -pub(crate) fn get_transaction_digest(tx_data: &[u8]) -> Vec { - get_transaction_digest_inner(Uint8Array::from(tx_data)).to_vec() -} - /// Inserts these values into the transaction and replaces placeholder values. -/// +/// /// - sender (overwritten as we assume a placeholder to be used in prepared transaction) /// - gas budget (value determined automatically if not provided) /// - gas price (value determined automatically) /// - gas coin / payment object (fetched automatically) /// - gas owner (equals sender) -/// +/// /// # Arguments -/// +/// /// * `iota_client` - client instance /// * `sender_address` - transaction sender (and the one paying for it) -/// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values +/// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values /// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided pub(crate) async fn add_gas_data_to_transaction( - iota_client: &WasmIotaClient, - sender_address: IotaAddress, - tx_bcs: Vec, - gas_budget: Option, + iota_client: &WasmIotaClient, + sender_address: IotaAddress, + tx_bcs: Vec, + gas_budget: Option, ) -> Result, TsSdkError> { let promise: Promise = Promise::resolve(&add_gas_data_to_transaction_inner( iota_client, @@ -367,7 +439,6 @@ impl IotaTransactionBlockResponseAdapter { pub async fn execute_transaction( iota_client: &WasmIotaClient, // --> Binding: WasmIotaClient sender_address: IotaAddress, // --> Binding: String - sender_public_key: &[u8], // --> Binding: Vec tx_bcs: ProgrammableTransactionBcs, // --> Binding: Vec signer: WasmStorageSigner, // --> Binding: WasmStorageSigner gas_budget: Option, // --> Binding: Option, @@ -375,7 +446,6 @@ pub async fn execute_transaction( let promise: Promise = Promise::resolve(&execute_transaction_inner( iota_client, sender_address.to_string(), - sender_public_key.to_vec(), tx_bcs, signer, gas_budget, diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index b3291d970d..6b7a182c76 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -6,8 +6,10 @@ use std::option::Option; use std::result::Result; use fastcrypto::hash::Blake2b256; +use fastcrypto::traits::ToFromBytes; use identity_iota_interaction::shared_crypto::intent::Intent; use identity_iota_interaction::shared_crypto::intent::IntentMessage; +use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::crypto::SignatureScheme; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; @@ -44,7 +46,6 @@ use identity_iota_interaction::SignatureBcs; use identity_iota_interaction::TransactionDataBcs; use crate::bindings::add_gas_data_to_transaction; -use crate::bindings::get_transaction_digest; use crate::bindings::sleep; use crate::bindings::IotaTransactionBlockResponseAdapter; use crate::bindings::ManagedWasmIotaClient; @@ -337,19 +338,17 @@ impl IotaClientTrait for IotaClientTsSdk { tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, - ) -> Result>, Self::Error> { + ) -> Result< + Box>, + Self::Error, + > { let tx: ProgrammableTransaction = tx_bcs.try_into()?; - let response = self - .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) - .await?; + let response = self.sdk_execute_transaction(tx, gas_budget, signer).await?; // wait a certain amount to time before continuing // a follow up step was fetching an object created with this tx, which - for some reason - wasn't available yet // TODO: check timing issues related to transactions finality here - sleep(500) - .await - .map_err(WasmError::from) - .map_err(TsSdkError::from)?; + sleep(500).await.map_err(WasmError::from).map_err(TsSdkError::from)?; Ok(Box::new(response)) } @@ -391,43 +390,30 @@ impl IotaClientTsSdk { } /// Builds message with `TransactionData` intent, hashes it, and constructs full signature. - async fn get_transaction_signature_bytes>( - signer: &S, - tx_data: &Vec, - sender_public_key: &[u8], - ) -> Result, TsSdkError> { - let digest = get_transaction_digest(tx_data); - - let raw_signature = signer - .sign(&digest.to_vec()) + async fn get_transaction_signature_bytes(signer: &S, tx_data: &[u8]) -> Result, TsSdkError> + where + S: Signer, + { + signer + .sign(&tx_data.to_vec()) .await - .map_err(|err| - TsSdkError::TransactionSerializationError(format!("could not sign transaction message; {err}")) - )?; - - let signature_bytes = [ - [SignatureScheme::ED25519.flag()].as_slice(), - &raw_signature, - sender_public_key, - ] - .concat(); - - Ok(signature_bytes) + .map(|sig| sig.as_bytes().to_vec()) + .map_err(|err| TsSdkError::TransactionSerializationError(format!("could not sign transaction message; {err}"))) } /// Inserts these values into the transaction and replaces placeholder values. - /// + /// /// - sender (overwritten as we assume a placeholder to be used in prepared transaction) /// - gas budget (value determined automatically if not provided) /// - gas price (value determined automatically) /// - gas coin / payment object (fetched automatically) /// - gas owner (equals sender) - /// + /// /// # Arguments - /// + /// /// * `iota_client` - client instance /// * `sender_address` - transaction sender (and the one paying for it) - /// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values + /// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values /// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided async fn replace_transaction_placeholder_values( &self, @@ -435,16 +421,8 @@ impl IotaClientTsSdk { sender_address: IotaAddress, gas_budget: Option, ) -> Result, TsSdkError> { - let tx_bcs = tx.0.build() - .await - .map_err(WasmError::from)? - .to_vec(); - let updated = add_gas_data_to_transaction( - &self.iota_client.0, - sender_address, - tx_bcs, - gas_budget, - ).await?; + let tx_bcs = tx.0.build().await.map_err(WasmError::from)?.to_vec(); + let updated = add_gas_data_to_transaction(&self.iota_client.0, sender_address, tx_bcs, gas_budget).await?; Ok(updated) } @@ -455,14 +433,19 @@ impl IotaClientTsSdk { // - calls execute_transaction_block to submit tx (with signatures created here) async fn sdk_execute_transaction>( &self, - sender_address: IotaAddress, - sender_public_key: &[u8], tx: ProgrammableTransaction, gas_budget: Option, signer: &S, ) -> Result { - let final_tx = self.replace_transaction_placeholder_values(tx, sender_address, gas_budget).await?; - let signature = Self::get_transaction_signature_bytes(signer, &final_tx, sender_public_key).await?; + let sender_public_key = signer + .public_key() + .await + .map_err(|e| TsSdkError::WasmError(String::from("SecretStorage"), e.to_string()))?; + let sender_address = IotaAddress::from(&sender_public_key); + let final_tx = self + .replace_transaction_placeholder_values(tx, sender_address, gas_budget) + .await?; + let signature = Self::get_transaction_signature_bytes(signer, &final_tx).await?; let wasm_response = self .quorum_driver_api() @@ -472,7 +455,8 @@ impl IotaClientTsSdk { Some(IotaTransactionBlockResponseOptions::full_content()), Some(ExecuteTransactionRequestType::WaitForLocalExecution), ) - .await.unwrap(); + .await + .unwrap(); let native = wasm_response.clone_native_response(); Ok(IotaTransactionBlockResponseProvider::new(native)) diff --git a/bindings/wasm/iota_interaction_ts/src/lib.rs b/bindings/wasm/iota_interaction_ts/src/lib.rs index 5bb979f78c..ce41edbadb 100644 --- a/bindings/wasm/iota_interaction_ts/src/lib.rs +++ b/bindings/wasm/iota_interaction_ts/src/lib.rs @@ -50,6 +50,11 @@ 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 bindings::WasmPublicKey; + #[allow(unused_imports)] pub use bindings::Ed25519PublicKey as WasmEd25519PublicKey; + #[allow(unused_imports)] pub use bindings::Secp256r1PublicKey as WasmSecp256r1PublicKey; + #[allow(unused_imports)] pub use bindings::Secp256k1PublicKey as WasmSecp256k1PublicKey; + #[allow(unused_imports)] pub use bindings::WasmIotaSignature; #[cfg(feature = "keytool-signer")] pub use bindings::WasmKeytoolSigner; diff --git a/identity_iota_core/src/rebased/client/full_client.rs b/identity_iota_core/src/rebased/client/full_client.rs index 416a09d189..90c125c862 100644 --- a/identity_iota_core/src/rebased/client/full_client.rs +++ b/identity_iota_core/src/rebased/client/full_client.rs @@ -12,8 +12,8 @@ use identity_iota_interaction::rpc_types::IotaObjectDataFilter; use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::crypto::PublicKey; use identity_verification::jwk::Jwk; -use iota_sdk::types::crypto::PublicKey; use secret_storage::Signer; use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs index 623c36176c..f8f2e46606 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs @@ -145,6 +145,27 @@ impl PublicKey { PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator, } } + + pub fn try_from_bytes( + curve: SignatureScheme, + key_bytes: &[u8], + ) -> Result { + match curve { + SignatureScheme::ED25519 => Ok(PublicKey::Ed25519( + (&Ed25519PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1( + (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1( + (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey( + (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), + )), + _ => Err(eyre::eyre!("Unsupported curve")), + } + } } pub trait IotaPublicKey: VerifyingKey { From 4ead237d19e958cf50b844950259e2a531b6b3ea Mon Sep 17 00:00:00 2001 From: wulfraem Date: Fri, 21 Feb 2025 17:35:54 +0100 Subject: [PATCH 44/63] Fix format issues (#1544) --- bindings/wasm/identity_wasm/Cargo.toml | 4 + .../identity_wasm/src/iota/iota_document.rs | 1 - .../src/rebased/client_dummy/identity.rs | 125 +----------------- .../src/rebased/client_dummy/mod.rs | 1 - .../rebased/client_dummy/multicontroller.rs | 2 + .../identity_wasm/src/rebased/identity.rs | 1 + .../wasm/identity_wasm/src/rebased/mod.rs | 4 +- .../src/rebased/multicontroller.rs | 3 - .../src/rebased/proposals/config_change.rs | 6 +- .../src/rebased/proposals/mod.rs | 22 +-- .../wasm/identity_wasm/src/rebased/types.rs | 3 +- .../src/rebased/wasm_identity_client.rs | 7 +- .../rebased/wasm_identity_client_read_only.rs | 8 +- bindings/wasm/iota_interaction_ts/Cargo.toml | 4 + .../lib/iota_client_helpers.ts | 6 +- .../lib/move_calls/identity/update.ts | 7 +- .../lib/move_calls/utils.ts | 6 +- .../src/asset_move_calls.rs | 12 +- .../iota_interaction_ts/src/bindings/mod.rs | 1 - .../iota_interaction_ts/src/bindings/types.rs | 3 - .../src/bindings/wasm_iota_client.rs | 14 +- .../src/bindings/wasm_types.rs | 34 ++--- .../iota_interaction_ts/src/common/types.rs | 1 + .../iota_interaction_ts/src/common/utils.rs | 1 - .../src/identity_move_calls.rs | 17 ++- .../src/iota_client_ts_sdk.rs | 53 +++----- .../src/migration_move_calls.rs | 1 - .../identity_move_calls.rs | 5 +- .../iota_client_rust_sdk.rs | 2 +- .../src/rebased/client/read_only.rs | 2 - .../src/rebased/migration/alias.rs | 1 + identity_iota_core/src/rebased/mod.rs | 1 + .../src/rebased/proposals/borrow.rs | 1 - .../src/rebased/proposals/mod.rs | 2 +- identity_iota_core/src/rebased/transaction.rs | 15 ++- identity_iota_core/src/rebased/utils.rs | 22 ++- .../src/state_metadata/document.rs | 5 +- identity_iota_interaction/Cargo.toml | 8 +- .../src/move_call_traits.rs | 6 +- .../src/sdk_types/iota_types/iota_serde.rs | 3 + .../sdk_types/iota_types/iota_types_lib.rs | 1 + .../sdk_types/iota_types/timelock/timelock.rs | 4 + 42 files changed, 165 insertions(+), 260 deletions(-) diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 144e6312f2..75e121ce62 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -73,6 +73,10 @@ lto = true # see https://github.com/rust-lang/rust-clippy/issues/12377 empty_docs = "allow" +[lints.rust] +# required for current wasm_bindgen version +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] } + [features] default = ["dummy-client"] dummy-client = ["dep:iota_interaction_ts"] diff --git a/bindings/wasm/identity_wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs index 44c586a858..842b46d500 100644 --- a/bindings/wasm/identity_wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -25,7 +25,6 @@ use identity_iota::storage::storage::JwsSignatureOptions; use identity_iota::verification::jose::jws::JwsAlgorithm; use identity_iota::verification::MethodScope; use identity_iota::verification::VerificationMethod; -use iota_sdk::types::TryFromDto; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs index ce65f3a663..ab5d6cd01f 100644 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs @@ -6,147 +6,24 @@ // The file provides a POC for the wasm-bindgen glue code needed to // implement the TS-Client-SDK integration. -use std::collections::HashMap; -use std::str::FromStr; - use serde; use serde::Deserialize; use serde::Serialize; -use super::DummySigner; use super::Multicontroller; -use super::Proposal; -use identity_iota::iota::rebased::client::IdentityClient; -use identity_iota::iota::rebased::Error; use identity_iota::iota::IotaDocument; -use identity_iota::iota_interaction::rpc_types::IotaObjectData; -use identity_iota::iota_interaction::rpc_types::OwnedObjectRef; -use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::types::base_types::ObjectID; use identity_iota::iota_interaction::types::id::UID; -use identity_iota::iota_interaction::IotaClientTrait; -use iota_interaction_ts::error::TsSdkError; #[derive(Debug, Deserialize, Serialize)] pub struct OnChainIdentity { pub id: UID, pub did_doc: Multicontroller>, - #[serde(skip)] - pub obj_ref: Option, - // used to have something to return a reference for in getter test - #[serde(skip)] - pub proposals: HashMap, } -impl OnChainIdentity { - pub fn is_shared(&self) -> bool { - true - } - - pub fn proposals(&self) -> &HashMap { - &self.proposals - } - - pub fn update_did_document(self, updated_doc: IotaDocument) -> ProposalBuilder - where - T: IotaClientTrait, - { - ProposalBuilder::new(self, ProposalAction::UpdateDocument(updated_doc)) - } - - pub fn deactivate_did(self) -> ProposalBuilder - where - T: IotaClientTrait, - { - ProposalBuilder::new(self, ProposalAction::Deactivate) - } - - pub async fn get_history( - &self, - _client: &IdentityClient, - _last_version: Option<&IotaObjectData>, - _page_size: Option, - ) -> Result, Error> - where - T: IotaClientTrait, - { - Ok(vec![]) - } -} +impl OnChainIdentity {} #[derive(Debug, Serialize, Deserialize)] pub enum ProposalAction { UpdateDocument(IotaDocument), Deactivate, } - -#[derive(Debug)] -pub struct ProposalBuilder {} - -impl ProposalBuilder { - pub fn new(_identity: OnChainIdentity, _action: ProposalAction) -> Self { - Self {} - } - - pub fn expiration_epoch(self, _exp: u64) -> Self { - self - } - - pub fn key(self, _key: String) -> Self { - self - } - - pub fn gas_budget(self, _amount: u64) -> Self { - self - } - - pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result, Error> - where - C: IotaClientTrait, - { - Ok(Some(Proposal {})) - } -} - -#[derive(Debug)] -pub struct IdentityBuilder {} - -impl IdentityBuilder { - pub fn new(_did_doc: &[u8], _package_id: ObjectID) -> Self { - Self {} - } - - pub fn controller(self, _address: IotaAddress, _voting_power: u64) -> Self { - self - } - - pub fn threshold(self, _threshold: u64) -> Self { - self - } - - pub fn gas_budget(self, _gas_budget: u64) -> Self { - self - } - - pub fn controllers(self, _controllers: I) -> Self - where - I: IntoIterator, - { - self - } - - pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result - where - C: IotaClientTrait, - { - Ok(OnChainIdentity { - id: UID::new( - ObjectID::from_str("did:iota:foobar:0x0000000000000000000000000000000000000000000000000000000000000000") - .map_err(|e| Error::InvalidArgument(e.to_string()))?, - ), - did_doc: Multicontroller::new(vec![1u8, 2u8, 3u8]), - obj_ref: None, - proposals: HashMap::new(), - }) - } -} diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs index cc7b992a9d..732ce8ed4d 100644 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs @@ -8,5 +8,4 @@ pub(crate) use identity::*; pub(crate) use multicontroller::*; // dummy types, have to be replaced with actual types later on -pub(crate) type DummySigner = str; pub(crate) type Hashable = Vec; diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs index 023ca74cdc..284635f25c 100644 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs +++ b/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs @@ -47,6 +47,8 @@ impl Multicontroller { pub fn proposals(&self) -> &HashMap { &self.proposals } + + #[allow(unused)] // API here will be replaced in the future. pub fn into_inner(self) -> T { self.controlled_value } diff --git a/bindings/wasm/identity_wasm/src/rebased/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs index f1568ab7f1..27b9a9410e 100644 --- a/bindings/wasm/identity_wasm/src/rebased/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -113,6 +113,7 @@ impl WasmOnChainIdentity { WasmCreateSendProposalTx::new(self, transfer_map, expiration_epoch) } + #[allow(unused)] // API will be updated in the future #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below pub async fn get_history( &self, diff --git a/bindings/wasm/identity_wasm/src/rebased/mod.rs b/bindings/wasm/identity_wasm/src/rebased/mod.rs index ffa168c0e3..18ddc45517 100644 --- a/bindings/wasm/identity_wasm/src/rebased/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/mod.rs @@ -4,13 +4,11 @@ mod client_dummy; mod identity; mod multicontroller; +mod proposals; mod types; mod wasm_identity_client; mod wasm_identity_client_read_only; -mod proposals; pub use identity::*; -pub use multicontroller::*; -pub use types::*; pub use wasm_identity_client::*; pub use wasm_identity_client_read_only::*; diff --git a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs index 4f751b45a3..43c0735436 100644 --- a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs +++ b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use identity_iota::core::Object; -use identity_iota::iota::rebased::migration::Proposal; -use identity_iota::iota::rebased::proposals::{DeactivateDid, UpdateDidDocument}; use super::client_dummy::Multicontroller; use wasm_bindgen::prelude::*; @@ -12,7 +10,6 @@ use crate::common::MapStringAny; use super::types::WasmObjectID; - #[wasm_bindgen(js_name = Multicontroller)] pub struct WasmMulticontroller(pub(crate) Multicontroller>); diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs index f60462273c..7e365f694b 100644 --- a/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs @@ -310,12 +310,16 @@ impl WasmCreateConfigChangeProposalTx { .add_multiple_controllers(controllers_to_add) .remove_multiple_controllers(controllers_to_remove) .update_multiple_controllers(controllers_to_update); - // identity_ref.deactivate_did(); let builder = if let Some(exp) = self.expiration_epoch { builder.expiration_epoch(exp) } else { builder }; + let builder = if let Some(tr) = self.threshold { + builder.threshold(tr) + } else { + builder + }; let tx_output = builder .finish(&client) diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs index 86d8638511..ec6c9d1172 100644 --- a/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs @@ -1,26 +1,26 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod config_change; mod deactivate_did; -mod update_did; mod send; -mod config_change; +mod update_did; +pub use config_change::*; pub use deactivate_did::*; -pub use update_did::*; pub use send::*; -pub use config_change::*; +pub use update_did::*; use std::collections::HashMap; use std::collections::HashSet; -use identity_iota::iota_interaction::types::base_types::ObjectID; use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::base_types::ObjectID; +use js_sys::JsString; +use js_sys::Reflect; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsValue; use wasm_bindgen::JsCast as _; -use js_sys::Reflect; -use js_sys::JsString; +use wasm_bindgen::JsValue; #[wasm_bindgen] extern "C" { @@ -53,7 +53,9 @@ impl From for (String, String) { impl From<(String, String)> for StringCouple { fn from(value: (String, String)) -> Self { - serde_wasm_bindgen::to_value(&value).expect("a string couple can be serialized to JS").unchecked_into() + serde_wasm_bindgen::to_value(&value) + .expect("a string couple can be serialized to JS") + .unchecked_into() } } @@ -100,4 +102,4 @@ impl TryFrom<&'_ HashSet> for StringSet { let js_value = serde_wasm_bindgen::to_value(value)?; Ok(js_value.dyn_into()?) } -} \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/rebased/types.rs b/bindings/wasm/identity_wasm/src/rebased/types.rs index 531e16177e..3a236f115e 100644 --- a/bindings/wasm/identity_wasm/src/rebased/types.rs +++ b/bindings/wasm/identity_wasm/src/rebased/types.rs @@ -7,8 +7,7 @@ use wasm_bindgen::prelude::*; use crate::error::wasm_error; use crate::error::Result; -pub use iota_interaction_ts::bindings::*; - +#[allow(unused)] // Might become part of `into_sdk_type`'s impl, so keep it for now. pub fn into_sdk_type>(wasm_type_instance: W) -> Result { let js_value: JsValue = wasm_type_instance.into(); //serde_wasm_bindgen::from_value::(js_value).map_err(wasm_error) diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index 082702bb90..f34e3d3274 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -1,8 +1,8 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::rc::Rc; use std::ops::Deref; +use std::rc::Rc; use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; @@ -58,7 +58,10 @@ impl Deref for WasmIdentityClient { #[wasm_bindgen(js_class = IdentityClient)] impl WasmIdentityClient { #[wasm_bindgen(js_name = create)] - pub async fn new(client: WasmIdentityClientReadOnly, signer: WasmTransactionSigner) -> Result { + pub async fn new( + client: WasmIdentityClientReadOnly, + signer: WasmTransactionSigner, + ) -> Result { let inner_client = IdentityClient::new(client.0, signer).await?; Ok(WasmIdentityClient(inner_client)) } diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs index 6f7760ae70..29ad3a4775 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs @@ -52,8 +52,12 @@ impl WasmIdentityClientReadOnly { } #[wasm_bindgen(js_name = createWithPkgId)] - pub async fn new_new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: String) -> Result { - let inner_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; + pub async fn new_new_with_pkg_id( + iota_client: WasmIotaClient, + iota_identity_pkg_id: String, + ) -> Result { + let inner_client = + IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; Ok(WasmIdentityClientReadOnly(inner_client)) } diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index b0b8bbdd70..f82f9542d5 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -52,3 +52,7 @@ instant = { version = "0.1", default-features = false, features = ["wasm-bindgen # can be removed as soon as fix has been added to clippy # see https://github.com/rust-lang/rust-clippy/issues/12377 empty_docs = "allow" + +[lints.rust] +# required for current wasm_bindgen version +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] } diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index 8f1473edee..a7d09fe10e 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -101,7 +101,7 @@ async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: stri * * @param iotaClient client instance * @param senderAddress transaction sender (and the one paying for it) - * @param txBcs transaction data serialized to bcs, most probably having placeholder values + * @param txBcs transaction data serialized to bcs, most probably having placeholder values * @param gasBudget optional fixed gas budget, determined automatically with a dry run if not provided * @returns updated transaction data */ @@ -198,7 +198,7 @@ export async function executeTransaction( * Helper function to pause execution. * * @param txBcs transaction data to hash - */ + */ export function sleep(durationMs: number) { return new Promise(resolve => setTimeout(resolve, durationMs)); -} \ No newline at end of file +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts index 5761aba667..661de1fce2 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts @@ -3,12 +3,7 @@ import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { - getClockRef, - getControllerDelegation, - insertPlaceholders, - putBackDelegationToken, -} from "../utils"; +import { getClockRef, getControllerDelegation, insertPlaceholders, putBackDelegationToken } from "../utils"; export function proposeUpdate( identity: SharedObjectRef, diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts index 1bfa5cab3b..80c49b4624 100644 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts @@ -1,7 +1,7 @@ import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; -const PLACEHOLDER_SENDER = '0x00000000000000090807060504030201'; +const PLACEHOLDER_SENDER = "0x00000000000000090807060504030201"; const PLACEHOLDER_GAS_BUDGET = 9; const PLACEHOLDER_GAS_PRICE = 8; const PLACEHOLDER_GAS_PAYMENT: ObjectRef[] = []; @@ -37,7 +37,7 @@ export function putBackDelegationToken( /** * Inserts placeholders related to sender and payment into transaction. - * + * * This is required if wanting to call `tx.build`, as this will check if these values have been set. * * @param tx transaction to update @@ -47,4 +47,4 @@ export function insertPlaceholders(tx: Transaction) { tx.setGasBudget(PLACEHOLDER_GAS_BUDGET); tx.setGasPayment([...PLACEHOLDER_GAS_PAYMENT]); // make sure, we're not sharing the array between tx tx.setSender(PLACEHOLDER_SENDER); -} \ No newline at end of file +} diff --git a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs index ca2d7e09e2..eebc634696 100644 --- a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs @@ -127,12 +127,12 @@ impl AssetMoveCalls for AssetMoveCallsTsSdk { } fn make_tx( - proposal: (ObjectID, SequenceNumber), - cap: ObjectRef, - asset: ObjectRef, - asset_type_param: TypeTag, - package: ObjectID, - function_name: &'static str, + _proposal: (ObjectID, SequenceNumber), + _cap: ObjectRef, + _asset: ObjectRef, + _asset_type_param: TypeTag, + _package: ObjectID, + _function_name: &'static str, ) -> Result { unimplemented!(); } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs index c04f573559..384584c416 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs @@ -7,4 +7,3 @@ mod wasm_types; pub use types::*; pub use wasm_iota_client::*; -pub use wasm_types::*; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs index e751016f5b..fe0645f61b 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs @@ -5,9 +5,6 @@ use wasm_bindgen::prelude::*; pub use super::wasm_types::*; -pub(crate) type WasmIotaAddress = String; -pub(crate) type WasmObjectID = String; - #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "Promise")] diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs index 41c37b26a7..4fd9a74d33 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -11,7 +11,6 @@ use identity_iota_interaction::generated_types::GetOwnedObjectsParams; use identity_iota_interaction::generated_types::GetTransactionBlockParams; use identity_iota_interaction::generated_types::QueryEventsParams; use identity_iota_interaction::generated_types::SortOrder; -use identity_iota_interaction::generated_types::TryGetPastObjectParams; use identity_iota_interaction::generated_types::WaitForTransactionParams; use identity_iota_interaction::rpc_types::CoinPage; use identity_iota_interaction::rpc_types::EventFilter; @@ -58,7 +57,6 @@ use crate::common::PromiseBigint; use crate::console_log; use crate::error::into_ts_sdk_result; use crate::error::TsSdkError; -use crate::error::WasmResult; // This file contains the wasm-bindgen 'glue code' providing // the interface of the TS Iota client to rust code. @@ -193,6 +191,7 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } @@ -218,6 +217,7 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } @@ -250,6 +250,7 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } @@ -287,14 +288,15 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } pub async fn try_get_parsed_past_object( &self, - object_id: ObjectID, - version: SequenceNumber, - options: IotaObjectDataOptions, + _object_id: ObjectID, + _version: SequenceNumber, + _options: IotaObjectDataOptions, ) -> IotaRpcResult { // TODO: does not work anymore, find out, why we need to pass a different `SequenceNumber` now unimplemented!("try_get_parsed_past_object"); @@ -349,6 +351,7 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } @@ -380,6 +383,7 @@ impl ManagedWasmIotaClient { IotaRpcError::FfiError(format!("{:?}", e)) })?; + #[allow(deprecated)] // will be refactored Ok(result.into_serde()?) } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index 7a909bd921..8f5849360f 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -14,8 +14,8 @@ use identity_iota_interaction::types::object::Owner; use identity_iota_interaction::ProgrammableTransactionBcs; use js_sys::Promise; use js_sys::Uint8Array; -use serde::Serialize; use serde::Deserialize; +use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; @@ -200,7 +200,8 @@ impl From for WasmObjectRef { "digest": value.2, }); - json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS ObjectRef's interface. .unchecked_into() @@ -215,7 +216,8 @@ impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { "mutable": value.2, }); - json_obj.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) .expect("a JSON object is a JS value") // safety: `json_obj` was constructed following TS SharedObjectRef's interface. .unchecked_into() @@ -289,11 +291,11 @@ extern "C" { #[wasm_bindgen(js_name = "addGasDataToTransaction")] fn add_gas_data_to_transaction_inner( - iota_client: &WasmIotaClient, // --> TypeScript: IotaClient - sender_address: String, // --> TypeScript: string - tx_bcs: Vec, // --> TypeScript: Uint8Array, - gas_budget: Option, // --> TypeScript: optional bigint -) -> PromiseUint8Array; + iota_client: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + tx_bcs: Vec, // --> TypeScript: Uint8Array, + gas_budget: Option, // --> TypeScript: optional bigint + ) -> PromiseUint8Array; #[wasm_bindgen(js_name = "sleep")] fn sleep_inner(ms: i32) -> Promise; @@ -305,24 +307,24 @@ pub(crate) fn get_transaction_digest(tx_data: &[u8]) -> Vec { } /// Inserts these values into the transaction and replaces placeholder values. -/// +/// /// - sender (overwritten as we assume a placeholder to be used in prepared transaction) /// - gas budget (value determined automatically if not provided) /// - gas price (value determined automatically) /// - gas coin / payment object (fetched automatically) /// - gas owner (equals sender) -/// +/// /// # Arguments -/// +/// /// * `iota_client` - client instance /// * `sender_address` - transaction sender (and the one paying for it) -/// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values +/// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values /// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided pub(crate) async fn add_gas_data_to_transaction( - iota_client: &WasmIotaClient, - sender_address: IotaAddress, - tx_bcs: Vec, - gas_budget: Option, + iota_client: &WasmIotaClient, + sender_address: IotaAddress, + tx_bcs: Vec, + gas_budget: Option, ) -> Result, TsSdkError> { let promise: Promise = Promise::resolve(&add_gas_data_to_transaction_inner( iota_client, diff --git a/bindings/wasm/iota_interaction_ts/src/common/types.rs b/bindings/wasm/iota_interaction_ts/src/common/types.rs index 9d596d53e3..aadea097dd 100644 --- a/bindings/wasm/iota_interaction_ts/src/common/types.rs +++ b/bindings/wasm/iota_interaction_ts/src/common/types.rs @@ -78,6 +78,7 @@ impl TryFrom<&Object> for MapStringAny { for (key, value) in properties.iter() { map.set( &JsValue::from_str(key.as_str()), + #[allow(deprecated)] // will be refactored &JsValue::from_serde(&value).wasm_result()?, ); } diff --git a/bindings/wasm/iota_interaction_ts/src/common/utils.rs b/bindings/wasm/iota_interaction_ts/src/common/utils.rs index 5b2d698be7..3ed8447a1b 100644 --- a/bindings/wasm/iota_interaction_ts/src/common/utils.rs +++ b/bindings/wasm/iota_interaction_ts/src/common/utils.rs @@ -4,7 +4,6 @@ use serde::de::DeserializeOwned; use wasm_bindgen::prelude::*; -use crate::error::wasm_error; use crate::error::WasmError; pub fn into_sdk_type<'a, T: DeserializeOwned, W: Into>( diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs index b8b44f980c..1c4a766ec5 100644 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -1,15 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use js_sys::Array; -use js_sys::Promise; +use async_trait::async_trait; use js_sys::Uint8Array; use std::cell::Cell; use std::collections::HashSet; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; -use wasm_bindgen_futures::JsFuture; use crate::bindings::WasmIotaObjectData; use crate::bindings::WasmObjectRef; @@ -217,6 +215,7 @@ extern "C" { pub struct IdentityMoveCallsTsSdk {} +#[async_trait(?Send)] impl IdentityMoveCalls for IdentityMoveCallsTsSdk { type Error = TsSdkError; type NativeTxBuilder = WasmTransactionBuilder; @@ -593,12 +592,12 @@ impl IdentityMoveCalls for IdentityMoveCallsTsSdk { } fn create_and_execute_send( - identity: OwnedObjectRef, - capability: ObjectRef, - transfer_map: Vec<(ObjectID, IotaAddress)>, - expiration: Option, - objects: Vec<(ObjectRef, TypeTag)>, - package: ObjectID, + _identity: OwnedObjectRef, + _capability: ObjectRef, + _transfer_map: Vec<(ObjectID, IotaAddress)>, + _expiration: Option, + _objects: Vec<(ObjectRef, TypeTag)>, + _package: ObjectID, ) -> anyhow::Result { todo!() } diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs index e3094aadac..b3ec123e29 100644 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -5,13 +5,9 @@ use std::boxed::Box; use std::option::Option; use std::result::Result; -use fastcrypto::hash::Blake2b256; -use identity_iota_interaction::shared_crypto::intent::Intent; -use identity_iota_interaction::shared_crypto::intent::IntentMessage; use identity_iota_interaction::types::crypto::SignatureScheme; use identity_iota_interaction::types::digests::TransactionDigest; use identity_iota_interaction::types::dynamic_field::DynamicFieldName; -use js_sys::Uint8Array; use secret_storage::Signer; use identity_iota_interaction::error::IotaRpcResult; @@ -45,7 +41,6 @@ use identity_iota_interaction::TransactionDataBcs; use crate::bindings::add_gas_data_to_transaction; use crate::bindings::get_transaction_digest; -use crate::bindings::sleep; use crate::bindings::IotaTransactionBlockResponseAdapter; use crate::bindings::ManagedWasmIotaClient; use crate::bindings::WasmIotaClient; @@ -343,7 +338,10 @@ impl IotaClientTrait for IotaClientTsSdk { tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, signer: &S, - ) -> Result>, Self::Error> { + ) -> Result< + Box>, + Self::Error, + > { let tx: ProgrammableTransaction = tx_bcs.try_into()?; let response = self .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) @@ -366,13 +364,13 @@ impl IotaClientTrait for IotaClientTsSdk { async fn default_gas_budget( &self, - sender_address: IotaAddress, - tx_bcs: &ProgrammableTransactionBcs, + _sender_address: IotaAddress, + _tx_bcs: &ProgrammableTransactionBcs, ) -> Result { unimplemented!(); } - async fn get_previous_version(&self, iod: IotaObjectData) -> Result, Self::Error> { + async fn get_previous_version(&self, _iod: IotaObjectData) -> Result, Self::Error> { unimplemented!(); } @@ -407,37 +405,35 @@ impl IotaClientTsSdk { sender_public_key: &[u8], ) -> Result, TsSdkError> { let digest = get_transaction_digest(tx_data); - + let raw_signature = signer .sign(&digest.to_vec()) .await - .map_err(|err| - TsSdkError::TransactionSerializationError(format!("could not sign transaction message; {err}")) - )?; - + .map_err(|err| TsSdkError::TransactionSerializationError(format!("could not sign transaction message; {err}")))?; + let signature_bytes = [ [SignatureScheme::ED25519.flag()].as_slice(), &raw_signature, sender_public_key, ] .concat(); - + Ok(signature_bytes) } /// Inserts these values into the transaction and replaces placeholder values. - /// + /// /// - sender (overwritten as we assume a placeholder to be used in prepared transaction) /// - gas budget (value determined automatically if not provided) /// - gas price (value determined automatically) /// - gas coin / payment object (fetched automatically) /// - gas owner (equals sender) - /// + /// /// # Arguments - /// + /// /// * `iota_client` - client instance /// * `sender_address` - transaction sender (and the one paying for it) - /// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values + /// * `tx_bcs` - transaction data serialized to bcs, most probably having placeholder values /// * `gas_budget` - optional fixed gas budget, determined automatically with a dry run if not provided async fn replace_transaction_placeholder_values( &self, @@ -445,16 +441,8 @@ impl IotaClientTsSdk { sender_address: IotaAddress, gas_budget: Option, ) -> Result, TsSdkError> { - let tx_bcs = tx.0.build() - .await - .map_err(WasmError::from)? - .to_vec(); - let updated = add_gas_data_to_transaction( - &self.iota_client.0, - sender_address, - tx_bcs, - gas_budget, - ).await?; + let tx_bcs = tx.0.build().await.map_err(WasmError::from)?.to_vec(); + let updated = add_gas_data_to_transaction(&self.iota_client.0, sender_address, tx_bcs, gas_budget).await?; Ok(updated) } @@ -471,7 +459,9 @@ impl IotaClientTsSdk { gas_budget: Option, signer: &S, ) -> Result { - let final_tx = self.replace_transaction_placeholder_values(tx, sender_address, gas_budget).await?; + let final_tx = self + .replace_transaction_placeholder_values(tx, sender_address, gas_budget) + .await?; let signature = Self::get_transaction_signature_bytes(signer, &final_tx, sender_public_key).await?; let wasm_response = self @@ -482,7 +472,8 @@ impl IotaClientTsSdk { Some(IotaTransactionBlockResponseOptions::full_content()), Some(ExecuteTransactionRequestType::WaitForLocalExecution), ) - .await.unwrap(); + .await + .unwrap(); let native = wasm_response.clone_native_response(); Ok(IotaTransactionBlockResponseProvider::new(native)) diff --git a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs index 8024cae580..6208a605fc 100644 --- a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs +++ b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs @@ -1,7 +1,6 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -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; diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs index 432fbdeab7..6dccdf5ab4 100644 --- a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -1,6 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use async_trait::async_trait; use itertools::Itertools; use std::collections::HashSet; @@ -303,6 +304,8 @@ fn execute_send_impl( #[derive(Clone)] pub(crate) struct IdentityMoveCallsRustSdk {} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl IdentityMoveCalls for IdentityMoveCallsRustSdk { type Error = Error; type NativeTxBuilder = PrgrTxBuilder; @@ -799,7 +802,7 @@ impl IdentityMoveCalls for IdentityMoveCallsRustSdk { async fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, - did_doc: impl AsRef<[u8]>, + did_doc: impl AsRef<[u8]> + Send, expiration: Option, package_id: ObjectID, ) -> Result { diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs index 4d52ca62e2..63e4686c60 100644 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -55,8 +55,8 @@ use identity_iota_interaction::IotaClient; use identity_iota_interaction::IotaClientTrait; use identity_iota_interaction::IotaKeySignature; use identity_iota_interaction::IotaTransactionBlockResponseT; -use identity_iota_interaction::ProgrammableTransactionBcs; use identity_iota_interaction::OptionalSync; +use identity_iota_interaction::ProgrammableTransactionBcs; use identity_iota_interaction::QuorumDriverTrait; use identity_iota_interaction::ReadTrait; use identity_iota_interaction::SignatureBcs; diff --git a/identity_iota_core/src/rebased/client/read_only.rs b/identity_iota_core/src/rebased/client/read_only.rs index 5464415c52..e3866887ec 100644 --- a/identity_iota_core/src/rebased/client/read_only.rs +++ b/identity_iota_core/src/rebased/client/read_only.rs @@ -43,8 +43,6 @@ use identity_iota_interaction::IotaClient; #[cfg(target_arch = "wasm32")] use iota_interaction_ts::bindings::WasmIotaClient; -#[cfg(target_arch = "wasm32")] -use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; /// An [`IotaClient`] enriched with identity-related /// functionalities. diff --git a/identity_iota_core/src/rebased/migration/alias.rs b/identity_iota_core/src/rebased/migration/alias.rs index 5226ed5542..ff9cc94707 100644 --- a/identity_iota_core/src/rebased/migration/alias.rs +++ b/identity_iota_core/src/rebased/migration/alias.rs @@ -88,6 +88,7 @@ cfg_if::cfg_if! { } impl UnmigratedAlias { + #[allow(unused)] // Currently not supported. pub(crate) async fn migrate_internal( self, client: &IdentityClientReadOnly, diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index 9dfd1f9139..493196cb95 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -14,6 +14,7 @@ pub mod proposals; /// Module for handling transactions. pub mod transaction; /// Contains utility functions. +#[cfg(not(target_arch = "wasm32"))] pub mod utils; pub use assets::*; diff --git a/identity_iota_core/src/rebased/proposals/borrow.rs b/identity_iota_core/src/rebased/proposals/borrow.rs index be61655026..6fabdc4395 100644 --- a/identity_iota_core/src/rebased/proposals/borrow.rs +++ b/identity_iota_core/src/rebased/proposals/borrow.rs @@ -39,7 +39,6 @@ use super::UserDrivenTx; cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { use iota_interaction_ts::NativeTsTransactionBuilderBindingWrapper as Ptb; - use iota_interaction_ts::error::TsSdkError as IotaInteractionError; /// Instances of BorrowIntentFnT can be used as user-provided function to describe how /// a borrowed assets shall be used. pub trait BorrowIntentFnT: FnOnce(&mut Ptb, &HashMap) {} diff --git a/identity_iota_core/src/rebased/proposals/mod.rs b/identity_iota_core/src/rebased/proposals/mod.rs index 836b834336..8396c67df5 100644 --- a/identity_iota_core/src/rebased/proposals/mod.rs +++ b/identity_iota_core/src/rebased/proposals/mod.rs @@ -17,12 +17,12 @@ cfg_if::cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse; use crate::rebased::transaction::Transaction; + use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; } } use crate::iota_interaction_adapter::AdapterError; use crate::iota_interaction_adapter::AdapterNativeResponse; use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; -use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; use identity_iota_interaction::IdentityMoveCalls; use identity_iota_interaction::IotaClientTrait; diff --git a/identity_iota_core/src/rebased/transaction.rs b/identity_iota_core/src/rebased/transaction.rs index e121407979..421307135c 100644 --- a/identity_iota_core/src/rebased/transaction.rs +++ b/identity_iota_core/src/rebased/transaction.rs @@ -70,8 +70,11 @@ pub trait Transaction: Sized { } } +/// The output type of a [`Transaction`]. pub struct TransactionOutputInternal { + /// The parsed Transaction output. See [`Transaction::Output`]. pub output: T, + /// The "raw" transaction execution response received. pub response: IotaTransactionBlockResponseAdaptedTraitObj, } @@ -94,17 +97,22 @@ impl From> for TransactionOutput { } } +/// Interface for operations that interact with the ledger through transactions. #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait TransactionInternal: Sized { + /// The result of performing the operation. type Output; - async fn execute_with_opt_gas_internal + OptionalSync,>( + /// Executes this operation using the given `client` and an optional `gas_budget`. + /// If no value for `gas_budget` is provided, an estimated value will be used. + async fn execute_with_opt_gas_internal + OptionalSync>( self, gas_budget: Option, client: &IdentityClient, ) -> Result, Error>; + /// Executes this operation using `client`. #[cfg(target_arch = "wasm32")] async fn execute>( self, @@ -113,6 +121,7 @@ pub trait TransactionInternal: Sized { self.execute_with_opt_gas_internal(None, client).await } + /// Executes this operation using `client` and a well defined `gas_budget`. #[cfg(target_arch = "wasm32")] async fn execute_with_gas + OptionalSync>( self, @@ -162,8 +171,8 @@ impl TransactionInternal for ProgrammableTransaction { type Output = (); async fn execute_with_opt_gas_internal( self, - gas_budget: Option, - client: &IdentityClient, + _gas_budget: Option, + _client: &IdentityClient, ) -> Result, Error> where S: Signer + OptionalSync, diff --git a/identity_iota_core/src/rebased/utils.rs b/identity_iota_core/src/rebased/utils.rs index f48eb9144b..c12905d1a6 100644 --- a/identity_iota_core/src/rebased/utils.rs +++ b/identity_iota_core/src/rebased/utils.rs @@ -5,6 +5,8 @@ use std::process::Output; use anyhow::Context as _; use identity_iota_interaction::types::base_types::ObjectID; +use iota_sdk::IotaClient; +use iota_sdk::IotaClientBuilder; use serde::Deserialize; #[cfg(not(target_arch = "wasm32"))] use tokio::process::Command; @@ -22,20 +24,14 @@ struct CoinOutput { nanos_balance: u64, } -cfg_if::cfg_if! { - if #[cfg(not(target_arch = "wasm32"))] { - use iota_sdk::{IotaClientBuilder, IotaClient}; +/// Builds an `IOTA` client for the given network. +pub async fn get_client(network: &str) -> Result { + let client = IotaClientBuilder::default() + .build(network) + .await + .map_err(|err| Error::Network(format!("failed to connect to {network}"), err))?; - /// Builds an `IOTA` client for the given network. - pub async fn get_client(network: &str) -> Result { - let client = IotaClientBuilder::default() - .build(network) - .await - .map_err(|err| Error::Network(format!("failed to connect to {network}"), err))?; - - Ok(client) - } - } + Ok(client) } fn unpack_command_output(output: &Output, task: &str) -> anyhow::Result { diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 511528038f..6c54e32702 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -74,7 +74,10 @@ impl StateMetadataDocument { /// Returns the corresponding [`IotaDocument`] with DID replaced by DID placeholder `did:0:0`. pub fn into_iota_document_with_placeholders(self) -> IotaDocument { - IotaDocument { document: self.document, metadata: self.metadata } + IotaDocument { + document: self.document, + metadata: self.metadata, + } } /// Pack a [`StateMetadataDocument`] into bytes, suitable for storing in an identity, diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml index 56f8ca991d..76afb6fef9 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_interaction/Cargo.toml @@ -56,5 +56,9 @@ rustdoc-args = ["--cfg", "docsrs"] default = ["send-sync-transaction", "secret-storage/default"] send-sync-transaction = ["secret-storage/send-sync-storage"] -[lints] -workspace = true +[lints.clippy] +result_large_err = "allow" + +[lints.rust] +# from local sdk types +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(msim)'] } diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_interaction/src/move_call_traits.rs index 1b8541f7cb..23bca21a4e 100644 --- a/identity_iota_interaction/src/move_call_traits.rs +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::iter::IntoIterator; +use async_trait::async_trait; use serde::Serialize; use crate::rpc_types::IotaObjectData; @@ -86,6 +87,8 @@ impl BorrowIntentFnInternalT for T where T: FnOnce(&mut B, &HashMap: FnOnce(&mut B, &Argument) {} impl ControllerIntentFnInternalT for T where T: FnOnce(&mut B, &Argument) {} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait IdentityMoveCalls { type Error; type NativeTxBuilder; @@ -221,7 +224,8 @@ pub trait IdentityMoveCalls { async fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, - did_doc: impl AsRef<[u8]>, + #[cfg(not(target_arch = "wasm32"))] did_doc: impl AsRef<[u8]> + Send, + #[cfg(target_arch = "wasm32")] did_doc: impl AsRef<[u8]>, expiration: Option, package_id: ObjectID, ) -> Result; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs b/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs index db7e3fd259..88bdef0efb 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs @@ -12,6 +12,7 @@ use std::{ use std::marker::Sized; use std::string::{String, ToString}; use std::result::Result::Ok; +#[allow(unused)] // Kept in sync with original source, so keep as is. use std::option::Option; use std::option::Option::Some; @@ -31,6 +32,7 @@ use super::super::move_core_types::{ language_storage::{StructTag, TypeTag} }; +#[allow(unused)] // Kept in sync with original source, so keep as is. use super::{IOTA_FRAMEWORK_ADDRESS, MOVE_STDLIB_ADDRESS, IOTA_SYSTEM_ADDRESS, STARDUST_ADDRESS, IOTA_SYSTEM_STATE_ADDRESS, IOTA_CLOCK_ADDRESS }; use super::parse_iota_struct_tag; @@ -62,6 +64,7 @@ impl ProtocolVersion { pub const MAX: Self = Self(MAX_PROTOCOL_VERSION); + #[allow(unused)] // Kept in sync with original source, so keep as is. #[cfg(not(msim))] const MAX_ALLOWED: Self = Self::MAX; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs b/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs index 797a01c006..4742ecc9b9 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs @@ -2,6 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#[allow(unused)] // Kept in sync with original source, so keep as is. use serde::{Deserialize, Serialize}; use super::super::move_core_types::{ account_address::AccountAddress, diff --git a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs index 6a2e0f1f4b..3fd4c1ec41 100644 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs @@ -6,11 +6,13 @@ use serde::Serialize; use crate::ident_str; +#[allow(unused)] // Kept in sync with original source, so keep as is. use crate::sdk_types::move_types::{ language_storage::{TypeTag, StructTag}, identifier::{IdentStr}, }; +#[allow(unused)] // Kept in sync with original source, so keep as is. use super::super::{ IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, @@ -20,6 +22,8 @@ use super::super::{ id::UID, }; + +#[allow(unused)] // Kept in sync with original source, so keep as is. use super::timelocked_staked_iota::{TIMELOCKED_STAKED_IOTA_MODULE_NAME, TIMELOCKED_STAKED_IOTA_STRUCT_NAME}; pub const TIMELOCK_MODULE_NAME: &IdentStr = ident_str!("timelock"); From eabf83c0d99cf2dd49997138531ee6a6e9642462 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 21 Feb 2025 17:57:55 +0100 Subject: [PATCH 45/63] fix custom key handler in example --- .../examples/src/1_advanced/4_custom_resolution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts index 6fa5384937..cbe6048cbf 100644 --- a/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts +++ b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts @@ -22,7 +22,7 @@ export async function customResolution() { // for demo purposes we'll just inject the custom property into a core document // to create a new KeyDocument instance - let coreDocument = CoreDocument.fromJSON(document.didDocument); + let coreDocument = CoreDocument.fromJSON(document); (coreDocument as unknown as KeyDocument).customProperty = "foobar"; return coreDocument as unknown as KeyDocument; }; From bec19aa42a162f29cd849e44fa4a105fc2779abe Mon Sep 17 00:00:00 2001 From: umr1352 Date: Fri, 21 Feb 2025 18:14:05 +0100 Subject: [PATCH 46/63] fix serialization/deserialization of Signature --- .../wasm/identity_wasm/examples/src/util.ts | 3 +- bindings/wasm/iota_interaction_ts/Cargo.toml | 1 + .../src/bindings/keytool_signer.rs | 2 +- .../src/bindings/wasm_types.rs | 31 +++++++++++++++++-- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index cd48ac63b8..0bd540e3d3 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -64,7 +64,8 @@ export async function getFundedClient(storage: Storage): Promise // create signer from storage let signer = new StorageSigner(storage, keyId, publicKeyJwk); - const identityClient = await IdentityClient.create(identityClientReadOnly, signer); + const keytoolSigner = await KeytoolSigner.create(); + const identityClient = await IdentityClient.create(identityClientReadOnly, keytoolSigner); await requestIotaFromFaucetV0({ host: getFaucetHost(NETWORK_NAME_FAUCET), diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 2a7aa70f0d..1575bee140 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -22,6 +22,7 @@ bcs = "0.1.6" bls12_381_plus = "0.8.17" cfg-if = "1.0.0" console_error_panic_hook = { version = "0.1" } +eyre = "0.6.12" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } futures = { version = "0.3" } js-sys = { version = "0.3.61" } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index 3372ef7554..7eca700f73 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -40,7 +40,7 @@ pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); #[wasm_bindgen(js_class = KeytoolSigner)] impl WasmKeytoolSigner { - #[wasm_bindgen(constructor)] + #[wasm_bindgen(js_name = create)] pub async fn new(address: Option, iota_bin_location: Option) -> Result { let address = address .as_deref() diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index b37699445f..a7b047b6f4 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -1,6 +1,8 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use fastcrypto::encoding::Base64; +use fastcrypto::encoding::Encoding; use fastcrypto::traits::EncodeDecodeBase64; use identity_iota_interaction::rpc_types::OwnedObjectRef; use identity_iota_interaction::types::base_types::IotaAddress; @@ -161,18 +163,41 @@ extern "C" { pub type WasmIotaSignature; } +#[derive(Serialize, Deserialize)] +enum IotaSignatureHelper { + Ed25519IotaSignature(String), + Secp256k1IotaSignature(String), + Secp256r1IotaSignature(String), +} + impl TryFrom for WasmIotaSignature { type Error = JsValue; fn try_from(sig: Signature) -> Result { - let js_value = serde_wasm_bindgen::to_value(&sig)?; - js_value.dyn_into() + let base64sig = Base64::encode(&sig); + let json_signature = match sig { + Signature::Ed25519IotaSignature(_) => IotaSignatureHelper::Ed25519IotaSignature(base64sig), + Signature::Secp256r1IotaSignature(_) => IotaSignatureHelper::Secp256r1IotaSignature(base64sig), + Signature::Secp256k1IotaSignature(_) => IotaSignatureHelper::Secp256k1IotaSignature(base64sig), + }; + + json_signature + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .map(JsCast::unchecked_into) + .map_err(|e| e.into()) } } impl TryFrom for Signature { type Error = JsValue; fn try_from(sig: WasmIotaSignature) -> Result { - serde_wasm_bindgen::from_value(sig.into()).wasm_result() + let sig_helper = serde_wasm_bindgen::from_value(sig.into())?; + let base64sig = match sig_helper { + IotaSignatureHelper::Ed25519IotaSignature(s) => s, + IotaSignatureHelper::Secp256k1IotaSignature(s) => s, + IotaSignatureHelper::Secp256r1IotaSignature(s) => s, + }; + + base64sig.parse().map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) } } From ac8a2908e1386308015eea3ee90a51b8b7fe8424 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Fri, 21 Feb 2025 18:27:20 +0100 Subject: [PATCH 47/63] update exampel to use new API flow --- .../examples/src/0_basic/0_create_did.ts | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts index 880c3cb9ca..3072ed8447 100644 --- a/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts @@ -20,22 +20,12 @@ export async function createIdentity(): Promise { console.log(`Unpublished DID document: ${JSON.stringify(unpublished, null, 2)}`); let did: IotaDID; - // TODO: decide upon wich style to use here - // so let's go with both for now, to show that both work - if (Math.random() > .5) { - console.log("Creating new identity fully via client flow"); - const { output: identity } = await identityClient - .createIdentity(unpublished) - .finish() - .execute(identityClient); - did = identity.didDocument().id(); - } else { - console.log("Publishing document to identity"); - const { output: published } = await identityClient - .publishDidDocument(unpublished) - .execute(identityClient); - did = published.id(); - } + console.log("Creating new identity"); + const { output: identity } = await identityClient + .createIdentity(unpublished) + .finish() + .execute(identityClient); + did = identity.didDocument().id(); // check if we can resolve it via client const resolved = await identityClient.resolveDid(did); From 75f10ab8ede734acce78d058b5bbc2eec29a619a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 21 Feb 2025 18:34:58 +0100 Subject: [PATCH 48/63] bump min engine version --- bindings/wasm/identity_wasm/package-lock.json | 2 +- bindings/wasm/identity_wasm/package.json | 2 +- bindings/wasm/iota_interaction_ts/package-lock.json | 2 +- bindings/wasm/iota_interaction_ts/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/identity_wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json index cea557b0c8..6a6a76930b 100644 --- a/bindings/wasm/identity_wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -39,7 +39,7 @@ "wasm-opt": "^1.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" }, "peerDependencies": { "@iota/iota-sdk": "^0.5.0" diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 38f22e442c..7a6c8dca42 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -90,6 +90,6 @@ "@iota/iota-sdk": "^0.5.0" }, "engines": { - "node": ">=16" + "node": ">=20" } } diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json index 13375b0396..18ac48f3c6 100644 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -38,7 +38,7 @@ "wasm-opt": "^1.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" }, "peerDependencies": { "@iota/iota-sdk": "^0.5.0" diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index 2d94cf81a4..40e34c7c00 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -84,6 +84,6 @@ "@iota/iota-sdk": "^0.5.0" }, "engines": { - "node": ">=16" + "node": ">=20" } } From 711bfd06748ccf3bda4af61affbc21ac63ee669e Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 25 Feb 2025 09:37:50 +0100 Subject: [PATCH 49/63] temp commit --- .../wasm/identity_wasm/examples/src/util.ts | 9 +++-- .../src/rebased/wasm_identity_client.rs | 2 ++ .../src/storage/wasm_storage_signer.rs | 25 +++++++++++++ .../src/storage/wasm_transaction_signer.rs | 27 +++++++++----- .../src/bindings/wasm_types.rs | 36 +++++++++++++------ 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index 0bd540e3d3..a559a8538f 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -14,7 +14,9 @@ import { StorageSigner, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; +import { Ed25519PublicKey } from "@iota/iota-sdk/keypairs/ed25519"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; +import assert = require("assert"); export const IOTA_IDENTITY_PKG_ID = process.env.IOTA_IDENTITY_PKG_ID || "0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818"; @@ -60,12 +62,15 @@ export async function getFundedClient(storage: Storage): Promise if (typeof publicKeyJwk === "undefined") { throw new Error("failed to derive public JWK from generated JWK"); } + console.log(publicKeyJwk); let keyId = generate.keyId(); + const pk_raw_bytes = Buffer.from(publicKeyJwk.paramsOkp()?.x!, 'base64url'); + const recreatedPk = new Ed25519PublicKey(pk_raw_bytes); + console.log(recreatedPk); // create signer from storage let signer = new StorageSigner(storage, keyId, publicKeyJwk); - const keytoolSigner = await KeytoolSigner.create(); - const identityClient = await IdentityClient.create(identityClientReadOnly, keytoolSigner); + const identityClient = await IdentityClient.create(identityClientReadOnly, signer); await requestIotaFromFaucetV0({ host: getFaucetHost(NETWORK_NAME_FAUCET), diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index ee60e4bbb0..100eff63cf 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use std::ops::Deref; use fastcrypto::ed25519::Ed25519PublicKey; +use fastcrypto::traits::EncodeDecodeBase64; use fastcrypto::traits::ToFromBytes; use identity_iota::iota::rebased::client::IdentityClient; @@ -13,6 +14,7 @@ use identity_iota::iota::rebased::transaction::TransactionInternal; use identity_iota::iota::rebased::transaction::TransactionOutputInternal; use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::crypto::PublicKey; use iota_interaction_ts::bindings::WasmExecutionStatus; use iota_interaction_ts::bindings::WasmOwnedObjectRef; use iota_interaction_ts::WasmPublicKey; diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index 2adbd1b4f9..d5ce32e5ba 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -1,12 +1,17 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use async_trait::async_trait; +use identity_iota::iota_interaction::types::crypto::PublicKey; +use identity_iota::iota_interaction::types::crypto::Signature; +use identity_iota::iota_interaction::IotaKeySignature; use identity_iota::storage::KeyId; use identity_iota::storage::StorageSigner; use identity_iota::verification::jwk::JwkParams; use identity_iota::verification::jwu; use iota_interaction_ts::WasmIotaSignature; use iota_interaction_ts::WasmPublicKey; +use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use wasm_bindgen::prelude::*; @@ -17,6 +22,8 @@ use crate::storage::WasmJwkStorage; use crate::storage::WasmKeyIdStorage; use crate::storage::WasmStorage; +use super::WasmTransactionSigner; + #[wasm_bindgen(js_name = StorageSigner)] pub struct WasmStorageSigner { storage: WasmStorage, @@ -58,5 +65,23 @@ impl WasmStorageSigner { .await .wasm_result() .and_then(|pk| WasmPublicKey::try_from(&pk)) + .inspect(|wasm_pk| console_log!("WasmStorageSigner's PK: {:?}", &wasm_pk.to_raw_bytes())) } } + +#[async_trait(?Send)] +impl Signer for WasmStorageSigner { + type KeyId = String; + + async fn sign(&self, data: &Vec) -> std::result::Result { + self.signer().sign(data).await + } + + async fn public_key(&self) -> std::result::Result { + Signer::public_key(&self.signer()).await + } + + fn key_id(&self) -> &String { + todo!() + } +} \ No newline at end of file diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index 89765fbb70..eb3a3597f9 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -1,6 +1,10 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::cell::LazyCell; +use std::cell::OnceCell; +use std::sync::OnceLock; + use async_trait::async_trait; use identity_iota::iota::rebased::client::IotaKeySignature; use identity_iota::iota_interaction::types::crypto::PublicKey; @@ -10,6 +14,8 @@ use iota_interaction_ts::WasmPublicKey; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsCast; +use wasm_bindgen::JsError; use crate::error::Result; @@ -30,15 +36,14 @@ extern "C" { #[wasm_bindgen(typescript_type = "TransactionSigner")] pub type WasmTransactionSigner; - #[wasm_bindgen(method, catch)] + #[wasm_bindgen(method, structural, catch)] pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; - #[wasm_bindgen(js_name = "publicKey", method, catch)] + + #[wasm_bindgen(js_name = "publicKey", method, structural, catch)] pub async fn public_key(this: &WasmTransactionSigner) -> Result; - // TODO: re-add WasmTransactionSigner::key_id - // #[wasm_bindgen(js_name = "keyId", structural, method)] - // // pub fn key_id(this: &WasmTransactionSigner) -> js_sys::JsString; - // pub fn key_id(this: &WasmTransactionSigner) -> String; + #[wasm_bindgen(js_name = "keyId", method, structural)] + pub fn key_id(this: &WasmTransactionSigner) -> String; } #[async_trait(?Send)] @@ -54,14 +59,18 @@ impl Signer for WasmTransactionSigner { } async fn public_key(&self) -> std::result::Result { - self.public_key().await.and_then(|v| v.try_into()).map_err(|err| { - let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); + self.public_key().await.and_then(|v| { + console_log!("WasmTransactionSigner's PK: {:?}", &v.to_raw_bytes()); + v.try_into() + }).map_err(|err| { + let details = String::default(); let message = format!("could not get public key{details}"); SecretStorageError::KeyNotFound(message) }) } fn key_id(&self) -> &String { - todo!("WasmTransactionSigner::key_id"); + static KEY_ID: OnceLock = OnceLock::new(); + KEY_ID.get_or_init(|| self.key_id()) } } diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index a7b047b6f4..5f6fdf8857 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -3,7 +3,6 @@ use fastcrypto::encoding::Base64; use fastcrypto::encoding::Encoding; -use fastcrypto::traits::EncodeDecodeBase64; use identity_iota_interaction::rpc_types::OwnedObjectRef; use identity_iota_interaction::types::base_types::IotaAddress; use identity_iota_interaction::types::base_types::ObjectID; @@ -11,6 +10,7 @@ use identity_iota_interaction::types::base_types::ObjectRef; use identity_iota_interaction::types::base_types::SequenceNumber; use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::types::crypto::SignatureScheme; use identity_iota_interaction::types::execution_status::CommandArgumentError; use identity_iota_interaction::types::execution_status::ExecutionStatus; use identity_iota_interaction::types::object::Owner; @@ -197,7 +197,9 @@ impl TryFrom for Signature { IotaSignatureHelper::Secp256r1IotaSignature(s) => s, }; - base64sig.parse().map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) + base64sig + .parse() + .map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) } } @@ -238,12 +240,16 @@ extern "C" { #[wasm_bindgen(typescript_type = PublicKey)] pub type WasmPublicKey; - #[wasm_bindgen(js_name = toIotaPublicKey, method)] - pub fn to_iota_public_key(this: &WasmPublicKey) -> String; + #[wasm_bindgen(js_name = toRawBytes, method)] + pub fn to_raw_bytes(this: &WasmPublicKey) -> Vec; + + #[wasm_bindgen(method)] + pub fn flag(this: &WasmPublicKey) -> u8; } #[wasm_bindgen(module = "@iota/iota-sdk/keypairs/ed25519")] extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] pub type Ed25519PublicKey; #[wasm_bindgen(constructor, catch)] @@ -252,6 +258,7 @@ extern "C" { #[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256r1")] extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] pub type Secp256r1PublicKey; #[wasm_bindgen(constructor, catch)] @@ -260,6 +267,7 @@ extern "C" { #[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256k1")] extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] pub type Secp256k1PublicKey; #[wasm_bindgen(constructor, catch)] @@ -270,13 +278,15 @@ impl TryFrom<&'_ PublicKey> for WasmPublicKey { type Error = JsValue; fn try_from(pk: &PublicKey) -> Result { let pk_bytes = pk.as_ref(); - let wasm_pk = match pk { - PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.unchecked_into(), - PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.unchecked_into(), - PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.unchecked_into(), + let wasm_pk: WasmPublicKey = match pk { + PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.into(), + PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.into(), + PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.into(), _ => return Err(JsError::new("unsupported PublicKey type").into()), }; + assert_eq!(pk_bytes, &wasm_pk.to_raw_bytes()); + Ok(wasm_pk) } } @@ -284,8 +294,14 @@ impl TryFrom<&'_ PublicKey> for WasmPublicKey { impl TryFrom for PublicKey { type Error = JsValue; fn try_from(pk: WasmPublicKey) -> Result { - let base64_pk = pk.to_iota_public_key(); - PublicKey::decode_base64(&base64_pk).map_err(|e| JsError::new(&e.to_string()).into()) + let key_bytes = pk.to_raw_bytes(); + let key_flag = pk.flag(); + let signature_scheme = SignatureScheme::from_flag_byte(&key_flag).map_err(|e| JsError::new(&e.to_string()))?; + let public_key = PublicKey::try_from_bytes(signature_scheme, &key_bytes).map_err(|e| JsError::new(&e.to_string()))?; + + assert_eq!(&key_bytes, public_key.as_ref()); + + Ok(public_key) } } From d3703c1d9a35d1a57379a885022cfa2d260e5635 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 25 Feb 2025 12:18:59 +0100 Subject: [PATCH 50/63] update to latest version of bindgen --- bindings/wasm/identity_wasm/Cargo.toml | 2 +- bindings/wasm/identity_wasm/package.json | 2 +- bindings/wasm/iota_interaction_ts/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 4d56de184c..2ba9c320e9 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -35,7 +35,7 @@ serde_repr = { version = "0.1", default-features = false } # Want to use the nice API of tokio::sync::RwLock for now even though we can't use threads. tokio = { version = "=1.39.2", default-features = false, features = ["sync"] } tsify = "0.4.5" -wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 312eccd77c..7a623ff0d1 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -13,7 +13,7 @@ "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", - "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", + "build:nodejs": "npm run build:src && npm run bundle:nodejs", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "typedoc && npm run fix_docs", "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist identity_wasm resolve", diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index c8928cc910..26f736d24e 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -14,7 +14,7 @@ "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool-signer", "bundle:nodejs": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", "bundle:web": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", - "build:nodejs": "npm run build:src:node && npm run bundle:nodejs && wasm-opt -O node/iota_interaction_ts_bg.wasm -o node/iota_interaction_ts_bg.wasm", + "build:nodejs": "npm run build:src:node && npm run bundle:nodejs", "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/iota_interaction_ts_bg.wasm -o web/iota_interaction_ts_bg.wasm", "build:docs": "typedoc && npm run fix_docs", "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist iota_interaction_ts resolve", From 6a9178bdabbc6381b950a7332ab5ad26afdf4dc3 Mon Sep 17 00:00:00 2001 From: Sebastian Wolfram Date: Tue, 25 Feb 2025 13:15:31 +0100 Subject: [PATCH 51/63] update storage singer to use Uint8Array for public key internally --- .../src/storage/wasm_storage_signer.rs | 34 +++++-------------- .../src/storage/wasm_transaction_signer.rs | 31 ++++++++++------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs index d5ce32e5ba..5690c33025 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -1,17 +1,10 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use async_trait::async_trait; -use identity_iota::iota_interaction::types::crypto::PublicKey; -use identity_iota::iota_interaction::types::crypto::Signature; -use identity_iota::iota_interaction::IotaKeySignature; use identity_iota::storage::KeyId; use identity_iota::storage::StorageSigner; -use identity_iota::verification::jwk::JwkParams; -use identity_iota::verification::jwu; use iota_interaction_ts::WasmIotaSignature; use iota_interaction_ts::WasmPublicKey; -use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use wasm_bindgen::prelude::*; @@ -22,8 +15,6 @@ use crate::storage::WasmJwkStorage; use crate::storage::WasmKeyIdStorage; use crate::storage::WasmStorage; -use super::WasmTransactionSigner; - #[wasm_bindgen(js_name = StorageSigner)] pub struct WasmStorageSigner { storage: WasmStorage, @@ -67,21 +58,14 @@ impl WasmStorageSigner { .and_then(|pk| WasmPublicKey::try_from(&pk)) .inspect(|wasm_pk| console_log!("WasmStorageSigner's PK: {:?}", &wasm_pk.to_raw_bytes())) } -} - -#[async_trait(?Send)] -impl Signer for WasmStorageSigner { - type KeyId = String; - - async fn sign(&self, data: &Vec) -> std::result::Result { - self.signer().sign(data).await - } - - async fn public_key(&self) -> std::result::Result { - Signer::public_key(&self.signer()).await - } - fn key_id(&self) -> &String { - todo!() + #[wasm_bindgen(js_name = iotaPublicKeyBytes)] + pub async fn iota_public_key_bytes(&self) -> Result> { + Signer::public_key(&self.signer()).await.wasm_result().map(|pk| { + let mut bytes: Vec = Vec::new(); + bytes.extend_from_slice(&[pk.flag()]); + bytes.extend_from_slice(pk.as_ref()); + bytes + }) } -} \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index eb3a3597f9..d41588574b 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -1,21 +1,17 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::cell::LazyCell; -use std::cell::OnceCell; use std::sync::OnceLock; use async_trait::async_trait; use identity_iota::iota::rebased::client::IotaKeySignature; use identity_iota::iota_interaction::types::crypto::PublicKey; use identity_iota::iota_interaction::types::crypto::Signature; +use identity_iota::iota_interaction::types::crypto::SignatureScheme; use iota_interaction_ts::WasmIotaSignature; -use iota_interaction_ts::WasmPublicKey; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsCast; -use wasm_bindgen::JsError; use crate::error::Result; @@ -27,6 +23,7 @@ import { Signature } from "@iota/iota-sdk/client"; interface TransactionSigner { sign: (data: Uint8Array) => Promise; publicKey: () => Promise; + iotaPublicKeyBytes: () => Promise; keyId: () => string; } "#; @@ -39,8 +36,8 @@ extern "C" { #[wasm_bindgen(method, structural, catch)] pub async fn sign(this: &WasmTransactionSigner, data: &[u8]) -> Result; - #[wasm_bindgen(js_name = "publicKey", method, structural, catch)] - pub async fn public_key(this: &WasmTransactionSigner) -> Result; + #[wasm_bindgen(js_name = "iotaPublicKeyBytes", method, structural, catch)] + pub async fn iota_public_key_bytes(this: &WasmTransactionSigner) -> Result; #[wasm_bindgen(js_name = "keyId", method, structural)] pub fn key_id(this: &WasmTransactionSigner) -> String; @@ -59,13 +56,23 @@ impl Signer for WasmTransactionSigner { } async fn public_key(&self) -> std::result::Result { - self.public_key().await.and_then(|v| { - console_log!("WasmTransactionSigner's PK: {:?}", &v.to_raw_bytes()); - v.try_into() - }).map_err(|err| { - let details = String::default(); + let uint8_array = self.iota_public_key_bytes().await.map_err(|err| { + let details = err.as_string().map(|v| format!("; {}", v)).unwrap_or_default(); let message = format!("could not get public key{details}"); SecretStorageError::KeyNotFound(message) + })?; + + let raw_bytes = uint8_array.to_vec(); + let signature_scheme = SignatureScheme::from_flag_byte(&raw_bytes[0]).map_err(|err| { + let details = format!("; {}", err); + let message = format!("could parse scheme flag of public key, {details}"); + SecretStorageError::Other(anyhow::anyhow!(message)) + })?; + + PublicKey::try_from_bytes(signature_scheme, &raw_bytes[1..]).map_err(|err| { + let details = format!("; {}", err); + let message = format!("could parse public key from bytes, {details}"); + SecretStorageError::Other(anyhow::anyhow!(message)) }) } From d74b28c132c225165fae906c7dc19f8ead880439 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 25 Feb 2025 13:50:27 +0100 Subject: [PATCH 52/63] cleanup --- .../wasm/identity_wasm/examples/src/util.ts | 7 - .../identity_wasm/src/iota/iota_document.rs | 1 - bindings/wasm/identity_wasm/src/iota/mod.rs | 1 - bindings/wasm/identity_wasm/src/lib.rs | 6 +- .../src/rebased/client_dummy/identity.rs | 152 ------------------ .../src/rebased/client_dummy/mod.rs | 12 -- .../rebased/client_dummy/multicontroller.rs | 56 ------- .../identity_wasm/src/rebased/identity.rs | 30 +--- .../wasm/identity_wasm/src/rebased/mod.rs | 10 +- .../src/rebased/multicontroller.rs | 74 --------- .../src/rebased/proposals/config_change.rs | 7 +- .../src/rebased/proposals/mod.rs | 22 +-- .../wasm/identity_wasm/src/rebased/types.rs | 37 ----- .../src/rebased/wasm_identity_client.rs | 18 +-- .../rebased/wasm_identity_client_read_only.rs | 10 +- .../src/storage/wasm_transaction_signer.rs | 6 +- 16 files changed, 45 insertions(+), 404 deletions(-) delete mode 100644 bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs delete mode 100644 bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs delete mode 100644 bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs delete mode 100644 bindings/wasm/identity_wasm/src/rebased/multicontroller.rs delete mode 100644 bindings/wasm/identity_wasm/src/rebased/types.rs diff --git a/bindings/wasm/identity_wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts index a559a8538f..4ed48d0941 100644 --- a/bindings/wasm/identity_wasm/examples/src/util.ts +++ b/bindings/wasm/identity_wasm/examples/src/util.ts @@ -8,15 +8,12 @@ import { JwkMemStore, JwsAlgorithm, KeyIdMemStore, - KeytoolSigner, MethodScope, Storage, StorageSigner, } from "@iota/identity-wasm/node"; import { IotaClient } from "@iota/iota-sdk/client"; -import { Ed25519PublicKey } from "@iota/iota-sdk/keypairs/ed25519"; import { getFaucetHost, requestIotaFromFaucetV0 } from "@iota/iota-sdk/faucet"; -import assert = require("assert"); export const IOTA_IDENTITY_PKG_ID = process.env.IOTA_IDENTITY_PKG_ID || "0x7a67dd504eb1291958495c71a07d20985951648dd5ebf01ac921a50257346818"; @@ -62,12 +59,8 @@ export async function getFundedClient(storage: Storage): Promise if (typeof publicKeyJwk === "undefined") { throw new Error("failed to derive public JWK from generated JWK"); } - console.log(publicKeyJwk); let keyId = generate.keyId(); - const pk_raw_bytes = Buffer.from(publicKeyJwk.paramsOkp()?.x!, 'base64url'); - const recreatedPk = new Ed25519PublicKey(pk_raw_bytes); - console.log(recreatedPk); // create signer from storage let signer = new StorageSigner(storage, keyId, publicKeyJwk); const identityClient = await IdentityClient.create(identityClientReadOnly, signer); diff --git a/bindings/wasm/identity_wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs index 41cd17cc7b..d415863033 100644 --- a/bindings/wasm/identity_wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -25,7 +25,6 @@ use identity_iota::storage::storage::JwsSignatureOptions; use identity_iota::verification::jose::jws::JwsAlgorithm; use identity_iota::verification::MethodScope; use identity_iota::verification::VerificationMethod; -use iota_sdk::types::TryFromDto; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; diff --git a/bindings/wasm/identity_wasm/src/iota/mod.rs b/bindings/wasm/identity_wasm/src/iota/mod.rs index fa68380d80..6c1df7fea0 100644 --- a/bindings/wasm/identity_wasm/src/iota/mod.rs +++ b/bindings/wasm/identity_wasm/src/iota/mod.rs @@ -1,7 +1,6 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub(crate) use identity_client::WasmIotaIdentityClient; pub use identity_client_ext::PromiseIotaDocument; pub use iota_did::WasmIotaDID; pub(crate) use iota_document::IotaDocumentLock; diff --git a/bindings/wasm/identity_wasm/src/lib.rs b/bindings/wasm/identity_wasm/src/lib.rs index a8116d0183..b6e4680d49 100644 --- a/bindings/wasm/identity_wasm/src/lib.rs +++ b/bindings/wasm/identity_wasm/src/lib.rs @@ -31,12 +31,12 @@ pub mod sd_jwt; pub mod storage; pub mod verification; -#[cfg(feature = "dummy-client")] -// Currently it's unclear if this module will be removed or can be used for integration or unit tests. -pub(crate) mod rebased; #[cfg(feature = "dummy-client")] // Remove this module when working on [Issue #1445 Replace mocked Identity client with real Identity client] pub(crate) mod obsolete; +#[cfg(feature = "dummy-client")] +// Currently it's unclear if this module will be removed or can be used for integration or unit tests. +pub(crate) mod rebased; /// Initializes the console error panic hook for better error messages #[wasm_bindgen(start)] diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs deleted file mode 100644 index ce65f3a663..0000000000 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/identity.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// This file has been moved here from identity_iota_core/src/client_dummy. -// The file will be removed after the TS-Client-SDK is integrated. -// The file provides a POC for the wasm-bindgen glue code needed to -// implement the TS-Client-SDK integration. - -use std::collections::HashMap; -use std::str::FromStr; - -use serde; -use serde::Deserialize; -use serde::Serialize; - -use super::DummySigner; -use super::Multicontroller; -use super::Proposal; -use identity_iota::iota::rebased::client::IdentityClient; -use identity_iota::iota::rebased::Error; -use identity_iota::iota::IotaDocument; -use identity_iota::iota_interaction::rpc_types::IotaObjectData; -use identity_iota::iota_interaction::rpc_types::OwnedObjectRef; -use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::types::base_types::ObjectID; -use identity_iota::iota_interaction::types::id::UID; -use identity_iota::iota_interaction::IotaClientTrait; -use iota_interaction_ts::error::TsSdkError; - -#[derive(Debug, Deserialize, Serialize)] -pub struct OnChainIdentity { - pub id: UID, - pub did_doc: Multicontroller>, - #[serde(skip)] - pub obj_ref: Option, - // used to have something to return a reference for in getter test - #[serde(skip)] - pub proposals: HashMap, -} - -impl OnChainIdentity { - pub fn is_shared(&self) -> bool { - true - } - - pub fn proposals(&self) -> &HashMap { - &self.proposals - } - - pub fn update_did_document(self, updated_doc: IotaDocument) -> ProposalBuilder - where - T: IotaClientTrait, - { - ProposalBuilder::new(self, ProposalAction::UpdateDocument(updated_doc)) - } - - pub fn deactivate_did(self) -> ProposalBuilder - where - T: IotaClientTrait, - { - ProposalBuilder::new(self, ProposalAction::Deactivate) - } - - pub async fn get_history( - &self, - _client: &IdentityClient, - _last_version: Option<&IotaObjectData>, - _page_size: Option, - ) -> Result, Error> - where - T: IotaClientTrait, - { - Ok(vec![]) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum ProposalAction { - UpdateDocument(IotaDocument), - Deactivate, -} - -#[derive(Debug)] -pub struct ProposalBuilder {} - -impl ProposalBuilder { - pub fn new(_identity: OnChainIdentity, _action: ProposalAction) -> Self { - Self {} - } - - pub fn expiration_epoch(self, _exp: u64) -> Self { - self - } - - pub fn key(self, _key: String) -> Self { - self - } - - pub fn gas_budget(self, _amount: u64) -> Self { - self - } - - pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result, Error> - where - C: IotaClientTrait, - { - Ok(Some(Proposal {})) - } -} - -#[derive(Debug)] -pub struct IdentityBuilder {} - -impl IdentityBuilder { - pub fn new(_did_doc: &[u8], _package_id: ObjectID) -> Self { - Self {} - } - - pub fn controller(self, _address: IotaAddress, _voting_power: u64) -> Self { - self - } - - pub fn threshold(self, _threshold: u64) -> Self { - self - } - - pub fn gas_budget(self, _gas_budget: u64) -> Self { - self - } - - pub fn controllers(self, _controllers: I) -> Self - where - I: IntoIterator, - { - self - } - - pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result - where - C: IotaClientTrait, - { - Ok(OnChainIdentity { - id: UID::new( - ObjectID::from_str("did:iota:foobar:0x0000000000000000000000000000000000000000000000000000000000000000") - .map_err(|e| Error::InvalidArgument(e.to_string()))?, - ), - did_doc: Multicontroller::new(vec![1u8, 2u8, 3u8]), - obj_ref: None, - proposals: HashMap::new(), - }) - } -} diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs deleted file mode 100644 index cc7b992a9d..0000000000 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod identity; -mod multicontroller; - -pub(crate) use identity::*; -pub(crate) use multicontroller::*; - -// dummy types, have to be replaced with actual types later on -pub(crate) type DummySigner = str; -pub(crate) type Hashable = Vec; diff --git a/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs deleted file mode 100644 index 023ca74cdc..0000000000 --- a/bindings/wasm/identity_wasm/src/rebased/client_dummy/multicontroller.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// This file has been moved here from identity_iota_core/src/client_dummy. -// The file will be removed after the TS-Client-SDK is integrated. -// The file provides a POC for the wasm-bindgen glue code needed to -// implement the TS-Client-SDK integration. - -use std::collections::HashMap; - -use serde::Deserialize; -use serde::Serialize; - -use identity_iota::iota_interaction::types::base_types::ObjectID; - -use super::Hashable; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Proposal {} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Multicontroller { - controlled_value: T, - controllers: HashMap, u64>, - proposals: HashMap, -} - -impl Multicontroller { - /// TODO: remove this, added to test interface in ts - pub fn new(controlled_value: T) -> Self { - Self { - controlled_value, - controllers: HashMap::new(), - proposals: HashMap::new(), - } - } - - pub fn controlled_value(&self) -> &T { - &self.controlled_value - } - pub fn threshold(&self) -> u64 { - 123 - } - pub fn controller_voting_power(&self, _controller_cap_id: ObjectID) -> Option { - Some(123) - } - pub fn proposals(&self) -> &HashMap { - &self.proposals - } - pub fn into_inner(self) -> T { - self.controlled_value - } - pub fn has_member(&self, _cap_id: ObjectID) -> bool { - true - } -} diff --git a/bindings/wasm/identity_wasm/src/rebased/identity.rs b/bindings/wasm/identity_wasm/src/rebased/identity.rs index 30f1a3ed44..f31a660cc3 100644 --- a/bindings/wasm/identity_wasm/src/rebased/identity.rs +++ b/bindings/wasm/identity_wasm/src/rebased/identity.rs @@ -11,7 +11,6 @@ use identity_iota::iota::rebased::transaction::TransactionOutputInternal; use identity_iota::iota::IotaDocument; use iota_interaction_ts::AdapterNativeResponse; use tokio::sync::RwLock; -use tsify::Tsify; use wasm_bindgen::prelude::*; use iota_interaction_ts::bindings::WasmIotaObjectData; @@ -21,18 +20,14 @@ use crate::error::Result; use crate::error::WasmResult; use crate::iota::WasmIotaDocument; -use super::client_dummy::ProposalAction; -// use super::client_dummy::DummySigner; -// use super::client_dummy::ProposalAction; -// use super::client_dummy::ProposalBuilder; use super::proposals::StringCouple; use super::proposals::WasmConfigChange; use super::proposals::WasmCreateConfigChangeProposalTx; use super::proposals::WasmCreateDeactivateDidProposalTx; use super::proposals::WasmCreateSendProposalTx; use super::proposals::WasmCreateUpdateDidProposalTx; -use super::types::WasmIotaAddress; use super::WasmIdentityClient; +use super::WasmIotaAddress; /// Helper type for `WasmIdentityBuilder::controllers`. /// Has getters to support `Clone` for serialization @@ -116,9 +111,9 @@ impl WasmOnChainIdentity { #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below pub async fn get_history( &self, - client: WasmIdentityClient, - last_version: Option, - page_size: Option, + _client: WasmIdentityClient, + _last_version: Option, + _page_size: Option, ) -> Result { unimplemented!("WasmOnChainIdentity::get_history"); // let rs_history = self @@ -143,23 +138,6 @@ const WASM_ON_CHAIN_IDENTITY_TYPES: &str = r###" } "###; -#[derive(Tsify, Serialize, Deserialize)] -#[tsify(into_wasm_abi, from_wasm_abi)] -#[serde(rename = "ProposalAction")] -pub enum WasmProposalAction { - UpdateDocument(IotaDocument), - Deactivate, -} - -impl From for ProposalAction { - fn from(val: WasmProposalAction) -> Self { - match val { - WasmProposalAction::UpdateDocument(doc) => ProposalAction::UpdateDocument(doc), - WasmProposalAction::Deactivate => ProposalAction::Deactivate, - } - } -} - // TODO: remove the following comment and commented out code if we don't run into a rename issue // -> in case `serde(rename` runs into issues with properties with renamed types still having the // original type, see [here](https://github.com/madonoharu/tsify/issues/43) for an example diff --git a/bindings/wasm/identity_wasm/src/rebased/mod.rs b/bindings/wasm/identity_wasm/src/rebased/mod.rs index ffa168c0e3..95907a37a5 100644 --- a/bindings/wasm/identity_wasm/src/rebased/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/mod.rs @@ -1,16 +1,14 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -mod client_dummy; mod identity; -mod multicontroller; -mod types; +mod proposals; mod wasm_identity_client; mod wasm_identity_client_read_only; -mod proposals; pub use identity::*; -pub use multicontroller::*; -pub use types::*; pub use wasm_identity_client::*; pub use wasm_identity_client_read_only::*; + +pub type WasmIotaAddress = String; +pub type WasmObjectID = String; diff --git a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs b/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs deleted file mode 100644 index 4f751b45a3..0000000000 --- a/bindings/wasm/identity_wasm/src/rebased/multicontroller.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::core::Object; -use identity_iota::iota::rebased::migration::Proposal; -use identity_iota::iota::rebased::proposals::{DeactivateDid, UpdateDidDocument}; - -use super::client_dummy::Multicontroller; -use wasm_bindgen::prelude::*; - -use crate::common::MapStringAny; - -use super::types::WasmObjectID; - - -#[wasm_bindgen(js_name = Multicontroller)] -pub struct WasmMulticontroller(pub(crate) Multicontroller>); - -#[wasm_bindgen(js_class = Multicontroller)] -impl WasmMulticontroller { - /// TODO: remove this, added to test interface in ts - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self(Multicontroller::>::new(vec![])) - } - - #[wasm_bindgen(js_name = controlledValue)] - pub fn controlled_value(&self) -> Vec { - self.0.controlled_value().clone() - } - - pub fn threshold(&self) -> u64 { - self.0.threshold() - } - - #[wasm_bindgen(js_name = controllerVotingPower)] - pub fn controller_voting_power(&self, controller_cap_id: WasmObjectID) -> Option { - self.0.controller_voting_power( - controller_cap_id - .parse() - .expect("Could not parse controller_cap_id into ObjectID"), - ) - } - - pub fn proposals(&self) -> Result { - let object_result: Result = self - .0 - .proposals() - .iter() - .map(|(k, v)| { - serde_json::to_value(v) - .map(|json_value| (k.to_owned(), json_value)) - .map_err(|err| JsValue::from_str(&format!("failed to serialize value; {err}"))) - }) - .collect(); - - MapStringAny::try_from(object_result?) - } - - /// Behaves as as alias for `controlled_value`, as TypeScript does not reflect consuming `self` - /// very well. Using the `Multicontroller` afterwards instance would not produce a compiler error - /// but would return a "null pointer passed to rust" error. - /// - /// TODO: consider removing this function from the bindings - #[wasm_bindgen(js_name = intoInner)] - pub fn into_inner(&self) -> Vec { - self.controlled_value() - } - - #[wasm_bindgen(js_name = hasMember)] - pub fn has_member(&self, cap_id: WasmObjectID) -> bool { - self.0.has_member(cap_id.parse().expect("cap_id is no valid ObjectID")) - } -} diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs index f60462273c..b639cf05d7 100644 --- a/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/config_change.rs @@ -310,12 +310,17 @@ impl WasmCreateConfigChangeProposalTx { .add_multiple_controllers(controllers_to_add) .remove_multiple_controllers(controllers_to_remove) .update_multiple_controllers(controllers_to_update); - // identity_ref.deactivate_did(); + let builder = if let Some(exp) = self.expiration_epoch { builder.expiration_epoch(exp) } else { builder }; + let builder = if let Some(threshold) = self.threshold { + builder.threshold(threshold) + } else { + builder + }; let tx_output = builder .finish(&client) diff --git a/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs index 86d8638511..ec6c9d1172 100644 --- a/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs +++ b/bindings/wasm/identity_wasm/src/rebased/proposals/mod.rs @@ -1,26 +1,26 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod config_change; mod deactivate_did; -mod update_did; mod send; -mod config_change; +mod update_did; +pub use config_change::*; pub use deactivate_did::*; -pub use update_did::*; pub use send::*; -pub use config_change::*; +pub use update_did::*; use std::collections::HashMap; use std::collections::HashSet; -use identity_iota::iota_interaction::types::base_types::ObjectID; use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::base_types::ObjectID; +use js_sys::JsString; +use js_sys::Reflect; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsValue; use wasm_bindgen::JsCast as _; -use js_sys::Reflect; -use js_sys::JsString; +use wasm_bindgen::JsValue; #[wasm_bindgen] extern "C" { @@ -53,7 +53,9 @@ impl From for (String, String) { impl From<(String, String)> for StringCouple { fn from(value: (String, String)) -> Self { - serde_wasm_bindgen::to_value(&value).expect("a string couple can be serialized to JS").unchecked_into() + serde_wasm_bindgen::to_value(&value) + .expect("a string couple can be serialized to JS") + .unchecked_into() } } @@ -100,4 +102,4 @@ impl TryFrom<&'_ HashSet> for StringSet { let js_value = serde_wasm_bindgen::to_value(value)?; Ok(js_value.dyn_into()?) } -} \ No newline at end of file +} diff --git a/bindings/wasm/identity_wasm/src/rebased/types.rs b/bindings/wasm/identity_wasm/src/rebased/types.rs deleted file mode 100644 index 531e16177e..0000000000 --- a/bindings/wasm/identity_wasm/src/rebased/types.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::de::DeserializeOwned; -use wasm_bindgen::prelude::*; - -use crate::error::wasm_error; -use crate::error::Result; - -pub use iota_interaction_ts::bindings::*; - -pub fn into_sdk_type>(wasm_type_instance: W) -> Result { - let js_value: JsValue = wasm_type_instance.into(); - //serde_wasm_bindgen::from_value::(js_value).map_err(wasm_error) - match serde_wasm_bindgen::from_value::(js_value.clone()) { - Ok(ret_val) => Ok(ret_val), - Err(e) => { - // TODO: Replace all console_log! usages by proper Error management and Result types. - // Use console_log! only for debug purposes - console_log!( - "[identity_wasm::rebased::types - fn into_sdk_type]\n js_value: {:?}\n Error: {:?}", - js_value, - e - ); - Err(wasm_error(e)) - } - } -} - -pub(crate) type WasmIotaAddress = String; -pub(crate) type WasmObjectID = String; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseBalance; -} diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs index 100eff63cf..8158ac670b 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client.rs @@ -1,20 +1,14 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::rc::Rc; use std::ops::Deref; - -use fastcrypto::ed25519::Ed25519PublicKey; -use fastcrypto::traits::EncodeDecodeBase64; -use fastcrypto::traits::ToFromBytes; +use std::rc::Rc; use identity_iota::iota::rebased::client::IdentityClient; use identity_iota::iota::rebased::client::PublishDidTx; use identity_iota::iota::rebased::transaction::TransactionInternal; use identity_iota::iota::rebased::transaction::TransactionOutputInternal; -use identity_iota::iota_interaction::types::base_types::IotaAddress; -use identity_iota::iota_interaction::types::crypto::PublicKey; use iota_interaction_ts::bindings::WasmExecutionStatus; use iota_interaction_ts::bindings::WasmOwnedObjectRef; use iota_interaction_ts::WasmPublicKey; @@ -25,6 +19,8 @@ use iota_interaction_ts::AdapterNativeResponse; use super::IdentityContainer; use super::WasmIdentityBuilder; use super::WasmIdentityClientReadOnly; +use super::WasmIotaAddress; +use super::WasmObjectID; use crate::error::wasm_error; use crate::iota::IotaDocumentLock; @@ -34,9 +30,6 @@ use crate::storage::WasmTransactionSigner; use identity_iota::iota::IotaDocument; use wasm_bindgen::prelude::*; -use super::types::WasmIotaAddress; -use super::types::WasmObjectID; - #[wasm_bindgen(getter_with_clone, inspectable, js_name = IotaTransactionBlockResponseEssence)] pub struct WasmIotaTransactionBlockResponseEssence { #[wasm_bindgen(js_name = effectsExist)] @@ -61,7 +54,10 @@ impl Deref for WasmIdentityClient { #[wasm_bindgen(js_class = IdentityClient)] impl WasmIdentityClient { #[wasm_bindgen(js_name = create)] - pub async fn new(client: WasmIdentityClientReadOnly, signer: WasmTransactionSigner) -> Result { + pub async fn new( + client: WasmIdentityClientReadOnly, + signer: WasmTransactionSigner, + ) -> Result { let inner_client = IdentityClient::new(client.0, signer).await?; Ok(WasmIdentityClient(inner_client)) } diff --git a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs index 6f7760ae70..ccd90a4d08 100644 --- a/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs +++ b/bindings/wasm/identity_wasm/src/rebased/wasm_identity_client_read_only.rs @@ -10,7 +10,7 @@ use identity_iota::iota_interaction::types::base_types::ObjectID; use iota_interaction_ts::bindings::WasmIotaClient; use wasm_bindgen::prelude::*; -use super::types::WasmObjectID; +use super::WasmObjectID; use super::WasmOnChainIdentity; use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; @@ -52,8 +52,12 @@ impl WasmIdentityClientReadOnly { } #[wasm_bindgen(js_name = createWithPkgId)] - pub async fn new_new_with_pkg_id(iota_client: WasmIotaClient, iota_identity_pkg_id: String) -> Result { - let inner_client = IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; + pub async fn new_new_with_pkg_id( + iota_client: WasmIotaClient, + iota_identity_pkg_id: String, + ) -> Result { + let inner_client = + IdentityClientReadOnly::new_with_pkg_id(iota_client, ObjectID::from_str(&iota_identity_pkg_id)?).await?; Ok(WasmIdentityClientReadOnly(inner_client)) } diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs index d41588574b..f08e2b79ec 100644 --- a/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_transaction_signer.rs @@ -1,8 +1,6 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::sync::OnceLock; - use async_trait::async_trait; use identity_iota::iota::rebased::client::IotaKeySignature; use identity_iota::iota_interaction::types::crypto::PublicKey; @@ -77,7 +75,7 @@ impl Signer for WasmTransactionSigner { } fn key_id(&self) -> &String { - static KEY_ID: OnceLock = OnceLock::new(); - KEY_ID.get_or_init(|| self.key_id()) + // TODO: Yikes! Find a way to work around this. + Box::leak(Box::new(self.key_id())) } } From 56445511ccc2995afc4b10673e6f57ba4ad64bda Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 25 Feb 2025 18:32:37 +0100 Subject: [PATCH 53/63] make KeytoolSigner work with keytool version 0.8.1 --- .../src/bindings/wasm_types.rs | 3 +- identity_iota_core/tests/e2e/client.rs | 2 +- .../src/keytool_signer.rs | 29 ++++++++++++++----- .../src/storage/storage_signer.rs | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs index a659483fd9..22d0150c1f 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -303,7 +303,8 @@ impl TryFrom for PublicKey { let key_bytes = pk.to_raw_bytes(); let key_flag = pk.flag(); let signature_scheme = SignatureScheme::from_flag_byte(&key_flag).map_err(|e| JsError::new(&e.to_string()))?; - let public_key = PublicKey::try_from_bytes(signature_scheme, &key_bytes).map_err(|e| JsError::new(&e.to_string()))?; + let public_key = + PublicKey::try_from_bytes(signature_scheme, &key_bytes).map_err(|e| JsError::new(&e.to_string()))?; assert_eq!(&key_bytes, public_key.as_ref()); diff --git a/identity_iota_core/tests/e2e/client.rs b/identity_iota_core/tests/e2e/client.rs index 22f7368c00..41c663ba7d 100644 --- a/identity_iota_core/tests/e2e/client.rs +++ b/identity_iota_core/tests/e2e/client.rs @@ -3,8 +3,8 @@ use std::ops::Deref; -use crate::common::TestClient; use crate::common::get_funded_test_client; +use crate::common::TestClient; use identity_iota_core::rebased::migration; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::IotaDocument; diff --git a/identity_iota_interaction/src/keytool_signer.rs b/identity_iota_interaction/src/keytool_signer.rs index 99f53006f8..716606b766 100644 --- a/identity_iota_interaction/src/keytool_signer.rs +++ b/identity_iota_interaction/src/keytool_signer.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use crate::types::base_types::IotaAddress; use crate::types::crypto::PublicKey; use crate::types::crypto::Signature; +use crate::types::crypto::SignatureScheme; use crate::IotaKeySignature; use crate::TransactionDataBcs; use anyhow::anyhow; @@ -14,10 +15,10 @@ use anyhow::Context as _; use async_trait::async_trait; use fastcrypto::encoding::Base64; use fastcrypto::encoding::Encoding; -use fastcrypto::traits::EncodeDecodeBase64; use jsonpath_rust::JsonPathQuery as _; use secret_storage::Error as SecretStorageError; use secret_storage::Signer; +use serde::Deserialize; use serde_json::Value; /// Builder structure to ease the creation of a [KeytoolSigner]. @@ -170,9 +171,9 @@ async fn get_active_address(iota_bin: impl AsRef) -> anyhow::Result, address: IotaAddress) -> anyhow::Result { - let query = format!("$[?(@.iotaAddress==\"{}\")].publicBase64Key", address); + let query = format!("$[?(@.iotaAddress==\"{}\")]", address); - let base64_pk_json = run_iota_cli_command_with_bin(iota_bin, "keytool list") + let pk_json_data = run_iota_cli_command_with_bin(iota_bin, "keytool list") .await .and_then(|json_value| { json_value @@ -182,9 +183,23 @@ async fn get_key(iota_bin: impl AsRef, address: IotaAddress) -> anyhow::Re .context("key not found") .map(Value::take) })?; - let base64_pk = base64_pk_json - .as_str() - .context("invalid JSON public key representation")?; + let Ok(KeytoolPublicKeyHelper { + public_base64_key, + flag, + }) = serde_json::from_value(pk_json_data) + else { + return Err(anyhow!("invalid key format")); + }; + + let signature_scheme = SignatureScheme::from_flag_byte(&flag).context(format!("invalid signature flag `{flag}`"))?; + let pk_bytes = Base64::decode(&public_base64_key).context("invalid base64 encoding for key")?; + + PublicKey::try_from_bytes(signature_scheme, &pk_bytes).map_err(|e| anyhow!("{e:?}")) +} - PublicKey::decode_base64(base64_pk).context("failed to decode base64 public key") +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct KeytoolPublicKeyHelper { + public_base64_key: String, + flag: u8, } diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index d1654101ab..0c7c1707d6 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -11,8 +11,8 @@ use identity_iota_interaction::types::crypto::PublicKey; use identity_iota_interaction::types::crypto::Signature; use identity_iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::TransactionDataBcs; use identity_iota_interaction::OptionalSync; +use identity_iota_interaction::TransactionDataBcs; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwk::JwkParamsEc; From 3c3d51fcc1d1db64266266284d96311336686bf8 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Tue, 25 Feb 2025 18:40:16 +0100 Subject: [PATCH 54/63] update CI to use wasm-bindgen version 0.2.100 --- .github/workflows/shared-build-wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 9f64117e7b..de090c0611 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -57,7 +57,7 @@ jobs: - name: Install wasm-bindgen-cli uses: jetli/wasm-bindgen-action@24ba6f9fff570246106ac3f80f35185600c3f6c9 with: - version: '0.2.93' + version: '0.2.100' - name: Setup sccache uses: './.github/actions/rust/sccache/setup-sccache' From a2e4ac074faf69cc69db586b1c99cfb471d373d1 Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 10:52:59 +0100 Subject: [PATCH 55/63] use node 20 in CI --- .github/workflows/shared-build-wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index de090c0611..a489b43feb 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -67,7 +67,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 20.x - name: Install JS dependencies run: npm ci From da69e6df0550fb2ccde4c803cc78b776a7d0dece Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 10:57:34 +0100 Subject: [PATCH 56/63] dprint fmt --- bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index 403d087652..166aeb93ab 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -170,7 +170,7 @@ export async function executeTransaction( /** * Helper function to pause execution. - */ + */ export function sleep(durationMs: number) { return new Promise(resolve => setTimeout(resolve, durationMs)); } From bc50f4031ff227d2793fb58241d40dd69fc4f84b Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 12:34:17 +0100 Subject: [PATCH 57/63] use latest toolchain for identity_wasm --- bindings/wasm/identity_wasm/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/identity_wasm/rust-toolchain.toml b/bindings/wasm/identity_wasm/rust-toolchain.toml index ab4db37df0..825d39b571 100644 --- a/bindings/wasm/identity_wasm/rust-toolchain.toml +++ b/bindings/wasm/identity_wasm/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.84.0" +channel = "stable" components = ["rustfmt"] targets = ["wasm32-unknown-unknown"] profile = "minimal" From cb0aa9fda7ca5ccb384388b22c1fa1ac9dad8e4f Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 12:41:46 +0100 Subject: [PATCH 58/63] add missing licence header --- .../wasm/iota_interaction_ts/src/bindings/keytool_signer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs index 7eca700f73..2731278d6e 100644 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool_signer.rs @@ -1,3 +1,6 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use std::path::PathBuf; use std::str::FromStr; From 430285beb48f4685a0eb06d24590c718e7466cfb Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 13:36:08 +0100 Subject: [PATCH 59/63] use node 20 in all CI actions --- .github/actions/publish/publish-wasm/action.yml | 2 +- .github/actions/release/bump-versions/action.yml | 2 +- .github/workflows/build-and-test.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/publish/publish-wasm/action.yml b/.github/actions/publish/publish-wasm/action.yml index 6bb1171fbd..bf08b06b3b 100644 --- a/.github/actions/publish/publish-wasm/action.yml +++ b/.github/actions/publish/publish-wasm/action.yml @@ -20,7 +20,7 @@ runs: - name: Set up Node.js uses: actions/setup-node@v2 with: - node-version: '16.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Download bindings/wasm artifacts diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index cf1cf65781..e5155a18e6 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -51,7 +51,7 @@ runs: uses: actions/setup-node@v2 if: ${{inputs.release-target == 'wasm'}} with: - node-version: '16.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Bump Wasm npm package version diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 431fa3de2c..0d77b0279f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -204,7 +204,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 20.x - name: Install JS dependencies run: npm ci @@ -255,7 +255,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 20.x - name: Install JS dependencies run: npm ci @@ -300,7 +300,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 20.x - name: Install JS dependencies run: npm ci From 7a3edf4214ffb1b6c7e6390faf3789c395bd798d Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 14:06:42 +0100 Subject: [PATCH 60/63] avoid fauceting if address already has enough funds --- identity_iota_core/tests/e2e/common.rs | 34 +++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index 676623d354..17f598f0a1 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -38,6 +38,7 @@ use lazy_static::lazy_static; use move_core_types::ident_str; use move_core_types::language_storage::StructTag; use secret_storage::Signer; +use serde::Deserialize; use serde_json::Value; use std::io::Write; use std::ops::Deref; @@ -168,6 +169,34 @@ fn get_public_key_bytes(sender_public_jwk: &Jwk) -> Result, anyhow::Erro identity_jose::jwu::decode_b64(public_key_base_64).map_err(|err| anyhow!("could not decode base64 public key; {err}")) } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GasObjectHelper { + nanos_balance: u64, +} + +async fn get_balance(address: IotaAddress) -> anyhow::Result { + let output = Command::new("iota") + .arg("client") + .arg("gas") + .arg("--json") + .arg(address.to_string()) + .output() + .await?; + + if !output.status.success() { + let error_msg = String::from_utf8(output.stderr)?; + anyhow::bail!("failed to get balance: {error_msg}"); + } + + let balance = serde_json::from_slice::>(&output.stdout)? + .into_iter() + .map(|gas_info| gas_info.nanos_balance) + .sum(); + + Ok(balance) +} + #[derive(Clone)] pub struct TestClient { client: Arc>, @@ -192,7 +221,10 @@ impl TestClient { let client = IotaClientBuilder::default().build(&api_endpoint).await?; let package_id = PACKAGE_ID.get_or_try_init(|| init(&client)).await.copied()?; - request_funds(&address).await?; + let balance = get_balance(address).await?; + if balance < TEST_GAS_BUDGET { + request_funds(&address).await?; + } let storage = Arc::new(Storage::new(JwkMemStore::new(), KeyIdMemstore::new())); let identity_client = IdentityClientReadOnly::new_with_pkg_id(client, package_id).await?; From 8596284fce8c655fdce2b8a5ca8348d5679a25eb Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 16:45:46 +0100 Subject: [PATCH 61/63] use binding/wasm/target to store WASM compilation artifacts --- bindings/wasm/identity_wasm/package.json | 6 +++--- bindings/wasm/iota_interaction_ts/package.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 987e92f77c..f083121216 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -10,9 +10,9 @@ "example": "examples" }, "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", - "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", - "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", + "bundle:nodejs": "wasm-bindgen ../target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", + "bundle:web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", "build:nodejs": "npm run build:src && npm run bundle:nodejs", "build:web": "npm run build:src && npm run bundle:web", "build:docs": "typedoc && npm run fix_docs", diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index 7e37bfc1cd..f1002086fd 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -10,10 +10,10 @@ "example": "examples" }, "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool-signer", - "bundle:nodejs": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", - "bundle:web": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", + "bundle:nodejs": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", + "bundle:web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", "build:nodejs": "npm run build:src:node && npm run bundle:nodejs", "build:web": "npm run build:src && npm run bundle:web", "build:docs": "typedoc && npm run fix_docs", From 67725ca52ec955b49b9ae72bf586576fb036475f Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:55:26 +0100 Subject: [PATCH 62/63] Update bindings/wasm/iota_interaction_ts/Cargo.toml Co-authored-by: wulfraem --- bindings/wasm/iota_interaction_ts/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml index 380b43fe36..dd0c77ace4 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -31,7 +31,7 @@ serde-wasm-bindgen = "0.6.5" serde_json.workspace = true thiserror.workspace = true tsify = "0.4.5" -wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } zkryptium = "0.2.2" From e91358629109dc0f2a3c61e0b879c10640166b6b Mon Sep 17 00:00:00 2001 From: umr1352 Date: Wed, 26 Feb 2025 17:12:55 +0100 Subject: [PATCH 63/63] avoid casting to any --- .../lib/iota_client_helpers.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts index 166aeb93ab..ba26adc758 100644 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -142,10 +142,8 @@ export async function executeTransaction( gasBudget?: bigint, ): Promise { const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); - const signature = await signer.sign(txWithGasData) as any; - const base64signature = (signature.Ed25519IotaSignature - || signature.Secp256r1IotaSignature - || signature.Secp256k1IotaSignature) as string; + const signature = await signer.sign(txWithGasData); + const base64signature = getSignatureValue(signature); const response = await iotaClient.executeTransactionBlock({ transactionBlock: txWithGasData, @@ -168,6 +166,20 @@ export async function executeTransaction( return new IotaTransactionBlockResponseAdapter(response); } +function getSignatureValue(signature: Signature): string { + if ("Ed25519IotaSignature" in signature) { + return signature.Ed25519IotaSignature; + } + if ("Secp256k1IotaSignature" in signature) { + return signature.Secp256k1IotaSignature; + } + if ("Secp256r1IotaSignature" in signature) { + return signature.Secp256r1IotaSignature; + } + + throw new Error("invalid `Signature` value given"); +} + /** * Helper function to pause execution. */