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

[move][test_scenario] Fix bug in test_scenario where we weren't returning allocated tickets #17991

Merged
merged 5 commits into from
Sep 13, 2024

Conversation

tzakian
Copy link
Contributor

@tzakian tzakian commented May 30, 2024

Description

Fixes a bug where we weren't returning allocated receiving tickets in a transaction at the end of the transaction. This fixes the bug, and adds tests to make sure allocated tickets are properly handled at the end of a test scenario transaction.

Test plan

Added new tests to test the failing behavior and made sure they pass.

@tzakian tzakian requested review from amnn, tnowacki and a team May 30, 2024 05:59
Copy link

vercel bot commented May 30, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sui-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 13, 2024 9:05pm
3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Sep 13, 2024 9:05pm
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Sep 13, 2024 9:05pm
sui-typescript-docs ⬜️ Ignored (Inspect) Visit Preview Sep 13, 2024 9:05pm

Copy link
Contributor

@amnn amnn left a comment

Choose a reason for hiding this comment

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

I think I understand the core change, but would appreciate some more context on the broader changes!

sender,
scenario.txn_number,
test_random_tx_hash,
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you share some context on how the (pseudo-)random tx digest generation fits into the overall fix?

Copy link
Contributor

Choose a reason for hiding this comment

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

(Might be worth adding some of that context as a code comment around this change).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Happy to add a code comment.

Basically:

  1. Object IDs are generated from the transaction context based on the transaction hash + number of generated IDs thus far in the transaction.
  2. In test_scenario we always generate the transaction context with the same initial state of for a given sender.
  3. This means that each Move unit test using the test scenario will generate the same sequence of object IDs when calling new_object.
  4. The object runtime (and storage) is shared across all Move unit tests, and Move unit tests are executed in parallel.

This set of things leads to situations where multiple Move unit tests using test scenario can read/write to the shared object runtime/storage with the same object ID and different values in parallel leading to race conditions on the state of the object runtime.

By seeding the tx digest pseudo-randomly whenever we start a transaction we make it basically impossible to run into this type of "multiple objects with the same ID being read/written in parallel to the shared runtime" in Move unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it -- thanks for explaining! Happy with this for now, but it kind of seems like the right long-term solution is to use a different scenario object per test or something (to graduate this from "basically impossible" to "actually impossible"), how difficult would something like that be?

Comment on lines 497 to 498
assert!(parent.value == 10, EValueMismatch);
assert!(obj2.value == 20, EValueMismatch);
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 use clever errors for this?

Suggested change
assert!(parent.value == 10, EValueMismatch);
assert!(obj2.value == 20, EValueMismatch);
assert!(parent.value == 10);
assert!(obj2.value == 20);

Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that in another place you were able to get rid of the abort code but not in this file -- is it just a change waiting to be pushed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah darn forgot to add these changes. Will update!

Comment on lines 475 to 476
// Make sure that we properly handle the case where we receive an object
// when dealing with allocated tickets.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: doc comment? Also could you describe what happens when we don't properly handle that case, for posterity?

sui-execution/latest/sui-move-natives/src/test_scenario.rs Outdated Show resolved Hide resolved
sui-execution/latest/sui-move-natives/src/test_scenario.rs Outdated Show resolved Hide resolved
@tzakian tzakian force-pushed the tzakian/tto-test-scenario-fix branch from 131c6c7 to 18a359e Compare July 16, 2024 22:13
@@ -51,7 +51,8 @@ module sui::random_tests {
scenario.ctx(),
);
scenario.next_tx(@0x0);
let gen1 = random_state.new_generator(scenario.ctx());
let mut ctx = tx_context::new_from_hint(@0x0, 1, 0, 0, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note the changes here: it was previously using the fact that the object IDs derived from the context were stable which is no longer the case so we manually stabilize them here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully understand, what happens if this isn't stable?

@@ -442,8 +456,10 @@ module sui::random_tests {
let _output = gen.generate_u128_in_range(0, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
let mut i = 0;
while (i < 50) {
let min = gen.generate_u128();
let max = min + (gen.generate_u64() as u128);
let (min, max) = (gen.generate_u128(), gen.generate_u128());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed logic here since this was incorrect and subject to overflow (and the tests were in fact failing for this).

Copy link
Contributor

@amnn amnn left a comment

Choose a reason for hiding this comment

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

Looks good! Some questions from me, and perhaps some final clean-ups on abort codes to get pushed still, but this is good to go, thanks @tzakian !

@@ -103,6 +106,9 @@ module sui::random_tests {
scenario.ctx(),
);

// Deterministically set tx context so the RNG seeded by the ctx below is deterministic.
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, we're kind of hoping that someone doesn't create an object using this ctx in later code, right? Because if they did, we could be in that bad situation where two tests run in parallel and write different contents for the same object.

FWIW, I preferred the explicit use of the ctx you get from new_from_hint instead of overwriting the scenario's context for that reason.

Comment on lines 497 to 498
assert!(parent.value == 10, EValueMismatch);
assert!(obj2.value == 20, EValueMismatch);
Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that in another place you were able to get rid of the abort code but not in this file -- is it just a change waiting to be pushed?

Comment on lines 103 to 105
// Randomly seed the transaction context's transaction hash to avoid
// object ID collisions in parallel Move tests.
//
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd remove this comment, since it won't be true long term.

I would just summarize here that the transaction digest is randomized to better help simulate real execution environments. Not sure we need to say more than that?

I think this block of comments would be a lot better placed in some rust file if possible

@@ -51,7 +51,8 @@ module sui::random_tests {
scenario.ctx(),
);
scenario.next_tx(@0x0);
let gen1 = random_state.new_generator(scenario.ctx());
let mut ctx = tx_context::new_from_hint(@0x0, 1, 0, 0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully understand, what happens if this isn't stable?

Comment on lines 226 to 227
*scenario.ctx() = tx_context::new_from_hint(@0x0, 1, 0, 0, 0);

Copy link
Contributor

Choose a reason for hiding this comment

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

I am afraid

Copy link
Contributor

Choose a reason for hiding this comment

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

When the Rng is generated, it takes the source of randomness that is shared between transactions in a checkpoint (Random) and mixes it with its own transaction digest (by generating a new address and then using that), so if the digest is not stable in this case, you'll get different random numbers each time.

Copy link
Contributor

Choose a reason for hiding this comment

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

My fear was from writing to scenario.ctx(), not the hard coding of the context itself

@tzakian
Copy link
Contributor Author

tzakian commented Jul 23, 2024

Just FYI @amnn and @tnowacki -- took a look at using thread local storage for the object store, and that solved the issues very nicely, and without the need to randomly seed the test scenario transaction context 🎉

Comment on lines -57 to +58
pub struct InMemoryTestStore(pub &'static RwLock<InMemoryStorage>);
pub struct InMemoryTestStore(pub &'static LocalKey<RefCell<InMemoryStorage>>);
Copy link
Contributor

Choose a reason for hiding this comment

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

Approving to unblock, but this still feels like a crime.

@tzakian tzakian force-pushed the tzakian/tto-test-scenario-fix branch from 4028c93 to a430508 Compare September 13, 2024 21:04
@tzakian tzakian merged commit 2de2e03 into main Sep 13, 2024
48 checks passed
@tzakian tzakian deleted the tzakian/tto-test-scenario-fix branch September 13, 2024 21:27
suiwombat pushed a commit that referenced this pull request Sep 16, 2024
…urning allocated tickets (#17991)

## Description 

Fixes a bug where we weren't returning allocated receiving tickets in a
transaction at the end of the transaction. This fixes the bug, and adds
tests to make sure allocated tickets are properly handled at the end of
a test scenario transaction.

## Test plan 

Added new tests to test the failing behavior and made sure they pass.
valeriyr added a commit to iotaledger/iota that referenced this pull request Feb 3, 2025
[move][test_scenario] Fix bug in test_scenario where we weren't returning allocated tickets (#17991)
MystenLabs/sui#17991
valeriyr added a commit to iotaledger/iota that referenced this pull request Feb 3, 2025
[move][test_scenario] Fix bug in test_scenario where we weren't returning allocated tickets (#17991)
MystenLabs/sui#17991
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