Skip to content

feat(consensus): Add new verifications for mempool only#1379

Merged
msbrogli merged 1 commit intomasterfrom
feat/mempool-verifications
Sep 25, 2025
Merged

feat(consensus): Add new verifications for mempool only#1379
msbrogli merged 1 commit intomasterfrom
feat/mempool-verifications

Conversation

@msbrogli
Copy link
Member

@msbrogli msbrogli commented Aug 22, 2025

Motivation

Add several verifications for mempool only (but one). As it impacts only the mempool, the policies can be more strict than the blockchain protocol itself.

Acceptance Criteria

  1. Add BlockVerifier.verify_checkpoints() for all blocks.
  2. Add NanoHeaderVerifier.verify_method_call() for nano transactions entering the mempool. This requires the blueprint to exist, the contract to have already been created, the method to exist (or a fallback method), and the call arguments to be parseable.
  3. Add NanoHeaderVerifier.verify_seqnum() for nano transactions entering the mempool.
  4. Add TransactionVerifier.verify_conflict() for transactions entering the mempool. This forbids transactions in the mempool to have a conflict with an already confirmed transaction.
  5. Add TransactionVerifier.verify_token() for transactions entering the mempool. This limits the number of tokens, ensure that there is no duplicate token in the list, and ensure that all tokens are used at least once.
  6. Add VertexVerifier.verify_old_timestamp() for all vertices entering the mempool. This limits how old these vertices can be.

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

@msbrogli msbrogli requested a review from jansegre as a code owner August 22, 2025 16:29
@msbrogli msbrogli self-assigned this Aug 22, 2025
@msbrogli msbrogli moved this from Todo to In Progress (WIP) in Hathor Network Aug 22, 2025
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch from 80f6f7f to 6d0cfa0 Compare August 22, 2025 16:30
@github-actions
Copy link

github-actions bot commented Aug 22, 2025

🐰 Bencher Report

Branchfeat/mempool-verifications
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.27 m
(+32.98%)Baseline: 1.71 m
2.05 m
(110.81%)

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.27 m
(+32.98%)Baseline: 1.71 m
1.54 m
(67.68%)
2.05 m
(110.81%)

🐰 View full continuous benchmarking report in Bencher

@msbrogli msbrogli force-pushed the feat/mempool-verifications branch from 6d0cfa0 to 5222b85 Compare August 22, 2025 20:05
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 5 times, most recently from 1930e49 to 3817185 Compare September 11, 2025 21:41
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 8 times, most recently from 0741c79 to 8d563c0 Compare September 12, 2025 20:44
Comment on lines +109 to +110
if params.reject_too_old_vertices:
self.verifiers.vertex.verify_old_timestamp(vertex)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I prefer having the conditionals inside the methods. That is, just call them here and they'll be nops depending on the params.

The reason being, I think the verification service should just be a "dumb router", and it would be the method's responsibility to know when it should or not make the check, which makes sense because being mempool-only is something specific and internal for each method. Also, it makes sure that the correct conditionals are used whenever these methods are called, even if they're called outside the verification service.

Same for all other calls. What do you think?

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 agree. Done!

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this solution is okay for now, but I think we should refactor it in the future to create a better separation of mempool-only checks and existing verification. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add all new mempool-only verifications to the consensus handling of reorgs, to remove invalid txs when the mempool changes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I'll do it in another PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add all new verification methods to test_verification.py?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I'll do it in another PR.

Comment on lines +114 to +116
for cp in self._settings.CHECKPOINTS:
if cp.height == block_height and cp.hash != block.hash:
raise CheckpointError(f'invalid checkpoint block (height={cp.height})')
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we refactor the settings.CHECKPOINTS attribute to be a dict with height keys instead of a list, so we can replace this O(n) with O(1)? At the very least we could break out of the loop when the height is found.

Copy link
Member Author

@msbrogli msbrogli Sep 19, 2025

Choose a reason for hiding this comment

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

I reduced the O(n) to O(1) but without changing the settings.CHECKPOINTS type.


nano_header = tx.get_nano_header()
if nano_header.is_creating_contract():
# TODO Confirm that the blueprint exists.
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO

Copy link
Member Author

Choose a reason for hiding this comment

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

Done!


def verify_standard_scripts(self, tx: Transaction) -> None:
"""Verify that all outputs have standard scripts."""
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO?

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 decided to not add this verification now. I'll do it separately in the consensus simplify project.

@msbrogli msbrogli force-pushed the feat/mempool-verifications branch from 8d563c0 to 897f6b4 Compare September 16, 2025 15:40
@jansegre jansegre mentioned this pull request Sep 17, 2025
2 tasks
block_storage = self._tx_storage.get_nc_block_storage(best_block)
seqnum = block_storage.get_address_seqnum(nano_header.nc_address)
diff = nano_header.nc_seqnum - seqnum
if diff < 0 or diff > MAX_SEQNUM_JUMP_SIZE:
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 might prevent the same address from having "a lot of nano transactions" in the same block.

Comment on lines +154 to +164
def verify_seqnum(self, tx: BaseTransaction) -> None:
assert tx.is_nano_contract()
assert isinstance(tx, Transaction)

nano_header = tx.get_nano_header()
best_block = self._tx_storage.get_best_block()
block_storage = self._tx_storage.get_nc_block_storage(best_block)
seqnum = block_storage.get_address_seqnum(nano_header.nc_address)
diff = nano_header.nc_seqnum - seqnum
if diff < 0 or diff > MAX_SEQNUM_JUMP_SIZE:
raise NCInvalidSeqnum(f'invalid seqnum (diff={diff})')
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we iterate over all txs in the mempool instead of getting the best block storage?

@msbrogli msbrogli force-pushed the feat/mempool-verifications branch from 897f6b4 to 3c9f5ed Compare September 18, 2025 16:42
@msbrogli msbrogli moved this from In Progress (WIP) to In Review (Done) in Hathor Network Sep 18, 2025
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 2 times, most recently from 17a16a8 to 7fc61e7 Compare September 19, 2025 14:44
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 3 times, most recently from fa7fd1d to 52dcc09 Compare September 22, 2025 20:33
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 2 times, most recently from 39aca90 to 62447bf Compare September 23, 2025 14:28
@codecov
Copy link

codecov bot commented Sep 23, 2025

Codecov Report

❌ Patch coverage is 96.27329% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.83%. Comparing base (33a2888) to head (297959c).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
hathor/dag_builder/artifacts.py 77.77% 1 Missing and 1 partial ⚠️
hathor/verification/block_verifier.py 88.88% 1 Missing and 1 partial ⚠️
hathor/verification/nano_header_verifier.py 96.61% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1379      +/-   ##
==========================================
+ Coverage   85.74%   85.83%   +0.08%     
==========================================
  Files         431      431              
  Lines       33133    33278     +145     
  Branches     5196     5225      +29     
==========================================
+ Hits        28411    28564     +153     
+ Misses       3679     3673       -6     
+ Partials     1043     1041       -2     

☔ 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.

@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 4 times, most recently from 74eeb43 to 3088105 Compare September 24, 2025 14:46
jansegre
jansegre previously approved these changes Sep 24, 2025
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 2 times, most recently from be64c1c to e84308c Compare September 25, 2025 15:41
jansegre
jansegre previously approved these changes Sep 25, 2025
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch 2 times, most recently from 5031493 to eec4b15 Compare September 25, 2025 18:10
@msbrogli msbrogli force-pushed the feat/mempool-verifications branch from eec4b15 to 297959c Compare September 25, 2025 18:10
@msbrogli msbrogli merged commit 297959c into master Sep 25, 2025
6 of 8 checks passed
@msbrogli msbrogli deleted the feat/mempool-verifications branch September 25, 2025 19:29
@github-project-automation github-project-automation bot moved this from In Review (Done) to Waiting to be deployed in Hathor Network Sep 25, 2025
This was referenced Sep 25, 2025
@jansegre jansegre moved this from Waiting to be deployed to Done in Hathor Network Sep 26, 2025
@jansegre jansegre mentioned this pull request Oct 7, 2025
2 tasks
@jansegre jansegre mentioned this pull request Oct 16, 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

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants