1use alloc::{vec, vec::Vec};
19use codec::{Decode, DecodeWithMemTracking, Encode, Error, Input};
20use core::cmp;
21use scale_info::TypeInfo;
22use sp_application_crypto::RuntimeAppPublic;
23
24use crate::{BeefyAuthorityId, Payload, ValidatorSet, ValidatorSetId};
25
26#[derive(Debug)]
28pub struct KnownSignature<TAuthorityId, TSignature> {
29 pub validator_id: TAuthorityId,
31 pub signature: TSignature,
33}
34
35impl<TAuthorityId: Clone, TSignature: Clone> KnownSignature<&TAuthorityId, &TSignature> {
36 pub fn to_owned(&self) -> KnownSignature<TAuthorityId, TSignature> {
39 KnownSignature {
40 validator_id: self.validator_id.clone(),
41 signature: self.signature.clone(),
42 }
43 }
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
53pub struct Commitment<TBlockNumber> {
54 pub payload: Payload,
64
65 pub block_number: TBlockNumber,
75
76 pub validator_set_id: ValidatorSetId,
83}
84
85impl<TBlockNumber> cmp::PartialOrd for Commitment<TBlockNumber>
86where
87 TBlockNumber: cmp::Ord,
88{
89 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
90 Some(self.cmp(other))
91 }
92}
93
94impl<TBlockNumber> cmp::Ord for Commitment<TBlockNumber>
95where
96 TBlockNumber: cmp::Ord,
97{
98 fn cmp(&self, other: &Self) -> cmp::Ordering {
99 self.validator_set_id
100 .cmp(&other.validator_set_id)
101 .then_with(|| self.block_number.cmp(&other.block_number))
102 .then_with(|| self.payload.cmp(&other.payload))
103 }
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, TypeInfo)]
112pub struct SignedCommitment<TBlockNumber, TSignature> {
113 pub commitment: Commitment<TBlockNumber>,
115 pub signatures: Vec<Option<TSignature>>,
120}
121
122impl<TBlockNumber: core::fmt::Debug, TSignature> core::fmt::Display
123 for SignedCommitment<TBlockNumber, TSignature>
124{
125 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
126 let signatures_count = self.signatures.iter().filter(|s| s.is_some()).count();
127 write!(
128 f,
129 "SignedCommitment(commitment: {:?}, signatures_count: {})",
130 self.commitment, signatures_count
131 )
132 }
133}
134
135impl<TBlockNumber, TSignature> SignedCommitment<TBlockNumber, TSignature> {
136 pub fn signature_count(&self) -> usize {
138 self.signatures.iter().filter(|x| x.is_some()).count()
139 }
140
141 pub fn verify_signatures<'a, TAuthorityId>(
146 &'a self,
147 target_number: TBlockNumber,
148 validator_set: &'a ValidatorSet<TAuthorityId>,
149 ) -> Result<Vec<KnownSignature<&'a TAuthorityId, &'a TSignature>>, u32>
150 where
151 TBlockNumber: Clone + Encode + PartialEq,
152 TAuthorityId: RuntimeAppPublic<Signature = TSignature> + BeefyAuthorityId,
153 {
154 if self.signatures.len() != validator_set.len() ||
155 self.commitment.validator_set_id != validator_set.id() ||
156 self.commitment.block_number != target_number
157 {
158 return Err(0);
159 }
160
161 let encoded_commitment = self.commitment.encode();
164 let signatories: Vec<_> = validator_set
165 .validators()
166 .into_iter()
167 .zip(self.signatures.iter())
168 .filter_map(|(id, maybe_signature)| {
169 let signature = maybe_signature.as_ref()?;
170 match BeefyAuthorityId::verify(id, signature, &encoded_commitment) {
171 true => Some(KnownSignature { validator_id: id, signature }),
172 false => None,
173 }
174 })
175 .collect();
176
177 Ok(signatories)
178 }
179}
180
181type BitField = Vec<u8>;
183const CONTAINER_BIT_SIZE: usize = 8;
185
186#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
188struct CompactSignedCommitment<TBlockNumber, TSignature> {
189 commitment: Commitment<TBlockNumber>,
191 signatures_from: BitField,
197 validator_set_len: u32,
203 signatures_compact: Vec<TSignature>,
209}
210
211impl<'a, TBlockNumber: Clone, TSignature> CompactSignedCommitment<TBlockNumber, &'a TSignature> {
212 fn pack(signed_commitment: &'a SignedCommitment<TBlockNumber, TSignature>) -> Self {
215 let SignedCommitment { commitment, signatures } = signed_commitment;
216 let validator_set_len = signatures.len() as u32;
217
218 let signatures_compact: Vec<&'a TSignature> =
219 signatures.iter().filter_map(|x| x.as_ref()).collect();
220 let bits = {
221 let mut bits: Vec<u8> =
222 signatures.iter().map(|x| if x.is_some() { 1 } else { 0 }).collect();
223 let excess_bits_len =
225 CONTAINER_BIT_SIZE - (validator_set_len as usize % CONTAINER_BIT_SIZE);
226 bits.resize(bits.len() + excess_bits_len, 0);
227 bits
228 };
229
230 let mut signatures_from: BitField = vec![];
231 let chunks = bits.chunks(CONTAINER_BIT_SIZE);
232 for chunk in chunks {
233 let mut iter = chunk.iter().copied();
234 let mut v = iter.next().unwrap() as u8;
235
236 for bit in iter {
237 v <<= 1;
238 v |= bit as u8;
239 }
240
241 signatures_from.push(v);
242 }
243
244 Self {
245 commitment: commitment.clone(),
246 signatures_from,
247 validator_set_len,
248 signatures_compact,
249 }
250 }
251
252 fn unpack(
254 temporary_signatures: CompactSignedCommitment<TBlockNumber, TSignature>,
255 ) -> SignedCommitment<TBlockNumber, TSignature> {
256 let CompactSignedCommitment {
257 commitment,
258 signatures_from,
259 validator_set_len,
260 signatures_compact,
261 } = temporary_signatures;
262 let mut bits: Vec<u8> = vec![];
263
264 for block in signatures_from {
265 for bit in 0..CONTAINER_BIT_SIZE {
266 bits.push((block >> (CONTAINER_BIT_SIZE - bit - 1)) & 1);
267 }
268 }
269
270 bits.truncate(validator_set_len as usize);
271
272 let mut next_signature = signatures_compact.into_iter();
273 let signatures: Vec<Option<TSignature>> = bits
274 .iter()
275 .map(|&x| if x == 1 { next_signature.next() } else { None })
276 .collect();
277
278 SignedCommitment { commitment, signatures }
279 }
280}
281
282impl<TBlockNumber, TSignature> Encode for SignedCommitment<TBlockNumber, TSignature>
283where
284 TBlockNumber: Encode + Clone,
285 TSignature: Encode,
286{
287 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
288 let temp = CompactSignedCommitment::pack(self);
289 temp.using_encoded(f)
290 }
291}
292
293impl<TBlockNumber, TSignature> Decode for SignedCommitment<TBlockNumber, TSignature>
294where
295 TBlockNumber: Decode + Clone,
296 TSignature: Decode,
297{
298 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
299 let temp = CompactSignedCommitment::decode(input)?;
300 Ok(CompactSignedCommitment::unpack(temp))
301 }
302}
303
304#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
312pub enum VersionedFinalityProof<N, S> {
313 #[codec(index = 1)]
314 V1(SignedCommitment<N, S>),
316}
317
318impl<N: core::fmt::Debug, S> core::fmt::Display for VersionedFinalityProof<N, S> {
319 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
320 match self {
321 VersionedFinalityProof::V1(sc) => write!(f, "VersionedFinalityProof::V1({})", sc),
322 }
323 }
324}
325
326impl<N, S> From<SignedCommitment<N, S>> for VersionedFinalityProof<N, S> {
327 fn from(commitment: SignedCommitment<N, S>) -> Self {
328 VersionedFinalityProof::V1(commitment)
329 }
330}
331
332#[cfg(test)]
333mod tests {
334
335 use super::*;
336 use crate::{ecdsa_crypto::Signature as EcdsaSignature, known_payloads};
337 use codec::Decode;
338 use sp_core::Pair;
339 use sp_crypto_hashing::keccak_256;
340
341 #[cfg(feature = "bls-experimental")]
342 use crate::bls_crypto::Signature as BlsSignature;
343
344 type TestCommitment = Commitment<u128>;
345
346 const LARGE_RAW_COMMITMENT: &[u8] = include_bytes!("../test-res/large-raw-commitment");
347
348 type TestEcdsaSignedCommitment = SignedCommitment<u128, EcdsaSignature>;
350 type TestVersionedFinalityProof = VersionedFinalityProof<u128, EcdsaSignature>;
351
352 #[cfg(feature = "bls-experimental")]
353 #[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
354 struct EcdsaBlsSignaturePair(EcdsaSignature, BlsSignature);
355
356 #[cfg(feature = "bls-experimental")]
357 type TestBlsSignedCommitment = SignedCommitment<u128, EcdsaBlsSignaturePair>;
358
359 fn mock_ecdsa_signatures() -> (EcdsaSignature, EcdsaSignature) {
362 let alice = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
363
364 let msg = keccak_256(b"This is the first message");
365 let sig1 = alice.sign_prehashed(&msg);
366
367 let msg = keccak_256(b"This is the second message");
368 let sig2 = alice.sign_prehashed(&msg);
369
370 (sig1.into(), sig2.into())
371 }
372
373 #[cfg(feature = "bls-experimental")]
376 fn mock_bls_signatures() -> (BlsSignature, BlsSignature) {
377 let alice = sp_core::bls::Pair::from_string("//Alice", None).unwrap();
378
379 let msg = b"This is the first message";
380 let sig1 = alice.sign(msg);
381
382 let msg = b"This is the second message";
383 let sig2 = alice.sign(msg);
384
385 (sig1.into(), sig2.into())
386 }
387
388 #[test]
389 fn commitment_encode_decode() {
390 let payload =
392 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
393 let commitment: TestCommitment =
394 Commitment { payload, block_number: 5, validator_set_id: 0 };
395
396 let encoded = codec::Encode::encode(&commitment);
398 let decoded = TestCommitment::decode(&mut &*encoded);
399
400 assert_eq!(decoded, Ok(commitment));
402 assert_eq!(
403 encoded,
404 array_bytes::hex2bytes_unchecked(
405 "046d68343048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000"
406 )
407 );
408 }
409
410 #[test]
411 fn signed_commitment_encode_decode_ecdsa() {
412 let payload =
414 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
415 let commitment: TestCommitment =
416 Commitment { payload, block_number: 5, validator_set_id: 0 };
417
418 let ecdsa_sigs = mock_ecdsa_signatures();
419
420 let ecdsa_signed = SignedCommitment {
421 commitment: commitment.clone(),
422 signatures: vec![None, None, Some(ecdsa_sigs.0.clone()), Some(ecdsa_sigs.1.clone())],
423 };
424
425 let encoded = codec::Encode::encode(&ecdsa_signed);
427 let decoded = TestEcdsaSignedCommitment::decode(&mut &*encoded);
428
429 assert_eq!(decoded, Ok(ecdsa_signed));
431 assert_eq!(
432 encoded,
433 array_bytes::hex2bytes_unchecked(
434 "\
435 046d68343048656c6c6f20576f726c64210500000000000000000000000000000000000000000000000\
436 4300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746c\
437 c321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba012d6e1f8105c337a86cdd9aa\
438 acdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6\
439 a0046395a71681be3d0c2a00\
440 "
441 )
442 );
443 }
444
445 #[test]
446 #[cfg(feature = "bls-experimental")]
447 fn signed_commitment_encode_decode_ecdsa_n_bls() {
448 let payload =
450 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
451 let commitment: TestCommitment =
452 Commitment { payload, block_number: 5, validator_set_id: 0 };
453
454 let ecdsa_sigs = mock_ecdsa_signatures();
455
456 let bls_signed_msgs = mock_bls_signatures();
458
459 let ecdsa_and_bls_signed = SignedCommitment {
460 commitment,
461 signatures: vec![
462 None,
463 None,
464 Some(EcdsaBlsSignaturePair(ecdsa_sigs.0, bls_signed_msgs.0)),
465 Some(EcdsaBlsSignaturePair(ecdsa_sigs.1, bls_signed_msgs.1)),
466 ],
467 };
468
469 let encoded = codec::Encode::encode(&ecdsa_and_bls_signed);
471 let decoded = TestBlsSignedCommitment::decode(&mut &*encoded);
472
473 assert_eq!(decoded, Ok(ecdsa_and_bls_signed));
475 assert_eq!(
476 encoded,
477 array_bytes::hex2bytes_unchecked(
478 "046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba0182022df4689ef25499205f7154a1a62eb2d6d5c4a3657efed321e2c277998130d1b01a264c928afb79534cb0fa9dcf79f67ed4e6bf2de576bb936146f2fa60fa56b8651677cc764ea4fe317c62294c2a0c5966e439653eed0572fded5e2461c888518e0769718dcce9f3ff612fb89d262d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00a90973bea76fac3a4e2d76a25ec3926d6a5a20aacee15ec0756cd268088ed5612b67b4a49349cee70bc1185078d17c7f7df9d944e8be30022d9680d0437c4ba4600d74050692e8ee9b96e37df2a39d1cb4b4af4b6a058342dd9e8c7481a3a0b8975ad8614c953e950253aa327698d842"
479 )
480 );
481 }
482
483 #[test]
484 fn signed_commitment_count_signatures() {
485 let payload =
487 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
488 let commitment: TestCommitment =
489 Commitment { payload, block_number: 5, validator_set_id: 0 };
490
491 let sigs = mock_ecdsa_signatures();
492
493 let mut signed = SignedCommitment {
494 commitment,
495 signatures: vec![None, None, Some(sigs.0), Some(sigs.1)],
496 };
497 assert_eq!(signed.signature_count(), 2);
498
499 signed.signatures[2] = None;
501
502 assert_eq!(signed.signature_count(), 1);
504 }
505
506 #[test]
507 fn commitment_ordering() {
508 fn commitment(
509 block_number: u128,
510 validator_set_id: crate::ValidatorSetId,
511 ) -> TestCommitment {
512 let payload =
513 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
514 Commitment { payload, block_number, validator_set_id }
515 }
516
517 let a = commitment(1, 0);
519 let b = commitment(2, 1);
520 let c = commitment(10, 0);
521 let d = commitment(10, 1);
522
523 assert!(a < b);
525 assert!(a < c);
526 assert!(c < b);
527 assert!(c < d);
528 assert!(b < d);
529 }
530
531 #[test]
532 fn versioned_commitment_encode_decode() {
533 let payload =
534 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
535 let commitment: TestCommitment =
536 Commitment { payload, block_number: 5, validator_set_id: 0 };
537
538 let sigs = mock_ecdsa_signatures();
539
540 let signed = SignedCommitment {
541 commitment,
542 signatures: vec![None, None, Some(sigs.0), Some(sigs.1)],
543 };
544
545 let versioned = TestVersionedFinalityProof::V1(signed.clone());
546
547 let encoded = codec::Encode::encode(&versioned);
548
549 assert_eq!(1, encoded[0]);
550 assert_eq!(encoded[1..], codec::Encode::encode(&signed));
551
552 let decoded = TestVersionedFinalityProof::decode(&mut &*encoded);
553
554 assert_eq!(decoded, Ok(versioned));
555 }
556
557 #[test]
558 fn large_signed_commitment_encode_decode() {
559 let payload =
561 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, "Hello World!".encode());
562 let commitment: TestCommitment =
563 Commitment { payload, block_number: 5, validator_set_id: 0 };
564
565 let sigs = mock_ecdsa_signatures();
566
567 let signatures: Vec<Option<_>> = (0..1024)
568 .into_iter()
569 .map(|x| if x < 340 { None } else { Some(sigs.0.clone()) })
570 .collect();
571 let signed = SignedCommitment { commitment, signatures };
572
573 let encoded = codec::Encode::encode(&signed);
575 let decoded = TestEcdsaSignedCommitment::decode(&mut &*encoded);
576
577 assert_eq!(decoded, Ok(signed));
579 assert_eq!(encoded, LARGE_RAW_COMMITMENT);
580 }
581}