Privacy, control, and safety
Under the published 2-of-3 mainnet configuration, one honest Operator cannot prevent a retained-share attack
The published mainnet SDK inherits a two-of-three Operator threshold. Because the user side combines with any two Operator shares, one honest or nonparticipating Operator cannot by itself prevent two matching retained shares from satisfying the Operator quorum. 1234
What the evidence shows
Technical and product details
- Documented behavior
Spark's published base configuration sets the Operator threshold to two. The mainnet configuration inherits that value and lists three Operators: Lightspark, Breez, and Flashnet. 1
Supporting material shown on this page
published threshold and three OperatorsPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 324 baseUrl: getSspUrl("REGTEST"), 325 identityPublicKey: getSspIdentityPublicKey("REGTEST"), 326 }, 327}; 328 329const MAINNET_WALLET_CONFIG: Required<ConfigOptions> = { 330 ...BASE_CONFIG, - Documented behavior
Operator code describes signing as
1 + (t, n): the user supplies one side andconfig.ThresholdOperators supply the group side. DKG likewise setsMinSignersto the configured threshold. 23Supporting material shown on this page
one-plus-threshold signing constructionPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 453type KeyPackageProvider func(ctx context.Context, config *so.Config, keyshareIDs []uuid.UUID) (map[uuid.UUID]*pbfrost.KeyPackage, error) 454 455func SignFrostInternal(ctx context.Context, config *so.Config, jobs []*SigningJob, getKeyPackages KeyPackageProvider, sparkServiceClientFactory SparkServiceFrostSignerFactory) ([]*SigningResult, error) { 456 selection := OperatorSelection{Option: OperatorSelectionOptionThreshold, Threshold: int(config.Threshold)} 457 signingKeyshareIDs := SigningKeyshareIDsFromSigningJobs(jobs) 458 signingKeyshares, err := getKeyPackages(ctx, config, signingKeyshareIDs) 459 if err != nil {DKG minimum signersPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 45 requestIDString := requestID.String() 46 initRequest := &pbdkg.InitiateDkgRequest{ 47 RequestId: requestIDString, 48 KeyCount: keyCount, 49 MinSigners: config.Threshold, 50 MaxSigners: uint64(len(config.SigningOperatorMap)), 51 CoordinatorIndex: config.Index, - Documented behavior
Any two matching Operator shares therefore satisfy the Operator side of the published default signature; participation by the third Operator is not required. 123
Supporting material shown on this page
published threshold and three OperatorsPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 324 baseUrl: getSspUrl("REGTEST"), 325 identityPublicKey: getSspIdentityPublicKey("REGTEST"), 326 }, 327}; 328 329const MAINNET_WALLET_CONFIG: Required<ConfigOptions> = { 330 ...BASE_CONFIG,one-plus-threshold signing constructionPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 453type KeyPackageProvider func(ctx context.Context, config *so.Config, keyshareIDs []uuid.UUID) (map[uuid.UUID]*pbfrost.KeyPackage, error) 454 455func SignFrostInternal(ctx context.Context, config *so.Config, jobs []*SigningJob, getKeyPackages KeyPackageProvider, sparkServiceClientFactory SparkServiceFrostSignerFactory) ([]*SigningResult, error) { 456 selection := OperatorSelection{Option: OperatorSelectionOptionThreshold, Threshold: int(config.Threshold)} 457 signingKeyshareIDs := SigningKeyshareIDsFromSigningJobs(jobs) 458 signingKeyshares, err := getKeyPackages(ctx, config, signingKeyshareIDs) 459 if err != nil {DKG minimum signersPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 45 requestIDString := requestID.String() 46 initRequest := &pbdkg.InitiateDkgRequest{ 47 RequestId: requestIDString, 48 KeyCount: keyCount, 49 MinSigners: config.Threshold, 50 MaxSigners: uint64(len(config.SigningOperatorMap)), 51 CoordinatorIndex: config.Index,
Interpretation
Why this matters for Spark's trust model
Spark's general research says one honest Operator deleting its old share defeats prior-owner collusion. Under the published two-of-three setting, one honest or nonparticipating Operator cannot veto the other two: preventing reconstruction requires at least two Operators to have deleted or withheld their matching old shares. 1234
This section states the trust-model consequence of the cited evidence. Conditional or inferred claims remain bounded by the next section.
Scope control
What this does not establish
Sources and excerpts
Primary sources
The relevant details and available source-code excerpts appear alongside the claims above. This list preserves the complete original-source trail for independent verification.
published threshold and three Operators
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
one-plus-threshold signing construction
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
DKG minimum signers
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
Spark's one-honest-Operator explanation
Web source · spark.money · Live source · checked 2026-07-14
How sources, absence findings, and limitations were evaluated