Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(cleanup): Remove legacy MultiSig #8

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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