-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[move][stdlib] Implement mem::swap native move call #14786
base: main
Are you sure you want to change the base?
Conversation
⏱️ 1h 45m total CI duration on this PR
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #14786 +/- ##
========================================
Coverage 60.1% 60.1%
========================================
Files 856 857 +1
Lines 211110 211341 +231
========================================
+ Hits 126962 127147 +185
- Misses 84148 84194 +46 ☔ View full report in Codecov by Sentry. |
What's this for? |
This is needed to be able to modify fields, when they hold a non-copyable non-dropable type. For example, you cannot do this today:
https://aptos-org.slack.com/archives/C05V6JEKZQ9/p1727393314700309 |
093a454
to
06d6cb3
Compare
6db4bf8
to
bd33c50
Compare
|
||
let cost = MEM_SWAP_BASE | ||
+ MEM_SWAP_PER_ABS_VAL_UNIT | ||
* (context.abs_val_size(&args[0]) + context.abs_val_size(&args[1])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to use abstract_value_size_dereferenced
as it is the sizes of the referenced values that matter, not the size of the references themselves.
https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/aptos-gas-schedule/src/gas_schedule/misc.rs#L293
You may need to add a helper to expose it through the native context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why dereferenced value? We are not allocating new memory space?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the current implementation effective makes deep clones of the two values being swapped.
Anyway, this discussion may no longer be relevant as I've come up with a more efficient constant-time implementation. Will share the PR very very soon.
783b338
to
3d7877e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I ended up just implementing the more efficient swap as I originally promised:
Please take a careful look and help me check if the implementation is correct. I also just realized that swap
is a really powerful feature, and we must take utmost caution to ensure its implementation is correct, or very bad things could happen. For this reason, I'd like to request at least one additional review from @runtian-zhou @ziaptos or @georgemitenkov + a third party audit.
@@ -0,0 +1,65 @@ | |||
#[test_only] | |||
module std::mem_tests { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests here are inadequate considering how important and dangerous the swap feature is.
We need to have full test coverage for all these swaps:
(container, container)
(indexed ref - generic, indexed ref - generic)
(indexed ref - specialized, indexed ref - specialized)
(indexed ref - generic, indexed ref - specialized)
(indexed ref - specialized, indexed ref - generic)
I would also encourage us add (rust-level) unit tests in move-vm/types/src/values/value_tests
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some tests, but can add more.
can you explain when does a &mut become each of the :
- indexer ref - generic
- indexed ref - specialized
- container
.map(|(s, _)| s) | ||
.partition(|s| s.is_aptos_code()); | ||
|
||
let expected_code = vec![ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these changes related? Not familiar with the code here and don't know what it is trying to achieve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a check that all resources published in genesis are indexed correctly.
here we added mem.move, so it failed. failing on 153>152 is confusing.
so I just pulled the list of resources - and am asserting on them, and failure will tell which resource is different.
cc @areshand
@wrwg I'm also wondering if we should just make this a bytecode instruction. Can probably do it later though. |
efbef47
to
7c0fbc1
Compare
c5f381e
to
8b0d35b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a way to unit-test call_native?
i.e. we are relying for a lot of tests in third_party/move/move-vm/types/src/values/value_tests.rs that are not failing checks internally, to fail checks when native call types get checked.
Otherwise I am not sure how to have that test coverage, as in end-to-end tests - such code would not compile.
8b0d35b
to
170eca3
Compare
170eca3
to
a99eccd
Compare
addressed all comments and added more tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass 1: everything but the values
@@ -0,0 +1,27 @@ | |||
/// Module with methods for safe memory manipulation. | |||
/// I.e. swapping/replacing non-copyable/non-droppable types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I.e.
reads a bit weird, maybe For example
? Also why break into 2 sentences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the example is just redundant, removing. swap below has the docstring as well.
@@ -69,7 +69,7 @@ | |||
/// global operations. | |||
/// - V1 | |||
/// - TBA | |||
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_22; | |||
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_23; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add documentation to new 1.23 in the comment above that new natives have been added
// Copyright © Aptos Foundation | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Copyright (c) The Diem Core Contributors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is wrong, multiple licenses and why we even have Diem here?
|
||
/// The feature is not enabled. | ||
/// (0xD is unavailable) | ||
pub const EFEATURE_NOT_ENABLED: u64 = 0x0D_0001; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document this in Move code? Running into these errors is such a pain because you need to search through aptos-core hoping to get the right constant....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, don't we have errors::unavailable
Rust helper already? I think it exists in aptos-types
though...
@@ -317,6 +320,10 @@ impl Features { | |||
self.is_enabled(FeatureFlag::TRANSACTION_SIMULATION_ENHANCEMENT) | |||
} | |||
|
|||
pub fn is_native_memory_operations_enabled(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: are
instead of is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
everything is using is
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just reads weird to me because it is plural, but maybe a personal taste
fn swap_contents(&self, other: &Self) -> PartialVMResult<()> { | ||
use Container::*; | ||
|
||
// TODO: check if unique ownership? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add a todo, add a github handle or create a ticket to track this so someone else can have more context about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh this was from Victor's change, missed it. removing, and adding comment.
/// Swap contents of two passed mutable references. | ||
public(friend) native fun swap<T>(left: &mut T, right: &mut T); | ||
|
||
/// Replace value reference points to with the given new value, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think articles are missing, but I am not a native speaker 😄
debug_assert!(args.len() == 2); | ||
|
||
if args.len() != 2 { | ||
return Err(SafeNativeError::InvariantViolation(PartialVMError::new( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I missed your previous review, addressing that one and the new one
@@ -0,0 +1,27 @@ | |||
/// Module with methods for safe memory manipulation. | |||
/// I.e. swapping/replacing non-copyable/non-droppable types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the example is just redundant, removing. swap below has the docstring as well.
fn swap_contents(&self, other: &Self) -> PartialVMResult<()> { | ||
use Container::*; | ||
|
||
// TODO: check if unique ownership? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh this was from Victor's change, missed it. removing, and adding comment.
@@ -317,6 +320,10 @@ impl Features { | |||
self.is_enabled(FeatureFlag::TRANSACTION_SIMULATION_ENHANCEMENT) | |||
} | |||
|
|||
pub fn is_native_memory_operations_enabled(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
everything is using is
?
23c63a3
to
c95646e
Compare
Description
If we have a field that contains non-copyable type, it is impossible to change it, and get the old value back.
Adding two methods:
How Has This Been Tested?
provided unit tests
Type of Change
Which Components or Systems Does This Change Impact?
Checklist