Add storage adapters

This commit is contained in:
Jon Staab
2026-06-08 14:42:08 -07:00
parent 19c741684c
commit 97cfbd3c22
8 changed files with 258 additions and 52 deletions
+141
View File
@@ -0,0 +1,141 @@
import type { ISigner } from "applesauce-signers"
import type { Hex, QuorumMember } from "./protocol"
// ── Quorum & shard records ────────────────────────────────────────────────────
/** A known quorum — no secret material */
export type QuorumRecord = {
quorumPubkey: Hex
members: QuorumMember[]
threshold: number
/** Feldman commitments per participant index, from the last completed DKG or resharing */
commitments: Record<number, Hex[]>
/** Ordered list of kind 7057 inner event IDs (rotation history) */
rotationRecords: Hex[]
}
/** The user's secret share for one quorum, with the shard encrypted at rest */
export type ShardRecord = {
quorumPubkey: Hex
index: number
verificationShare: Hex
/** NIP-44 self-encrypted hex string of the shard bigint */
encryptedShard: string
}
// ── Session helpers ───────────────────────────────────────────────────────────
/** Proof-of-knowledge from a round-1 message */
export type PoKProof = { R: Hex; s: Hex }
/** Round-1 broadcast from one participant */
export type Round1Data = { commitments: Hex[]; proof: PoKProof }
// ── DKG session ───────────────────────────────────────────────────────────────
export type DkgPhase = "round1" | "round2" | "confirming" | "complete"
/** In-progress DKG session, keyed by the kind 7050 inner event ID */
export type DkgSession = {
inviteId: Hex
members: Hex[]
threshold: number
phase: DkgPhase
/** Own Feldman commitments (set once we broadcast round 1) */
myCommitments?: Hex[]
/** Own polynomial coefficients, NIP-44 self-encrypted */
myEncryptedPoly?: string
/** Round-1 data received, keyed by sender pubkey */
round1: Record<Hex, Round1Data>
/** Round-2 shares received, keyed by sender participant index */
round2: Record<number, Hex>
/** Members who declined, keyed by pubkey, value is their optional reason */
declines: Record<Hex, string>
}
// ── Resharing session ─────────────────────────────────────────────────────────
export type ResharingPhase = "round1" | "round2" | "confirming" | "complete"
/** In-progress resharing session, keyed by the kind 7054 inner event ID */
export type ResharingSession = {
proposalId: Hex
quorumPubkey: Hex
contributors: Hex[]
newMembers: Hex[]
newThreshold: number
phase: ResharingPhase
myCommitments?: Hex[]
myEncryptedPoly?: string
/** Round-1 data received, keyed by sender pubkey */
round1: Record<Hex, Round1Data>
/** Round-2 shares received, keyed by sender participant index */
round2: Record<number, Hex>
/** Members who declined, keyed by pubkey, value is their optional reason */
declines: Record<Hex, string>
}
// ── Signing session ───────────────────────────────────────────────────────────
export type SigningPhase = "round1" | "round2" | "complete"
/** In-progress signing session, keyed by the kind 7058 inner event ID */
export type SigningSession = {
requestId: Hex
quorumPubkey: Hex
/** Hex-encoded message bytes */
msgHex: Hex
phase: SigningPhase
signingSet: number[]
/** Own nonce commitments (present until we have broadcast our round-2 share) */
myNonces?: { D: Hex; E: Hex }
/** Round-1 nonce commitments from others, keyed by sender pubkey */
round1: Record<Hex, { D: Hex; E: Hex }>
/** Round-2 signature shares, keyed by participant index */
shares: Record<number, Hex>
/** Members who declined, keyed by pubkey, value is their optional reason */
declines: Record<Hex, string>
}
// ── Shard / polynomial encryption ────────────────────────────────────────────
// Shards and polynomials are self-encrypted: NIP-44 encrypt(myPubkey, ...).
// The signer derives the conversation key from (myPrivkey, myPubkey), which is
// unique to the key and opaque to any other party.
export async function encryptShard(
shard: bigint,
myPubkey: Hex,
signer: ISigner,
): Promise<string> {
if (!signer.nip44) { throw new Error("Signer does not support NIP-44") }
return signer.nip44.encrypt(myPubkey, shard.toString(16))
}
export async function decryptShard(
encryptedShard: string,
myPubkey: Hex,
signer: ISigner,
): Promise<bigint> {
if (!signer.nip44) { throw new Error("Signer does not support NIP-44") }
const hex = await signer.nip44.decrypt(myPubkey, encryptedShard)
return BigInt("0x" + hex)
}
export async function encryptPoly(
poly: bigint[],
myPubkey: Hex,
signer: ISigner,
): Promise<string> {
if (!signer.nip44) { throw new Error("Signer does not support NIP-44") }
return signer.nip44.encrypt(myPubkey, JSON.stringify(poly.map(c => c.toString(16))))
}
export async function decryptPoly(
encryptedPoly: string,
myPubkey: Hex,
signer: ISigner,
): Promise<bigint[]> {
if (!signer.nip44) { throw new Error("Signer does not support NIP-44") }
const json = await signer.nip44.decrypt(myPubkey, encryptedPoly)
return (JSON.parse(json) as string[]).map(h => BigInt("0x" + h))
}