diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md index e77924001c3e9..b82be8c9a55b1 100644 --- a/aptos-move/framework/aptos-framework/doc/multisig_account.md +++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md @@ -88,10 +88,10 @@ and implement the governance voting logic on top. - [Function `reject_transaction`](#0x1_multisig_account_reject_transaction) - [Function `vote_transanction`](#0x1_multisig_account_vote_transanction) - [Function `execute_rejected_transaction`](#0x1_multisig_account_execute_rejected_transaction) -- [Function `flush_next_sequence_number_and_owner_schema`](#0x1_multisig_account_flush_next_sequence_number_and_owner_schema) +- [Function `flush_last_resolved_sequence_number_and_owner_schema`](#0x1_multisig_account_flush_last_resolved_sequence_number_and_owner_schema) - [Function `validate_multisig_transaction`](#0x1_multisig_account_validate_multisig_transaction) - [Function `validate_multisig_transaction_with_optional_sequence_number`](#0x1_multisig_account_validate_multisig_transaction_with_optional_sequence_number) -- [Function `flush_next_sequence_number_and_owner_schema_execution`](#0x1_multisig_account_flush_next_sequence_number_and_owner_schema_execution) +- [Function `flush_last_resolved_sequence_number_and_owner_schema_execution`](#0x1_multisig_account_flush_last_resolved_sequence_number_and_owner_schema_execution) - [Function `successful_transaction_execution_cleanup`](#0x1_multisig_account_successful_transaction_execution_cleanup) - [Function `failed_transaction_execution_cleanup`](#0x1_multisig_account_failed_transaction_execution_cleanup) - [Function `remove_executed_transaction`](#0x1_multisig_account_remove_executed_transaction) @@ -781,6 +781,16 @@ Owner list cannot contain the same address more than once. + + +Flush transaction proposal has expired. + + +
const EFLUSH_TXN_EXPIRED: u64 = 19;
+
+ + + Payload hash must be exactly 32 bytes (sha3-256). @@ -2082,9 +2092,9 @@ Remove the next transaction if it has sufficient owner rejections. - + -## Function `flush_next_sequence_number_and_owner_schema` +## Function `flush_last_resolved_sequence_number_and_owner_schema` Prototype function signature used for flush proposal payload generation. @@ -2111,17 +2121,20 @@ corresponds to a valid flush transaction: if the proposal stores the entire payl on-chain then the payload is deserialized and verified, but if the proposal stores only a payload hash, then the payload included in the Aptos API MultisigFlush transaction is instead verified. -5. The VM calls flush_next_sequence_number_and_owner_schema_execution, applying owner and -required signature count updates, effectively bumping the proposal to the head of the -pending transaction queue such that all other pending transactions are disregarded. +5. The VM calls flush_last_resolved_sequence_number_and_owner_schema_execution, applying +owner and required signature count updates, effectively bumping the proposal to the head +of the pending transaction queue such that all other pending transactions are +disregarded. 6. The VM calls successful_transaction_execution_cleanup. An optional new number of signatures must be passed as a singleton vector due to entry function constraints: pass either an empty vector for no update, or a singleton vector with one element specifying the new number of signatures required. +An expiry time in UNIX seconds is required to safeguard against untimely execution. + -
entry fun flush_next_sequence_number_and_owner_schema(_new_owners: vector<address>, _owners_to_remove: vector<address>, _optional_new_num_signatures_required_as_vector: vector<u64>)
+
entry fun flush_last_resolved_sequence_number_and_owner_schema(_new_owners: vector<address>, _owners_to_remove: vector<address>, _optional_new_num_signatures_required_as_vector: vector<u64>, _expiry_in_unix_seconds: u64)
 
@@ -2130,10 +2143,11 @@ one element specifying the new number of signatures required. Implementation -
entry fun flush_next_sequence_number_and_owner_schema(
+
entry fun flush_last_resolved_sequence_number_and_owner_schema(
     _new_owners: vector<address>,
     _owners_to_remove: vector<address>,
     _optional_new_num_signatures_required_as_vector: vector<u64>,
+    _expiry_in_unix_seconds: u64
 ) {}
 
@@ -2246,18 +2260,18 @@ Check next sequence number to execute if none specified. - + -## Function `flush_next_sequence_number_and_owner_schema_execution` +## Function `flush_last_resolved_sequence_number_and_owner_schema_execution` Flush pending transactions and owner schema in reponse to a DoS attack. -Bumps a flush transaction proposal to the front of the pending transaction queue, then -updates the next transaction proposal sequence number such all other pending transactions -are ignored, to be overwritten by future transaction proposals. +Bumps a flush transaction proposal to the front of the pending transaction queue, by +updating the last resolved sequence number such that all pending transactions +are ignored. -
fun flush_next_sequence_number_and_owner_schema_execution(multisig_address: address, new_owners: vector<address>, owners_to_remove: vector<address>, optional_new_num_signatures_required: option::Option<u64>, flush_transaction_proposal_sequence_number: u64)
+
fun flush_last_resolved_sequence_number_and_owner_schema_execution(multisig_address: address, new_owners: vector<address>, owners_to_remove: vector<address>, optional_new_num_signatures_required: option::Option<u64>, expiry_in_unix_seconds: u64, flush_transaction_proposal_sequence_number: u64)
 
@@ -2266,20 +2280,26 @@ are ignored, to be overwritten by future transaction proposals. Implementation -
fun flush_next_sequence_number_and_owner_schema_execution(
+
fun flush_last_resolved_sequence_number_and_owner_schema_execution(
     multisig_address: address,
     new_owners: vector<address>,
     owners_to_remove: vector<address>,
     optional_new_num_signatures_required: Option<u64>,
-    flush_transaction_proposal_sequence_number: u64,
+    expiry_in_unix_seconds: u64,
+    flush_transaction_proposal_sequence_number: u64
 ) acquires MultisigAccount {
+    // Verify that the proposal has not yet expired.
+    assert!(
+        expiry_in_unix_seconds < now_seconds(),
+        error::invalid_state(EFLUSH_TXN_EXPIRED)
+    );
     let multisig_account_ref_mut =
         borrow_global_mut<MultisigAccount>(multisig_address);
     let transactions_table_ref_mut =
         &mut multisig_account_ref_mut.transactions;
     // Get new sequence number for flush transaction proposal.
     let new_flush_transaction_proposal_sequence_number =
-        multisig_account_ref_mut.last_executed_sequence_number + 1;
+        multisig_account_ref_mut.next_sequence_number;
     // Update flush transaction proposal sequence number.
     let flush_transaction_proposal = table::remove(
         transactions_table_ref_mut,
@@ -2293,6 +2313,9 @@ are ignored, to be overwritten by future transaction proposals.
     // Update sequence number for next transaction proposal.
     multisig_account_ref_mut.next_sequence_number =
         new_flush_transaction_proposal_sequence_number + 1;
+    // Update sequence number for last resolved transaction proposal.
+    multisig_account_ref_mut.last_executed_sequence_number =
+        new_flush_transaction_proposal_sequence_number - 1;
     flush_owner_schema(
         multisig_address,
         new_owners,
diff --git a/aptos-move/framework/aptos-framework/sources/multisig_account.move b/aptos-move/framework/aptos-framework/sources/multisig_account.move
index 0dd3a23a0432f..04bc02cb90c07 100644
--- a/aptos-move/framework/aptos-framework/sources/multisig_account.move
+++ b/aptos-move/framework/aptos-framework/sources/multisig_account.move
@@ -93,6 +93,8 @@ module aptos_framework::multisig_account {
     const EINVALID_SEQUENCE_NUMBER: u64 = 17;
     /// Provided owners to remove and new owners overlap.
     const EOWNERS_TO_REMOVE_NEW_OWNERS_OVERLAP: u64 = 18;
+    /// Flush transaction proposal has expired.
+    const EFLUSH_TXN_EXPIRED: u64 = 19;
 
     /// Represents a multisig account's configurations and transactions.
     /// This will be stored in the multisig account (created as a resource account separate from any owner accounts).
@@ -802,18 +804,22 @@ module aptos_framework::multisig_account {
     ///    on-chain then the payload is deserialized and verified, but if the proposal stores only
     ///    a payload hash, then the payload included in the Aptos API MultisigFlush transaction is
     ///    instead verified.
-    /// 5. The VM calls `flush_next_sequence_number_and_owner_schema_execution`, applying owner and
-    ///    required signature count updates, effectively bumping the proposal to the head of the
-    ///    pending transaction queue such that all other pending transactions are disregarded.
+    /// 5. The VM calls `flush_last_resolved_sequence_number_and_owner_schema_execution`, applying
+    ///    owner and required signature count updates, effectively bumping the proposal to the head
+    ///    of the pending transaction queue such that all other pending transactions are
+    ///    disregarded.
     /// 6. The VM calls `successful_transaction_execution_cleanup`.
     ///
     /// An optional new number of signatures must be passed as a singleton vector due to entry
     /// function constraints: pass either an empty vector for no update, or a singleton vector with
     /// one element specifying the new number of signatures required.
-    entry fun flush_next_sequence_number_and_owner_schema(
+    ///
+    /// An expiry time in UNIX seconds is required to safeguard against untimely execution.
+    entry fun flush_last_resolved_sequence_number_and_owner_schema(
         _new_owners: vector
, _owners_to_remove: vector
, _optional_new_num_signatures_required_as_vector: vector, + _expiry_in_unix_seconds: u64 ) {} ////////////////////////// To be called by VM only /////////////////////////////// @@ -885,23 +891,29 @@ module aptos_framework::multisig_account { /// Flush pending transactions and owner schema in reponse to a DoS attack. /// - /// Bumps a flush transaction proposal to the front of the pending transaction queue, then - /// updates the next transaction proposal sequence number such all other pending transactions - /// are ignored, to be overwritten by future transaction proposals. - fun flush_next_sequence_number_and_owner_schema_execution( + /// Bumps a flush transaction proposal to the front of the pending transaction queue, by + /// updating the last resolved sequence number such that all pending transactions + /// are ignored. + fun flush_last_resolved_sequence_number_and_owner_schema_execution( multisig_address: address, new_owners: vector
, owners_to_remove: vector
, optional_new_num_signatures_required: Option, - flush_transaction_proposal_sequence_number: u64, + expiry_in_unix_seconds: u64, + flush_transaction_proposal_sequence_number: u64 ) acquires MultisigAccount { + // Verify that the proposal has not yet expired. + assert!( + expiry_in_unix_seconds < now_seconds(), + error::invalid_state(EFLUSH_TXN_EXPIRED) + ); let multisig_account_ref_mut = borrow_global_mut(multisig_address); let transactions_table_ref_mut = &mut multisig_account_ref_mut.transactions; // Get new sequence number for flush transaction proposal. let new_flush_transaction_proposal_sequence_number = - multisig_account_ref_mut.last_executed_sequence_number + 1; + multisig_account_ref_mut.next_sequence_number; // Update flush transaction proposal sequence number. let flush_transaction_proposal = table::remove( transactions_table_ref_mut, @@ -915,6 +927,9 @@ module aptos_framework::multisig_account { // Update sequence number for next transaction proposal. multisig_account_ref_mut.next_sequence_number = new_flush_transaction_proposal_sequence_number + 1; + // Update sequence number for last resolved transaction proposal. + multisig_account_ref_mut.last_executed_sequence_number = + new_flush_transaction_proposal_sequence_number - 1; flush_owner_schema( multisig_address, new_owners, diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index b19d03487e4fc..d820886a76033 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -429,18 +429,22 @@ pub enum EntryFunctionCall { /// on-chain then the payload is deserialized and verified, but if the proposal stores only /// a payload hash, then the payload included in the Aptos API MultisigFlush transaction is /// instead verified. - /// 5. The VM calls `flush_next_sequence_number_and_owner_schema_execution`, applying owner and - /// required signature count updates, effectively bumping the proposal to the head of the - /// pending transaction queue such that all other pending transactions are disregarded. + /// 5. The VM calls `flush_last_resolved_sequence_number_and_owner_schema_execution`, applying + /// owner and required signature count updates, effectively bumping the proposal to the head + /// of the pending transaction queue such that all other pending transactions are + /// disregarded. /// 6. The VM calls `successful_transaction_execution_cleanup`. /// /// An optional new number of signatures must be passed as a singleton vector due to entry /// function constraints: pass either an empty vector for no update, or a singleton vector with /// one element specifying the new number of signatures required. - MultisigAccountFlushNextSequenceNumberAndOwnerSchema { + /// + /// An expiry time in UNIX seconds is required to safeguard against untimely execution. + MultisigAccountFlushLastResolvedSequenceNumberAndOwnerSchema { _new_owners: Vec, _owners_to_remove: Vec, _optional_new_num_signatures_required_as_vector: Vec, + _expiry_in_unix_seconds: u64, }, /// Reject a multisig transaction. @@ -1060,14 +1064,16 @@ impl EntryFunctionCall { MultisigAccountExecuteRejectedTransaction { multisig_account } => { multisig_account_execute_rejected_transaction(multisig_account) }, - MultisigAccountFlushNextSequenceNumberAndOwnerSchema { + MultisigAccountFlushLastResolvedSequenceNumberAndOwnerSchema { _new_owners, _owners_to_remove, _optional_new_num_signatures_required_as_vector, - } => multisig_account_flush_next_sequence_number_and_owner_schema( + _expiry_in_unix_seconds, + } => multisig_account_flush_last_resolved_sequence_number_and_owner_schema( _new_owners, _owners_to_remove, _optional_new_num_signatures_required_as_vector, + _expiry_in_unix_seconds, ), MultisigAccountRejectTransaction { multisig_account, @@ -2367,18 +2373,22 @@ pub fn multisig_account_execute_rejected_transaction( /// on-chain then the payload is deserialized and verified, but if the proposal stores only /// a payload hash, then the payload included in the Aptos API MultisigFlush transaction is /// instead verified. -/// 5. The VM calls `flush_next_sequence_number_and_owner_schema_execution`, applying owner and -/// required signature count updates, effectively bumping the proposal to the head of the -/// pending transaction queue such that all other pending transactions are disregarded. +/// 5. The VM calls `flush_last_resolved_sequence_number_and_owner_schema_execution`, applying +/// owner and required signature count updates, effectively bumping the proposal to the head +/// of the pending transaction queue such that all other pending transactions are +/// disregarded. /// 6. The VM calls `successful_transaction_execution_cleanup`. /// /// An optional new number of signatures must be passed as a singleton vector due to entry /// function constraints: pass either an empty vector for no update, or a singleton vector with /// one element specifying the new number of signatures required. -pub fn multisig_account_flush_next_sequence_number_and_owner_schema( +/// +/// An expiry time in UNIX seconds is required to safeguard against untimely execution. +pub fn multisig_account_flush_last_resolved_sequence_number_and_owner_schema( _new_owners: Vec, _owners_to_remove: Vec, _optional_new_num_signatures_required_as_vector: Vec, + _expiry_in_unix_seconds: u64, ) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new( @@ -2388,12 +2398,13 @@ pub fn multisig_account_flush_next_sequence_number_and_owner_schema( ]), ident_str!("multisig_account").to_owned(), ), - ident_str!("flush_next_sequence_number_and_owner_schema").to_owned(), + ident_str!("flush_last_resolved_sequence_number_and_owner_schema").to_owned(), vec![], vec![ bcs::to_bytes(&_new_owners).unwrap(), bcs::to_bytes(&_owners_to_remove).unwrap(), bcs::to_bytes(&_optional_new_num_signatures_required_as_vector).unwrap(), + bcs::to_bytes(&_expiry_in_unix_seconds).unwrap(), ], )) } @@ -4219,18 +4230,19 @@ mod decoder { } } - pub fn multisig_account_flush_next_sequence_number_and_owner_schema( + pub fn multisig_account_flush_last_resolved_sequence_number_and_owner_schema( payload: &TransactionPayload, ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some( - EntryFunctionCall::MultisigAccountFlushNextSequenceNumberAndOwnerSchema { + EntryFunctionCall::MultisigAccountFlushLastResolvedSequenceNumberAndOwnerSchema { _new_owners: bcs::from_bytes(script.args().get(0)?).ok()?, _owners_to_remove: bcs::from_bytes(script.args().get(1)?).ok()?, _optional_new_num_signatures_required_as_vector: bcs::from_bytes( script.args().get(2)?, ) .ok()?, + _expiry_in_unix_seconds: bcs::from_bytes(script.args().get(3)?).ok()?, }, ) } else { @@ -5163,8 +5175,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy