Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2e3570e
feat(tbtc): UTXO reservation wallet-side foundations
mswilkison Aug 7, 2026
77fca94
fix(tbtcpg): align GetRedemptionParameters call with struct return
mswilkison Aug 7, 2026
7826468
fix(tbtc): validate reservation proposal payloads
mswilkison Aug 9, 2026
62b18c1
fix(tbtc): bind reservations to action generations
mswilkison Aug 9, 2026
b4f6394
fix(tbtc): bind dissolution inputs to action snapshot
mswilkison Aug 9, 2026
277865c
feat(tbtc): regenerate ABI bindings for reservation router surface
piotr-roslaniec Aug 26, 2026
e49e954
feat(tbtc): implement reservation read/validate methods against real …
piotr-roslaniec Aug 26, 2026
4ba2f22
feat(tbtc): add reservation write methods, additional views, and even…
Aug 26, 2026
0535779
feat(tbtc): add reservation methods to tbtcpg and spv Chain interfaces
piotr-roslaniec Aug 26, 2026
ac58650
feat(tbtc): implement reservation watchers
Aug 26, 2026
e37211b
feat(tbtc): implement reservation acceptance executor
piotr-roslaniec Aug 26, 2026
603ad0d
feat(tbtc): implement reservation re-anchor executor
piotr-roslaniec Aug 26, 2026
2bd8731
Merge branch 'm1/h-acceptance' into m1/keep-core-client
piotr-roslaniec Aug 26, 2026
daeb6af
Merge branch 'm1/h-reanchor' into m1/keep-core-client
piotr-roslaniec Aug 26, 2026
c29de0b
merge: parallel builders for acceptance, re-anchor, watchers
piotr-roslaniec Aug 26, 2026
4898545
feat(tbtc): wire reservation executors and watchers
piotr-roslaniec Aug 26, 2026
5daf210
fix: add context to GetTransactionConfirmations call in reservation a…
piotr-roslaniec Aug 27, 2026
b14a388
merge: pull in reservations-epic base updates (bitcoin.Chain context-…
piotr-roslaniec Aug 27, 2026
20cb160
fix(tbtc,spv): wire reservation watchers to real chain calls and add …
piotr-roslaniec Sep 1, 2026
1de2631
fix(clientinfo): gate reservation metrics registration behind config …
piotr-roslaniec Sep 1, 2026
753d717
test(ethereum): add unit tests for reservation ABI conversion functions
piotr-roslaniec Sep 1, 2026
cb8ad1a
fix(tbtcpg): reservation acceptance/re-anchor correctness fixes and c…
piotr-roslaniec Sep 1, 2026
d75ae12
fix(tbtc): dispatch reservation coordination results, gate on activat…
piotr-roslaniec Sep 1, 2026
29ce21a
fix(ethereum): remove dead reservation router API, add conversion tes…
piotr-roslaniec Sep 1, 2026
4bfa523
fix(clientinfo): split reservation metric names from the always-regis…
piotr-roslaniec Sep 1, 2026
3eda058
fix(spv): fix reservation watcher notifications, proof loop, and wiri…
piotr-roslaniec Sep 1, 2026
c48d17b
fix(tbtcpg): additional reservation acceptance/reanchor fixes and cov…
piotr-roslaniec Sep 1, 2026
757c6d8
fix(tbtc): resolve review findings across coordination, reservation, …
piotr-roslaniec Sep 1, 2026
85af2bd
fix(tbtcpg): guard reservation acceptance/reanchor tasks against stuc…
piotr-roslaniec Sep 1, 2026
e202c2c
fix(spv): correct reservation watcher ordering, RPC scan cost, and wi…
piotr-roslaniec Sep 1, 2026
7054614
fix(client): remove dead reservation router API surface, wire watcher…
piotr-roslaniec Sep 1, 2026
5149267
fix(tbtcpg): rewrite reservation acceptance task stateless
piotr-roslaniec Sep 2, 2026
b91c818
fix(tbtcpg): fix reservation reanchor target-wallet cache and dead check
piotr-roslaniec Sep 2, 2026
0f39259
test(tbtcpg): cover reservation tx max-fee boundary
piotr-roslaniec Sep 2, 2026
4fdb9bb
fix(spv): tighten reservation proof transaction matching
piotr-roslaniec Sep 2, 2026
09c03b0
fix(spv): bound reservation action timeout wallet scan
piotr-roslaniec Sep 2, 2026
695590d
fix(spv): fix stale-deposit watch fallback and nonce lookup
piotr-roslaniec Sep 2, 2026
dbf4ade
fix(spv): make reservation watcher startup catch-up non-fatal
piotr-roslaniec Sep 2, 2026
1a43292
fix(tbtc): fix past-due activation block and doc/overflow issues
piotr-roslaniec Sep 2, 2026
0358cc7
ci(tbtc): unblock ReservationRouter bindings via tbtc-v2 PR #1112 shim
piotr-roslaniec Sep 2, 2026
91f8c59
ci(tbtc): pin Node 22 for the tbtc-v2 shim build
piotr-roslaniec Sep 2, 2026
e6d0bfe
ci(tbtc): drop the tbtc-v2 shim's local deploy step
piotr-roslaniec Sep 2, 2026
d66c7b2
ci(tbtc): treat empty ENVIRONMENT as development in the shim guard
piotr-roslaniec Sep 2, 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
39 changes: 39 additions & 0 deletions .github/workflows/client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,45 @@ jobs:
with:
environment: ${{ github.event.inputs.environment }}

# TODO(https://github.com/threshold-network/tbtc-v2/pull/1112): remove once
# @keep-network/tbtc-v2 publishes the reservation-router surface (Bridge,
# WalletProposalValidator, RedemptionWatchtower, ReservationRouter, and the
# rest of the tbtc module's required_contracts) under the `development` npm
# tag. Until then, PRs targeting `reservations-epic` compile tbtc-v2 PR #1112
# (pinned SHA, not a moving branch ref) locally to produce the artifacts
# `make get_artifacts` can't fetch from npm yet. `environment=development`
# only ever reads the `.abi` field off these files (the gen Makefile writes
# a hardcoded zero address rather than reading one - see
# pkg/chain/ethereum/common/gen/Makefile's `_address/%` rule), so a plain
# `hardhat compile` artifact is sufficient; no local deployment needed. See
# ./ci-shims/tbtc-artifacts and the matching Dockerfile step.
- name: Prepare tbtc-v2 artifact shim directory
run: mkdir -p ci-shims/tbtc-artifacts

- name: Set up Node.js for tbtc-v2 shim build
if: github.base_ref == 'reservations-epic'
uses: actions/setup-node@v4
with:
node-version: "22.23.1"

- name: Build tbtc-v2 module artifacts from PR #1112 (temporary shim)
if: github.base_ref == 'reservations-epic'
run: |
set -euo pipefail
git clone --quiet https://github.com/threshold-network/tbtc-v2.git /tmp/tbtc-v2-shim
cd /tmp/tbtc-v2-shim
git checkout --quiet 1c8c1cd1437c077700b372544677aa0f9b08ef87
cd solidity
corepack enable
git config --global url."https://".insteadOf git://
yarn install --immutable
yarn build
for contract in Bridge MaintainerProxy LightRelay LightRelayMaintainerProxy \
WalletProposalValidator RedemptionWatchtower ReservationRouter; do
artifact="$(find build/contracts -iname "$contract.json" -path "*/$contract.sol/*")"
cp "$artifact" "$GITHUB_WORKSPACE/ci-shims/tbtc-artifacts/$contract.json"
done

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
# Executables
/keep-client

# Temporary CI-only artifact injected before the Docker build; see Dockerfile
# and .github/workflows/client.yml (ReservationRouter tbtc-v2 PR #1112 shim).
/ci-shims/

# IDEs
.vscode/
.idea/
Expand Down
22 changes: 21 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,31 @@ COPY ./pkg/protocol/inactivity/gen $APP_DIR/pkg/protocol/inactivity/gen
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.32.0

# Environment is to download published and tagged NPM packages versions.
ARG ENVIRONMENT
# Defaults to `development` to mirror the root Makefile's `ifndef environment`
# fallback (the "Build Docker Build Image" CI step never passes this build-arg).
ARG ENVIRONMENT=development

COPY ./Makefile $APP_DIR/Makefile
RUN make get_artifacts environment=$ENVIRONMENT

# TODO(https://github.com/threshold-network/tbtc-v2/pull/1112): remove once
# @keep-network/tbtc-v2 publishes Bridge/WalletProposalValidator/RedemptionWatchtower/
# ReservationRouter (and the rest of the tbtc module's required_contracts) under the
# `development` npm tag. Until then, `get_artifacts` fetches a tbtc-v2 package whose
# Bridge/WalletProposalValidator/RedemptionWatchtower don't yet expose the reservation
# methods this PR binds against, and has no ReservationRouter artifact at all. The
# `client.yml` workflow locally compiles tbtc-v2 PR #1112 (pinned SHA) and drops its
# compiled ABI artifacts for the tbtc module's required_contracts at
# ./ci-shims/tbtc-artifacts/*.json when it runs; this only overrides the tbtc module's
# artifacts, and only for `environment=development` (PR CI) builds - sepolia/mainnet
# builds and the beacon/ecdsa/threshold modules are untouched.
COPY ./ci-shims/tbtc-artifacts /tmp/tbtc-artifacts
RUN if { [ -z "$ENVIRONMENT" ] || [ "$ENVIRONMENT" = "development" ]; } && [ -n "$(ls -A /tmp/tbtc-artifacts 2>/dev/null)" ]; then \
echo "Using tbtc-v2 module artifacts built from tbtc-v2 PR #1112 (temporary shim)"; \
cp /tmp/tbtc-artifacts/*.json \
$APP_DIR/tmp/contracts/development/@keep-network/tbtc-v2/artifacts/; \
fi

# Need this to resolve imports in generated Ethereum commands.
COPY ./config $APP_DIR/config
RUN make generate environment=$ENVIRONMENT
Expand Down
35 changes: 32 additions & 3 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"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/electrum"
"github.com/keep-network/keep-core/pkg/operator"
Expand All @@ -22,6 +23,7 @@ import (
"github.com/keep-network/keep-core/pkg/clientinfo"
"github.com/keep-network/keep-core/pkg/firewall"
"github.com/keep-network/keep-core/pkg/generator"
"github.com/keep-network/keep-core/pkg/maintainer/spv"
"github.com/keep-network/keep-core/pkg/net"
"github.com/keep-network/keep-core/pkg/net/libp2p"
"github.com/keep-network/keep-core/pkg/net/retransmission"
Expand Down Expand Up @@ -92,7 +94,11 @@ func start(cmd *cobra.Command) error {
// Wire performance metrics into network provider if available
var perfMetrics *clientinfo.PerformanceMetrics
if clientInfoRegistry != nil {
perfMetrics = clientinfo.NewPerformanceMetrics(ctx, clientInfoRegistry)
perfMetrics = clientinfo.NewPerformanceMetrics(
ctx,
clientInfoRegistry,
clientConfig.Tbtc.Reservations.Enabled,
)
// Type assert to libp2p provider to set metrics recorder
// The provider struct is not exported, so we use interface assertion
if setter, ok := netProvider.(interface {
Expand Down Expand Up @@ -160,9 +166,10 @@ func start(cmd *cobra.Command) error {
proposalGenerator := tbtcpg.NewProposalGenerator(
tbtcChain,
btcChain,
clientConfig.Tbtc.Reservations.Enabled,
)

err = tbtc.Initialize(
resolver, err := tbtc.Initialize(
ctx,
tbtcChain,
btcChain,
Expand All @@ -177,7 +184,29 @@ func start(cmd *cobra.Command) error {
clientConfig.Ethereum.Network,
)
if err != nil {
return fmt.Errorf("error initializing TBTC: [%v]", err)
return fmt.Errorf("cannot initialize TBTC: [%v]", err)
}

// Wire the reservation watchers (stranding, stale-deposit,
// action-timeout) directly against the same tbtcChain handle:
// cmd/start.go already imports both tbtc and spv, so there is no
// import-cycle reason to thread this through tbtc.Initialize via a
// callback type. Gated on the same flag that gates the reservation
// proposal generator tasks above. Failing to wire the watchers is
// fatal: the operator opted into reservations, so a missing
// watcher would silently strand anchors.
if clientConfig.Tbtc.Reservations.Enabled {
if err := spv.WireReservationWatchers(
ctx,
tbtcChain,
tbtcChain,
resolver,
); err != nil {
return fmt.Errorf(
"failed to wire reservation watchers: [%v]",
err,
)
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ func TestReadConfigFromFile(t *testing.T) {
readValueFunc: func(c *Config) interface{} { return c.Maintainer.Spv.IdleBackoffTime },
expectedValue: 15 * time.Minute,
},
"Maintainer.Spv.Reservations.Enabled": {
readValueFunc: func(c *Config) interface{} { return c.Maintainer.Spv.Reservations.Enabled },
expectedValue: true,
},
"Tbtc.Reservations.Enabled": {
readValueFunc: func(c *Config) interface{} { return c.Tbtc.Reservations.Enabled },
expectedValue: true,
},
}

for _, filePath := range filePaths {
Expand Down
Loading
Loading