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

[TON]: Eliminate nondeterminism in TON BoC serialization #4005

Merged
merged 2 commits into from
Sep 2, 2024

Conversation

10gic
Copy link
Contributor

@10gic 10gic commented Aug 31, 2024

Fix issue #4004

The test case SignMessageToTransferAndDeployWalletV5R1 occasionally fails.

Root Cause

A HashMap is unordered, so iterating over it multiple times may yield different results.

for index_cell in cells_by_hash.values() {

Resolution

Change the HashMap to an ordered data structure, such as a BTreeMap.

@10gic 10gic changed the title Eliminate nondeterminism in TON BoC serialization [TON]: Eliminate nondeterminism in TON BoC serialization Aug 31, 2024
Copy link
Collaborator

@satoshiotomakan satoshiotomakan left a comment

Choose a reason for hiding this comment

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

Good catch! I believe you spent a lot of time on trying to find it

@@ -96,7 +101,7 @@ fn build_and_verify_index(roots: &[CellArc]) -> HashMap<H256, IndexedCellRef> {
}
}

cells_by_hash
cells_by_hash.into_iter().collect()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This still will be non-deterministic. That's because HashMap is randomly seeded:
https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html
Look like we need to replace HashMap<H256, IndexedCellRef> with BTreeMap<H256, IndexedCellRef> everywhere in this file, and even add a type alias like type CellsByHash = BTreeMap<H256, IndexedCellRef>.

Copy link
Contributor Author

@10gic 10gic Sep 2, 2024

Choose a reason for hiding this comment

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

You are right. A HashMap is unordered; however, the subsequent code does not iterate over it directly; instead, it converts it to a Vec and then sorts it by the index of the cell (not the HashMap key).

index_slice.sort_unstable_by(|a, b| a.borrow().index.cmp(&b.borrow().index));

So, I think it's deterministic.

Even if I use a BTreeMap, I won't remove the sorting process because it needs to be sorted by the index of the cell.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's also passed into

let raw_cells = raw_cells_from_cells(cells_iter, &cells_by_hash)?;
let root_indices = root_indices(&boc.roots, &cells_by_hash)?;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's also passed into

let raw_cells = raw_cells_from_cells(cells_iter, &cells_by_hash)?;
let root_indices = root_indices(&boc.roots, &cells_by_hash)?;

The scenarios you mentioned do not iterate over the HashMap.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok I see, you're right, I didn't notice that.
We still have two options with pros and cons:

  1. Use BTreeMap within build_and_verify_index only, and then convert it into HashMap.
    Cons: Double memory allocation + iterate over all items within BTreeMap to insert them into HashMap
  2. Use BTreeMap within build_and_verify_index and other functions too.
    Cons: Searching for a Cell hash within BTreeMap has O(logn) complexity, while searching within HashMap has O(1).

I'd follow the second way tbh as it's more clear, and less overhead from my point of view

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've made the requested adjustments to the code. Could you please review it again? The code has been formatted using cargo fmt.

Copy link
Collaborator

@satoshiotomakan satoshiotomakan left a comment

Choose a reason for hiding this comment

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

Thank you for the adjustments! LGTM

@satoshiotomakan satoshiotomakan merged commit f703596 into trustwallet:master Sep 2, 2024
12 checks passed
@10gic 10gic deleted the ton-deterministic-boc branch September 24, 2024 15:14
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.

2 participants