Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wasm32 identity client implementation #1517

Merged
merged 37 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8fa2d1c
add wip state for identity client
wulfraem Jan 27, 2025
bf91fa6
add signing and exection for exampe 0 (create did)
wulfraem Feb 5, 2025
6cd3c40
fix smaller TODOs and unwraps
wulfraem Feb 5, 2025
c5b952f
add update did example impl
wulfraem Feb 6, 2025
6e23af1
add example to deactivate a did
wulfraem Feb 7, 2025
e5f751e
apply review suggestions - remove unused code from -1 example
wulfraem Feb 7, 2025
7634aab
update IOTA sdk dependency to `@iota/iota.js`
wulfraem Feb 7, 2025
b965dc2
Apply suggestions from code review
wulfraem Feb 7, 2025
4ee56ba
Apply suggestions from code review
wulfraem Feb 7, 2025
5ad547e
apply review suggestion
wulfraem Feb 7, 2025
8f8c8cc
apply review suggestions
wulfraem Feb 7, 2025
6c23d94
apply reivew suggestions
wulfraem Feb 7, 2025
e30c980
remove unused error variant
wulfraem Feb 10, 2025
84c92fc
add helper function for Uint8 array to pt bcs conversion
wulfraem Feb 11, 2025
dfe8d41
add comment and formatting
wulfraem Feb 11, 2025
aa6c35b
StorageSigner (#1523)
UMR1352 Feb 11, 2025
634d5be
Remove any mention of "Kinesis" from identity_wasm (#1524)
UMR1352 Feb 11, 2025
d679051
Re-add Resolver API for WASM builds (#1526)
wulfraem Feb 19, 2025
bc9c441
Update and re-enable WASM examples (#1528)
wulfraem Feb 19, 2025
f7ffc94
WASM Proposals (#1529)
UMR1352 Feb 19, 2025
86468ed
Add waiting for transaction block availability to TS variant of `exec…
wulfraem Feb 20, 2025
b329aa9
Fix document consuming in WASM when publishing updates (#1541)
wulfraem Feb 20, 2025
ba0f4b7
Reenable Wasm tests (#1536)
eike-hass Feb 21, 2025
fe9c5cc
update how DIDs are fetched from created identity (#1542)
wulfraem Feb 21, 2025
512cb6b
Remove stardust artifacts (#1543)
eike-hass Feb 21, 2025
4ead237
Fix format issues (#1544)
wulfraem Feb 21, 2025
5a53009
Merge remote-tracking branch 'origin/feat/identity-rebased-alpha' int…
wulfraem Feb 21, 2025
eabf83c
fix custom key handler in example
wulfraem Feb 21, 2025
ac8a290
update exampel to use new API flow
wulfraem Feb 21, 2025
75f10ab
bump min engine version
eike-hass Feb 21, 2025
18e9226
fix typedoc warnings
wulfraem Feb 25, 2025
a63e265
Revert "fix typedoc warnings"
wulfraem Feb 25, 2025
eaeea71
WASM full signing ability + Keytool Signer (#1551)
UMR1352 Feb 26, 2025
9ab44eb
Clean packages (#1546)
eike-hass Feb 26, 2025
10cc9b0
Fix typedoc issues (#1552)
wulfraem Feb 27, 2025
d26dfd8
Limit parallel test execution (#1560)
eike-hass Feb 27, 2025
f7e837f
Bump iota version (#1563)
eike-hass Feb 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion bindings/wasm/identity_wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -26,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, 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 }
Expand Down
456 changes: 227 additions & 229 deletions bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts

Large diffs are not rendered by default.

125 changes: 46 additions & 79 deletions bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts
Original file line number Diff line number Diff line change
@@ -1,83 +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 {
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(),
};
}
createDocumentForNetwork,
getClientAndCreateAccount,
getMemstorage,
NETWORK_URL,
} from '../utils_alpha';

/** Demonstrate how to create a DID Document and publish it. */
export async function createIdentity(): Promise<void> {
// create new client to connect to IOTA network
const kinesisClient = new KinesisClient({ url: NETWORK_URL });
const network = await kinesisClient.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 identityClient = await getClientAndCreateAccount(storage);

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(did);
console.log(`Resolved DID document: ${JSON.stringify(resolved, null, 2)}`);
}

106 changes: 48 additions & 58 deletions bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,77 @@
// SPDX-License-Identifier: Apache-2.0

import {
IotaDocument,
IotaIdentityClient,
IotaDID,
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 { IotaClient as KinesisClient } from "@iota/iota.js/client";
import {
createDocumentForNetwork,
getClientAndCreateAccount,
getMemstorage,
NETWORK_URL,
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(),
};
// 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);

// 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 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);

// 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({
id: did.join("#linked-domain"),
type: "LinkedDomains",
serviceEndpoint: "https://iota.org/",
});
document.insertService(service);
document.setMetadataUpdated(Timestamp.nowUTC());

// Remove a verification method.
let originalMethod = document.resolveMethod(fragment) 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(),
});
resolved.insertService(service);
resolved.setMetadataUpdated(Timestamp.nowUTC());

// Publish the output.
const updated: IotaDocument = await didClient.publishDidOutput(secretManager, aliasOutput);
console.log("Updated DID document:", 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)}`);
}
40 changes: 17 additions & 23 deletions bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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.
Expand Down
Loading
Loading