Skip to content

Commit 5aebb31

Browse files
UMR1352wulfraem
andauthored
Refactor Transaction interface (#1621)
* TxBuilder * partial gas data in TxBuilder * Lazily compute programmable transaction * Merge IntoProgrammableTransaction into TxEffect * sponsor tx in build * allow sponsoring with partial gas information * publishing DID document uses new transaction API * update authenticated asset code to use the new transaction API * make IotaTransactionBlockEffects available for WASM * all operations use the new transaction API * ensure tests work * WASM buildable * avoid the use of bcs when possible * fix tests, fmt and clippy * create_did and update_did * fix ts typing * even better ts * re-add ts examples and tests * fix deactivate_did * config change TS * WASM send proposal * clippy * fix partial gas data bug * advanced transaction example * TS advanced transaction example * fix merge bug * fmt, clippy, fixes * fix feature flag issue * Apply suggestions from code review Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * review comments * Apply suggestions from code review Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * review comments * use published version of iota-interaction-ts * use iota-interaction-ts ^0.3.0 * fmt * make tokio an unconditional dependency * tokio issue * Update identity_iota_core/src/rebased/proposals/borrow.rs Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * Tx application error * adapt rust code to use new Transaction::apply * Fix new apply implementations * adapt wasm code to use the new apply function * fix example * fmt, grammar * missing license header * Merge branch 'main' into feat/transaction-interface-refactor * Merge branch 'main' into feat/transaction-interface-refactor * clippy & fmt * fmt clippy * adapt keytool example to use new transaction API * Fix keytool integration --------- Co-authored-by: wulfraem <wulfraem@users.noreply.github.com>
1 parent b31741f commit 5aebb31

File tree

97 files changed

+4618
-2253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+4618
-2253
lines changed

bindings/wasm/identity_wasm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ crate-type = ["cdylib", "rlib"]
1818
[dependencies]
1919
anyhow = "1.0.95"
2020
async-trait = { version = "0.1", default-features = false }
21+
bcs = "0.1.6"
2122
console_error_panic_hook = { version = "0.1" }
2223
fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto" }
2324
identity_ecdsa_verifier = { path = "../../../identity_ecdsa_verifier", default-features = false, features = ["es256", "es256k"] }

bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export async function createIdentity(): Promise<void> {
2424
const { output: identity } = await identityClient
2525
.createIdentity(unpublished)
2626
.finish()
27-
.execute(identityClient);
27+
.buildAndExecute(identityClient);
2828
did = identity.didDocument().id();
2929

3030
// check if we can resolve it via client

bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export async function updateIdentity() {
2525
const { output: identity } = await identityClient
2626
.createIdentity(unpublished)
2727
.finish()
28-
.execute(identityClient);
28+
.buildAndExecute(identityClient);
2929
const did = identity.didDocument().id();
3030

3131
// Resolve the latest state of the document.
@@ -56,10 +56,12 @@ export async function updateIdentity() {
5656
let originalMethod = resolved.resolveMethod(vmFragment1) as VerificationMethod;
5757
await resolved.purgeMethod(storage, originalMethod?.id());
5858

59+
let controllerToken = await identity.getControllerToken(identityClient);
60+
5961
let maybePendingProposal = await identity
60-
.updateDidDocument(resolved.clone())
62+
.updateDidDocument(resolved.clone(), controllerToken!)
6163
.withGasBudget(TEST_GAS_BUDGET)
62-
.execute(identityClient)
64+
.buildAndExecute(identityClient)
6365
.then(result => result.output);
6466

6567
console.assert(maybePendingProposal == null, "the proposal should have been executed right away!");

bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function resolveIdentity() {
2828
const { output: identity } = await identityClient
2929
.createIdentity(unpublished)
3030
.finish()
31-
.execute(identityClient);
31+
.buildAndExecute(identityClient);
3232
const did = identity.didDocument().id();
3333

3434
// Resolve the associated identity and extract the DID document from it.

bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ export async function deactivateIdentity() {
1717
const { output: identity } = await identityClient
1818
.createIdentity(unpublished)
1919
.finish()
20-
.execute(identityClient);
20+
.buildAndExecute(identityClient);
2121
const did = identity.didDocument().id();
2222

2323
// Resolve the latest state of the document.
2424
// Technically this is equivalent to the document above.
2525
const resolved = await identityClient.resolveDid(did);
2626
console.log("Resolved DID document:", JSON.stringify(resolved, null, 2));
2727

28+
const controllerToken = await identity.getControllerToken(identityClient);
2829
// Deactivate the DID.
2930
await identity
30-
.deactivateDid()
31+
.deactivateDid(controllerToken!)
3132
.withGasBudget(TEST_GAS_BUDGET)
32-
.execute(identityClient);
33+
.buildAndExecute(identityClient);
3334

3435
// Resolving a deactivated DID returns an empty DID document
3536
// with its `deactivated` metadata field set to `true`.
@@ -42,9 +43,9 @@ export async function deactivateIdentity() {
4243
// Re-activate the DID by publishing a valid DID document.
4344
console.log("Publishing this:", JSON.stringify(resolved, null, 2));
4445
await identity
45-
.updateDidDocument(resolved)
46+
.updateDidDocument(resolved, controllerToken!)
4647
.withGasBudget(TEST_GAS_BUDGET)
47-
.execute(identityClient);
48+
.buildAndExecute(identityClient);
4849

4950
// Resolve the reactivated DID document.
5051
let resolvedReactivated = await identityClient.resolveDid(did);

bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@ export async function deleteIdentityDID() {
1717
const { output: identity } = await identityClient
1818
.createIdentity(unpublished)
1919
.finish()
20-
.execute(identityClient);
20+
.buildAndExecute(identityClient);
2121
const did = identity.didDocument().id();
2222

2323
// Resolve the latest state of the document.
2424
// Technically this is equivalent to the document above.
2525
const resolved = await identityClient.resolveDid(did);
2626
console.log("Resolved DID document:", JSON.stringify(resolved, null, 2));
2727

28+
const controllerToken = await identity.getControllerToken(identityClient);
29+
2830
// delete the DID.
2931
await identity
30-
.deleteDid()
32+
.deleteDid(controllerToken!)
3133
.withGasBudget(TEST_GAS_BUDGET)
32-
.execute(identityClient);
34+
.buildAndExecute(identityClient);
3335

3436
// After an Identity's DID has been deleted, the document will be
3537
// empty and inactive. Identity.hasDeletedDid must return `true`.
@@ -49,9 +51,9 @@ export async function deleteIdentityDID() {
4951
// Trying to update a deleted DID must fail!
5052
try {
5153
await identity
52-
.updateDidDocument(resolved)
54+
.updateDidDocument(resolved, controllerToken!)
5355
.withGasBudget(TEST_GAS_BUDGET)
54-
.execute(identityClient);
56+
.buildAndExecute(identityClient);
5557
} catch (_) {
5658
console.log("A deleted DID cannot be updated!");
5759
}

bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export async function createVC() {
3030
const { output: issuerIdentity } = await issuerClient
3131
.createIdentity(unpublishedIssuerDocument)
3232
.finish()
33-
.execute(issuerClient);
33+
.buildAndExecute(issuerClient);
3434
const issuerDocument = issuerIdentity.didDocument();
3535

3636
// Create an identity for the holder, and publish DID document for it, in this case also the subject.
@@ -40,7 +40,7 @@ export async function createVC() {
4040
const { output: aliceIdentity } = await aliceClient
4141
.createIdentity(unpublishedAliceDocument)
4242
.finish()
43-
.execute(aliceClient);
43+
.buildAndExecute(aliceClient);
4444
const aliceDocument = aliceIdentity.didDocument();
4545

4646
// Create a credential subject indicating the degree earned by Alice, linked to their DID.

bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export async function createVP() {
4646
const { output: issuerIdentity } = await issuerClient
4747
.createIdentity(unpublishedIssuerDocument)
4848
.finish()
49-
.execute(issuerClient);
49+
.buildAndExecute(issuerClient);
5050
const issuerDocument = issuerIdentity.didDocument();
5151

5252
// create holder account, create identity, and publish DID document for it
@@ -56,7 +56,7 @@ export async function createVP() {
5656
const { output: aliceIdentity } = await aliceClient
5757
.createIdentity(unpublishedAliceDocument)
5858
.finish()
59-
.execute(aliceClient);
59+
.buildAndExecute(aliceClient);
6060
const aliceDocument = aliceIdentity.didDocument();
6161

6262
// ===========================================================================

bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function revokeVC() {
5252
const { output: issuerIdentity } = await issuerClient
5353
.createIdentity(unpublishedIssuerDocument)
5454
.finish()
55-
.execute(issuerClient);
55+
.buildAndExecute(issuerClient);
5656
let issuerDocument = issuerIdentity.didDocument();
5757

5858
// create holder account, create identity, and publish DID document for it.
@@ -62,7 +62,7 @@ export async function revokeVC() {
6262
const { output: aliceIdentity } = await aliceClient
6363
.createIdentity(unpublishedAliceDocument)
6464
.finish()
65-
.execute(aliceClient);
65+
.buildAndExecute(aliceClient);
6666
const aliceDocument = aliceIdentity.didDocument();
6767

6868
// Create a new empty revocation bitmap. No credential is revoked yet.
@@ -73,11 +73,13 @@ export async function revokeVC() {
7373
const service: Service = revocationBitmap.toService(serviceId);
7474
issuerDocument.insertService(service);
7575

76+
const issuerIdentityToken = await issuerIdentity.getControllerToken(issuerClient);
77+
7678
// Publish the updated document.
7779
await issuerIdentity
78-
.updateDidDocument(issuerDocument)
80+
.updateDidDocument(issuerDocument, issuerIdentityToken!)
7981
.withGasBudget(TEST_GAS_BUDGET)
80-
.execute(issuerClient);
82+
.buildAndExecute(issuerClient);
8183

8284
// Create a credential subject indicating the degree earned by Alice, linked to their DID.
8385
const subject = {
@@ -131,9 +133,9 @@ export async function revokeVC() {
131133

132134
// Publish the changes.
133135
await issuerIdentity
134-
.updateDidDocument(issuerDocument)
136+
.updateDidDocument(issuerDocument, issuerIdentityToken!)
135137
.withGasBudget(TEST_GAS_BUDGET)
136-
.execute(issuerClient);
138+
.buildAndExecute(issuerClient);
137139

138140
// Credential verification now fails.
139141
try {
@@ -161,9 +163,9 @@ export async function revokeVC() {
161163

162164
// Publish the changes.
163165
await issuerIdentity
164-
.updateDidDocument(issuerDocument)
166+
.updateDidDocument(issuerDocument, issuerIdentityToken!)
165167
.withGasBudget(TEST_GAS_BUDGET)
166-
.execute(issuerClient);
168+
.buildAndExecute(issuerClient);
167169

168170
issuerDocument = issuerIdentity.didDocument();
169171

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2020-2025 IOTA Stiftung
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { IdentityClient, IotaDocument } from "@iota/identity-wasm/node";
5+
import { IotaClient } from "@iota/iota-sdk/client";
6+
import { TransactionDataBuilder } from "@iota/iota-sdk/transactions";
7+
import { getFundedClient, getMemstorage, NETWORK_URL } from "../util";
8+
9+
/**
10+
* This example demonstrates:
11+
* 1. A user - Alice - can build a transaction that is sponsored by another user - Bob;
12+
* 2. Deconstruct the transaction into its parts, to execute it manually through the SDK's IotaClient;
13+
* 3. Apply the transaction's off-chain effects, from its on-chain ones.
14+
*/
15+
export async function advancedTransaction(): Promise<void> {
16+
const aliceStorage = getMemstorage();
17+
const aliceClient = await getFundedClient(aliceStorage);
18+
19+
const bobStorage = getMemstorage();
20+
const bobClient = await getFundedClient(bobStorage);
21+
22+
const [txDataBcs, signatures, tx] = await aliceClient
23+
.createIdentity(new IotaDocument(aliceClient.network()))
24+
.finish()
25+
.withSender(aliceClient.senderAddress())
26+
.withSponsor(aliceClient.readOnly(), (tx_data: TransactionDataBuilder) => bobSponsorFn(tx_data, bobClient))
27+
.then(txBuilder => txBuilder.build(aliceClient));
28+
29+
// create new client to connect to IOTA network
30+
const iotaClient = new IotaClient({ url: NETWORK_URL });
31+
const tx_response = await iotaClient.executeTransactionBlock({
32+
transactionBlock: txDataBcs,
33+
signature: signatures,
34+
options: { showEffects: true },
35+
});
36+
await iotaClient.waitForTransaction({ digest: tx_response.digest });
37+
38+
const identity = await tx.apply(tx_response.effects!, aliceClient.readOnly());
39+
40+
console.log(`Alice successfully created Identity ${identity.id()}! Thanks for the gas Bob!`);
41+
}
42+
43+
async function bobSponsorFn(tx_data: TransactionDataBuilder, client: IdentityClient): Promise<string> {
44+
const coin = await client.iotaClient().getCoins({ owner: client.senderAddress(), coinType: "0x2::iota::IOTA" })
45+
.then(res => res.data[0]);
46+
tx_data.gasData.owner = client.senderAddress();
47+
tx_data.gasData.price = 1000;
48+
tx_data.gasData.budget = 50000000;
49+
tx_data.gasData.payment = [{ version: coin.version, objectId: coin.coinObjectId, digest: coin.digest }];
50+
51+
return await client.signer().sign(tx_data.build());
52+
}

bindings/wasm/identity_wasm/examples/src/1_advanced/12_iota_keytool_integration.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export async function iotaKeytoolIntegration() {
5252

5353
// Let's publish our new DID Document.
5454
let publishedDidDocument = await identityClient
55-
.publishDidDocument(didDocument)
56-
.execute(identityClient)
55+
.publishDidDocument(didDocument, identityClient.senderAddress())
56+
.buildAndExecute(identityClient)
5757
.then(res => res.output);
5858

5959
console.log(`Here is our published DID document: ${JSON.stringify(publishedDidDocument, null, 2)}`);

bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export async function customResolution() {
6666
const { output: identity } = await identityClient
6767
.createIdentity(unpublished)
6868
.finish()
69-
.execute(identityClient);
69+
.buildAndExecute(identityClient);
7070
const did = identity.didDocument().id();
7171

7272
// Construct a Resolver capable of resolving the did:key and iota methods.

bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export async function domainLinkage() {
3434
const { output: identity } = await identityClient
3535
.createIdentity(unpublished)
3636
.finish()
37-
.execute(identityClient);
37+
.buildAndExecute(identityClient);
3838
const document = identity.didDocument();
3939
const did = document.id();
4040

@@ -53,7 +53,8 @@ export async function domainLinkage() {
5353
domains: [domainFoo, domainBar],
5454
});
5555
document.insertService(linkedDomainService.toService());
56-
await identity.updateDidDocument(document).execute(identityClient);
56+
const controllerToken = await identity.getControllerToken(identityClient);
57+
await identity.updateDidDocument(document, controllerToken!).buildAndExecute(identityClient);
5758

5859
let updatedDidDocument = identity.didDocument();
5960
console.log("Updated DID document:", JSON.stringify(updatedDidDocument, null, 2));

bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function sdJwt() {
3939
const { output: issuerIdentity } = await issuerClient
4040
.createIdentity(unpublishedIssuerDocument)
4141
.finish()
42-
.execute(issuerClient);
42+
.buildAndExecute(issuerClient);
4343
const issuerDocument = issuerIdentity.didDocument();
4444

4545
// Create an identity for the holder, and publish DID document for it, in this case also the subject.
@@ -49,7 +49,7 @@ export async function sdJwt() {
4949
const { output: aliceIdentity } = await aliceClient
5050
.createIdentity(unpublishedAliceDocument)
5151
.finish()
52-
.execute(aliceClient);
52+
.buildAndExecute(aliceClient);
5353
const aliceDocument = aliceIdentity.didDocument();
5454

5555
// ===========================================================================

bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export async function statusList2021() {
3434
const { output: issuerIdentity } = await issuerClient
3535
.createIdentity(unpublishedIssuerDocument)
3636
.finish()
37-
.execute(issuerClient);
37+
.buildAndExecute(issuerClient);
3838
const issuerDocument = issuerIdentity.didDocument();
3939

4040
// Create an identity for the holder, and publish DID document for it, in this case also the subject.
@@ -44,7 +44,7 @@ export async function statusList2021() {
4444
const { output: aliceIdentity } = await aliceClient
4545
.createIdentity(unpublishedAliceDocument)
4646
.finish()
47-
.execute(aliceClient);
47+
.buildAndExecute(aliceClient);
4848
const aliceDocument = aliceIdentity.didDocument();
4949

5050
// Create a new empty status list. No credentials have been revoked yet.

bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export async function zkp() {
4141
const { output: issuerIdentity } = await issuerClient
4242
.createIdentity(unpublishedIssuerDocument)
4343
.finish()
44-
.execute(issuerClient);
44+
.buildAndExecute(issuerClient);
4545
const issuerDocument = issuerIdentity.didDocument();
4646

4747
// ===========================================================================

bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export async function zkp_revocation() {
4646
const { output: issuerIdentity } = await issuerClient
4747
.createIdentity(unpublishedIssuerDocument)
4848
.finish()
49-
.execute(issuerClient);
49+
.buildAndExecute(issuerClient);
5050
let issuerDocument = issuerIdentity.didDocument();
5151

5252
// Create an identity for the holder, and publish DID document for it, in this case also the subject.
@@ -56,7 +56,7 @@ export async function zkp_revocation() {
5656
const { output: holderIdentity } = await holderClient
5757
.createIdentity(unpublishedholderDocument)
5858
.finish()
59-
.execute(holderClient);
59+
.buildAndExecute(holderClient);
6060
const holderDocument = holderIdentity.didDocument();
6161

6262
// =========================================================================================
@@ -192,10 +192,11 @@ export async function zkp_revocation() {
192192

193193
console.log("Issuer decides to revoke the Credential");
194194

195+
const issuerIdentityToken = await issuerIdentity.getControllerToken(issuerClient);
195196
// Update the RevocationBitmap service in the issuer's DID Document.
196197
// This revokes the credential's unique index.
197198
issuerDocument.revokeCredentials("my-revocation-service", 5);
198-
await issuerIdentity.updateDidDocument(issuerDocument).execute(issuerClient);
199+
await issuerIdentity.updateDidDocument(issuerDocument, issuerIdentityToken!).buildAndExecute(issuerClient);
199200
issuerDocument = issuerIdentity.didDocument();
200201

201202
// Holder checks if his credential has been revoked by the Issuer

0 commit comments

Comments
 (0)