-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[TON]: Eliminate nondeterminism in TON BoC serialization #4005
Conversation
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.
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() |
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.
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>
.
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 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.
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's also passed into
wallet-core/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
Lines 41 to 42 in af67cae
let raw_cells = raw_cells_from_cells(cells_iter, &cells_by_hash)?; | |
let root_indices = root_indices(&boc.roots, &cells_by_hash)?; |
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's also passed into
wallet-core/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
Lines 41 to 42 in af67cae
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.
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.
Ok I see, you're right, I didn't notice that.
We still have two options with pros and cons:
- 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 - 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
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've made the requested adjustments to the code. Could you please review it again? The code has been formatted using cargo fmt
.
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.
Thank you for the adjustments! LGTM
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.
wallet-core/rust/frameworks/tw_ton_sdk/src/boc/boc_to_raw_boc.rs
Line 85 in af67cae
Resolution
Change the HashMap to an ordered data structure, such as a BTreeMap.