Skip to content

feat: add RootHashPadded for fixed-height subtree composition#121

Merged
mrz1836 merged 4 commits into
bsv-blockchain:masterfrom
ordishs:feat/root-hash-padded
May 13, 2026
Merged

feat: add RootHashPadded for fixed-height subtree composition#121
mrz1836 merged 4 commits into
bsv-blockchain:masterfrom
ordishs:feat/root-hash-padded

Conversation

@ordishs

@ordishs ordishs commented May 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds Subtree.RootHashPadded(targetHeight int) (*chainhash.Hash, error) — a method that computes the subtree's merkle root and then lifts it to a caller-specified height by repeatedly hashing the root with itself (H(prev, prev) per phantom level). This is bitcoin's "duplicate-last-when-odd" rule applied to phantom slots above the actual leaves.

The motivation is to make subtree-root composition produce the same merkle root as building a single big merkle tree over all leaves, even when the last subtree is partial. Today, combining a complete subtree's root with a partial subtree's un-padded root yields a different value than the bitcoin merkle root over the same transactions — RootHashPadded closes that gap. Consumer (Teranode) intends to use it for block validation where one block carries multiple fixed-height subtrees and the final one may be incomplete.

What's in the PR

  • errors.go: two new sentinels — ErrTargetHeightTooSmall, ErrNotPowerOfTwoLeafCount.
  • subtree.go: the RootHashPadded method, placed adjacent to RootHash.
  • subtree_padded_test.go: seven tests covering the happy path, no-lift, empty-subtree, both error paths, an 8+2-leaves big-tree equivalence check, and a block-scale 258-tx (256+2 composition) equivalence check.

Purely additive — no existing behaviour changes.

Preconditions enforced by RootHashPadded

  • len(Nodes) must be a power of two (or zero) → otherwise ErrNotPowerOfTwoLeafCount.
  • targetHeight >= ceil(log2(len(Nodes))) → otherwise ErrTargetHeightTooSmall.
  • Empty subtree: returns (nil, nil) (matching RootHash's nil-for-empty contract; guarded by a //nolint:nilnil directive with explanation).

Test plan

  • go test -race -count=1 ./... — 76 tests pass, 0 failures
  • go vet ./... — clean
  • golangci-lint run --new-from-rev=master — 0 issues
  • go build ./... — clean

Note on archive status

I see the master README now carries an archive notice pointing to the go-stack monorepo. Happy to redirect this to go-stack if maintainers prefer — let me know. Submitting here first since (a) the repo isn't actually archived yet and (b) downstream consumers' go.mod still points here.

@ordishs ordishs requested a review from mrz1836 as a code owner May 13, 2026 14:50
@github-actions github-actions Bot added fork-pr PR originated from a forked repository requires-manual-review PR or issue requires manual review by a maintainer or security team labels May 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

👋 Thanks, @ordishs!

This pull request comes from a fork. For security, our CI runs in a restricted mode.
A maintainer will triage this shortly and run any additional checks as needed.

  • 🏷️ Labeled: fork-pr, requires-manual-review
  • 👀 We'll review and follow up here if anything else is needed.

Thanks for contributing to bsv-blockchain/go-subtree! 🚀

@mrz1836 mrz1836 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@mrz1836 mrz1836 merged commit a5dd727 into bsv-blockchain:master May 13, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork-pr PR originated from a forked repository requires-manual-review PR or issue requires manual review by a maintainer or security team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants