Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .github/workflows/frost-cgo-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ jobs:
frost_tbtc_state_anchor_bootstrap_facts \
frost_tbtc_acknowledge_state_witness_checkpoint \
frost_tbtc_recover_state_witness_checkpoint \
frost_tbtc_begin_share_repair_session \
frost_tbtc_finish_share_repair_session \
frost_tbtc_share_repair_part1 \
frost_tbtc_share_repair_part2 \
frost_tbtc_install_repaired_share \
frost_tbtc_version \
frost_tbtc_abi_version; do
if ! nm -D --defined-only "$lib" | grep -q " ${sym}$"; then
Expand Down
2 changes: 1 addition & 1 deletion ci/frost-signer-pin.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#
# After the scaffold and mirror branches merge into one, replace the cross-branch
# checkout with an in-tree cargo build and retire this pin (keep the gate).
FROST_SIGNER_MIRROR_REF=08b6d6f40027016101f32c1ffc509fde4746d0a2
FROST_SIGNER_MIRROR_REF=c0d08c0c0c831c54ff765c9520be674789b12efa
5 changes: 5 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"context"
"errors"
"fmt"
"time"

Expand Down Expand Up @@ -249,6 +250,10 @@ func start(cmd *cobra.Command) error {
clientConfig.Ethereum.Network,
)
if err != nil {
if errors.Is(err, tbtc.ErrFrostShareRepairMaintenanceComplete) {
logger.Infof("%v", tbtc.ErrFrostShareRepairMaintenanceComplete)
return nil
}
return fmt.Errorf("error initializing TBTC: [%v]", err)
}
}
Expand Down
3 changes: 3 additions & 0 deletions docs/development/frost-readiness-manifest.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ This manifest was originally planned for the tBTC monorepo's
binaries adopt the `frost_roast_retry` build tag once the gates
below read `present`
(`docs/development/frost-roast-retry-rollout.adoc`).
Share repair is a separately gated disaster-recovery capability; its
ceremony, crash matrix, and production-shape test are specified in
`docs/development/frost-share-repair.adoc`.

== Gates

Expand Down
293 changes: 293 additions & 0 deletions docs/development/frost-share-repair.adoc

Large diffs are not rendered by default.

69 changes: 45 additions & 24 deletions pkg/chain/ethereum/tbtc_frost_pre_sign_authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ type frostPreSignManifestNativeSignerAnchor struct {
type frostPreSignManifestFrostSigner struct {
TrustDomainID string `json:"trustDomainID"`
DurableSessionStoreFingerprint string `json:"durableSessionStoreFingerprint"`
ShareRepairActivationRegistryRoot string `json:"shareRepairActivationRegistryRoot,omitempty"`
ProtocolID string `json:"protocolID"`
ReservationProtocolID string `json:"reservationProtocolID"`
BitcoinOutboxProtocolID string `json:"bitcoinOutboxProtocolID"`
Expand Down Expand Up @@ -1020,6 +1021,14 @@ func validateFrostPreSignActivationManifest(
if err != nil || durableSessionStoreFingerprint == [32]byte{} {
return fmt.Errorf("invalid FROST durable session store fingerprint")
}
if frost.ShareRepairActivationRegistryRoot != "" {
shareRepairActivationRegistryRoot, err := frostPreSignParseBytes32(
frost.ShareRepairActivationRegistryRoot,
)
if err != nil || shareRepairActivationRegistryRoot == [32]byte{} {
return fmt.Errorf("invalid FROST share-repair activation registry root")
}
}
anchorManifest, err := frostPreSignNativeSignerAnchorManifest(manifest)
if err != nil {
return err
Expand Down Expand Up @@ -2923,31 +2932,43 @@ func (tc *TbtcChain) FrostPreSignActivationRuntimeManifest() (
if err != nil {
return tbtc.FrostPreSignActivationRuntimeManifest{}, err
}
shareRepairActivationRegistryRoot := [32]byte{}
if frost.ShareRepairActivationRegistryRoot != "" {
shareRepairActivationRegistryRoot, err = parse(
frost.ShareRepairActivationRegistryRoot,
)
if err != nil || shareRepairActivationRegistryRoot == [32]byte{} {
return tbtc.FrostPreSignActivationRuntimeManifest{}, fmt.Errorf(
"invalid FROST share-repair activation registry root",
)
}
}
return tbtc.FrostPreSignActivationRuntimeManifest{
ManifestHash: adapter.profile.ActivationManifestHash,
ActivationAuthorityKeyHash: adapter.manifest.activationAuthorityKeyHash,
VerifierOperatorFingerprint: verifierOperatorFingerprint,
HandshakeOperatorFingerprint: handshakeOperatorFingerprint,
DomainChainID: adapter.profile.DomainChainID,
GenesisBlockHash: genesisBlockHash,
ProfileHash: adapter.profile.ProfileHash,
ImplementationSetHash: adapter.profile.ImplementationSetHash,
LinkedLibraryDescriptorSetHash: linkedLibraryDescriptorSetHash,
EndpointIdentitySetHash: endpointIdentitySetHash,
Deployments: frostPreSignRuntimeDeploymentEvidence(adapter.deployments),
SignerProtocolID: signerProtocolID,
ReservationProtocolID: adapter.profile.ReservationProtocolID,
BitcoinOutboxProtocolID: bitcoinOutboxProtocolID,
SigningPolicyHash: adapter.profile.SigningPolicyHash,
DurableSessionStoreFingerprint: frost.DurableSessionStoreFingerprint,
CompleteRouterAddress: adapter.profile.CompleteRouter,
AuthorizationRegistryAddress: adapter.profile.RegistryAddress,
AttestationSignerKeyHash: attestationSignerKeyHash,
Threshold: frost.Threshold,
MaximumGroupSize: frost.MaximumGroupSize,
RetainedGroupInventoryProtocolID: retainedGroupInventoryProtocolID,
NativeSignerAnchor: nativeSignerAnchor,
ActivationAuthorityPublicKey: adapter.manifest.activationAuthorityPublicKey,
ManifestHash: adapter.profile.ActivationManifestHash,
ActivationAuthorityKeyHash: adapter.manifest.activationAuthorityKeyHash,
VerifierOperatorFingerprint: verifierOperatorFingerprint,
HandshakeOperatorFingerprint: handshakeOperatorFingerprint,
DomainChainID: adapter.profile.DomainChainID,
GenesisBlockHash: genesisBlockHash,
ProfileHash: adapter.profile.ProfileHash,
ImplementationSetHash: adapter.profile.ImplementationSetHash,
LinkedLibraryDescriptorSetHash: linkedLibraryDescriptorSetHash,
EndpointIdentitySetHash: endpointIdentitySetHash,
Deployments: frostPreSignRuntimeDeploymentEvidence(adapter.deployments),
SignerProtocolID: signerProtocolID,
ReservationProtocolID: adapter.profile.ReservationProtocolID,
BitcoinOutboxProtocolID: bitcoinOutboxProtocolID,
SigningPolicyHash: adapter.profile.SigningPolicyHash,
DurableSessionStoreFingerprint: frost.DurableSessionStoreFingerprint,
ShareRepairActivationRegistryRoot: shareRepairActivationRegistryRoot,
CompleteRouterAddress: adapter.profile.CompleteRouter,
AuthorizationRegistryAddress: adapter.profile.RegistryAddress,
AttestationSignerKeyHash: attestationSignerKeyHash,
Threshold: frost.Threshold,
MaximumGroupSize: frost.MaximumGroupSize,
RetainedGroupInventoryProtocolID: retainedGroupInventoryProtocolID,
NativeSignerAnchor: nativeSignerAnchor,
ActivationAuthorityPublicKey: adapter.manifest.activationAuthorityPublicKey,
CanonicalJournal: tbtc.FrostRetainedGroupCanonicalJournalManifest{
StoreID: journal.StoreID,
StoreFingerprint: storeFingerprint,
Expand Down
15 changes: 15 additions & 0 deletions pkg/chain/ethereum/tbtc_frost_retained_group_manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,21 @@ func TestValidateFrostPreSignActivationManifest_CanonicalJournal(t *testing.T) {
t.Fatalf("expected durable-session fingerprint failure, got [%v]", err)
}
})
t.Run("valid share-repair activation registry root", func(t *testing.T) {
manifest := testFrostJournalActivationManifest()
manifest.FrostSigner.ShareRepairActivationRegistryRoot = testManifestHex32(0x18)
if err := validateFrostPreSignActivationManifest(manifest); err != nil {
t.Fatalf("expected optional share-repair registry root to validate: %v", err)
}
})
t.Run("malformed share-repair activation registry root", func(t *testing.T) {
manifest := testFrostJournalActivationManifest()
manifest.FrostSigner.ShareRepairActivationRegistryRoot = "operator-authored-label"
if err := validateFrostPreSignActivationManifest(manifest); err == nil ||
!strings.Contains(err.Error(), "share-repair activation registry root") {
t.Fatalf("expected share-repair registry-root failure, got [%v]", err)
}
})
t.Run("native anchor stream mismatch", func(t *testing.T) {
manifest := testFrostJournalActivationManifest()
manifest.FrostSigner.NativeSignerAnchor.StreamID = testManifestHex32(0xee)
Expand Down
Loading
Loading