Skip to content

domain: extract ChainDepthAndParentMarkers helper (part of #1159) - #1160

Open
bitcoin-coder-bob wants to merge 4 commits into
masterfrom
bob/onchain-arkade-depth-helper
Open

domain: extract ChainDepthAndParentMarkers helper (part of #1159)#1160
bitcoin-coder-bob wants to merge 4 commits into
masterfrom
bob/onchain-arkade-depth-helper

Conversation

@bitcoin-coder-bob

@bitcoin-coder-bob bitcoin-coder-bob commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

First slice of #1159 (on-chain Arkade execution).

Extracts the chain-depth + parent-marker computation into one pure domain helper, domain.ChainDepthAndParentMarkers, and routes the two existing off-chain call sites through it (SubmitOffchainTx and the offchain update handler). Behaviour-preserving cleanup: one computation, one place, so the two call sites cannot drift.

Deliberately the smallest decision-independent piece of the epic, no fee-strategy, discriminator or schema decision is involved.

Update after the #1159 design call: the on-chain scope no longer extends Depth and the marker DAG to on-chain txs (the mempool already provides that ancestry), so this stands purely on its off-chain merit rather than as groundwork for on-chain chaining. Unaffected either way, it only touches existing off-chain paths.

Behaviour note (not strictly byte-identical)

The update handler previously computed depth as an unconditional max(parent depths) + 1, which yields 1 for an empty spent set, while the authoritative accept path yields 0. The helper uses the accept-path (0) semantics. That edge is unreachable, a finalized offchain tx always spends at least one vtxo, and 0 is the correct value, but calling it out rather than claiming no behaviour change.

Test plan

  • go build ./...
  • go test ./internal/core/domain/... (new TestChainDepthAndParentMarkers, 4 cases)
  • go test ./internal/core/application/... (offchain flow stays green, proving behaviour-preserving)
  • make lint (0 issues), gofmt clean

Status

Independent of the rest of the #1159 stack and of every open decision there; merge whenever. If it lands after #1162, that PR needs a master merge, since both edit SubmitOffchainTx.

Summary by CodeRabbit

  • Bug Fixes

    • Improved consistency when calculating offchain transaction depth and parent markers.
    • Ensured parent markers are deduplicated, sorted, and empty values are excluded.
    • Correctly handles transactions with no parent markers while preserving depth calculations.
    • Improves the accuracy and consistency of accepted offchain transaction details.
  • Tests

    • Added coverage for empty inputs, nested parent depths, duplicate markers, sorting, and parents without markers.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 3c8f7eb5-8fa9-44cf-890c-9a4f43ef27f1

📥 Commits

Reviewing files that changed from the base of the PR and between dbd27e0 and 207eb99.

📒 Files selected for processing (1)
  • internal/core/application/service.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/core/application/service.go

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

The change adds a shared domain helper for VTXO chain depth and parent markers. Offchain transaction finalization and submission use the helper. Tests cover depth calculation, marker filtering, deduplication, and sorting.

Changes

VTXO chain metadata

Layer / File(s) Summary
Chain metadata helper and validation
internal/core/domain/marker.go, internal/core/domain/marker_test.go
Adds ChainDepthAndParentMarkers, which returns the computed depth and sorted, deduplicated non-empty parent markers. Tests cover empty, nested, duplicate, sorted, and missing-marker inputs.
Application service integration
internal/core/application/service.go
Uses the shared helper in offchain transaction finalization and submission.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 207eb

This change centralizes offchain chain-depth and parent-marker calculation while preserving the authoritative empty-spent-set depth behavior. No current merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: extracting the ChainDepthAndParentMarkers helper into the domain package. It is concise and relevant.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bob/onchain-arkade-depth-helper

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed at head 7cd9a54 — LGTM.

Verified

  • ChainDepthAndParentMarkers in internal/core/domain/marker.go:39-63 preserves the existing semantics exactly: depth is max(parent depths) + 1 when spent is non-empty, 0 otherwise; parents are deduped, empty strings dropped, output sorted for determinism.
  • Both call sites now go through the helper: service.go (event handler at ~L313, SubmitOffchainTx at ~L1077). Diffed the behaviour — sort.Strings at SubmitOffchainTx moves inside the helper (same result), and the event-handler path previously did no marker inheritance (dropped) — this PR now inherits parent markers in that path too. That's a behavioural change worth calling out in the PR body: on the OffchainTxAccepted / settlement event path, new vtxos will now carry inherited MarkerIDs. If that's intentional (which reads that way from the docstring), please confirm; if not, the event-handler branch should still drop them.
  • sort import removed from service.go since the last use moved to the helper. Clean.
  • Tests cover: empty spent → depth 0 / no markers; single parent inheritance; multi-parent dedup + sort + empty-drop; parents-with-no-markers → depth-only. Boundary cases are all pinned.

Nit

  • Docstring says "single source of truth for the computation recorded on the OffchainTxAccepted event." Since after this PR the same helper is also used from the event handler path that assigns marker IDs to new vtxos (not just the accepted event), the phrasing understates its role. Minor.

Part of the #1159 series — merge order will matter for the domain type additions that follow (VtxoKind, ChainDepthAndParentMarkers, single-spend). No cross-repo impact: ChainDepthAndParentMarkers is a Go domain helper, no wire surface.

Pull the chain-depth and parent-marker computation out of SubmitOffchainTx and
the offchain update handler into one pure domain helper, so the two call sites
cannot drift and on-chain paths (issue #1159) can reuse the same computation.

Behaviour-preserving on the reachable paths. The update handler previously did
an unconditional max+1, yielding depth 1 for an empty spent set, while the
authoritative accept path yields 0; the helper uses the accept-path (0)
semantics. That edge is unreachable, a finalized offchain tx always spends at
least one vtxo, and 0 is the correct value.

Part of #1159.
@bitcoin-coder-bob
bitcoin-coder-bob force-pushed the bob/onchain-arkade-depth-helper branch from 740b0cb to 5c818e9 Compare July 30, 2026 14:41
@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 3+ days without review. @bitcoin-coder-bob is anyone looking at this?

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 14 days without review. @bitcoin-coder-bob is anyone looking at this?

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana review — sha 5c818e9

Looks ready to merge.

ChainDepthAndParentMarkers is a clean extraction of duplicated logic that existed in both service.go::registerEventHandlers and service.go::SubmitOffchainTx. The implementations were almost identical but the event-handler path previously omitted the parent-marker accumulation (it only tracked depth). The new shared helper is the full version; the event handler intentionally discards the markers return value (depth, _ := domain.ChainDepthAndParentMarkers(spentVtxos)), which matches the old behavior.

Tests are comprehensive: nil input, single parent, max-depth selection, dedup/sort of marker IDs, and dropped empty strings. The sorting guarantees a deterministic wire format regardless of input order.

No concerns.

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review — domain: extract ChainDepthAndParentMarkers helper

What changed:
Extracts inline depth+marker computation from two call sites in into . Reduces duplication and makes the rule a single source of truth.

Subtle behaviour change in registerEventHandlers: The old inline code did newVtxos[i].Depth = maxDepth + 1 without guarding on len(spentVtxos) > 0, so an event with no spent vtxos (e.g. boarding) would set depth=1. The new helper correctly returns depth=0 when len(spent)==0. This is the right behaviour — a vtxo with no parents should be at depth 0. The SubmitOffchainTx path already had the if len(spentVtxos) > 0 guard, so it was consistent; registerEventHandlers is now fixed to match.

Tests: TestChainDepthAndParentMarkers covers empty input (depth 0), single parent, multiple parents with dedup+sort, and empty marker strings dropped. Complete.

Looks ready to merge.

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review — ChainDepthAndParentMarkers helper

Verdict: looks ready to merge.

Clean extraction of the repeated depth + parent-marker computation that existed in two places (service.go:SubmitOffchainTx and service.go:registerEventHandlers) into a single, tested domain function.

  • The semantics are identical to what both call sites did inline: max(parent depths) + 1, deduplicated sorted parent marker IDs with empty strings dropped.
  • The zero-input case (depth 0, no markers) is explicit and correct.
  • Test coverage is thorough: nil input, single parent, multiple-parent dedup+sort+empty-drop, no-marker parents.
  • Removing the inline sort.Strings import from service.go is a nice hygiene win.

No concerns.

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 5+ days without a review. @bitcoin-coder-bob is anyone looking at this? (ChainDepthAndParentMarkers helper (part of #1159); 5 days without review.)

@bitcoin-coder-bob
bitcoin-coder-bob marked this pull request as ready for review September 3, 2026 17:44

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana review — domain: extract ChainDepthAndParentMarkers helper

Not protocol-critical. No VTXO signing, forfeit, round lifecycle, or unilateral exit paths are touched.


Summary

Clean, well-scoped refactoring. The new domain.ChainDepthAndParentMarkers correctly captures the logic that previously lived inline in SubmitOffchainTx, and the two call sites now share a single source of truth. Test coverage is adequate for the helper.


Findings

1. Behavior delta in the event handler is reachable (though safe) — internal/core/application/service.go

The PR correctly discloses the semantic difference: old handler did maxDepth + 1 unconditionally (yielding depth 1 for an empty spent set); the new helper returns 0 for empty input.

The claim that the empty path is unreachable is almost right, but the guard at line 320 (len(spentVtxos) != len(spentVtxoKeys)) only fires when the counts differ. If decodeTx returns an empty spentVtxoKeys, both slices are length-zero, the guard passes, and execution continues with depth = 0. The protocol invariant that a finalized offchain tx always spends ≥ 1 VTXO is real, but it's enforced upstream (at submission time), not here. A future code path that re-uses RegisterOffchainTxUpdateHandler with an edge-case tx could hit this. 0 is the correct value either way, so this is not a bug, just worth documenting. An inline comment on the depth, _ := line explaining why 0 is safe (depth-from-empty is unreachable because Accept rejects zero-input txs) would serve future readers.

2. Silent _ discard of parent markers in the event handler — internal/core/application/service.go

depth, _ := domain.ChainDepthAndParentMarkers(spentVtxos)

Correct: MarkerIDs are already set on the VTXOs during offchainTx.Accept() in SubmitOffchainTx; the finalization event handler is a post-projection callback that only needs to propagate depth to the decoded newVtxos. The discard is intentional and sound. A brief comment (// MarkerIDs already set during Accept; only depth propagated here) would prevent someone from incorrectly "fixing" this discard later.

3. Test coverage — internal/core/domain/marker_test.go

The four cases cover the key behaviors (zero input, single parent, deduplication/sorting/empty-string dropping, depth-only parents). One gap: no test asserts uint32 overflow behavior when maxDepth == math.MaxUint32. Since Depth is uint32 and the increment is unchecked, a chain long enough to wrap would silently emit depth 0. The MarkerInterval of 100 means a wrapping chain would need ~42 million hops, which is implausibly deep for off-chain operation, but a comment in the function or a //nolint with rationale would make the deliberate non-handling explicit. Not a blocker at current scale.

4. Cross-repo impact

ChainDepthAndParentMarkers is new and unexported outside the arkd module boundary. No SDK repos reference it. No interface or proto changes. No cross-repo breakage.

5. Conflict note

The PR body flags a potential merge conflict with #1162 (both edit SubmitOffchainTx). That's a merge coordination concern, not a correctness one. The logic here is encapsulated in the helper so the conflict, when it arises, is mechanical.


Verdict

No blocking issues. The two comments above (explaining the _ discard and the zero-input semantics) are minor polish suggestions, not change requests. Mechanically correct refactoring; safe to merge once the #1162 merge order is resolved.

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Follow-up pass @ 207eb99 — one commit since last review (comments only); 1 prior open item fixed, 0 remain open.


Prior-issue close-out

Behavioural change: marker inheritance in the event-handler path (raised in review at 7cd9a543) — Fixed in 207eb99.

The commit adds an explicit comment block at service.go:334-342 that documents the design decision: marker IDs are intentionally dropped in the event-handler path because the projection of the OffchainTxAccepted event already assigns MarkerIDs to new vtxos from ParentMarkerIDs recorded at Accept. Only depth needs to be carried forward here. The comment also documents why the empty-spent-set depth=0 case is unreachable in practice. Both concerns I raised are now addressed in code.

Nit (docstring scope in marker.go) — Still open, still minor. The ChainDepthAndParentMarkers docstring still says "single source of truth for the computation recorded on the OffchainTxAccepted event," which understates its role now that it serves both the accept path and the settlement event path. The new service.go comment provides the context, so this is readable, just slightly imprecise at the definition site.


Incremental findings (207eb99)

None. The commit is documentation only — no logic, no interface, no protocol surface changed.


Status: No blocking issues. The one open nit (marker.go docstring wording) is cosmetic and can be cleaned up in a follow-on if desired. Code is otherwise clean.

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 41+ days without a review. @bitcoin-coder-bob — ChainDepthAndParentMarkers helper: stacked — is it waiting on #1161 or #1159?

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