Skip to content

🔄 Auto-sync: from Discussion #1748 every hour.

Dynamic devshard: per-escrow BLS DKG (NIZK dealings, on-chain T0)

Автор: @akup · Категория: ⚙ Protocol Improvements · Создано: 2026-09-10 13:56 UTC · Обновлено: 2026-09-11 04:38 UTC


📝 Описание

Dynamic devshard: per-escrow BLS DKG (NIZK dealings, on-chain T0)

Status: draft — design only; not implemented.
Related: host-initiated close in finalization.

This document specifies a per-escrow BLS12-381 distributed key generation (DKG) so a devshard has a stable root public key PK that a third party can check was generated by the hosts who actually dealt for that escrow. Dealings are publicly verifiable (NIZK). There is no complaint / dispute phase.

Dealings are p2p off-chain. The chain sees only the final artifact T0 (signed dealings + PK). It re-verifies that artifact (hosts are not trusted). A deadline unions every valid signed dealing anyone submitted, so one assembler cannot drop a host. Then PK = ∏ A_i over that union.

Resharing (keep PK after a membership tx) is a follow-on. Genesis uniqueness does not depend on it.


1. Summary

Today MsgCreateDevshardEscrow locks funds and samples slots (typically 16 participant addresses). Settlement still uses per-slot signatures. There is no per-escrow BLS key, and the existing x/bls DKG is epoch-wide, interactive, and rebuilt every epoch.

This proposal makes escrow creation heavier:

  1. Chain creates the escrow and lists eligible participants (slots) — already true. The creator does not pick the host set.
  2. Listed hosts run the DKG p2p (NIZK dealings, §5). They do not put each dealing on-chain.
  3. Any listed host submits MsgSubmitDevshardT0 with the artifact (signed dealings + claimed PK). Gas-free if the signer is a listed slot (§6.5). The chain re-verifies every dealing; “we already checked p2p” is not trusted.
  4. The chain does not lock PK on the first tx. Until dkg_deadline it unions every valid signed dealing from every submit (happy path = one full T0; an omitted host adds only their dealing). At the deadline, if |D| ≥ t, it stores T0 and PK = ∏ A_i. Otherwise DKG fails.
  5. The escrow is not execution-ready until DKG completes. After that, PK is a compact quorum verifier key (telemetry, and optionally a settle encoding). It is not what makes host-initiated close or roster changes possible — see What this buys.

PK alone is a group element and proves nothing about origin. T0 is the proof. Hosts assemble it off-chain; the chain re-verifies and unions fragments so the submitter does not choose the dealer subset.

Heavier create is the cost. The win is a chain-stored PK that third parties can check without trusting the creator or fetching n slot signatures — see What this buys.


What this buys

The chain-stored PK is a compact quorum verifier that a third party can check without trusting the escrow creator. That is the product. Self-settle and dynamic membership are not.

Not a win: self-settle

Today MsgSettleDevshardEscrow requires settler == escrow.Creator (VerifyDevshardSettlement) and 2n/3+1 per-slot secp signatures. Host-initiated close needs the first check dropped. The second check is already committee authorization.

finalization.md is the close protocol: off-chain vote/commit, then one FinalizeSubmit carrying VoteQC / CommitQC (or today’s slot signatures). The Cosmos signer can be any host or relayer. BLS is not required.

A threshold signature Verify(PK, settlement_digest, σ) can shrink that tx. Compactness is not a new capability and is not a reason to run this DKG. Per-slot signatures stay a valid settle encoding.

Not a win: dynamic membership

Today slots are frozen at MsgCreateDevshardEscrow. Changing the roster without a new escrow is a membership tx (add host / remove host): update slots, version the roster for nonce routing and quorum. That tx is required whether or not a BLS key exists — it is how every host and the chain learn who is in and when the set changed.

Resharing (§11) does not replace that tx. It is extra work after the membership tx, only if a PK already exists and third parties should keep verifying the same key. BLS does not make the shard dynamic; the membership tx does. A new DKG on each add would only matter if you had chosen PK as a cached identity (telemetry below).

Cross-devshard verifiable telemetry

Each host sits in many devshards. Inflight work (“how much is this host processing right now”) is local to a shard unless it is published. Self-signed load is cheap to lie about. A threshold BLS signature under that shard’s PK is a quorum statement:

telemetry = {
  escrow_id,
  height,             // or height-sync aligned height
  loads: [ (slot_or_host, inflight_work), ... ]
}
σ = ThresholdBLS(sk, domain || telemetry)

Gossip telemetry ‖ σ among chain participants (hosts / controllers), not as per-inference on-chain txs. Any other devshard:

  1. Reads PK for escrow_id from chain (the DKG output).
  2. Checks Verify(PK, telemetry, σ).
  3. Treats the loads as quorum-attested by that shard.

No need to be in the escrow, no need for T0 at verify time, no trust in the gossip relay. The chain-stored PK is the verifier key. (n slot signatures would also work if the verifier fetches the current slots; PK is the compact form.)

Uses: avoid sending more work to a host already hot in other shards; validation / admission / routing with a shared view of in-process load.

Constraints (so this stays telemetry, not a second consensus):

  • Periodic, height-bound, compact (counts / tokens in flight — not prompts).
  • Replay: bind escrow_id + height (and maybe a monotonic telemetry nonce); ignore stale heights.
  • Not per-inference gossip. Same bias as cPoC / finalization: flood only attestations, not every request.
  • Wire, cadence, and aggregation across many escrows are a follow-on spec; this DKG supplies the compact PK those attestations verify against.

2. Goals

  1. Joint generation — no listed host (and no outsider) knows sk unless they corrupt more than the signing threshold.
  2. BindingPK is uniquely determined by this escrow’s ceremony id and the union of valid signed dealings at the deadline. A censored subset packed by one T0 sender cannot be the final product.
  3. Public verifiability — a third party (and the chain) verifies T0 without private shares and without a dispute round.
  4. No DKG disputes — invalid dealings are dropped; they are not argued on-chain.
  5. Stable root key (optional follow-on) — if third parties cache PK, a membership tx may be followed by resharing so PK does not rotate. Resharing is not what changes the roster.
  6. Reuse identities — dealers are the escrow slots; encryption and dealing signatures use each participant’s already-registered secp256k1 account key.
  7. Gas-free T0 submitMsgSubmitDevshardT0 from a listed slot charges no fee. Cost is prepaid by CreateDevshardFee.
  8. Off-shard verify — any chain participant can check a shard BLS signature (telemetry) against on-chain PK. Compact BLS settle is an optional encoding, not a close-authority change.

3. Non-goals (this document)

  • Replacing the epoch x/bls DKG used for network-wide PoC signatures.
  • Host-initiated close — drop settler == Creator and verify host quorum (CommitQC or slot signatures). finalization.md owns that. BLS settle is optional compactness.
  • Membership txs (add host / remove host) — required to change slots whether or not this DKG exists. This doc does not specify who may add or how new_slots is chosen.
  • Exact protobuf for an optional BLS settle field vs today’s repeated DevshardSlotSignature.
  • Full telemetry gossip protocol (cadence, transport, cross-escrow merge). This doc only states that threshold-signed load is the payload those protocols verify.
  • P2p wire for dealing gossip (any all-to-all among slots is enough; NIZKs do not need extra rounds). This doc only requires: each dealing is signed, and the chain sees a union of those signed blobs by the deadline.
  • SNARK compression of T0 (only if on-chain verify gas becomes a problem).

4. Glossary

Term Meaning
n Number of listed hosts. Today n = len(slots) = GroupSize (16).
t Signing threshold. Align with settlement quorum: t = DevshardQuorumSlots (2n/3 + 1 = 11). t shares reconstruct sk / a signature.
Dealer / player In genesis, the same set: every listed slot deals and receives a share.
A_i Dealer i’s public contribution g^{a_{i,0}} (G2, BLS12-381).
PK Root group public key ∏ A_i over accepted dealers (all valid dealings at the deadline).
PK_i Public share of participant i, used later for share verification and resharing.
NIZK dealing One message: verification vector, encrypted shares, proofs, identity signature. Anyone can check it.
T0 Frozen genesis artifact: union of valid signed dealings at the deadline, plus derived PK. Built p2p; chain re-verifies and unions fragments.
T_k Reshare transcript for round k≥1: old public shares, new committee, accepted reshare dealings, same PK.
Ceremony id Domain separator tying dealings to this escrow on this chain (and, for reshare, round k + new slot list).

5. Why NIZK (and what it is not)

A signature on A_i only proves identity i published a group element. It does not prove:

  • i knows a_i with A_i = g^{a_i} (rogue-key / contribution cancellation: last dealer sets A_n = PK* · (∏_{j<n} A_j)^{-1} and knows sk*);
  • ciphertexts are Shamir evaluations of the committed polynomial (classical Feldman needs a complaint round).

Two NIZKs per dealing close that:

  1. PoK of discrete log of A_i (Schnorr / Fiat–Shamir), bound to the ceremony id — blocks cancellation.
  2. Share correctness — ciphertexts match the Feldman vector (C_i[0] = A_i). Specified in §5.2 as DLEQ/PVSS, not Groth 2021.

NIZKs do not by themselves name the dealers. Long-term signatures on the dealing do. NIZKs do not survive as a proof if the chain only stores PK and throws T0 away.

Protocol effect: dealing is one shot; aggregation is “keep valid dealings.” No VERIFYING phase, no MsgSubmitVerificationVector, no dispute txs.

That path is specified at the message layer (§6.3–§6.4): the keeper re-verifies every dealing inside the submitted artifact; a bad dealing is dropped from the union (or fails that tx), not a complaint. Hosts already verified p2p; the chain does it again so a lying T0 cannot pass. What §5.1 specifies is which checks run.

5.1 What the chain verifies (no DKG disputes)

On each dealing inside MsgSubmitDevshardT0 (and on any fragment merged before the deadline) the keeper MUST check, in order:

  1. Eligibility — dealing signer ∈ slots, at most one dealing per slot in the union, window open. Not a NIZK.
  2. Schnorr PoK of A_i = C_i[0] — Fiat–Shamir, bound to ceremony_id. Anti-cancellation. Required. Foundational Schnorr/Fiat–Shamir patents have expired; this is the same class as RFC 8235.
  3. Share correctness (PVSS) — for every recipient slot j, the ciphertext encrypts f_i(j) consistent with the Feldman vector C_i. Anyone can check; recipients do not complain. Required for “no disputes.” Must not depend on Groth 2021 (§5.2).
  4. Subgroup / degreeC_i has t coefficients in the BLS12-381 subgroup used for PK.

If any check fails, that dealing is not admitted to the union. There is no complaint phase.

Schnorr without (3) would still need Feldman decrypt-and-complain (today’s epoch x/bls). That contradicts this section. Groth 2021 is one implementation of (3); it is not the spec.

5.2 Share correctness without Groth 2021 (patent-avoidance)

Normative v1: Feldman vector + hashed-ElGamal in the same group as g + Chaum–Pedersen DLEQ, Fiat–Shamir (Schoenmakers-style PVSS, 1990s). No pairing product argument, no Groth NI-DKG.

Each participant has an encryption key pk_j = g^{x_j} in the same group as the Feldman generator (G2, matching PK). Not secp256k1 ECIES: proving ECIES plaintext in a cheap NIZK is why people reach for SNARKs or Groth’s pairing encryption.

For each recipient j, dealer i with share s = f_i(j):

Y_j = ∏_k C_i[k]^{j^k}                 // Feldman, Y_j = g^s
r  ← random
R  = g^r
U  = pk_j^r
ct = s + H(ceremony_id ‖ i ‖ j ‖ U)    // field add (scalar)
π_j = DLEQ(g, R, pk_j, U)              // Chaum–Pedersen: same r

Fiat–Shamir for π_j binds ceremony_id, i, j, C_i, R, U, ct.

Chain (n = 16):

  1. Y_j equals Feldman(C_i, j)
  2. π_j verifies
  3. Y_j ≟ g^{ct - H(ceremony_id ‖ i ‖ j ‖ U)}
    (this binds the plaintext to Y_j; without it, hashed-ElGamal would need a recipient complaint)

The chain never decrypts.

Recipient j: s = ct - H(ceremony_id ‖ i ‖ j ‖ R^{x_j}). If the three checks passed, g^s = Y_j already.

Do not use for v1: Groth 2021 NI-DKG, Groth16, or a vendor SNARK/STARK stack. Those can carry implementation patents. Schnorr + DLEQ are 1980s/1990s sigma protocols whose foundational patents have expired. This is not a legal opinion; it is the construction this spec requires so we are not tied to Groth 2021.

Optional later: swap Groth-style multi-receiver pairing encryption behind the same verify(dealing) → bool interface (compression only). For n = 16 that is not expected to win on-chain verify cost — see §5.3.

5.3 Comparison with Groth NI-DKG (2021)

Reference: Jens Groth, Non-interactive distributed key generation and key resharing, ePrint 2021/339. Not Groth16 (2016). (If “Groth 2011” is used loosely, it is the same pairing-NIZK family; the DKG paper is 2021.)

Same protocol. P2p one dealing per dealer, one T0 on chain, same deadline union, same Lagrange reshare (§11). Groth does not cut interactions or rounds.

Different payload. DLEQ: Feldman vector + n hashed-ElGamals + n Chaum–Pedersen proofs. Groth: packed pairing CCA ciphertext + one pairing NIZK for the whole dealing.

DLEQ (this spec, v1) Groth 2021
Interactions 1 dealing / dealer; 1 artifact tx same
Dealing size O(t + n) smaller (multi-receiver pack)
On-chain verify n cheap G2 DLEQs + 1 Schnorr per dealing 1 pairing-product NIZK per dealing
Full T0 (n dealers) DLEQs (16² = 256 at GroupSize) n pairing verifies
Patents / v1 Schnorr + DLEQ only do not require

n = 16: DLEQ is expected as fast or faster on-chain. Each DLEQ is a few G2 multi-exps (same class as existing BLS ops). Groth verify is pairings, which cost much more per op. Groth reduces count of proofs, not protocol work; at 16 slots that count is small, so pairings do not pay off. Groth’s win is large n (Internet Computer scale), where DLEQs and n ElGamals dominate size and time.

v1 stays DLEQ. Revisit Groth only if a profiled MsgSubmitDevshardT0 verify is too slow and counsel accepts that implementation.


6. On-chain create → DKG → store PK

6.1 Escrow create (existing, extended)

MsgCreateDevshardEscrow stays the listing step:

  • Sample slots from the model epoch group (current GetSlotsFromSorted / app_hash / escrow_id / model_id).
  • Lock funds, persist escrow.
  • CreateDevshardFee (already charged at create) pre-pays on-chain artifact verify (MsgSubmitDevshardT0), not n dealing txs.

New escrow fields (conceptual):

Field After create After successful DKG
slots listed participants unchanged
dkg_status PENDING COMPLETED or FAILED
dkg_deadline create height + dkg_timeout_blocks
bls_pk empty PK
t0_hash empty H(T0)
t0 empty full transcript (or blob + hash; see §8)

The escrow is not ready for inference while dkg_status != COMPLETED. Hosts that would otherwise attach on create wait for PK.

dkg_timeout_blocks is a new param: the dealing window. At that height the EndBlocker either finalizes PK or marks FAILED (refund / refusal, same spirit as refusal_timeout).

6.2 P2P dealings, on-chain artifact only

Dealings are not chain txs. Listed hosts gossip signed NIZK dealings among slots (transport out of scope). Each host verifies Schnorr + DLEQ locally, decrypts its ciphertexts, and holds a candidate set D.

What goes on-chain is the artifact: the set of signed dealings plus a claimed PK. The chain re-verifies (same checks as hosts). “Already verified off-chain” is not an input.

Waiting for a deadline and accepting a quorum is still required (offline hosts should not fail create). A single assembled T0 would otherwise allow:

Did the submitter drop some hosts’ dealings?

That is subset censorship, not a NIZK failure. Close it without putting n dealings on-chain in the happy path:

  1. Each dealing is signed by its slot (already true). Anyone who has a signed dealing can include it in a MsgSubmitDevshardT0.
  2. Do not finalize on the first tx. Until dkg_deadline, the keeper unions valid dealings by slot id from every submit (full T0 or a one-dealing fragment). PK is the product over that union at the deadline, not over a preferred blob.
  3. Happy path: one listed host submits one complete T0 (all p2p dealings). Omitted host: one small extra submit of their signed dealing; it merges.

A listed host who never dealt p2p and never submitted a fragment is absent, not censored.

6.3 Off-chain dealing window

Listed hosts watch devshard_escrow_created. Each host i ∈ slots that participates:

  1. Builds ceremony id (§7).
  2. Samples polynomial f_i of degree t-1.
  3. P2p-broadcasts one signed dealing (Feldman C_i, per-recipient ElGamal + DLEQ, Schnorr on A_i, identity signature). Ciphertexts cover the full slots list.
  4. Collects peers’ dealings, verifies §5.1, decrypts own shares. Does not freeze sk_i until the chain deadline (the accepted set may still grow).

Any listed host may then submit:

MsgSubmitDevshardT0 {
  creator     // must be ∈ slots
  escrow_id
  dealings    // 1..n signed dealings (full T0 or a fragment)
  pk          // claimed product; must match recompute over
              // (existing union ∪ these dealings) at finalize
}

Keeper, per dealing in the message:

  1. Escrow exists, not settled, dkg_status == PENDING, height < dkg_deadline.
  2. Dealer ∈ slots; if that slot already has a dealing in the union, the new one must be byte-identical (else reject that dealing).
  3. ceremony_id matches §7.
  4. Identity signature is that slot’s account key.
  5. Schnorr + all DLEQs (§5.1–§5.2).
  6. Ciphertexts addressed to the full slots list.

Admit each valid dealing into the escrow’s union. Invalid dealings are skipped; they are not disputes. Claimed pk is not stored until the deadline (recomputed from the union).

Fee is 0 when the Cosmos signer is a listed slot (§6.5).

6.4 Deadline aggregation (chain freezes T0)

At dkg_deadline, let D be the union of admitted dealings, ordered by slot index.

  • If |D| < t (or min_dealers): dkg_status = FAILED. No PK.
  • Else:
  • PK = ∏_{i ∈ D} A_i (ignore the claimed pk except as a sanity check: mismatch → still use the recomputed product, or fail the artifact if a submit insisted on a wrong pknormative: always recompute)
  • Derive PK_i
  • Persist T0 = (ceremony_id, slots, D, PK, …), t0_hash, dkg_status = COMPLETED
  • Emit devshard_dkg_completed

The submitter never chooses the product. They only relay signed dealings the chain re-checks.

Accepted dealers set sk_i = ∑_{j ∈ D} f_j(i) from ciphertexts in the frozen T0 (same bytes they already had p2p if the union matches).

MsgCreateDevshardEscrow
  slots listed, dkg_status=PENDING, deadline set
  p2p NIZK dealings among slots
  MsgSubmitDevshardT0  (artifact or fragment, gas-free, window open)
        │  union more T0/fragments until deadline
  dkg_deadline EndBlocker
        ├── |D| < t  → FAILED
        └── |D| ≥ t  → T0 := union D, PK := ∏ A_i, COMPLETED

6.5 MsgSubmitDevshardT0 is gas-free

Plan: listed hosts post the artifact (or a missing-dealing fragment) with zero gas fee. Create already paid; the signer must be ∈ slots. That is spam protection — at most a handful of T0 txs per escrow, not an open faucet.

Ante (cheap, before DLEQ verify): 0 fee only if:

  • msg type is MsgSubmitDevshardT0;
  • escrow PENDING, height < dkg_deadline;
  • signer ∈ slots.

Anyone else pays the normal min fee.

Still meter gas. Cap dealings per tx and bytes. Prepaid verify budget (from CreateDevshardFee) decrements on each dealing verified (success or fail) so a listed host cannot loop junk proofs for free forever.


7. Ceremony id

Computed by hosts and by the keeper. Suggested preimage (canonical protobuf / byte encoding TBD):

ceremony_id = H(
  "gonka/devshard/dkg/v1" ||
  chain_id ||
  escrow_id ||
  epoch_index ||
  model_id ||
  app_hash ||          // create-time, already on escrow
  encode(slots) ||     // ordered addresses as stored
  n || t
)

Every NIZK challenge and every dealing signature MUST include ceremony_id. That is what stops:

  • replaying another escrow’s T0;
  • mixing dealers from a different slot list;
  • using a PK generated offline, or from a subset packed by one submitter.

Reshare rounds use a different domain (dkg/v1 vs reshare/v1) and bind round k plus the new slot list (§11). Genesis dealings cannot be replayed as reshares.


8. T0 artifact

T0 is public. It MUST NOT contain secret polynomials or decrypted sk_i.

Minimum contents:

  • version, ceremony_id, n, t
  • full eligible slots (create-time list)
  • accepted dealer set D ⊆ slots (those with a valid dealing by the deadline)
  • per dealer in D: the dealing already verified at submit time
  • derived PK and PK_i (recomputable)

T0 is the concatenation of keeper-accepted dealings plus the EndBlocker product. It is not a message someone uploads.

Storage:

  • Dealings live in submitted T0 / fragments until the deadline; then the escrow points at the frozen union (t0_hash over canonical encoding).
  • Must persist PK and H(T0). Full dealings stay on-chain (n = 16). Do not skip NIZK verify at submit and keep only a later PK.

Hosts keep sk_i in local sealed storage (same class of problem as escrow-keys-at-rest.md, but for BLS shares).


9. Cryptographic profile (v1)

Item Choice
Curve BLS12-381, PK and commitments in G2 (same convention as current x/bls)
Signatures later threshold BLS, signatures in G1, hash-to-G1
Sharing Shamir, degree t-1
Encryption Same-group hashed-ElGamal: pk_j = g^{x_j} in G2 (§5.2). Not secp256k1 ECIES.
PoK Schnorr NIZK on A_i, Fiat–Shamir with ceremony_id
Share NIZK Chaum–Pedersen DLEQ per recipient (§5.2). Groth 2021 is not v1.
Identity sig secp256k1 over the dealing body

Do not use interactive Feldman + verification-vector acks for this ceremony.


10. Using PK after COMPLETED

  • Clients / other shards learn PK from escrow query. They verify BLS signatures against PK. They do not need T0 unless they audit origin.
  • Telemetry (the consumer this DKG is for). Gossip Verify(PK, load_attestation, σ) across chain participants (What this buys).
  • Settlement (optional encoding). The keeper may accept Verify(PK, settlement_digest, σ) as a compact stand-in for slot signatures / CommitQC. That does not replace dropping settler == Creator or the finalization commit procedure. Per-slot signatures remain sufficient.
  • Partial signatures during the session still use sk_i / PK_i among hosts; aggregation is off-chain.

Until COMPLETED, gateways must not treat the escrow as live.


11. Resharing ceremony (math)

Join/leave is a membership tx on mainnet (add host / remove host). That tx is what changes slots. Recreating the escrow is unnecessary even with secp-only settlement.

This section is only for keeping an already issued PK after that tx. A new DKG would sample a new sk and a new PK, breaking telemetry verifiers that cached the old key. Resharing produces a fresh Shamir sharing of the same sk for a new player set (n', t'). Who may add or remove, and how new_slots is chosen, belong on the membership tx — not here.

membership tx updates slots
T0 (origin, immutable)  →  T1, T2, … (custody of the same sk)

11.1 What is already known

After genesis (or after T_{k-1}), there is a unique polynomial F of degree < t with F(0) = sk and PK = g^{sk}.

Old player i holds sk_i = F(i). Everyone can compute the public share

PK_i = g^{sk_i} = g^{F(i)}

from the previous verification vector (evaluate commitments at i). The chain stores PK and the PK_i for the current committee.

Write λ_{i,Q}(0) for the Lagrange coefficient of old id i in a set Q of size t, interpolating at 0:

λ_{i,Q}(0) = ∏_{m ∈ Q, m ≠ i}  (0 - m) / (i - m)

Any honest t-set Q of old shares satisfies ∑_{i ∈ Q} λ_{i,Q}(0) · sk_i = sk, hence

∏_{i ∈ Q} PK_i^{λ_{i,Q}(0)}  =  PK

That identity is what a reshare must preserve. It is not the genesis rule PK = ∏ A_i (sum of fresh secrets). Mixing the two aggregations would yield the wrong key.

11.2 Each old holder re-shares sk_i

Old dealer i does not sample a new a_{i,0}. They sample a polynomial g_i of degree < t' with constant term equal to their existing share:

g_i(0) = sk_i = F(i)
deg g_i < t'

Verification vector C'_i = (g^{g_i(0)}, g^{g_i,1}, …, g^{g_i,t'-1}). Encrypted values g_i(j) for every new player id j in the new slot list (size n'). Same NIZK dealing shape as genesis: PoK of dlog of C'_i[0], share-correctness of the ciphertexts.

The binding that this is a reshare of the shard key, not a new DKG:

C'_i[0]  =  PK_i     (public equality, no extra proof system)

If that fails, the dealing is invalid. A dealer cannot re-share an arbitrary scalar and still pass.

Ceremony id for round k (hosts and keeper):

reshare_id = H(
  "gonka/devshard/reshare/v1" ||
  chain_id || escrow_id || k ||
  encode(old_slots) || encode(new_slots) ||
  t || t' || PK
)

NIZKs and dealing signatures bind reshare_id. Ciphertexts are addressed to new_slots, not to the old list.

11.3 Deadline set and canonical interpolating set

Each old slot that still holds sk_i p2p-broadcasts one reshare dealing. Same inclusion as genesis: signed dealings, MsgSubmitDevshardTk (or reuse T0 msg shape) relays the artifact; union until reshare_deadline; take every valid dealing D.

If |D| < t, the round fails. PK unchanged; old shares still work. No T_k.

If |D| ≥ t, let Q be the canonical t-subset of D (lowest old slot indices in D). New shares and the new verification vector are interpolated from Q only. Extra valid dealings (D \ Q) are stored and checked (C'_i[0] = PK_i) but do not enter Lagrange — using |Q| ≠ t in the sum below would be the wrong degree.

This is the dual of genesis “product of all A_i”: here the secret is already fixed, so the combinator is Lagrange on a t-set, not a product over everyone who showed up.

11.4 New shares

New player j decrypts g_i(j) for each i ∈ Q and sets

sk'_j  =  ∑_{i ∈ Q}  λ_{i,Q}(0) · g_i(j)

Then sk'_j = F'(j) for a unique F' of degree < t' with F'(0) = sk. In general sk'_j ≠ sk_j even if j was already in the old committee.

Public new verification vector (homomorphic, chain-computable):

C'  =  ∑_{i ∈ Q}  λ_{i,Q}(0) · C'_i

Constant term:

C'[0]  =  ∏_{i ∈ Q} PK_i^{λ_{i,Q}(0)}  =  PK

That is the keeper check that the reshare did not change the root key. New public shares PK'_j = C'(j) (evaluate C' at new ids). Old sk_i for members who left are useless for F'; they cannot sign under the new sharing.

Add is the same formulas with n' > n (new ids appear in the ciphertext list). Remove is n' < n (left ids get no new share). t' may equal t.

11.5 T_k

Chain-built, like T0:

  • reshare_id, old slots, new slots, t, t', Q, D
  • each accepted reshare dealing
  • stored PK (must equal C'[0])
  • new PK'_j

Third parties: origin is still T0; current custody is T_k. Signature verify uses PK only.

Who may open a reshare round is the membership tx (or a follow-on after it). Fee exemption for reshare txs is not specified here. The math does not depend on them.


12. Open questions

  1. Minimum |D|. Default t (11 of 16) matches signing quorum. A higher min_dealers (e.g. n-f) makes post-DKG liveness safer if more hosts drop. Not a trust issue if §6.2–§6.4 hold.
  2. Warm vs cold key. Dealings must be signed by the same key the chain uses to identify slots[j] (cold / account). Warm keys stay for session traffic, not for DKG identity.
  3. Library / encodings. Freeze Schnorr + DLEQ encodings (§5.2) before proto freeze. Groth 2021 is not required.
  4. Failed DKG refunds — exact reuse of refusal_timeout vs a dedicated DKG fail handler.

13. Contrast with current x/bls epoch DKG

Epoch x/bls today This proposal
Scope whole epoch validator set one escrow’s slots
Lifetime new key every epoch PK stable for the escrow
Dealings Feldman + private share check p2p Feldman + Schnorr + DLEQ; chain re-verifies T0 (§5–§6)
Phases DEALING → VERIFYING → COMPLETED p2p deal → submit artifact → deadline freeze T0
Disputes verification vectors / implicit complaints none (bad dealing omitted from union)
Proof of origin not aimed at third parties per escrow frozen T0 on the escrow
Chain role collect parts, then aggregate C_k0 re-verify artifact, union fragments, PK = ∏ A_i

Do not overload InitiateKeyGenerationForEpoch for devshards.


14. Implementation sketch (when scheduled)

  1. Params: dkg_timeout_blocks, optional min_dealers; proto fields on DevshardEscrow; MsgSubmitDevshardT0.
  2. Ante: MsgSubmitDevshardT0 is gas-free when signer ∈ slots (§6.5), plus prepaid verify budget.
  3. Host: p2p NIZK deal, submit artifact with 0 fee, at deadline persist sk_i from frozen T0.
  4. Keeper: §6.3 per-dealing checks on the artifact; union fragments; EndBlocker §6.4.
  5. Tests: cancellation fails verify; wrong escrow_id rejected; omitted dealing still counts if submitted as a fragment; finalize only at deadline; PK = ∏ A_i over union; unlisted signer not fee-exempt.
  6. Later: telemetry gossip; optional compact BLS settle encoding; reshare p2p + T_k after a membership tx if PK must stay stable (§11).

💬 Комментарии (2)

Комментарий 1 — @vitaly-andr

2026-09-10 14:31 UTC

One thing worth knowing before you settle the "failed DKG refunds" question, since it touches the mechanism you would most likely reuse: the existing refund-on-failure path is currently not wired up.

InvokeSetBlsHooks (x/bls/module/module.go:243) asks depinject for *keeper.Keeper, while ModuleOutputs provides the keeper by value (module.go:214-218). Nothing provides the pointer, invoker dependencies are optional, so the parameter arrives nil and the guard at the top returns without calling SetHooks. Hooks() then keeps returning the empty implementation, and both AfterThresholdSigningFailed and AfterThresholdSigningCompleted are no-ops in any app built this way. I confirmed it in a simulation run where a bridge mint's signature expired after the usual three attempts and the inference-side failure hook never fired.

Filed with the details in #1749. Raising it here because a per-escrow DKG that leans on the same hook surface for its own failure handling would inherit the same silence, and because the choice between "reuse refusal_timeout" and "a dedicated DKG fail handler" reads differently once you know the current handler is not reached at all.

↳ Ответ от @aikuznetsov · 2026-09-10 15:34 UTC

Thanks for catching this — the concern looks valid.

We should fix the existing BLS hook wiring as part of this work, or preferably as a prerequisite, since it already affects the current threshold-signing failure and completion paths independently of the proposed per-escrow DKG.

For the DKG implementation, we should also add an integration test that exercises the complete failure path through the actual app wiring and verifies that: - the escrow transitions to DKG_FAILED; - the expected refund is executed; - the registered failure handler is actually invoked.

↳ Ответ от @akup · 2026-09-10 17:09 UTC

I think that it is possible that x/bls/module/module.go will not be used as it is.

Most important thing here we need to use NIZK for share dealing, that is currently not used. It eliminates disputes and also helps to publish verifiable artifact to prove public key that we generate with DKG belongs to the devshard. Also it is absolutely another module and it is not intersecting with current bridge BLS

Also current proposal is currently draft and far away from implementation, it is to discuss the idea and concept

Комментарий 2 — @aikuznetsov

2026-09-10 15:27 UTC

I like the original idea, especially removing the sequencer as the single authority capable of closing an escrow. A chain-verifiable committee authorization mechanism would make devshards substantially more resilient to a cheating or unavailable sequencer.

My main concern is the increase in devshard startup time.

Today, escrow creation requires one transaction and the shard can start almost immediately. With the proposed DKG, the escrow remains PENDING until dkg_deadline, even if enough—or all—dealings arrive earlier. Depending on dkg_timeout_blocks, this could increase startup time from roughly one block to tens of seconds or more.

Have we considered alternative approaches or a phased implementation?

Option Expected startup Advantages Disadvantages
1. Keep the existing 11-of-16 secp256k1 slot signatures and remove the creator-only restriction ~1 block Smallest protocol change; immediately solves the main self-close problem; any relayer can submit a settlement authorized by the existing quorum Larger settlement proof; no stable per-devshard public key; committee changes require updating the authorization set
2. Pre-register an individual BLS key with proof-of-possession for every host, then use an aggregate signature plus a signer bitmap ~1 block Constant-size aggregate signature; no per-escrow DKG; efficient quorum verification; suitable for settlement and signed telemetry The effective verification key depends on the signer bitmap and committee version; no single stable PK across membership changes
3. Keep the proposed threshold DKG, but add an optimistic 16-of-16 fast path ~2–3 blocks in the healthy case; full deadline otherwise Preserves the stable root PK, resharing, and the complete security model of the proposal; if every listed slot submits, nobody can have been omitted Still requires complex NI-DKG/PVSS implementation; an offline host disables the fast path and startup falls back to the full deadline

For option 3, the rule could be:

```text all 16 valid dealings received → finalize immediately 11–15 valid dealings received → wait until dkg_deadline and include all received dealings fewer than 11 at deadline → DKG_FAILED

↳ Ответ от @akup · 2026-09-10 17:04 UTC

Actually, I think we can already start to use devshardEscrow immediately without waiting for DKG. As it's finalization is for settlement and load share. It seams to be ok to wait 10-20 seconds before we can start settle and share load.

Also I think it's not so bad to wait even 20 seconds before we start. In case of gateway it has multiple devshards and can wait for new ones to start without downtime visible for enduser. In case when user creates devshard wscrow on his own, it also seams to be ok to wait 20 seconds, and then use it during one epoch (around 24 hours)