DealSession

class DealSession(seam: Seam, scheme: CommutativeScheme, myKey: SchemeKeyPair, allPlayers: Set<PlayerId>, myId: PlayerId, scope: CoroutineScope) : ScopedCloseable

Drives a cryptographically fair card deal over a Seam, as an op-based CRDT.

Each card is a CardState; players sequentially encrypt the deck (SRA layers compose because the cipher is commutative), then non-quorum players strip their layer so only the visibility quorum can read each card. State converges via the CardOp stream broadcast over the seam.

Single-writer assumption. The shuffle and strip helpers read the base ciphertext from state outside the _state.update block, run crypto, then commit. If the incoming-op collector races to apply a remote op to the same card in that window, the commit can overwrite converged state with a stale-base ciphertext (canApply checks GSet membership, not ciphertext provenance). This is safe under the current model where each session has exactly one local writer; concurrent local shuffle/strip while remote ops mutate the same card is a production hazard that needs an "await my turn" gate (deferred).

Constructors

Link copied to clipboard
constructor(seam: Seam, scheme: CommutativeScheme, myKey: SchemeKeyPair, allPlayers: Set<PlayerId>, myId: PlayerId, scope: CoroutineScope)

Properties

Link copied to clipboard
val state: StateFlow<DeckState>

Functions

Link copied to clipboard
fun assignQuorums(assignments: Map<Int, Set<PlayerId>>)

Set visibility quorums for each card (local state — no broadcast).

Link copied to clipboard
override fun close()
Link copied to clipboard
fun decrypt(cardIndex: Int): ByteArray

Remove my own encryption layer from the card at cardIndex and decode the original plaintext. Call once the card is CardPhase.REVEALED (all non-quorum players have stripped). Local — does not broadcast.

Link copied to clipboard
suspend fun depositKey(escrow: KeyEscrow)

Optionally escrow my key to a trusted server for liveness recovery.

Link copied to clipboard
suspend fun shuffle(deck: List<ByteArray>)

Encrypt every card with my key. Seeds the deck from deck on first call (encoding each plaintext once via encodePlaintext). If remote ops have already grown the deck with placeholder cards (the synchronous test harness delivers the first shuffler's ops before the second shuffler calls this), the deck is used as-is so the second shuffler's key composes on top.

Link copied to clipboard
suspend fun strip()

Strip my encryption layer from all cards where I am not in the visibility quorum.