Skip to content

fix(legacy): remove subtree notification listener causing tx re-broadcasts#735

Merged
oskarszoon merged 1 commit into
bsv-blockchain:mainfrom
oskarszoon:fix/legacy-inv
Apr 21, 2026
Merged

fix(legacy): remove subtree notification listener causing tx re-broadcasts#735
oskarszoon merged 1 commit into
bsv-blockchain:mainfrom
oskarszoon:fix/legacy-inv

Conversation

@oskarszoon

Copy link
Copy Markdown
Contributor

Summary

  • Removed the subtree notification listener from legacy sync manager that was re-announcing all transactions in block assembly to legacy peers after every new block
  • Root cause: block assembly reorganizes remaining txs into new subtrees after each block, firing subtree notifications — legacy was listening to these and re-broadcasting every tx in the new subtrees
  • Tx announcements to legacy peers now flow exclusively through the txmeta Kafka path, which already covers all validated transactions without the re-broadcast problem

Context

The subtree listener was added in 5d00b079 (March 2025) as a redundant announcement path alongside Kafka, relying on the batcher's 1-minute dedup window. However, since reorg'd subtrees contain the same txs under new subtree hashes, and most txs are older than 1 minute, the dedup didn't prevent re-announces.

Subtree notifications still fire normally for P2P service (teranode-to-teranode subtree propagation) — only legacy stops listening.

Test plan

  • Existing TestProcessTXmetaBatchMessage tests pass (Kafka path unchanged)
  • Build passes, linter passes
  • Verify on testnet that legacy peers still receive tx announcements via Kafka path
  • Confirm no duplicate INV messages after new blocks

…ll txs after each block

The subtree notification handler was re-announcing every transaction in
block assembly to legacy peers whenever a new block arrived, because
block assembly reorganizes remaining txs into new subtrees and fires
subtree notifications. The txmeta Kafka path already handles all tx
announcements correctly without this re-broadcast problem.
@github-actions

github-actions Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude Code Review

Status: Complete


No issues found. The changes are clean and correct:

  • The removed subtree listener code is completely eliminated (51 lines)
  • Unused imports (subtreepkg and fileformat) are properly removed
  • The Kafka txmeta path remains intact at line 2584 (sm.txAnnounceBatcher.Put)
  • P2P service still correctly handles subtree notifications (services/p2p/Server.go:1469-1471)
  • The replacement comment accurately documents the new behavior
  • No other references to the removed code exist in the file

The fix correctly addresses the re-announcement issue by removing the redundant listener while preserving the primary Kafka-based announcement path.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Comparison Report

Baseline: main (unknown)

Current: PR-735 (1c956ca)

Summary

  • Regressions: 0
  • Improvements: 0
  • Unchanged: 142
  • Significance level: p < 0.05
All benchmark results (sec/op)
Benchmark Baseline Current Change p-value
_NewBlockFromBytes-4 1.680µ 1.698µ ~ 0.600
SplitSyncedParentMap_SetIfNotExists/256_buckets-4 61.57n 61.54n ~ 1.000
SplitSyncedParentMap_SetIfNotExists/16_buckets-4 61.46n 61.53n ~ 0.700
SplitSyncedParentMap_SetIfNotExists/1_bucket-4 61.54n 61.48n ~ 1.000
SplitSyncedParentMap_ConcurrentSetIfNotExists/256_buckets... 29.98n 30.32n ~ 0.700
SplitSyncedParentMap_ConcurrentSetIfNotExists/16_buckets_... 51.48n 51.83n ~ 1.000
SplitSyncedParentMap_ConcurrentSetIfNotExists/1_bucket_pa... 104.9n 105.2n ~ 0.600
MiningCandidate_Stringify_Short-4 263.4n 261.7n ~ 0.400
MiningCandidate_Stringify_Long-4 1.896µ 1.895µ ~ 0.600
MiningSolution_Stringify-4 974.4n 970.3n ~ 0.200
BlockInfo_MarshalJSON-4 1.775µ 1.776µ ~ 1.000
NewFromBytes-4 126.8n 127.6n ~ 0.200
Mine_EasyDifficulty-4 64.69µ 64.46µ ~ 0.200
Mine_WithAddress-4 4.840µ 4.856µ ~ 0.400
BlockAssembler_AddTx-4 0.02859n 0.03300n ~ 0.200
AddNode-4 11.52 10.91 ~ 0.100
AddNodeWithMap-4 10.64 11.59 ~ 0.700
DiskTxMap_SetIfNotExists-4 3.488µ 3.571µ ~ 0.400
DiskTxMap_SetIfNotExists_Parallel-4 3.312µ 3.352µ ~ 0.700
DiskTxMap_ExistenceOnly-4 305.3n 302.1n ~ 0.700
Queue-4 193.4n 192.6n ~ 1.000
AtomicPointer-4 4.579n 4.452n ~ 0.200
ReorgOptimizations/DedupFilterPipeline/Old/10K-4 868.7µ 841.6µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/10K-4 862.8µ 845.9µ ~ 0.100
ReorgOptimizations/AllMarkFalse/Old/10K-4 125.1µ 108.0µ ~ 0.100
ReorgOptimizations/AllMarkFalse/New/10K-4 62.51µ 62.17µ ~ 0.400
ReorgOptimizations/HashSlicePool/Old/10K-4 64.88µ 68.49µ ~ 0.700
ReorgOptimizations/HashSlicePool/New/10K-4 11.27µ 11.14µ ~ 0.100
ReorgOptimizations/NodeFlags/Old/10K-4 5.368µ 5.553µ ~ 0.200
ReorgOptimizations/NodeFlags/New/10K-4 1.819µ 1.941µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/Old/100K-4 10.22m 11.40m ~ 0.700
ReorgOptimizations/DedupFilterPipeline/New/100K-4 10.31m 10.19m ~ 0.700
ReorgOptimizations/AllMarkFalse/Old/100K-4 1.157m 1.143m ~ 1.000
ReorgOptimizations/AllMarkFalse/New/100K-4 678.7µ 677.9µ ~ 0.700
ReorgOptimizations/HashSlicePool/Old/100K-4 678.1µ 657.5µ ~ 0.700
ReorgOptimizations/HashSlicePool/New/100K-4 313.9µ 305.9µ ~ 1.000
ReorgOptimizations/NodeFlags/Old/100K-4 58.27µ 57.40µ ~ 0.700
ReorgOptimizations/NodeFlags/New/100K-4 19.57µ 20.14µ ~ 0.100
TxMapSetIfNotExists-4 51.03n 51.48n ~ 0.100
TxMapSetIfNotExistsDuplicate-4 37.98n 38.08n ~ 0.400
ChannelSendReceive-4 629.8n 608.3n ~ 0.100
DirectSubtreeAdd/4_per_subtree-4 61.30n 60.31n ~ 0.700
DirectSubtreeAdd/64_per_subtree-4 28.98n 29.72n ~ 0.700
DirectSubtreeAdd/256_per_subtree-4 27.63n 27.65n ~ 1.000
DirectSubtreeAdd/1024_per_subtree-4 26.49n 26.38n ~ 0.100
DirectSubtreeAdd/2048_per_subtree-4 26.03n 26.08n ~ 1.000
SubtreeProcessorAdd/4_per_subtree-4 308.3n 312.5n ~ 0.500
SubtreeProcessorAdd/64_per_subtree-4 311.8n 318.2n ~ 0.200
SubtreeProcessorAdd/256_per_subtree-4 316.4n 315.6n ~ 1.000
SubtreeProcessorAdd/1024_per_subtree-4 315.3n 315.7n ~ 0.700
SubtreeProcessorAdd/2048_per_subtree-4 316.9n 312.6n ~ 0.400
SubtreeProcessorRotate/4_per_subtree-4 317.9n 318.5n ~ 0.800
SubtreeProcessorRotate/64_per_subtree-4 315.2n 320.0n ~ 0.700
SubtreeProcessorRotate/256_per_subtree-4 315.2n 320.8n ~ 0.200
SubtreeProcessorRotate/1024_per_subtree-4 308.0n 310.3n ~ 0.400
SubtreeNodeAddOnly/4_per_subtree-4 68.16n 69.12n ~ 0.100
SubtreeNodeAddOnly/64_per_subtree-4 40.87n 40.82n ~ 1.000
SubtreeNodeAddOnly/256_per_subtree-4 39.43n 39.25n ~ 0.500
SubtreeNodeAddOnly/1024_per_subtree-4 38.67n 38.69n ~ 0.400
SubtreeCreationOnly/4_per_subtree-4 165.5n 167.1n ~ 0.100
SubtreeCreationOnly/64_per_subtree-4 601.9n 630.5n ~ 0.400
SubtreeCreationOnly/256_per_subtree-4 1.851µ 1.952µ ~ 0.100
SubtreeCreationOnly/1024_per_subtree-4 4.796µ 4.593µ ~ 0.400
SubtreeCreationOnly/2048_per_subtree-4 7.893µ 7.350µ ~ 0.400
SubtreeProcessorOverheadBreakdown/64_per_subtree-4 307.7n 317.2n ~ 0.400
SubtreeProcessorOverheadBreakdown/1024_per_subtree-4 310.9n 311.6n ~ 1.000
ParallelGetAndSetIfNotExists/1k_nodes-4 926.2µ 919.9µ ~ 0.400
ParallelGetAndSetIfNotExists/10k_nodes-4 1.957m 1.964m ~ 0.700
ParallelGetAndSetIfNotExists/50k_nodes-4 9.095m 9.007m ~ 1.000
ParallelGetAndSetIfNotExists/100k_nodes-4 17.74m 18.06m ~ 0.100
SequentialGetAndSetIfNotExists/1k_nodes-4 758.6µ 754.9µ ~ 0.400
SequentialGetAndSetIfNotExists/10k_nodes-4 3.138m 3.071m ~ 1.000
SequentialGetAndSetIfNotExists/50k_nodes-4 11.33m 11.52m ~ 0.400
SequentialGetAndSetIfNotExists/100k_nodes-4 21.44m 21.70m ~ 0.200
ProcessOwnBlockSubtreeNodesParallel/1k_nodes-4 1.014m 1.022m ~ 1.000
ProcessOwnBlockSubtreeNodesParallel/10k_nodes-4 5.135m 5.180m ~ 0.200
ProcessOwnBlockSubtreeNodesParallel/100k_nodes-4 19.46m 19.81m ~ 0.100
ProcessOwnBlockSubtreeNodesSequential/1k_nodes-4 806.8µ 801.8µ ~ 0.100
ProcessOwnBlockSubtreeNodesSequential/10k_nodes-4 6.520m 6.469m ~ 0.700
ProcessOwnBlockSubtreeNodesSequential/100k_nodes-4 42.44m 41.56m ~ 0.100
CalcBlockWork-4 494.0n 499.1n ~ 0.700
CalculateWork-4 679.2n 682.7n ~ 0.200
BuildBlockLocatorString_Helpers/Size_10-4 1.622µ 1.522µ ~ 0.700
BuildBlockLocatorString_Helpers/Size_100-4 12.60µ 12.61µ ~ 1.000
BuildBlockLocatorString_Helpers/Size_1000-4 124.9µ 124.8µ ~ 1.000
CatchupWithHeaderCache-4 104.0m 104.1m ~ 0.400
_BufferPoolAllocation/16KB-4 3.400µ 3.523µ ~ 0.100
_BufferPoolAllocation/32KB-4 8.010µ 7.816µ ~ 1.000
_BufferPoolAllocation/64KB-4 16.74µ 14.78µ ~ 0.700
_BufferPoolAllocation/128KB-4 30.31µ 32.21µ ~ 0.100
_BufferPoolAllocation/512KB-4 116.2µ 111.2µ ~ 0.100
_BufferPoolConcurrent/32KB-4 18.80µ 19.51µ ~ 0.100
_BufferPoolConcurrent/64KB-4 28.99µ 30.11µ ~ 0.700
_BufferPoolConcurrent/512KB-4 144.9µ 141.0µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/16KB-4 613.5µ 648.2µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/32KB-4 625.2µ 637.6µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/64KB-4 604.1µ 632.2µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/128KB-4 608.4µ 640.7µ ~ 0.200
_SubtreeDeserializationWithBufferSizes/512KB-4 627.4µ 669.4µ ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/16KB-4 35.79m 35.77m ~ 0.700
_SubtreeDataDeserializationWithBufferSizes/32KB-4 35.53m 35.58m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/64KB-4 35.69m 35.66m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/128KB-4 35.50m 35.59m ~ 0.700
_SubtreeDataDeserializationWithBufferSizes/512KB-4 35.18m 35.16m ~ 1.000
_PooledVsNonPooled/Pooled-4 745.9n 742.7n ~ 0.700
_PooledVsNonPooled/NonPooled-4 6.474µ 7.115µ ~ 0.100
_MemoryFootprint/Current_512KB_32concurrent-4 6.536µ 6.629µ ~ 0.400
_MemoryFootprint/Proposed_32KB_32concurrent-4 9.402µ 9.473µ ~ 0.400
_MemoryFootprint/Alternative_64KB_32concurrent-4 8.884µ 9.353µ ~ 0.100
_prepareTxsPerLevel-4 405.6m 404.7m ~ 1.000
_prepareTxsPerLevelOrdered-4 3.591m 3.893m ~ 0.100
_prepareTxsPerLevel_Comparison/Original-4 422.9m 414.4m ~ 0.400
_prepareTxsPerLevel_Comparison/Optimized-4 3.577m 3.677m ~ 0.700
SubtreeSizes/10k_tx_4_per_subtree-4 1.312m 1.325m ~ 1.000
SubtreeSizes/10k_tx_16_per_subtree-4 317.9µ 310.9µ ~ 0.400
SubtreeSizes/10k_tx_64_per_subtree-4 76.18µ 76.01µ ~ 1.000
SubtreeSizes/10k_tx_256_per_subtree-4 19.11µ 19.06µ ~ 1.000
SubtreeSizes/10k_tx_512_per_subtree-4 9.447µ 9.391µ ~ 0.200
SubtreeSizes/10k_tx_1024_per_subtree-4 4.624µ 4.648µ ~ 0.700
SubtreeSizes/10k_tx_2k_per_subtree-4 2.319µ 2.304µ ~ 1.000
BlockSizeScaling/10k_tx_64_per_subtree-4 73.94µ 73.78µ ~ 1.000
BlockSizeScaling/10k_tx_256_per_subtree-4 18.54µ 18.76µ ~ 0.300
BlockSizeScaling/10k_tx_1024_per_subtree-4 4.612µ 4.631µ ~ 0.200
BlockSizeScaling/50k_tx_64_per_subtree-4 386.4µ 390.6µ ~ 0.400
BlockSizeScaling/50k_tx_256_per_subtree-4 93.78µ 92.88µ ~ 0.400
BlockSizeScaling/50k_tx_1024_per_subtree-4 22.75µ 22.80µ ~ 0.700
SubtreeAllocations/small_subtrees_exists_check-4 154.6µ 156.7µ ~ 0.700
SubtreeAllocations/small_subtrees_data_fetch-4 162.6µ 162.8µ ~ 1.000
SubtreeAllocations/small_subtrees_full_validation-4 317.9µ 320.6µ ~ 0.100
SubtreeAllocations/medium_subtrees_exists_check-4 9.245µ 9.276µ ~ 1.000
SubtreeAllocations/medium_subtrees_data_fetch-4 9.591µ 9.682µ ~ 0.100
SubtreeAllocations/medium_subtrees_full_validation-4 18.62µ 18.62µ ~ 1.000
SubtreeAllocations/large_subtrees_exists_check-4 2.208µ 2.216µ ~ 1.000
SubtreeAllocations/large_subtrees_data_fetch-4 2.371µ 2.350µ ~ 0.400
SubtreeAllocations/large_subtrees_full_validation-4 4.632µ 4.649µ ~ 0.100
StoreBlock_Sequential/BelowCSVHeight-4 310.0µ 324.0µ ~ 0.400
StoreBlock_Sequential/AboveCSVHeight-4 314.2µ 310.2µ ~ 0.100
GetUtxoHashes-4 259.4n 261.8n ~ 0.700
GetUtxoHashes_ManyOutputs-4 42.76µ 42.51µ ~ 0.400
_NewMetaDataFromBytes-4 236.9n 235.8n ~ 0.200
_Bytes-4 609.9n 605.5n ~ 0.400
_MetaBytes-4 558.4n 563.6n ~ 0.400

Threshold: >10% with p < 0.05 | Generated: 2026-04-21 13:52 UTC

@oskarszoon oskarszoon merged commit 6ea6120 into bsv-blockchain:main Apr 21, 2026
25 checks passed
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