diff --git a/identity_iota_core/packages/iota_identity/sources/asset.move b/identity_iota_core/packages/iota_identity/sources/asset.move index 5747bf21a..c021d4834 100644 --- a/identity_iota_core/packages/iota_identity/sources/asset.move +++ b/identity_iota_core/packages/iota_identity/sources/asset.move @@ -11,6 +11,26 @@ module iota_identity::asset { const EInvalidSender: u64 = 4; const EInvalidAsset: u64 = 5; + // ===== Events ===== + + /// Event emitted when the owner of an `AuthenticatedAsset` + /// proposes its transfer to a new address. + public struct AssetTransferCreated has copy, drop { + asset: ID, + proposal: ID, + sender: address, + recipient: address, + } + + /// Event emitted when an active transfer is concluded, + /// either canceled or completed. + public struct AssetTransferConcluded has copy, drop { + asset: ID, + proposal: ID, + sender: address, + recipient: address, + concluded: bool, + } /// Structures that couples some data `T` with well known /// ownership and origin, along configurable abilities e.g. @@ -105,10 +125,17 @@ module iota_identity::asset { ctx: &mut TxContext, ) { assert!(asset.transferable, ENonTransferable); - let sender_cap = SenderCap { id: object::new(ctx) }; - let recipient_cap = RecipientCap { id: object::new(ctx) }; - let proposal = TransferProposal { + let proposal_id = object::new(ctx); + let sender_cap = SenderCap { id: object::new(ctx), + transfer_id: proposal_id.to_inner(), + }; + let recipient_cap = RecipientCap { + id: object::new(ctx), + transfer_id: proposal_id.to_inner(), + }; + let proposal = TransferProposal { + id: proposal_id, asset_id: object::id(&asset), sender_cap_id: object::id(&sender_cap), sender_address: asset.owner, @@ -117,6 +144,13 @@ module iota_identity::asset { done: false, }; + iota::event::emit(AssetTransferCreated { + proposal: object::id(&proposal), + asset: object::id(&asset), + sender: asset.owner, + recipient, + }); + transfer::transfer(sender_cap, asset.owner); transfer::transfer(recipient_cap, recipient); transfer::transfer(asset, proposal.id.to_address()); @@ -138,10 +172,12 @@ module iota_identity::asset { public struct SenderCap has key { id: UID, + transfer_id: ID, } public struct RecipientCap has key { id: UID, + transfer_id: ID, } /// Accept the transfer of the asset. @@ -159,6 +195,14 @@ module iota_identity::asset { cap.delete(); self.done = true; + + iota::event::emit(AssetTransferConcluded { + proposal: self.id.to_inner(), + asset: self.asset_id, + sender: self.sender_address, + recipient: self.recipient_address, + concluded: true, + }) } /// The sender of the asset consumes the `TransferProposal` to either @@ -173,6 +217,14 @@ module iota_identity::asset { let asset = transfer::receive(&mut proposal.id, asset); assert!(proposal.asset_id == object::id(&asset), EInvalidAsset); transfer::transfer(asset, proposal.sender_address); + + iota::event::emit(AssetTransferConcluded { + proposal: proposal.id.to_inner(), + asset: proposal.asset_id, + sender: proposal.sender_address, + recipient: proposal.recipient_address, + concluded: false, + }) }; delete_transfer(proposal); @@ -182,6 +234,7 @@ module iota_identity::asset { public(package) fun delete_sender_cap(cap: SenderCap) { let SenderCap { id, + .. } = cap; object::delete(id); } @@ -189,6 +242,7 @@ module iota_identity::asset { public fun delete_recipient_cap(cap: RecipientCap) { let RecipientCap { id, + .. } = cap; object::delete(id); } diff --git a/identity_iota_core/packages/iota_identity/sources/identity.move b/identity_iota_core/packages/iota_identity/sources/identity.move index 0e68188df..e222e0473 100644 --- a/identity_iota_core/packages/iota_identity/sources/identity.move +++ b/identity_iota_core/packages/iota_identity/sources/identity.move @@ -24,6 +24,25 @@ module iota_identity::identity { /// The controller list must contain at least 1 element. const EInvalidControllersList: u64 = 3; + // ===== Events ====== + /// Event emitted when an `identity`'s `Proposal` with `ID` `proposal` is created or executed by `controller`. + public struct ProposalEvent has copy, drop { + identity: ID, + controller: ID, + proposal: ID, + // Set to `true` if `proposal` has been executed. + executed: bool, + } + + /// Event emitted when a `Proposal` has reached the AC threshold and + /// can now be executed. + public struct ProposalApproved has copy, drop { + /// ID of the `Identity` owning the proposal. + identity: ID, + /// ID of the created `Proposal`. + proposal: ID, + } + /// On-chain Identity. public struct Identity has key, store { id: UID, @@ -127,7 +146,14 @@ module iota_identity::identity { cap: &ControllerCap, proposal_id: ID, ) { - self.did_doc.approve_proposal, T>(cap, proposal_id); + self.did_doc.approve_proposal<_, T>(cap, proposal_id); + // If proposal is ready to be executed send an event. + if (self.did_doc.is_proposal_approved<_, T>(proposal_id)) { + iota::event::emit(ProposalApproved { + identity: self.id().to_inner(), + proposal: proposal_id, + }) + } } /// Proposes the deativates the DID Document contained in this `Identity`. @@ -149,10 +175,12 @@ module iota_identity::identity { let is_approved = self .did_doc .is_proposal_approved<_, did_deactivation_proposal::DidDeactivation>(proposal_id); + if (is_approved) { self.execute_deactivation(cap, proposal_id, clock, ctx); option::none() } else { + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, false); option::some(proposal_id) } } @@ -172,6 +200,8 @@ module iota_identity::identity { ).unwrap(); self.did_doc.set_controlled_value(vector[]); self.updated = clock.timestamp_ms(); + + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, true); } /// Proposes an update to the DID Document contained in this `Identity`. @@ -201,6 +231,7 @@ module iota_identity::identity { self.execute_update(cap, proposal_id, clock, ctx); option::none() } else { + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, false); option::some(proposal_id) } } @@ -221,6 +252,7 @@ module iota_identity::identity { ); self.updated = clock.timestamp_ms(); + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, true); } /// Proposes to update this `Identity`'s AC. @@ -254,6 +286,7 @@ module iota_identity::identity { self.execute_config_change(cap, proposal_id, ctx); option::none() } else { + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, false); option::some(proposal_id) } } @@ -270,7 +303,8 @@ module iota_identity::identity { cap, proposal_id, ctx, - ) + ); + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, true); } /// Proposes the transfer of a set of objects owned by this `Identity`. @@ -282,7 +316,7 @@ module iota_identity::identity { recipients: vector
, ctx: &mut TxContext, ) { - transfer_proposal::propose_send( + let proposal_id = transfer_proposal::propose_send( &mut self.did_doc, cap, expiration, @@ -290,6 +324,7 @@ module iota_identity::identity { recipients, ctx ); + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, false); } /// Sends one object among the one specified in a `Send` proposal. @@ -310,15 +345,16 @@ module iota_identity::identity { objects: vector, ctx: &mut TxContext, ) { - let identity_address = self.id().to_address(); - borrow_proposal::propose_borrow( - &mut self.did_doc, - cap, - expiration, - objects, - identity_address, - ctx, - ); + let identity_address = self.id().to_address(); + let proposal_id = borrow_proposal::propose_borrow( + &mut self.did_doc, + cap, + expiration, + objects, + identity_address, + ctx, + ); + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, false); } /// Takes one of the borrowed assets. @@ -353,6 +389,7 @@ module iota_identity::identity { proposal_id: ID, ctx: &mut TxContext, ): Action { + emit_proposal_event(self.id().to_inner(), cap.id().to_inner(), proposal_id, true); self.did_doc.execute_proposal(cap, proposal_id, ctx) } @@ -372,6 +409,20 @@ module iota_identity::identity { public(package) fun to_address(self: &Identity): address { self.id().to_inner().id_to_address() } + + public(package) fun emit_proposal_event( + identity: ID, + controller: ID, + proposal: ID, + executed: bool, + ) { + iota::event::emit(ProposalEvent { + identity, + controller, + proposal, + executed, + }) + } } diff --git a/identity_iota_core/packages/iota_identity/sources/proposals/borrow.move b/identity_iota_core/packages/iota_identity/sources/proposals/borrow.move index 4195e3461..1f014044b 100644 --- a/identity_iota_core/packages/iota_identity/sources/proposals/borrow.move +++ b/identity_iota_core/packages/iota_identity/sources/proposals/borrow.move @@ -24,10 +24,10 @@ module iota_identity::borrow_proposal { objects: vector, owner: address, ctx: &mut TxContext, - ) { + ): ID { let action = Borrow { objects, objects_to_return: vector::empty(), owner }; - multi.create_proposal(cap, action,expiration, ctx); + multi.create_proposal(cap, action,expiration, ctx) } /// Borrows an asset from this action. This function will fail if: diff --git a/identity_iota_core/packages/iota_identity/sources/proposals/transfer.move b/identity_iota_core/packages/iota_identity/sources/proposals/transfer.move index d16f8c36c..cf43e3472 100644 --- a/identity_iota_core/packages/iota_identity/sources/proposals/transfer.move +++ b/identity_iota_core/packages/iota_identity/sources/proposals/transfer.move @@ -21,11 +21,11 @@ module iota_identity::transfer_proposal { objects: vector, recipients: vector
, ctx: &mut TxContext, - ) { + ): ID { assert!(objects.length() == recipients.length(), EDifferentLength); let action = Send { objects, recipients }; - multi.create_proposal(cap, action,expiration, ctx); + multi.create_proposal(cap, action,expiration, ctx) } public fun send(