Protocol Integration
Ice Nine provides integration points for external consensus and coordination protocols. This document describes how to integrate Ice Nine's threshold signing with protocols like Aura.
External Context
External protocols can attach context to signing messages using the ExternalContext structure. This enables binding signatures to consensus instances without coupling Ice Nine to specific protocols.
structure ExternalContext where
consensusId : Option ByteArray := none -- external session identifier
resultId : Option ByteArray := none -- what is being signed (e.g., H(op, prestate))
prestateHash : Option ByteArray := none -- state validation
evidenceDelta : Option ByteArray := none -- CRDT evidence for propagation
Usage Pattern
-
Attach context when creating messages:
let ctx : ExternalContext := { consensusId := some myConsensusId resultId := some (hashOf operation prestate) prestateHash := some prestateHash } let share : SignShareMsg S := { sender := pid, session := sess, z_i := z, context := ctx } -
Validate context consistency during aggregation:
match aggregateWithContextValidation S c signers commits shares with | .ok signature => -- all shares reference same consensus instance | .error msg => -- inconsistent context detected -
Access context from signatures:
let sig := aggregateSignature S c signers commits shares let consensusId := sig.context.consensusId
Evidence Carrier
The EvidenceCarrier typeclass enables piggybacking CRDT evidence deltas on signing messages. This is useful for protocols like Aura that propagate evidence alongside consensus messages.
class EvidenceCarrier (M : Type*) where
attachEvidence : M → ByteArray → M
extractEvidence : M → Option ByteArray
hasEvidence : M → Bool
Instances
All signing message types implement EvidenceCarrier:
SignCommitMsgSignRevealWMsgSignShareMsgSignatureValidatableShare
Usage
-- Attach evidence before sending
let msgWithEvid := EvidenceCarrier.attachEvidence msg myEvidenceDelta
-- Extract evidence on receive
match EvidenceCarrier.extractEvidence msg with
| some delta => mergeEvidence delta
| none => ()
Fast Path Signing
For low-latency consensus, Ice Nine supports precomputed nonces that enable single-round signing.
Precomputed Nonces
Parties can pre-generate nonces during idle time:
-- Generate during idle time
let precomputed := PrecomputedNonce.create S hiding binding opening currentTime
-- Pre-share the commitment
let preshared := PreSharedCommitment.fromPrecomputed S partyId precomputed
broadcast preshared
-- When signing request arrives, produce share immediately
match signFast S keyShare precomputed challenge bindingFactor session context with
| some share => send share
| none => -- norm check failed, need fresh nonce
Nonce Pool Management
For concurrent signing sessions, use NoncePool:
-- Initialize pool
let pool := NoncePool.empty S
-- Add precomputed nonces during idle
let pool := pool.add precomputed1
let pool := pool.add precomputed2
-- Take nonce for signing (automatically prunes expired)
match pool.take currentTime with
| some (nonce, pool') => use nonce, update pool to pool'
| none => generate fresh nonce
Security Assumptions
Fast path signing trusts that:
- Nonce commitments were honestly generated
- Nonces are not being reused
- Precomputed nonces have not expired
- Coordinator will aggregate honestly
Use standard two-round signing when these assumptions don't hold.
Self-Validating Shares
For fallback gossip protocols, ValidatableShare bundles a share with enough context for independent validation:
structure ValidatableShare (S : Scheme) where
share : SignShareMsg S
nonceCommit : S.Commitment
publicHiding : S.Public
publicBinding : S.Public
pkShare : S.Public
Creating ValidatableShares
let vs := ValidatableShare.create S shareMsg commitMsg partyPublicKey
Use Case: Aura Fallback
In Aura's fallback protocol, witnesses gossip shares to achieve threshold:
-- Create self-validating share for gossip
let validatable := ValidatableShare.create S myShare myCommit myPkShare
-- Recipients can validate without full session state
if validatable.isWellFormed then
addToProposals validatable
Aura Integration Example
Here's how Ice Nine maps to Aura's fast path:
Setup Phase
- DKG: Use Ice Nine's DKG to establish shared threshold key material
- Pre-share commitments: Witnesses periodically broadcast
PreSharedCommitmentvalues
Fast Path
-
Execute: Initiator sends
Execute(cid, Op, prestate_hash, evidΔ) -
Witness Share Production:
-- Compute result ID let rid := hash operation prestate -- Create context let ctx : ExternalContext := { consensusId := some cid resultId := some rid prestateHash := some prestateHash evidenceDelta := some evidDelta } -- Produce share using precomputed nonce let share := signFast S keyShare precomputed challenge bindingFactor session ctx -
Aggregation:
-- Validate all shares reference same consensus instance match aggregateWithContextValidation S challenge signers commits shares with | .ok signature => -- Extract merged context for CommitFact let thresholdSig := signature let attesters := signature.Sset commitFact cid rid thresholdSig attesters | .error msg => -- Inconsistent shares, enter fallback startFallback msg
Fallback
Use ValidatableShare for gossip:
-- Wrap shares for independent validation
let proposal := ValidatableShare.create S share commit pkShare
-- Gossip to peers
for peer in randomSubset witnesses k do
send (AggregateShare cid proposals evidDelta) peer
-- On receive, validate and accumulate
for vs in received do
if vs.isWellFormed && !hasEquivocated vs.sender then
addProposal vs
if proposalCount rid >= threshold then
produceThresholdSignature rid proposals
Context Validation
When aggregating shares from multiple parties, validate context consistency:
def validateShareContexts (shares : List (SignShareMsg S)) : ContextValidationResult
-- Returns:
-- - .ok : all contexts consistent
-- - .inconsistentConsensusId i j : shares i,j have different cid
-- - .inconsistentResultId i j : shares i,j have different rid
-- - .inconsistentPrestateHash i j : shares i,j have different prestate
This catches Byzantine behavior where a party signs different operations.
Best Practices
- Always set consensusId: Bind shares to specific consensus instances
- Include resultId: Ensures all parties sign the same computed result
- Use precomputed nonces for latency: But regenerate periodically (default: 1 hour)
- Validate context on aggregation: Detect Byzantine behavior early
- Use ValidatableShare for gossip: Enables independent validation in fallback
- Propagate evidence: Use EvidenceCarrier for CRDT convergence