diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index 88aba243f29..76ddfaba3eb 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -1660,7 +1660,8 @@ message GetGroupActionsResponse { // Burn event message BurnEvent { uint64 amount = 1; // Amount to burn - optional string public_note = 2; // Public note + bytes burn_from_id = 2; // The identifier to burn from + optional string public_note = 3; // Public note } // Freeze event diff --git a/packages/rs-dpp/src/group/group_action/mod.rs b/packages/rs-dpp/src/group/group_action/mod.rs index efbba5665b9..41a4a67060c 100644 --- a/packages/rs-dpp/src/group/group_action/mod.rs +++ b/packages/rs-dpp/src/group/group_action/mod.rs @@ -18,6 +18,8 @@ pub enum GroupAction { pub trait GroupActionAccessors { fn contract_id(&self) -> Identifier; + + fn proposer_id(&self) -> Identifier; fn token_contract_position(&self) -> TokenContractPosition; fn event(&self) -> &GroupActionEvent; } @@ -28,6 +30,12 @@ impl GroupActionAccessors for GroupAction { } } + fn proposer_id(&self) -> Identifier { + match self { + GroupAction::V0(inner) => inner.proposer_id(), + } + } + fn token_contract_position(&self) -> TokenContractPosition { match self { GroupAction::V0(inner) => inner.token_contract_position(), diff --git a/packages/rs-dpp/src/group/group_action/v0/mod.rs b/packages/rs-dpp/src/group/group_action/v0/mod.rs index ad624d361f7..a2c5d89175a 100644 --- a/packages/rs-dpp/src/group/group_action/v0/mod.rs +++ b/packages/rs-dpp/src/group/group_action/v0/mod.rs @@ -12,6 +12,7 @@ use platform_value::Identifier; #[platform_serialize(unversioned)] //versioned directly, no need to use platform_version pub struct GroupActionV0 { pub contract_id: Identifier, + pub proposer_id: Identifier, pub token_contract_position: TokenContractPosition, pub event: GroupActionEvent, } @@ -21,6 +22,10 @@ impl GroupActionAccessors for GroupActionV0 { self.contract_id } + fn proposer_id(&self) -> Identifier { + self.proposer_id + } + fn token_contract_position(&self) -> TokenContractPosition { self.token_contract_position } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs index 0c4b9357909..1c8c8617dc5 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_transition.rs @@ -399,7 +399,9 @@ impl TokenTransitionV0Methods for TokenTransition { ) -> Result { Ok(match self { TokenTransition::Burn(burn) => { - TokenEvent::Burn(burn.burn_amount(), burn.public_note().cloned()) + // The owner id might be incorrect when doing group actions + // However it will be fixed in verify_state_transition was executed area. + TokenEvent::Burn(burn.burn_amount(), owner_id, burn.public_note().cloned()) } TokenTransition::Mint(mint) => { let recipient = match mint.issued_to_identity_id() { diff --git a/packages/rs-dpp/src/tokens/token_event.rs b/packages/rs-dpp/src/tokens/token_event.rs index 35be8b84088..92ed079b906 100644 --- a/packages/rs-dpp/src/tokens/token_event.rs +++ b/packages/rs-dpp/src/tokens/token_event.rs @@ -33,6 +33,9 @@ use crate::ProtocolError; /// Alias representing the identity that will receive tokens or other effects from a token operation. pub type RecipientIdentifier = Identifier; +/// Alias representing the identity that will have tokens burned from their account. +pub type BurnFromIdentifier = Identifier; + /// Alias representing the identity performing a token purchase. pub type PurchaserIdentifier = Identifier; @@ -65,8 +68,9 @@ pub enum TokenEvent { /// Event representing the burning of tokens, removing them from circulation. /// /// - `TokenAmount`: The amount of tokens burned. + /// - `BurnFromIdentifier`: The account to burn from. /// - `TokenEventPublicNote`: Optional note associated with the event. - Burn(TokenAmount, TokenEventPublicNote), + Burn(TokenAmount, BurnFromIdentifier, TokenEventPublicNote), /// Event representing freezing of tokens for a specific identity. /// @@ -144,8 +148,14 @@ impl fmt::Display for TokenEvent { TokenEvent::Mint(amount, recipient, note) => { write!(f, "Mint {} to {}{}", amount, recipient, format_note(note)) } - TokenEvent::Burn(amount, note) => { - write!(f, "Burn {}{}", amount, format_note(note)) + TokenEvent::Burn(amount, burn_from_identifier, note) => { + write!( + f, + "Burn {} from {}{}", + amount, + burn_from_identifier, + format_note(note) + ) } TokenEvent::Freeze(identity, note) => { write!(f, "Freeze {}{}", identity, format_note(note)) @@ -219,7 +229,7 @@ impl TokenEvent { pub fn public_note(&self) -> Option<&str> { match self { TokenEvent::Mint(_, _, Some(note)) - | TokenEvent::Burn(_, Some(note)) + | TokenEvent::Burn(_, _, Some(note)) | TokenEvent::Freeze(_, Some(note)) | TokenEvent::Unfreeze(_, Some(note)) | TokenEvent::DestroyFrozenFunds(_, _, Some(note)) @@ -266,9 +276,10 @@ impl TokenEvent { } properties } - TokenEvent::Burn(burn_amount, public_note) => { + TokenEvent::Burn(burn_amount, burn_from_identifier, public_note) => { let mut properties = BTreeMap::from([ ("tokenId".to_string(), token_id.into()), + ("burnFromId".to_string(), burn_from_identifier.into()), ("amount".to_string(), burn_amount.into()), ]); if let Some(note) = public_note { diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs index c6239b862fc..77b00f7edf8 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs @@ -89,8 +89,13 @@ impl Platform { let action_id = Identifier::new([32; 32]); let group_action = GroupAction::V0(GroupActionV0 { contract_id: DATA_CONTRACT_ID, + proposer_id: IDENTITY_ID_1, token_contract_position: 2, - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some("world on fire".into()))), + event: GroupActionEvent::TokenEvent(TokenEvent::Burn( + 10, + IDENTITY_ID_1, + Some("world on fire".into()), + )), }); self.drive.add_group_action( diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_burn_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_burn_transition_action/state_v0/mod.rs index d7e362ba355..2633713736e 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_burn_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_burn_transition_action/state_v0/mod.rs @@ -75,7 +75,7 @@ impl TokenBurnTransitionActionStateValidationV0 for TokenBurnTransitionAction { } if let Some(original_group_action) = self.base().original_group_action() { - if let GroupActionEvent::TokenEvent(TokenEvent::Burn(old_group_action_amount, _)) = + if let GroupActionEvent::TokenEvent(TokenEvent::Burn(old_group_action_amount, _, _)) = original_group_action.event() { if old_group_action_amount != &self.burn_amount() { @@ -111,7 +111,7 @@ impl TokenBurnTransitionActionStateValidationV0 for TokenBurnTransitionAction { .drive .fetch_identity_token_balance( self.token_id().to_buffer(), - owner_id.to_buffer(), + self.burn_from_identifier().to_buffer(), transaction, platform_version, )? @@ -123,7 +123,7 @@ impl TokenBurnTransitionActionStateValidationV0 for TokenBurnTransitionAction { ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( IdentityDoesNotHaveEnoughTokenBalanceError::new( self.token_id(), - owner_id, + self.burn_from_identifier(), self.burn_amount(), balance, "burn".to_string(), diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs index 98fe26773fe..48923a42d90 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/burn/mod.rs @@ -2,6 +2,7 @@ use super::*; mod token_burn_tests { use super::*; + use dpp::state_transition::batch_transition::TokenBurnTransition; #[test] fn test_token_burn() { @@ -277,4 +278,528 @@ mod token_burn_tests { .expect("expected to fetch token balance"); assert_eq!(token_balance, None); } + + #[test] + fn test_token_burn_group_action_tokens_transferred_before_completion() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + let platform_state = platform.state.load(); + + let (identity1, signer1, key1) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (identity2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (recipient, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity1.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_burning_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity1.id(), 1), (identity2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + // Step 1: Mint tokens + add_tokens_to_identity(&platform, token_id, identity1.id(), 100000); + + // Step 2: Initiate burn as proposer + let burn_transition = BatchTransition::new_token_burn_transition( + token_id, + identity1.id(), + contract.id(), + 0, + 100000, + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key1, + 2, + 0, + &signer1, + platform_version, + None, + ) + .expect("expected to create burn transition"); + + let token_burn_serialized_transition = burn_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_burn_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Step 3: Transfer tokens away + + let token_transfer_transition = BatchTransition::new_token_transfer_transition( + token_id, + identity1.id(), + contract.id(), + 0, + 1337, + recipient.id(), + None, + None, + None, + &key1, + 3, + 0, + &signer1, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_serialized_transition = token_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity1.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(198663)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 1337; + assert_eq!(token_balance, Some(expected_amount)); + + // Step 4: Confirm burn by second group member + let action_id = TokenBurnTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity1.id().as_bytes(), + 2, + 100000, + ); + + let confirm_burn_transition = BatchTransition::new_token_burn_transition( + token_id, + identity2.id(), + contract.id(), + 0, + 100000, + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expected to create confirmation transition"); + + let token_burn_confirm_serialized_transition = confirm_burn_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_burn_confirm_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Validate the burn still succeeded even though tokens were transferred + let balance1 = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity1.id().to_buffer(), + None, + platform_version, + ) + .expect("expected balance fetch"); + + let balance2 = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected balance fetch"); + + assert_eq!(balance1, Some(98663)); // Original identity should have no tokens + assert_eq!(balance2, Some(1337)); // Recipient should not keep transferred tokens if burn was enforced + } + + #[test] + fn test_token_burn_group_action_tokens_transferred_before_completion_not_enough_balance() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + let platform_state = platform.state.load(); + + let (identity1, signer1, key1) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (identity2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (recipient, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity1.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_burning_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity1.id(), 1), (identity2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + // Step 1: Initiate burn as proposer + let burn_transition = BatchTransition::new_token_burn_transition( + token_id, + identity1.id(), + contract.id(), + 0, + 100000, + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key1, + 2, + 0, + &signer1, + platform_version, + None, + ) + .expect("expected to create burn transition"); + + let token_burn_serialized_transition = burn_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_burn_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Step 2: Transfer tokens away + + let token_transfer_transition = BatchTransition::new_token_transfer_transition( + token_id, + identity1.id(), + contract.id(), + 0, + 1337, + recipient.id(), + None, + None, + None, + &key1, + 3, + 0, + &signer1, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_serialized_transition = token_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity1.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(98663)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 1337; + assert_eq!(token_balance, Some(expected_amount)); + + // Step 3: Confirm burn by second group member + let action_id = TokenBurnTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity1.id().as_bytes(), + 2, + 100000, + ); + + let confirm_burn_transition = BatchTransition::new_token_burn_transition( + token_id, + identity2.id(), + contract.id(), + 0, + 100000, + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expected to create confirmation transition"); + + let token_burn_confirm_serialized_transition = confirm_burn_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_burn_confirm_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( + _ + )), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Validate the burn still succeeded even though tokens were transferred + let balance1 = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity1.id().to_buffer(), + None, + platform_version, + ) + .expect("expected balance fetch"); + + let balance2 = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected balance fetch"); + + assert_eq!(balance1, Some(98663)); + assert_eq!(balance2, Some(1337)); + } } diff --git a/packages/rs-drive-abci/src/query/group_queries/group_actions/v0/mod.rs b/packages/rs-drive-abci/src/query/group_queries/group_actions/v0/mod.rs index 0db9f666932..a6af261c424 100644 --- a/packages/rs-drive-abci/src/query/group_queries/group_actions/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/group_queries/group_actions/v0/mod.rs @@ -143,10 +143,11 @@ impl Platform { })), }) } - TokenEvent::Burn(amount, public_note) => { + TokenEvent::Burn(amount, burn_from_id, public_note) => { group_action_event::EventType::TokenEvent(TokenEventResponse { r#type: Some(token_event::Type::Burn(BurnEvent { amount, + burn_from_id: burn_from_id.to_vec(), public_note, })), }) diff --git a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs index eab3d5423fb..7ae113a9786 100644 --- a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs @@ -177,14 +177,16 @@ mod tests { let action_1 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_1_id, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(100, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_2_id, token_contract_position: 0, - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), + event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, identity_1_id, None)), }); // Add actions using `add_group_action` @@ -414,18 +416,21 @@ mod tests { let action_1 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_1_id, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(200, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_1_id, token_contract_position: 0, - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), + event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, identity_1_id, None)), }); let action_3 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_2_id, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Transfer( identity_2_id, @@ -438,6 +443,7 @@ mod tests { let action_4 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_3_id, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Transfer( identity_3_id, diff --git a/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs b/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs index 3a5aa1ede7d..a259b36b485 100644 --- a/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs @@ -173,14 +173,16 @@ mod tests { let action_1 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_1_id, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(100, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_2_id, token_contract_position: 1, - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), + event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, identity_2_id, None)), }); // Add actions using `add_group_action` @@ -443,8 +445,9 @@ mod tests { let action_id = Identifier::random(); let action = GroupAction::V0(GroupActionV0 { contract_id, + proposer_id: identity_2_id, token_contract_position: 0, - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), + event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, identity_2_id, None)), }); drive diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs index 9e3b26be8e5..01dc6006537 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs @@ -52,11 +52,16 @@ impl DriveHighLevelBatchOperationConverter for TokenBurnTransitionAction { .. }) = self.base().store_in_group() { - let event = TokenEvent::Burn(self.burn_amount(), self.public_note().cloned()); + let event = TokenEvent::Burn( + self.burn_amount(), + self.burn_from_identifier(), + self.public_note().cloned(), + ); let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) @@ -78,7 +83,7 @@ impl DriveHighLevelBatchOperationConverter for TokenBurnTransitionAction { if self.base().perform_action() { ops.push(TokenOperation(TokenOperationType::TokenBurn { token_id: self.token_id(), - identity_balance_holder_id: owner_id, + identity_balance_holder_id: self.burn_from_identifier(), burn_amount: self.burn_amount(), })); @@ -88,7 +93,11 @@ impl DriveHighLevelBatchOperationConverter for TokenBurnTransitionAction { token_id: self.token_id(), owner_id, nonce: identity_contract_nonce, - event: TokenEvent::Burn(self.burn_amount(), self.public_note_owned()), + event: TokenEvent::Burn( + self.burn_amount(), + self.burn_from_identifier(), + self.public_note_owned(), + ), })); } } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs index 4a4849e28d7..48d514167cd 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs @@ -61,6 +61,7 @@ impl DriveHighLevelBatchOperationConverter for TokenConfigUpdateTransitionAction let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs index f099c36bb86..494d4061c21 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs @@ -56,6 +56,7 @@ impl DriveHighLevelBatchOperationConverter for TokenDestroyFrozenFundsTransition let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs index 424cab2afdc..ee45bc8e700 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs @@ -56,6 +56,7 @@ impl DriveHighLevelBatchOperationConverter for TokenEmergencyActionTransitionAct let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs index 6d9c97dbf67..2cba071dd61 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs @@ -60,6 +60,7 @@ impl DriveHighLevelBatchOperationConverter for TokenFreezeTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs index 14672d15c5a..c17296d7083 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs @@ -61,6 +61,7 @@ impl DriveHighLevelBatchOperationConverter for TokenMintTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs index 117df8fac4b..69ded20b25a 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs @@ -60,6 +60,7 @@ impl DriveHighLevelBatchOperationConverter for TokenSetPriceForDirectPurchaseTra let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_transition.rs index 156608bcf14..54c8009d197 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_transition.rs @@ -74,6 +74,7 @@ impl TokenTransitionAction { match self { TokenTransitionAction::BurnAction(burn_action) => TokenEvent::Burn( burn_action.burn_amount(), + burn_action.burn_from_identifier(), burn_action.public_note().cloned(), ), TokenTransitionAction::MintAction(mint_action) => TokenEvent::Mint( diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs index a9bd1787069..47011cd88ff 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs @@ -60,6 +60,7 @@ impl DriveHighLevelBatchOperationConverter for TokenUnfreezeTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { contract_id: self.base().data_contract_id(), + proposer_id: owner_id, token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs index bda035a5a12..cf579345979 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs @@ -85,7 +85,7 @@ impl TokenBaseTransitionActionV0 { Err(e) => return Err(e.into()), }; let required_power = group.required_power(); - let (current_power, current_group_action_event, change_to_original_public_note) = + let (current_power, current_group_action, change_to_original_public_note) = if action_is_proposer { (0, None, None) } else { @@ -189,7 +189,7 @@ impl TokenBaseTransitionActionV0 { }; ( perform_action, - Some((store_in_group, current_group_action_event)), + Some((store_in_group, current_group_action)), change_to_original_public_note, ) } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/mod.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/mod.rs index 0e24f14920b..7d75c5ab511 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/mod.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/mod.rs @@ -1,4 +1,5 @@ use derive_more::From; +use dpp::identifier::Identifier; /// transformer module for token burn transition action pub mod transformer; @@ -28,6 +29,18 @@ impl TokenBurnTransitionActionAccessorsV0 for TokenBurnTransitionAction { } } + fn burn_from_identifier(&self) -> Identifier { + match self { + TokenBurnTransitionAction::V0(v0) => v0.burn_from_identifier, + } + } + + fn set_burn_from_identifier(&mut self, burn_from_identifier: Identifier) { + match self { + TokenBurnTransitionAction::V0(v0) => v0.burn_from_identifier = burn_from_identifier, + } + } + fn burn_amount(&self) -> u64 { match self { TokenBurnTransitionAction::V0(v0) => v0.burn_amount, diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/mod.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/mod.rs index 7a6c9a51845..74163beb70a 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/mod.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/mod.rs @@ -1,6 +1,7 @@ mod transformer; use std::sync::Arc; +use dpp::balances::credits::TokenAmount; use dpp::identifier::Identifier; use dpp::prelude::IdentityNonce; use crate::drive::contract::DataContractFetchInfo; @@ -11,8 +12,10 @@ use crate::state_transition_action::batch::batched_transition::token_transition: pub struct TokenBurnTransitionActionV0 { /// Base token transition action pub base: TokenBaseTransitionAction, + /// Burn from identifier + pub burn_from_identifier: Identifier, /// The amount of tokens to burn - pub burn_amount: u64, + pub burn_amount: TokenAmount, /// A public note pub public_note: Option, } @@ -25,6 +28,12 @@ pub trait TokenBurnTransitionActionAccessorsV0 { /// Consumes self and returns the base token transition action fn base_owned(self) -> TokenBaseTransitionAction; + /// Returns the identifier of the identity account from which we will burn + fn burn_from_identifier(&self) -> Identifier; + + /// Sets the identifier of the identity account from which we will burn + fn set_burn_from_identifier(&mut self, burn_from_identifier: Identifier); + /// Returns the amount of tokens to burn fn burn_amount(&self) -> u64; @@ -80,6 +89,14 @@ impl TokenBurnTransitionActionAccessorsV0 for TokenBurnTransitionActionV0 { self.base } + fn burn_from_identifier(&self) -> Identifier { + self.burn_from_identifier + } + + fn set_burn_from_identifier(&mut self, burn_from_identifier: Identifier) { + self.burn_from_identifier = burn_from_identifier; + } + fn burn_amount(&self) -> u64 { self.burn_amount } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/transformer.rs index 0cc66ba5d68..021239e60b9 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_burn_transition_action/v0/transformer.rs @@ -5,7 +5,10 @@ use grovedb::TransactionArg; use std::sync::Arc; use dpp::block::block_info::BlockInfo; use dpp::fee::fee_result::FeeResult; +use dpp::group::action_event::GroupActionEvent; +use dpp::group::group_action::GroupActionAccessors; use dpp::prelude::UserFeeIncrease; +use dpp::tokens::token_event::TokenEvent; use dpp::validation::ConsensusValidationResult; use platform_version::version::PlatformVersion; use crate::drive::contract::DataContractFetchInfo; @@ -13,7 +16,7 @@ use crate::drive::Drive; use crate::error::Error; use crate::state_transition_action::batch::batched_transition::BatchedTransitionAction; use crate::state_transition_action::batch::batched_transition::token_transition::TokenTransitionAction; -use crate::state_transition_action::batch::batched_transition::token_transition::token_base_transition_action::TokenBaseTransitionAction; +use crate::state_transition_action::batch::batched_transition::token_transition::token_base_transition_action::{TokenBaseTransitionAction, TokenBaseTransitionActionAccessorsV0}; use crate::state_transition_action::batch::batched_transition::token_transition::token_burn_transition_action::v0::TokenBurnTransitionActionV0; use crate::state_transition_action::system::bump_identity_data_contract_nonce_action::BumpIdentityDataContractNonceAction; @@ -109,10 +112,27 @@ impl TokenBurnTransitionActionV0 { } }; + let burn_from_identifier = + if let Some(original_group_action) = base_action.original_group_action() { + let GroupActionEvent::TokenEvent(TokenEvent::Burn(_, burn_from_id, _)) = + original_group_action.event() + else { + return Err(ProtocolError::CorruptedCodeExecution(format!( + "received a non burn token event: {}", + original_group_action.event() + )) + .into()); + }; + *burn_from_id + } else { + owner_id + }; + Ok(( BatchedTransitionAction::TokenAction(TokenTransitionAction::BurnAction( TokenBurnTransitionActionV0 { base: base_action, + burn_from_identifier, burn_amount, public_note: change_note.unwrap_or(public_note), } @@ -215,10 +235,27 @@ impl TokenBurnTransitionActionV0 { } }; + let burn_from_identifier = + if let Some(original_group_action) = base_action.original_group_action() { + let GroupActionEvent::TokenEvent(TokenEvent::Burn(_, burn_from_id, _)) = + original_group_action.event() + else { + return Err(ProtocolError::CorruptedCodeExecution(format!( + "received a non burn token event: {}", + original_group_action.event() + )) + .into()); + }; + *burn_from_id + } else { + owner_id + }; + Ok(( BatchedTransitionAction::TokenAction(TokenTransitionAction::BurnAction( TokenBurnTransitionActionV0 { base: base_action, + burn_from_identifier, burn_amount: *burn_amount, public_note: change_note.unwrap_or(public_note.clone()), } diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index 993384bc585..0bcf499dfa3 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -365,6 +365,7 @@ impl Drive { TokenTransition::DestroyFrozenFunds(_) => { Some(vec!["destroyedAmount", "note"]) } + TokenTransition::Burn(_) => Some(vec!["burnFromId", "note"]), TokenTransition::Claim(_) => Some(vec!["amount"]), TokenTransition::DirectPurchase(_) => { let purchase_cost: Credits = @@ -377,8 +378,7 @@ impl Drive { } Some(vec!["purchaseCost"]) } - TokenTransition::Burn(_) - | TokenTransition::Mint(_) + TokenTransition::Mint(_) | TokenTransition::Freeze(_) | TokenTransition::Unfreeze(_) | TokenTransition::EmergencyAction(_) diff --git a/packages/rs-sdk/tests/fetch/group_actions.rs b/packages/rs-sdk/tests/fetch/group_actions.rs index 98282439406..ba90aaaaa01 100644 --- a/packages/rs-sdk/tests/fetch/group_actions.rs +++ b/packages/rs-sdk/tests/fetch/group_actions.rs @@ -150,7 +150,7 @@ async fn test_fetch_all_group_actions() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - contract_id: DATA_CONTRACT_ID, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, proposer_id: IDENTITY_ID_1, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, IDENTITY_ID_1, Some(note))), }))) if note == "world on fire" ); } @@ -182,7 +182,7 @@ async fn test_fetch_one_group_action_since_existing_one_with_limit() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - contract_id: DATA_CONTRACT_ID, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, proposer_id: IDENTITY_ID_1, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, IDENTITY_ID_1, Some(note))), }))) if note == "world on fire" ); } diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json index cd259253587..55cea88ac8f 100644 Binary files a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json and b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json differ diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json new file mode 100644 index 00000000000..bfc59fcc143 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json @@ -0,0 +1 @@ +a97f1e11fae25a84dc77703a43c16b3d749ed36bea6a41f3eae108a5c5a1312eeef5ecac1f73819dc78db311804308b0 \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json deleted file mode 100644 index 9a3152f6510..00000000000 --- a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json +++ /dev/null @@ -1 +0,0 @@ -8ee1e9729e6a059cffb01530a9f823b0ed7fc60e93d3a0bbb99d3940bb62939d0394ec83e0abb9b16e1233907d9e36f7 \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json index 17fa25a7e03..9fdf5763f8d 100644 Binary files a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json and b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json differ diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json new file mode 100644 index 00000000000..bfc59fcc143 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-0acdeecd6241d2cdec7ff61f91adb6f84188c97ff5d6f33f2225522b6f666be8.json @@ -0,0 +1 @@ +a97f1e11fae25a84dc77703a43c16b3d749ed36bea6a41f3eae108a5c5a1312eeef5ecac1f73819dc78db311804308b0 \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json deleted file mode 100644 index a8544f7dc02..00000000000 --- a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json +++ /dev/null @@ -1 +0,0 @@ -aa8fcaad83dc96339d97ce135e408ca1eb5f7392ade678e224856b394b6370b2fa522a002acb149020e17d3bbbe6444e \ No newline at end of file diff --git a/packages/token-history-contract/schema/v1/token-history-contract-documents.json b/packages/token-history-contract/schema/v1/token-history-contract-documents.json index 799128d7d4c..04524effc25 100644 --- a/packages/token-history-contract/schema/v1/token-history-contract-documents.json +++ b/packages/token-history-contract/schema/v1/token-history-contract-documents.json @@ -40,6 +40,20 @@ "$createdAt": "asc" } ] + }, + { + "name": "byBurnFromId", + "properties": [ + { + "tokenId": "asc" + }, + { + "burnFromId": "asc" + }, + { + "$createdAt": "asc" + } + ] } ], "properties": { @@ -52,21 +66,31 @@ "position": 0, "contentMediaType": "application/x.dash.dpp.identifier" }, + "burnFromId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "description": "The identifier of the identity from which tokens will be burned", + "position": 1, + "contentMediaType": "application/x.dash.dpp.identifier" + }, "amount": { "type": "integer", "minimum": 0, "description": "The amount that was burned", - "position": 1 + "position": 2 }, "note": { "type": "string", "maxLength": 2048, "description": "An optional explanation of why this burn took place", - "position": 2 + "position": 3 } }, "required": [ "tokenId", + "burnFromId", "amount", "$createdAt", "$createdAtBlockHeight" @@ -145,7 +169,7 @@ "byteArray": true, "minItems": 32, "maxItems": 32, - "description": "The token ID", + "description": "The recipient ID", "position": 1, "contentMediaType": "application/x.dash.dpp.identifier" }, diff --git a/packages/token-history-contract/test/unit/tokenHistoryContract.spec.js b/packages/token-history-contract/test/unit/tokenHistoryContract.spec.js index ce3d9195664..1a7a1412f6a 100644 --- a/packages/token-history-contract/test/unit/tokenHistoryContract.spec.js +++ b/packages/token-history-contract/test/unit/tokenHistoryContract.spec.js @@ -49,6 +49,7 @@ describe('Token History Contract', () => { beforeEach(() => { rawBurnDocument = { tokenId: crypto.randomBytes(32), + burnFromId: crypto.randomBytes(32), amount: 100, note: 'Burning tokens', };