Files
chains/zkvm/fhe/threshold_quorum_test.go
zeekayandHanzo Dev 3d38bef46f zkvm/fhe: count distinct parties toward the decryption threshold
SubmitDecryptionShare stored the share in a map keyed by partyID, which dedups,
but incremented a separate ShareCount on every call and compared THAT to the
threshold. One party calling three times on a 3-of-5 session drove ShareCount to
3 and flipped the session to StatusProcessing, so a single share holder
satisfied the quorum and the value t-of-n exists to protect was released to it.
Proven: three calls from partyID 1 left ShareCount == 3.

The share set is now the only source of truth — ShareCount is len(Shares) — and
a repeat submission from a party that already contributed is refused outright.
The sibling implementations in mpcvm/fhe already dedup before counting.

Tests: TestOnePartyCannotReachDecryptionThreshold,
TestDistinctPartiesReachDecryptionThreshold.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 16:48:10 -07:00

62 lines
1.9 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package fhe
import (
"testing"
"github.com/stretchr/testify/require"
)
// decryptSession builds a 3-of-5 handler with one open decryption session.
func decryptSession(threshold int) (*FHEProtocolHandler, [32]byte) {
id := [32]byte{1}
h := &FHEProtocolHandler{
threshold: threshold,
total: 5,
sessions: map[[32]byte]*FHESession{
id: {
ID: id,
Type: SessionDecrypt,
Status: StatusPending,
Shares: make(map[uint64][]byte),
},
},
}
return h, id
}
// TestOnePartyCannotReachDecryptionThreshold proves the threshold counts
// DISTINCT parties. One party resubmitting must never satisfy a 3-of-5 quorum:
// that would let a single share holder decrypt a value the scheme exists to keep
// from any minority.
func TestOnePartyCannotReachDecryptionThreshold(t *testing.T) {
require := require.New(t)
h, id := decryptSession(3)
require.NoError(h.SubmitDecryptionShare(id, 1, []byte("share-from-party-1")))
require.Error(h.SubmitDecryptionShare(id, 1, []byte("again")))
require.Error(h.SubmitDecryptionShare(id, 1, []byte("and-again")))
session := h.sessions[id]
require.Equal(1, session.ShareCount, "one party contributed one share")
require.Equal(StatusCollecting, session.Status,
"a single party must not advance the session to processing")
}
// TestDistinctPartiesReachDecryptionThreshold is the other half: three different
// parties do satisfy 3-of-5.
func TestDistinctPartiesReachDecryptionThreshold(t *testing.T) {
require := require.New(t)
h, id := decryptSession(3)
require.NoError(h.SubmitDecryptionShare(id, 1, []byte("s1")))
require.NoError(h.SubmitDecryptionShare(id, 2, []byte("s2")))
require.Equal(StatusCollecting, h.sessions[id].Status)
require.NoError(h.SubmitDecryptionShare(id, 3, []byte("s3")))
require.Equal(3, h.sessions[id].ShareCount)
require.Equal(StatusProcessing, h.sessions[id].Status)
}