Skip to content

tapgarden: support caller-defined genesis anchor PSBTs - #2238

Open
SadiqJaf wants to merge 34 commits into
lightninglabs:mainfrom
SadiqJaf:codex/issue-721-custom-genesis-psbt
Open

SadiqJaf wants to merge 34 commits into
lightninglabs:mainfrom
SadiqJaf:codex/issue-721-custom-genesis-psbt

Conversation

@SadiqJaf

Copy link
Copy Markdown

Summary

This adds a caller-controlled Bitcoin anchor PSBT flow for minting batches.

  • FundBatch can accept a funded anchor PSBT plus explicit asset-anchor, change, and supply-precommit output indexes.
  • PrepareBatch commits the asset tree and pauses before Bitcoin publication so an external signer can finalize caller-controlled inputs.
  • FinalizeBatch validates and merges the exact signed transaction, imports the anchor, persists the publication boundary, and resumes confirmation/proof handling.
  • Wallet-owned inputs are leased across the external-signing pause; external inputs remain supported and are never unlocked by tapd.
  • Pending, Frozen, Committed, ImportPending, PublishPending, and Broadcast restart behavior is fail-closed and preserves immutable transaction state.
  • Runtime lease/publication/key-repair health is exposed through mint batch status.
  • Historical custom-anchor managed-key rows are repaired only when the locator and wallet ownership are independently verified.
  • A live integration test covers a foreign two-party MuSig2 input, a preserved successor output, a wallet-owned asset anchor at vout 1, external plus wallet signing, confirmation/proof verification, and a post-mint asset spend.

Safety properties

  • A prepared batch cannot be mutated by adding seedlings, re-funding, sealing, or preparing again.
  • Import completes before the durable publication transition.
  • Once publication may have occurred, the signed transaction remains immutable, watched, non-cancellable, and retried byte-for-byte.
  • Ambiguous, conflict, missing-input, and policy errors cannot roll the batch back into a mutable state.
  • Local input leases are acquired atomically, renewed during signing/recovery, and released only after a durable cancellation or terminal boundary.
  • Caller PSBT maps and proprietary fields are preserved; tapd metadata is isolated and validated.
  • Wallet key locators are never synthesized or silently downgraded.

Compatibility and scope

The default minting path is unchanged.

This initial custom-anchor implementation requires the selected asset-anchor internal key to be owned by the backing lnd wallet. Arbitrary external or NUMS anchor internal keys require a different custody, spending, backup, and recovery model and are tracked separately in #2237.

Validation

  • go test -p 1 ./tapgarden ./tapdb ./lndservices ./rpcserver ./tapnode/tapnodemock -count=1
  • go test -race ./tapgarden -run '^(TestIssue721|TestCustomAnchorHistoricalKeyRepair)' -count=1 -timeout=180s
  • go test ./itest -run '^TestCustomAnchor(ForeignInputPreserved|SparseWalletCommitment|AuthorityCommitmentBindsFields|RegtestBits)$' -count=1
  • go test -tags=itest ./itest -run '^$' -count=1
  • Selected live integration harness: passed mint, confirmation, proof verification, and post-mint spend
  • git diff --check

Fixes #721

@SadiqJaf

SadiqJaf commented Aug 21, 2026 •

Copy link
Copy Markdown
Author

Follow-up e648a343 fixes the CI-reported custom-anchor data races and lint violations. Local gates pass: the two exact race regressions, full ^TestIssue721 under -race, and ./tapgarden ./tapdb ./tapnode ./lndservices.

…tom-genesis-psbt

# Conflicts:
#	docs/release-notes/release-notes-0.9.0.md
#	lndservices/chain_bridge_test.go
#	tapdb/sqlc/querier.go
Use MintingBatch.Copy to avoid copying atomic state, cover staging isolation and error paths, and wrap wallet-anchor lines to satisfy CI lint.
@SadiqJaf

SadiqJaf commented Sep 5, 2026

Copy link
Copy Markdown
Author

Fork CI rehearsal passed at 7cc9a17 against upstream main 4466280, using unchanged workflows and an identical merge tree: CI — 40 jobs passed and backward compatibility passed. The fork-only release-note check was skipped; the unchanged checker passed separately using PR number 2238. Ready for maintainer approval of official upstream CI; these rehearsal results do not replace upstream checks.

Integrate upstream cancellation lease cleanup through a shared dispatcher. Preserve batch-scoped custom leases and never unlock foreign custom-anchor inputs through the ordinary wallet fallback. Add mixed-input and all-foreign cancellation regressions.

Signed-off-by: Sadiq Jaffer <sadiq.jaffer@satscryption.io>
@SadiqJaf

SadiqJaf commented Sep 8, 2026 •

Copy link
Copy Markdown
Author

Synced with upstream main at 643f8714c18f459720af9e02b0874ea0d25d4670 through history-preserving merge 542192bf3cd3274212a5e2c8dd982c5f763fdb36.

The cancellation conflicts with #2262 are resolved through one lease-release dispatcher. Ordinary wallet-funded batches retain upstream cleanup. Custom anchors release only recorded leases using the batch-specific owner ID; foreign inputs never reach the ordinary wallet-unlock fallback. Added mixed-input and all-foreign cancellation regressions. Publication/cancellation boundaries and immutable signed transactions are unchanged.

Validation before updating this PR:

  • Affected-package tests passed: tapgarden, tapdb, lndservices, rpcserver, tapnode/tapnodemock.
  • Targeted batch-cancellation tests and expanded Issue [feature]: Enhance caretaker w/ ability to set user-defined UTXOs and GenesisAmtSats values #721 cancellation/lease lifecycle race tests passed.
  • CI-pinned golangci-lint v2.10.1: 0 issues; formatting and diff checks passed.
  • Fork rehearsal PR: CI succeeded with 40 successful jobs, covering unit/race, PostgreSQL, integration, remote signer, custom channels, generation, Docker and cross-compilation checks. Backward compatibility also passed.
  • CI checked out merge df3141a4d0742a151b0cc85e70e97990d320e4a7; its tree 65bdbe252305da307d0d1eef263fc81d117cead4 equals the local candidate tree. Upstream base and the original PR head were rechecked immediately before this fast-forward update.

Failure history: attempt 1 had one PostgreSQL integration partition fail during daemon startup near the harness's 10-second readiness deadline. The retained logs show incomplete migrations/connection closure and one node opening RPC just after the deadline. The failed-only retry passed on the identical source and binary artifacts, without timeout or workflow changes. The initial failure remains recorded on the rehearsal PR; the underlying resource/timing cause is not conclusively established.

The rehearsal retains its existing fork-only release-note metadata skip because its PR number differs from upstream #2238. The unchanged release-note script passed locally with GITHUB_REF=refs/pull/2238/merge. No workflow definitions or validation checks were weakened. Fork results do not replace upstream maintainer approval or official upstream checks. No force push or PR merge was performed.

Preserve both custom-anchor key repair queries and upstream orphan-UTXO candidate selection at their shared SQL insertion point. Keep generated bindings and upstream orphan-validation regressions intact.

Signed-off-by: Sadiq Jaffer <sadiq.jaffer@satscryption.io>
@SadiqJaf

SadiqJaf commented Sep 9, 2026

Copy link
Copy Markdown
Author

Resolved the new merge conflict after upstream #2265 and fast-forwarded this PR to c78723d2fb6b89f4c1ca05542f7df2796d436725, incorporating upstream 7deef4bd8bf7ca8888f062c9cb05f03822f68931.

The conflict was two independent additions at the same position in tapdb/sqlc/queries/assets.sql. The merge retains both custom-anchor repair queries and upstream's orphan-UTXO candidate query, without changing either's SQL semantics. SQLC 1.29.0 regeneration reproduces the merged bindings exactly. Existing custom-anchor behavior, upstream orphan-validation regressions, and workflow definitions are preserved.

Validation before this update:

  • Full local tapdb and tapgarden suites passed (204.655s / 213.499s).
  • Race-enabled orphan-query and custom-anchor persistence regressions passed (27.411s).
  • SQL generation, formatting, diff checks and pinned golangci-lint v2.10.1 on tapdb passed (0 issues).
  • Fork CI passed on attempt 1: 40 successful jobs, including SQLite/PostgreSQL integration, remote signer, custom channels, coverage and race tests. No failed-job retries.
  • Backward compatibility passed.

The fork's existing release-note metadata job remains skipped because its PR number differs; the unchanged checker passed locally using GITHUB_REF=refs/pull/2238/merge. Redundant events from updating fork base/head, and this sync's separate fork-main run, were cancelled and are not counted as validation passes. Full rehearsal history is retained in fork PR #1.

The tested fork merge 190b3243f81c44258eb935fde561045e6f93da3e, the local candidate and this PR's current merge d20faac291516d68ee4660e4cc91d06d07f562ae all have tree 374dc5b26fe9f78baee0725f7a3c7bd864998acd. The SQLC CI checkout log confirms the fork merge revision. Upstream base/source refs were refreshed and checked immediately before the fast-forward update.

GitHub now reports this PR as conflict-free (MERGEABLE). Official upstream checks and maintainer requirements remain separate. No force push or PR merge was performed; Satscrip branches/worktrees were untouched.

SadiqJaf and others added 3 commits September 14, 2026 21:49
Integrate upstream cd3253d through a history-preserving merge. Retain custom genesis anchor changes alongside upstream proof encoding, grouped receive, invoice validation, integration coverage, and dependency updates.

Signed-off-by: Sadiq Jaffer <sadiq.jaffer@satscryption.io>
Merge upstream 9bf78a5. Preserve custom-anchor publication retries and batch-scoped lease renewal after registering the new mint anchoring watcher, and release only recorded custom leases on durable abandonment. Retain legacy confirmation behavior and immutable signed packets. Regenerate SQL and RPC bindings with pinned tools and add watcher failure, confirmation, retry and abandonment regressions.

Signed-off-by: Sadiq Jaffer <sadiq.jaffer@satscryption.io>
Merge upstream main (93420d4) into
codex/issue-721-custom-genesis-psbt (d85ccfd). The PR's 33 commits
are preserved. Upstream's re-org watcher rework removed the cultivator's
legacy RegisterConfirmationsNtfn path, so confirmation is learned only
by staking a mint anchoring and waiting on its delivered phase.

Conflict resolutions:

tapgarden/cultivator.go
- Drop proofsWatched. It only gated ProofWatcher.WatchProofs on the
  legacy confirmation path, which upstream deleted. Nothing else reads
  it. UpdateMintingProofs is dropped with it.
- Broadcast keeps upstream's anchoring-only flow: registerMintAnchoring
  before any Broadcast publish, then one goroutine waits on the
  registry. Re-apply the custom-genesis trace log (isCustomAnchorPsbt)
  and publishBroadcast. An earlier Committed-state publish may already
  have relayed the transaction, so a later publish error must not cancel
  confirmation tracking or return the batch to a mutable state.
  publishBroadcast renews custom-anchor leases and treats an ambiguous
  wallet rejection as still-watched. Non-custom publishes still fail the
  state step when PublishTransaction fails.

tapgarden/planter.go
- Cultivator config keeps upstream's anchoring waiter wiring and drops
  UpdateMintingProofs. CustomAnchorLeaseRenewalInterval is still passed
  through so Broadcast retries and lease renewal keep the PR's cadence.

tapgarden/planter_test.go
- mintAnchorings is upstream's trigger-outpoint lookup. assertTxPublished
  only waits on PublishReq. The legacy ConfReqSignal handshake is gone.
  assertAnchoringRegistered also rejects a non-zero confirmation
  subscription count.

bench/fixture/mint_driver.go
- The pump confirms a published genesis transaction on the mock re-org
  watcher (upstream). It no longer correlates ConfReqSignal with
  publishes.

itest/assertions.go
- AssertAssetsMintedAtOutpoint keeps the PR's outpoint check
  (AssetAnchorOutpointCheck, so a non-zero asset anchor index is
  asserted) and upstream's scriptKeyLocalCheck (external script keys are
  not required to be wallet-local).

Tests:
- Issue 721 stateful tests wait on mint anchorings instead of
  ConfReqSignal, deliver confirmation through confirmAnchoring, and
  fail registration with MockRegistrar.FailNextRegister. PauseNextRegister
  holds Register after the anchoring is stored so a lease fault can be
  injected before publishBroadcast. DropAnchorings covers a Broadcast
  batch whose anchoring was not persisted across restart.

Co-authored-by: Sadiq Jaffer <SadiqJaf@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🆕 New

Development

Successfully merging this pull request may close these issues.

[feature]: Enhance caretaker w/ ability to set user-defined UTXOs and GenesisAmtSats values

2 participants