1use crate::{
5 crypto::{CompressedSignature, SignatureScheme},
6 digests::ZKLoginInputsDigest,
7 error::SuiErrorKind,
8 multisig::{MultiSig, MultiSigPublicKey},
9 signature::{AuthenticatorTrait, GenericSignature, VerifyParams},
10 signature_verification::VerifiedDigestCache,
11 sui_serde::SuiBitmap,
12};
13pub use enum_dispatch::enum_dispatch;
14use fastcrypto::{
15 encoding::Base64,
16 error::FastCryptoError,
17 traits::{EncodeDecodeBase64, ToFromBytes},
18};
19use once_cell::sync::OnceCell;
20use roaring::RoaringBitmap;
21use schemars::JsonSchema;
22use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeSeq};
23use serde_with::serde_as;
24use shared_crypto::intent::IntentMessage;
25use std::{
26 hash::{Hash, Hasher},
27 sync::Arc,
28};
29
30use crate::{
31 base_types::{EpochId, SuiAddress},
32 crypto::PublicKey,
33 error::SuiError,
34};
35
36pub type WeightUnit = u8;
37pub type ThresholdUnit = u16;
38pub const MAX_SIGNER_IN_MULTISIG: usize = 10;
39
40#[serde_as]
43#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
44pub struct MultiSigLegacy {
45 sigs: Vec<CompressedSignature>,
47 #[schemars(with = "Base64")]
49 #[serde_as(as = "SuiBitmap")]
50 bitmap: RoaringBitmap,
51 multisig_pk: MultiSigPublicKeyLegacy,
53 #[serde(skip)]
55 bytes: OnceCell<Vec<u8>>,
56}
57
58impl AsRef<[u8]> for MultiSigLegacy {
62 fn as_ref(&self) -> &[u8] {
63 self.bytes
64 .get_or_try_init::<_, eyre::Report>(|| {
65 let as_bytes = bcs::to_bytes(self).expect("BCS serialization should not fail");
66 let mut bytes = Vec::with_capacity(1 + as_bytes.len());
67 bytes.push(SignatureScheme::MultiSig.flag());
68 bytes.extend_from_slice(as_bytes.as_slice());
69 Ok(bytes)
70 })
71 .expect("OnceCell invariant violated")
72 }
73}
74
75impl PartialEq for MultiSigLegacy {
77 fn eq(&self, other: &Self) -> bool {
78 self.sigs == other.sigs
79 && self.bitmap == other.bitmap
80 && self.multisig_pk == other.multisig_pk
81 }
82}
83
84impl Eq for MultiSigLegacy {}
86
87impl Hash for MultiSigLegacy {
89 fn hash<H: Hasher>(&self, state: &mut H) {
90 self.as_ref().hash(state);
91 }
92}
93
94impl AuthenticatorTrait for MultiSigLegacy {
95 fn verify_user_authenticator_epoch(
96 &self,
97 epoch_id: EpochId,
98 max_epoch_upper_bound_delta: Option<u64>,
99 ) -> Result<(), SuiError> {
100 let multisig: MultiSig =
101 self.clone()
102 .try_into()
103 .map_err(|_| SuiErrorKind::InvalidSignature {
104 error: "Invalid legacy multisig".to_string(),
105 })?;
106 multisig.verify_user_authenticator_epoch(epoch_id, max_epoch_upper_bound_delta)
107 }
108
109 fn verify_claims<T>(
110 &self,
111 value: &IntentMessage<T>,
112 author: SuiAddress,
113 aux_verify_data: &VerifyParams,
114 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
115 ) -> Result<(), SuiError>
116 where
117 T: Serialize,
118 {
119 let multisig: MultiSig =
120 self.clone()
121 .try_into()
122 .map_err(|_| SuiErrorKind::InvalidSignature {
123 error: "Invalid legacy multisig".to_string(),
124 })?;
125 multisig.verify_claims(value, author, aux_verify_data, zklogin_inputs_cache)
126 }
127}
128
129impl TryFrom<MultiSigLegacy> for MultiSig {
130 type Error = FastCryptoError;
131
132 fn try_from(multisig: MultiSigLegacy) -> Result<Self, Self::Error> {
133 MultiSig::insecure_new(
134 multisig.clone().sigs,
135 bitmap_to_u16(multisig.clone().bitmap)?,
136 multisig.multisig_pk.try_into()?,
137 )
138 .init_and_validate()
139 }
140}
141
142impl TryFrom<MultiSigPublicKeyLegacy> for MultiSigPublicKey {
143 type Error = FastCryptoError;
144 fn try_from(multisig: MultiSigPublicKeyLegacy) -> Result<Self, Self::Error> {
145 let multisig_pk_legacy =
146 MultiSigPublicKey::insecure_new(multisig.pk_map, multisig.threshold).validate()?;
147 Ok(multisig_pk_legacy)
148 }
149}
150
151pub fn bitmap_to_u16(roaring: RoaringBitmap) -> Result<u16, FastCryptoError> {
153 let mut val = 0;
154 for i in roaring {
155 if i >= 10 {
156 return Err(FastCryptoError::InvalidInput);
157 }
158 val |= 1 << i as u8;
159 }
160 Ok(val)
161}
162
163impl MultiSigLegacy {
164 pub fn combine(
166 full_sigs: Vec<GenericSignature>,
167 multisig_pk: MultiSigPublicKeyLegacy,
168 ) -> Result<Self, SuiError> {
169 multisig_pk
170 .validate()
171 .map_err(|_| SuiErrorKind::InvalidSignature {
172 error: "Invalid multisig public key".to_string(),
173 })?;
174
175 if full_sigs.len() > multisig_pk.pk_map.len() || full_sigs.is_empty() {
176 return Err(SuiErrorKind::InvalidSignature {
177 error: "Invalid number of signatures".to_string(),
178 }
179 .into());
180 }
181 let mut bitmap = RoaringBitmap::new();
182 let mut sigs = Vec::with_capacity(full_sigs.len());
183 for s in full_sigs {
184 let pk = s.to_public_key()?;
185 let inserted = bitmap.insert(multisig_pk.get_index(&pk).ok_or(
186 SuiErrorKind::IncorrectSigner {
187 error: format!("pk does not exist: {:?}", pk),
188 },
189 )?);
190 if !inserted {
191 return Err(SuiErrorKind::InvalidSignature {
192 error: "Duplicate signature".to_string(),
193 }
194 .into());
195 }
196 sigs.push(s.to_compressed()?);
197 }
198 Ok(MultiSigLegacy {
199 sigs,
200 bitmap,
201 multisig_pk,
202 bytes: OnceCell::new(),
203 })
204 }
205
206 pub fn validate(&self) -> Result<(), FastCryptoError> {
207 if bitmap_to_u16(self.bitmap.clone()).is_err()
208 || self.sigs.len() > self.multisig_pk.pk_map.len()
209 || self.sigs.is_empty()
210 {
211 return Err(FastCryptoError::InvalidInput);
212 }
213 self.multisig_pk.validate()?;
214 Ok(())
215 }
216
217 pub fn get_pk(&self) -> &MultiSigPublicKeyLegacy {
218 &self.multisig_pk
219 }
220
221 pub fn get_sigs(&self) -> &[CompressedSignature] {
222 &self.sigs
223 }
224
225 pub fn get_bitmap(&self) -> &RoaringBitmap {
226 &self.bitmap
227 }
228}
229
230impl ToFromBytes for MultiSigLegacy {
231 fn from_bytes(bytes: &[u8]) -> Result<MultiSigLegacy, FastCryptoError> {
232 if bytes.first().ok_or(FastCryptoError::InvalidInput)? != &SignatureScheme::MultiSig.flag()
234 {
235 return Err(FastCryptoError::InvalidInput);
236 }
237 let multisig: MultiSigLegacy =
238 bcs::from_bytes(&bytes[1..]).map_err(|_| FastCryptoError::InvalidInput)?;
239 multisig.validate()?;
240 Ok(multisig)
241 }
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
247pub struct MultiSigPublicKeyLegacy {
248 #[serde(serialize_with = "serialize_pk_map")]
250 #[serde(deserialize_with = "deserialize_pk_map")]
251 pk_map: Vec<(PublicKey, WeightUnit)>,
252 threshold: ThresholdUnit,
254}
255
256fn serialize_pk_map<S>(pk_map: &[(PublicKey, WeightUnit)], serializer: S) -> Result<S::Ok, S::Error>
258where
259 S: Serializer,
260{
261 let pk_weight_arr: Vec<(String, WeightUnit)> = pk_map
262 .iter()
263 .map(|(pk, w)| (pk.encode_base64(), *w))
264 .collect();
265
266 let mut seq = serializer.serialize_seq(Some(pk_weight_arr.len()))?;
267 for (pk_string, w) in pk_weight_arr {
268 seq.serialize_element(&(pk_string, w))?;
269 }
270 seq.end()
271}
272
273fn deserialize_pk_map<'de, D>(deserializer: D) -> Result<Vec<(PublicKey, WeightUnit)>, D::Error>
275where
276 D: Deserializer<'de>,
277{
278 use serde::de::Error;
279 let pk_weight_arr: Vec<(String, WeightUnit)> = Vec::deserialize(deserializer)?;
280 pk_weight_arr
281 .into_iter()
282 .map(|(s, w)| {
283 let pk = <PublicKey as EncodeDecodeBase64>::decode_base64(&s)
284 .map_err(|e| Error::custom(e.to_string()))?;
285 Ok((pk, w))
286 })
287 .collect()
288}
289
290impl MultiSigPublicKeyLegacy {
291 pub fn new(
292 pks: Vec<PublicKey>,
293 weights: Vec<WeightUnit>,
294 threshold: ThresholdUnit,
295 ) -> Result<Self, SuiError> {
296 if pks.is_empty()
297 || weights.is_empty()
298 || threshold == 0
299 || pks.len() != weights.len()
300 || pks.len() > MAX_SIGNER_IN_MULTISIG
301 || weights.contains(&0)
302 || weights
303 .iter()
304 .map(|w| *w as ThresholdUnit)
305 .sum::<ThresholdUnit>()
306 < threshold
307 {
308 return Err(SuiErrorKind::InvalidSignature {
309 error: "Invalid multisig public key construction".to_string(),
310 }
311 .into());
312 }
313 Ok(MultiSigPublicKeyLegacy {
314 pk_map: pks.into_iter().zip(weights).collect(),
315 threshold,
316 })
317 }
318
319 pub fn get_index(&self, pk: &PublicKey) -> Option<u32> {
320 self.pk_map
321 .iter()
322 .position(|x| &x.0 == pk)
323 .map(|x| x as u32)
324 }
325
326 pub fn threshold(&self) -> &ThresholdUnit {
327 &self.threshold
328 }
329
330 pub fn pubkeys(&self) -> &[(PublicKey, WeightUnit)] {
331 &self.pk_map
332 }
333
334 pub fn validate(&self) -> Result<Self, FastCryptoError> {
335 let multisig: MultiSigPublicKey = self.clone().try_into()?;
336 multisig.validate()?;
337 Ok(self.clone())
338 }
339}