Skip to content

Security release/candidate 1 - #4109

Open
lionakhnazarov wants to merge 446 commits into
threshold-network:mainfrom
lionakhnazarov:security-release/candidate-1
Open

Security release/candidate 1#4109
lionakhnazarov wants to merge 446 commits into
threshold-network:mainfrom
lionakhnazarov:security-release/candidate-1

Conversation

@lionakhnazarov

@lionakhnazarov lionakhnazarov commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Integration candidate for the coordinated security release (sr-candidate-1).

Merges:

Head: 971f60358 (rebased onto main @ 038b7ced1; geth/Go CVE bumps restored)
tss-lib pin: github.com/threshold-network/tss-lib@v0.0.0-20260615180949-86bd1a375cc0 (86bd1a3)
Deps: go-ethereum v1.17.3, keep-common v1.7.1-tlabs.1, go 1.25.7 / toolchain go1.25.10

Sepolia smoke on security-candidate-2 @ cbf31e0e8 (pre-geth-restore tip of refresh stack): PASS (SR-1, SR-2 groupSize=100, SR-4 live SPV proof, SR-5 rollback). Local short regression after geth restore: PASS. Details below.


⚠️ Coordinated upgrade required

All operators in a DKG or signing ceremony must run the same build. Mixed old/new nodes will fail TSS rounds (session IDs, proof transcripts, wire formats differ).

Do not roll out node-by-node during an in-flight ceremony. Upgrade when idle; rollback only when idle.


Breaking changes (operator-visible)

ID Change Action
BC-1..BC-5 tss-lib hardening + session ID binding (GG20 proofs, DKG/signing session formats) Simultaneous upgrade of all ceremony participants
BC-6..BC-8 ECDH/HKDF, G1HashToPoint, PrepareForSigning API changes Rebuild integrators; no in-tree keep-core callers found for BC-8
BC-9 Bootstrap removal (#3909) + embedded well-known peers Remove custom bootstrap config; metric connected_wellknown_peers_count
BC-10 RandomBeacon reentrancy guard (new storage slot) Only if beacon proxy upgraded in same train
OV-1 Metrics/diagnostics opt-in (port 0 default) Enable [clientinfo] Port explicitly if scraping /metrics
OV-2 connected_bootstrap_countconnected_wellknown_peers_count Update dashboards/alerts
OV-3 --network.bootstrap=true deprecated Remove when convenient

Full table: see epic CHANGELOG.md and SECURITY-BREAKING-CHANGES.md on keep-core-security.


Conflict resolution (vs main + epic)

Area Resolution
libp2p transport Kept main #4008 (applyHandshakeDeadline / SetDeadline)
bitcoin / SPV / tbtc / tecdsa Epic hardening + OOB guards
SPV proof headers Cherry-pick DIFF1 getProofInfo fix (testnet4 minimum-difficulty blocks)
Legacy dirs Kept main removal of solidity-v1, dashboard, token-stakedrop
tss-lib Pin 86bd1a3 via go.mod replace

Dependencies

github.com/bnb-chain/tss-lib => github.com/threshold-network/tss-lib v0.0.0-20260615180949-86bd1a375cc0
github.com/threshold-network/keep-common v1.7.1-tlabs.1
go 1.25.7 / toolchain go1.25.10

Local regression (pre-merge)

go test ./pkg/tecdsa/dkg ./pkg/tecdsa/signing ./pkg/maintainer/spv ./pkg/net/libp2p ./pkg/tbtc  → PASS

Sepolia validation (security-candidate-1 @ dd2c87360)

Droplet: 162.243.170.240 · Image: keep-client:security-candidate-1

Scenario Result Evidence
SR-1 Operator mesh PASS 31/31 containers; libp2p mesh stable; metrics when [clientinfo] enabled
SR-2 DKG + wallet FULL PASS (groupSize=3) WalletCreated @ Sepolia block 11130388; wallet PKH 0xa8abdbaa…
SR-2 (30-op, groupSize=100) PARTIAL 100/100 announcement quorum + keygen; on-chain timeout (4-vCPU droplet)
SR-3 Deposit + mint WAIVED Out of smoke scope
SR-4 SPV deposit sweep proof PASS Live path: reveal → operator sweep (123c75b8…) → submitDepositSweepProof 0x0d5de901… @ block 11136453 (status=1)
SR-5 Rollback PASS Revert to prior image; mesh recovered in ~3 min

Baseline replay: prior image sepolia-dev-forklev-btcdiff-idle-flag vs candidate — no mesh/metrics/rollback regression; candidate proves new crypto + live SPV.


Docker image

docker build --platform linux/amd64 -f Dockerfile --target runtime-docker \
  --build-arg ENVIRONMENT=sepolia \
  --build-arg VERSION=security-candidate-1 \
  --build-arg REVISION=dd2c87360 \
  -t keep-client:security-candidate-1 .

Important: default Dockerfile final stage (output-bins) is not runnable — use --target runtime-docker.


Operator upgrade checklist

  1. Build/pull image for all operators in the same ceremony.
  2. Rebuild config from current samples (metrics opt-in, no bootstrap flag).
  3. Start nodes; verify peer mesh (connected_wellknown_peers_count).
  4. SPV maintainer: ensure LightRelayMaintainerProxy.authorize + authorizeSpvMaintainer after fresh deploy; fund maintainer gas for btcdiff catch-up.
  5. Observe first DKG/signing on new build before mainnet rollout.

Rollback: revert image tag when chain/ceremony is idle only.


Review focus

  • Epic merge completeness vs keep-core-security#41 CHANGELOG
  • tss-lib pin 86bd1a3 matches intended release
  • libp2p fix(libp2p): bound Keep authentication handshake with SetDeadline #4008 preserved; epic transport changes not regressed
  • SPV getProofInfo DIFF1 logic matches Bridge
  • Breaking-change docs / operator samples consistent (metrics opt-in, session IDs)
  • CI green on this branch

Related


Test plan

  • CI / go test on changed packages
  • Spot-check CHANGELOG [Unreleased] vs epic
  • Confirm coordinated-upgrade callout in operator docs
  • (Optional) Re-run Sepolia SR-1/SR-2/SR-4 on fresh image after merge

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 395 files, which is 295 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a8745083-4f50-49e1-ad0d-7230428d3521

📥 Commits

Reviewing files that changed from the base of the PR and between a7ac898 and d2a95ee.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (395)
  • .clusterfuzzlite/Dockerfile
  • .clusterfuzzlite/README.md
  • .clusterfuzzlite/build.sh
  • .clusterfuzzlite/check_targets.sh
  • .clusterfuzzlite/project.yaml
  • .dockerignore
  • .github/actions/docker-build-push/action.yml
  • .github/workflows/cflite_batch.yml
  • .github/workflows/cflite_pr.yml
  • .github/workflows/client.yml
  • .github/workflows/contracts-ecdsa-docs.yml
  • .github/workflows/contracts-ecdsa.yml
  • .github/workflows/contracts-random-beacon-docs.yml
  • .github/workflows/contracts-random-beacon.yml
  • .github/workflows/cutover-rehearsal.yml
  • .github/workflows/cutover-scaffold-lint.yml
  • .github/workflows/release.yml
  • .gitignore
  • .golangci-ruleguard.rules.go
  • .golangci.yml
  • CHANGELOG.md
  • Dockerfile
  • Makefile
  • README.adoc
  • SECURITY-BREAKING-CHANGES.md
  • cmd/cmd.go
  • cmd/cutover-roster/main.go
  • cmd/cutover-roster/main_test.go
  • cmd/evidence_window_lifecycle_test.go
  • cmd/flags.go
  • cmd/flags_test.go
  • cmd/maintainer.go
  • cmd/maintainer_metrics_test.go
  • cmd/participation-state-audit/main.go
  • cmd/participation-state-audit/main_test.go
  • cmd/participation_bounds_test.go
  • cmd/quiesce_lifecycle_test.go
  • cmd/releasemanifest.go
  • cmd/releasemanifest_test.go
  • cmd/start.go
  • config/category.go
  • config/config.go
  • config/config_test.go
  • configs/config.toml.SAMPLE
  • docs/performance-metrics.adoc
  • docs/resources/client-start-help
  • docs/resources/docker-start-mainnet-sample
  • docs/resources/docker-start-testnet-sample
  • docs/run-keep-node.adoc
  • go.mod
  • infrastructure/eth-networks/private-testnet/bundles/bundle-guide.adoc
  • infrastructure/kube/keep-dev/eth-tx-rpc-ws-networkpolicy.yaml
  • infrastructure/kube/keep-prd/monitoring/README.adoc
  • infrastructure/kube/keep-prd/monitoring/alertmanager/alertmanager-deployment.yaml
  • infrastructure/kube/keep-prd/monitoring/alertmanager/alertmanager-service.yaml
  • infrastructure/kube/keep-prd/monitoring/alertmanager/config/alertmanager.yaml
  • infrastructure/kube/keep-prd/monitoring/alertmanager/kustomization.yaml
  • infrastructure/kube/keep-prd/monitoring/cutover-roster/deployment.yaml
  • infrastructure/kube/keep-prd/monitoring/cutover-roster/kustomization.yaml
  • infrastructure/kube/keep-prd/monitoring/cutover-roster/pvc.yaml
  • infrastructure/kube/keep-prd/monitoring/cutover-roster/service.yaml
  • infrastructure/kube/keep-prd/monitoring/grafana/config/datasources.yaml
  • infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/cutover-readiness.json
  • infrastructure/kube/keep-prd/monitoring/grafana/grafana-deployment.yaml
  • infrastructure/kube/keep-prd/monitoring/grafana/kustomization.yaml
  • infrastructure/kube/keep-prd/monitoring/monitoring-ingress.yaml
  • infrastructure/kube/keep-prd/monitoring/prometheus/config/config.yaml
  • infrastructure/kube/keep-prd/monitoring/prometheus/config/rules.yaml
  • keep-core-release/threshold-network/keep-common/16.md
  • keep-core-release/threshold-network/keep-common/17.md
  • keep-core-release/threshold-network/keep-core/3945.md
  • keep-core-release/threshold-network/keep-core/3948.md
  • keep-core-release/threshold-network/keep-core/3952.md
  • keep-core-release/threshold-network/tss-lib/4.md
  • keep-core-release/threshold-network/tss-lib/5.md
  • keep-core-release/threshold-network/tss-lib/6.md
  • keep-core-release/tlabs-xyz/keep-core-security/10.md
  • keep-core-release/tlabs-xyz/keep-core-security/11.md
  • keep-core-release/tlabs-xyz/keep-core-security/13.md
  • keep-core-release/tlabs-xyz/keep-core-security/2.md
  • keep-core-release/tlabs-xyz/keep-core-security/8.md
  • keep-core-release/tlabs-xyz/keep-core-security/9.md
  • pkg/altbn128/altbn128.go
  • pkg/altbn128/altbn128_test.go
  • pkg/beacon/beacon.go
  • pkg/beacon/beacon_test.go
  • pkg/beacon/chain/chain.go
  • pkg/beacon/dkg/dkg.go
  • pkg/beacon/dkg/dkg_test.go
  • pkg/beacon/dkg/result/fuzz_test.go
  • pkg/beacon/dkg/result/publish.go
  • pkg/beacon/dkg/result/states.go
  • pkg/beacon/dkg/result/submission.go
  • pkg/beacon/dkg/result/submission_test.go
  • pkg/beacon/entry/entry.go
  • pkg/beacon/entry/fuzz_test.go
  • pkg/beacon/entry/session_test.go
  • pkg/beacon/entry/submission.go
  • pkg/beacon/event/deduplicator.go
  • pkg/beacon/event/deduplicator_test.go
  • pkg/beacon/event/event.go
  • pkg/beacon/gjkr/byzantine_strategy_integration_test.go
  • pkg/beacon/gjkr/cutover_integration_test.go
  • pkg/beacon/gjkr/export_test.go
  • pkg/beacon/gjkr/fuzz_test.go
  • pkg/beacon/gjkr/gjkr.go
  • pkg/beacon/gjkr/integration_test.go
  • pkg/beacon/gjkr/member.go
  • pkg/beacon/gjkr/message_filter_test.go
  • pkg/beacon/gjkr/message_test.go
  • pkg/beacon/gjkr/protocol.go
  • pkg/beacon/gjkr/protocol_combinations_test.go
  • pkg/beacon/gjkr/protocol_ecdh_info_test.go
  • pkg/beacon/gjkr/protocol_ecdh_test.go
  • pkg/beacon/gjkr/protocol_nilguard_test.go
  • pkg/beacon/gjkr/protocol_parameters.go
  • pkg/beacon/gjkr/protocol_sharing_test.go
  • pkg/beacon/gjkr/result.go
  • pkg/beacon/gjkr/states.go
  • pkg/beacon/integration_test.go
  • pkg/beacon/node.go
  • pkg/beacon/node_cutover_test.go
  • pkg/beacon/node_test.go
  • pkg/beacon/participation.go
  • pkg/beacon/participation_outcome_test.go
  • pkg/beacon/participation_test.go
  • pkg/beacon/registry/groups.go
  • pkg/beacon/registry/quarantine.go
  • pkg/beacon/registry/quarantine_test.go
  • pkg/beacon/relay_timeout_settlement_test.go
  • pkg/bitcoin/block.go
  • pkg/bitcoin/block_test.go
  • pkg/bitcoin/electrum/electrum.go
  • pkg/bitcoin/electrum/electrum_integration_test.go
  • pkg/bitcoin/fuzz_test.go
  • pkg/bitcoin/testdata/fuzz/FuzzTransactionDeserialize/73d4d7631f5b3691
  • pkg/bitcoin/testdata/fuzz/FuzzTransactionDeserialize/ed2e47109acbcdc8
  • pkg/bitcoin/transaction.go
  • pkg/bitcoin/transaction_bounds_test.go
  • pkg/bitcoin/transaction_builder.go
  • pkg/bitcoin/transaction_builder_test.go
  • pkg/chain/ethereum/beacon.go
  • pkg/chain/ethereum/beacon_test.go
  • pkg/chain/ethereum/ethereum.go
  • pkg/chain/ethereum/tbtc.go
  • pkg/chain/ethereum/tbtc_redemption_event_property_test.go
  • pkg/chain/ethereum/tbtc_redemption_event_test.go
  • pkg/chain/local_v1/local.go
  • pkg/chain/local_v1/local_test.go
  • pkg/clientinfo/clientinfo_test.go
  • pkg/clientinfo/cutover_metrics_test.go
  • pkg/clientinfo/metrics.go
  • pkg/clientinfo/metrics_test.go
  • pkg/clientinfo/performance.go
  • pkg/clientinfo/performance_test.go
  • pkg/crypto/ephemeral/full_ecdh_test.go
  • pkg/crypto/ephemeral/symmetric_key.go
  • pkg/crypto/ephemeral/symmetric_key_test.go
  • pkg/generator/scheduler_test.go
  • pkg/internal/byzantine/strategy.go
  • pkg/internal/byzantine/strategy_test.go
  • pkg/internal/dkgtest/assertions.go
  • pkg/internal/dkgtest/capturing_logger.go
  • pkg/internal/dkgtest/capturing_logger_test.go
  • pkg/internal/dkgtest/determinism_probe_test.go
  • pkg/internal/dkgtest/dkgtest.go
  • pkg/internal/entrytest/assertions.go
  • pkg/internal/entrytest/entrytest.go
  • pkg/internal/interception/interception.go
  • pkg/internal/interception/strategy_test.go
  • pkg/internal/signingtest/assertions.go
  • pkg/internal/signingtest/signingtest.go
  • pkg/maintainer/btcdiff/bitcoin_chain_test.go
  • pkg/maintainer/btcdiff/chain_test.go
  • pkg/maintainer/maintainer.go
  • pkg/maintainer/spv/bitcoin_chain_test.go
  • pkg/maintainer/spv/control_loop_test.go
  • pkg/maintainer/spv/deposit_sweep.go
  • pkg/maintainer/spv/header_cache_test.go
  • pkg/maintainer/spv/moved_funds_sweep.go
  • pkg/maintainer/spv/moving_funds.go
  • pkg/maintainer/spv/oob_regression_test.go
  • pkg/maintainer/spv/redemptions.go
  • pkg/maintainer/spv/redemptions_metrics_test.go
  • pkg/maintainer/spv/spv.go
  • pkg/maintainer/spv/spv_test.go
  • pkg/monitoring/cutoverroster/alerts.go
  • pkg/monitoring/cutoverroster/alerts_test.go
  • pkg/monitoring/cutoverroster/api.go
  • pkg/monitoring/cutoverroster/api_test.go
  • pkg/monitoring/cutoverroster/collector.go
  • pkg/monitoring/cutoverroster/collector_disappearance_test.go
  • pkg/monitoring/cutoverroster/collector_hardening_test.go
  • pkg/monitoring/cutoverroster/collector_test.go
  • pkg/monitoring/cutoverroster/collector_trustchain_test.go
  • pkg/monitoring/cutoverroster/collector_validation_test.go
  • pkg/monitoring/cutoverroster/identity.go
  • pkg/monitoring/cutoverroster/metrics.go
  • pkg/monitoring/cutoverroster/production_integration_test.go
  • pkg/monitoring/cutoverroster/quarantine.go
  • pkg/monitoring/cutoverroster/reportadapter.go
  • pkg/monitoring/cutoverroster/servicediscovery.go
  • pkg/monitoring/cutoverroster/store.go
  • pkg/monitoring/cutoverroster/types.go
  • pkg/net/libp2p/authenticated_connection.go
  • pkg/net/libp2p/bootstrap_test.go
  • pkg/net/libp2p/channel_manager.go
  • pkg/net/libp2p/channel_test.go
  • pkg/net/libp2p/fuzz_test.go
  • pkg/net/libp2p/libp2p.go
  • pkg/net/local/broadcast_channel_test.go
  • pkg/net/local/local.go
  • pkg/net/net.go
  • pkg/net/retransmission/retransmission_test.go
  • pkg/net/retransmission/ticker.go
  • pkg/net/retransmission/ticker_test.go
  • pkg/net/security/handshake/fuzz_test.go
  • pkg/net/watchtower/watchtower_test.go
  • pkg/protocol/announcer/announcer.go
  • pkg/protocol/announcer/announcer_test.go
  • pkg/protocol/announcer/fuzz_test.go
  • pkg/protocol/compatibility/mixed_transcript_test.go
  • pkg/protocol/compatibility/strategies.go
  • pkg/protocol/compatibility/strategies_test.go
  • pkg/protocol/compatibility/transcript_ownership_test.go
  • pkg/protocol/group/group.go
  • pkg/protocol/group/member_index_test.go
  • pkg/protocol/inactivity/fuzz_test.go
  • pkg/protocol/participation/cutover_peer_roster.go
  • pkg/protocol/participation/cutover_peer_roster_ownership_test.go
  • pkg/protocol/participation/cutover_peer_roster_test.go
  • pkg/protocol/participation/diagnostics.go
  • pkg/protocol/participation/diagnostics_test.go
  • pkg/protocol/participation/gate.go
  • pkg/protocol/participation/gate_test.go
  • pkg/protocol/participation/identity_ownership_test.go
  • pkg/protocol/participation/mode.go
  • pkg/protocol/participation/permit_ownership_test.go
  • pkg/protocol/participation/quiescence.go
  • pkg/protocol/participation/quiescence_test.go
  • pkg/protocol/participation/release.go
  • pkg/protocol/participation/schedule.go
  • pkg/protocol/participation/schedule_test.go
  • pkg/protocol/state/sync_machine.go
  • pkg/protocol/state/sync_machine_test.go
  • pkg/sortition/internal/local/chain.go
  • pkg/tbtc/audit.go
  • pkg/tbtc/audit_test.go
  • pkg/tbtc/chain_test.go
  • pkg/tbtc/coordination.go
  • pkg/tbtc/coordination_byzantine_test.go
  • pkg/tbtc/coordination_test.go
  • pkg/tbtc/coordination_window_metrics_test.go
  • pkg/tbtc/cutover_observer.go
  • pkg/tbtc/cutover_observer_test.go
  • pkg/tbtc/deduplicator.go
  • pkg/tbtc/deduplicator_test.go
  • pkg/tbtc/deposit_sweep.go
  • pkg/tbtc/deposit_sweep_test.go
  • pkg/tbtc/dkg.go
  • pkg/tbtc/dkg_cutover_integration_test.go
  • pkg/tbtc/dkg_loop.go
  • pkg/tbtc/dkg_loop_test.go
  • pkg/tbtc/dkg_submit.go
  • pkg/tbtc/dkg_submit_test.go
  • pkg/tbtc/dkg_test.go
  • pkg/tbtc/fuzz_test.go
  • pkg/tbtc/heartbeat.go
  • pkg/tbtc/heartbeat_test.go
  • pkg/tbtc/inactivity.go
  • pkg/tbtc/inactivity_test.go
  • pkg/tbtc/moved_funds_sweep.go
  • pkg/tbtc/moved_funds_sweep_test.go
  • pkg/tbtc/moving_funds.go
  • pkg/tbtc/moving_funds_test.go
  • pkg/tbtc/node.go
  • pkg/tbtc/node_block.go
  • pkg/tbtc/node_coordination.go
  • pkg/tbtc/node_executors.go
  • pkg/tbtc/node_operated_seats_test.go
  • pkg/tbtc/node_proposals.go
  • pkg/tbtc/node_test.go
  • pkg/tbtc/node_wallet_closure.go
  • pkg/tbtc/participation.go
  • pkg/tbtc/participation_gate_test.go
  • pkg/tbtc/participation_outcome.go
  • pkg/tbtc/participation_outcome_test.go
  • pkg/tbtc/participation_permit_test.go
  • pkg/tbtc/participation_test.go
  • pkg/tbtc/quarantine.go
  • pkg/tbtc/redemption.go
  • pkg/tbtc/redemption_test.go
  • pkg/tbtc/registry.go
  • pkg/tbtc/registry_test.go
  • pkg/tbtc/session_id_test.go
  • pkg/tbtc/signing.go
  • pkg/tbtc/signing_cutover_integration_test.go
  • pkg/tbtc/signing_done.go
  • pkg/tbtc/signing_done_test.go
  • pkg/tbtc/signing_loop.go
  • pkg/tbtc/signing_loop_test.go
  • pkg/tbtc/signing_test.go
  • pkg/tbtc/tbtc.go
  • pkg/tbtc/tbtc_startup_test.go
  • pkg/tbtc/wallet.go
  • pkg/tbtc/wallet_test.go
  • pkg/tecdsa/common/compatibility.go
  • pkg/tecdsa/dkg/dkg.go
  • pkg/tecdsa/dkg/fuzz_test.go
  • pkg/tecdsa/dkg/member.go
  • pkg/tecdsa/dkg/member_test.go
  • pkg/tecdsa/dkg/protocol.go
  • pkg/tecdsa/dkg/protocol_ecdh_info_test.go
  • pkg/tecdsa/dkg/protocol_test.go
  • pkg/tecdsa/retry/retry_property_test.go
  • pkg/tecdsa/signing/fuzz_test.go
  • pkg/tecdsa/signing/integration_test.go
  • pkg/tecdsa/signing/member.go
  • pkg/tecdsa/signing/member_receive_test.go
  • pkg/tecdsa/signing/member_test.go
  • pkg/tecdsa/signing/protocol.go
  • pkg/tecdsa/signing/protocol_ecdh_info_test.go
  • pkg/tecdsa/signing/protocol_test.go
  • pkg/tecdsa/signing/signing.go
  • pkg/tecdsa/signing/states.go
  • renovate.json
  • scripts/release/pr4109/README.md
  • scripts/release/pr4109/capture-cutover-evidence-window.sh
  • scripts/release/pr4109/chain-inputs.sha256
  • scripts/release/pr4109/clientinfo-port-smoke.sh
  • scripts/release/pr4109/compose.rehearsal.yaml
  • scripts/release/pr4109/compose.yaml
  • scripts/release/pr4109/deploy/keep-client-termination-grace.k8s-patch.yaml
  • scripts/release/pr4109/deploy/keep-client-termination-grace.systemd-dropin.conf
  • scripts/release/pr4109/rehearsal-evidence.schema.json
  • scripts/release/pr4109/rehearse.sh
  • scripts/release/pr4109/release-docker-tags.sh
  • scripts/release/pr4109/release-manifest.json
  • scripts/release/pr4109/release-manifest.schema.json
  • scripts/release/pr4109/release-provenance.schema.json
  • scripts/release/pr4109/release-trigger-tag.sh
  • scripts/release/pr4109/test-attest-release-manifest.sh
  • scripts/release/pr4109/test-cutover-evidence-window.sh
  • scripts/release/pr4109/test-rehearsal-matrix.sh
  • scripts/release/pr4109/test-release-docker-tags.sh
  • scripts/release/pr4109/test-source-binding.sh
  • scripts/release/pr4109/test-validate-evidence.sh
  • security/README.md
  • security/architecture.md
  • security/attack-surface.md
  • security/critical-paths.md
  • security/crypto-review.md
  • security/findings/F-01.md
  • security/findings/F-02.md
  • security/findings/F-03.md
  • security/findings/F-04.md
  • security/findings/F-05.md
  • security/findings/F-06.md
  • security/findings/F-07.md
  • security/findings/F-08.md
  • security/findings/F-09.md
  • security/findings/F-10.md
  • security/findings/F-11.md
  • security/findings/F-12.md
  • security/findings/F-13.md
  • security/findings/F-14.md
  • security/findings/F-15.md
  • security/findings/F-16.md
  • security/findings/F-17.md
  • security/smart-contracts.md
  • security/threat-model.md
  • solidity-v1/contracts/stubs/RelayEntryServiceStub.sol
  • solidity/ecdsa/Dockerfile
  • solidity/ecdsa/git-yarn-compat
  • solidity/ecdsa/test/DKGValidator.test.ts
  • solidity/ecdsa/test/WalletRegistry.Rewards.test.ts
  • solidity/ecdsa/test/WalletRegistry.WalletCreation.test.ts
  • solidity/ecdsa/test/utils/dkg.ts
  • solidity/random-beacon/Dockerfile
  • solidity/random-beacon/contracts/RandomBeacon.sol
  • solidity/random-beacon/contracts/RandomBeaconGovernance.sol
  • solidity/random-beacon/contracts/test/RandomBeaconStub.sol
  • solidity/random-beacon/contracts/test/ReentrantBeaconConsumer.sol
  • solidity/random-beacon/git-yarn-compat
  • solidity/random-beacon/hardhat.config.ts
  • solidity/random-beacon/test/RandomBeacon.Reentrancy.test.ts
  • solidity/random-beacon/test/RandomBeacon.Relay.test.ts
  • solidity/random-beacon/test/RandomBeacon.StorageLayout.test.ts
  • solidity/random-beacon/test/RandomBeaconGovernance.test.ts
  • solidity/random-beacon/test/fixtures/index.ts
  • test/config_clientinfo_9601.toml
  • test/config_clientinfo_zero.toml
  • test/config_participation_cutover.toml
  • test/config_participation_cutover_zero.toml
  • tools.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lrsaturnino

Copy link
Copy Markdown
Member

The PR description says head is dd2c87360 with go-ethereum bumped to v1.17.3 (+ Go 1.25), but the current branch tip ec5dbd1b3 doesn't have those changes. Looks like they got dropped during the July 12 rebase.

Worth double-checking go.mod before this merges — go-ethereum v1.13.15 has an open CVE advisory.

@lionakhnazarov
lionakhnazarov force-pushed the security-release/candidate-1 branch from dc42a85 to 971f603 Compare July 16, 2026 13:53
@lionakhnazarov

Copy link
Copy Markdown
Collaborator Author

The PR description says head is dd2c87360 with go-ethereum bumped to v1.17.3 (+ Go 1.25), but the current branch tip ec5dbd1b3 doesn't have those changes. Looks like they got dropped during the July 12 rebase.

Worth double-checking go.mod before this merges — go-ethereum v1.13.15 has an open CVE advisory.

thanks Leo! Fix applied

lrsaturnino
lrsaturnino previously approved these changes Jul 20, 2026

@lrsaturnino lrsaturnino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lrsaturnino
lrsaturnino dismissed their stale review July 21, 2026 15:28

The merge-base changed after approval.

lrsaturnino added a commit to lionakhnazarov/keep-core that referenced this pull request Jul 24, 2026
…ee RPCs, monitoring wiring (vet round 2)

Addresses the round-2 vet report (findings 1, 3, 6 + three net-new).

P0 — production discovery/identity is now authoritative, not optional (finding 1):
- Completeness now REQUIRES service-discovery reconciliation and on-chain
  identity verification (CollectorConfig.RequireServiceDiscovery /
  RequireIdentityVerification, set true by the command). A missing feed blocks
  readiness instead of degrading to trusting inventory; the command warns that
  readiness cannot be certified. (isComplete, main.go)
- Service discovery is keyed by network ID (per-instance) not operator-only, so
  multiple instances of one operator resolve to distinct discovered targets
  (ServiceDiscovery.byNetworkID, MetricsURLForInstance, InventoryInstance.NetworkID,
  applyDiscoveredTargets).
- The metrics adapter validates the responding node's self-attested identity
  (diagnostics chain_address + network_id) instead of copying InstanceID/
  OperatorAddress from inventory; a mismatch is rejected (reportadapter.go).

P0 — reporter revision is durable across collector restart (net-new 1):
- ReporterRevision now derives from the attestation timestamp (UnixNano) rather
  than a process-local counter that reset on restart and sat below the persisted
  high-water mark. Added TestCollector_ReportsAcceptedImmediatelyAfterRestart and
  TestMetricsReportSource_ReporterRevisionSurvivesRestart.

P1 — remaining evidence gaps (finding 3):
- StakingProvider must be a canonical, non-zero address; expected image digest
  must be sha256:<64 hex> (test fixture corrected to a full digest); cross-instance
  staking-provider contradictions are fail-closed (last claim no longer wins).

P1 — no collector lock across network calls (net-new 2):
- Identity verification runs BEFORE the central-state lock (verify first, apply
  under lock) so a degraded RPC never blocks readers; the RPC timeout derives from
  the passed-in context (CollectContext(ctx), IdentityVerifier now takes ctx,
  ethCall uses context.WithTimeout(ctx, ...)).

P1 — monitoring deployment wiring (finding 6):
- A non-loopback API bind without an allowlist is refused at startup.
- cutover-readiness.json added to the Grafana ConfigMap; instance-reason text
  panel replaced with a real table sourced from the readiness API via a new
  Infinity datasource (+ plugin install).
- Added a cutover-roster Deployment/Service/PVC and a Prometheus scrape job, plus
  an Alertmanager workload + routing tree matching the alerts' route_to label.
  (Image digest, allowlist CIDR, inventory ConfigMap/Secret, and receiver
  integrations are REPLACE_ placeholders — ops follow-up, documented in README.adoc.)

P1 — 30-day resolved purge (net-new 3): resolution of the semantic conflict.
- The fail-closed reopening of a vanished resolved operator (finding 2, RESOLVED)
  and the naive 30-day purge of a departed resolved operator are in genuine,
  irreconcilable tension for the "resolved operator departs" population: a departed
  operator MUST reopen offline_unknown and be retained indefinitely, so it can
  never age out via the resolved path, and an actively-resolved operator is
  continuously re-confirmed (ResolvedAt refreshed) and must not be dropped. purge
  therefore remains a bounded-store backstop for a resolved record no longer being
  re-confirmed. Restored independent coverage as a white-box test of the purge
  mechanism (TestCollector_PurgeResolvedAfter30Days): a resolved record older than
  the retention window is purged with its instances, while a fresh resolved record
  and any blocking record are retained.

P1/P2 — harness: README/compose/script examples now use an immutable @sha256:
digest instead of the mutable keep-client:candidate tag the harness itself rejects.

P2 — draft PR body: corrected the inaccurate "wire-compatible / no session-ID/KDF
change" claim to scope it to THIS diff only and explicitly note PR threshold-network#4109's Part A
cryptographic changes are wire-breaking and not described here. Live PR/issue
untouched (out of band).

Local CI oracle (format, vet, staticcheck SA*, golangci-lint, gosec, go test ./...,
race tier-2 subset, integration, race over new packages): PASS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lrsaturnino added a commit to lionakhnazarov/keep-core that referenced this pull request Jul 24, 2026
…ee RPCs, monitoring wiring (vet round 2)

Addresses the round-2 vet report (findings 1, 3, 6 + three net-new).

P0 — production discovery/identity is now authoritative, not optional (finding 1):
- Completeness now REQUIRES service-discovery reconciliation and on-chain
  identity verification (CollectorConfig.RequireServiceDiscovery /
  RequireIdentityVerification, set true by the command). A missing feed blocks
  readiness instead of degrading to trusting inventory; the command warns that
  readiness cannot be certified. (isComplete, main.go)
- Service discovery is keyed by network ID (per-instance) not operator-only, so
  multiple instances of one operator resolve to distinct discovered targets
  (ServiceDiscovery.byNetworkID, MetricsURLForInstance, InventoryInstance.NetworkID,
  applyDiscoveredTargets).
- The metrics adapter validates the responding node's self-attested identity
  (diagnostics chain_address + network_id) instead of copying InstanceID/
  OperatorAddress from inventory; a mismatch is rejected (reportadapter.go).

P0 — reporter revision is durable across collector restart (net-new 1):
- ReporterRevision now derives from the attestation timestamp (UnixNano) rather
  than a process-local counter that reset on restart and sat below the persisted
  high-water mark. Added TestCollector_ReportsAcceptedImmediatelyAfterRestart and
  TestMetricsReportSource_ReporterRevisionSurvivesRestart.

P1 — remaining evidence gaps (finding 3):
- StakingProvider must be a canonical, non-zero address; expected image digest
  must be sha256:<64 hex> (test fixture corrected to a full digest); cross-instance
  staking-provider contradictions are fail-closed (last claim no longer wins).

P1 — no collector lock across network calls (net-new 2):
- Identity verification runs BEFORE the central-state lock (verify first, apply
  under lock) so a degraded RPC never blocks readers; the RPC timeout derives from
  the passed-in context (CollectContext(ctx), IdentityVerifier now takes ctx,
  ethCall uses context.WithTimeout(ctx, ...)).

P1 — monitoring deployment wiring (finding 6):
- A non-loopback API bind without an allowlist is refused at startup.
- cutover-readiness.json added to the Grafana ConfigMap; instance-reason text
  panel replaced with a real table sourced from the readiness API via a new
  Infinity datasource (+ plugin install).
- Added a cutover-roster Deployment/Service/PVC and a Prometheus scrape job, plus
  an Alertmanager workload + routing tree matching the alerts' route_to label.
  (Image digest, allowlist CIDR, inventory ConfigMap/Secret, and receiver
  integrations are REPLACE_ placeholders — ops follow-up, documented in README.adoc.)

P1 — 30-day resolved purge (net-new 3): resolution of the semantic conflict.
- The fail-closed reopening of a vanished resolved operator (finding 2, RESOLVED)
  and the naive 30-day purge of a departed resolved operator are in genuine,
  irreconcilable tension for the "resolved operator departs" population: a departed
  operator MUST reopen offline_unknown and be retained indefinitely, so it can
  never age out via the resolved path, and an actively-resolved operator is
  continuously re-confirmed (ResolvedAt refreshed) and must not be dropped. purge
  therefore remains a bounded-store backstop for a resolved record no longer being
  re-confirmed. Restored independent coverage as a white-box test of the purge
  mechanism (TestCollector_PurgeResolvedAfter30Days): a resolved record older than
  the retention window is purged with its instances, while a fresh resolved record
  and any blocking record are retained.

P1/P2 — harness: README/compose/script examples now use an immutable @sha256:
digest instead of the mutable keep-client:candidate tag the harness itself rejects.

P2 — draft PR body: corrected the inaccurate "wire-compatible / no session-ID/KDF
change" claim to scope it to THIS diff only and explicitly note PR threshold-network#4109's Part A
cryptographic changes are wire-breaking and not described here. Live PR/issue
untouched (out of band).

Local CI oracle (format, vet, staticcheck SA*, golangci-lint, gosec, go test ./...,
race tier-2 subset, integration, race over new packages): PASS.
@lrsaturnino
lrsaturnino force-pushed the security-release/candidate-1 branch from 54cdc4a to 33808cb Compare July 24, 2026 14:55
@mswilkison

Copy link
Copy Markdown
Contributor

Two go.mod observations from a dependency sweep. The tss-lib upgrade itself looks right — the bump to 86bd1a375cc0 is the current fork head, and both mandatory caller obligations from the protocol break are wired in (SetSessionNonce 21×, fullBytesLen 11× in the diff), which is the part that would have failed closed if missed.

1. The x/crypto bump lands just below the advisory floor

This PR goes 0.32.0 → 0.47.0. The seven open critical advisories against x/crypto (CVE-2026-39830, -39831, -39832, -39833, -39834, -42508, -46595) are all first patched in 0.52.0, so they stay open after this merges.

There is also a separate open PR #4142 doing 0.32.0 → 0.52.0. These two will conflict on go.mod, and whichever lands second should win at >= 0.52.0.

Worth keeping in proportion: all seven are in ssh / ssh/agent, and go list -deps ./... shows zero x/crypto/ssh packages in the build — keep-core only pulls sha3, blake2b, chacha20, curve25519, scrypt and friends. So this is closing advisory noise rather than real exposure. Raising it only because a release framed as a security release probably wants the alerts actually cleared.

2. btcd is untouched, and the replace is a downgrade

Not changed by this PR, but it sits in the same file and the contrast with the above is sharp:

go.mod:24  github.com/btcsuite/btcd v0.23.2                              // require
go.mod:13  github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.3  // replace

The replace pins below the require, and v0.26.2 is current. OSV reports 6 advisories against v0.22.3:

advisory summary fixed in
GO-2022-1098 DoS in message decoding 0.23.2
GHSA-2chg-86hq-7w38 mishandles witness size checking 0.23.2
GO-2024-2818 consensus failures 0.24.0
GHSA-3jgf-r68h-xfqm consensus failures 0.24.0
GO-2024-3189 consensus failure 0.24.2-beta.rc1
GHSA-27vh-h6mc-q6g8 incorrect re-implementation of FindAndDelete 0.24.2-beta.rc1

The first two are already fixed at the version this repo requires — the replace reintroduces them.

Unlike the x/crypto case, this code is in the build: go list -deps ./... returns 16 btcsuite/btcd packages including blockchain, blockchain/indexers, btcec, btcjson and btcutil. Consensus-failure advisories in a Bitcoin bridge seem worth either fixing here or recording why the downgrade is deliberate — right now the replace carries no comment, unlike the keep-common and protobuf ones above it.

mswilkison added a commit that referenced this pull request Jul 26, 2026
All five workflows that build and test the contracts pin Node 18.15.0,
which reached end of life on 2025-04-30 and receives no further security
patches. `engines.node` in both solidity manifests and the two contract
Dockerfiles say the same.

Target is Node 22 (Jod), EOL 2027-04-30. Node 24 has more than twice the
remaining support and was tried first, but Hardhat's undici call is
incompatible with Node 24 until ~2.20, and @defi-wonderland/smock breaks on
hardhat >= 2.20 -- the two windows do not overlap. Node 22 is the ceiling
in both repos until smock is replaced, and smock is archived upstream
(wonderland-archive/smock).

No dependency changes. Verified against this branch, solidity/random-beacon:

    Node v18.20.8   168 passing, 12 failing
    Node v22.23.1   168 passing, 12 failing

The 12 are identical and pre-existing on both -- every one is
"No deployment found for: T", a missing external deployment artifact in a
local checkout, unrelated to the runtime. hardhat 2.10.0 and
@defi-wonderland/smock 2.0.7, the packages most likely to break on a newer
runtime, are clean.

infrastructure/ is deliberately untouched. Its Dockerfiles are not built
by CI -- `client.yml` lists `infrastructure/**` under `paths-ignore`, and
the only build contexts in the workflows are `.`, `./solidity/ecdsa` and
`./solidity/random-beacon`. The node:11 init container there wants a
decision about whether it is still deployed, not a bump to a file nothing
builds.

Stacked on #4109 -- carries that branch's commits and should merge after
it. #4109 changes no Node-runtime surface, so there is no overlap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
client_info now carries the three labels fleet reconciliation matches
against — release version, exact source revision, and compiled protocol
epoch — instead of version alone; the maintainer process exports the same
identity. Diagnostics gain a protocol_participation object with the epoch,
resolved cutover block and its source, live gate state, clock availability,
and active per-mode ceremony counts, so a port-enabled node answers the
readiness questions from one scrape.
…rmit

The beacon DKG permit previously selected only a protocol mode: neither
cancellation nor commit safety reached the running ceremony, so a clock
failure or forced quiescence could cancel a permit while GJKR kept
executing, submitted a result, and registered key material.

The sync state machine now executes under the ceremony context, and the
beacon DKG threads its permit end to end: the terminal result submission
and the signer activation both pass a last-moment completion fence, with
no implicit default guard.

Interruption after key generation preserves the orphaned signer in a new
quarantine namespace beside the active beacon keystore, together with an
audit metadata record. The namespace is a sibling directory, so an
active-group scan of any release cannot load quarantined outputs as
active signers. An accepted result whose local activation the gate
refuses is saved durably without cache activation, since dropping an
accepted share would permanently reduce its group.

Deterministic node-level tests cover a legacy permit completing after
the cutover block, forced shutdown and chain-clock failure inside the
publication window preserving signers only in quarantine, and mid-GJKR
cancellation aborting cleanly with nothing persisted.
…ermit

The lifecycle controller previously drained only beacon DKG permits:
relay signing, restart resume, share forwarding, and timeout monitoring
ran untracked, so quiescence could declare the process drained while
real protocol work was still in flight, and a canceled gate could not
reach any of those paths.

Relay entry signing now issues one permit per local membership, with the
restart path going through the gate's resume verification against the
on-chain request start block. The terminal relay entry submission passes
a completion fence. Timeout monitoring holds a permit anchored at the
relay request block and fences the report as a penalty commit, so a
legacy monitor cannot create new penalty state at or after the cutover
block and no monitor can report once quiescence begins.

BroadcastChannelForwarderFor returns a lifecycle handle with idempotent
Close and a Done channel: the libp2p relay stops on TTL, provider
shutdown, or explicit close, and the local provider returns an
already-done no-op. Share forwarding runs under a forwarding permit that
closes the handle when the gate cancels, and releases the permit when
the relay ends on its own.

Tests cover the suppressed legacy timeout report after the cutover, the
normal report below it, and both directions of the forwarding lifecycle.
…kstop

Signal capture was installed only after both applications started,
leaving every protocol callback spawned during startup exposed to the
default signal action. The channel is now armed before any component
initializes; a signal arriving mid-startup is held and handled by the
lifecycle controller once startup completes.

The quiesce backstop previously multiplied the completion bound straight
into a time.Duration with no overflow check and no block margin. It is
now derived at startup from the maximum legacy completion bound plus a
reviewed block margin, with every step overflow-checked; an overflowing
deadline refuses startup instead of silently truncating the grace
period. The startup log records the bound, margin, block-interval bound,
and resulting backstop as the same inputs the release manifest derives
the external termination grace from.
… smoke

The release directory previously claimed the cutover gate was
intentionally unimplemented and carried no Part A rehearsal structure.
It now holds the rehearsal driver, fleet shell, and evidence schema for
the two mandatory container rehearsals: the exact-image single-release
rehearsal and the homogeneous rollback rehearsal.

The driver runs the repository-local Go proofs of the gate today and
validates the container-rehearsal inputs, requiring immutable image
digests. The container stages refuse to run with an explicit BLOCKED
report naming their missing inputs — a rehearsal chain with deployed
contracts, prior and R1 digests, per-node keys, storage snapshots —
rather than pretending to pass. Accepted runs must produce an evidence
record conforming to the schema: exact SHAs, per-architecture digests,
chain ID and cutover block, per-stage canonical and callback blocks,
permit modes, gauges, transaction hashes, and state checksums.
… barrier

The rollback barrier requires an offline classification of every stopped
node's persisted protocol state before any prior binary may start. The
new participation-state-audit command produces that manifest from a
storage snapshot copy: a full file inventory of the beacon, beacon
quarantine, and tBTC namespaces with checksums of the at-rest encrypted
bytes, plus an interpreted view of the beacon active memberships and
quarantined outputs when the storage password is supplied.

Every inconsistency is a finding and fails the audit: quarantine
metadata without its preserved membership, memberships without metadata,
records that cannot be decrypted or decoded, and quarantine state
surfacing in the active-group scan. Without the password the tool
degrades to a raw inventory and refuses to classify the snapshot as
consistent. Chain reconciliation is explicitly out of scope: the
manifest records it as not performed and never authorizes activating
quarantined material by itself.
…tion-safe

A canceled ceremony could be held hostage by a stalled chain: the sync
machine's start-block wait and its between-state delay waits were
synchronous WaitForBlockHeight calls that ignored the machine context, so
a permit canceled during a held wait could not return, surface its
cancellation cause, or reach the signer recovery path. Both waits now
select on the machine context and return the cancellation cause promptly;
two held-wait tests pin the behavior before execution and between states.

Beacon result and relay event subscriptions also sent on unbuffered
channels, so a callback in flight when its consumer returned on
cancellation or timeout blocked forever. The three remaining unbuffered
subscription channels now carry the same one-slot buffer the relay
timeout monitor already uses.
A termination signal was captured at the top of startup but acted on only
after every component had initialized, so an operator's shutdown request
during a slow startup left the gate issuing new permits until the end of
initialization. The quiesce drive now runs in a dedicated lifecycle
controller launched immediately after the gate and its backstop deadline
are constructed — before the network provider, beacon, or tBTC can begin
protocol work — so the first signal refuses all subsequent permits no
matter when it arrives. The controller reports its shutdown cause before
canceling the run context, so the main goroutine always prefers the
signal report over a bare context end.
…ross-validated

The offline state audit could race its own findings list — the persistence
error-drain goroutines appended concurrently with the descriptor loops —
and could classify a snapshot as consistent from namespace inventory
alone, leaving every rollback-manifest question unasked. Findings now go
through one mutex, and the manifest carries the snapshot identity
(aggregate checksum, root access mode), an expected-layout scan that flags
any entry the audit cannot classify, and explicit rollback blockers: the
audit exits nonzero until chain reconciliation, Bitcoin reconciliation,
the quiescence outcome report, and prior-reader compatibility evidence are
supplied and recorded, so inventory alone can never read as
rollback-ready.

Interpretation now cross-validates what it decodes. Beacon quarantine
metadata is checked against its schema version, this release's epoch, the
ceremony class, the cutover arithmetic of its recorded mode and anchor,
the directory and member file name it is stored under, the decrypted
membership it pairs with, and the active namespace — a group present on
both sides of the quarantine boundary is the exact ambiguity the
quarantine exists to prevent. Active beacon records are checked against
their storage location the active scan trusts. tBTC keystore records are
decoded with the same full decode the wallet registry loader uses, via a
new exported audit decoder proven against the loader fixtures, and the
work namespace is classified with unclassifiable records reported.
The rehearsal fleet shell attached every node only to an internal network
while requiring an external chain endpoint, so no node could ever reach
ETH_WS_URL; the fleet now spans an internal protocol network plus a
chain-egress network, and the mounted per-node inputs are actually wired
into each node through --config, a read-only keystore mount, the storage
volume, and the key password pass-through. Preflight verifies the per-node
config exists before pulling images.

The driver gains the missing halves of the rehearsal contract: a
validate-evidence stage that checks every produced record against the
evidence schema, local proofs extended to the signal lifecycle, held-wait
cancellation, state audit, and audit decoder tests, and a manually
dispatched workflow that runs the local proofs in the same build image the
client CI uses and keeps the container stages BLOCKED-red until the fleet
inputs exist.

The port smoke harness asserted the participation gate metrics were not
exposed, which stopped being true when the gate landed in this tree; the
positive-response assertions now require the gate gauges and the
participation diagnostics object. Harness and schema comments now describe
the rehearsals in their own terms.
The block counters deliver exactly one notification per registered waiter
with a blocking send on an unbuffered channel. A canceled execution that
simply walked away from its waiter — the interruptible start-block and
delay waits, or the machine select abandoning a state's end-block waiter —
left that sender goroutine parked forever once the height was eventually
reached.

Ownership of an abandoned waiter now passes to a drain goroutine that
performs the single receive, so the eventual sender terminates and the
drain exits with it. The new tests drive a manually advanced counter that
reproduces the production waiter contract and prove the sender completes
after a cancellation, both for the bare wait helper and for the machine's
abandoned end-block waiter.
Every tBTC ceremony choke point now runs under a permit from the shared
release gate, so process quiescence, clock failure, and the active-mode
accounting finally see tBTC work:

- Each locally controlled DKG member acquires a permit anchored at the
  DKG started event block immediately before its goroutine; the retry
  loop, announcer classification, and execution context all derive from
  the permit, and a gate cancellation is no longer counted as an
  ordinary DKG failure.
- Each wallet action acquires one permit before handler and dispatcher
  setup, anchored at the proposal-processing start block; the action owns
  the permit for its whole execution, and its signing and Bitcoin
  broadcast run on the permit context instead of unowned background
  contexts.
- The coordination procedure runs under a tracking permit anchored at the
  window's coordination block; its wire format is shared by both
  releases, so the procedure itself runs in either mode.
- The last-moment completion fences guard signer activation, DKG result
  submission, and every Bitcoin broadcast attempt; the penalty fence
  guards the heartbeat's consecutive-failure accounting and the terminal
  inactivity claim submission, so quiescence or a legacy-after-cutover
  result suppresses new penalty state instead of punishing the grace.
- A refused activation preserves the generated share without activating
  it: durably in the active namespace when the wallet is registered on
  chain, otherwise in the new protected tbtc-quarantine storage
  namespace no release's active-wallet scan reads, with audit metadata
  recording mode, cutover arithmetic, and a seed hash but never raw
  seeds or shares.
- registerSigner is split into durable save and cache activation so the
  non-activating save path exists.

The pinned tss-lib revision has no reviewed per-party legacy mode, so a
tECDSA ceremony cannot reproduce the legacy proof transcript. A
legacy-mode permit for DKG, signing, heartbeat, or any wallet action is
therefore refused outright — emitting the hardened transcript under a
legacy permit would produce wire traffic compatible with neither
release. The signing executor additionally hard-refuses any
non-security-v2 mode as defense in depth.

The tbtc block-height wait helper also drains its abandoned waiter on
context cancellation, matching the protocol state machine fix.
lrsaturnino and others added 14 commits July 31, 2026 11:10
The shell-analysis stage runs whatever shellcheck the runner image ships,
and that is 0.9.0 while local runs are on 0.11.0. The two report the same
false positive about helpers reached only through a dispatch they cannot
follow under different codes: SC2317 on the function's body before 0.11.0,
the release that added SC2329, and SC2329 on the definition from there on.
The suppressions already at those sites named only the newer code, so 172
findings the local analyzer never shows failed the gate on the runner.

Name both codes wherever one was already carried. That holds the gate
green either side of the split rather than on the version the runner
image happens to ship, which nothing in this path pins.
…ntext

.dockerignore drops docs*/ and scripts/ wholesale, but tests under cmd and
pkg/clientinfo open the release manifest, the three schemas its records are
validated against, the deployment scaffolds its termination grace is
compared to, and the participation metrics reference, all through paths
relative to their own package. Those tests run inside the build image, so
the files resolve in a working tree and are missing exactly where CI
executes them.

The scaffolds surfaced only by fixing the rest: each case stops at the
first file it cannot read, so the run reported one missing input per test
and hid the two behind it.

Negate the eight paths back into the context — the files themselves, not
the trees they sit under — and re-derive the scaffold's hand-written mirror
of these rules, which verify_build_context_mirror holds to the real file on
every push that touches either.
…test greps

GNU grep 3.11 (the CI runner's grep) doesn't expand a literal \t inside a
single-quoted -E/-oE pattern, so the three ceremony/case-line extraction
greps in test-validate-evidence.sh matched nothing and returned 1. Under
set -euo pipefail that silently aborted the self-test before it printed
its own pass/fail tally, failing the scaffold-lint job with no visible
error. It passed locally because BSD grep on macOS does expand \t there.

Switching each pattern to $'...' ANSI-C quoting makes bash substitute a
real tab byte before grep ever sees it, so both implementations match
identically. Verified against GNU grep 3.11 in an Ubuntu 24.04 container:
the self-test now completes with '642 passed, 0 failed' where it
previously died silently.
…CI parallelism

Updated the block time from 100ms to 200ms in the DKG cutover integration tests to ensure the real key-generation transcript completes within one attempt's protocol window under CI package parallelism. This change addresses potential race conditions during testing.
CI measured 345003 gas, which sat 3 over the old 330000±15000 band.
piotr-roslaniec added a commit that referenced this pull request Aug 7, 2026
## Summary

`go.mod` replaces the whole `btcd` module with **v0.22.3** - *below* the
`v0.23.2` the module itself requires - which keeps three already-fixed
security advisories open against the Bitcoin consensus, wire, and script
packages the bridge actually compiles. (Three vulnerabilities, six
identifiers counting the GHSA aliases.)

```
go.mod  github.com/btcsuite/btcd v0.23.2                              // require
go.mod  github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.3  // replace — wins
```

This is long-standing on `main` and not specific to any release branch.
It surfaced during a dependency sweep of #4109; that PR's integration
branch inherits it, and this fix is deliberately targeted here rather
than stacked on the release candidate so it gets CI and normal review.

Unlike an unused-code advisory, this code **is** in the build — `go list
-deps ./...` returns 16 `btcsuite/btcd` packages including `blockchain`,
`blockchain/indexers`, `btcjson`, `btcutil`, `txscript`, and `wire`.

| Advisory | Package | Fixed in | Status before | After |
|---|---|---|---|---|
| GO-2022-1098 / GHSA-2chg-86hq-7w38 — DoS in message decoding, witness
size checking | `wire` | 0.23.2 | **open** (below the repo's own
require) | fixed |
| GO-2024-2818 / GHSA-3jgf-r68h-xfqm — consensus failures |
`blockchain`, `txscript` | 0.24.0 | **open** | fixed |
| GO-2024-3189 / GHSA-27vh-h6mc-q6g8 — incorrect `FindAndDelete`
re-implementation | `txscript` | 0.24.2-beta.rc1 | **open** | fixed |

## Why the downgrade was there, and why it can go

btcd v0.23 extracted `btcd/btcec` into the separate module
`btcd/btcec/v2`, and later versions don't ship the pre-split package at
all. Two consumers still import the pre-split path and can't simply be
edited: the pinned **tss-lib fork** (`crypto/ckd`, `tss/curve`) and
**first-party key-handling code** (`pkg/crypto/ephemeral`,
`pkg/crypto/secp256k1`, `pkg/net/...`, `pkg/chain/local_v1`,
`pkg/internal/pbutils`), which relies on v1 `btcec` types being aliases
of the `crypto/ecdsa` types.

So one crypto package was holding the entire module — consensus code
included - a minor version back.

This PR serves *just that package* from an in-tree copy of the btcd
v0.22.3 `btcec` sources (`third_party/btcsuite/btcec`) and moves the
module itself to **v0.24.2**. The vendored copy is byte-identical to
upstream apart from a mechanical `gofmt` pass; `VENDOR.md` records
provenance, a reproducible `diff -r` verification recipe, and the exit
path (delete the directory once tss-lib migrates to `btcec/v2`). The
upstream btcec test suite ships with it and passes.

### Why v0.24.2 and not v0.26.2

**v0.24.2 is the lowest version that fixes all three advisories** (the
last one lands in `0.24.2-beta.rc1`), and going higher isn't currently
possible:

- **v0.25.0 requires `btcec/v2` v2.3.5**, and `btcec/v2` **v2.3.4**
changed `ecdsa.SignCompact` from `([]byte, error)` to `[]byte`.
go-ethereum v1.13.15 calls the two-value form in
`crypto/signature_nocgo.go`, so pulling `btcec/v2` that far forward
breaks **every cgo-less build** - including the cross-compiled release
binaries that `make release` produces. Raising btcd past v0.24.2
requires upgrading go-ethereum first, which is out of scope here.
- **v0.26** additionally restructured btcd: `wire`, `txscript`,
`chaincfg`, `chainhash`, and `btcutil` moved to separate `/v2` modules
and the plain-path packages no longer exist. The go-electrum and tss-lib
forks import the plain paths with import strings we don't control, so
v0.26.x isn't reachable without forking both.

So this is a compatibility ceiling, not a security gap - v0.24.2 is
advisory-clean. Both constraints are recorded in the `go.mod` comment so
the next person to touch this pin doesn't have to rediscover them.

### The `btcd/v2` alias goes too

`replace github.com/btcsuite/btcd/v2 => github.com/btcsuite/btcd
v0.23.4` only isolates *self-contained* packages. Any aliased package
that imports a sibling btcd package does so by plain path, which
resolved back to the **v0.22.3** module - so the "patched" alias was
linking vulnerable `wire`/`txscript` underneath it. With the downgrade
gone the alias has no purpose, and the two `pkg/bitcoin/electrum` files
using it return to plain paths.

## First-party fallout

- `txscript.NewTxSigHashes` takes a `PrevOutputFetcher` since v0.23 and
(as of v0.24.2) requires the previous outputs of all inputs.
`TransactionBuilder` now registers each input's locking script and value
in a `MultiPrevOutFetcher` as inputs are added - it already fetched
exactly that data for its sighash args. `ComputeSignatureHashes` also
asserts the set is complete and returns an error, rather than letting a
future gap reach the panicking path.
- `mempool.GetTxVirtualSize` / `btcutil.Hash160` / `btcutil.NewTx` move
from the archived `btcsuite/btcutil` to `btcd/btcutil` (same semantics).

### Deserialization cap and recover - required by the version jump

btcd v0.24.2 rewrote `MsgTx.btcDecode` to slice every script of a
transaction out of **one fixed-size 4 MiB buffer** (`scriptSlabSize = 1
<< 22`), advancing it after each read. `readScriptBuf` bounds each
individual script against `maxWitnessItemSize` but never against the
*remaining* buffer, so once the cumulative script length of a single
transaction passes 4 MiB the next slice **panics** instead of returning
an error:

```
panic: runtime error: slice bounds out of range [:900000] with capacity 594304
  btcd@v0.24.2/wire/msgtx.go:1041  readScriptBuf
  ...
  pkg/bitcoin/transaction.go       (*Transaction).Deserialize
```

Reproduced directly: 4 × 900 KB output scripts decode cleanly, 5 panic.
Both decoders this PR replaces (v0.22.3 and the v0.23.4 alias) allocate
per script and handle the same bytes fine, so this would be a
**regression introduced by the upgrade** - on a path fed by the
untrusted Electrum backend. `decodeTransaction` runs *before*
`GetTransaction`'s txid check, `GetTransactionConfirmations` performs no
hash check at all, and no `recover()` covers the tbtc signing paths, so
it would terminate the node. Upstream fixed it only in the split-out
`wire/v2` module, part of the v0.26 layout the plain-path forks make
unreachable.

The fix is two layers, and **only the combination closes the panic**: a
`MaxTransactionByteLength = 4_000_000` cap applied in
`bitcoin.Transaction.Deserialize` and `electrum.decodeTransaction` (a
transaction's weight is `3*base_size + total_size` and cannot exceed the
4,000,000 WU maximum block weight, so a consensus-valid transaction is
always strictly shorter than the cap - nothing legitimate is rejected),
**plus** a `recover()` that turns the slice-bounds panic that can still
fire below the cap into a plain error. Two regression tests
(`TestTransaction_DeserializeDeclaredNotDelivered` and
`TestDecodeTransactionDeclaredNotDelivered`, both added in `626538d2`)
construct a 194,378-byte (189.8 KiB, 4.86% of the cap) transaction that
panics in unpatched btcd and confirm the `recover()` returns the error.
The cap alone is *not* sufficient - without the `recover()`, payloads of
that size terminate the process. The cap bounds grossly oversized input;
the `recover()` is what actually turns the under-cap panic into an
error.

A subsequent commit also pre-checks `len(rawTx)/2` against
`MaxTransactionByteLength` in `electrum.decodeTransaction` *before*
`hex.DecodeString` allocates, so a malicious multi-GB hex string can't
pay the full allocation before the guard runs.

Note: the `recover()` covers slice-bounds panics from `readScriptBuf`
only. It does **not** cover fatal runtime errors such as out-of-memory
from attacker-declared varint counts that btcd's wire decoder honors
before reading backing bytes. Closing that class of input requires
either a pre-decode budget check on declared input/output counts or a
future btcd release; both are out of scope here.

The v0.22.3-to-v0.24.2 jump also changes btcd's script-decoding
internals beyond the slab panic. Two material changes worth knowing for
future triage:

- The shared script free-list ceiling rises from 6.4 MB globally (12,500
× 512 B entries) to roughly 524 MB globally (125 × 4 MiB slabs) - a ~82×
increase, not ~8000× (the earlier draft conflated bytes-per-entry with
bytes-per-slab). Per-decode worst case *becomes* one 4 MiB slab (v0.22.3
allocated per script); only the global retention grows.
- The per-script cap tightens from 32 MiB (`MaxMessagePayload`) to
4,000,000 (`maxWitnessItemSize`) - strictly safer, no legitimate
transaction is affected.

Neither is a security issue; both are documented in the corresponding
`MaxTransactionByteLength` doc comment and in `VENDOR.md`.

### Docker

`third_party/` is copied before `RUN go mod download`. The directory
`replace` target must exist for the module graph to resolve, so the
image build would otherwise fail at that step even though local builds
pass.

## Verification

- `go build ./...`, `gofmt -l .` clean; full `go test ./...` green.
- `CGO_ENABLED=0 go build` for `darwin/amd64`, `darwin/arm64`,
`linux/amd64`, `linux/arm64`, `windows/amd64` - the cgo-less
cross-compile path `make release` uses, which selects go-ethereum's
`signature_nocgo.go` and is where the `btcec/v2` incompatibility above
shows up.
- `go vet ./...` reports only the pre-existing
`pkg/tecdsa/signing/protocol.go` copylock warning, byte-identical on
unmodified `main`.
- `TestTransactionBuilder_Signing` byte-vector fixtures pass: DER
signatures, sighashes, and compressed public keys are **byte-identical**
across the version jump. The signing path still uses v1 btcec (now
vendored, byte-identical), so its implicit BIP-62 low-S normalization is
preserved.
- Vendored-copy identity verified via the `VENDOR.md` recipe: only the
four added files (`go.mod`, `go.sum`, `LICENSE`, `VENDOR.md`) differ
from gofmt'd upstream `btcec@v0.22.3`.
- `docker build --target build-sources` passes, including the `go mod
download` step that fails without the `third_party` COPY.
- Panic reproduced before the fix and confirmed to return a clean error
after.

## Known residuals

- The archived `github.com/btcsuite/btcutil` (2019) is still linked,
because the tss-lib fork's `crypto/ckd` imports its `base58` package -
the same unchangeable-import-string constraint that motivates the
vendoring. One pure codec package, no advisories against it; first-party
code no longer touches it. MVS now selects an ~20-months-older
`btcsuite/btcutil` pseudo-version than `main` did (the archived
`v1.0.3-0.20201208143702` direct require is gone, replaced by the
tss-lib fork's `v0.0.0-20190425235716-9e5f4b9a998d`); the only compiled
package (`base58`) is functionally identical between the two commits, so
this is benign.
- `go.sum` keeps `/go.mod`-only hash lines for two old btcd
pseudo-versions reached through transitive requires that MVS overrides.
No `h1:` hashes, so no source is compiled; the vulnerable
`v0.22.3`/`v0.23.4` full hashes are gone. Naive graph-level SCA tools
may still surface them.
- The vendored `btcec` sits at a module path OSV doesn't index, so a
hypothetical *future* advisory against pre-split btcec code wouldn't be
flagged automatically. None of the current three are in `btcec`. The
vendored dir is also invisible to root-module `go test ./...` and has no
automated advisory monitor; byte-identity relies on a human running the
`VENDOR.md` recipe.
- The versionless directory `replace` means external modules importing
keep-core packages can't resolve the btcec requirement. Not a new blast
radius: `main` already carries the same unresolvable-with-replace
pattern for `google.golang.org/protobuf/dev`, which downstream consumers
cannot resolve either. The btcec pseudo-version is new on this branch,
but the underlying pattern is pre-existing.

## Test plan

- [x] `go build ./...` / `go vet ./...` / `gofmt -l .`
- [x] `go test ./...` (full suite)
- [x] Vendored btcec upstream test suite, from the main module and
standalone
- [x] `VENDOR.md` byte-identity verification recipe
- [x] `docker build --target build-sources`
- [ ] CI green on this branch

## Note for #4109

The security-release integration branch carries the same downgrade. Once
this lands, its next rebase picks the fix up; the `go.mod` resolution
there is mechanical (that branch has a much newer dependency set, so the
conflict is in surrounding lines rather than the btcd ones).



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Bitcoin transaction decoding now rejects oversized data and returns
errors instead of crashing on malformed input.
* Signature-hash generation now reports missing previous transaction
outputs.
* Updated Bitcoin cryptography support improves compatibility and
dependency resolution.

* **Security**
* Added secp256k1 key, signature, encryption, and decryption
capabilities with validation safeguards.

* **Tests**
* Expanded coverage for transaction handling, cryptographic operations,
key parsing, signatures, and error scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@piotr-roslaniec

Copy link
Copy Markdown
Collaborator

Checking in on this security release candidate — it conflicts with main across 15 files (pkg/tbtc/node.go, pkg/tbtc/dkg.go, pkg/bitcoin/transaction_builder.go, pkg/maintainer/spv/spv.go, go.mod/go.sum, among others). Given the security-sensitive scope, we'd rather you drive the rebase on your own timeline than have us touch it. Since this PR is from your fork, we can't push a rebase to it ourselves either way — only you can push to lionakhnazarov/keep-core. Let us know if there's a target window, or if it'd help to split this into smaller pieces.

lionakhnazarov added a commit to lionakhnazarov/keep-core that referenced this pull request Aug 10, 2026
Bring PR threshold-network#4109 current with threshold-network/keep-core main. Keep cutover
participation permits and fold in main's stuck-transaction monitor plus the
btcd/btcec third_party replace. Drop main's node.go file splits that
duplicated the candidate's monolithic node (handlers stay permit-aware).

Co-authored-by: Cursor <cursoragent@cursor.com>
@lionakhnazarov
lionakhnazarov force-pushed the security-release/candidate-1 branch from 6713089 to 2d858b7 Compare August 10, 2026 13:51
lionakhnazarov pushed a commit to lionakhnazarov/keep-core that referenced this pull request Aug 10, 2026
…ee RPCs, monitoring wiring (vet round 2)

Addresses the round-2 vet report (findings 1, 3, 6 + three net-new).

P0 — production discovery/identity is now authoritative, not optional (finding 1):
- Completeness now REQUIRES service-discovery reconciliation and on-chain
  identity verification (CollectorConfig.RequireServiceDiscovery /
  RequireIdentityVerification, set true by the command). A missing feed blocks
  readiness instead of degrading to trusting inventory; the command warns that
  readiness cannot be certified. (isComplete, main.go)
- Service discovery is keyed by network ID (per-instance) not operator-only, so
  multiple instances of one operator resolve to distinct discovered targets
  (ServiceDiscovery.byNetworkID, MetricsURLForInstance, InventoryInstance.NetworkID,
  applyDiscoveredTargets).
- The metrics adapter validates the responding node's self-attested identity
  (diagnostics chain_address + network_id) instead of copying InstanceID/
  OperatorAddress from inventory; a mismatch is rejected (reportadapter.go).

P0 — reporter revision is durable across collector restart (net-new 1):
- ReporterRevision now derives from the attestation timestamp (UnixNano) rather
  than a process-local counter that reset on restart and sat below the persisted
  high-water mark. Added TestCollector_ReportsAcceptedImmediatelyAfterRestart and
  TestMetricsReportSource_ReporterRevisionSurvivesRestart.

P1 — remaining evidence gaps (finding 3):
- StakingProvider must be a canonical, non-zero address; expected image digest
  must be sha256:<64 hex> (test fixture corrected to a full digest); cross-instance
  staking-provider contradictions are fail-closed (last claim no longer wins).

P1 — no collector lock across network calls (net-new 2):
- Identity verification runs BEFORE the central-state lock (verify first, apply
  under lock) so a degraded RPC never blocks readers; the RPC timeout derives from
  the passed-in context (CollectContext(ctx), IdentityVerifier now takes ctx,
  ethCall uses context.WithTimeout(ctx, ...)).

P1 — monitoring deployment wiring (finding 6):
- A non-loopback API bind without an allowlist is refused at startup.
- cutover-readiness.json added to the Grafana ConfigMap; instance-reason text
  panel replaced with a real table sourced from the readiness API via a new
  Infinity datasource (+ plugin install).
- Added a cutover-roster Deployment/Service/PVC and a Prometheus scrape job, plus
  an Alertmanager workload + routing tree matching the alerts' route_to label.
  (Image digest, allowlist CIDR, inventory ConfigMap/Secret, and receiver
  integrations are REPLACE_ placeholders — ops follow-up, documented in README.adoc.)

P1 — 30-day resolved purge (net-new 3): resolution of the semantic conflict.
- The fail-closed reopening of a vanished resolved operator (finding 2, RESOLVED)
  and the naive 30-day purge of a departed resolved operator are in genuine,
  irreconcilable tension for the "resolved operator departs" population: a departed
  operator MUST reopen offline_unknown and be retained indefinitely, so it can
  never age out via the resolved path, and an actively-resolved operator is
  continuously re-confirmed (ResolvedAt refreshed) and must not be dropped. purge
  therefore remains a bounded-store backstop for a resolved record no longer being
  re-confirmed. Restored independent coverage as a white-box test of the purge
  mechanism (TestCollector_PurgeResolvedAfter30Days): a resolved record older than
  the retention window is purged with its instances, while a fresh resolved record
  and any blocking record are retained.

P1/P2 — harness: README/compose/script examples now use an immutable @sha256:
digest instead of the mutable keep-client:candidate tag the harness itself rejects.

P2 — draft PR body: corrected the inaccurate "wire-compatible / no session-ID/KDF
change" claim to scope it to THIS diff only and explicitly note PR threshold-network#4109's Part A
cryptographic changes are wire-breaking and not described here. Live PR/issue
untouched (out of band).

Local CI oracle (format, vet, staticcheck SA*, golangci-lint, gosec, go test ./...,
race tier-2 subset, integration, race over new packages): PASS.
lionakhnazarov added a commit to lionakhnazarov/keep-core that referenced this pull request Aug 10, 2026
Bring PR threshold-network#4109 current with threshold-network/keep-core main. Keep cutover
participation permits and fold in main's stuck-transaction monitor plus the
btcd/btcec third_party replace. Drop main's node.go file splits that
duplicated the candidate's monolithic node (handlers stay permit-aware).
Bring PR threshold-network#4109 current with threshold-network/keep-core main. Keep cutover
participation permits and fold in main's stuck-transaction monitor plus the
btcd/btcec third_party replace. Drop main's node.go file splits that
duplicated the candidate's monolithic node (handlers stay permit-aware).
@lionakhnazarov
lionakhnazarov force-pushed the security-release/candidate-1 branch from 2d858b7 to da9de1b Compare August 10, 2026 14:07
@lionakhnazarov

Copy link
Copy Markdown
Collaborator Author

Checking in on this security release candidate — it conflicts with main across 15 files (pkg/tbtc/node.go, pkg/tbtc/dkg.go, pkg/bitcoin/transaction_builder.go, pkg/maintainer/spv/spv.go, go.mod/go.sum, among others). Given the security-sensitive scope, we'd rather you drive the rebase on your own timeline than have us touch it. Since this PR is from your fork, we can't push a rebase to it ourselves either way — only you can push to lionakhnazarov/keep-core. Let us know if there's a target window, or if it'd help to split this into smaller pieces.

@piotr-roslaniec Thanks for the ping.

I’ve synced security-release/candidate-1 with current main (merge commit on the PR tip) so the conflict set should be cleared for re-review. I’m aiming to keep this as a single PR and finish remaining review/CI follow-ups. I’d prefer not to split unless review becomes blocked on size/scope. The cutover/participation changes are tightly coupled; splitting would likely create awkward intermediate states

lionakhnazarov and others added 4 commits August 10, 2026 17:07
Update GetTransactionConfirmations stub signature after main merge so
cmd tests compile under lint/golangci/CI.
Treat IsDKGResultValid errors (e.g. high-s OpenZeppelin recover reverts)
as invalid results and enter the challenge loop, matching on-chain
challengeDkgResult's validation-reverted path.
…ionLayer

The merge that ported this branch's monolithic node.go from main's split
node files (da9de1b) carried over the transaction monitor's construction,
metrics wiring, and action wiring, but dropped the five lines that actually
started its polling goroutine. The monitor has been registering broadcast
transactions since that merge without ever checking them: no stuck-warning,
no 24h eviction, and the stuck/unmonitored counters read a healthy zero
whether the loop is alive or not.

Restore the single nil-guarded launch at the top of runCoordinationLayer,
bound to its own context so it starts asynchronously and stops with the
node. Add a private check-interval seam so tests don't wait out the
production five-minute tick, and three liveness/progress metrics (running,
check cycles, tracked count) as positive evidence an operator can gate on,
since the existing counters can't distinguish an inert loop from a healthy
one.
@lrsaturnino

Copy link
Copy Markdown
Member

Transparency note for @lionakhnazarov: I accidentally pushed 591b879 directly to security-release/candidate-1 while setting up to open a PR for the transaction-monitor lifecycle-launch fix (a local clone had push.default=upstream, which redirected the push to the tracked ref instead of creating a new branch). I reverted it immediately with a forward commit (d2a95ee) that restores the branch tree to its exact prior state without rewriting shared history, then reopened the same verified patch properly at #15 from my fork. Sorry for the disruption.

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.

4 participants