diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md index b787024d32e31e..bf59b1f081df0f 100644 --- a/aptos-move/framework/aptos-framework/doc/multisig_account.md +++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md @@ -69,6 +69,7 @@ and implement the governance voting logic on top. - [Function `last_resolved_sequence_number`](#0x1_multisig_account_last_resolved_sequence_number) - [Function `next_sequence_number`](#0x1_multisig_account_next_sequence_number) - [Function `vote`](#0x1_multisig_account_vote) +- [Function `available_transaction_queue_capacity`](#0x1_multisig_account_available_transaction_queue_capacity) - [Function `create_with_existing_account`](#0x1_multisig_account_create_with_existing_account) - [Function `create_with_existing_account_and_revoke_auth_key`](#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key) - [Function `create`](#0x1_multisig_account_create) @@ -91,7 +92,10 @@ and implement the governance voting logic on top. - [Function `approve_transaction`](#0x1_multisig_account_approve_transaction) - [Function `reject_transaction`](#0x1_multisig_account_reject_transaction) - [Function `vote_transanction`](#0x1_multisig_account_vote_transanction) +- [Function `vote_transaction`](#0x1_multisig_account_vote_transaction) +- [Function `vote_transactions`](#0x1_multisig_account_vote_transactions) - [Function `execute_rejected_transaction`](#0x1_multisig_account_execute_rejected_transaction) +- [Function `execute_rejected_transactions`](#0x1_multisig_account_execute_rejected_transactions) - [Function `validate_multisig_transaction`](#0x1_multisig_account_validate_multisig_transaction) - [Function `successful_transaction_execution_cleanup`](#0x1_multisig_account_successful_transaction_execution_cleanup) - [Function `failed_transaction_execution_cleanup`](#0x1_multisig_account_failed_transaction_execution_cleanup) @@ -884,6 +888,16 @@ Number of signatures required must be more than zero and at most the total numbe + + +The number of pending transactions has exceeded the maximum allowed. + + +
const EMAX_PENDING_TRANSACTIONS_EXCEEDED: u64 = 19;
+
+
+
+
Multisig accounts has not been enabled on this current network yet.
@@ -894,6 +908,16 @@ Multisig accounts has not been enabled on this current network yet.
+
+
+The multisig v2 enhancement feature is not enabled.
+
+
+const EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED: u64 = 20;
+
+
+
+
Transaction has not received enough approvals to be executed.
@@ -994,6 +1018,15 @@ Transaction with specified id cannot be found.
+
+
+
+
+const MAX_PENDING_TRANSACTIONS: u64 = 20;
+
+
+
+
## Function `metadata`
@@ -1361,6 +1394,37 @@ Return a bool tuple indicating whether an owner has voted and if so, whether the
+
+
+
+
+## Function `available_transaction_queue_capacity`
+
+
+
+#[view]
+public fun available_transaction_queue_capacity(multisig_account: address): u64
+
+
+
+
+public fun available_transaction_queue_capacity(multisig_account: address): u64 acquires MultisigAccount {
+ let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account);
+ let num_pending_transactions = multisig_account_resource.next_sequence_number - multisig_account_resource.last_executed_sequence_number - 1;
+ if (num_pending_transactions > MAX_PENDING_TRANSACTIONS) {
+ 0
+ } else {
+ MAX_PENDING_TRANSACTIONS - num_pending_transactions
+ }
+}
+
+
+
+
public entry fun vote_transanction(owner: &signer, multisig_account: address, sequence_number: u64, approved: bool)
@@ -2273,6 +2339,64 @@ Generic function that can be used to either approve or reject a multisig transac
+
+
+
+
+## Function `vote_transaction`
+
+Generic function that can be used to either approve or reject a multisig transaction
+
+
+public entry fun vote_transaction(owner: &signer, multisig_account: address, sequence_number: u64, approved: bool)
+
+
+
+
+
+Implementation
+
+
+public entry fun vote_transaction(
+ owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+}
+
+
+
+
+
+
+
+
+## Function `vote_transactions`
+
+Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+
+
+public entry fun vote_transactions(owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool)
+
+
+
+
+
+Implementation
+
+
+public entry fun vote_transactions(
+ owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ let sequence_number = starting_sequence_number;
+ while(sequence_number <= final_sequence_number) {
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+ sequence_number = sequence_number + 1;
+ }
+}
+
+
+
+
@@ -2322,6 +2446,40 @@ Remove the next transaction if it has sufficient owner rejections.
+
+
+
+
+## Function `execute_rejected_transactions`
+
+Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+
+
+public entry fun execute_rejected_transactions(owner: &signer, multisig_account: address, final_sequence_number: u64)
+
+
+
+
+
+Implementation
+
+
+public entry fun execute_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+ final_sequence_number: u64,
+) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ assert!(last_resolved_sequence_number(multisig_account) < final_sequence_number, error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ assert!(final_sequence_number < next_sequence_number(multisig_account), error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ while(last_resolved_sequence_number(multisig_account) < final_sequence_number) {
+ execute_rejected_transaction(owner, multisig_account);
+ }
+}
+
+
+
+
@@ -2501,6 +2659,14 @@ This function is private so no other code can call this beside the VM itself as
fun add_transaction(creator: address, multisig_account: &mut MultisigAccount, transaction: MultisigTransaction) {
+ if(features::multisig_v2_enhancement_feature_enabled()) {
+ let num_pending_transactions = multisig_account.next_sequence_number - (multisig_account.last_executed_sequence_number + 1);
+ assert!(
+ num_pending_transactions < MAX_PENDING_TRANSACTIONS,
+ error::invalid_state(EMAX_PENDING_TRANSACTIONS_EXCEEDED)
+ );
+ };
+
// The transaction creator also automatically votes for the transaction.
simple_map::add(&mut transaction.votes, creator, true);
diff --git a/aptos-move/framework/aptos-framework/sources/multisig_account.move b/aptos-move/framework/aptos-framework/sources/multisig_account.move
index ed657770abb619..7289fccc3ffc9a 100644
--- a/aptos-move/framework/aptos-framework/sources/multisig_account.move
+++ b/aptos-move/framework/aptos-framework/sources/multisig_account.move
@@ -93,9 +93,16 @@ 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;
+ /// The number of pending transactions has exceeded the maximum allowed.
+ const EMAX_PENDING_TRANSACTIONS_EXCEEDED: u64 = 19;
+ /// The multisig v2 enhancement feature is not enabled.
+ const EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED: u64 = 20;
+
const ZERO_AUTH_KEY: vector = x"0000000000000000000000000000000000000000000000000000000000000000";
+ const MAX_PENDING_TRANSACTIONS: u64 = 20;
+
/// 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).
struct MultisigAccount has key {
@@ -384,6 +391,17 @@ module aptos_framework::multisig_account {
(voted, vote)
}
+ #[view]
+ public fun available_transaction_queue_capacity(multisig_account: address): u64 acquires MultisigAccount {
+ let multisig_account_resource = borrow_global_mut(multisig_account);
+ let num_pending_transactions = multisig_account_resource.next_sequence_number - multisig_account_resource.last_executed_sequence_number - 1;
+ if (num_pending_transactions > MAX_PENDING_TRANSACTIONS) {
+ 0
+ } else {
+ MAX_PENDING_TRANSACTIONS - num_pending_transactions
+ }
+ }
+
////////////////////////// Multisig account creation functions ///////////////////////////////
/// Creates a new multisig account on top of an existing account.
@@ -832,6 +850,8 @@ module aptos_framework::multisig_account {
}
/// Generic function that can be used to either approve or reject a multisig transaction
+ /// Retained for backward compatibility: the function with the typographical error in its name
+ /// will continue to be an accessible entry point.
public entry fun vote_transanction(
owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
assert_multisig_account_exists(multisig_account);
@@ -862,6 +882,24 @@ module aptos_framework::multisig_account {
);
}
+ /// Generic function that can be used to either approve or reject a multisig transaction
+ public entry fun vote_transaction(
+ owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+ }
+
+ /// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+ public entry fun vote_transactions(
+ owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ let sequence_number = starting_sequence_number;
+ while(sequence_number <= final_sequence_number) {
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+ sequence_number = sequence_number + 1;
+ }
+ }
+
/// Remove the next transaction if it has sufficient owner rejections.
public entry fun execute_rejected_transaction(
owner: &signer,
@@ -891,6 +929,20 @@ module aptos_framework::multisig_account {
);
}
+ /// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+ public entry fun execute_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+ final_sequence_number: u64,
+ ) acquires MultisigAccount {
+ assert!(features::multisig_v2_enhancement_feature_enabled(), error::invalid_state(EMULTISIG_V2_ENHANCEMENT_NOT_ENABLED));
+ assert!(last_resolved_sequence_number(multisig_account) < final_sequence_number, error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ assert!(final_sequence_number < next_sequence_number(multisig_account), error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ while(last_resolved_sequence_number(multisig_account) < final_sequence_number) {
+ execute_rejected_transaction(owner, multisig_account);
+ }
+ }
+
////////////////////////// To be called by VM only ///////////////////////////////
/// Called by the VM as part of transaction prologue, which is invoked during mempool transaction validation and as
@@ -978,6 +1030,14 @@ module aptos_framework::multisig_account {
}
fun add_transaction(creator: address, multisig_account: &mut MultisigAccount, transaction: MultisigTransaction) {
+ if(features::multisig_v2_enhancement_feature_enabled()) {
+ let num_pending_transactions = multisig_account.next_sequence_number - (multisig_account.last_executed_sequence_number + 1);
+ assert!(
+ num_pending_transactions < MAX_PENDING_TRANSACTIONS,
+ error::invalid_state(EMAX_PENDING_TRANSACTIONS_EXCEEDED)
+ );
+ };
+
// The transaction creator also automatically votes for the transaction.
simple_map::add(&mut transaction.votes, creator, true);
@@ -1170,7 +1230,7 @@ module aptos_framework::multisig_account {
fun setup() {
let framework_signer = &create_signer(@0x1);
features::change_feature_flags(
- framework_signer, vector[features::get_multisig_accounts_feature()], vector[]);
+ framework_signer, vector[features::get_multisig_accounts_feature(), features::get_multisig_v2_enhancement_feature()], vector[]);
timestamp::set_time_has_started_for_testing(framework_signer);
chain_id::initialize_for_test(framework_signer, 1);
}
@@ -1888,4 +1948,100 @@ module aptos_framework::multisig_account {
option::none()
);
}
+
+ #[test(owner_1 = @0x123, owner_2 = @0x124, owner_3 = @0x125)]
+ #[expected_failure(abort_code = 196627, location = Self)]
+ fun test_max_pending_transaction_limit_should_fail(
+ owner_1: &signer,
+ owner_2: &signer,
+ owner_3: &signer
+ ) acquires MultisigAccount {
+ setup();
+ let owner_1_addr = address_of(owner_1);
+ let owner_2_addr = address_of(owner_2);
+ let owner_3_addr = address_of(owner_3);
+ create_account(owner_1_addr);
+ let multisig_address = get_next_multisig_account_address(owner_1_addr);
+ create_with_owners(
+ owner_1,
+ vector[owner_2_addr, owner_3_addr],
+ 2,
+ vector[],
+ vector[]
+ );
+
+ let remaining_iterations = MAX_PENDING_TRANSACTIONS + 1;
+ while (remaining_iterations > 0) {
+ create_transaction(owner_1, multisig_address, PAYLOAD);
+ remaining_iterations = remaining_iterations - 1;
+ }
+ }
+
+ #[test_only]
+ fun create_transaction_with_eviction(
+ owner: &signer,
+ multisig_account: address,
+ payload: vector,
+ ) acquires MultisigAccount {
+ while(available_transaction_queue_capacity(multisig_account) == 0) {
+ execute_rejected_transaction(owner, multisig_account)
+ };
+ create_transaction(owner, multisig_account, payload);
+ }
+
+ #[test_only]
+ fun vote_all_transactions(
+ owner: &signer, multisig_account: address, approved: bool) acquires MultisigAccount {
+ let starting_sequence_number = last_resolved_sequence_number(multisig_account) + 1;
+ let final_sequence_number = next_sequence_number(multisig_account) - 1;
+ vote_transactions(owner, multisig_account, starting_sequence_number, final_sequence_number, approved);
+ }
+
+ #[test(owner_1 = @0x123, owner_2 = @0x124, owner_3 = @0x125)]
+ fun test_dos_mitigation_end_to_end(
+ owner_1: &signer,
+ owner_2: &signer,
+ owner_3: &signer
+ ) acquires MultisigAccount {
+ setup();
+ let owner_1_addr = address_of(owner_1);
+ let owner_2_addr = address_of(owner_2);
+ let owner_3_addr = address_of(owner_3);
+ create_account(owner_1_addr);
+ let multisig_address = get_next_multisig_account_address(owner_1_addr);
+ create_with_owners(
+ owner_1,
+ vector[owner_2_addr, owner_3_addr],
+ 2,
+ vector[],
+ vector[]
+ );
+
+ // owner_3 is compromised and creates a bunch of bogus transactions.
+ let remaining_iterations = MAX_PENDING_TRANSACTIONS;
+ while (remaining_iterations > 0) {
+ create_transaction(owner_3, multisig_address, PAYLOAD);
+ remaining_iterations = remaining_iterations - 1;
+ };
+
+ // No one can create a transaction anymore because the transaction queue is full.
+ assert!(available_transaction_queue_capacity(multisig_address) == 0, 0);
+
+ // owner_1 and owner_2 vote "no" on all transactions.
+ vote_all_transactions(owner_1, multisig_address, false);
+ vote_all_transactions(owner_2, multisig_address, false);
+
+ // owner_1 evicts a transaction and creates a transaction to remove the compromised owner.
+ // Note that `PAYLOAD` is a placeholder and is not actually executed in this unit test.
+ create_transaction_with_eviction(owner_1, multisig_address, PAYLOAD);
+
+ // owner_2 approves the eviction transaction.
+ approve_transaction(owner_2, multisig_address, 11);
+
+ // owner_1 flushes the transaction queue except for the eviction transaction.
+ execute_rejected_transactions(owner_1, multisig_address, 10);
+
+ // execute the eviction transaction to remove the compromised owner.
+ assert!(can_be_executed(multisig_address, 11), 0);
+ }
}
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 c55f002469df48..21296b2c03c383 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
@@ -503,6 +503,12 @@ pub enum EntryFunctionCall {
multisig_account: AccountAddress,
},
+ /// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+ MultisigAccountExecuteRejectedTransactions {
+ multisig_account: AccountAddress,
+ final_sequence_number: u64,
+ },
+
/// Reject a multisig transaction.
MultisigAccountRejectTransaction {
multisig_account: AccountAddress,
@@ -567,6 +573,23 @@ pub enum EntryFunctionCall {
},
/// Generic function that can be used to either approve or reject a multisig transaction
+ MultisigAccountVoteTransaction {
+ multisig_account: AccountAddress,
+ sequence_number: u64,
+ approved: bool,
+ },
+
+ /// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+ MultisigAccountVoteTransactions {
+ multisig_account: AccountAddress,
+ starting_sequence_number: u64,
+ final_sequence_number: u64,
+ approved: bool,
+ },
+
+ /// Generic function that can be used to either approve or reject a multisig transaction
+ /// Retained for backward compatibility: the function with the typographical error in its name
+ /// will continue to be an accessible entry point.
MultisigAccountVoteTransanction {
multisig_account: AccountAddress,
sequence_number: u64,
@@ -1220,6 +1243,13 @@ impl EntryFunctionCall {
MultisigAccountExecuteRejectedTransaction { multisig_account } => {
multisig_account_execute_rejected_transaction(multisig_account)
},
+ MultisigAccountExecuteRejectedTransactions {
+ multisig_account,
+ final_sequence_number,
+ } => multisig_account_execute_rejected_transactions(
+ multisig_account,
+ final_sequence_number,
+ ),
MultisigAccountRejectTransaction {
multisig_account,
sequence_number,
@@ -1253,6 +1283,22 @@ impl EntryFunctionCall {
MultisigAccountUpdateSignaturesRequired {
new_num_signatures_required,
} => multisig_account_update_signatures_required(new_num_signatures_required),
+ MultisigAccountVoteTransaction {
+ multisig_account,
+ sequence_number,
+ approved,
+ } => multisig_account_vote_transaction(multisig_account, sequence_number, approved),
+ MultisigAccountVoteTransactions {
+ multisig_account,
+ starting_sequence_number,
+ final_sequence_number,
+ approved,
+ } => multisig_account_vote_transactions(
+ multisig_account,
+ starting_sequence_number,
+ final_sequence_number,
+ approved,
+ ),
MultisigAccountVoteTransanction {
multisig_account,
sequence_number,
@@ -2766,6 +2812,28 @@ pub fn multisig_account_execute_rejected_transaction(
))
}
+/// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+pub fn multisig_account_execute_rejected_transactions(
+ multisig_account: AccountAddress,
+ final_sequence_number: u64,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("execute_rejected_transactions").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&final_sequence_number).unwrap(),
+ ],
+ ))
+}
+
/// Reject a multisig transaction.
pub fn multisig_account_reject_transaction(
multisig_account: AccountAddress,
@@ -2946,6 +3014,58 @@ pub fn multisig_account_update_signatures_required(
}
/// Generic function that can be used to either approve or reject a multisig transaction
+pub fn multisig_account_vote_transaction(
+ multisig_account: AccountAddress,
+ sequence_number: u64,
+ approved: bool,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("vote_transaction").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&sequence_number).unwrap(),
+ bcs::to_bytes(&approved).unwrap(),
+ ],
+ ))
+}
+
+/// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+pub fn multisig_account_vote_transactions(
+ multisig_account: AccountAddress,
+ starting_sequence_number: u64,
+ final_sequence_number: u64,
+ approved: bool,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("vote_transactions").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&starting_sequence_number).unwrap(),
+ bcs::to_bytes(&final_sequence_number).unwrap(),
+ bcs::to_bytes(&approved).unwrap(),
+ ],
+ ))
+}
+
+/// Generic function that can be used to either approve or reject a multisig transaction
+/// Retained for backward compatibility: the function with the typographical error in its name
+/// will continue to be an accessible entry point.
pub fn multisig_account_vote_transanction(
multisig_account: AccountAddress,
sequence_number: u64,
@@ -4836,6 +4956,21 @@ mod decoder {
}
}
+ pub fn multisig_account_execute_rejected_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(
+ EntryFunctionCall::MultisigAccountExecuteRejectedTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ final_sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ },
+ )
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_reject_transaction(
payload: &TransactionPayload,
) -> Option {
@@ -4936,6 +5071,35 @@ mod decoder {
}
}
+ pub fn multisig_account_vote_transaction(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(EntryFunctionCall::MultisigAccountVoteTransaction {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ approved: bcs::from_bytes(script.args().get(2)?).ok()?,
+ })
+ } else {
+ None
+ }
+ }
+
+ pub fn multisig_account_vote_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(EntryFunctionCall::MultisigAccountVoteTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ starting_sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ final_sequence_number: bcs::from_bytes(script.args().get(2)?).ok()?,
+ approved: bcs::from_bytes(script.args().get(3)?).ok()?,
+ })
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_vote_transanction(
payload: &TransactionPayload,
) -> Option {
@@ -5861,6 +6025,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy
+
+Whether the Multisig V2 enhancement feature is enabled.
+
+Lifetime: transient
+
+
+const MULTISIG_V2_ENHANCEMENT: u64 = 55;
+
+
+
+
Whether the new aptos_stdlib::multi_ed25519::public_key_validate_internal_v2()
native is enabled.
@@ -2356,6 +2370,52 @@ Lifetime: transient
+
+
+
+
+## Function `get_multisig_v2_enhancement_feature`
+
+
+
+public fun get_multisig_v2_enhancement_feature(): u64
+
+
+
+
+
+Implementation
+
+
+public fun get_multisig_v2_enhancement_feature(): u64 { MULTISIG_V2_ENHANCEMENT }
+
+
+
+
+
+
+
+
+## Function `multisig_v2_enhancement_feature_enabled`
+
+
+
+public fun multisig_v2_enhancement_feature_enabled(): bool
+
+
+
+
+
+Implementation
+
+
+public fun multisig_v2_enhancement_feature_enabled(): bool acquires Features {
+ is_enabled(MULTISIG_V2_ENHANCEMENT)
+}
+
+
+
+
diff --git a/aptos-move/framework/move-stdlib/sources/configs/features.move b/aptos-move/framework/move-stdlib/sources/configs/features.move
index dacd668ca3342d..a23db2d95dbe41 100644
--- a/aptos-move/framework/move-stdlib/sources/configs/features.move
+++ b/aptos-move/framework/move-stdlib/sources/configs/features.move
@@ -428,6 +428,17 @@ module std::features {
is_enabled(KEYLESS_ACCOUNTS_WITH_PASSKEYS)
}
+ /// Whether the Multisig V2 enhancement feature is enabled.
+ ///
+ /// Lifetime: transient
+ const MULTISIG_V2_ENHANCEMENT: u64 = 55;
+
+ public fun get_multisig_v2_enhancement_feature(): u64 { MULTISIG_V2_ENHANCEMENT }
+
+ public fun multisig_v2_enhancement_feature_enabled(): bool acquires Features {
+ is_enabled(MULTISIG_V2_ENHANCEMENT)
+ }
+
// ============================================================================================
// Feature Flag Implementation