1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Error types
use core::fmt;
/// Errors returned by this crate
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// No contributions provided
EmptyContributions,
/// Not enough contributions for threshold
InsufficientContributions { got: usize, need: usize },
/// Duplicate custodian index
DuplicateIndex(u32),
/// Two contributions carry the same Schnorr commitment `u_i`
///
/// Honest contributions collide only if two holders sampled the same
/// nonce, which has negligible probability. In practice this means one
/// holder produced both — the shared-nonce leak, which matters wherever a
/// single process holds more than one share (weighted deployments that
/// virtualize a weight-`w` party into `w` indices). The challenge
/// `c_i = H(u_i || payload)` does not bind the index, so two
/// contributions over one payload under one nonce satisfy
/// `s_i - s_j = c(x_i - x_j)` and leak the difference of the two shares.
DuplicateCommitment(u32, u32),
/// Challenge hash resulted in zero (astronomically unlikely)
ZeroChallenge,
/// Invalid commitment point (not on curve)
InvalidCommitment,
/// Invalid response scalar (not canonical)
InvalidResponse,
/// Lagrange computation failed (duplicate indices)
LagrangeError,
/// Index out of valid range (must be > 0)
InvalidIndex,
/// Sub-share from a dealer outside the agreed dealer set
UnexpectedDealer(u32),
/// Dealers committed to different new thresholds
ThresholdMismatch { expected: u32, got: u32 },
/// The signing package's message is not the message the signer approved
MessageMismatch,
/// A commitment in the package is not the one produced in the local round
UnexpectedCommitment,
/// A coordinator-supplied outer context does not match the locally
/// recomputed one (binding factor, challenge or Lagrange coefficient)
ChallengeMismatch,
/// Two rounds of the same protocol were mixed (session id mismatch)
SessionMismatch,
/// A dealer's proof of knowledge of its constant term did not verify.
/// Carries the failing dealer's index: this is a complaint, and it names
/// who to disqualify.
InvalidProofOfKnowledge(u32),
/// A dealer's sub-share did not verify against its commitment. Carries the
/// failing dealer's index.
InvalidSubShare(u32),
/// The ceremony cannot continue: too few dealers remain after
/// disqualification.
DkgAborted { qualified: usize, need: usize },
/// A sealed package did not open. Carries the dealer it claimed to come
/// from. Which of wrong-sender, wrong-recipient, wrong-ceremony or
/// tampering caused it is deliberately not reported.
SealedOpenFailed(u32),
/// A participant is not on the sealed roster.
UnknownParticipant(u32),
/// An index in a coordinator-supplied `active_indices` has no round-1
/// commitment in the set the nested aggregate was formed over.
UnknownQuorumMember(u32),
/// Two participants published different views of the same round-1
/// commitment set: a dealer equivocated, or the broadcast is not
/// reliable. Refuse to enter round 2.
EchoMismatch,
/// A revealed inner commitment has no matching round-0 precommitment, or
/// does not match the one it claims.
PrecommitMismatch(u32),
/// `(session_id, holder_index)` has already produced a share. Signing
/// again would be nonce reuse.
SessionSpent,
/// A complaint's signature did not verify under the accuser's identity
/// key, or it names a ceremony other than this one.
InvalidComplaint,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyContributions => write!(f, "no contributions provided"),
Self::InsufficientContributions { got, need } => {
write!(f, "insufficient contributions: got {}, need {}", got, need)
}
Self::DuplicateIndex(idx) => write!(f, "duplicate custodian index: {}", idx),
Self::DuplicateCommitment(a, b) => write!(
f,
"indices {} and {} share a Schnorr commitment (shared nonce)",
a, b
),
Self::ZeroChallenge => write!(f, "challenge hash is zero"),
Self::InvalidCommitment => write!(f, "invalid commitment point"),
Self::InvalidResponse => write!(f, "invalid response scalar"),
Self::LagrangeError => write!(f, "lagrange coefficient computation failed"),
Self::InvalidIndex => write!(f, "index must be greater than 0"),
Self::UnexpectedDealer(idx) => {
write!(f, "dealer {} is not in the agreed dealer set", idx)
}
Self::ThresholdMismatch { expected, got } => {
write!(f, "dealer committed to threshold {}, expected {}", got, expected)
}
Self::MessageMismatch => {
write!(f, "signing package message is not the approved message")
}
Self::UnexpectedCommitment => {
write!(f, "commitment is not the one produced in this round")
}
Self::ChallengeMismatch => {
write!(f, "coordinator-supplied outer context does not match")
}
Self::SessionMismatch => write!(f, "session id mismatch"),
Self::InvalidProofOfKnowledge(idx) => {
write!(f, "dealer {} published an invalid proof of knowledge", idx)
}
Self::InvalidSubShare(idx) => {
write!(f, "dealer {} sent an invalid sub-share", idx)
}
Self::SealedOpenFailed(idx) => {
write!(f, "sealed package from dealer {} did not open", idx)
}
Self::UnknownParticipant(idx) => {
write!(f, "participant {} is not on the roster", idx)
}
Self::UnknownQuorumMember(idx) => {
write!(f, "quorum member {} has no round-1 commitment", idx)
}
Self::EchoMismatch => write!(f, "round-1 commitment sets disagree"),
Self::PrecommitMismatch(idx) => {
write!(f, "holder {} revealed a commitment it did not precommit to", idx)
}
Self::SessionSpent => write!(f, "this session has already produced a share"),
Self::InvalidComplaint => write!(f, "complaint did not verify"),
Self::DkgAborted { qualified, need } => write!(
f,
"dkg aborted: {} qualified dealers remain, need {}",
qualified, need
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}