1mod block_signatures_v1;
2mod block_signatures_v2;
3
4pub use block_signatures_v1::BlockSignaturesV1;
5pub use block_signatures_v2::BlockSignaturesV2;
6
7use alloc::{collections::BTreeMap, vec::Vec};
8use core::{
9 fmt::{self, Display, Formatter},
10 hash::Hash,
11};
12use itertools::Either;
13#[cfg(feature = "std")]
14use std::error::Error as StdError;
15
16#[cfg(feature = "datasize")]
17use datasize::DataSize;
18#[cfg(any(feature = "testing", test))]
19use rand::Rng;
20#[cfg(any(feature = "std", test))]
21use serde::{Deserialize, Serialize};
22
23#[cfg(any(feature = "testing", test))]
24use crate::testing::TestRng;
25use crate::{
26 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
27 crypto, BlockHash, ChainNameDigest, EraId, FinalitySignature, PublicKey, Signature,
28};
29
30const TAG_LENGTH: usize = U8_SERIALIZED_LENGTH;
31
32pub const BLOCK_SIGNATURES_V1_TAG: u8 = 0;
34pub const BLOCK_SIGNATURES_V2_TAG: u8 = 1;
36
37#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
40#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize))]
41#[cfg_attr(feature = "datasize", derive(DataSize))]
42pub enum BlockSignatures {
43 V1(BlockSignaturesV1),
45 V2(BlockSignaturesV2),
47}
48
49impl BlockSignatures {
50 pub fn block_hash(&self) -> &BlockHash {
52 match self {
53 BlockSignatures::V1(block_signatures) => block_signatures.block_hash(),
54 BlockSignatures::V2(block_signatures) => block_signatures.block_hash(),
55 }
56 }
57
58 pub fn era_id(&self) -> EraId {
60 match self {
61 BlockSignatures::V1(block_signatures) => block_signatures.era_id(),
62 BlockSignatures::V2(block_signatures) => block_signatures.era_id(),
63 }
64 }
65
66 pub fn finality_signature(&self, public_key: &PublicKey) -> Option<FinalitySignature> {
68 match self {
69 BlockSignatures::V1(block_signatures) => block_signatures
70 .finality_signature(public_key)
71 .map(FinalitySignature::V1),
72 BlockSignatures::V2(block_signatures) => block_signatures
73 .finality_signature(public_key)
74 .map(FinalitySignature::V2),
75 }
76 }
77
78 pub fn has_finality_signature(&self, public_key: &PublicKey) -> bool {
80 match self {
81 BlockSignatures::V1(block_signatures) => {
82 block_signatures.has_finality_signature(public_key)
83 }
84 BlockSignatures::V2(block_signatures) => {
85 block_signatures.has_finality_signature(public_key)
86 }
87 }
88 }
89
90 pub fn finality_signatures(&self) -> impl Iterator<Item = FinalitySignature> + '_ {
92 match self {
93 BlockSignatures::V1(block_signatures) => Either::Left(
94 block_signatures
95 .finality_signatures()
96 .map(FinalitySignature::V1),
97 ),
98 BlockSignatures::V2(block_signatures) => Either::Right(
99 block_signatures
100 .finality_signatures()
101 .map(FinalitySignature::V2),
102 ),
103 }
104 }
105
106 pub fn proofs(&self) -> &BTreeMap<PublicKey, Signature> {
108 match self {
109 BlockSignatures::V1(block_signatures) => &block_signatures.proofs,
110 BlockSignatures::V2(block_signatures) => &block_signatures.proofs,
111 }
112 }
113
114 pub fn signers(&self) -> impl Iterator<Item = &'_ PublicKey> + '_ {
116 match self {
117 BlockSignatures::V1(block_signatures) => Either::Left(block_signatures.signers()),
118 BlockSignatures::V2(block_signatures) => Either::Right(block_signatures.signers()),
119 }
120 }
121
122 pub fn len(&self) -> usize {
124 match self {
125 BlockSignatures::V1(block_signatures) => block_signatures.len(),
126 BlockSignatures::V2(block_signatures) => block_signatures.len(),
127 }
128 }
129
130 pub fn is_empty(&self) -> bool {
132 match self {
133 BlockSignatures::V1(block_signatures) => block_signatures.is_empty(),
134 BlockSignatures::V2(block_signatures) => block_signatures.is_empty(),
135 }
136 }
137
138 pub fn merge(&mut self, mut other: Self) -> Result<(), BlockSignaturesMergeError> {
143 if self.block_hash() != other.block_hash() {
144 return Err(BlockSignaturesMergeError::BlockHashMismatch {
145 self_hash: *self.block_hash(),
146 other_hash: *other.block_hash(),
147 });
148 }
149
150 if self.era_id() != other.era_id() {
151 return Err(BlockSignaturesMergeError::EraIdMismatch {
152 self_era_id: self.era_id(),
153 other_era_id: other.era_id(),
154 });
155 }
156
157 match (self, &mut other) {
158 (BlockSignatures::V1(self_), BlockSignatures::V1(other)) => {
159 self_.proofs.append(&mut other.proofs);
160 }
161 (BlockSignatures::V2(self_), BlockSignatures::V2(other)) => {
162 if self_.block_height != other.block_height {
163 return Err(BlockSignaturesMergeError::BlockHeightMismatch {
164 self_height: self_.block_height,
165 other_height: other.block_height,
166 });
167 }
168
169 if self_.chain_name_hash != other.chain_name_hash {
170 return Err(BlockSignaturesMergeError::ChainNameHashMismatch {
171 self_chain_name_hash: self_.chain_name_hash,
172 other_chain_name_hash: other.chain_name_hash,
173 });
174 }
175
176 self_.proofs.append(&mut other.proofs);
177 }
178 _ => return Err(BlockSignaturesMergeError::VersionMismatch),
179 }
180
181 Ok(())
182 }
183
184 pub fn is_verified(&self) -> Result<(), crypto::Error> {
186 match self {
187 BlockSignatures::V1(block_signatures) => block_signatures.is_verified(),
188 BlockSignatures::V2(block_signatures) => block_signatures.is_verified(),
189 }
190 }
191
192 pub fn into_proofs(self) -> BTreeMap<PublicKey, Signature> {
194 match self {
195 BlockSignatures::V1(block_signatures) => block_signatures.proofs,
196 BlockSignatures::V2(block_signatures) => block_signatures.proofs,
197 }
198 }
199
200 pub fn insert_signature(&mut self, public_key: PublicKey, signature: Signature) {
202 match self {
203 BlockSignatures::V1(block_signatures) => {
204 block_signatures.insert_signature(public_key, signature)
205 }
206 BlockSignatures::V2(block_signatures) => {
207 block_signatures.insert_signature(public_key, signature)
208 }
209 }
210 }
211
212 pub fn remove_signature(&mut self, public_key: &PublicKey) -> Option<Signature> {
214 match self {
215 BlockSignatures::V1(block_signatures) => block_signatures.proofs.remove(public_key),
216 BlockSignatures::V2(block_signatures) => block_signatures.proofs.remove(public_key),
217 }
218 }
219
220 #[cfg(any(feature = "testing", test))]
223 pub fn invalidate_era(&mut self) {
224 match self {
225 BlockSignatures::V1(block_signatures) => block_signatures.era_id = EraId::new(u64::MAX),
226 BlockSignatures::V2(block_signatures) => block_signatures.era_id = EraId::new(u64::MAX),
227 }
228 }
229
230 #[cfg(any(feature = "testing", test))]
233 pub fn invalidate_last_signature(&mut self) {
234 let proofs = match self {
235 BlockSignatures::V1(block_signatures) => &mut block_signatures.proofs,
236 BlockSignatures::V2(block_signatures) => &mut block_signatures.proofs,
237 };
238 let last_proof = proofs
239 .last_entry()
240 .expect("should have at least one signature");
241 *last_proof.into_mut() = Signature::System;
242 }
243
244 #[cfg(any(feature = "testing", test))]
246 pub fn random(rng: &mut TestRng) -> Self {
247 if rng.gen() {
248 BlockSignatures::V1(BlockSignaturesV1::random(rng))
249 } else {
250 BlockSignatures::V2(BlockSignaturesV2::random(rng))
251 }
252 }
253}
254
255impl Display for BlockSignatures {
256 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
257 match self {
258 BlockSignatures::V1(block_signatures) => write!(formatter, "{}", block_signatures),
259 BlockSignatures::V2(block_signatures) => write!(formatter, "{}", block_signatures),
260 }
261 }
262}
263
264impl From<BlockSignaturesV1> for BlockSignatures {
265 fn from(block_signatures: BlockSignaturesV1) -> Self {
266 BlockSignatures::V1(block_signatures)
267 }
268}
269
270impl From<BlockSignaturesV2> for BlockSignatures {
271 fn from(block_signatures: BlockSignaturesV2) -> Self {
272 BlockSignatures::V2(block_signatures)
273 }
274}
275
276impl ToBytes for BlockSignatures {
277 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
278 let mut buf = bytesrepr::allocate_buffer(self)?;
279 self.write_bytes(&mut buf)?;
280 Ok(buf)
281 }
282
283 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
284 match self {
285 BlockSignatures::V1(block_signatures) => {
286 writer.push(BLOCK_SIGNATURES_V1_TAG);
287 block_signatures.write_bytes(writer)?;
288 }
289 BlockSignatures::V2(block_signatures) => {
290 writer.push(BLOCK_SIGNATURES_V2_TAG);
291 block_signatures.write_bytes(writer)?;
292 }
293 }
294 Ok(())
295 }
296
297 fn serialized_length(&self) -> usize {
298 TAG_LENGTH
299 + match self {
300 BlockSignatures::V1(block_signatures) => block_signatures.serialized_length(),
301 BlockSignatures::V2(block_signatures) => block_signatures.serialized_length(),
302 }
303 }
304}
305
306impl FromBytes for BlockSignatures {
307 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
308 let (tag, remainder) = u8::from_bytes(bytes)?;
309 match tag {
310 BLOCK_SIGNATURES_V1_TAG => {
311 let (block_signatures, remainder) = BlockSignaturesV1::from_bytes(remainder)?;
312 Ok((BlockSignatures::V1(block_signatures), remainder))
313 }
314 BLOCK_SIGNATURES_V2_TAG => {
315 let (block_signatures, remainder) = BlockSignaturesV2::from_bytes(remainder)?;
316 Ok((BlockSignatures::V2(block_signatures), remainder))
317 }
318 _ => Err(bytesrepr::Error::Formatting),
319 }
320 }
321}
322
323#[derive(Copy, Clone, Eq, PartialEq, Debug)]
325#[non_exhaustive]
326pub enum BlockSignaturesMergeError {
327 BlockHashMismatch {
329 self_hash: BlockHash,
331 other_hash: BlockHash,
333 },
334 BlockHeightMismatch {
336 self_height: u64,
338 other_height: u64,
340 },
341 EraIdMismatch {
343 self_era_id: EraId,
345 other_era_id: EraId,
347 },
348 ChainNameHashMismatch {
350 self_chain_name_hash: ChainNameDigest,
352 other_chain_name_hash: ChainNameDigest,
354 },
355 VersionMismatch,
357}
358
359impl Display for BlockSignaturesMergeError {
360 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
361 match self {
362 BlockSignaturesMergeError::BlockHashMismatch {
363 self_hash,
364 other_hash,
365 } => {
366 write!(
367 formatter,
368 "mismatch between block hashes while merging block signatures - self: {}, \
369 other: {}",
370 self_hash, other_hash
371 )
372 }
373 BlockSignaturesMergeError::BlockHeightMismatch {
374 self_height,
375 other_height,
376 } => {
377 write!(
378 formatter,
379 "mismatch between block heights while merging block signatures - self: {}, \
380 other: {}",
381 self_height, other_height
382 )
383 }
384 BlockSignaturesMergeError::EraIdMismatch {
385 self_era_id,
386 other_era_id,
387 } => {
388 write!(
389 formatter,
390 "mismatch between era ids while merging block signatures - self: {}, other: \
391 {}",
392 self_era_id, other_era_id
393 )
394 }
395 BlockSignaturesMergeError::ChainNameHashMismatch {
396 self_chain_name_hash,
397 other_chain_name_hash,
398 } => {
399 write!(
400 formatter,
401 "mismatch between chain name hashes while merging block signatures - self: {}, \
402 other: {}",
403 self_chain_name_hash, other_chain_name_hash
404 )
405 }
406 BlockSignaturesMergeError::VersionMismatch => {
407 write!(
408 formatter,
409 "mismatch between versions of block signatures while merging"
410 )
411 }
412 }
413 }
414}
415
416#[cfg(feature = "std")]
417impl StdError for BlockSignaturesMergeError {}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn bytesrepr_roundtrip() {
425 let rng = &mut TestRng::new();
426 let hash = BlockSignatures::random(rng);
427 bytesrepr::test_serialization_roundtrip(&hash);
428 }
429}