derive cell level masks instead of storing them - #502
Merged
Conversation
boc.Cell.mask was only ever populated by the deserializer (from the d1 byte) and hand-set once in pruneCells. Nothing else maintained it: NewCell leaves it at 0 and AddRef never ORs a child's mask into the parent, so any tree built in memory around a nonzero-level cell - everything tlb.Marshal produces, e.g. via txemulator.tlbStructToBase64 - serialized as a level 0 parent over a level 1 child. ton rejects that with "level mask mismatch". The same stale mask made newImmutableCell compute the wrong number of hashes, so Hash256() of a freshly built tree disagreed with the identical wire-decoded one. Drop the field and derive the mask from a cell's type, content and refs the way ton's DataCell::create does: ordinary is the union of its refs, pruned branch takes its mask from the data, library is 0, and merkle cells shift right - a plain union would give a merkle proof root the wrong level. Both consumers go through the one rule, so serialization and hashing can no longer drift apart. Deriving a pruned branch's mask also validates its body length, which closes an out of range read in immutableCell.Hash/Depth reachable from any boc. Hash256() keeps returning the hash of the highest level, matching ton's Cell::get_hash(). For a tree with pruned branches that is no longer the representation hash, so HashAtLevel is added to get it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
boc.Cell.mask was only ever populated by the deserializer (from the d1 byte) and hand-set once in pruneCells. Nothing else maintained it: NewCell leaves it at 0 and AddRef never ORs a child's mask into the parent, so any tree built in memory around a nonzero-level cell - everything tlb.Marshal produces, e.g. via txemulator.tlbStructToBase64 - serialized as a level 0 parent over a level 1 child. ton rejects that with "level mask mismatch". The same stale mask made newImmutableCell compute the wrong number of hashes, so Hash256() of a freshly built tree disagreed with the identical wire-decoded one.
Drop the field and derive the mask from a cell's type, content and refs the way ton's DataCell::create does: ordinary is the union of its refs, pruned branch takes its mask from the data, library is 0, and merkle cells shift right - a plain union would give a merkle proof root the wrong level. Both consumers go through the one rule, so serialization and hashing can no longer drift apart.
Deriving a pruned branch's mask also validates its body length, which closes an out of range read in immutableCell.Hash/Depth reachable from any boc.
Hash256() keeps returning the hash of the highest level, matching ton's Cell::get_hash(). For a tree with pruned branches that is no longer the representation hash, so HashAtLevel is added to get it.