1use super::Ed25519PublicKey;
2use super::Ed25519Signature;
3use super::PasskeyAuthenticator;
4use super::PasskeyPublicKey;
5use super::Secp256k1PublicKey;
6use super::Secp256k1Signature;
7use super::Secp256r1PublicKey;
8use super::Secp256r1Signature;
9use super::SignatureScheme;
10use super::zklogin::ZkLoginAuthenticator;
11use super::zklogin::ZkLoginPublicIdentifier;
12
13pub type WeightUnit = u8;
14pub type ThresholdUnit = u16;
15pub type BitmapUnit = u16;
16
17const MAX_COMMITTEE_SIZE: usize = 10;
18#[derive(Clone, Debug, PartialEq, Eq)]
50#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
51#[non_exhaustive]
52pub enum MultisigMemberPublicKey {
53 Ed25519(Ed25519PublicKey),
54 Secp256k1(Secp256k1PublicKey),
55 Secp256r1(Secp256r1PublicKey),
56 ZkLogin(ZkLoginPublicIdentifier),
57 Passkey(PasskeyPublicKey),
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
78#[cfg_attr(
79 feature = "serde",
80 derive(serde_derive::Serialize, serde_derive::Deserialize)
81)]
82#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
83pub struct MultisigMember {
84 public_key: MultisigMemberPublicKey,
85 weight: WeightUnit,
86}
87
88impl MultisigMember {
89 pub fn new(public_key: MultisigMemberPublicKey, weight: WeightUnit) -> Self {
91 Self { public_key, weight }
92 }
93
94 pub fn public_key(&self) -> &MultisigMemberPublicKey {
96 &self.public_key
97 }
98
99 pub fn weight(&self) -> WeightUnit {
101 self.weight
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
143#[cfg_attr(
144 feature = "serde",
145 derive(serde_derive::Serialize, serde_derive::Deserialize)
146)]
147#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
148pub struct MultisigCommittee {
149 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))]
151 members: Vec<MultisigMember>,
152
153 threshold: ThresholdUnit,
156}
157
158impl MultisigCommittee {
159 pub fn new(members: Vec<MultisigMember>, threshold: ThresholdUnit) -> Self {
164 Self { members, threshold }
165 }
166
167 pub fn members(&self) -> &[MultisigMember] {
169 &self.members
170 }
171
172 pub fn threshold(&self) -> ThresholdUnit {
175 self.threshold
176 }
177
178 pub fn scheme(&self) -> SignatureScheme {
180 SignatureScheme::Multisig
181 }
182
183 pub fn is_valid(&self) -> bool {
193 self.threshold != 0
194 && !self.members.is_empty()
195 && self.members.len() <= MAX_COMMITTEE_SIZE
196 && !self.members.iter().any(|member| member.weight == 0)
197 && self
198 .members
199 .iter()
200 .map(|member| member.weight as ThresholdUnit)
201 .sum::<ThresholdUnit>()
202 >= self.threshold
203 && !self.members.iter().enumerate().any(|(i, member)| {
204 self.members
205 .iter()
206 .skip(i + 1)
207 .any(|m| member.public_key == m.public_key)
208 })
209 }
210}
211
212#[derive(Debug, Clone)]
238#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
239pub struct MultisigAggregatedSignature {
240 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))]
244 signatures: Vec<MultisigMemberSignature>,
245 bitmap: BitmapUnit,
248 #[cfg_attr(feature = "proptest", strategy(proptest::strategy::Just(None)))]
251 legacy_bitmap: Option<crate::Bitmap>,
252 committee: MultisigCommittee,
255}
256
257impl MultisigAggregatedSignature {
258 pub fn new(
265 committee: MultisigCommittee,
266 signatures: Vec<MultisigMemberSignature>,
267 bitmap: BitmapUnit,
268 ) -> Self {
269 Self {
270 signatures,
271 bitmap,
272 legacy_bitmap: None,
273 committee,
274 }
275 }
276
277 pub fn signatures(&self) -> &[MultisigMemberSignature] {
279 &self.signatures
280 }
281
282 pub fn bitmap(&self) -> BitmapUnit {
284 self.bitmap
285 }
286
287 pub fn legacy_bitmap(&self) -> Option<&crate::Bitmap> {
289 self.legacy_bitmap.as_ref()
290 }
291
292 pub fn with_legacy_bitmap(&mut self, legacy_bitmap: crate::Bitmap) {
294 self.legacy_bitmap = Some(legacy_bitmap);
295 }
296
297 pub fn committee(&self) -> &MultisigCommittee {
299 &self.committee
300 }
301}
302
303impl PartialEq for MultisigAggregatedSignature {
304 fn eq(&self, other: &Self) -> bool {
305 self.bitmap == other.bitmap
315 && self.legacy_bitmap == other.legacy_bitmap
316 && self.signatures == other.signatures
317 && self.committee == other.committee
318 }
319}
320
321impl Eq for MultisigAggregatedSignature {}
322
323#[cfg(feature = "serde")]
325fn roaring_bitmap_to_u16(roaring: &crate::Bitmap) -> Result<BitmapUnit, &'static str> {
326 let mut val = 0;
327 for i in roaring.iter() {
328 if i >= MAX_COMMITTEE_SIZE as u32 {
329 return Err("invalid bitmap");
330 }
331 val |= 1 << i as u8;
332 }
333 Ok(val)
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
354#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
355#[non_exhaustive]
356pub enum MultisigMemberSignature {
357 Ed25519(Ed25519Signature),
358 Secp256k1(Secp256k1Signature),
359 Secp256r1(Secp256r1Signature),
360 ZkLogin(Box<ZkLoginAuthenticator>),
361 Passkey(PasskeyAuthenticator),
362}
363
364#[cfg(feature = "serde")]
365#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
366mod serialization {
367 use super::*;
368 use crate::Ed25519PublicKey;
369 use crate::PasskeyPublicKey;
370 use crate::Secp256k1PublicKey;
371 use crate::Secp256r1PublicKey;
372 use crate::SignatureScheme;
373 use crate::crypto::Base64Array33;
374 use crate::crypto::Base64Array34;
375 use base64ct::Base64;
376 use base64ct::Encoding;
377 use serde::Deserialize;
378 use serde::Deserializer;
379 use serde::Serialize;
380 use serde::Serializer;
381 use serde_with::Bytes;
382 use serde_with::DeserializeAs;
383 use serde_with::SerializeAs;
384 use std::borrow::Cow;
385
386 pub struct Base64MultisigMemberPublicKey;
387
388 impl SerializeAs<MultisigMemberPublicKey> for Base64MultisigMemberPublicKey {
389 fn serialize_as<S>(
390 source: &MultisigMemberPublicKey,
391 serializer: S,
392 ) -> Result<S::Ok, S::Error>
393 where
394 S: Serializer,
395 {
396 match source {
397 MultisigMemberPublicKey::Ed25519(public_key) => {
398 let mut buf = [0; 1 + Ed25519PublicKey::LENGTH];
399 buf[0] = SignatureScheme::Ed25519 as u8;
400 buf[1..].copy_from_slice(public_key.as_ref());
401 Base64Array33::serialize_as(&buf, serializer)
402 }
403 MultisigMemberPublicKey::Secp256k1(public_key) => {
404 let mut buf = [0; 1 + Secp256k1PublicKey::LENGTH];
405 buf[0] = SignatureScheme::Secp256k1 as u8;
406 buf[1..].copy_from_slice(public_key.as_ref());
407 Base64Array34::serialize_as(&buf, serializer)
408 }
409 MultisigMemberPublicKey::Secp256r1(public_key) => {
410 let mut buf = [0; 1 + Secp256r1PublicKey::LENGTH];
411 buf[0] = SignatureScheme::Secp256r1 as u8;
412 buf[1..].copy_from_slice(public_key.as_ref());
413 Base64Array34::serialize_as(&buf, serializer)
414 }
415 MultisigMemberPublicKey::ZkLogin(_) => Err(serde::ser::Error::custom(
416 "zklogin not supported in legacy multisig",
417 )),
418 MultisigMemberPublicKey::Passkey(_) => Err(serde::ser::Error::custom(
419 "passkey not supported in legacy multisig",
420 )),
421 }
422 }
423 }
424
425 impl<'de> DeserializeAs<'de, MultisigMemberPublicKey> for Base64MultisigMemberPublicKey {
426 fn deserialize_as<D>(deserializer: D) -> Result<MultisigMemberPublicKey, D::Error>
427 where
428 D: Deserializer<'de>,
429 {
430 let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
431 let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
432 let flag = SignatureScheme::from_byte(
433 *bytes
434 .first()
435 .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?,
436 )
437 .map_err(serde::de::Error::custom)?;
438 let public_key_bytes = &bytes[1..];
439 match flag {
440 SignatureScheme::Ed25519 => {
441 let public_key = Ed25519PublicKey::from_bytes(public_key_bytes)
442 .map_err(serde::de::Error::custom)?;
443 Ok(MultisigMemberPublicKey::Ed25519(public_key))
444 }
445 SignatureScheme::Secp256k1 => {
446 let public_key = Secp256k1PublicKey::from_bytes(public_key_bytes)
447 .map_err(serde::de::Error::custom)?;
448 Ok(MultisigMemberPublicKey::Secp256k1(public_key))
449 }
450 SignatureScheme::Secp256r1 => {
451 let public_key = Secp256r1PublicKey::from_bytes(public_key_bytes)
452 .map_err(serde::de::Error::custom)?;
453 Ok(MultisigMemberPublicKey::Secp256r1(public_key))
454 }
455 SignatureScheme::Multisig
456 | SignatureScheme::Bls12381
457 | SignatureScheme::ZkLogin
458 | SignatureScheme::Passkey => {
459 Err(serde::de::Error::custom("invalid public key type"))
460 }
461 }
462 }
463 }
464
465 pub struct LegacyMultisigMember;
466
467 impl SerializeAs<MultisigMember> for LegacyMultisigMember {
468 fn serialize_as<S>(source: &MultisigMember, serializer: S) -> Result<S::Ok, S::Error>
469 where
470 S: Serializer,
471 {
472 #[derive(serde_derive::Serialize)]
473 struct LegacyMember<'a> {
474 #[serde(with = "::serde_with::As::<Base64MultisigMemberPublicKey>")]
475 public_key: &'a MultisigMemberPublicKey,
476 weight: WeightUnit,
477 }
478
479 let legacy = LegacyMember {
480 public_key: &source.public_key,
481 weight: source.weight,
482 };
483
484 legacy.serialize(serializer)
485 }
486 }
487
488 impl<'de> DeserializeAs<'de, MultisigMember> for LegacyMultisigMember {
489 fn deserialize_as<D>(deserializer: D) -> Result<MultisigMember, D::Error>
490 where
491 D: Deserializer<'de>,
492 {
493 #[derive(serde_derive::Deserialize)]
494 struct LegacyMember {
495 #[serde(with = "::serde_with::As::<Base64MultisigMemberPublicKey>")]
496 public_key: MultisigMemberPublicKey,
497 weight: WeightUnit,
498 }
499
500 let legacy = LegacyMember::deserialize(deserializer)?;
501
502 Ok(MultisigMember {
503 public_key: legacy.public_key,
504 weight: legacy.weight,
505 })
506 }
507 }
508
509 #[derive(serde_derive::Deserialize)]
510 pub struct Multisig {
511 signatures: Vec<MultisigMemberSignature>,
512 bitmap: BitmapUnit,
513 committee: MultisigCommittee,
514 }
515
516 #[derive(serde_derive::Serialize)]
517 pub struct MultisigRef<'a> {
518 signatures: &'a [MultisigMemberSignature],
519 bitmap: BitmapUnit,
520 committee: &'a MultisigCommittee,
521 }
522
523 #[derive(serde_derive::Deserialize)]
524 pub struct LegacyMultisig {
525 signatures: Vec<MultisigMemberSignature>,
526 bitmap: crate::Bitmap,
527 committee: LegacyMultisigCommittee,
528 }
529
530 #[derive(serde_derive::Serialize)]
531 pub struct LegacyMultisigRef<'a> {
532 signatures: &'a [MultisigMemberSignature],
533 bitmap: &'a crate::Bitmap,
534 committee: LegacyMultisigCommitteeRef<'a>,
535 }
536
537 #[derive(serde_derive::Deserialize)]
538 struct LegacyMultisigCommittee {
539 #[serde(with = "::serde_with::As::<Vec<LegacyMultisigMember>>")]
540 members: Vec<MultisigMember>,
541 threshold: ThresholdUnit,
542 }
543
544 #[derive(serde_derive::Serialize)]
545 struct LegacyMultisigCommitteeRef<'a> {
546 #[serde(with = "::serde_with::As::<&[LegacyMultisigMember]>")]
547 members: &'a [MultisigMember],
548 threshold: ThresholdUnit,
549 }
550
551 #[derive(serde_derive::Deserialize)]
552 struct ReadableMultisigAggregatedSignature {
553 signatures: Vec<MultisigMemberSignature>,
554 bitmap: BitmapUnit,
555 legacy_bitmap: Option<crate::Bitmap>,
556 committee: MultisigCommittee,
557 }
558
559 #[derive(serde_derive::Serialize)]
560 struct ReadableMultisigAggregatedSignatureRef<'a> {
561 signatures: &'a [MultisigMemberSignature],
562 bitmap: BitmapUnit,
563 #[serde(skip_serializing_if = "Option::is_none")]
564 legacy_bitmap: &'a Option<crate::Bitmap>,
565 committee: &'a MultisigCommittee,
566 }
567
568 impl Serialize for MultisigAggregatedSignature {
569 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
570 where
571 S: Serializer,
572 {
573 if serializer.is_human_readable() {
574 let readable = ReadableMultisigAggregatedSignatureRef {
575 signatures: &self.signatures,
576 bitmap: self.bitmap,
577 legacy_bitmap: &self.legacy_bitmap,
578 committee: &self.committee,
579 };
580 readable.serialize(serializer)
581 } else {
582 let bytes = self.to_bytes();
583 serializer.serialize_bytes(&bytes)
584 }
585 }
586 }
587
588 impl<'de> Deserialize<'de> for MultisigAggregatedSignature {
589 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
590 where
591 D: Deserializer<'de>,
592 {
593 if deserializer.is_human_readable() {
594 let readable = ReadableMultisigAggregatedSignature::deserialize(deserializer)?;
595 if let Some(legacy_bitmap) = &readable.legacy_bitmap {
606 let derived =
607 roaring_bitmap_to_u16(legacy_bitmap).map_err(serde::de::Error::custom)?;
608 if derived != readable.bitmap {
609 return Err(serde::de::Error::custom(
610 "bitmap does not match legacy_bitmap",
611 ));
612 }
613 for member in &readable.committee.members {
626 match member.public_key {
627 MultisigMemberPublicKey::ZkLogin(_) => {
628 return Err(serde::de::Error::custom(
629 "zklogin member is not representable in legacy multisig",
630 ));
631 }
632 MultisigMemberPublicKey::Passkey(_) => {
633 return Err(serde::de::Error::custom(
634 "passkey member is not representable in legacy multisig",
635 ));
636 }
637 MultisigMemberPublicKey::Ed25519(_)
638 | MultisigMemberPublicKey::Secp256k1(_)
639 | MultisigMemberPublicKey::Secp256r1(_) => {}
640 }
641 }
642 }
643 Ok(Self {
644 signatures: readable.signatures,
645 bitmap: readable.bitmap,
646 legacy_bitmap: readable.legacy_bitmap,
647 committee: readable.committee,
648 })
649 } else {
650 let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?;
651 Self::from_serialized_bytes(bytes)
652 }
653 }
654 }
655
656 impl MultisigAggregatedSignature {
657 pub(crate) fn to_bytes(&self) -> Vec<u8> {
658 let mut buf = Vec::new();
659 buf.push(SignatureScheme::Multisig as u8);
660
661 if let Some(bitmap) = &self.legacy_bitmap {
662 let legacy = LegacyMultisigRef {
663 signatures: &self.signatures,
664 bitmap,
665 committee: LegacyMultisigCommitteeRef {
666 members: &self.committee.members,
667 threshold: self.committee.threshold,
668 },
669 };
670
671 bcs::serialize_into(&mut buf, &legacy).expect("serialization cannot fail");
672 } else {
673 let multisig = MultisigRef {
674 signatures: &self.signatures,
675 bitmap: self.bitmap,
676 committee: &self.committee,
677 };
678 bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail");
679 }
680 buf
681 }
682
683 pub(crate) fn from_serialized_bytes<T: AsRef<[u8]>, E: serde::de::Error>(
684 bytes: T,
685 ) -> Result<Self, E> {
686 let bytes = bytes.as_ref();
687 let flag = SignatureScheme::from_byte(
688 *bytes
689 .first()
690 .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?,
691 )
692 .map_err(serde::de::Error::custom)?;
693 if flag != SignatureScheme::Multisig {
694 return Err(serde::de::Error::custom("invalid multisig flag"));
695 }
696 let bcs_bytes = &bytes[1..];
697
698 if let Ok(multisig) = bcs::from_bytes::<Multisig>(bcs_bytes) {
704 Ok(Self {
705 signatures: multisig.signatures,
706 bitmap: multisig.bitmap,
707 legacy_bitmap: None,
708 committee: multisig.committee,
709 })
710 } else if let Ok(legacy) = bcs::from_bytes::<LegacyMultisig>(bcs_bytes) {
711 Ok(Self {
712 signatures: legacy.signatures,
713 bitmap: roaring_bitmap_to_u16(&legacy.bitmap)
714 .map_err(serde::de::Error::custom)?,
715 legacy_bitmap: Some(legacy.bitmap),
716 committee: MultisigCommittee {
717 members: legacy.committee.members,
718 threshold: legacy.committee.threshold,
719 },
720 })
721 } else {
722 Err(serde::de::Error::custom("invalid multisig"))
723 }
724 }
725 }
726
727 #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
728 enum MemberPublicKey {
729 Ed25519(Ed25519PublicKey),
730 Secp256k1(Secp256k1PublicKey),
731 Secp256r1(Secp256r1PublicKey),
732 ZkLogin(ZkLoginPublicIdentifier),
733 Passkey(PasskeyPublicKey),
734 }
735
736 #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
737 #[serde(tag = "scheme", rename_all = "lowercase")]
738 #[serde(rename = "MultisigMemberPublicKey")]
739 enum ReadableMemberPublicKey {
740 Ed25519 { public_key: Ed25519PublicKey },
741 Secp256k1 { public_key: Secp256k1PublicKey },
742 Secp256r1 { public_key: Secp256r1PublicKey },
743 ZkLogin(ZkLoginPublicIdentifier),
744 Passkey { public_key: PasskeyPublicKey },
745 }
746
747 impl Serialize for MultisigMemberPublicKey {
748 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
749 where
750 S: Serializer,
751 {
752 if serializer.is_human_readable() {
753 let readable = match self {
754 MultisigMemberPublicKey::Ed25519(public_key) => {
755 ReadableMemberPublicKey::Ed25519 {
756 public_key: *public_key,
757 }
758 }
759 MultisigMemberPublicKey::Secp256k1(public_key) => {
760 ReadableMemberPublicKey::Secp256k1 {
761 public_key: *public_key,
762 }
763 }
764 MultisigMemberPublicKey::Secp256r1(public_key) => {
765 ReadableMemberPublicKey::Secp256r1 {
766 public_key: *public_key,
767 }
768 }
769 MultisigMemberPublicKey::ZkLogin(public_id) => {
770 ReadableMemberPublicKey::ZkLogin(public_id.clone())
771 }
772 MultisigMemberPublicKey::Passkey(public_key) => {
773 ReadableMemberPublicKey::Passkey {
774 public_key: *public_key,
775 }
776 }
777 };
778 readable.serialize(serializer)
779 } else {
780 let binary = match self {
781 MultisigMemberPublicKey::Ed25519(public_key) => {
782 MemberPublicKey::Ed25519(*public_key)
783 }
784 MultisigMemberPublicKey::Secp256k1(public_key) => {
785 MemberPublicKey::Secp256k1(*public_key)
786 }
787 MultisigMemberPublicKey::Secp256r1(public_key) => {
788 MemberPublicKey::Secp256r1(*public_key)
789 }
790 MultisigMemberPublicKey::ZkLogin(public_id) => {
791 MemberPublicKey::ZkLogin(public_id.clone())
792 }
793 MultisigMemberPublicKey::Passkey(public_key) => {
794 MemberPublicKey::Passkey(*public_key)
795 }
796 };
797 binary.serialize(serializer)
798 }
799 }
800 }
801
802 impl<'de> Deserialize<'de> for MultisigMemberPublicKey {
803 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
804 where
805 D: Deserializer<'de>,
806 {
807 if deserializer.is_human_readable() {
808 let readable = ReadableMemberPublicKey::deserialize(deserializer)?;
809 Ok(match readable {
810 ReadableMemberPublicKey::Ed25519 { public_key } => Self::Ed25519(public_key),
811 ReadableMemberPublicKey::Secp256k1 { public_key } => {
812 Self::Secp256k1(public_key)
813 }
814 ReadableMemberPublicKey::Secp256r1 { public_key } => {
815 Self::Secp256r1(public_key)
816 }
817 ReadableMemberPublicKey::ZkLogin(public_id) => Self::ZkLogin(public_id),
818 ReadableMemberPublicKey::Passkey { public_key } => Self::Passkey(public_key),
819 })
820 } else {
821 let binary = MemberPublicKey::deserialize(deserializer)?;
822 Ok(match binary {
823 MemberPublicKey::Ed25519(public_key) => Self::Ed25519(public_key),
824 MemberPublicKey::Secp256k1(public_key) => Self::Secp256k1(public_key),
825 MemberPublicKey::Secp256r1(public_key) => Self::Secp256r1(public_key),
826 MemberPublicKey::ZkLogin(public_id) => Self::ZkLogin(public_id),
827 MemberPublicKey::Passkey(public_key) => Self::Passkey(public_key),
828 })
829 }
830 }
831 }
832
833 #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
834 enum MemberSignature {
835 Ed25519(Ed25519Signature),
836 Secp256k1(Secp256k1Signature),
837 Secp256r1(Secp256r1Signature),
838 ZkLogin(Box<ZkLoginAuthenticator>),
839 Passkey(PasskeyAuthenticator),
840 }
841
842 #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
843 #[serde(tag = "scheme", rename_all = "lowercase")]
844 #[serde(rename = "MultisigMemberSignature")]
845 enum ReadableMemberSignature {
846 Ed25519 { signature: Ed25519Signature },
847 Secp256k1 { signature: Secp256k1Signature },
848 Secp256r1 { signature: Secp256r1Signature },
849 ZkLogin(Box<ZkLoginAuthenticator>),
850 Passkey(PasskeyAuthenticator),
851 }
852
853 impl Serialize for MultisigMemberSignature {
854 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
855 where
856 S: Serializer,
857 {
858 if serializer.is_human_readable() {
859 let readable = match self {
860 MultisigMemberSignature::Ed25519(signature) => {
861 ReadableMemberSignature::Ed25519 {
862 signature: *signature,
863 }
864 }
865 MultisigMemberSignature::Secp256k1(signature) => {
866 ReadableMemberSignature::Secp256k1 {
867 signature: *signature,
868 }
869 }
870 MultisigMemberSignature::Secp256r1(signature) => {
871 ReadableMemberSignature::Secp256r1 {
872 signature: *signature,
873 }
874 }
875 MultisigMemberSignature::ZkLogin(authenticator) => {
876 ReadableMemberSignature::ZkLogin(authenticator.clone())
877 }
878 MultisigMemberSignature::Passkey(authenticator) => {
879 ReadableMemberSignature::Passkey(authenticator.clone())
880 }
881 };
882 readable.serialize(serializer)
883 } else {
884 let binary = match self {
885 MultisigMemberSignature::Ed25519(signature) => {
886 MemberSignature::Ed25519(*signature)
887 }
888 MultisigMemberSignature::Secp256k1(signature) => {
889 MemberSignature::Secp256k1(*signature)
890 }
891 MultisigMemberSignature::Secp256r1(signature) => {
892 MemberSignature::Secp256r1(*signature)
893 }
894 MultisigMemberSignature::ZkLogin(authenticator) => {
895 MemberSignature::ZkLogin(authenticator.clone())
896 }
897 MultisigMemberSignature::Passkey(authenticator) => {
898 MemberSignature::Passkey(authenticator.clone())
899 }
900 };
901 binary.serialize(serializer)
902 }
903 }
904 }
905
906 impl<'de> Deserialize<'de> for MultisigMemberSignature {
907 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
908 where
909 D: Deserializer<'de>,
910 {
911 if deserializer.is_human_readable() {
912 let readable = ReadableMemberSignature::deserialize(deserializer)?;
913 Ok(match readable {
914 ReadableMemberSignature::Ed25519 { signature } => Self::Ed25519(signature),
915 ReadableMemberSignature::Secp256k1 { signature } => Self::Secp256k1(signature),
916 ReadableMemberSignature::Secp256r1 { signature } => Self::Secp256r1(signature),
917 ReadableMemberSignature::ZkLogin(authenticator) => Self::ZkLogin(authenticator),
918 ReadableMemberSignature::Passkey(authenticator) => Self::Passkey(authenticator),
919 })
920 } else {
921 let binary = MemberSignature::deserialize(deserializer)?;
922 Ok(match binary {
923 MemberSignature::Ed25519(signature) => Self::Ed25519(signature),
924 MemberSignature::Secp256k1(signature) => Self::Secp256k1(signature),
925 MemberSignature::Secp256r1(signature) => Self::Secp256r1(signature),
926 MemberSignature::ZkLogin(authenticator) => Self::ZkLogin(authenticator),
927 MemberSignature::Passkey(authenticator) => Self::Passkey(authenticator),
928 })
929 }
930 }
931 }
932}
933
934#[cfg(test)]
935mod test {
936 use super::*;
937
938 #[cfg(target_arch = "wasm32")]
939 use wasm_bindgen_test::wasm_bindgen_test as test;
940
941 #[test]
946 fn partial_eq_includes_legacy_bitmap() {
947 let committee = MultisigCommittee::new(Vec::new(), 0);
948 let a = MultisigAggregatedSignature::new(committee.clone(), Vec::new(), 0);
949 let mut b = MultisigAggregatedSignature::new(committee, Vec::new(), 0);
950 assert_eq!(a, b);
951
952 b.with_legacy_bitmap(crate::Bitmap::new());
953 assert_ne!(a, b);
954 }
955
956 #[cfg(feature = "serde")]
964 #[test]
965 fn json_dual_bitmap_must_be_consistent() {
966 let mut roaring = crate::Bitmap::new();
967 roaring.insert(5);
968 let legacy_b64 = {
969 use base64ct::Encoding;
970 let mut buf = Vec::new();
971 roaring.serialize_into(&mut buf).unwrap();
972 base64ct::Base64::encode_string(&buf)
973 };
974
975 let inconsistent = format!(
977 r#"{{"signatures":[],"bitmap":1,"legacy_bitmap":"{legacy_b64}",
978 "committee":{{"members":[],"threshold":0}}}}"#
979 );
980 let err = serde_json::from_str::<MultisigAggregatedSignature>(&inconsistent)
981 .expect_err("inconsistent dual bitmap must be rejected");
982 assert!(
983 err.to_string().contains("legacy_bitmap"),
984 "unexpected error: {err}"
985 );
986
987 let consistent = format!(
990 r#"{{"signatures":[],"bitmap":{},"legacy_bitmap":"{legacy_b64}",
991 "committee":{{"members":[],"threshold":0}}}}"#,
992 1u16 << 5,
993 );
994 serde_json::from_str::<MultisigAggregatedSignature>(&consistent)
995 .expect("consistent dual bitmap must be accepted");
996 }
997
998 #[cfg(feature = "serde")]
1006 #[test]
1007 fn json_legacy_bitmap_with_zklogin_member_is_rejected() {
1008 let legacy_b64 = {
1009 use base64ct::Encoding;
1010 let mut buf = Vec::new();
1011 crate::Bitmap::new().serialize_into(&mut buf).unwrap();
1012 base64ct::Base64::encode_string(&buf)
1013 };
1014
1015 let payload = format!(
1016 r#"{{
1017 "signatures":[],
1018 "bitmap":0,
1019 "legacy_bitmap":"{legacy_b64}",
1020 "committee":{{
1021 "members":[{{
1022 "public_key":{{
1023 "scheme":"zklogin",
1024 "iss":"https://accounts.google.com",
1025 "address_seed":"7"
1026 }},
1027 "weight":1
1028 }}],
1029 "threshold":1
1030 }}
1031 }}"#
1032 );
1033 let err = serde_json::from_str::<MultisigAggregatedSignature>(&payload)
1034 .expect_err("zklogin member with legacy bitmap must be rejected");
1035 assert!(
1036 err.to_string().contains("zklogin"),
1037 "unexpected error: {err}"
1038 );
1039 }
1040}