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