From 160ecc85f5175867736b6598a9f404d9254a5a85 Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:24:43 +0100 Subject: [PATCH] Make Move handle DID Document's time metadata (#62) * update move package to use internal clock for time metadata * update rust code to use timestamp provided by move object for Identity * fix number encoding * fix proposal tests * Update identity_iota_core/src/rebased/sui/move_calls/utils.rs Co-authored-by: Yasir * update & deactivation modify updated timestamp --------- Co-authored-by: Yasir --- .../packages/identity_iota/Move.lock | 6 +- .../identity_iota/sources/identity.move | 100 +++++++++++++++--- .../identity_iota/sources/migration.move | 41 +++++-- .../src/rebased/migration/identity.rs | 26 ++++- .../rebased/sui/move_calls/identity/create.rs | 11 +- .../sui/move_calls/identity/deactivate.rs | 8 +- .../rebased/sui/move_calls/identity/update.rs | 8 +- .../src/rebased/sui/move_calls/utils.rs | 13 +++ identity_iota_core/tests/e2e/client.rs | 6 +- 9 files changed, 178 insertions(+), 41 deletions(-) diff --git a/identity_iota_core/packages/identity_iota/Move.lock b/identity_iota_core/packages/identity_iota/Move.lock index 25a0a7be7..59f65e9ea 100644 --- a/identity_iota_core/packages/identity_iota/Move.lock +++ b/identity_iota_core/packages/identity_iota/Move.lock @@ -46,7 +46,7 @@ latest-published-id = "0xbf2ba9e9383be1dc349b571cbc44006c6fa760f3b900e0be992232d published-version = "1" [env.localnet] -chain-id = "872e97bc" -original-published-id = "0xbfa6eba1dba58206b3b57a2cee55385b4909060912eb4d64d2f6b97a6853ae58" -latest-published-id = "0xbfa6eba1dba58206b3b57a2cee55385b4909060912eb4d64d2f6b97a6853ae58" +chain-id = "c6810496" +original-published-id = "0x12ab82f924a30a1f778215a06c0d7de737a6483203222f510a186fc1eb0ca728" +latest-published-id = "0x12ab82f924a30a1f778215a06c0d7de737a6483203222f510a186fc1eb0ca728" published-version = "1" diff --git a/identity_iota_core/packages/identity_iota/sources/identity.move b/identity_iota_core/packages/identity_iota/sources/identity.move index 40ec8c764..c290dee15 100644 --- a/identity_iota_core/packages/identity_iota/sources/identity.move +++ b/identity_iota_core/packages/identity_iota/sources/identity.move @@ -1,5 +1,10 @@ module identity_iota::identity { - use iota::{vec_map::{Self, VecMap}, transfer::Receiving}; + use iota::{ + transfer::Receiving, + vec_map::{Self, VecMap}, + vec_set::VecSet, + clock::Clock, + }; use identity_iota::{ multicontroller::{Self, ControllerCap, Multicontroller, Action}, update_value_proposal, @@ -10,6 +15,7 @@ module identity_iota::identity { }; const ENotADidDocument: u64 = 0; + const EInvalidTimestamp: u64 = 1; /// The threshold specified upon document creation was not valid. /// Threshold must be greater than or equal to 1. const EInvalidThreshold: u64 = 2; @@ -19,23 +25,50 @@ module identity_iota::identity { /// On-chain Identity. public struct Identity has key, store { id: UID, - /// same as stardust `state_metadata`. + /// Same as stardust `state_metadata`. did_doc: Multicontroller>, + /// Timestamp of this Identity's creation. + created: u64, + /// Timestamp of this Identity's last update. + updated: u64, } /// Creates a new DID Document with a single controller. - public fun new(doc: vector, ctx: &mut TxContext): Identity { - new_with_controller(doc, ctx.sender(), ctx) + public fun new( + doc: vector, + clock: &Clock, + ctx: &mut TxContext + ): Identity { + new_with_controller(doc, ctx.sender(), clock, ctx) + } + + /// Creates an identity specifying its `created` timestamp. + /// Should only be used for migration! + public(package) fun new_with_creation_timestamp( + doc: vector, + creation_timestamp: u64, + clock: &Clock, + ctx: &mut TxContext + ): Identity { + let mut identity = new_with_controller(doc, ctx.sender(), clock, ctx); + assert!(identity.updated >= creation_timestamp, EInvalidTimestamp); + identity.created = creation_timestamp; + + identity } public fun new_with_controller( doc: vector, controller: address, + clock: &Clock, ctx: &mut TxContext, ): Identity { + let now = clock.timestamp_ms(); Identity { id: object::new(ctx), - did_doc: multicontroller::new_with_controller(doc, controller, ctx) + did_doc: multicontroller::new_with_controller(doc, controller, ctx), + created: now, + updated: now, } } @@ -46,15 +79,19 @@ module identity_iota::identity { doc: vector, controllers: VecMap, threshold: u64, + clock: &Clock, ctx: &mut TxContext, ): Identity { assert!(is_did_output(&doc), ENotADidDocument); assert!(threshold >= 1, EInvalidThreshold); assert!(controllers.size() > 0, EInvalidControllersList); + let now = clock.timestamp_ms(); Identity { id: object::new(ctx), did_doc: multicontroller::new_with_controllers(doc, controllers, threshold, ctx), + created: now, + updated: now, } } @@ -62,6 +99,14 @@ module identity_iota::identity { &self.id } + public fun created(self: &Identity): u64 { + self.created + } + + public fun updated(self: &Identity): u64 { + self.updated + } + public fun threshold(self: &Identity): u64 { self.did_doc.threshold() } @@ -78,6 +123,7 @@ module identity_iota::identity { self: &mut Identity, cap: &ControllerCap, expiration: Option, + clock: &Clock, ctx: &mut TxContext, ): Option { let proposal_id = self.did_doc.create_proposal( @@ -90,7 +136,7 @@ module identity_iota::identity { .did_doc .is_proposal_approved<_, did_deactivation_proposal::DidDeactivation>(proposal_id); if (is_approved) { - self.execute_deactivation(cap, proposal_id, ctx); + self.execute_deactivation(cap, proposal_id, clock, ctx); option::none() } else { option::some(proposal_id) @@ -101,6 +147,7 @@ module identity_iota::identity { self: &mut Identity, cap: &ControllerCap, proposal_id: ID, + clock: &Clock, ctx: &mut TxContext, ) { let _ = self.did_doc.execute_proposal, DidDeactivation>( @@ -109,6 +156,7 @@ module identity_iota::identity { ctx, ).unwrap(); self.did_doc.set_controlled_value(vector[]); + self.updated = clock.timestamp_ms(); } public fun propose_update( @@ -116,6 +164,7 @@ module identity_iota::identity { cap: &ControllerCap, updated_doc: vector, expiration: Option, + clock: &Clock, ctx: &mut TxContext, ): Option { assert!(is_did_output(&updated_doc), ENotADidDocument); @@ -131,7 +180,7 @@ module identity_iota::identity { .did_doc .is_proposal_approved<_, update_value_proposal::UpdateValue>>(proposal_id); if (is_approved) { - self.execute_update(cap, proposal_id, ctx); + self.execute_update(cap, proposal_id, clock, ctx); option::none() } else { option::some(proposal_id) @@ -142,6 +191,7 @@ module identity_iota::identity { self: &mut Identity, cap: &ControllerCap, proposal_id: ID, + clock: &Clock, ctx: &mut TxContext, ) { update_value_proposal::execute_update( @@ -150,6 +200,8 @@ module identity_iota::identity { proposal_id, ctx, ); + + self.updated = clock.timestamp_ms(); } public fun propose_config_change( @@ -299,16 +351,19 @@ module identity_iota::identity_tests { use identity_iota::config_proposal::Modify; use identity_iota::multicontroller::{ControllerCap, EExpiredProposal, EThresholdNotReached}; use iota::vec_map; + use iota::clock; #[test] fun adding_a_controller_works() { let controller1 = @0x1; let controller2 = @0x2; let mut scenario = test_scenario::begin(controller1); + let clock = clock::create_for_testing(scenario.ctx()); + // Create a DID document with no funds and 1 controller with a weight of 1 and a threshold of 1. // Share the document and send the controller capability to `controller1`. - let identity = new(b"DID", scenario.ctx()); + let identity = new(b"DID", &clock, scenario.ctx()); transfer::public_share_object(identity); scenario.next_tx(controller1); @@ -331,6 +386,7 @@ module identity_iota::identity_tests { test_scenario::return_shared(identity); let _ = scenario.end(); + clock::destroy_for_testing(clock); } #[test] @@ -339,6 +395,7 @@ module identity_iota::identity_tests { let controller2 = @0x2; let controller3 = @0x3; let mut scenario = test_scenario::begin(controller1); + let clock = clock::create_for_testing(scenario.ctx()); let mut controllers = vec_map::empty(); controllers.insert(controller1, 1); @@ -350,6 +407,7 @@ module identity_iota::identity_tests { b"DID", controllers, 2, + &clock, scenario.ctx(), ); transfer::public_share_object(identity); @@ -390,6 +448,7 @@ module identity_iota::identity_tests { test_scenario::return_shared(identity); let _ = scenario.end(); + clock::destroy_for_testing(clock); } #[test, expected_failure(abort_code = EThresholdNotReached)] @@ -402,6 +461,7 @@ module identity_iota::identity_tests { let controller_d = @0x4; let mut scenario = test_scenario::begin(controller_a); + let clock = clock::create_for_testing(scenario.ctx()); let mut controllers = vec_map::empty(); controllers.insert(controller_a, 10); @@ -415,6 +475,7 @@ module identity_iota::identity_tests { b"DID", controllers, 10, + &clock, scenario.ctx(), ); transfer::public_share_object(identity); @@ -445,6 +506,7 @@ module identity_iota::identity_tests { b"DID", controllers, 10, + &clock, scenario.ctx(), ); transfer::public_share_object(identity); @@ -467,6 +529,7 @@ module identity_iota::identity_tests { test_scenario::return_shared(identity); }; let _ = scenario.end(); + clock::destroy_for_testing(clock); } #[test] @@ -479,6 +542,7 @@ module identity_iota::identity_tests { let controller_d = @0x4; let mut scenario = test_scenario::begin(controller_b); + let clock = clock::create_for_testing(scenario.ctx()); let mut controllers = vec_map::empty(); controllers.insert(controller_a, 10); @@ -491,6 +555,7 @@ module identity_iota::identity_tests { b"DID", controllers, 10, + &clock, scenario.ctx(), ); transfer::public_share_object(identity); @@ -521,15 +586,16 @@ module identity_iota::identity_tests { test_scenario::return_to_address(controller_d, controller_d_cap); let _ = scenario.end(); - + clock::destroy_for_testing(clock); } #[test] fun check_identity_can_own_another_identity() { let controller_a = @0x1; let mut scenario = test_scenario::begin(controller_a); + let clock = clock::create_for_testing(scenario.ctx()); - let first_identity = new(b"DID", scenario.ctx()); + let first_identity = new(b"DID", &clock, scenario.ctx()); transfer::public_share_object(first_identity); scenario.next_tx(controller_a); @@ -543,6 +609,7 @@ module identity_iota::identity_tests { b"DID", controllers, 10, + &clock, scenario.ctx(), ); @@ -568,14 +635,16 @@ module identity_iota::identity_tests { test_scenario::return_shared(first_identity); let _ = scenario.end(); + clock::destroy_for_testing(clock); } #[test, expected_failure(abort_code = ENotADidDocument)] fun test_update_proposal_cannot_propose_non_did_doc() { let controller = @0x1; let mut scenario = test_scenario::begin(controller); + let clock = clock::create_for_testing(scenario.ctx()); - let identity = new(b"DID", scenario.ctx()); + let identity = new(b"DID", &clock, scenario.ctx()); transfer::public_share_object(identity); scenario.next_tx(controller); @@ -584,12 +653,13 @@ module identity_iota::identity_tests { let mut identity = scenario.take_shared(); let cap = scenario.take_from_address(controller); - let _proposal_id = identity.propose_update(&cap, b"NOT DID", option::none(), scenario.ctx()); + let _proposal_id = identity.propose_update(&cap, b"NOT DID", option::none(), &clock, scenario.ctx()); test_scenario::return_to_address(controller, cap); test_scenario::return_shared(identity); scenario.end(); + clock::destroy_for_testing(clock); } #[test, expected_failure(abort_code = EExpiredProposal)] @@ -599,12 +669,13 @@ module identity_iota::identity_tests { let new_controller = @0x3; let mut scenario = test_scenario::begin(controller_a); let expiration_epoch = scenario.ctx().epoch(); - + let clock = clock::create_for_testing(scenario.ctx()); + let mut controllers = vec_map::empty(); controllers.insert(controller_a, 1); controllers.insert(controller_b, 1); - let identity = new_with_controllers(b"DID", controllers, 2, scenario.ctx()); + let identity = new_with_controllers(b"DID", controllers, 2, &clock, scenario.ctx()); transfer::public_share_object(identity); scenario.next_tx(controller_a); @@ -626,5 +697,6 @@ module identity_iota::identity_tests { test_scenario::return_shared(identity); scenario.end(); + clock::destroy_for_testing(clock); } } diff --git a/identity_iota_core/packages/identity_iota/sources/migration.move b/identity_iota_core/packages/identity_iota/sources/migration.move index 79d427df2..69bcb3dd6 100644 --- a/identity_iota_core/packages/identity_iota/sources/migration.move +++ b/identity_iota_core/packages/identity_iota/sources/migration.move @@ -1,13 +1,15 @@ module identity_iota::migration { use identity_iota::{migration_registry::MigrationRegistry, identity}; use stardust::{alias::Alias, alias_output::{AliasOutput, extract_assets}}; - use iota::coin; + use iota::{coin, clock::Clock}; const ENotADidOutput: u64 = 1; public fun migrate_alias( alias: Alias, migration_registry: &mut MigrationRegistry, + creation_timestamp: u64, + clock: &Clock, ctx: &mut TxContext, ): address { let ( @@ -26,7 +28,12 @@ module identity_iota::migration { // Destroy alias. object::delete(alias_id); - let identity = identity::new(state_metadata.extract(), ctx); + let identity = identity::new_with_creation_timestamp( + state_metadata.extract(), + creation_timestamp, + clock, + ctx + ); let identity_addr = identity.id().to_address(); // Add a migration record. @@ -36,12 +43,24 @@ module identity_iota::migration { identity_addr } - /// Creates a new `Document` from an Iota 1.0 legacy `AliasOutput`. - public fun migrate_alias_output(alias_output: AliasOutput, migration_registry: &mut MigrationRegistry, ctx: &mut TxContext) { + /// Creates a new `Identity` from an Iota 1.0 legacy `AliasOutput` containing a DID Document. + public fun migrate_alias_output( + alias_output: AliasOutput, + migration_registry: &mut MigrationRegistry, + creation_timestamp: u64, + clock: &Clock, + ctx: &mut TxContext + ) { // Extract required data from output. let (iota, native_tokens, alias_data) = extract_assets(alias_output); - let doc_addr = migrate_alias(alias_data, migration_registry, ctx); + let doc_addr = migrate_alias( + alias_data, + migration_registry, + creation_timestamp, + clock, + ctx + ); let coin = coin::from_balance(iota, ctx); transfer::public_transfer(coin, doc_addr); @@ -52,7 +71,7 @@ module identity_iota::migration { #[test_only] module identity_iota::migration_tests { - use iota::test_scenario; + use iota::{test_scenario, clock}; use stardust::alias_output::{create_empty_for_testing, AliasOutput, attach_alias}; use identity_iota::identity::{Identity}; use identity_iota::migration::migrate_alias_output; @@ -64,6 +83,7 @@ module identity_iota::migration_tests { fun test_migration_of_legacy_did_output() { let controller_a = @0x1; let mut scenario = test_scenario::begin(controller_a); + let clock = clock::create_for_testing(scenario.ctx()); let alias_output = create_empty_for_testing(scenario.ctx()); transfer::public_transfer(alias_output, controller_a); @@ -92,7 +112,13 @@ module identity_iota::migration_tests { scenario.next_tx(controller_a); let mut registry = scenario.take_shared(); - migrate_alias_output(alias_output, &mut registry, scenario.ctx()); + migrate_alias_output( + alias_output, + &mut registry, + 0, + &clock, + scenario.ctx() + ); scenario.next_tx(controller_a); let identity = scenario.take_shared(); @@ -112,5 +138,6 @@ module identity_iota::migration_tests { test_scenario::return_shared(registry); test_scenario::return_shared(identity); let _ = scenario.end(); + clock::destroy_for_testing(clock); } } diff --git a/identity_iota_core/src/rebased/migration/identity.rs b/identity_iota_core/src/rebased/migration/identity.rs index 69766bc22..69fe79265 100644 --- a/identity_iota_core/src/rebased/migration/identity.rs +++ b/identity_iota_core/src/rebased/migration/identity.rs @@ -7,6 +7,8 @@ use std::ops::Deref; use std::str::FromStr; use async_trait::async_trait; +use identity_core::common::Timestamp; +use crate::rebased::sui::types::Number; use crate::IotaDID; use crate::IotaDocument; use crate::StateMetadataDocument; @@ -91,6 +93,7 @@ impl Identity { } } +/// An on-chain entity that wraps a DID Document. #[derive(Debug, Serialize)] pub struct OnChainIdentity { id: UID, @@ -358,11 +361,15 @@ pub async fn get_identity( struct TempOnChainIdentity { id: UID, did_doc: Multicontroller>, + created: Number, + updated: Number, } let TempOnChainIdentity { id, did_doc: multi_controller, + created, + updated } = serde_json::from_value::(value.fields.to_json_value()).map_err(|err| { Error::ObjectLookup(format!( "could not parse identity document with object id {object_id}; {err}" @@ -370,13 +377,22 @@ pub async fn get_identity( })?; let original_did = IotaDID::from_alias_id(id.object_id().to_string().as_str(), client.network()); let controlled_value = multi_controller.controlled_value(); + // Parse DID document timestamps + let created = { + let timestamp_ms: u64 = created.try_into().expect("Move string-encoded u64 are valid u64"); + // `Timestamp` requires a timestamp expessed in seconds. + Timestamp::from_unix(timestamp_ms as i64 / 1000).expect("On-chain clock produses valid timestamps") + }; + let updated = { + let timestamp_ms: u64 = updated.try_into().expect("Move string-encoded u64 are valid u64"); + // `Timestamp` requires a timestamp expessed in seconds. + Timestamp::from_unix(timestamp_ms as i64 / 1000).expect("On-chain clock produses valid timestamps") + }; // check if DID has been deactivated - let did_doc = if controlled_value.len() == 0 { + let mut did_doc = if controlled_value.len() == 0 { // DID has been deactivated by setting controlled value empty, therefore craft an empty document let mut empty_document = IotaDocument::new_with_id(original_did.clone()); - empty_document.metadata.created = None; - empty_document.metadata.updated = None; empty_document.metadata.deactivated = Some(true); empty_document @@ -387,6 +403,10 @@ pub async fn get_identity( .map_err(|e| Error::DidDocParsingFailed(e.to_string()))? }; + // Overwrite `created` and `updated` with trusted value coming from the on-chain `Identity` object. + did_doc.metadata.created = Some(created); + did_doc.metadata.updated = Some(updated); + Ok(Some(OnChainIdentity { id, multi_controller, diff --git a/identity_iota_core/src/rebased/sui/move_calls/identity/create.rs b/identity_iota_core/src/rebased/sui/move_calls/identity/create.rs index fec9a1c5c..5a83f7ae2 100644 --- a/identity_iota_core/src/rebased/sui/move_calls/identity/create.rs +++ b/identity_iota_core/src/rebased/sui/move_calls/identity/create.rs @@ -14,9 +14,11 @@ use crate::rebased::sui::move_calls::utils; use crate::rebased::utils::MoveType; use crate::rebased::Error; -pub fn new(did_doc: &[u8], package_id: ObjectID) -> Result { +/// Build a transaction that creates a new on-chain Identity containing `did_doc`. +pub(crate) fn new(did_doc: &[u8], package_id: ObjectID) -> Result { let mut ptb = ProgrammableTransactionBuilder::new(); let doc_arg = utils::ptb_pure(&mut ptb, "did_doc", did_doc)?; + let clock = utils::get_clock_ref(&mut ptb); // Create a new identity, sending its capability to the tx's sender. let identity_res = ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { @@ -24,7 +26,7 @@ pub fn new(did_doc: &[u8], package_id: ObjectID) -> Result Result( +pub(crate) fn new_with_controllers( did_doc: &[u8], controllers: C, threshold: u64, @@ -64,6 +66,7 @@ where }; let doc_arg = ptb.pure(did_doc).map_err(|e| Error::InvalidArgument(e.to_string()))?; let threshold_arg = ptb.pure(threshold).map_err(|e| Error::InvalidArgument(e.to_string()))?; + let clock = utils::get_clock_ref(&mut ptb); // Create a new identity, sending its capabilities to the specified controllers. let identity_res = ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { @@ -71,7 +74,7 @@ where module: ident_str!("identity").into(), function: ident_str!("new_with_controllers").into(), type_arguments: vec![], - arguments: vec![doc_arg, controllers, threshold_arg], + arguments: vec![doc_arg, controllers, threshold_arg, clock], }))); // Share the resulting identity. diff --git a/identity_iota_core/src/rebased/sui/move_calls/identity/deactivate.rs b/identity_iota_core/src/rebased/sui/move_calls/identity/deactivate.rs index 2f2bb90d5..0bd4a0c8e 100644 --- a/identity_iota_core/src/rebased/sui/move_calls/identity/deactivate.rs +++ b/identity_iota_core/src/rebased/sui/move_calls/identity/deactivate.rs @@ -8,7 +8,7 @@ use move_core_types::ident_str; use crate::rebased::sui::move_calls::utils; -pub fn propose_deactivation( +pub(crate) fn propose_deactivation( identity: OwnedObjectRef, capability: ObjectRef, expiration: Option, @@ -18,13 +18,14 @@ pub fn propose_deactivation( let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id)?; + let clock = utils::get_clock_ref(&mut ptb); let _proposal_id = ptb.programmable_move_call( package_id, ident_str!("identity").into(), ident_str!("propose_deactivation").into(), vec![], - vec![identity_arg, cap_arg, exp_arg], + vec![identity_arg, cap_arg, exp_arg, clock], ); Ok(ptb.finish()) @@ -40,13 +41,14 @@ pub fn execute_deactivation( let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; let proposal_id = ptb.pure(proposal_id)?; let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let clock = utils::get_clock_ref(&mut ptb); let _ = ptb.programmable_move_call( package_id, ident_str!("identity").into(), ident_str!("execute_deactivation").into(), vec![], - vec![identity_arg, cap_arg, proposal_id], + vec![identity_arg, cap_arg, proposal_id, clock], ); Ok(ptb.finish()) diff --git a/identity_iota_core/src/rebased/sui/move_calls/identity/update.rs b/identity_iota_core/src/rebased/sui/move_calls/identity/update.rs index 6b258e68d..8797de61a 100644 --- a/identity_iota_core/src/rebased/sui/move_calls/identity/update.rs +++ b/identity_iota_core/src/rebased/sui/move_calls/identity/update.rs @@ -8,7 +8,7 @@ use move_core_types::ident_str; use crate::rebased::sui::move_calls::utils; -pub fn propose_update( +pub(crate) fn propose_update( identity: OwnedObjectRef, capability: ObjectRef, did_doc: impl AsRef<[u8]>, @@ -20,13 +20,14 @@ pub fn propose_update( let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id)?; let doc_arg = ptb.pure(did_doc.as_ref())?; + let clock = utils::get_clock_ref(&mut ptb); let _proposal_id = ptb.programmable_move_call( package_id, ident_str!("identity").into(), ident_str!("propose_update").into(), vec![], - vec![identity_arg, cap_arg, doc_arg, exp_arg], + vec![identity_arg, cap_arg, doc_arg, exp_arg, clock], ); Ok(ptb.finish()) @@ -42,13 +43,14 @@ pub fn execute_update( let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; let proposal_id = ptb.pure(proposal_id)?; let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let clock = utils::get_clock_ref(&mut ptb); let _ = ptb.programmable_move_call( package_id, ident_str!("identity").into(), ident_str!("execute_update").into(), vec![], - vec![identity_arg, cap_arg, proposal_id], + vec![identity_arg, cap_arg, proposal_id, clock], ); Ok(ptb.finish()) diff --git a/identity_iota_core/src/rebased/sui/move_calls/utils.rs b/identity_iota_core/src/rebased/sui/move_calls/utils.rs index 4fb1c51cf..aa7b5165f 100644 --- a/identity_iota_core/src/rebased/sui/move_calls/utils.rs +++ b/identity_iota_core/src/rebased/sui/move_calls/utils.rs @@ -7,10 +7,23 @@ use iota_sdk::types::object::Owner; use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; use iota_sdk::types::transaction::Argument; use iota_sdk::types::transaction::ObjectArg; +use iota_sdk::types::IOTA_CLOCK_OBJECT_ID; +use iota_sdk::types::IOTA_CLOCK_OBJECT_SHARED_VERSION; use iota_sdk::types::MOVE_STDLIB_PACKAGE_ID; use move_core_types::ident_str; use serde::Serialize; +/// Adds a reference to the on-chain clock to `ptb`'s arguments. +pub fn get_clock_ref(ptb: &mut Ptb) -> Argument { + ptb + .obj(ObjectArg::SharedObject { + id: IOTA_CLOCK_OBJECT_ID, + initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION, + mutable: false, + }) + .expect("network has a singleton clock instantiated") +} + pub fn owned_ref_to_shared_object_arg( owned_ref: OwnedObjectRef, ptb: &mut Ptb, diff --git a/identity_iota_core/tests/e2e/client.rs b/identity_iota_core/tests/e2e/client.rs index 5c5d9c4b6..6260e0511 100644 --- a/identity_iota_core/tests/e2e/client.rs +++ b/identity_iota_core/tests/e2e/client.rs @@ -11,13 +11,11 @@ async fn can_create_an_identity() -> anyhow::Result<()> { let test_client = get_test_client().await?; let identity_client = test_client.new_user_client().await?; - let result = identity_client + let _identity = identity_client .create_identity(TEST_DOC) .finish() .execute(&identity_client) - .await; - - assert!(result.is_ok()); + .await?; Ok(()) }