Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
eb14768
feat(tbtc): add covenant signer job substrate
mswilkison Mar 9, 2026
d0998da
fix(tbtc): harden covenant signer substrate
mswilkison Mar 9, 2026
e71fdbb
feat(tbtc): add covenant signer job substrate (#3881)
mswilkison Mar 9, 2026
a5e084d
feat(tbtc): validate migration destination reservation artifacts
mswilkison Mar 9, 2026
12c11e4
test(tbtc): expand reservation artifact validation coverage
mswilkison Mar 9, 2026
1c364ac
feat(tbtc): validate migration destination reservation artifacts (#3883)
mswilkison Mar 9, 2026
d073f88
feat(tbtc): validate covenant migration transaction plan
mswilkison Mar 9, 2026
a5e2864
docs(tbtc): note transaction-plan rollout dependency
mswilkison Mar 9, 2026
858705b
feat(tbtc): validate covenant migration transaction plan (#3884)
mswilkison Mar 9, 2026
2db6226
feat(tbtc): implement self_v1 signer completion
mswilkison Mar 9, 2026
8cc87cd
fix(tbtc): harden self_v1 signer validation
mswilkison Mar 9, 2026
1e1dbc7
feat(tbtc): implement self_v1 signer completion (#3885)
mswilkison Mar 9, 2026
38987f9
feat(tbtc): add qc_v1 signer handoff
mswilkison Mar 9, 2026
2ea4e58
fix(tbtc): repair covenant signer project branch CI
mswilkison Mar 9, 2026
9e15a5d
test(tbtc): tighten qc_v1 handoff coverage
mswilkison Mar 9, 2026
dc12dd2
feat(tbtc): add qc_v1 signer handoff (#3886)
mswilkison Mar 9, 2026
0d64dcc
fix(tbtc): gofmt covenant signer
mswilkison Mar 9, 2026
d4e83a3
Harden covenant signer service exposure
mswilkison Mar 9, 2026
d738861
Verify canonical migration plan commitments
mswilkison Mar 9, 2026
2d1366a
Fix signer poll race and locktime bounds
mswilkison Mar 10, 2026
21b6381
Fix gosec G118 findings
mswilkison Mar 10, 2026
bd3f203
Harden covenant signer route controls
mswilkison Mar 10, 2026
6baadb2
[PSBT Covenant] Harden covenant signer service exposure (#3887)
mswilkison Mar 10, 2026
a246ad0
Validate role-tagged covenant approvals
mswilkison Mar 12, 2026
57eafa6
Normalize covenant signer request digests
mswilkison Mar 12, 2026
866dc0a
Harden covenant signer request digests
mswilkison Mar 12, 2026
7470fca
Add covenant signer contract vectors
mswilkison Mar 12, 2026
04971dd
Add mixed-case covenant signer coverage
mswilkison Mar 12, 2026
29f0756
Verify depositor and custodian approval signatures
mswilkison Mar 12, 2026
d65c8a4
Clarify signer approval deferral and canonicalize commitment JSON
mswilkison Mar 12, 2026
9195543
Verify migration plan quotes in covenant signer
mswilkison Mar 13, 2026
5994e52
Add migration plan quote verification vectors
mswilkison Mar 13, 2026
0329fd8
Spike signer approval certificates
mswilkison Mar 13, 2026
13adc30
Bind signer approval cert to on-chain wallet identity
mswilkison Mar 13, 2026
4568657
Verify structured covenant signer approvals
mswilkison Mar 13, 2026
90e59e6
Require structured signer approval on engine path
mswilkison Mar 13, 2026
da023c4
Tighten signer approval cutover validation
mswilkison Mar 13, 2026
a6054d2
Warn on signer verifier-less startup
mswilkison Mar 13, 2026
2186c3d
Remove legacy signer approval role path
mswilkison Mar 13, 2026
38e6773
Harden signer approval verifier boundary
mswilkison Mar 13, 2026
7d4beff
Validate role-tagged covenant approvals (#3890)
mswilkison Mar 13, 2026
909c824
Pin covenant approval trust roots
mswilkison Mar 13, 2026
d367c8e
Tighten covenant approval trust roots
mswilkison Mar 13, 2026
d9b493d
Pin covenant approval trust roots (#3899)
mswilkison Mar 13, 2026
e56e0b7
Merge remote-tracking branch 'origin/codex/psbt-covenant-signer-appro…
mswilkison Mar 13, 2026
7a2f299
Differentiate self_v1 presign and reconstruction requests
mswilkison Mar 13, 2026
351bee9
Add self_v1 presign request vectors
mswilkison Mar 13, 2026
dac9bc3
Run gofmt on covenant signer files
mswilkison Mar 13, 2026
ad3d0cb
Differentiate self_v1 presign and reconstruction requests (#3900)
mswilkison Mar 13, 2026
9639373
Merge branch 'feat/psbt-covenant-final-project-pr' of https://github.…
mswilkison Mar 13, 2026
cecf08f
Require covenant approval trust roots in production mode
mswilkison Mar 14, 2026
df9f316
Require covenant approval trust roots in production mode (#3901)
mswilkison Mar 14, 2026
7c34a9c
fix(covenantsigner): add HTTP timeouts and body size cap
piotr-roslaniec Mar 16, 2026
c3bc500
fix(covenantsigner): set explicit max header size
piotr-roslaniec Mar 16, 2026
7f84b94
fix(tbtc): require signer set hash in certificate verification
piotr-roslaniec Mar 16, 2026
acecf4c
fix(tbtc): canonicalize signer set hash payload JSON
piotr-roslaniec Mar 16, 2026
40d434b
fix(bitcoin): guard tx output index bounds in getScript
piotr-roslaniec Mar 16, 2026
14e2272
fix(tbtc): guard against empty witness stack
piotr-roslaniec Mar 16, 2026
1d3928d
fix(covenantsigner): return 405 for poll path method mismatch
piotr-roslaniec Mar 16, 2026
590e4d9
fix(covenantsigner): return generic JSON decode errors
piotr-roslaniec Mar 16, 2026
9edc9ce
fix(covenantsigner): reject unknown JSON envelope fields
piotr-roslaniec Mar 16, 2026
c2bd723
fix(covenantsigner): unescape poll request ID before slash validation
piotr-roslaniec Mar 16, 2026
27efef7
fix(covenantsigner): validate facade and idempotency identifiers
piotr-roslaniec Mar 16, 2026
ff09f1c
fix(covenantsigner): error on unsupported submit route
piotr-roslaniec Mar 16, 2026
aed5b13
refactor(covenantsigner): replace variadic validation options with ex…
piotr-roslaniec Mar 16, 2026
aec52a6
fix(covenantsigner): make store replacement write-first
piotr-roslaniec Mar 16, 2026
f439645
test: align unknown-field behavior and fix go1.24 fmt vet issue
piotr-roslaniec Mar 16, 2026
6a3a831
chore: open follow-up branch for feat/psbt-covenant-final-project-pr
piotr-roslaniec Mar 16, 2026
815a09b
ci(client): add race detector job for covenant signer and tbtc
piotr-roslaniec Mar 16, 2026
64f96b5
test(covenantsigner): add HTTP boundary error matrix coverage
piotr-roslaniec Mar 16, 2026
c724705
test(covenantsigner): add store durability fault-injection coverage
piotr-roslaniec Mar 16, 2026
24854b5
test(cmd): add fail-fast startup tests with injected init handles
piotr-roslaniec Mar 16, 2026
7cd92d6
test(crypto): add negative parsing coverage for signer approvals and …
piotr-roslaniec Mar 16, 2026
18b46ad
ci(client): gate race checks on high-risk path changes
piotr-roslaniec Mar 16, 2026
458e09e
test(tbtc): cover poll no-op and unsupported route transitions
piotr-roslaniec Mar 16, 2026
def205f
test(chain/ethereum): add tbtc contract config and wallet-state error…
piotr-roslaniec Mar 16, 2026
2c8cadb
test(fuzz): add decoder fuzz targets for signer validation paths
piotr-roslaniec Mar 16, 2026
7970929
test(covenantsigner): split store and server suites into thematic files
piotr-roslaniec Mar 16, 2026
178b578
ci(client): scope race checks to stable tbtc tests
piotr-roslaniec Mar 17, 2026
5863464
fix(covenantsigner): parse UpdatedAt for store dedup ordering
piotr-roslaniec Mar 17, 2026
bd8f804
fix(covenantsigner): reject trailing JSON tokens in decodeJSON
piotr-roslaniec Mar 17, 2026
03ada74
fix(covenantsigner): decouple poll digest from trust-root drift
piotr-roslaniec Mar 17, 2026
1b6a3c8
fix(tbtc): classify wallet-not-found errors with sentinel
piotr-roslaniec Mar 17, 2026
0fb8ac5
refactor(tbtc): share covenant build-and-sign flow
piotr-roslaniec Mar 17, 2026
ab8c251
fix(tbtc): require active outpoint confirmation before signing
piotr-roslaniec Mar 17, 2026
b23fc5a
docs(tbtc): document uncompressed signer-approval key format
piotr-roslaniec Mar 17, 2026
a318999
refactor(cmd): inject start dependencies without mutable globals
piotr-roslaniec Mar 17, 2026
f1883de
fix(covenantsigner): require signer approval verifier in strict mode
piotr-roslaniec Mar 17, 2026
d40deee
fix(covenantsigner): harden request dedupe and signature canonicaliza…
piotr-roslaniec Mar 17, 2026
b159f9f
style(cmd): apply gofmt to start dependency wiring
piotr-roslaniec Mar 17, 2026
d3e5184
test(covenantsigner): stabilize invalid UpdatedAt assertion
piotr-roslaniec Mar 17, 2026
7a9b88d
chore: follow-up branch into feat/psbt-covenant-final-project-pr (#3903)
piotr-roslaniec Mar 17, 2026
5941949
refactor(covenantsigner): extract shared marshalCanonicalJSON utility
lrsaturnino Mar 19, 2026
86cfc66
feat(covenantsigner): make active outpoint confirmations configurable
lrsaturnino Mar 19, 2026
de3e745
feat(covenantsigner): add exclusive file lock to prevent concurrent s…
lrsaturnino Mar 19, 2026
3606c29
fix(covenantsigner): add domain separation to request digest
lrsaturnino Mar 19, 2026
6d12e8f
fix(covenantsigner): detach submit context from HTTP lifecycle
lrsaturnino Mar 19, 2026
7562a65
test(canonicaljson): expand Marshal tests with struct input, determin…
lrsaturnino Mar 19, 2026
054ef08
test(tbtc): add deterministic key ordering test for QcV1 signer hando…
lrsaturnino Mar 19, 2026
99b79f5
docs(covenantsigner): add deployment topology and coordination guide
lrsaturnino Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ jobs:
- './config/_electrum_urls/**'
- './pkg/bitcoin/electrum/**'

client-risk-detect-changes:
runs-on: ubuntu-latest
outputs:
path-filter: ${{ steps.filter.outputs.path-filter }}
steps:
- uses: actions/checkout@v4
if: github.event_name == 'pull_request'

- uses: dorny/paths-filter@v2
if: github.event_name == 'pull_request'
id: filter
with:
filters: |
path-filter:
- './pkg/covenantsigner/**'
- './pkg/tbtc/**'
- './pkg/chain/ethereum/**'
- './cmd/start.go'

client-build-test-publish:
needs: client-detect-changes
if: |
Expand Down Expand Up @@ -301,6 +320,24 @@ jobs:
install-go: false
checks: "-SA1019"

client-race:
needs: client-risk-detect-changes
if: |
github.event_name == 'push'
|| needs.client-risk-detect-changes.outputs.path-filter == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Race detector (high-risk packages)
run: |
go test -race -timeout 20m ./pkg/covenantsigner
go test -race -timeout 20m ./pkg/tbtc \
-run '^(TestSignerApprovalCertificateSignerSetHashBindsOnChainWalletIdentityAndThreshold|TestValidateMigrationOutputValues_RejectsValuesExceedingInt64)$' \
-count=1

client-integration-test:
needs: [electrum-integration-detect-changes, client-build-test-publish]
if: |
Expand Down
36 changes: 36 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/keep-network/keep-core/pkg/bitcoin/electrum"
chainEthereum "github.com/keep-network/keep-core/pkg/chain/ethereum"
"github.com/keep-network/keep-core/pkg/clientinfo"
"github.com/keep-network/keep-core/pkg/covenantsigner"
"github.com/keep-network/keep-core/pkg/maintainer/spv"
"github.com/keep-network/keep-core/pkg/net/libp2p"
"github.com/keep-network/keep-core/pkg/tbtc"
Expand Down Expand Up @@ -46,6 +47,8 @@ func initFlags(
initStorageFlags(cmd, cfg)
case config.ClientInfo:
initClientInfoFlags(cmd, cfg)
case config.CovenantSigner:
initCovenantSignerFlags(cmd, cfg)
case config.Tbtc:
initTbtcFlags(cmd, cfg)
case config.Maintainer:
Expand Down Expand Up @@ -310,6 +313,39 @@ func initTbtcFlags(cmd *cobra.Command, cfg *config.Config) {
)
}

func initCovenantSignerFlags(cmd *cobra.Command, cfg *config.Config) {
cmd.Flags().IntVar(
&cfg.CovenantSigner.Port,
"covenantSigner.port",
covenantsigner.Config{}.Port,
"Covenant signer provider HTTP server listening port. Zero disables the service.",
)
cmd.Flags().StringVar(
&cfg.CovenantSigner.ListenAddress,
"covenantSigner.listenAddress",
covenantsigner.DefaultListenAddress,
"Covenant signer provider HTTP listen address. Defaults to loopback-only.",
)
cmd.Flags().StringVar(
&cfg.CovenantSigner.AuthToken,
"covenantSigner.authToken",
covenantsigner.Config{}.AuthToken,
"Covenant signer provider static Bearer auth token. Required for non-loopback binds; prefer config file or env var over CLI in production.",
)
cmd.Flags().BoolVar(
&cfg.CovenantSigner.EnableSelfV1,
"covenantSigner.enableSelfV1",
false,
"Expose self_v1 covenant signer HTTP routes. Keep disabled for a qc_v1-first launch unless self_v1 is explicitly approved.",
)
cmd.Flags().BoolVar(
&cfg.CovenantSigner.RequireApprovalTrustRoots,
"covenantSigner.requireApprovalTrustRoots",
false,
"Fail startup when enabled covenant routes are missing route-level approval trust roots. Request-time validation still enforces exact reserve/network trust-root matches.",
)
}

// Initialize flags for Maintainer configuration.
func initMaintainerFlags(command *cobra.Command, cfg *config.Config) {
command.Flags().BoolVar(
Expand Down
36 changes: 36 additions & 0 deletions cmd/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
ethereumEcdsa "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen"
ethereumTbtc "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen"
ethereumThreshold "github.com/keep-network/keep-core/pkg/chain/ethereum/threshold/gen"
"github.com/keep-network/keep-core/pkg/covenantsigner"
)

var cmdFlagsTests = map[string]struct {
Expand Down Expand Up @@ -190,6 +191,41 @@ var cmdFlagsTests = map[string]struct {
expectedValueFromFlag: 76 * time.Second,
defaultValue: 10 * time.Minute,
},
"covenantSigner.port": {
readValueFunc: func(c *config.Config) interface{} { return c.CovenantSigner.Port },
flagName: "--covenantSigner.port",
flagValue: "9711",
expectedValueFromFlag: 9711,
defaultValue: 0,
},
"covenantSigner.listenAddress": {
readValueFunc: func(c *config.Config) interface{} { return c.CovenantSigner.ListenAddress },
flagName: "--covenantSigner.listenAddress",
flagValue: "0.0.0.0",
expectedValueFromFlag: "0.0.0.0",
defaultValue: covenantsigner.DefaultListenAddress,
},
"covenantSigner.authToken": {
readValueFunc: func(c *config.Config) interface{} { return c.CovenantSigner.AuthToken },
flagName: "--covenantSigner.authToken",
flagValue: "secret-token",
expectedValueFromFlag: "secret-token",
defaultValue: "",
},
"covenantSigner.enableSelfV1": {
readValueFunc: func(c *config.Config) interface{} { return c.CovenantSigner.EnableSelfV1 },
flagName: "--covenantSigner.enableSelfV1",
flagValue: "",
expectedValueFromFlag: true,
defaultValue: false,
},
"covenantSigner.requireApprovalTrustRoots": {
readValueFunc: func(c *config.Config) interface{} { return c.CovenantSigner.RequireApprovalTrustRoots },
flagName: "--covenantSigner.requireApprovalTrustRoots",
flagValue: "",
expectedValueFromFlag: true,
defaultValue: false,
},
"tbtc.preParamsPoolSize": {
readValueFunc: func(c *config.Config) interface{} { return c.Tbtc.PreParamsPoolSize },
flagName: "--tbtc.preParamsPoolSize",
Expand Down
104 changes: 97 additions & 7 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"fmt"
"time"

commonEthereum "github.com/keep-network/keep-common/pkg/chain/ethereum"
"github.com/keep-network/keep-core/pkg/tbtcpg"

"github.com/keep-network/keep-common/pkg/persistence"
"github.com/keep-network/keep-core/build"
"github.com/keep-network/keep-core/pkg/bitcoin"
"github.com/keep-network/keep-core/pkg/bitcoin/electrum"
"github.com/keep-network/keep-core/pkg/operator"
"github.com/keep-network/keep-core/pkg/storage"
Expand All @@ -17,9 +19,11 @@ import (

"github.com/keep-network/keep-core/config"
"github.com/keep-network/keep-core/pkg/beacon"
beaconchain "github.com/keep-network/keep-core/pkg/beacon/chain"
"github.com/keep-network/keep-core/pkg/chain"
"github.com/keep-network/keep-core/pkg/chain/ethereum"
"github.com/keep-network/keep-core/pkg/clientinfo"
"github.com/keep-network/keep-core/pkg/covenantsigner"
"github.com/keep-network/keep-core/pkg/firewall"
"github.com/keep-network/keep-core/pkg/generator"
"github.com/keep-network/keep-core/pkg/net"
Expand All @@ -45,6 +49,77 @@ var StartCommand = &cobra.Command{
},
}

type startDeps struct {
connectEthereum func(
ctx context.Context,
config commonEthereum.Config,
) (
*ethereum.BeaconChain,
*ethereum.TbtcChain,
chain.BlockCounter,
chain.Signing,
*operator.PrivateKey,
error,
)
connectElectrum func(
ctx context.Context,
config electrum.Config,
) (bitcoin.Chain, error)
initializeNetwork func(
ctx context.Context,
applications []firewall.Application,
operatorPrivateKey *operator.PrivateKey,
blockCounter chain.BlockCounter,
) (net.Provider, error)
initializePersistence func() (
beaconKeyStorePersistence persistence.ProtectedHandle,
tbtcKeyStorePersistence persistence.ProtectedHandle,
tbtcDataPersistence persistence.BasicHandle,
err error,
)
initializeBeacon func(
ctx context.Context,
chain beaconchain.Interface,
netProvider net.Provider,
keyStorePersistence persistence.ProtectedHandle,
scheduler *generator.Scheduler,
) error
initializeTbtc func(
ctx context.Context,
chain tbtc.Chain,
btcChain bitcoin.Chain,
netProvider net.Provider,
keyStorePersistance persistence.ProtectedHandle,
workPersistence persistence.BasicHandle,
scheduler *generator.Scheduler,
proposalGenerator tbtc.CoordinationProposalGenerator,
config tbtc.Config,
clientInfoRegistry *clientinfo.Registry,
perfMetrics *clientinfo.PerformanceMetrics,
minActiveOutpointConfirmations uint,
) (covenantsigner.Engine, error)
initializeSigner func(
ctx context.Context,
config covenantsigner.Config,
handle persistence.BasicHandle,
engine covenantsigner.Engine,
) (*covenantsigner.Server, bool, error)
startScheduler func() *generator.Scheduler
}

func defaultStartDeps() startDeps {
return startDeps{
connectEthereum: ethereum.Connect,
connectElectrum: electrum.Connect,
initializeNetwork: initializeNetwork,
initializePersistence: initializePersistence,
initializeBeacon: beacon.Initialize,
initializeTbtc: tbtc.Initialize,
initializeSigner: covenantsigner.Initialize,
startScheduler: generator.StartScheduler,
}
}

func init() {
initFlags(StartCommand, &configFilePath, clientConfig, config.StartCmdCategories...)

Expand All @@ -63,15 +138,19 @@ Environment variables:

// start starts a node
func start(cmd *cobra.Command) error {
return startWithDeps(cmd, defaultStartDeps())
}

func startWithDeps(cmd *cobra.Command, deps startDeps) error {
ctx := context.Background()

beaconChain, tbtcChain, blockCounter, signing, operatorPrivateKey, err :=
ethereum.Connect(ctx, clientConfig.Ethereum)
deps.connectEthereum(ctx, clientConfig.Ethereum)
if err != nil {
return fmt.Errorf("error connecting to Ethereum node: [%v]", err)
}

netProvider, err := initializeNetwork(
netProvider, err := deps.initializeNetwork(
ctx,
[]firewall.Application{beaconChain, tbtcChain},
operatorPrivateKey,
Expand Down Expand Up @@ -110,20 +189,20 @@ func start(cmd *cobra.Command) error {
// Skip initialization for bootstrap nodes as they are only used for network
// discovery.
if !isBootstrap() {
btcChain, err := electrum.Connect(ctx, clientConfig.Bitcoin.Electrum)
btcChain, err := deps.connectElectrum(ctx, clientConfig.Bitcoin.Electrum)
if err != nil {
return fmt.Errorf("could not connect to Electrum chain: [%v]", err)
}

beaconKeyStorePersistence,
tbtcKeyStorePersistence,
tbtcDataPersistence,
err := initializePersistence()
err := deps.initializePersistence()
if err != nil {
return fmt.Errorf("cannot initialize persistence: [%w]", err)
}

scheduler := generator.StartScheduler()
scheduler := deps.startScheduler()

if clientInfoRegistry != nil {
clientInfoRegistry.ObserveBtcConnectivity(
Expand All @@ -142,7 +221,7 @@ func start(cmd *cobra.Command) error {
rpcHealthChecker.Start(ctx)
}

err = beacon.Initialize(
err = deps.initializeBeacon(
ctx,
beaconChain,
netProvider,
Expand All @@ -158,7 +237,7 @@ func start(cmd *cobra.Command) error {
btcChain,
)

err = tbtc.Initialize(
covenantSignerEngine, err := deps.initializeTbtc(
ctx,
tbtcChain,
btcChain,
Expand All @@ -170,10 +249,21 @@ func start(cmd *cobra.Command) error {
clientConfig.Tbtc,
clientInfoRegistry,
perfMetrics, // Pass the existing performance metrics instance to avoid duplicate registrations
clientConfig.CovenantSigner.MinActiveOutpointConfirmations,
)
if err != nil {
return fmt.Errorf("error initializing TBTC: [%v]", err)
}

_, _, err = deps.initializeSigner(
ctx,
clientConfig.CovenantSigner,
tbtcDataPersistence,
covenantSignerEngine,
)
if err != nil {
return fmt.Errorf("error initializing covenant signer: [%v]", err)
}
}

nodeHeader(
Expand Down
Loading
Loading