Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aptos-move/aptos-vm/src/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ pub(crate) fn check_gas_for_parameters(
speculative_warn!(
log_context,
format!(
"[VM] Gas unit error; min {}, submitted {}",
"[VM] Gas unit error; max {}, submitted {}",
txn_gas_params.max_price_per_gas_unit,
txn_gas_metadata.gas_unit_price
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub const EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED: u64 = 0x03_0009;

/// The maximum length of the input string for derived string snapshot.
/// If we want to increase this, we need to modify BITS_FOR_SIZE in types/src/delayed_fields.rs.
pub const DERIVED_STRING_INPUT_MAX_LENGTH: usize = 1024;
pub const DERIVED_STRING_INPUT_MAX_LENGTH: usize = 256;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change to the execution semantics and needs to be feature gated. It would be simpler to update the docs to reflect the actual 1024 byte limit, if that is the only place that it is referenced. This will also avoid breaking any objects that have already been created in mainnet with a length between 256 and 1024 bytes. We need to check if any other code expects 256 bytes before doing this.


fn get_width_by_type(ty_arg: &Type, error_code_if_incorrect: u64) -> SafeNativeResult<u32> {
match ty_arg {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,21 +822,23 @@ module supra_framework::automation_registry {

/// Retrieves specific metadata details of an automation task entry by its task index.
///
/// 1. `address` - The owner of the task.
/// 2. `vector<u8>` - The payload transaction (encoded).
/// 3. `u64` - The expiry time of the task (timestamp).
/// 4. `vector<u8>` - The hash of the transaction.
/// 5. `u64` - The maximum gas amount allowed for the task.
/// 6. `u64` - The gas price cap for executing the task.
/// 7. `u64` - The automation fee cap for the current epoch.
/// 8. `vector<vector<u8>>` - Auxiliary data related to the task (can be multiple items).
/// 9. `u64` - The time at which the task was registered (timestamp).
/// 10. `u8` - The state of the task (e.g., active, cancelled, completed).
/// 11. `u64` - The locked fee reserved for the next epoch execution.
/// 1. `u64` - The task index.
/// 2. `address` - The owner of the task.
/// 3. `vector<u8>` - The payload transaction (encoded).
/// 4. `u64` - The expiry time of the task (timestamp).
/// 5. `vector<u8>` - The hash of the transaction.
/// 6. `u64` - The maximum gas amount allowed for the task.
/// 7. `u64` - The gas price cap for executing the task.
/// 8. `u64` - The automation fee cap for the current epoch.
/// 9. `vector<vector<u8>>` - Auxiliary data related to the task (can be multiple items).
/// 10. `u64` - The time at which the task was registered (timestamp).
/// 11. `u8` - The state of the task (e.g., active, cancelled, completed).
/// 12. `u64` - The locked fee reserved for the next epoch execution.
public fun deconstruct_task_metadata(
task_metadata: &AutomationTaskMetaData
): (address, vector<u8>, u64, vector<u8>, u64, u64, u64, vector<vector<u8>>, u64, u8, u64) {
): (u64, address, vector<u8>, u64, vector<u8>, u64, u64, u64, vector<vector<u8>>, u64, u8, u64) {
(
task_metadata.task_index,
task_metadata.owner,
task_metadata.payload_tx,
task_metadata.expiry_time,
Expand Down
159 changes: 156 additions & 3 deletions aptos-move/framework/supra-framework/sources/committee_map.move
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,22 @@ module supra_framework::committee_map {
// f+1, number of nodes in a family committee should be greater than 1
assert!(num_of_nodes > 1, INVALID_NODE_NUMBERS);
} else if (committee_type == CLAN) {
// 2f+1, number of nodes in a clan committee should be odd and greater than 3
// 2f+1, number of nodes in a clan committee should be odd and greater than or equal to 3
assert!(num_of_nodes >= 3 && num_of_nodes % 2 == 1, INVALID_NODE_NUMBERS);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INVALID_NODE_NUMBERS sound to generic, maybe error per committee type will be more descriptive. e.g. INVALID_CLAN_NODE_NUMBERS, INVALID_FAMILIY_NODE_NUMBERS

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ECLAN_TOO_SMALL_OR_EVEN_NODES_IN_CLAN

} else {
// 3f+1, number of nodes in a tribe committee should be in the format of 3f+1 and greater than 4
// 3f+1, number of nodes in a tribe committee should be in the format of 3f+1 and greater than or equal to 4
assert!(num_of_nodes >= 4 && (num_of_nodes - 1) % 3 == 0, INVALID_NODE_NUMBERS);
};
committee_type
}

/// Ensures removing exactly one member keeps the committee type invariants valid.
fun validate_committee_type_after_member_removal(committee: &CommitteeInfo) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion for the function name: assert_removal_allowed

let current_num_of_nodes = simple_map::length(&committee.map);
assert!(current_num_of_nodes > 0, INVALID_NODE_NUMBERS);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here INVALID_NODE_NUMBERS ZERO_COMMITTE_NODE_NUMBER

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EZERO_NUM_NODE let us use E prefix for error codes.

validate_committee_type(committee.committee_type, current_num_of_nodes - 1);
}

#[view]
/// Get the committee's node vector and committee type
public fun get_committee_info(com_store_addr: address, id: u64): (vector<NodeData>, u8) acquires CommitteeInfoStore {
Expand Down Expand Up @@ -545,6 +552,27 @@ module supra_framework::committee_map {
let _acquire = &capability::acquire(owner_signer, &OwnerCap {});

let committee_store = borrow_global_mut<CommitteeInfoStore>(com_store_addr);
let event_handler = borrow_global_mut<SupraCommitteeEventHandler>(get_committeeInfo_address(owner_signer));

// If the node is already associated with another committee, remove the stale entry first.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how we're using this module, but I know that when we eventually add support for Clans and Families (sub-committees) to the L1 that a node will be able to participate in multiple committees simultaneously. If we intend to use this module for that use case then node_to_committee_map should be updated from SimpleMap<address, u64> to SimpleMap<address, vector<u64>> instead. Perhaps Dr @sjoshisupra knows more about the intended purpose of this module.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, this is used for DORA nodes for discovery @dhaval-supraoracles , but yes, we can modify it to make it more general.

if (simple_map::contains_key(&committee_store.node_to_committee_map, &node_address)) {
let old_committee_id = *simple_map::borrow(&committee_store.node_to_committee_map, &node_address);
if (old_committee_id != id && simple_map::contains_key(&committee_store.committee_map, &old_committee_id)) {
let old_committee = simple_map::borrow_mut(&mut committee_store.committee_map, &old_committee_id);
if (does_node_exist(old_committee, node_address)) {
validate_committee_type_after_member_removal(old_committee);
let (_, old_node_info) = simple_map::remove(&mut old_committee.map, &node_address);
emit_event(
&mut event_handler.remove_committee_member,
RemoveCommitteeMemberEvent {
committee_id: old_committee_id,
committee_member: old_node_info
}
)
}
}
};

let committee = simple_map::borrow_mut(&mut committee_store.committee_map, &id);
let node_info = NodeInfo {
ip_public_address: copy ip_public_address,
Expand All @@ -554,7 +582,6 @@ module supra_framework::committee_map {
network_port: network_port,
rpc_port: rpc_port,
};
let event_handler = borrow_global_mut<SupraCommitteeEventHandler>(get_committeeInfo_address(owner_signer));
if (!does_node_exist(committee, node_address)) {
emit_event(
&mut event_handler.add_committee_member,
Expand Down Expand Up @@ -655,6 +682,7 @@ module supra_framework::committee_map {
let committee_store = borrow_global_mut<CommitteeInfoStore>(com_store_addr);
let committee = simple_map::borrow_mut(&mut committee_store.committee_map, &id);
ensure_node_address_exist(committee, node_address);
validate_committee_type_after_member_removal(committee);
let (_, node_info) = simple_map::remove(&mut committee.map, &node_address);
let event_handler = borrow_global_mut<SupraCommitteeEventHandler>(get_committeeInfo_address(owner_signer));
emit_event(
Expand Down Expand Up @@ -880,6 +908,131 @@ module supra_framework::committee_map {
);
}

#[test(owner_signer = @0xCEFEF)]
#[expected_failure(abort_code = INVALID_NODE_NUMBERS, location = Self)]
public entry fun test_remove_committee_member_type_validation(
owner_signer: &signer
) acquires CommitteeInfoStore, SupraCommitteeEventHandler {
set_up_test(owner_signer);
let resource_address = account::create_resource_address(&@0xCEFEF, SEED_COMMITTEE);
upsert_committee(
owner_signer,
resource_address,
1,
vector[@0x1, @0x2],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[123, 124],
vector[123, 124],
FAMILY
);
remove_committee_member(owner_signer, resource_address, 1, @0x1);
}

#[test(owner_signer = @0xCEFEF)]
#[expected_failure(abort_code = INVALID_NODE_NUMBERS, location = Self)]
public entry fun test_transfer_member_type_validation(
owner_signer: &signer
) acquires CommitteeInfoStore, SupraCommitteeEventHandler {
set_up_test(owner_signer);
let resource_address = account::create_resource_address(&@0xCEFEF, SEED_COMMITTEE);
upsert_committee(
owner_signer,
resource_address,
1,
vector[@0x1, @0x2],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[vector[123], vector[124]],
vector[123, 124],
vector[123, 124],
FAMILY
);
upsert_committee(
owner_signer,
resource_address,
2,
vector[@0x3, @0x4],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[125, 126],
vector[125, 126],
FAMILY
);
upsert_committee_member(
owner_signer,
resource_address,
2,
@0x1,
vector[127],
vector[127],
vector[127],
vector[127],
127,
127
);
}

#[test(owner_signer = @0xCEFEF)]
public entry fun test_transfer_committee_member_removes_old_committee_entry(
owner_signer: &signer
) acquires CommitteeInfoStore, SupraCommitteeEventHandler {
set_up_test(owner_signer);
let resource_address = account::create_resource_address(&@0xCEFEF, SEED_COMMITTEE);
upsert_committee(
owner_signer,
resource_address,
1,
vector[@0x1, @0x2, @0x5],
vector[vector[123], vector[124], vector[125]],
vector[vector[123], vector[124], vector[125]],
vector[vector[123], vector[124], vector[125]],
vector[vector[123], vector[124], vector[125]],
vector[123, 124, 125],
vector[123, 124, 125],
1
);
upsert_committee(
owner_signer,
resource_address,
2,
vector[@0x3, @0x4],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[vector[125], vector[126]],
vector[125, 126],
vector[125, 126],
1
);
upsert_committee_member(
owner_signer,
resource_address,
2,
@0x1,
vector[127],
vector[127],
vector[127],
vector[127],
127,
127
);

let (exists_in_old_committee, _) = find_node_in_committee(resource_address, 1, @0x1);
assert!(!exists_in_old_committee, 1);
assert!(get_committee_id(resource_address, @0x1) == 2, 2);
let (exists_in_new_committee, _) = find_node_in_committee(resource_address, 2, @0x1);
assert!(exists_in_new_committee, 3);

// Should succeed because node_to_committee_map and committee maps stay in sync.
remove_committee(owner_signer, resource_address, 1);
}

#[test(owner_signer = @0xCEFEF)]
public entry fun test_update_dkg_flag(
owner_signer: &signer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module supra_framework::config_buffer {
/// Should only be used at the end of a reconfiguration.
///
/// Typically used in `X::on_new_epoch()` where X is an on-chaon config.
public fun extract<T: store>(): T acquires PendingConfigs {
public(friend) fun extract<T: store>(): T acquires PendingConfigs {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this we can't upgrade I think.
we need to have another interface for the same.

which is included in fwk upgrade PR LINK

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhaval-supraoracles , has the necessary changes been done to the calling code? Are they now calling extract_v2? I do not see that in the PR link you mentioned above.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes all relevant module also uses extract_v2 on the link that I share.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All modules that call this function need to be marked as friends of this one. Have they already been marked as such? Also, I'm not sure if this might prevent us from using this function in scripts.

I think a simpler change would be to add system_addresses::assert_supra_framework(framework); as the first line of the function instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, since the method does not take a signer it is not possible to do assert_supra_framework, we have to make changes to all the callers.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhaval-supraoracles Can we make this change in the upcoming FWK upgrade?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already updated on FWK upgrade changes. @supra-yoga

let configs = borrow_global_mut<PendingConfigs>(@supra_framework);
let key = type_info::type_name<T>();
let (_, value_packed) = simple_map::remove(&mut configs.configs, &key);
Expand Down
22 changes: 22 additions & 0 deletions aptos-move/framework/supra-framework/sources/fungible_asset.move
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,28 @@ module supra_framework::fungible_asset {
let metadata_address = object::object_address(&metadata_ref.metadata);
let mutable_metadata = borrow_global_mut<Metadata>(metadata_address);

// Validate all provided values before mutating any metadata fields.
if (option::is_some(&name)){
let new_name = option::borrow(&name);
assert!(string::length(new_name) <= MAX_NAME_LENGTH, error::out_of_range(ENAME_TOO_LONG));
};
if (option::is_some(&symbol)){
let new_symbol = option::borrow(&symbol);
assert!(string::length(new_symbol) <= MAX_SYMBOL_LENGTH, error::out_of_range(ESYMBOL_TOO_LONG));
};
if (option::is_some(&decimals)){
let new_decimals = option::borrow(&decimals);
assert!(*new_decimals <= MAX_DECIMALS, error::out_of_range(EDECIMALS_TOO_LARGE));
};
if (option::is_some(&icon_uri)){
let new_icon_uri = option::borrow(&icon_uri);
assert!(string::length(new_icon_uri) <= MAX_URI_LENGTH, error::out_of_range(EURI_TOO_LONG));
};
if (option::is_some(&project_uri)){
let new_project_uri = option::borrow(&project_uri);
assert!(string::length(new_project_uri) <= MAX_URI_LENGTH, error::out_of_range(EURI_TOO_LONG));
};

Comment on lines +897 to +918

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than 2 conditional statement for a same cause

Can we just update the values after the assert

For example

if (option::is_some(&name)){ let new_name = option::extract(&mut name); assert!(string::length(new_name) <= MAX_NAME_LENGTH, error::out_of_range(ENAME_TOO_LONG)); mutable_metadata.name = new_name; };

Can we use destroy_some instead of extract Dr @sjoshisupra ?

Copy link
Author

@supra-yoga supra-yoga Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, if the 2nd or 3rd assertion fails, the 1st metadata would've been set incorrectly already. Therefore, checking for all the metadata limits before assigning them might be better, no?

I am not sure how the Move compiler will process this flow.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@supra-yoga if one of the asserts gets failed then the txn will get failed so there wont be any partial state update

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anshuman-supraoracles , @supra-yoga ,

Yes we can use destroy_some instead of extract but we need to know that the caller has no use of the same after the call. extract will make it option::none, whereas destroy_some would destroy the option altogether.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, no need to perform option::is_some check twice for every field. If anything fails, the whole tx reverts anyway.

Copy link
Author

@supra-yoga supra-yoga Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Will update it as per the suggestion and also use option::destroy_some. Thanks @anshuman-supraoracles.

if (option::is_some(&name)){
mutable_metadata.name = option::extract(&mut name);
};
Expand Down
Loading