-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[compiler][vm] add visibility modifier to structs/enums: step 5 #18489
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
base: teng/add-wellform-check-for-struct-apis
Are you sure you want to change the base?
[compiler][vm] add visibility modifier to structs/enums: step 5 #18489
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
06824ad to
41b73e1
Compare
0bb5405 to
54ce6ce
Compare
41b73e1 to
2aed166
Compare
54ce6ce to
12ef626
Compare
2aed166 to
760ad44
Compare
12ef626 to
2e6c361
Compare
42360a0 to
ac79908
Compare
2e6c361 to
c253dbf
Compare
9a7e33b to
a6252ac
Compare
c253dbf to
b0f9e72
Compare
a6252ac to
ce33809
Compare
b0f9e72 to
da3998a
Compare
ce33809 to
4b0a271
Compare
da3998a to
35d544b
Compare
dd786c6 to
beb910c
Compare
35d544b to
3442487
Compare
beb910c to
1480203
Compare
3442487 to
4731fc1
Compare
dd7a3d2 to
966306b
Compare
8ac52fd to
ca38b6b
Compare
966306b to
a6c536f
Compare
georgemitenkov
left a comment
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 do we need a tag in WithVariants layout here?
| WithVariants(Vec<MoveVariantLayout>), | ||
| /// Like WithTypes, this carries the type tag for proper type identification. | ||
| WithVariants { | ||
| type_: StructTag, |
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.
Question: why this change? Looking through PR I do not see any usages of type_? But maybe I am missing something
| "Invalid MoveTypeLayout -> StructTag conversion--needed MoveLayoutType::WithTypes or WithVariants" | ||
| ), | ||
| WithTypes { type_, .. } => Ok(type_.clone()), | ||
| WithTypes { type_, .. } | WithVariants { type_, .. } => Ok(type_.clone()), |
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 see, so it is used here. Do we need this for API? Or why exactly?
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.
In build_from_definition, when we instantiate a generic with enum, we need to support MoveStructLayout for enums. If we don't support it here, error will be raised.
| /// Whether this VM should support public copy structs/enums as transaction arguments. | ||
| /// When enabled, non-private structs and enums with the copy ability can be used as | ||
| /// transaction arguments if they have public pack functions with the Pack attribute. | ||
| pub enable_public_struct_args: 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.
I think having feature gate by v10 format lesehwere is better. For Move VM txn args are irrelevant, so I am not sure this is the best place to add this thing. Just gating in txn_arg_validation is good enough?
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.
Added a new feature flag PUBLIC_STRUCT_ENUM_ARGS to control it instead of using V10.
| ), | ||
| LayoutType::WithTypes => { | ||
| let mid = m.self_id(); | ||
| // All type_arguments layouts can now be converted to TypeTags |
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: looks like useless comment?
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.
removed
| match layout_type { | ||
| LayoutType::WithTypes => { | ||
| let mid = m.self_id(); | ||
| // All type_arguments layouts can now be converted to TypeTags |
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: looks like useless comment?
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.
removed
| @@ -263,6 +265,7 @@ impl StructLayoutBuilder { | |||
| match layout_type { | |||
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 factor out his so that:
if m.self_id().is_option() {
build_option_layout_from_definition(...)
} else {
build_any_variant_layout_from_definition(...)
}
| .collect::<anyhow::Result<Vec<_>>>()?; | ||
|
|
||
| Ok(match layout_type { | ||
| LayoutType::Runtime => MoveStructLayout::RuntimeVariants( |
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 we support it here, but not for option? I think we do not need this here and only WithTypes need to be supported, and if so, we can process all layouts here and do not even match on layout type: check it is with_types in advance.
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.
removed
| Vector(ety) => self.is_valid_txn_arg_type(ety), | ||
| Struct(mid, sid, inst) => { | ||
| let qid = mid.qualified(*sid); | ||
| self.is_allowed_input_struct(qid) || self.is_public_copy_struct(qid, inst) |
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 this correct: we would allow Option<NonPublicStruct> here. Don't we need to check inst for Option (because we know it is a field)? How did it work before could you construct None of non-public type, bit not Some(..)?
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.
yes, because we don't know at the compile time whether T will be instantiated with a public or non-public values, which should be checked when running the entry function with full type instantiation.
| public entry fun test_option_point(sender: &signer, opt_point: std::option::Option<Point>) acquires TestResult { | ||
| let result = borrow_global_mut<TestResult>(std::signer::address_of(sender)); | ||
| if (std::option::is_some(&opt_point)) { | ||
| let p = std::option::destroy_some(opt_point); |
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: imports and use . syntax?
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.
done
| ); | ||
| assert_success!(result); | ||
|
|
||
| // Now disable VM_BINARY_FORMAT_V10 feature flag, which disables public struct/enum args |
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.
we should never disable it though, because if you published v10 bytecode it becomes unloadable? is it better to split feature gating and having a dedicated feature for controlling such args? in a sense that this scenario is not really possible? what can happen is v9 bytecode is published and Point is non-public. We fail to run functions/ Then we enable v10. Running this entry starts to work?
|
|
||
| // Verify view function returns deserialization error as ErrorMessage | ||
| // (not MoveAbort, since CODE_DESERIALIZATION_ERROR is not an abort) | ||
| match view_result.values { |
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.
Do we need a match? can unwrap in tests
| } | ||
|
|
||
| #[view] | ||
| public fun view_point(p: Point): u64 { |
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.
Q: can we return Point struct from this view function, does it work even for non-public structs?
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.
currently view functions can already return a struct value
ca38b6b to
52495b2
Compare
a6c536f to
810bb48
Compare
52495b2 to
c07e5bd
Compare
fb023bd to
f37f6c7
Compare
c07e5bd to
14fb59d
Compare
aptos-move/e2e-move-tests/src/tests/public_structs_enums_upgrade.rs
Outdated
Show resolved
Hide resolved
a354293 to
8fd8607
Compare
14fb59d to
65a945b
Compare
8fd8607 to
05382f0
Compare
65a945b to
24787e4
Compare
05382f0 to
12b69f7
Compare
24787e4 to
8720ccb
Compare
12b69f7 to
82b0fc2
Compare
| assert!( | ||
| !found_test_txn, | ||
| "Transaction with test_generic_container should NOT exist in ledger - validation should have rejected 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.
Negative test incorrectly assumes transaction is discarded
Medium Severity
The test test_generic_container_with_non_public_type_arg_rejected calls api_execute_txn (which expects HTTP 202 and commits the transaction) then asserts the transaction is NOT in the ledger. However, INVALID_MAIN_FUNCTION_SIGNATURE (status code 1011) falls in the Verification status range, so keep_or_discard returns Ok(KeptVMStatus::MiscellaneousError) — the transaction is kept, not discarded. The transaction will be committed to the ledger with a failed execution status, and the assertion !found_test_txn will fail because the transaction IS present.
| assert!( | ||
| !found_test_txn, | ||
| "Transaction with test_generic_container should NOT exist in ledger - validation should have rejected 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.
Negative test incorrectly assumes transaction is discarded
Medium Severity
The test test_generic_container_with_non_public_type_arg_rejected calls api_execute_txn (which expects HTTP 202 and commits the transaction) then asserts the transaction is NOT in the ledger. However, INVALID_MAIN_FUNCTION_SIGNATURE (status code 1011) falls in the Verification status range, so keep_or_discard returns Ok(KeptVMStatus::MiscellaneousError) — the transaction is kept, not discarded. The transaction will be committed to the ledger with a failed execution status, and the assertion !found_test_txn will fail because the transaction IS present.
8720ccb to
c815eba
Compare
82b0fc2 to
428e114
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.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| variant_index as u16, | ||
| field_results, | ||
| ), | ||
| )); |
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.
Variant index cast to u16 without bounds check
Low Severity
In try_into_vm_value_struct, variant_index from enumerate() (a usize) is cast to u16 with variant_index as u16 without a bounds check. If an enum has more than 65535 variants, this would silently truncate. The VM-side construct_public_copy_struct correctly validates variant_idx_usize > u16::MAX, but this JSON conversion path does not.
c815eba to
fc5f574
Compare
428e114 to
4f22f38
Compare



Description
This PR
a. public (have public pack functions with #[struct_api(pack)] attribute)
b. have the copy ability
c. all field types are also valid transaction arguments
PUBLIC_STRUCT_ENUM_ARGSfor enabling public structs/enums as txn args.Note that this PR does not contain code to support public structs in CLI because it requires encoding the type information.
How Has This Been Tested?
new e2e move tests and api tests.
Key Areas to Review
Whether the validation logic is correct and complete;
Whether the test cases give good coverage;
Whether this change may cause any compatibility issues.
Type of Change
Which Components or Systems Does This Change Impact?
Checklist
Note
High Risk
Touches VM transaction argument validation/construction, type/layout plumbing, and on-chain feature gating, which can affect transaction acceptance and execution semantics. Although gated by
PUBLIC_STRUCT_ENUM_ARGS, the change is broad and adds new parsing/construction paths (including generics and enums).Overview
Enable public
copystructs/enums as txn args behindPUBLIC_STRUCT_ENUM_ARGS. The VM verifier now treats non-whitelisted structs/enums as valid transaction arguments when they havecopyability, are public via generatedpack$...functions, and all (recursively substituted) field types are themselves valid; it also constructs these arguments by invoking the appropriate cached pack function (including enum variants).Expand API and tooling to encode/validate these types. The API JSON-to-BCS converter gains enum JSON parsing and generic parameter substitution for entry functions before argument conversion; Move layout/types plumbing is updated to carry type tags for
WithVariantsand to emitWithVariantslayouts for general enums (while preserving Option’s legacy representation). A new on-chain feature flag is wired through release/prod configs, compiler extended checks are updated to match VM rules, and new API + e2e tests cover positive flows, malformed JSON/variants, generic/private type rejection, and feature-disable/enable behavior.Written by Cursor Bugbot for commit 4f22f38. This will update automatically on new commits. Configure here.