Complete the ChillDKG protocol surface with the recovery and blame-
attribution procedures (bip-frost-dkg v0.3.0-dev, reference commit
a91896883f85b159415ecf298d5e844879af112d).
Recovery:
- secp256k1_chilldkg_participant_recover / _coordinator_recover: parse
the self-delimiting recovery layout u32be(t) || sum_coms(33t) ||
hostpubkeys(33n) || pubnonces(33n) || enc_secshares(32n, checked) ||
cert(64n), deriving n = (len-4-33t)/162 exactly as the reference's
deserialize_recovery_data; re-verify the certificate, recompute the
TapTweak and the receiver's ECDH/self pads from (hostseckey,
pubnonces, enc_context), recompute the tweaked secshare, and
sanity-check secshare*G == pubshares[own]. Also return hostpubkeys,
n and t so callers can re-derive session params. RecoveryDataError /
HostSeckeyError / params failures map to INVALID_INPUT (no index,
as in the reference); an invalid pubnonce during decrypt passes
through as FAULTY_PARTICIPANT_OR_COORDINATOR(i), matching the
reference leaking that exception from recover().
Recovery acks:
- secp256k1_chilldkg_recovery_ack_sign / _acks_verify: BIP-340
(standard BIP0340 tags) over pad33("BIP DKG/recovery acknowledgment")
|| u32be(i) || recovery_data. Verification failure maps to
FAULTY_PARTICIPANT(i) (InvalidRecoveryAckError subclasses
FaultyParticipantError in the reference).
Investigation:
- secp256k1_chilldkg_coordinator_investigate: builds one 65n-byte
per-participant message (per-dealer encrypted partial secshares
(32n) + partial pubshares (33n)) per call; the reference returns all
n at once -- equivalent, the caller iterates.
- secp256k1_chilldkg_participant_investigate: the reference's
three-step blame attribution -- sum-of-pubshares check ->
FAULTY_COORDINATOR; sum-of-secshares check -> FAULTY_COORDINATOR
(covers the reference's SecshareSumError translation); per-dealer
decrypted share vs commitment -> FAULTY_PARTICIPANT_OR_COORDINATOR(i)
(or FAULTY_COORDINATOR for the own index); all-consistent ->
INVALID_INPUT (the reference's uncaught RuntimeError).
- Investigation data is transported via a new opaque, secret-bearing
secp256k1_chilldkg_participant_inv_data object (4205 bytes,
magic-validated save/load, secret-cleared) filled by
participant_step2 on the UNKNOWN_FAULT paths. This amends the
Phase 3 participant_step2 signature with a nullable inv_data
out-param -- required because recomputing inside
participant_investigate would duplicate step2's decrypt/verify
logic.
- New length helper secp256k1_chilldkg_investigation_msg_len (65n).
tests_impl.h: chilldkg_recovery_test (recover roundtrips byte-exact
vs the session outputs and reference vectors, tampered/truncated/
over-long recovery data, unknown/invalid hostseckey, ack sign
byte-exact + verify with wrong-index and tampered-ack blame,
params/recovery mismatch rejects, misuse) and
chilldkg_investigate_test (two end-to-end public-API scenarios
generated from the reference: dealer corrupting a participant's
encrypted share, and coordinator tampering with an enc_secshare;
cmsg1/cinv/inv_data byte-exact, blame codes and indices matching the
reference's exception type and index; malformed cinv ->
FAULTY_COORDINATOR; malformed pmsg1 -> FAULTY_PARTICIPANT(j); misuse).
The all-consistent investigate path is not constructible without
discrete logs and matches the reference's unreachable RuntimeError.
Verified: make check 3/3 (incl. noverify); CMake ctest 369/369;
make distdir clean.