use super::zklogin::ZkLoginAuthenticator;
use super::zklogin::ZkLoginPublicIdentifier;
use super::Ed25519PublicKey;
use super::Ed25519Signature;
use super::Secp256k1PublicKey;
use super::Secp256k1Signature;
use super::Secp256r1PublicKey;
use super::Secp256r1Signature;
use super::SignatureScheme;
pub type WeightUnit = u8;
pub type ThresholdUnit = u16;
pub type BitmapUnit = u16;
const MAX_COMMITTEE_SIZE: usize = 10;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub enum MultisigMemberPublicKey {
Ed25519(Ed25519PublicKey),
Secp256k1(Secp256k1PublicKey),
Secp256r1(Secp256r1PublicKey),
ZkLogin(ZkLoginPublicIdentifier),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub struct MultisigMember {
public_key: MultisigMemberPublicKey,
weight: WeightUnit,
}
impl MultisigMember {
pub fn new(public_key: MultisigMemberPublicKey, weight: WeightUnit) -> Self {
Self { public_key, weight }
}
pub fn public_key(&self) -> &MultisigMemberPublicKey {
&self.public_key
}
pub fn weight(&self) -> WeightUnit {
self.weight
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub struct MultisigCommittee {
#[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))]
members: Vec<MultisigMember>,
threshold: ThresholdUnit,
}
impl MultisigCommittee {
pub fn new(members: Vec<MultisigMember>, threshold: ThresholdUnit) -> Self {
Self { members, threshold }
}
pub fn members(&self) -> &[MultisigMember] {
&self.members
}
pub fn threshold(&self) -> ThresholdUnit {
self.threshold
}
pub fn scheme(&self) -> SignatureScheme {
SignatureScheme::Multisig
}
pub fn is_valid(&self) -> bool {
self.threshold != 0
&& !self.members.is_empty()
&& self.members.len() <= MAX_COMMITTEE_SIZE
&& !self.members.iter().any(|member| member.weight == 0)
&& self
.members
.iter()
.map(|member| member.weight as ThresholdUnit)
.sum::<ThresholdUnit>()
>= self.threshold
&& !self.members.iter().enumerate().any(|(i, member)| {
self.members
.iter()
.skip(i + 1)
.any(|m| member.public_key == m.public_key)
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub struct MultisigAggregatedSignature {
#[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))]
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
#[cfg_attr(
feature = "schemars",
schemars(
skip_serializing_if = "Option::is_none",
with = "Option<crate::_schemars::Base64>",
)
)]
#[cfg_attr(test, strategy(proptest::strategy::Just(None)))]
legacy_bitmap: Option<roaring::RoaringBitmap>,
committee: MultisigCommittee,
}
impl MultisigAggregatedSignature {
pub fn new(
committee: MultisigCommittee,
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
) -> Self {
Self {
signatures,
bitmap,
legacy_bitmap: None,
committee,
}
}
pub fn signatures(&self) -> &[MultisigMemberSignature] {
&self.signatures
}
pub fn bitmap(&self) -> BitmapUnit {
self.bitmap
}
pub fn legacy_bitmap(&self) -> Option<&roaring::RoaringBitmap> {
self.legacy_bitmap.as_ref()
}
pub fn with_legacy_bitmap(&mut self, legacy_bitmap: roaring::RoaringBitmap) {
self.legacy_bitmap = Some(legacy_bitmap);
}
pub fn committee(&self) -> &MultisigCommittee {
&self.committee
}
}
impl PartialEq for MultisigAggregatedSignature {
fn eq(&self, other: &Self) -> bool {
self.bitmap == other.bitmap
&& self.signatures == other.signatures
&& self.committee == other.committee
}
}
impl Eq for MultisigAggregatedSignature {}
#[cfg(feature = "serde")]
fn roaring_bitmap_to_u16(roaring: &roaring::RoaringBitmap) -> Result<BitmapUnit, &'static str> {
let mut val = 0;
for i in roaring.iter() {
if i >= MAX_COMMITTEE_SIZE as u32 {
return Err("invalid bitmap");
}
val |= 1 << i as u8;
}
Ok(val)
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub enum MultisigMemberSignature {
Ed25519(Ed25519Signature),
Secp256k1(Secp256k1Signature),
Secp256r1(Secp256r1Signature),
ZkLogin(Box<ZkLoginAuthenticator>),
}
#[cfg(feature = "serde")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
mod serialization {
use super::*;
use crate::types::crypto::Base64Array33;
use crate::types::crypto::Base64Array34;
use crate::types::Ed25519PublicKey;
use crate::types::Secp256k1PublicKey;
use crate::types::Secp256r1PublicKey;
use crate::types::SignatureScheme;
use base64ct::Base64;
use base64ct::Encoding;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde_with::Bytes;
use serde_with::DeserializeAs;
use serde_with::SerializeAs;
use std::borrow::Cow;
pub struct Base64MultisigMemberPublicKey;
impl SerializeAs<MultisigMemberPublicKey> for Base64MultisigMemberPublicKey {
fn serialize_as<S>(
source: &MultisigMemberPublicKey,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match source {
MultisigMemberPublicKey::Ed25519(public_key) => {
let mut buf = [0; 1 + Ed25519PublicKey::LENGTH];
buf[0] = SignatureScheme::Ed25519 as u8;
buf[1..].copy_from_slice(public_key.as_ref());
Base64Array33::serialize_as(&buf, serializer)
}
MultisigMemberPublicKey::Secp256k1(public_key) => {
let mut buf = [0; 1 + Secp256k1PublicKey::LENGTH];
buf[0] = SignatureScheme::Secp256k1 as u8;
buf[1..].copy_from_slice(public_key.as_ref());
Base64Array34::serialize_as(&buf, serializer)
}
MultisigMemberPublicKey::Secp256r1(public_key) => {
let mut buf = [0; 1 + Secp256r1PublicKey::LENGTH];
buf[0] = SignatureScheme::Secp256r1 as u8;
buf[1..].copy_from_slice(public_key.as_ref());
Base64Array34::serialize_as(&buf, serializer)
}
MultisigMemberPublicKey::ZkLogin(_) => Err(serde::ser::Error::custom(
"zklogin not supported in legacy multisig",
)),
}
}
}
impl<'de> DeserializeAs<'de, MultisigMemberPublicKey> for Base64MultisigMemberPublicKey {
fn deserialize_as<D>(deserializer: D) -> Result<MultisigMemberPublicKey, D::Error>
where
D: Deserializer<'de>,
{
let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
let flag = SignatureScheme::from_byte(
*bytes
.first()
.ok_or_else(|| serde::de::Error::custom("missing signature scheme falg"))?,
)
.map_err(serde::de::Error::custom)?;
let public_key_bytes = &bytes[1..];
match flag {
SignatureScheme::Ed25519 => {
let public_key = Ed25519PublicKey::from_bytes(public_key_bytes)
.map_err(serde::de::Error::custom)?;
Ok(MultisigMemberPublicKey::Ed25519(public_key))
}
SignatureScheme::Secp256k1 => {
let public_key = Secp256k1PublicKey::from_bytes(public_key_bytes)
.map_err(serde::de::Error::custom)?;
Ok(MultisigMemberPublicKey::Secp256k1(public_key))
}
SignatureScheme::Secp256r1 => {
let public_key = Secp256r1PublicKey::from_bytes(public_key_bytes)
.map_err(serde::de::Error::custom)?;
Ok(MultisigMemberPublicKey::Secp256r1(public_key))
}
SignatureScheme::Multisig
| SignatureScheme::Bls12381
| SignatureScheme::ZkLogin
| SignatureScheme::Passkey => {
Err(serde::de::Error::custom("invalid public key type"))
}
}
}
}
pub struct LegacyMultisigMember;
impl SerializeAs<MultisigMember> for LegacyMultisigMember {
fn serialize_as<S>(source: &MultisigMember, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(serde_derive::Serialize)]
struct LegacyMember<'a> {
#[serde(with = "::serde_with::As::<Base64MultisigMemberPublicKey>")]
public_key: &'a MultisigMemberPublicKey,
weight: WeightUnit,
}
let legacy = LegacyMember {
public_key: &source.public_key,
weight: source.weight,
};
legacy.serialize(serializer)
}
}
impl<'de> DeserializeAs<'de, MultisigMember> for LegacyMultisigMember {
fn deserialize_as<D>(deserializer: D) -> Result<MultisigMember, D::Error>
where
D: Deserializer<'de>,
{
#[derive(serde_derive::Deserialize)]
struct LegacyMember {
#[serde(with = "::serde_with::As::<Base64MultisigMemberPublicKey>")]
public_key: MultisigMemberPublicKey,
weight: WeightUnit,
}
let legacy = LegacyMember::deserialize(deserializer)?;
Ok(MultisigMember {
public_key: legacy.public_key,
weight: legacy.weight,
})
}
}
#[derive(serde_derive::Deserialize)]
pub struct Multisig {
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
committee: MultisigCommittee,
}
#[derive(serde_derive::Serialize)]
pub struct MultisigRef<'a> {
signatures: &'a [MultisigMemberSignature],
bitmap: BitmapUnit,
committee: &'a MultisigCommittee,
}
#[derive(serde_derive::Deserialize)]
pub struct LegacyMultisig {
signatures: Vec<MultisigMemberSignature>,
#[serde(with = "::serde_with::As::<crate::_serde::BinaryRoaringBitmap>")]
bitmap: roaring::RoaringBitmap,
committee: LegacyMultisigCommittee,
}
#[derive(serde_derive::Serialize)]
pub struct LegacyMultisigRef<'a> {
signatures: &'a [MultisigMemberSignature],
#[serde(with = "::serde_with::As::<crate::_serde::BinaryRoaringBitmap>")]
bitmap: &'a roaring::RoaringBitmap,
committee: LegacyMultisigCommitteeRef<'a>,
}
#[derive(serde_derive::Deserialize)]
struct LegacyMultisigCommittee {
#[serde(with = "::serde_with::As::<Vec<LegacyMultisigMember>>")]
members: Vec<MultisigMember>,
threshold: ThresholdUnit,
}
#[derive(serde_derive::Serialize)]
struct LegacyMultisigCommitteeRef<'a> {
#[serde(with = "::serde_with::As::<&[LegacyMultisigMember]>")]
members: &'a [MultisigMember],
threshold: ThresholdUnit,
}
#[derive(serde_derive::Deserialize)]
struct ReadableMultisigAggregatedSignature {
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
#[serde(default)]
#[serde(with = "::serde_with::As::<Option<crate::_serde::Base64RoaringBitmap>>")]
legacy_bitmap: Option<roaring::RoaringBitmap>,
committee: MultisigCommittee,
}
#[derive(serde_derive::Serialize)]
struct ReadableMultisigAggregatedSignatureRef<'a> {
signatures: &'a [MultisigMemberSignature],
bitmap: BitmapUnit,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "::serde_with::As::<Option<crate::_serde::Base64RoaringBitmap>>")]
legacy_bitmap: &'a Option<roaring::RoaringBitmap>,
committee: &'a MultisigCommittee,
}
impl Serialize for MultisigAggregatedSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let readable = ReadableMultisigAggregatedSignatureRef {
signatures: &self.signatures,
bitmap: self.bitmap,
legacy_bitmap: &self.legacy_bitmap,
committee: &self.committee,
};
readable.serialize(serializer)
} else {
let bytes = self.to_bytes();
serializer.serialize_bytes(&bytes)
}
}
}
impl<'de> Deserialize<'de> for MultisigAggregatedSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let readable = ReadableMultisigAggregatedSignature::deserialize(deserializer)?;
Ok(Self {
signatures: readable.signatures,
bitmap: readable.bitmap,
legacy_bitmap: readable.legacy_bitmap,
committee: readable.committee,
})
} else {
let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?;
Self::from_serialized_bytes(bytes)
}
}
}
impl MultisigAggregatedSignature {
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.push(SignatureScheme::Multisig as u8);
if let Some(bitmap) = &self.legacy_bitmap {
let legacy = LegacyMultisigRef {
signatures: &self.signatures,
bitmap,
committee: LegacyMultisigCommitteeRef {
members: &self.committee.members,
threshold: self.committee.threshold,
},
};
bcs::serialize_into(&mut buf, &legacy).expect("serialization cannot fail");
} else {
let multisig = MultisigRef {
signatures: &self.signatures,
bitmap: self.bitmap,
committee: &self.committee,
};
bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail");
}
buf
}
pub(crate) fn from_serialized_bytes<T: AsRef<[u8]>, E: serde::de::Error>(
bytes: T,
) -> Result<Self, E> {
let bytes = bytes.as_ref();
let flag = SignatureScheme::from_byte(
*bytes
.first()
.ok_or_else(|| serde::de::Error::custom("missing signature scheme falg"))?,
)
.map_err(serde::de::Error::custom)?;
if flag != SignatureScheme::Multisig {
return Err(serde::de::Error::custom("invalid multisig flag"));
}
let bcs_bytes = &bytes[1..];
if let Ok(multisig) = bcs::from_bytes::<Multisig>(bcs_bytes) {
Ok(Self {
signatures: multisig.signatures,
bitmap: multisig.bitmap,
legacy_bitmap: None,
committee: multisig.committee,
})
} else if let Ok(legacy) = bcs::from_bytes::<LegacyMultisig>(bcs_bytes) {
Ok(Self {
signatures: legacy.signatures,
bitmap: roaring_bitmap_to_u16(&legacy.bitmap)
.map_err(serde::de::Error::custom)?,
legacy_bitmap: Some(legacy.bitmap),
committee: MultisigCommittee {
members: legacy.committee.members,
threshold: legacy.committee.threshold,
},
})
} else {
Err(serde::de::Error::custom("invalid multisig"))
}
}
}
#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
enum MemberPublicKey {
Ed25519(Ed25519PublicKey),
Secp256k1(Secp256k1PublicKey),
Secp256r1(Secp256r1PublicKey),
ZkLogin(ZkLoginPublicIdentifier),
}
#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[serde(tag = "scheme", rename_all = "lowercase")]
#[serde(rename = "MultisigMemberPublicKey")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
enum ReadableMemberPublicKey {
Ed25519 { public_key: Ed25519PublicKey },
Secp256k1 { public_key: Secp256k1PublicKey },
Secp256r1 { public_key: Secp256r1PublicKey },
ZkLogin(ZkLoginPublicIdentifier),
}
#[cfg(feature = "schemars")]
impl schemars::JsonSchema for MultisigMemberPublicKey {
fn schema_name() -> String {
ReadableMemberPublicKey::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
ReadableMemberPublicKey::json_schema(gen)
}
}
impl Serialize for MultisigMemberPublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let readable = match self {
MultisigMemberPublicKey::Ed25519(public_key) => {
ReadableMemberPublicKey::Ed25519 {
public_key: *public_key,
}
}
MultisigMemberPublicKey::Secp256k1(public_key) => {
ReadableMemberPublicKey::Secp256k1 {
public_key: *public_key,
}
}
MultisigMemberPublicKey::Secp256r1(public_key) => {
ReadableMemberPublicKey::Secp256r1 {
public_key: *public_key,
}
}
MultisigMemberPublicKey::ZkLogin(public_id) => {
ReadableMemberPublicKey::ZkLogin(public_id.clone())
}
};
readable.serialize(serializer)
} else {
let binary = match self {
MultisigMemberPublicKey::Ed25519(public_key) => {
MemberPublicKey::Ed25519(*public_key)
}
MultisigMemberPublicKey::Secp256k1(public_key) => {
MemberPublicKey::Secp256k1(*public_key)
}
MultisigMemberPublicKey::Secp256r1(public_key) => {
MemberPublicKey::Secp256r1(*public_key)
}
MultisigMemberPublicKey::ZkLogin(public_id) => {
MemberPublicKey::ZkLogin(public_id.clone())
}
};
binary.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for MultisigMemberPublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let readable = ReadableMemberPublicKey::deserialize(deserializer)?;
Ok(match readable {
ReadableMemberPublicKey::Ed25519 { public_key } => Self::Ed25519(public_key),
ReadableMemberPublicKey::Secp256k1 { public_key } => {
Self::Secp256k1(public_key)
}
ReadableMemberPublicKey::Secp256r1 { public_key } => {
Self::Secp256r1(public_key)
}
ReadableMemberPublicKey::ZkLogin(public_id) => Self::ZkLogin(public_id),
})
} else {
let binary = MemberPublicKey::deserialize(deserializer)?;
Ok(match binary {
MemberPublicKey::Ed25519(public_key) => Self::Ed25519(public_key),
MemberPublicKey::Secp256k1(public_key) => Self::Secp256k1(public_key),
MemberPublicKey::Secp256r1(public_key) => Self::Secp256r1(public_key),
MemberPublicKey::ZkLogin(public_id) => Self::ZkLogin(public_id),
})
}
}
}
#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
enum MemberSignature {
Ed25519(Ed25519Signature),
Secp256k1(Secp256k1Signature),
Secp256r1(Secp256r1Signature),
ZkLogin(Box<ZkLoginAuthenticator>),
}
#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[serde(tag = "scheme", rename_all = "lowercase")]
#[serde(rename = "MultisigMemberSignature")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
enum ReadableMemberSignature {
Ed25519 { signature: Ed25519Signature },
Secp256k1 { signature: Secp256k1Signature },
Secp256r1 { signature: Secp256r1Signature },
ZkLogin(Box<ZkLoginAuthenticator>),
}
#[cfg(feature = "schemars")]
impl schemars::JsonSchema for MultisigMemberSignature {
fn schema_name() -> String {
ReadableMemberSignature::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
ReadableMemberSignature::json_schema(gen)
}
}
impl Serialize for MultisigMemberSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let readable = match self {
MultisigMemberSignature::Ed25519(signature) => {
ReadableMemberSignature::Ed25519 {
signature: *signature,
}
}
MultisigMemberSignature::Secp256k1(signature) => {
ReadableMemberSignature::Secp256k1 {
signature: *signature,
}
}
MultisigMemberSignature::Secp256r1(signature) => {
ReadableMemberSignature::Secp256r1 {
signature: *signature,
}
}
MultisigMemberSignature::ZkLogin(authenticator) => {
ReadableMemberSignature::ZkLogin(authenticator.clone())
}
};
readable.serialize(serializer)
} else {
let binary = match self {
MultisigMemberSignature::Ed25519(signature) => {
MemberSignature::Ed25519(*signature)
}
MultisigMemberSignature::Secp256k1(signature) => {
MemberSignature::Secp256k1(*signature)
}
MultisigMemberSignature::Secp256r1(signature) => {
MemberSignature::Secp256r1(*signature)
}
MultisigMemberSignature::ZkLogin(authenticator) => {
MemberSignature::ZkLogin(authenticator.clone())
}
};
binary.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for MultisigMemberSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let readable = ReadableMemberSignature::deserialize(deserializer)?;
Ok(match readable {
ReadableMemberSignature::Ed25519 { signature } => Self::Ed25519(signature),
ReadableMemberSignature::Secp256k1 { signature } => Self::Secp256k1(signature),
ReadableMemberSignature::Secp256r1 { signature } => Self::Secp256r1(signature),
ReadableMemberSignature::ZkLogin(authenticator) => Self::ZkLogin(authenticator),
})
} else {
let binary = MemberSignature::deserialize(deserializer)?;
Ok(match binary {
MemberSignature::Ed25519(signature) => Self::Ed25519(signature),
MemberSignature::Secp256k1(signature) => Self::Secp256k1(signature),
MemberSignature::Secp256r1(signature) => Self::Secp256r1(signature),
MemberSignature::ZkLogin(authenticator) => Self::ZkLogin(authenticator),
})
}
}
}
}