Skip to content

Commit

Permalink
chore(cleanup): Remove legacy MultiSig (#8)
Browse files Browse the repository at this point in the history
* Add .idea/ to .gitignore

* Remove legacy MultiSig

* Remove test of legacy fixture
  • Loading branch information
piotrm50 authored Oct 22, 2024
1 parent 014c888 commit 46d46d6
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 252 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
.idea
240 changes: 8 additions & 232 deletions crates/iota-rust-sdk/src/types/crypto/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ pub type WeightUnit = u8;
pub type ThresholdUnit = u16;
pub type BitmapUnit = u16;

#[cfg(feature = "serde")]
const MAX_COMMITTEE_SIZE: usize = 10;
// TODO validate sigs
// const MAX_BITMAP_VALUE: BitmapUnit = 0b1111111111;

Expand Down Expand Up @@ -86,17 +84,6 @@ pub struct MultisigAggregatedSignature {
/// A bitmap that indicates the position of which public key the signature
/// should be authenticated with.
bitmap: BitmapUnit,
/// Legacy encoding for the bitmap.
// TODO implement a strategy for legacy bitmap
#[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>,
/// The public key encoded with each public key with its signature scheme
/// used along with the corresponding weight.
committee: MultisigCommittee,
Expand All @@ -111,19 +98,13 @@ impl MultisigAggregatedSignature {
self.bitmap
}

pub fn legacy_bitmap(&self) -> Option<&roaring::RoaringBitmap> {
self.legacy_bitmap.as_ref()
}

pub fn committee(&self) -> &MultisigCommittee {
&self.committee
}
}

impl PartialEq for MultisigAggregatedSignature {
fn eq(&self, other: &Self) -> bool {
// Skip comparing the legacy bitmap since we always convert to the new bitmap
// form
self.bitmap == other.bitmap
&& self.signatures == other.signatures
&& self.committee == other.committee
Expand All @@ -132,19 +113,6 @@ impl PartialEq for MultisigAggregatedSignature {

impl Eq for MultisigAggregatedSignature {}

/// Convert a roaring bitmap to plain bitmap.
#[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))]
#[allow(clippy::large_enum_variant)]
Expand All @@ -160,135 +128,11 @@ pub enum MultisigMemberSignature {
mod serialization {
use std::borrow::Cow;

use base64ct::{Base64, Encoding};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{Bytes, DeserializeAs, SerializeAs};
use serde_with::{Bytes, DeserializeAs};

use super::*;
use crate::types::{
Ed25519PublicKey, Secp256k1PublicKey, Secp256r1PublicKey, SignatureScheme,
crypto::{Base64Array33, Base64Array34},
};

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 flag"))?,
)
.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,
})
}
}
use crate::types::{Ed25519PublicKey, Secp256k1PublicKey, Secp256r1PublicKey, SignatureScheme};

#[derive(serde_derive::Deserialize)]
pub struct Multisig {
Expand All @@ -304,53 +148,17 @@ mod serialization {
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,
}

Expand All @@ -363,33 +171,19 @@ mod serialization {
let readable = ReadableMultisigAggregatedSignatureRef {
signatures: &self.signatures,
bitmap: self.bitmap,
legacy_bitmap: &self.legacy_bitmap,
committee: &self.committee,
};
readable.serialize(serializer)
} else {
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");
}
let multisig = MultisigRef {
signatures: &self.signatures,
bitmap: self.bitmap,
committee: &self.committee,
};
bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail");
serializer.serialize_bytes(&buf)
}
}
Expand All @@ -405,7 +199,6 @@ mod serialization {
Ok(Self {
signatures: readable.signatures,
bitmap: readable.bitmap,
legacy_bitmap: readable.legacy_bitmap,
committee: readable.committee,
})
} else {
Expand All @@ -431,29 +224,12 @@ mod serialization {
}
let bcs_bytes = &bytes[1..];

// Unfortunately we have no information in the serialized form of a Multisig to
// be able to determine if its a Legacy format or the new standard
// format so we just need to try each.
//
// We'll start with the newer format as that should be more prevalent.
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"))
}
Expand Down
20 changes: 0 additions & 20 deletions crates/iota-rust-sdk/src/types/crypto/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,26 +580,6 @@ mod serialization {
}
}

#[test]
fn legacy_multisig_fixtures() {
const FIXTURE1: &str = "rgIDAgAnwUSyrALP8m0eEPZE6aPggBELk72n1u3LU+i4nx5kqzhahcICbskEYzHJrbarvFr/RQITgDMoorqpDhN8dgsKATyrN3CD8g37D60dYiGW6sOBqIcf3E1mdMsKvX2pbOZsYQv8VNL+2Jz3vnMXcwEZF32PplKjcnmyUGRhV11M7n4UOjAAAAEAAAAAAAEAEAAAAAAAAQADLEFBMTlxeldNamEycVR2b0FTYWRiQjBObFZiRUtOb0ladTJnUGNGY1RTZGQxATBBUUlPRjgxWk9lUnJHV1pCbG96WFdaRUxvbGQrSi9wei9lT0hiYm0reGJ6ckt3PT0BMEFnTkgrNjhqOERpcnhNTUlvbkVSZWlwTS82N2R2Ri80SEhVWHZHeDBwKzIwTUE9PQECAA==";

const FIXTURE2: &str = "8QIDAwDYAAra4KQGp2Oq1TCOgWfH8IxC4UA5wJB/NqOcNmMh54Y5d5pnVQfTlqgq4J17a8+W+y3+jk9h4YMB9LzPDYcLAaJJBH+WLPfPaQ7T3Cv8nqpZ1TbPrT8E61FrSgeIbN4OTJeijjguv1pd3ImvTeo4AMYZczf5OH6+5yBaur7R6YACiooT5J36agjUk0TpVcTKMGwykIwD7NBkZ0gbinHxuVJwdi1tSbqhMpqvNgP+CFO6F7FSTe+xiHh0MDOKyYQItxY6MAAAAQAAAAAAAgAQAAAAAAABAAIAAyxBQTE5cXpXTWphMnFUdm9BU2FkYkIwTmxWYkVLTm9JWnUyZ1BjRmNUU2RkMQEwQVFJT0Y4MVpPZVJyR1daQmxvelhXWkVMb2xkK0ovcHovZU9IYmJtK3hienJLdz09ATBBZ05IKzY4ajhEaXJ4TU1Jb25FUmVpcE0vNjdkdkYvNEhIVVh2R3gwcCsyME1BPT0BAgA=";

for fixture in [FIXTURE1, FIXTURE2] {
let bcs = Base64::decode_vec(fixture).unwrap();

let sig: UserSignature = bcs::from_bytes(&bcs).unwrap();
assert_eq!(SignatureScheme::Multisig, sig.scheme());
let bytes = bcs::to_bytes(&sig).unwrap();
assert_eq!(bcs, bytes);

let json = serde_json::to_string_pretty(&sig).unwrap();
println!("{json}");
assert_eq!(sig, serde_json::from_str(&json).unwrap());
}
}

#[test]
fn multisig_fixtures() {
const FIXTURE1: &str = "sgIDAwCTLgVngjC4yeuvpAGKVkgcvIKVFUJnL1r6oFZScQVE5DNIz6kfxAGDRcVUczE9CUb7/sN/EuFJ8ot86Sdb8pAFASoQ91stRHXdW5dLy0BQ6v+7XWptawy2ItMyPk508p+PHdtZcm2aKl3lZGIvXe6MPY73E+1Hakv/xJbTYsw5SPMC5dx3gBwxds2GV12c7VUSqkyXamliSF1W/QBMufqrlmdIOZ1ox9gbsvIPtXYahfvKm8ozA7rsZWwRv8atsnyfYgcAAwANfas1jI2tqk76AEmnWwdDZVWxCjaCGbtoD3BXE0nXdQEBAg4XzVk55GsZZkGWjNdZkQuiV34n+nP944dtub7FvOsrAQIDR/uvI/A4q8TDCKJxEXoqTP+u3bxf+Bx1F7xsdKfttDABAgA=";
Expand Down

0 comments on commit 46d46d6

Please sign in to comment.