Skip to content

feat(nano): support key mangling for nested container fields#1312

Merged
jansegre merged 1 commit intomasterfrom
feat/nested-container-fields
Sep 18, 2025
Merged

feat(nano): support key mangling for nested container fields#1312
jansegre merged 1 commit intomasterfrom
feat/nested-container-fields

Conversation

@jansegre
Copy link
Member

@jansegre jansegre commented Jul 4, 2025

Motivation

Before this PR, compound containers fields will fully serialize the whole contents of contained fields:

class MyBlueprint(Blueprint):
    records_per_address: dict[Address, list[bytes]]

Would mean each list[bytes] is fully serialized, which hinders large lists.

This PR makes it so the list[bytes] above is serialized as a records: list[bytes] field would be (using a DequeContainer), but using a dynamic prefix, instead of a prefix that only uses the field name, so (field_name, key) would become the prefix used by DequeContainer.

Impact

Currently we support 3 types of containers (before this PR, they were only "attached" directly to fields), they are: DequeContainer, DictContainer, SetContainer. Out of this 3, only a DequeContainer is iterable.

By making inner containers also use our container implementations, some of them will not behave like the native Python type that it is replacing. For example:

class MyBlueprint(Blueprint):
    distances: dict[str, dict[str, int]]

In the blueprint above, distances['foo'] would return a dict[str, int] which is Iterable, however after this PR it will return something equivalent to a DictContainer, whic is not Iterable. That is, previously distances['foo'].keys() would work, but after this PR it will no longer be the case.

The same happens for a set[T], but in this case the old behavior can be achieved using frozenset[T] instead. A dict however, doesn't really have a "frozen" equivalent, a mappingproxy comes close, but we don't allow it to be imported, in theory we could support something like mappingproxy[K, V].

Another impact is that foo: tuple[dict[K, V]] will make it so foo[0] still has the old behavior. But it's planned that a TupleContainer is introduced to make foo[0] return a DictContainer.

Acceptance Criteria

  • Make Field not generic anymore, now it is the only concrete field type that carries a ContainerNodeFactory
  • Introduce a "container system":
    • a ContainerNode is an abstract type that can be either a ContainerProxy or a ContainerLeaf, a ContainerNode is never directly accessible in Blueprint code
    • a ContainerProxy implements operations that compound the db-prefix until the operations reach a ContainerLeaf
    • a ContainerLeaf implements operations on a specific NCType that actually stores data in the contract storage
    • a Container abstract some common functionality that all containers have, whether a container uses a ContainerNode depends on the specific implementation, a concrete implementation of a Container is what ends up being used in runtime when a blueprint does self.foo[1] for example, so starting from a Container it is important to not expose anything to blueprint code, this is achieved by only using double underscore properties (either __dunder for private, implementation-specific properties; or __special__ for properties that have to be accessible to some other class/module but not the blueprint)
    • a SetContainer uses ContainerLeaf directly, not a ContainerNode, because it doesn't allow its members to mutate
    • a DequeContainer uses a ContainerNode to represent its members, it doesn't know directly whether a member is a ContainerProxy or a ContainerNode, but it operates on them through the abstract operations of a ContainerNode
    • a DictContainer uses a bare NCType to serialize keys (since they are only needed for generating the prefix for the values) and a ContainerNode to work with its values, similarly to how a DequeContainer does it
    • tests are adapted to these changes, but more tests covering new container combinations will be created in a continuation PR
    • a container can be deleted as long as it is empty, otherwise it will fail
    • a container can only be set once, for it to be set again the previous container must be deleted

Checklist

  • If you are requesting a merge into master, confirm this code is production-ready and can be included in future releases as soon as it gets merged

@jansegre jansegre moved this from Todo to In Progress (WIP) in Hathor Network Jul 4, 2025
@jansegre jansegre self-assigned this Jul 4, 2025
@github-actions
Copy link

github-actions bot commented Jul 4, 2025

🐰 Bencher Report

Branchfeat/nested-container-fields
Testbedubuntu-22.04

🚨 1 Alert

BenchmarkMeasure
Units
ViewBenchmark Result
(Result Δ%)
Upper Boundary
(Limit %)
sync-v2 (up to 20000 blocks)Latency
minutes (m)
📈 plot
🚷 threshold
🚨 alert (🔔)
2.02 m
(+20.46%)Baseline: 1.68 m
2.01 m
(100.38%)

Click to view all benchmark results
BenchmarkLatencyBenchmark Result
minutes (m)
(Result Δ%)
Lower Boundary
minutes (m)
(Limit %)
Upper Boundary
minutes (m)
(Limit %)
sync-v2 (up to 20000 blocks)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
2.02 m
(+20.46%)Baseline: 1.68 m
1.51 m
(74.71%)
2.01 m
(100.38%)

🐰 View full continuous benchmarking report in Bencher

@jansegre jansegre force-pushed the feat/nested-container-fields branch 2 times, most recently from 364375c to 8093e9a Compare July 9, 2025 00:05
@jansegre jansegre marked this pull request as ready for review July 9, 2025 16:27
@jansegre jansegre requested a review from msbrogli as a code owner July 9, 2025 16:27
@codecov
Copy link

codecov bot commented Jul 9, 2025

Codecov Report

❌ Patch coverage is 87.21805% with 51 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.83%. Comparing base (8f23d02) to head (95b75f6).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
hathor/nanocontracts/fields/dict_container.py 68.11% 19 Missing and 3 partials ⚠️
hathor/nanocontracts/fields/container.py 92.59% 6 Missing and 6 partials ⚠️
hathor/nanocontracts/fields/deque_container.py 80.70% 6 Missing and 5 partials ⚠️
hathor/nanocontracts/fields/set_container.py 92.10% 2 Missing and 1 partial ⚠️
hathor/nanocontracts/fields/field.py 91.30% 2 Missing ⚠️
hathor/nanocontracts/nc_types/tuple_nc_type.py 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1312      +/-   ##
==========================================
- Coverage   86.03%   85.83%   -0.21%     
==========================================
  Files         432      430       -2     
  Lines       32822    32973     +151     
  Branches     5114     5161      +47     
==========================================
+ Hits        28239    28301      +62     
- Misses       3575     3639      +64     
- Partials     1008     1033      +25     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jansegre jansegre force-pushed the feat/nested-container-fields branch 2 times, most recently from 647c6e0 to 254b387 Compare July 11, 2025 16:02
@jansegre jansegre requested a review from glevco July 11, 2025 16:03
@jansegre jansegre moved this from In Progress (WIP) to In Progress (Done) in Hathor Network Jul 11, 2025
@jansegre jansegre force-pushed the feat/nested-container-fields branch from 254b387 to 3904b1b Compare July 14, 2025 16:24
@jansegre jansegre force-pushed the feat/nested-container-fields branch from 3904b1b to a4ce768 Compare July 21, 2025 19:22
Copy link
Member

Choose a reason for hiding this comment

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

I couldn't find tests that confirm that final key used to store compound types in the RocksDB storage.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll add some tests for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Test will be added in the next PR.

@github-project-automation github-project-automation bot moved this from In Progress (Done) to In Review (WIP) in Hathor Network Jul 22, 2025
@jansegre jansegre force-pushed the feat/nested-container-fields branch 2 times, most recently from a857f6f to ae0fe2f Compare July 29, 2025 16:47
@jansegre jansegre force-pushed the feat/nested-container-fields branch from ae0fe2f to 2cbccd6 Compare August 21, 2025 19:01
Comment on lines -22 to -25
try:
self.dc['foo']
except KeyError as e:
assert e.args[0] == b'dc:\x03foo'
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we assert pytest.raises(KeyError) for this case?

Copy link
Member Author

Choose a reason for hiding this comment

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

This doesn't happen because of the implicit creation.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should remember to readd this when the implicit creation is removed, then

@jansegre jansegre force-pushed the feat/nested-container-fields branch from 2cbccd6 to 462329a Compare August 21, 2025 21:42
@jansegre jansegre force-pushed the feat/nested-container-fields branch from a3ee9e2 to c52cfd2 Compare September 16, 2025 16:40
@jansegre jansegre mentioned this pull request Sep 17, 2025
2 tasks
msbrogli
msbrogli previously approved these changes Sep 17, 2025
@jansegre jansegre force-pushed the feat/nested-container-fields branch from c52cfd2 to eda7f6f Compare September 17, 2025 20:21
@jansegre jansegre moved this from In Review (WIP) to In Review (Done) in Hathor Network Sep 17, 2025
glevco
glevco previously approved these changes Sep 17, 2025
msbrogli
msbrogli previously approved these changes Sep 18, 2025
@github-project-automation github-project-automation bot moved this from In Review (Done) to In Review (WIP) in Hathor Network Sep 18, 2025
@jansegre jansegre moved this from In Review (WIP) to In Review (Done) in Hathor Network Sep 18, 2025
@jansegre jansegre dismissed stale reviews from msbrogli and glevco via 95b75f6 September 18, 2025 16:22
@jansegre jansegre force-pushed the feat/nested-container-fields branch from eda7f6f to 95b75f6 Compare September 18, 2025 16:22
@jansegre jansegre merged commit c645587 into master Sep 18, 2025
1 of 2 checks passed
@jansegre jansegre deleted the feat/nested-container-fields branch September 18, 2025 16:22
@github-project-automation github-project-automation bot moved this from In Review (Done) to Waiting to be deployed in Hathor Network Sep 18, 2025
@jansegre jansegre moved this from Waiting to be deployed to Done in Hathor Network Sep 18, 2025
@jansegre jansegre mentioned this pull request Sep 24, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants