Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
a1b3e12
Add FROST migration scaffold package and RFC
Feb 19, 2026
2d0a5c8
Set Schnorr/FROST RFC author to Threshold Labs
Feb 19, 2026
fc58fed
Wire tbtc runtime signing flow to frost signature types
Feb 20, 2026
2702c7a
Add FROST retry and coordinator attempt metadata path
Feb 20, 2026
3fc7c9f
Fix triplet retry eligibility to use third operator seat count
Feb 20, 2026
b57775a
Add pluggable FROST signing backend execution seam
Feb 20, 2026
952946f
Wire tbtc config to selectable FROST signing backend
Feb 20, 2026
0df807c
Add native FROST signing backend scaffold
Feb 20, 2026
7817a13
Expose tbtc FROST signing backend CLI flag
Feb 20, 2026
fc4c750
Add build-tagged native FROST adapter bootstrap
Feb 20, 2026
f85b3d5
Test FROST backend selection in node startup
Feb 20, 2026
01ea9f4
Execute native-tag FROST adapter via legacy bridge
Feb 20, 2026
9734656
Harden backend-state test guidance after interim review
Feb 20, 2026
f57aa09
Add native bridge scaffold with fallback routing
Feb 20, 2026
7efb2f9
Split native and ffi backend fallback semantics
Feb 20, 2026
e42bf43
Fail fast for strict ffi mode availability
Feb 20, 2026
606e73d
Add dynamic native bridge registration for ffi
Feb 20, 2026
d007635
Register build-tagged bridge for strict ffi path
Feb 20, 2026
8e23f5e
frost/signing: add ffi executor registration path
Feb 20, 2026
ed642b2
frost/signing: restore mode on failed backend selection
Feb 20, 2026
74e894c
frost/signing: decouple request signer material from tecdsa share
Feb 20, 2026
656f62f
frost/tbtc: thread generic signer material through execute request
Feb 20, 2026
83bf3af
frost/signing: add native signer material and ffi adapter contract
Feb 20, 2026
3083e15
frost/signing: include transport context in ffi adapter request
Feb 21, 2026
31026eb
tbtc: persist native signer material envelope in signer state
Feb 21, 2026
a1525a0
frost/native: fallback when ffi signer material is unavailable
Feb 21, 2026
eeaad8f
tbtc: add signer-material resolver hook for dkg signer creation
Feb 21, 2026
4069ffe
frost/native: add provider-based build registration hooks
Feb 21, 2026
c3e8b02
frost/native: wire default transitional provider integrations
Feb 21, 2026
9aa474c
tbtc: resolve legacy signer material through build resolver on load
Feb 21, 2026
245c64c
tbtc: add frost-native legacy roundtrip migration test
Feb 21, 2026
8050396
tbtc: recover legacy key share from native envelope on load
Feb 21, 2026
6532456
tbtc: synchronize signing-done state and retransmission backoff ticks
Feb 21, 2026
8ef5071
frost/native: add v2 native round-signing protocol path
Feb 21, 2026
753bf31
tbtc: validate strict ffi path with v2 signer material
Feb 21, 2026
f765eec
Add build-tagged UniFFI native FROST signing engine scaffold
Feb 21, 2026
90caa23
tbtc: thread canonical wallet ID compatibility through chain models
Feb 21, 2026
a49e35d
tbtc: refresh bridge bindings and wire canonical wallet IDs
Feb 21, 2026
bbd1b53
tbtc: keep bridge address embed placeholder empty
Feb 21, 2026
b418549
tbtc: lower expected wallet closure resolution miss to debug
Feb 22, 2026
5d0b9da
tbtc: add wallet-id fallback coverage for ethereum adapter
Feb 22, 2026
8f9016d
feat(frost): scaffold tbtc-signer native engine registration
mswilkison Feb 23, 2026
db36fa0
refactor(frost): scaffold coarse tbtc-signer session engine path
mswilkison Feb 23, 2026
abe32f9
Add frost_tbtc_signer material payload and legacy fallback shim
mswilkison Feb 23, 2026
7d74326
Address Claude review blockers in tbtc-signer scaffold
mswilkison Feb 23, 2026
e837a32
Add tbtc-signer fallback telemetry and metric wiring
mswilkison Feb 23, 2026
f2ad3ae
Wire tbtc-signer cgo bridge with runtime symbol resolution
mswilkison Feb 23, 2026
b82db05
Add bridge payload/response tests for tbtc-signer cgo engine
mswilkison Feb 23, 2026
4ff5405
Add RunDKG support to tbtc-signer coarse bridge
mswilkison Feb 23, 2026
29a46ab
Invoke RunDKG in transitional tbtc-signer signing path
mswilkison Feb 23, 2026
af5fe87
Gate coarse round scaffold on bootstrap tbtc-signer version
mswilkison Feb 23, 2026
b953dc5
Treat RunDKG key-group as authoritative for legacy scaffold source
mswilkison Feb 23, 2026
5a9ea5b
Make bootstrap synthetic contributions deterministic and round-bound
mswilkison Feb 23, 2026
8ec68f3
Harden bootstrap gate and fallback diagnostics
mswilkison Feb 23, 2026
1c36eb5
Plumb tbtc-signer bootstrap contributions over channel
mswilkison Feb 23, 2026
f6e1943
Plumb member-scoped StartSignRound contributions
mswilkison Feb 23, 2026
cfc38dc
Pass signing participants into tbtc-signer start round
mswilkison Feb 24, 2026
9f555e6
Add threshold-cohort coverage for tbtc-signer bootstrap round
mswilkison Feb 24, 2026
69e8442
Add bootstrap attempt-variation cohort conflict coverage
mswilkison Feb 24, 2026
9ff8804
Add native FROST cohort attempt-variation coverage
mswilkison Feb 24, 2026
d63d08b
Add signer-executor cohort retry integration coverage
mswilkison Feb 24, 2026
7814f81
Add tbtc-signer runtime cohort retry integration test
mswilkison Feb 24, 2026
7f7b9a2
Fix coarse-round participant validation and member derivation consist…
mswilkison Feb 24, 2026
7012a16
Run gofmt on signing request struct
mswilkison Feb 24, 2026
ad2aeb5
Fix tbtc signer material symbols for non-frost builds
mswilkison Feb 24, 2026
a7157c9
Update tbtcpg LocalChain test double for BridgeChain method
mswilkison Feb 24, 2026
d4e8323
Make tbtc bridge access resilient to generated API variants
mswilkison Feb 24, 2026
c372eff
Harden V2 wallet event reflection and expand coverage
mswilkison Feb 24, 2026
a71975e
Stabilize tbtc signer tests for native material migration
mswilkison Feb 24, 2026
4582710
Pass explicit signing participants to tbtc-signer StartSignRound (#3868)
mswilkison Feb 24, 2026
f7f618d
Scaffold frost_tbtc_signer native engine registration (#3867)
mswilkison Feb 24, 2026
661febf
Merge remote-tracking branch 'origin/main' into feat/frost-schnorr-mi…
mswilkison Feb 24, 2026
baf63ea
Switch tbtc-signer bootstrap path to coarse signature output
mswilkison Feb 24, 2026
71b33ec
Document decode validation boundary and test decode-fallback path
mswilkison Feb 24, 2026
346e87b
Add coarse-signature success telemetry
mswilkison Feb 24, 2026
c14712a
Document coarse observer scope and nil-observer no-op
mswilkison Feb 24, 2026
98301c9
Guard tbtc-signer telemetry observer re-registration
mswilkison Feb 24, 2026
1b483cd
Wire BuildTaprootTx through keep-core wallet orchestration
mswilkison Feb 25, 2026
a11cbfa
Split native bridge operation errors and compare BuildTaprootTx IO
mswilkison Feb 25, 2026
a8e1513
Gate BuildTaprootTx signing substitution on verified native tx IO
mswilkison Feb 25, 2026
bd9efb6
Add end-to-end tests for BuildTaprootTx substitution path
mswilkison Feb 25, 2026
9a708a6
Harden BuildTaprootTx substitution E2E coverage
mswilkison Feb 25, 2026
3743421
Harden native BuildTaprootTx structural divergence checks
mswilkison Feb 25, 2026
016f2f7
Clean up legacy BuildTaprootTx IO divergence path
mswilkison Feb 25, 2026
2454c3e
Remove dead unsigned input reference converter
mswilkison Feb 25, 2026
7dff0d2
Reject signed data in unsigned transaction replacement
mswilkison Feb 25, 2026
4490ac4
Add detailed BuildTaprootTx divergence diagnostics
mswilkison Feb 26, 2026
5451d92
Harden ReplaceUnsignedTransaction against pre-populated signing data …
mswilkison Feb 26, 2026
03665fd
Cut over bootstrap tbtc-signer path to coarse signature output (#3869)
mswilkison Feb 26, 2026
69b5ffa
Classify tbtc-signer operation errors as bridge failures
mswilkison Feb 26, 2026
1c2ea9f
Apply review follow-ups for bridge error taxonomy
mswilkison Feb 26, 2026
8da4253
Harden BuildTaprootTx error-path tests
mswilkison Feb 26, 2026
ff5c0b7
Harden tbtc-signer bridge error classification (#3871)
mswilkison Feb 26, 2026
d4e95c5
Remove UniFFI SDK dependency path from frost-native build
mswilkison Feb 26, 2026
106642d
Remove dead UniFFI legacy registration stubs
mswilkison Feb 26, 2026
b10fd0c
frost/signing: enforce attempt coordinator inclusion policy
mswilkison Feb 27, 2026
5f23834
frost/signing: enforce attempt coordinator inclusion policy (#3872)
mswilkison Feb 27, 2026
99294e2
frost/signing: fail closed on invalid coarse attempt policy
mswilkison Feb 27, 2026
53bae0c
frost/signing: fail closed on invalid coarse attempt policy (#3873)
mswilkison Feb 27, 2026
b19e57c
frost/signing: add coarse attempt-policy error matrix coverage
mswilkison Feb 27, 2026
85f843c
frost/signing: add coarse attempt-policy error matrix coverage (#3874)
mswilkison Feb 27, 2026
1415e04
fix: address gosec G118 context cancellation findings
mswilkison Feb 27, 2026
7a0b24a
fix: annotate scheduler cancel lifecycle for gosec
mswilkison Feb 27, 2026
23b3b94
fix: address gosec G118 context cancellation findings (#3875)
mswilkison Feb 27, 2026
d1eaa11
frost-signing: fail-close consumed attempt replay errors
mswilkison Feb 27, 2026
4d194b9
frost-signing: fail-close consumed attempt replay errors (#3876)
mswilkison Feb 27, 2026
c3d6833
test(frost): add Gemini audit coverage for ffi error payloads
mswilkison Mar 1, 2026
37b2ce7
Merge remote-tracking branch 'origin/main' into HEAD
mswilkison May 17, 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
9 changes: 9 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,15 @@ func initTbtcFlags(cmd *cobra.Command, cfg *config.Config) {
tbtc.DefaultKeyGenerationConcurrency,
"tECDSA key generation concurrency.",
)

cmd.Flags().StringVar(
&cfg.Tbtc.FrostSigningBackend,
"tbtc.frostSigningBackend",
"",
"FROST signing backend name (legacy, native, ffi). "+
"`native` allows transitional legacy fallback; `ffi` requires native execution. "+
"Empty value selects legacy.",
)
}

// Initialize flags for Maintainer configuration.
Expand Down
7 changes: 7 additions & 0 deletions cmd/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ var cmdFlagsTests = map[string]struct {
expectedValueFromFlag: 101,
defaultValue: runtime.GOMAXPROCS(0),
},
"tbtc.frostSigningBackend": {
readValueFunc: func(c *config.Config) interface{} { return c.Tbtc.FrostSigningBackend },
flagName: "--tbtc.frostSigningBackend",
flagValue: "native",
expectedValueFromFlag: "native",
defaultValue: "",
},
"maintainer.bitcoinDifficulty": {
readValueFunc: func(c *config.Config) interface{} { return c.Maintainer.BitcoinDifficulty.Enabled },
flagName: "--bitcoinDifficulty",
Expand Down
49 changes: 49 additions & 0 deletions docs/rfc/rfc-20-schnorr-frost-migration-scaffold.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
= RFC-20: Schnorr/FROST Migration Scaffold

*Author:* Threshold Labs
*Status:* Draft
*Date:* 2026-02-19

== Summary

This RFC introduces the initial keep-core scaffolding for migrating tBTC from
threshold ECDSA signatures to Schnorr/FROST signatures.

This change does not switch runtime signing logic yet. It defines core data
types and compatibility helpers required by follow-up protocol, chain, and
wallet orchestration changes.

== Initial Deliverables

* New `pkg/frost` package with:
** Taproot x-only output key type (`OutputKey`)
** BIP-340 Schnorr signature type (`Signature`)
** Serialization and logging helpers for Schnorr signatures
** Legacy compatibility alias helper:
`HASH160(0x02 || xOnlyOutputKey)`

== Compatibility Model

FROST wallets are expected to use 32-byte x-only keys as canonical identifiers.
During migration, legacy 20-byte wallet key hash paths are supported via
compatibility alias:

----
walletPubKeyHashCompat = HASH160(0x02 || xOnlyOutputKey)
----

== Follow-up Work

1. Add FROST signer and coordinator interfaces to replace `pkg/tecdsa/signing`.
2. Introduce FROST DKG executor replacing GG18 pre-params and DKG wiring.
3. Update tBTC chain interfaces and wallet registry integration to accept
x-only keys as canonical wallet identities.
4. Update Bitcoin transaction builders to support P2TR key-path spends.
5. Add dual-stack runtime routing: GG18 existing wallets + FROST new wallets.
6. Add full integration tests for mixed wallet generations and migration flows.

== Non-Goals (This RFC Revision)

* No production FROST coordinator implementation.
* No on-chain contract ABI migration in this repository.
* No replacement of existing GG18 runtime paths yet.
138 changes: 138 additions & 0 deletions pkg/bitcoin/transaction_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bitcoin

import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"

Expand Down Expand Up @@ -309,6 +310,143 @@ func (tb *TransactionBuilder) TotalInputsValue() int64 {
return totalInputsValue
}

// ReplaceUnsignedTransaction replaces the internal unsigned transaction while
// preserving per-input sighash metadata collected during builder input setup.
func (tb *TransactionBuilder) ReplaceUnsignedTransaction(
transaction *Transaction,
) error {
if transaction == nil {
return fmt.Errorf("transaction is nil")
}

if len(transaction.Inputs) != len(tb.sigHashArgs) {
return fmt.Errorf(
"input metadata mismatch: [%d] tx inputs, [%d] sighash args",
len(transaction.Inputs),
len(tb.sigHashArgs),
)
}

previousInputs := tb.internal.TxIn

replacedInternal := newInternalTransaction()
replacedInternal.fromTransaction(transaction)

for i := range replacedInternal.TxIn {
previousInput := previousInputs[i]
replacedInput := replacedInternal.TxIn[i]

if previousInput == nil || replacedInput == nil {
continue
}

if len(replacedInput.SignatureScript) > 0 {
return fmt.Errorf(
"replacement transaction input [%d] has unexpected non-empty signature script",
i,
)
}

if len(replacedInput.Witness) > 0 {
return fmt.Errorf(
"replacement transaction input [%d] has unexpected non-empty witness",
i,
)
}

if tb.sigHashArgs[i].witness {
if len(replacedInput.Witness) == 0 && len(previousInput.Witness) == 1 {
redeemScript := append([]byte{}, previousInput.Witness[0]...)
replacedInput.Witness = wire.TxWitness{redeemScript}
}
} else {
if len(replacedInput.SignatureScript) == 0 && len(previousInput.SignatureScript) > 0 {
replacedInput.SignatureScript = append(
[]byte{},
previousInput.SignatureScript...,
)
}
}
}

tb.internal = replacedInternal
tb.sigHashes = nil

return nil
}

// UnsignedTransaction returns the current unsigned transaction builder state.
func (tb *TransactionBuilder) UnsignedTransaction() *Transaction {
return tb.internal.toTransaction()
}

// UnsignedTransactionInput carries canonical unsigned input metadata extracted
// from the builder state.
type UnsignedTransactionInput struct {
TxIDHex string
Vout uint32
ValueSats uint64
}

// UnsignedTransactionOutput carries canonical unsigned output metadata
// extracted from the builder state.
type UnsignedTransactionOutput struct {
ScriptPubKeyHex string
ValueSats uint64
}

// UnsignedTransactionIO returns canonical unsigned transaction input/output
// metadata from the builder state.
func (tb *TransactionBuilder) UnsignedTransactionIO() (
[]UnsignedTransactionInput,
[]UnsignedTransactionOutput,
error,
) {
if len(tb.internal.TxIn) != len(tb.sigHashArgs) {
return nil, nil, fmt.Errorf(
"input metadata mismatch: [%d] tx inputs, [%d] sighash args",
len(tb.internal.TxIn),
len(tb.sigHashArgs),
)
}

inputs := make([]UnsignedTransactionInput, 0, len(tb.internal.TxIn))
for i, input := range tb.internal.TxIn {
value := tb.sigHashArgs[i].value
if value < 0 {
return nil, nil, fmt.Errorf("input [%d] value is negative", i)
}

inputs = append(
inputs,
UnsignedTransactionInput{
// chainhash.Hash.String renders txid in standard Bitcoin display
// (RPC/explorer) byte order, i.e. reversed vs internal bytes.
TxIDHex: input.PreviousOutPoint.Hash.String(),
Vout: input.PreviousOutPoint.Index,
ValueSats: uint64(value),
},
)
}

outputs := make([]UnsignedTransactionOutput, 0, len(tb.internal.TxOut))
for i, output := range tb.internal.TxOut {
if output.Value < 0 {
return nil, nil, fmt.Errorf("output [%d] value is negative", i)
}

outputs = append(
outputs,
UnsignedTransactionOutput{
ScriptPubKeyHex: hex.EncodeToString(output.PkScript),
ValueSats: uint64(output.Value),
},
)
}

return inputs, outputs, nil
}

// inputSigHashArgs is a helper structure holding some arguments required to
// compute a sighash for the given input.
type inputSigHashArgs struct {
Expand Down
Loading
Loading