Add storage adapters
This commit is contained in:
+141
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user