fix: validate tx graph finality - #273
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. Walkthrough
ChangesTransaction graph finality
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: 🔵 Low · up to The PR rejects transaction graphs with non-zero locktime or non-final input sequences before signing, improving admission safety without changing well-formed graphs. Merge is reasonable with explicit owner awareness that external graph producers must emit the stricter values or risk bounded availability failures in signing or recovery flows. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
|
CI red here is pre-existing and unrelated to this change — flagging so it isn't mistaken for a regression. All three failures (
Locally, with the repo's documented workaround from
Unblocking CI needs either a |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Review — fix: validate tx graph finality
Reviewed at head 58a906d. Scope: ark-core/src/tx_graph.rs (117 added lines, 0 deleted). Cross-repo impact checked against arkd, ts-sdk, go-sdk, dotnet-sdk.
Protocol / correctness
validate_finality logic is sound. lock_time == 0 and every input.sequence == Sequence::MAX (0xFFFFFFFF) is the strictest possible finality condition — it guarantees tree nodes are unconditionally broadcastable regardless of chain state and explicitly opts out of both BIP68 relative timelocks and RBF. Confirmed correct for the protocol: arkd/pkg/ark-lib/tree/builder.go:228 constructs every VTXO-tree node with psbt.New(…, 3, 0, []uint32{wire.MaxTxInSequenceNum}), so well-formed operator graphs will always pass.
Early validation is the right placement. The check sits inside the chunks loop before chunks_by_txid.insert, so every chunk is screened before graph assembly begins. A malformed node deep in the tree cannot survive by being unreachable in the traversal.
Conservative lock_time rejection. A tx with lock_time != 0 but all sequences at MAX would be immediately broadcastable under Bitcoin consensus (MAX sequence disables lock_time). This PR rejects it anyway — which is intentional defence-in-depth: if a signing bug later produces a non-MAX sequence, a non-zero lock_time that was previously masked would suddenly make the graph unbroadcastable exactly when needed. The restriction is appropriate here; the error message will guide operators to the offending tx.
Minor observations (no changes required)
Zero-input chunk passes silently (tx_graph.rs, new validate_finality): the for input in &tx.unsigned_tx.input loop is a no-op when the input list is empty, so a hypothetical input-less chunk would pass finality checks. Such a tx would be consensus-invalid and would never be produced by the operator builder, so this is not actionable — just worth being aware of.
Cross-SDK inconsistency: ts-sdk/src/tree/validation.ts (validateVtxoTxGraph) and dotnet-sdk/NArk.Core/Batches/TreeValidator.cs (ValidateVtxoTxGraph) do not have equivalent finality checks. This is not a regression introduced here, but it means TS/dotnet clients currently accept graphs with non-zero locktimes or non-MAX sequences. Whether to mirror the check in those SDKs is a separate decision.
Tests
Four tests covering accept, reject-locktime, reject-sequence-on-root, and reject-sequence-on-non-root-chunk — the happy path and all three rejection branches. Error message assertions verify both the offending value and the txid, which is the right granularity. Mutation test described in the PR body (commenting out the check breaks the three reject tests, passes the accept test) is a good sanity signal.
Danger false-positive: CI flagged "source changed with no test changes" — 95 lines of new tests are clearly present. The warning can be ignored.
Verdict
Implementation is correct, tests are thorough, cross-repo impact is nil (private method, no API surface change). No protocol paths (VTXO handling, signing, forfeit, round lifecycle, unilateral exit) are modified. No changes requested.
|
Can you rebase to get the CI green please? |
TxGraph::newchecked structure and node count but never that a node is actually broadcastable. It now also requires a zero locktime and a final sequence on every chunk.The check sits in the chunk-ingestion loop, so it covers every chunk before any graph assembly rather than only the reachable nodes. The operator-side builder already emits exactly these values, so well-formed graphs are unaffected.
Test plan
cargo test --workspace --exclude e2e-tests: 279 passed, 0 failed (baseline 275/0 — delta is exactly the four new tests).cargo clippy --all-targets --all-featuresclean.Summary by CodeRabbit