Skip to content
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

[Spec] add boogie native for aggregator_v2 #14881

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

rahxephon89
Copy link
Contributor

@rahxephon89 rahxephon89 commented Oct 5, 2024

Description

This PR adds native implementation of aggregator_v2 in the Move prover.

How Has This Been Tested?

  1. Several tests for verification are added to aggregator_v2.move.
  2. A spec is added to collection.move in aptos-token-objects.

Key Areas to Review

  1. Aggregator_v2 can only be instantiated with u64 and u128 so for all other concrete types, all APIs will directly abort.
  2. For generic type #..., only the abort_if conditions are specified, need to make sure it is correct.

Type of Change

  • New feature
  • Bug fix
  • Breaking change
  • Performance improvement
  • Refactoring
  • Dependency update
  • Documentation update
  • Tests

Which Components or Systems Does This Change Impact?

  • Validator Node
  • Full Node (API, Indexer, etc.)
  • Move/Aptos Virtual Machine
  • Aptos Framework
  • Aptos CLI/SDK
  • Developer Infrastructure
  • Move Compiler
  • Other (specify)

Checklist

  • I have read and followed the CONTRIBUTING doc
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I identified and added all stakeholders and component owners affected by this change as reviewers
  • I tested both happy and unhappy path of the functionality
  • I have made corresponding changes to the documentation

Copy link

trunk-io bot commented Oct 5, 2024

⏱️ 2h 4m total CI duration on this PR
Slowest 15 Jobs Cumulative Duration Recent Runs
rust-move-unit-coverage 16m 🟩
rust-move-unit-coverage 15m 🟩
rust-move-unit-coverage 12m 🟩
rust-move-unit-coverage 11m 🟩
rust-move-tests 9m 🟩
rust-move-tests 9m 🟩
rust-move-tests 9m 🟩
rust-move-tests 9m 🟩
rust-cargo-deny 9m 🟩🟩🟩🟩🟩
rust-move-tests 7m
rust-move-unit-coverage 7m
check-dynamic-deps 5m 🟩🟩🟩🟩🟩
general-lints 2m 🟩🟩🟩🟩🟩
semgrep/ci 2m 🟩🟩🟩🟩🟩
file_change_determinator 55s 🟩🟩🟩🟩🟩

settingsfeedbackdocs ⋅ learn more about trunk.io

Copy link

codecov bot commented Oct 5, 2024

Codecov Report

Attention: Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.

Project coverage is 59.7%. Comparing base (d9c7970) to head (bf81777).
Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
third_party/move/move-prover/src/cli.rs 0.0% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (d9c7970) and HEAD (bf81777). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (d9c7970) HEAD (bf81777)
2 1
Additional details and impacted files
@@             Coverage Diff             @@
##             main   #14881       +/-   ##
===========================================
- Coverage    72.8%    59.7%    -13.1%     
===========================================
  Files        2400      853     -1547     
  Lines      485208   208289   -276919     
===========================================
- Hits       353490   124541   -228949     
+ Misses     131718    83748    -47970     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rahxephon89 rahxephon89 marked this pull request as ready for review October 6, 2024 02:23
@rahxephon89 rahxephon89 changed the title [WIP][Spec] add native for aggregator_v2 [Spec] add boogie native for aggregator_v2 Oct 6, 2024
let supply = global<ConcurrentSupply>(collection_addr);
let post supply_post = global<ConcurrentSupply>(collection_addr);
aborts_if exists<ConcurrentSupply>(collection_addr) &&
aggregator_v2::spec_get_value(supply.current_supply) + 1 > aggregator_v2::spec_get_max_value(supply.current_supply);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make line breaks better here? Shouldn't be over 100 characters

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

&& $IsEqual'{{S}}'(s1->$max_value, s2->$max_value)
}

{% if S == "u64" -%}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ther seems to be a bit of duplication in this if-else? Could we perhaps loop over the various types instead>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I refactored the template here to reduce duplication.

@@ -194,7 +194,7 @@ module aptos_framework::aggregator_v2 {
/// Concatenates `before`, `snapshot` and `after` into a single string.
/// snapshot passed needs to have integer type - currently supported types are u64 and u128.
/// Raises EUNSUPPORTED_AGGREGATOR_SNAPSHOT_TYPE if called with another type.
/// If length of prefix and suffix together exceed 256 bytes, ECONCAT_STRING_LENGTH_TOO_LARGE is raised.
/// If length of prefix and suffix together exceed 1024 bytes, ECONCAT_STRING_LENGTH_TOO_LARGE is raised.
Copy link
Contributor

Choose a reason for hiding this comment

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

another typo: exceed --> exceeds

"object_instances".to_string(),
true,
)],
module_instance_names: vec![
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like module_instances can be factored out? It is duplicated across multiple files causing more changes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

spec fun spec_read_derived_string(snapshot: DerivedStringSnapshot): String {
snapshot.value
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this extra line is not needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

spec native fun spec_get_max_value<IntElement>(aggregator: Aggregator<IntElement>): IntElement;
// Uninterpreted spec function that translates the value inside aggregator into corresponding string representation
spec fun spec_get_string_value<IntElement>(aggregator: AggregatorSnapshot<IntElement>): String;
spec fun spec_read_snapshot<IntElement>(snapshot: AggregatorSnapshot<IntElement>): IntElement {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: for consistency can also use get instead of read in these names? So we get aggregator value, get snapshot integer value, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used read here because there are corresponding APIs read_snapshot and read_derived_string.

pragma opaque;
include AbortsIfIntElement<IntElement>;
ensures [abstract] result.value == spec_get_value(aggregator);
Copy link
Contributor

Choose a reason for hiding this comment

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

Question, why here we need to call a spec native for aggregator, and not aggregator.value? Is it because the spec is intrinsic? Cause technically for sub for example you can say: aggregator.value = old(aggregator).value - amount if no overflow, etc. or does the spec not support these kind of things because of generic types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, when the spec of a struct is intrinsic, we cannot directly get access to its field. The reason of using intrinsics for spec is because generic types do not support + and -.

@@ -209,6 +209,199 @@ module aptos_framework::aggregator_v2 {
/// DEPRECATED, use derive_string_concat() instead. always raises EAGGREGATOR_FUNCTION_NOT_YET_SUPPORTED.
public native fun string_concat<IntElement>(before: String, snapshot: &AggregatorSnapshot<IntElement>, after: String): AggregatorSnapshot<String>;

#[verify_only]
Copy link
Contributor

Choose a reason for hiding this comment

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

I was not aware we had these attributes, so this can be used for spec testing? Nice to know!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, with this attribute, the function is only visible for verification.

let x = try_add(&mut agg, 5);
spec {
assert x;
assert is_at_least(agg, 5);
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious why at least and not exact equality?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I use this assertion to test the native implementation for the API is_at_least.

Copy link
Contributor Author

rahxephon89 commented Oct 8, 2024

This stack of pull requests is managed by Graphite. Learn more about stacking.

Join @rahxephon89 and the rest of your teammates on Graphite Graphite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants