Skip to content

feat(tbtc): covenant signer final project branch#3882

Draft
mswilkison wants to merge 100 commits intomainfrom
feat/psbt-covenant-final-project-pr
Draft

feat(tbtc): covenant signer final project branch#3882
mswilkison wants to merge 100 commits intomainfrom
feat/psbt-covenant-final-project-pr

Conversation

@mswilkison
Copy link

@mswilkison mswilkison commented Mar 9, 2026

This is the draft final project PR for the keep-core side of the PSBT covenant extension.

It accumulates the keep-core covenant signer work for the current threshold ECDSA signer set before a final merge to main.

Current contents:

  • covenant signer substrate

This PR is intentionally draft and will be updated as the remaining keep-core covenant work lands.

## Summary
- add a new `pkg/covenantsigner` substrate package with durable
submit/poll semantics for covenant signer jobs
- wire an optional `covenantSigner.port` HTTP server into `keep start`
backed by work persistence
- add route-aware request validation, job dedup by `routeRequestId`,
health endpoint, and targeted tests

## Scope
This is the first keep-core covenant signer extension slice only:
- common covenant job domain
- persistent store
- HTTP+JSON signer-provider surface
- request validation and idempotency

It does **not** yet implement the real signer cryptography / artifact
generation path. The default engine accepts jobs and leaves them pending
until the signing internals are plugged in by follow-up work.

## Verification
- `go test ./pkg/covenantsigner`
- `go test ./config ./cmd`
## Summary
- extend the covenant signer request contract with a concrete migration
destination reservation artifact
- validate reservation script hash, migration extraData encoding, and
canonical destination commitment hash inside keep-core
- add fixed-vector and mismatch coverage so later real signing can rely
on a verified destination artifact instead of only an opaque hash

## Why now
The new tBTC covenant migration destination reservation service is
merged on the covenant project branch. The keep-core signer substrate
now needs to consume that concrete artifact so later self_v1/qc_v1
signing slices can fail closed on destination mismatch before
implementing real tx construction.

## Verification
- `go test ./pkg/covenantsigner`
- `go test ./config`
- `go test ./cmd`
## Summary
- require a concrete migration transaction plan alongside the
reservation artifact
- validate canonical Leaf-1 pre-signed policy fields in keep-core before
real signing work
- add focused negative coverage for bad plan inputs and HTTP submit
payloads

## Testing
- go test ./pkg/covenantsigner ./config ./cmd
## Summary
- wire the covenant signer server to a real engine from tbtc instead of
the passive stub
- add the first real self_v1 signer path that fetches the active
outpoint, verifies the reserved migration destination and transaction
plan, signs the canonical maturity spend, and returns READY with
transactionHex + deterministic artifact hash
- extend the bitcoin transaction builder with explicit
locktime/sequence/witness controls and add end-to-end signer coverage

## Testing
- go test ./pkg/bitcoin ./pkg/covenantsigner
- go test ./pkg/tbtc -run TestCovenantSignerEngine_SubmitSelfV1Ready -v
- go test ./config ./cmd
## Summary
- add a real qc_v1 signer path in keep-core that produces signer handoff
bundles
- build and sign the canonical unsigned migration spend, then return a
typed handoff instead of a final artifact
- cover the new handoff path with direct pkg/tbtc tests

## Testing
- go test ./pkg/tbtc -run focused covenant signer tests
- go test ./pkg/bitcoin ./pkg/covenantsigner ./config ./cmd
## Summary
- default the covenant signer HTTP listener to loopback and require a
bearer auth token for non-loopback exposure
- stop holding the submit mutex across engine.OnSubmit so deduped
callers are not serialized behind long signer work
- add focused tests for auth enforcement, config flags, and in-flight
submit dedupe semantics

## Testing
- go test ./pkg/covenantsigner -count=1
- go test ./pkg/tbtc -run 'TestCovenantSignerEngine_' -count=1
- go test ./cmd -count=1
piotr-roslaniec and others added 30 commits March 16, 2026 20:02
Move the duplicated marshalCanonicalJSON function from
pkg/covenantsigner/validation.go and pkg/tbtc/signer_approval_certificate.go
into a shared pkg/internal/canonicaljson package. The shared implementation
uses bytes.TrimSuffix (the more precise variant) and includes cross-package
equivalence tests covering map inputs, struct inputs, no trailing newline,
HTML non-escaping, and deterministic map key ordering.

The two original implementations differed textually (TrimSuffix vs TrimSpace)
though they produced identical output in practice. Consolidating eliminates
the latent divergence risk.
Replace the hardcoded minimumActiveOutpointConfirmations = 1 with a
configurable MinActiveOutpointConfirmations field in the covenant signer
config. Default to 6 when unset, aligning with
DepositSweepRequiredFundingTxConfirmations used elsewhere in the tBTC
subsystem. The previous 1-confirmation threshold accepted active outpoints
vulnerable to Bitcoin reorgs; CLTV constrains spend height, not reorg depth.

Adds 6 new tests covering default application, custom override, zero-value
fallback, and confirmation rejection behavior.
…tore corruption

Add advisory file locking (flock) to the covenant signer store to prevent
multiple processes from simultaneously writing to the same data directory.

- Add DataDir config field and WithDataDir service option
- Acquire LOCK_EX|LOCK_NB on startup; fail fast if another process holds it
- Add Store.Close() and Service.Close() for lock release on shutdown
- Reorder NewStore creation to run after service options are applied
- Skip locking for in-memory handles (empty dataDir)
Prepend "covenant-signer-request-v1:" domain prefix to the SHA256 input
in requestDigestFromNormalized to prevent cross-context hash collisions
with other SHA256-based identifiers in the protocol.

- Add covenantSignerRequestDigestDomain constant
- Update requestDigestFromNormalized to use domain-prefixed hashing
- Update test vectors to match new domain-separated digests
- Add TestRequestDigestUsesDomainSeparation verifying prefix effect
Use context.WithoutCancel in submitHandler so threshold signing survives
HTTP write-timeout expiration and client disconnects.

- Detach context passed to service.Submit from r.Context() cancellation
- Add tests verifying context survives cancellation, pre-cancelled
  contexts still succeed, and context values are preserved
…ism, and clearer assertions

Replace basic Marshal tests with comprehensive coverage: map input,
struct input, trailing newline check, HTML non-escaping, and
deterministic key ordering with repeated-call idempotency verification.
…ff payload hash

Verify computeQcV1SignerHandoffPayloadHash produces a stable pinned hash
and that json.Marshal preserves alphabetical key ordering for map inputs.
Document single-node-per-wallet deployment model, load balancer sticky
session requirements, node-local request deduplication scope, P2P signing
session convergence behavior, and wallet ownership guard limitations.
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.

3 participants