sui_types/
crypto.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3use crate::base_types::{AuthorityName, ConciseableName, SuiAddress};
4use crate::committee::CommitteeTrait;
5use crate::committee::{Committee, EpochId, StakeUnit};
6use crate::error::{SuiError, SuiErrorKind, SuiResult};
7use crate::signature::GenericSignature;
8use crate::sui_serde::{Readable, SuiBitmap};
9use anyhow::{Error, anyhow};
10use derive_more::{AsMut, AsRef, From};
11pub use enum_dispatch::enum_dispatch;
12use eyre::eyre;
13use fastcrypto::bls12381::min_sig::{
14    BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair,
15    BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature,
16};
17use fastcrypto::ed25519::{
18    Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, Ed25519Signature,
19    Ed25519SignatureAsBytes,
20};
21use fastcrypto::encoding::{Base64, Bech32, Encoding, Hex};
22use fastcrypto::error::{FastCryptoError, FastCryptoResult};
23use fastcrypto::hash::{Blake2b256, HashFunction};
24use fastcrypto::secp256k1::{
25    Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature,
26    Secp256k1SignatureAsBytes,
27};
28use fastcrypto::secp256r1::{
29    Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature,
30    Secp256r1SignatureAsBytes,
31};
32pub use fastcrypto::traits::KeyPair as KeypairTraits;
33pub use fastcrypto::traits::Signer;
34pub use fastcrypto::traits::{
35    AggregateAuthenticator, Authenticator, EncodeDecodeBase64, SigningKey, ToFromBytes,
36    VerifyingKey,
37};
38use fastcrypto_zkp::bn254::zk_login::ZkLoginInputs;
39use fastcrypto_zkp::zk_login_utils::Bn254FrElement;
40use rand::SeedableRng;
41use rand::rngs::{OsRng, StdRng};
42use roaring::RoaringBitmap;
43use schemars::JsonSchema;
44use serde::ser::Serializer;
45use serde::{Deserialize, Deserializer, Serialize};
46use serde_with::{Bytes, serde_as};
47use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
48use std::collections::BTreeMap;
49use std::fmt::Debug;
50use std::fmt::{self, Display, Formatter};
51use std::hash::{Hash, Hasher};
52use std::str::FromStr;
53use strum::EnumString;
54use tracing::{instrument, warn};
55
56#[cfg(test)]
57#[path = "unit_tests/crypto_tests.rs"]
58mod crypto_tests;
59
60#[cfg(test)]
61#[path = "unit_tests/intent_tests.rs"]
62mod intent_tests;
63
64// Authority Objects
65pub type AuthorityKeyPair = BLS12381KeyPair;
66pub type AuthorityPublicKey = BLS12381PublicKey;
67pub type AuthorityPrivateKey = BLS12381PrivateKey;
68pub type AuthoritySignature = BLS12381Signature;
69pub type AggregateAuthoritySignature = BLS12381AggregateSignature;
70pub type AggregateAuthoritySignatureAsBytes = BLS12381AggregateSignatureAsBytes;
71
72// TODO(joyqvq): prefix these types with Default, DefaultAccountKeyPair etc
73pub type AccountKeyPair = Ed25519KeyPair;
74pub type AccountPublicKey = Ed25519PublicKey;
75pub type AccountPrivateKey = Ed25519PrivateKey;
76
77pub type NetworkKeyPair = Ed25519KeyPair;
78pub type NetworkPublicKey = Ed25519PublicKey;
79pub type NetworkPrivateKey = Ed25519PrivateKey;
80
81pub type DefaultHash = Blake2b256;
82
83pub const DEFAULT_EPOCH_ID: EpochId = 0;
84pub const SUI_PRIV_KEY_PREFIX: &str = "suiprivkey";
85
86/// Creates a proof of that the authority account address is owned by the
87/// holder of authority protocol key, and also ensures that the authority
88/// protocol public key exists. A proof of possession is an authority
89/// signature committed over the intent message `intent || message || epoch` (See
90/// more at [struct IntentMessage] and [struct Intent]) where the message is
91/// constructed as `authority_pubkey_bytes || authority_account_address`.
92pub fn generate_proof_of_possession(
93    keypair: &AuthorityKeyPair,
94    address: SuiAddress,
95) -> AuthoritySignature {
96    let mut msg: Vec<u8> = Vec::new();
97    msg.extend_from_slice(keypair.public().as_bytes());
98    msg.extend_from_slice(address.as_ref());
99    AuthoritySignature::new_secure(
100        &IntentMessage::new(Intent::sui_app(IntentScope::ProofOfPossession), msg),
101        &DEFAULT_EPOCH_ID,
102        keypair,
103    )
104}
105
106/// Verify proof of possession against the expected intent message,
107/// consisting of the protocol pubkey and the authority account address.
108pub fn verify_proof_of_possession(
109    pop: &AuthoritySignature,
110    protocol_pubkey: &AuthorityPublicKey,
111    sui_address: SuiAddress,
112) -> Result<(), SuiError> {
113    protocol_pubkey
114        .validate()
115        .map_err(|_| SuiErrorKind::InvalidSignature {
116            error: "Fail to validate pubkey".to_string(),
117        })?;
118    let mut msg = protocol_pubkey.as_bytes().to_vec();
119    msg.extend_from_slice(sui_address.as_ref());
120    pop.verify_secure(
121        &IntentMessage::new(Intent::sui_app(IntentScope::ProofOfPossession), msg),
122        DEFAULT_EPOCH_ID,
123        protocol_pubkey.into(),
124    )
125}
126///////////////////////////////////////////////
127// Account Keys
128//
129// * The following section defines the keypairs that are used by
130// * accounts to interact with Sui.
131// * Currently we support eddsa and ecdsa on Sui.
132//
133
134#[allow(clippy::large_enum_variant)]
135#[derive(Debug, From, PartialEq, Eq)]
136pub enum SuiKeyPair {
137    Ed25519(Ed25519KeyPair),
138    Secp256k1(Secp256k1KeyPair),
139    Secp256r1(Secp256r1KeyPair),
140}
141
142impl SuiKeyPair {
143    pub fn public(&self) -> PublicKey {
144        match self {
145            SuiKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()),
146            SuiKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()),
147            SuiKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()),
148        }
149    }
150
151    pub fn copy(&self) -> Self {
152        match self {
153            SuiKeyPair::Ed25519(kp) => kp.copy().into(),
154            SuiKeyPair::Secp256k1(kp) => kp.copy().into(),
155            SuiKeyPair::Secp256r1(kp) => kp.copy().into(),
156        }
157    }
158}
159
160impl Signer<Signature> for SuiKeyPair {
161    fn sign(&self, msg: &[u8]) -> Signature {
162        match self {
163            SuiKeyPair::Ed25519(kp) => kp.sign(msg),
164            SuiKeyPair::Secp256k1(kp) => kp.sign(msg),
165            SuiKeyPair::Secp256r1(kp) => kp.sign(msg),
166        }
167    }
168}
169
170impl EncodeDecodeBase64 for SuiKeyPair {
171    fn encode_base64(&self) -> String {
172        Base64::encode(self.to_bytes())
173    }
174
175    fn decode_base64(value: &str) -> FastCryptoResult<Self> {
176        let bytes = Base64::decode(value)?;
177        Self::from_bytes(&bytes).map_err(|_| FastCryptoError::InvalidInput)
178    }
179}
180impl SuiKeyPair {
181    pub fn to_bytes(&self) -> Vec<u8> {
182        let mut bytes: Vec<u8> = Vec::new();
183        bytes.push(self.public().flag());
184
185        match self {
186            SuiKeyPair::Ed25519(kp) => {
187                bytes.extend_from_slice(kp.as_bytes());
188            }
189            SuiKeyPair::Secp256k1(kp) => {
190                bytes.extend_from_slice(kp.as_bytes());
191            }
192            SuiKeyPair::Secp256r1(kp) => {
193                bytes.extend_from_slice(kp.as_bytes());
194            }
195        }
196        bytes
197    }
198
199    pub fn from_bytes(bytes: &[u8]) -> Result<Self, eyre::Report> {
200        match SignatureScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?)
201        {
202            Ok(x) => match x {
203                SignatureScheme::ED25519 => Ok(SuiKeyPair::Ed25519(Ed25519KeyPair::from_bytes(
204                    bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
205                )?)),
206                SignatureScheme::Secp256k1 => {
207                    Ok(SuiKeyPair::Secp256k1(Secp256k1KeyPair::from_bytes(
208                        bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
209                    )?))
210                }
211                SignatureScheme::Secp256r1 => {
212                    Ok(SuiKeyPair::Secp256r1(Secp256r1KeyPair::from_bytes(
213                        bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
214                    )?))
215                }
216                _ => Err(eyre!("Invalid flag byte")),
217            },
218            _ => Err(eyre!("Invalid bytes")),
219        }
220    }
221
222    pub fn to_bytes_no_flag(&self) -> Vec<u8> {
223        match self {
224            SuiKeyPair::Ed25519(kp) => kp.as_bytes().to_vec(),
225            SuiKeyPair::Secp256k1(kp) => kp.as_bytes().to_vec(),
226            SuiKeyPair::Secp256r1(kp) => kp.as_bytes().to_vec(),
227        }
228    }
229
230    /// Encode a SuiKeyPair as `flag || privkey` in Bech32 starting with "suiprivkey" to a string. Note that the pubkey is not encoded.
231    pub fn encode(&self) -> Result<String, eyre::Report> {
232        Bech32::encode(self.to_bytes(), SUI_PRIV_KEY_PREFIX).map_err(|e| eyre!(e))
233    }
234
235    /// Decode a SuiKeyPair from `flag || privkey` in Bech32 starting with "suiprivkey" to SuiKeyPair. The public key is computed directly from the private key bytes.
236    pub fn decode(value: &str) -> Result<Self, eyre::Report> {
237        let bytes = Bech32::decode(value, SUI_PRIV_KEY_PREFIX)?;
238        Self::from_bytes(&bytes)
239    }
240}
241
242impl Serialize for SuiKeyPair {
243    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
244    where
245        S: Serializer,
246    {
247        let s = self.encode_base64();
248        serializer.serialize_str(&s)
249    }
250}
251
252impl<'de> Deserialize<'de> for SuiKeyPair {
253    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254    where
255        D: Deserializer<'de>,
256    {
257        use serde::de::Error;
258        let s = String::deserialize(deserializer)?;
259        SuiKeyPair::decode_base64(&s).map_err(|e| Error::custom(e.to_string()))
260    }
261}
262
263#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
264pub enum PublicKey {
265    Ed25519(Ed25519PublicKeyAsBytes),
266    Secp256k1(Secp256k1PublicKeyAsBytes),
267    Secp256r1(Secp256r1PublicKeyAsBytes),
268    ZkLogin(ZkLoginPublicIdentifier),
269    Passkey(Secp256r1PublicKeyAsBytes),
270}
271
272/// A wrapper struct to retrofit in [enum PublicKey] for zkLogin.
273/// Useful to construct [struct MultiSigPublicKey].
274#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
275pub struct ZkLoginPublicIdentifier(#[schemars(with = "Base64")] pub Vec<u8>);
276
277impl ZkLoginPublicIdentifier {
278    /// Consists of iss_bytes_len || iss_bytes || padded_32_byte_address_seed.
279    pub fn new(iss: &str, address_seed: &Bn254FrElement) -> SuiResult<Self> {
280        let mut bytes = Vec::new();
281        let iss_bytes = iss.as_bytes();
282        bytes.extend([iss_bytes.len() as u8]);
283        bytes.extend(iss_bytes);
284        bytes.extend(address_seed.padded());
285
286        Ok(Self(bytes))
287    }
288
289    /// Validates zkLogin public identifier structure: iss_len || iss || padded_32_byte_address_seed.
290    pub fn validate(&self) -> SuiResult<()> {
291        let bytes = &self.0;
292
293        // Parse issuer length and bytes.
294        let iss_len = *bytes
295            .first()
296            .ok_or_else(|| SuiErrorKind::InvalidSignature {
297                error: "invalid zklogin pk".to_string(),
298            })? as usize;
299        let iss_bytes =
300            bytes
301                .get(1..1 + iss_len)
302                .ok_or_else(|| SuiErrorKind::InvalidSignature {
303                    error: "invalid zklogin pk iss length".to_string(),
304                })?;
305
306        // Validate issuer string.
307        std::str::from_utf8(iss_bytes).map_err(|e| SuiErrorKind::InvalidSignature {
308            error: format!("zkLogin pk issuer is not valid: {}", e),
309        })?;
310
311        // Validate address seed length <= 32 bytes.
312        let address_seed_bytes =
313            bytes
314                .get(1 + iss_len..)
315                .ok_or_else(|| SuiErrorKind::InvalidSignature {
316                    error: "zkLogin pk has no address seed".to_string(),
317                })?;
318
319        if address_seed_bytes.len() > 32 {
320            return Err(SuiErrorKind::InvalidSignature {
321                error: format!(
322                    "address seed must be at most 32 bytes, got {}",
323                    address_seed_bytes.len()
324                ),
325            }
326            .into());
327        }
328
329        Ok(())
330    }
331}
332impl AsRef<[u8]> for PublicKey {
333    fn as_ref(&self) -> &[u8] {
334        match self {
335            PublicKey::Ed25519(pk) => &pk.0,
336            PublicKey::Secp256k1(pk) => &pk.0,
337            PublicKey::Secp256r1(pk) => &pk.0,
338            PublicKey::ZkLogin(z) => &z.0,
339            PublicKey::Passkey(pk) => &pk.0,
340        }
341    }
342}
343
344impl EncodeDecodeBase64 for PublicKey {
345    fn encode_base64(&self) -> String {
346        let mut bytes: Vec<u8> = Vec::new();
347        bytes.extend_from_slice(&[self.flag()]);
348        bytes.extend_from_slice(self.as_ref());
349        Base64::encode(&bytes[..])
350    }
351
352    fn decode_base64(value: &str) -> FastCryptoResult<Self> {
353        let bytes = Base64::decode(value)?;
354        match bytes.first() {
355            Some(x) => {
356                if x == &SignatureScheme::ED25519.flag() {
357                    let pk: Ed25519PublicKey =
358                        Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or(
359                            FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1),
360                        )?)?;
361                    Ok(PublicKey::Ed25519((&pk).into()))
362                } else if x == &SignatureScheme::Secp256k1.flag() {
363                    let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or(
364                        FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1),
365                    )?)?;
366                    Ok(PublicKey::Secp256k1((&pk).into()))
367                } else if x == &SignatureScheme::Secp256r1.flag() {
368                    let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or(
369                        FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1),
370                    )?)?;
371                    Ok(PublicKey::Secp256r1((&pk).into()))
372                } else if x == &SignatureScheme::PasskeyAuthenticator.flag() {
373                    let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or(
374                        FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1),
375                    )?)?;
376                    Ok(PublicKey::Passkey((&pk).into()))
377                } else {
378                    Err(FastCryptoError::InvalidInput)
379                }
380            }
381            _ => Err(FastCryptoError::InvalidInput),
382        }
383    }
384}
385
386impl PublicKey {
387    pub fn flag(&self) -> u8 {
388        self.scheme().flag()
389    }
390
391    pub fn try_from_bytes(
392        curve: SignatureScheme,
393        key_bytes: &[u8],
394    ) -> Result<PublicKey, eyre::Report> {
395        match curve {
396            SignatureScheme::ED25519 => Ok(PublicKey::Ed25519(
397                (&Ed25519PublicKey::from_bytes(key_bytes)?).into(),
398            )),
399            SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(
400                (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(),
401            )),
402            SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(
403                (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(),
404            )),
405            SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey(
406                (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(),
407            )),
408            _ => Err(eyre!("Unsupported curve")),
409        }
410    }
411
412    pub fn scheme(&self) -> SignatureScheme {
413        match self {
414            PublicKey::Ed25519(_) => Ed25519SuiSignature::SCHEME,
415            PublicKey::Secp256k1(_) => Secp256k1SuiSignature::SCHEME,
416            PublicKey::Secp256r1(_) => Secp256r1SuiSignature::SCHEME,
417            PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator,
418            PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator,
419        }
420    }
421
422    pub fn from_zklogin_inputs(inputs: &ZkLoginInputs) -> SuiResult<Self> {
423        Ok(PublicKey::ZkLogin(ZkLoginPublicIdentifier::new(
424            inputs.get_iss(),
425            inputs.get_address_seed(),
426        )?))
427    }
428}
429
430/// Defines the compressed version of the public key that we pass around
431/// in Sui
432#[serde_as]
433#[derive(
434    Copy,
435    Clone,
436    PartialEq,
437    Eq,
438    Hash,
439    PartialOrd,
440    Ord,
441    Serialize,
442    Deserialize,
443    schemars::JsonSchema,
444    AsRef,
445)]
446#[as_ref(forward)]
447pub struct AuthorityPublicKeyBytes(
448    #[schemars(with = "Base64")]
449    #[serde_as(as = "Readable<Base64, Bytes>")]
450    pub [u8; AuthorityPublicKey::LENGTH],
451);
452
453impl AuthorityPublicKeyBytes {
454    fn fmt_impl(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
455        let s = Hex::encode(self.0);
456        write!(f, "k#{}", s)?;
457        Ok(())
458    }
459}
460
461impl<'a> ConciseableName<'a> for AuthorityPublicKeyBytes {
462    type ConciseTypeRef = ConciseAuthorityPublicKeyBytesRef<'a>;
463    type ConciseType = ConciseAuthorityPublicKeyBytes;
464
465    /// Get a ConciseAuthorityPublicKeyBytesRef. Usage:
466    ///
467    ///   debug!(name = ?authority.concise());
468    ///   format!("{:?}", authority.concise());
469    fn concise(&'a self) -> ConciseAuthorityPublicKeyBytesRef<'a> {
470        ConciseAuthorityPublicKeyBytesRef(self)
471    }
472
473    fn concise_owned(&self) -> ConciseAuthorityPublicKeyBytes {
474        ConciseAuthorityPublicKeyBytes(*self)
475    }
476}
477
478/// A wrapper around AuthorityPublicKeyBytes that provides a concise Debug impl.
479pub struct ConciseAuthorityPublicKeyBytesRef<'a>(&'a AuthorityPublicKeyBytes);
480
481impl Debug for ConciseAuthorityPublicKeyBytesRef<'_> {
482    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
483        let s = Hex::encode(self.0.0.get(0..4).ok_or(std::fmt::Error)?);
484        write!(f, "k#{}..", s)
485    }
486}
487
488impl Display for ConciseAuthorityPublicKeyBytesRef<'_> {
489    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
490        Debug::fmt(self, f)
491    }
492}
493
494/// A wrapper around AuthorityPublicKeyBytes but owns it.
495#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
496pub struct ConciseAuthorityPublicKeyBytes(AuthorityPublicKeyBytes);
497
498impl Debug for ConciseAuthorityPublicKeyBytes {
499    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
500        let s = Hex::encode(self.0.0.get(0..4).ok_or(std::fmt::Error)?);
501        write!(f, "k#{}..", s)
502    }
503}
504
505impl Display for ConciseAuthorityPublicKeyBytes {
506    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
507        Debug::fmt(self, f)
508    }
509}
510
511impl TryFrom<AuthorityPublicKeyBytes> for AuthorityPublicKey {
512    type Error = FastCryptoError;
513
514    fn try_from(bytes: AuthorityPublicKeyBytes) -> Result<AuthorityPublicKey, Self::Error> {
515        AuthorityPublicKey::from_bytes(bytes.as_ref())
516    }
517}
518
519impl From<&AuthorityPublicKey> for AuthorityPublicKeyBytes {
520    fn from(pk: &AuthorityPublicKey) -> AuthorityPublicKeyBytes {
521        AuthorityPublicKeyBytes::from_bytes(pk.as_ref()).unwrap()
522    }
523}
524
525impl Debug for AuthorityPublicKeyBytes {
526    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
527        self.fmt_impl(f)
528    }
529}
530
531impl Display for AuthorityPublicKeyBytes {
532    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
533        self.fmt_impl(f)
534    }
535}
536
537impl ToFromBytes for AuthorityPublicKeyBytes {
538    fn from_bytes(bytes: &[u8]) -> Result<Self, fastcrypto::error::FastCryptoError> {
539        let bytes: [u8; AuthorityPublicKey::LENGTH] = bytes
540            .try_into()
541            .map_err(|_| fastcrypto::error::FastCryptoError::InvalidInput)?;
542        Ok(AuthorityPublicKeyBytes(bytes))
543    }
544}
545
546impl AuthorityPublicKeyBytes {
547    pub const ZERO: Self = Self::new([0u8; AuthorityPublicKey::LENGTH]);
548
549    /// This ensures it's impossible to construct an instance with other than registered lengths
550    pub const fn new(bytes: [u8; AuthorityPublicKey::LENGTH]) -> AuthorityPublicKeyBytes
551where {
552        AuthorityPublicKeyBytes(bytes)
553    }
554}
555
556impl FromStr for AuthorityPublicKeyBytes {
557    type Err = Error;
558
559    fn from_str(s: &str) -> Result<Self, Self::Err> {
560        let value = Hex::decode(s).map_err(|e| anyhow!(e))?;
561        Self::from_bytes(&value[..]).map_err(|e| anyhow!(e))
562    }
563}
564
565impl Default for AuthorityPublicKeyBytes {
566    fn default() -> Self {
567        Self::ZERO
568    }
569}
570
571//
572// Add helper calls for Authority Signature
573//
574
575pub trait SuiAuthoritySignature {
576    fn verify_secure<T>(
577        &self,
578        value: &IntentMessage<T>,
579        epoch_id: EpochId,
580        author: AuthorityPublicKeyBytes,
581    ) -> Result<(), SuiError>
582    where
583        T: Serialize;
584
585    fn new_secure<T>(
586        value: &IntentMessage<T>,
587        epoch_id: &EpochId,
588        secret: &dyn Signer<Self>,
589    ) -> Self
590    where
591        T: Serialize;
592}
593
594impl SuiAuthoritySignature for AuthoritySignature {
595    #[instrument(level = "trace", skip_all)]
596    fn new_secure<T>(value: &IntentMessage<T>, epoch: &EpochId, secret: &dyn Signer<Self>) -> Self
597    where
598        T: Serialize,
599    {
600        let mut intent_msg_bytes =
601            bcs::to_bytes(&value).expect("Message serialization should not fail");
602        epoch.write(&mut intent_msg_bytes);
603        secret.sign(&intent_msg_bytes)
604    }
605
606    #[instrument(level = "trace", skip_all)]
607    fn verify_secure<T>(
608        &self,
609        value: &IntentMessage<T>,
610        epoch: EpochId,
611        author: AuthorityPublicKeyBytes,
612    ) -> Result<(), SuiError>
613    where
614        T: Serialize,
615    {
616        let mut message = bcs::to_bytes(&value).expect("Message serialization should not fail");
617        epoch.write(&mut message);
618
619        let public_key = AuthorityPublicKey::try_from(author).map_err(|_| {
620            SuiErrorKind::KeyConversionError(
621                "Failed to serialize public key bytes to valid public key".to_string(),
622            )
623        })?;
624        public_key.verify(&message[..], self).map_err(|e| {
625            SuiErrorKind::InvalidSignature {
626                error: format!(
627                    "Fail to verify auth sig {} epoch: {} author: {}",
628                    e,
629                    epoch,
630                    author.concise()
631                ),
632            }
633            .into()
634        })
635    }
636}
637
638// TODO: get_key_pair() and get_key_pair_from_bytes() should return KeyPair only.
639// TODO: rename to random_key_pair
640pub fn get_key_pair<KP: KeypairTraits>() -> (SuiAddress, KP)
641where
642    <KP as KeypairTraits>::PubKey: SuiPublicKey,
643{
644    get_key_pair_from_rng(&mut OsRng)
645}
646
647/// Generate a random committee key pairs with a given committee size
648pub fn random_committee_key_pairs_of_size(size: usize) -> Vec<AuthorityKeyPair> {
649    let mut rng = StdRng::from_seed([0; 32]);
650    (0..size)
651        .map(|_| {
652            // TODO: We are generating the keys 4 times to match exactly as how we generate
653            // keys in ConfigBuilder::build (sui-config/src/network_config_builder). This is because
654            // we are using these key generation functions as fixtures and we call them
655            // independently in different paths and exact the results to be the same.
656            // We should eliminate them.
657            let key_pair = get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut rng);
658            get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut rng);
659            get_key_pair_from_rng::<AccountKeyPair, _>(&mut rng);
660            get_key_pair_from_rng::<AccountKeyPair, _>(&mut rng);
661            key_pair.1
662        })
663        .collect()
664}
665
666pub fn deterministic_random_account_key() -> (SuiAddress, AccountKeyPair) {
667    let mut rng = StdRng::from_seed([0; 32]);
668    get_key_pair_from_rng(&mut rng)
669}
670
671pub fn get_account_key_pair() -> (SuiAddress, AccountKeyPair) {
672    get_key_pair()
673}
674
675pub fn get_authority_key_pair() -> (SuiAddress, AuthorityKeyPair) {
676    get_key_pair()
677}
678
679/// Generate a keypair from the specified RNG (useful for testing with seedable rngs).
680pub fn get_key_pair_from_rng<KP: KeypairTraits, R>(csprng: &mut R) -> (SuiAddress, KP)
681where
682    R: rand::CryptoRng + rand::RngCore,
683    <KP as KeypairTraits>::PubKey: SuiPublicKey,
684{
685    let kp = KP::generate(&mut StdRng::from_rng(csprng).unwrap());
686    (kp.public().into(), kp)
687}
688
689// TODO: C-GETTER
690pub fn get_key_pair_from_bytes<KP: KeypairTraits>(bytes: &[u8]) -> SuiResult<(SuiAddress, KP)>
691where
692    <KP as KeypairTraits>::PubKey: SuiPublicKey,
693{
694    let priv_length = <KP as KeypairTraits>::PrivKey::LENGTH;
695    let pub_key_length = <KP as KeypairTraits>::PubKey::LENGTH;
696    if bytes.len() != priv_length + pub_key_length {
697        return Err(SuiErrorKind::KeyConversionError(format!(
698            "Invalid input byte length, expected {}: {}",
699            priv_length,
700            bytes.len()
701        ))
702        .into());
703    }
704    let sk = <KP as KeypairTraits>::PrivKey::from_bytes(
705        bytes
706            .get(..priv_length)
707            .ok_or(SuiErrorKind::InvalidPrivateKey)?,
708    )
709    .map_err(|_| SuiErrorKind::InvalidPrivateKey)?;
710    let kp: KP = sk.into();
711    Ok((kp.public().into(), kp))
712}
713
714//
715// Account Signatures
716//
717
718// Enums for signature scheme signatures
719#[enum_dispatch]
720#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)]
721pub enum Signature {
722    Ed25519SuiSignature,
723    Secp256k1SuiSignature,
724    Secp256r1SuiSignature,
725}
726
727impl Serialize for Signature {
728    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
729    where
730        S: Serializer,
731    {
732        let bytes = self.as_ref();
733
734        if serializer.is_human_readable() {
735            let s = Base64::encode(bytes);
736            serializer.serialize_str(&s)
737        } else {
738            serializer.serialize_bytes(bytes)
739        }
740    }
741}
742
743impl<'de> Deserialize<'de> for Signature {
744    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
745    where
746        D: Deserializer<'de>,
747    {
748        use serde::de::Error;
749
750        let bytes = if deserializer.is_human_readable() {
751            let s = String::deserialize(deserializer)?;
752            Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))?
753        } else {
754            let data: Vec<u8> = Vec::deserialize(deserializer)?;
755            data
756        };
757
758        Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string()))
759    }
760}
761
762impl Signature {
763    /// The messaged passed in is already hashed form.
764    pub fn new_hashed(hashed_msg: &[u8], secret: &dyn Signer<Signature>) -> Self {
765        Signer::sign(secret, hashed_msg)
766    }
767
768    pub fn new_secure<T>(value: &IntentMessage<T>, secret: &dyn Signer<Signature>) -> Self
769    where
770        T: Serialize,
771    {
772        // Compute the BCS hash of the value in intent message. In the case of transaction data,
773        // this is the BCS hash of `struct TransactionData`, different from the transaction digest
774        // itself that computes the BCS hash of the Rust type prefix and `struct TransactionData`.
775        // (See `fn digest` in `impl Message for SenderSignedData`).
776        let mut hasher = DefaultHash::default();
777        hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail"));
778
779        Signer::sign(secret, &hasher.finalize().digest)
780    }
781}
782
783impl AsRef<[u8]> for Signature {
784    fn as_ref(&self) -> &[u8] {
785        match self {
786            Signature::Ed25519SuiSignature(sig) => sig.as_ref(),
787            Signature::Secp256k1SuiSignature(sig) => sig.as_ref(),
788            Signature::Secp256r1SuiSignature(sig) => sig.as_ref(),
789        }
790    }
791}
792impl AsMut<[u8]> for Signature {
793    fn as_mut(&mut self) -> &mut [u8] {
794        match self {
795            Signature::Ed25519SuiSignature(sig) => sig.as_mut(),
796            Signature::Secp256k1SuiSignature(sig) => sig.as_mut(),
797            Signature::Secp256r1SuiSignature(sig) => sig.as_mut(),
798        }
799    }
800}
801
802impl ToFromBytes for Signature {
803    fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
804        match bytes.first() {
805            Some(x) => {
806                if x == &Ed25519SuiSignature::SCHEME.flag() {
807                    Ok(<Ed25519SuiSignature as ToFromBytes>::from_bytes(bytes)?.into())
808                } else if x == &Secp256k1SuiSignature::SCHEME.flag() {
809                    Ok(<Secp256k1SuiSignature as ToFromBytes>::from_bytes(bytes)?.into())
810                } else if x == &Secp256r1SuiSignature::SCHEME.flag() {
811                    Ok(<Secp256r1SuiSignature as ToFromBytes>::from_bytes(bytes)?.into())
812                } else {
813                    Err(FastCryptoError::InvalidInput)
814                }
815            }
816            _ => Err(FastCryptoError::InvalidInput),
817        }
818    }
819}
820
821//
822// BLS Port
823//
824
825impl SuiPublicKey for BLS12381PublicKey {
826    const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::BLS12381;
827}
828
829//
830// Ed25519 Sui Signature port
831//
832
833#[serde_as]
834#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
835#[as_ref(forward)]
836#[as_mut(forward)]
837pub struct Ed25519SuiSignature(
838    #[schemars(with = "Base64")]
839    #[serde_as(as = "Readable<Base64, Bytes>")]
840    [u8; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1],
841);
842
843// Implementation useful for simplify testing when mock signature is needed
844impl Default for Ed25519SuiSignature {
845    fn default() -> Self {
846        Self([0; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1])
847    }
848}
849
850impl SuiSignatureInner for Ed25519SuiSignature {
851    type Sig = Ed25519Signature;
852    type PubKey = Ed25519PublicKey;
853    type KeyPair = Ed25519KeyPair;
854    const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1;
855}
856
857impl SuiPublicKey for Ed25519PublicKey {
858    const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519;
859}
860
861impl ToFromBytes for Ed25519SuiSignature {
862    fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
863        if bytes.len() != Self::LENGTH {
864            return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
865        }
866        let mut sig_bytes = [0; Self::LENGTH];
867        sig_bytes.copy_from_slice(bytes);
868        Ok(Self(sig_bytes))
869    }
870}
871
872impl Signer<Signature> for Ed25519KeyPair {
873    fn sign(&self, msg: &[u8]) -> Signature {
874        Ed25519SuiSignature::new(self, msg).into()
875    }
876}
877
878//
879// Secp256k1 Sui Signature port
880//
881#[serde_as]
882#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
883#[as_ref(forward)]
884#[as_mut(forward)]
885pub struct Secp256k1SuiSignature(
886    #[schemars(with = "Base64")]
887    #[serde_as(as = "Readable<Base64, Bytes>")]
888    [u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1],
889);
890
891impl SuiSignatureInner for Secp256k1SuiSignature {
892    type Sig = Secp256k1Signature;
893    type PubKey = Secp256k1PublicKey;
894    type KeyPair = Secp256k1KeyPair;
895    const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1;
896}
897
898impl SuiPublicKey for Secp256k1PublicKey {
899    const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1;
900}
901
902impl ToFromBytes for Secp256k1SuiSignature {
903    fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
904        if bytes.len() != Self::LENGTH {
905            return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
906        }
907        let mut sig_bytes = [0; Self::LENGTH];
908        sig_bytes.copy_from_slice(bytes);
909        Ok(Self(sig_bytes))
910    }
911}
912
913impl Signer<Signature> for Secp256k1KeyPair {
914    fn sign(&self, msg: &[u8]) -> Signature {
915        Secp256k1SuiSignature::new(self, msg).into()
916    }
917}
918
919//
920// Secp256r1 Sui Signature port
921//
922#[serde_as]
923#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
924#[as_ref(forward)]
925#[as_mut(forward)]
926pub struct Secp256r1SuiSignature(
927    #[schemars(with = "Base64")]
928    #[serde_as(as = "Readable<Base64, Bytes>")]
929    [u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1],
930);
931
932impl SuiSignatureInner for Secp256r1SuiSignature {
933    type Sig = Secp256r1Signature;
934    type PubKey = Secp256r1PublicKey;
935    type KeyPair = Secp256r1KeyPair;
936    const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1;
937}
938
939impl SuiPublicKey for Secp256r1PublicKey {
940    const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1;
941}
942
943impl ToFromBytes for Secp256r1SuiSignature {
944    fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
945        if bytes.len() != Self::LENGTH {
946            return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
947        }
948        let mut sig_bytes = [0; Self::LENGTH];
949        sig_bytes.copy_from_slice(bytes);
950        Ok(Self(sig_bytes))
951    }
952}
953
954impl Signer<Signature> for Secp256r1KeyPair {
955    fn sign(&self, msg: &[u8]) -> Signature {
956        Secp256r1SuiSignature::new(self, msg).into()
957    }
958}
959
960//
961// This struct exists due to the limitations of the `enum_dispatch` library.
962//
963pub trait SuiSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash {
964    type Sig: Authenticator<PubKey = Self::PubKey>;
965    type PubKey: VerifyingKey<Sig = Self::Sig> + SuiPublicKey;
966    type KeyPair: KeypairTraits<PubKey = Self::PubKey, Sig = Self::Sig>;
967
968    const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1;
969    const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME;
970
971    /// Returns the deserialized signature and deserialized pubkey.
972    fn get_verification_inputs(&self) -> SuiResult<(Self::Sig, Self::PubKey)> {
973        let pk = Self::PubKey::from_bytes(self.public_key_bytes())
974            .map_err(|_| SuiErrorKind::KeyConversionError("Invalid public key".to_string()))?;
975
976        // deserialize the signature
977        let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| {
978            SuiErrorKind::InvalidSignature {
979                error: "Fail to get pubkey and sig".to_string(),
980            }
981        })?;
982
983        Ok((signature, pk))
984    }
985
986    fn new(kp: &Self::KeyPair, message: &[u8]) -> Self {
987        let sig = Signer::sign(kp, message);
988
989        let mut signature_bytes: Vec<u8> = Vec::new();
990        signature_bytes
991            .extend_from_slice(&[<Self::PubKey as SuiPublicKey>::SIGNATURE_SCHEME.flag()]);
992        signature_bytes.extend_from_slice(sig.as_ref());
993        signature_bytes.extend_from_slice(kp.public().as_ref());
994        Self::from_bytes(&signature_bytes[..])
995            .expect("Serialized signature did not have expected size")
996    }
997}
998
999pub trait SuiPublicKey: VerifyingKey {
1000    const SIGNATURE_SCHEME: SignatureScheme;
1001}
1002
1003#[enum_dispatch(Signature)]
1004pub trait SuiSignature: Sized + ToFromBytes {
1005    fn signature_bytes(&self) -> &[u8];
1006    fn public_key_bytes(&self) -> &[u8];
1007    fn scheme(&self) -> SignatureScheme;
1008
1009    fn verify_secure<T>(
1010        &self,
1011        value: &IntentMessage<T>,
1012        author: SuiAddress,
1013        scheme: SignatureScheme,
1014    ) -> SuiResult<()>
1015    where
1016        T: Serialize;
1017}
1018
1019impl<S: SuiSignatureInner + Sized> SuiSignature for S {
1020    fn signature_bytes(&self) -> &[u8] {
1021        // Access array slice is safe because the array bytes is initialized as
1022        // flag || signature || pubkey with its defined length.
1023        &self.as_ref()[1..1 + S::Sig::LENGTH]
1024    }
1025
1026    fn public_key_bytes(&self) -> &[u8] {
1027        // Access array slice is safe because the array bytes is initialized as
1028        // flag || signature || pubkey with its defined length.
1029        &self.as_ref()[S::Sig::LENGTH + 1..]
1030    }
1031
1032    fn scheme(&self) -> SignatureScheme {
1033        S::PubKey::SIGNATURE_SCHEME
1034    }
1035
1036    fn verify_secure<T>(
1037        &self,
1038        value: &IntentMessage<T>,
1039        author: SuiAddress,
1040        scheme: SignatureScheme,
1041    ) -> Result<(), SuiError>
1042    where
1043        T: Serialize,
1044    {
1045        let mut hasher = DefaultHash::default();
1046        hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail"));
1047        let digest = hasher.finalize().digest;
1048
1049        let (sig, pk) = &self.get_verification_inputs()?;
1050        match scheme {
1051            SignatureScheme::ZkLoginAuthenticator => {} // Pass this check because zk login does not derive address from pubkey.
1052            _ => {
1053                let address = SuiAddress::from(pk);
1054                if author != address {
1055                    return Err(SuiErrorKind::IncorrectSigner {
1056                        error: format!(
1057                            "Incorrect signer, expected {:?}, got {:?}",
1058                            author, address
1059                        ),
1060                    }
1061                    .into());
1062                }
1063            }
1064        }
1065
1066        pk.verify(&digest, sig).map_err(|e| {
1067            SuiErrorKind::InvalidSignature {
1068                error: format!("Fail to verify user sig {}", e),
1069            }
1070            .into()
1071        })
1072    }
1073}
1074
1075/// AuthoritySignInfoTrait is a trait used specifically for a few structs in messages.rs
1076/// to template on whether the struct is signed by an authority. We want to limit how
1077/// those structs can be instantiated on, hence the sealed trait.
1078/// TODO: We could also add the aggregated signature as another impl of the trait.
1079///       This will make CertifiedTransaction also an instance of the same struct.
1080pub trait AuthoritySignInfoTrait: private::SealedAuthoritySignInfoTrait {
1081    fn verify_secure<T: Serialize>(
1082        &self,
1083        data: &T,
1084        intent: Intent,
1085        committee: &Committee,
1086    ) -> SuiResult;
1087
1088    fn add_to_verification_obligation<'a>(
1089        &self,
1090        committee: &'a Committee,
1091        obligation: &mut VerificationObligation<'a>,
1092        message_index: usize,
1093    ) -> SuiResult<()>;
1094}
1095
1096#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1097pub struct EmptySignInfo {}
1098impl AuthoritySignInfoTrait for EmptySignInfo {
1099    fn verify_secure<T: Serialize>(
1100        &self,
1101        _data: &T,
1102        _intent: Intent,
1103        _committee: &Committee,
1104    ) -> SuiResult {
1105        Ok(())
1106    }
1107
1108    fn add_to_verification_obligation<'a>(
1109        &self,
1110        _committee: &'a Committee,
1111        _obligation: &mut VerificationObligation<'a>,
1112        _message_index: usize,
1113    ) -> SuiResult<()> {
1114        Ok(())
1115    }
1116}
1117
1118#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
1119pub struct AuthoritySignInfo {
1120    pub epoch: EpochId,
1121    pub authority: AuthorityName,
1122    pub signature: AuthoritySignature,
1123}
1124
1125impl AuthoritySignInfoTrait for AuthoritySignInfo {
1126    fn verify_secure<T: Serialize>(
1127        &self,
1128        data: &T,
1129        intent: Intent,
1130        committee: &Committee,
1131    ) -> SuiResult<()> {
1132        let mut obligation = VerificationObligation::default();
1133        let idx = obligation.add_message(data, self.epoch, intent);
1134        self.add_to_verification_obligation(committee, &mut obligation, idx)?;
1135        obligation.verify_all()?;
1136        Ok(())
1137    }
1138
1139    fn add_to_verification_obligation<'a>(
1140        &self,
1141        committee: &'a Committee,
1142        obligation: &mut VerificationObligation<'a>,
1143        message_index: usize,
1144    ) -> SuiResult<()> {
1145        fp_ensure!(
1146            self.epoch == committee.epoch(),
1147            SuiErrorKind::WrongEpoch {
1148                expected_epoch: committee.epoch(),
1149                actual_epoch: self.epoch,
1150            }
1151            .into()
1152        );
1153        let weight = committee.weight(&self.authority);
1154        fp_ensure!(
1155            weight > 0,
1156            SuiErrorKind::UnknownSigner {
1157                signer: Some(self.authority.concise().to_string()),
1158                index: None,
1159                committee: Box::new(committee.clone())
1160            }
1161            .into()
1162        );
1163
1164        obligation
1165            .public_keys
1166            .get_mut(message_index)
1167            .ok_or(SuiErrorKind::InvalidAddress)?
1168            .push(committee.public_key(&self.authority)?);
1169        obligation
1170            .signatures
1171            .get_mut(message_index)
1172            .ok_or(SuiErrorKind::InvalidAddress)?
1173            .add_signature(self.signature.clone())
1174            .map_err(|_| SuiErrorKind::InvalidSignature {
1175                error: "Fail to aggregator auth sig".to_string(),
1176            })?;
1177        Ok(())
1178    }
1179}
1180
1181impl AuthoritySignInfo {
1182    pub fn new<T>(
1183        epoch: EpochId,
1184        value: &T,
1185        intent: Intent,
1186        name: AuthorityName,
1187        secret: &dyn Signer<AuthoritySignature>,
1188    ) -> Self
1189    where
1190        T: Serialize,
1191    {
1192        Self {
1193            epoch,
1194            authority: name,
1195            signature: AuthoritySignature::new_secure(
1196                &IntentMessage::new(intent, value),
1197                &epoch,
1198                secret,
1199            ),
1200        }
1201    }
1202}
1203
1204impl Hash for AuthoritySignInfo {
1205    fn hash<H: Hasher>(&self, state: &mut H) {
1206        self.epoch.hash(state);
1207        self.authority.hash(state);
1208    }
1209}
1210
1211impl Display for AuthoritySignInfo {
1212    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1213        write!(
1214            f,
1215            "AuthoritySignInfo {{ epoch: {:?}, authority: {} }}",
1216            self.epoch, self.authority,
1217        )
1218    }
1219}
1220
1221impl PartialEq for AuthoritySignInfo {
1222    fn eq(&self, other: &Self) -> bool {
1223        // We do not compare the signature, because there can be multiple
1224        // valid signatures for the same epoch and authority.
1225        self.epoch == other.epoch && self.authority == other.authority
1226    }
1227}
1228
1229/// Represents at least a quorum (could be more) of authority signatures.
1230/// STRONG_THRESHOLD indicates whether to use the quorum threshold for quorum check.
1231/// When STRONG_THRESHOLD is true, the quorum is valid when the total stake is
1232/// at least the quorum threshold (2f+1) of the committee; when STRONG_THRESHOLD is false,
1233/// the quorum is valid when the total stake is at least the validity threshold (f+1) of
1234/// the committee.
1235#[serde_as]
1236#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1237pub struct AuthorityQuorumSignInfo<const STRONG_THRESHOLD: bool> {
1238    pub epoch: EpochId,
1239    #[schemars(with = "Base64")]
1240    pub signature: AggregateAuthoritySignature,
1241    #[schemars(with = "Base64")]
1242    #[serde_as(as = "SuiBitmap")]
1243    pub signers_map: RoaringBitmap,
1244}
1245
1246pub type AuthorityStrongQuorumSignInfo = AuthorityQuorumSignInfo<true>;
1247
1248// Variant of [AuthorityStrongQuorumSignInfo] but with a serialized signature, to be used in
1249// external APIs.
1250#[serde_as]
1251#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1252pub struct SuiAuthorityStrongQuorumSignInfo {
1253    pub epoch: EpochId,
1254    pub signature: AggregateAuthoritySignatureAsBytes,
1255    #[schemars(with = "Base64")]
1256    #[serde_as(as = "SuiBitmap")]
1257    pub signers_map: RoaringBitmap,
1258}
1259
1260impl From<&AuthorityStrongQuorumSignInfo> for SuiAuthorityStrongQuorumSignInfo {
1261    fn from(info: &AuthorityStrongQuorumSignInfo) -> Self {
1262        Self {
1263            epoch: info.epoch,
1264            signature: (&info.signature).into(),
1265            signers_map: info.signers_map.clone(),
1266        }
1267    }
1268}
1269
1270impl TryFrom<&SuiAuthorityStrongQuorumSignInfo> for AuthorityStrongQuorumSignInfo {
1271    type Error = FastCryptoError;
1272
1273    fn try_from(info: &SuiAuthorityStrongQuorumSignInfo) -> Result<Self, Self::Error> {
1274        Ok(Self {
1275            epoch: info.epoch,
1276            signature: (&info.signature).try_into()?,
1277            signers_map: info.signers_map.clone(),
1278        })
1279    }
1280}
1281
1282// Note: if you meet an error due to this line it may be because you need an Eq implementation for `CertifiedTransaction`,
1283// or one of the structs that include it, i.e. `ConfirmationTransaction`, `TransactionInfoResponse` or `ObjectInfoResponse`.
1284//
1285// Please note that any such implementation must be agnostic to the exact set of signatures in the certificate, as
1286// clients are allowed to equivocate on the exact nature of valid certificates they send to the system. This assertion
1287// is a simple tool to make sure certificates are accounted for correctly - should you remove it, you're on your own to
1288// maintain the invariant that valid certificates with distinct signatures are equivalent, but yet-unchecked
1289// certificates that differ on signers aren't.
1290//
1291// see also https://github.com/MystenLabs/sui/issues/266
1292static_assertions::assert_not_impl_any!(AuthorityStrongQuorumSignInfo: Hash, Eq, PartialEq);
1293
1294impl<const STRONG_THRESHOLD: bool> AuthoritySignInfoTrait
1295    for AuthorityQuorumSignInfo<STRONG_THRESHOLD>
1296{
1297    fn verify_secure<T: Serialize>(
1298        &self,
1299        data: &T,
1300        intent: Intent,
1301        committee: &Committee,
1302    ) -> SuiResult {
1303        let mut obligation = VerificationObligation::default();
1304        let idx = obligation.add_message(data, self.epoch, intent);
1305        self.add_to_verification_obligation(committee, &mut obligation, idx)?;
1306        obligation.verify_all()?;
1307        Ok(())
1308    }
1309
1310    fn add_to_verification_obligation<'a>(
1311        &self,
1312        committee: &'a Committee,
1313        obligation: &mut VerificationObligation<'a>,
1314        message_index: usize,
1315    ) -> SuiResult<()> {
1316        // Check epoch
1317        fp_ensure!(
1318            self.epoch == committee.epoch(),
1319            SuiErrorKind::WrongEpoch {
1320                expected_epoch: committee.epoch(),
1321                actual_epoch: self.epoch,
1322            }
1323            .into()
1324        );
1325
1326        let mut weight = 0;
1327
1328        // Create obligations for the committee signatures
1329        obligation
1330            .signatures
1331            .get_mut(message_index)
1332            .ok_or(SuiErrorKind::InvalidAuthenticator)?
1333            .add_aggregate(self.signature.clone())
1334            .map_err(|_| SuiErrorKind::InvalidSignature {
1335                error: "Signature Aggregation failed".to_string(),
1336            })?;
1337
1338        let selected_public_keys = obligation
1339            .public_keys
1340            .get_mut(message_index)
1341            .ok_or(SuiErrorKind::InvalidAuthenticator)?;
1342
1343        let mut seen = std::collections::BTreeSet::new();
1344        for authority_index in self.signers_map.iter() {
1345            if !seen.insert(authority_index) {
1346                continue;
1347            }
1348
1349            // Update weight when seeing the authority for the first time.
1350            let authority = committee
1351                .authority_by_index(authority_index)
1352                .ok_or_else(|| SuiErrorKind::UnknownSigner {
1353                    signer: None,
1354                    index: Some(authority_index),
1355                    committee: Box::new(committee.clone()),
1356                })?;
1357            let voting_rights = committee.weight(authority);
1358            fp_ensure!(
1359                voting_rights > 0,
1360                SuiErrorKind::UnknownSigner {
1361                    signer: Some(authority.concise().to_string()),
1362                    index: Some(authority_index),
1363                    committee: Box::new(committee.clone()),
1364                }
1365                .into()
1366            );
1367            weight += voting_rights;
1368
1369            selected_public_keys.push(committee.public_key(authority)?);
1370        }
1371
1372        fp_ensure!(
1373            weight >= Self::quorum_threshold(committee),
1374            SuiErrorKind::CertificateRequiresQuorum.into()
1375        );
1376
1377        Ok(())
1378    }
1379}
1380
1381impl<const STRONG_THRESHOLD: bool> AuthorityQuorumSignInfo<STRONG_THRESHOLD> {
1382    pub fn new_from_auth_sign_infos(
1383        auth_sign_infos: Vec<AuthoritySignInfo>,
1384        committee: &Committee,
1385    ) -> SuiResult<Self> {
1386        fp_ensure!(
1387            auth_sign_infos.iter().all(|a| a.epoch == committee.epoch),
1388            SuiErrorKind::InvalidSignature {
1389                error: "All signatures must be from the same epoch as the committee".to_string()
1390            }
1391            .into()
1392        );
1393        let total_stake: StakeUnit = auth_sign_infos
1394            .iter()
1395            .map(|a| committee.weight(&a.authority))
1396            .sum();
1397        fp_ensure!(
1398            total_stake >= Self::quorum_threshold(committee),
1399            SuiErrorKind::InvalidSignature {
1400                error: "Signatures don't have enough stake to form a quorum".to_string()
1401            }
1402            .into()
1403        );
1404
1405        let signatures: BTreeMap<_, _> = auth_sign_infos
1406            .into_iter()
1407            .map(|a| (a.authority, a.signature))
1408            .collect();
1409        let mut map = RoaringBitmap::new();
1410        for pk in signatures.keys() {
1411            map.insert(committee.authority_index(pk).ok_or_else(|| {
1412                SuiErrorKind::UnknownSigner {
1413                    signer: Some(pk.concise().to_string()),
1414                    index: None,
1415                    committee: Box::new(committee.clone()),
1416                }
1417            })?);
1418        }
1419        let sigs: Vec<AuthoritySignature> = signatures.into_values().collect();
1420
1421        Ok(AuthorityQuorumSignInfo {
1422            epoch: committee.epoch,
1423            signature: AggregateAuthoritySignature::aggregate(&sigs).map_err(|e| {
1424                SuiErrorKind::InvalidSignature {
1425                    error: e.to_string(),
1426                }
1427            })?,
1428            signers_map: map,
1429        })
1430    }
1431
1432    pub fn authorities<'a>(
1433        &'a self,
1434        committee: &'a Committee,
1435    ) -> impl Iterator<Item = SuiResult<&'a AuthorityName>> {
1436        self.signers_map.iter().map(|i| {
1437            committee
1438                .authority_by_index(i)
1439                .ok_or(SuiErrorKind::InvalidAuthenticator.into())
1440        })
1441    }
1442
1443    pub fn quorum_threshold(committee: &Committee) -> StakeUnit {
1444        committee.threshold::<STRONG_THRESHOLD>()
1445    }
1446
1447    pub fn len(&self) -> u64 {
1448        self.signers_map.len()
1449    }
1450
1451    pub fn is_empty(&self) -> bool {
1452        self.signers_map.is_empty()
1453    }
1454}
1455
1456impl<const S: bool> Display for AuthorityQuorumSignInfo<S> {
1457    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1458        writeln!(
1459            f,
1460            "{} {{ epoch: {:?}, signers_map: {:?} }}",
1461            if S {
1462                "AuthorityStrongQuorumSignInfo"
1463            } else {
1464                "AuthorityWeakQuorumSignInfo"
1465            },
1466            self.epoch,
1467            self.signers_map,
1468        )?;
1469        Ok(())
1470    }
1471}
1472
1473mod private {
1474    pub trait SealedAuthoritySignInfoTrait {}
1475    impl SealedAuthoritySignInfoTrait for super::EmptySignInfo {}
1476    impl SealedAuthoritySignInfoTrait for super::AuthoritySignInfo {}
1477    impl<const S: bool> SealedAuthoritySignInfoTrait for super::AuthorityQuorumSignInfo<S> {}
1478}
1479
1480/// Something that we know how to hash and sign.
1481pub trait Signable<W> {
1482    fn write(&self, writer: &mut W);
1483}
1484
1485pub trait SignableBytes
1486where
1487    Self: Sized,
1488{
1489    fn from_signable_bytes(bytes: &[u8]) -> Result<Self, Error>;
1490}
1491
1492/// Activate the blanket implementation of `Signable` based on serde and BCS.
1493/// * We use `serde_name` to extract a seed from the name of structs and enums.
1494/// * We use `BCS` to generate canonical bytes suitable for hashing and signing.
1495///
1496/// # Safety
1497/// We protect the access to this marker trait through a "sealed trait" pattern:
1498/// impls must be add added here (nowehre else) which lets us note those impls
1499/// MUST be on types that comply with the `serde_name` machinery
1500/// for the below implementations not to panic. One way to check they work is to write
1501/// a unit test for serialization to / deserialization from signable bytes.
1502mod bcs_signable {
1503
1504    pub trait BcsSignable: serde::Serialize + serde::de::DeserializeOwned {}
1505    impl BcsSignable for crate::committee::Committee {}
1506    impl BcsSignable for crate::messages_checkpoint::CheckpointSummary {}
1507    impl BcsSignable for crate::messages_checkpoint::CheckpointContents {}
1508
1509    impl BcsSignable for crate::effects::TransactionEffects {}
1510    impl BcsSignable for crate::effects::TransactionEvents {}
1511    impl BcsSignable for crate::transaction::TransactionData {}
1512    impl BcsSignable for crate::transaction::SenderSignedData {}
1513    impl BcsSignable for crate::object::ObjectInner {}
1514
1515    impl BcsSignable for crate::global_state_hash::GlobalStateHash {}
1516
1517    impl BcsSignable for super::bcs_signable_test::Foo {}
1518    #[cfg(test)]
1519    impl BcsSignable for super::bcs_signable_test::Bar {}
1520}
1521
1522impl<T, W> Signable<W> for T
1523where
1524    T: bcs_signable::BcsSignable,
1525    W: std::io::Write,
1526{
1527    fn write(&self, writer: &mut W) {
1528        let name = serde_name::trace_name::<Self>().expect("Self must be a struct or an enum");
1529        // Note: This assumes that names never contain the separator `::`.
1530        write!(writer, "{}::", name).expect("Hasher should not fail");
1531        bcs::serialize_into(writer, &self).expect("Message serialization should not fail");
1532    }
1533}
1534
1535impl<W> Signable<W> for EpochId
1536where
1537    W: std::io::Write,
1538{
1539    fn write(&self, writer: &mut W) {
1540        bcs::serialize_into(writer, &self).expect("Message serialization should not fail");
1541    }
1542}
1543
1544impl<T> SignableBytes for T
1545where
1546    T: bcs_signable::BcsSignable,
1547{
1548    fn from_signable_bytes(bytes: &[u8]) -> Result<Self, Error> {
1549        // Remove name tag before deserialization using BCS
1550        let name = serde_name::trace_name::<Self>().expect("Self should be a struct or an enum");
1551        let name_byte_len = format!("{}::", name).bytes().len();
1552        Ok(bcs::from_bytes(bytes.get(name_byte_len..).ok_or_else(
1553            || anyhow!("Failed to deserialize to {name}."),
1554        )?)?)
1555    }
1556}
1557
1558fn hash<S: Signable<H>, H: HashFunction<DIGEST_SIZE>, const DIGEST_SIZE: usize>(
1559    signable: &S,
1560) -> [u8; DIGEST_SIZE] {
1561    let mut digest = H::default();
1562    signable.write(&mut digest);
1563    let hash = digest.finalize();
1564    hash.into()
1565}
1566
1567pub fn default_hash<S: Signable<DefaultHash>>(signable: &S) -> [u8; 32] {
1568    hash::<S, DefaultHash, 32>(signable)
1569}
1570
1571#[derive(Default)]
1572pub struct VerificationObligation<'a> {
1573    pub messages: Vec<Vec<u8>>,
1574    pub signatures: Vec<AggregateAuthoritySignature>,
1575    pub public_keys: Vec<Vec<&'a AuthorityPublicKey>>,
1576}
1577
1578impl<'a> VerificationObligation<'a> {
1579    pub fn new() -> VerificationObligation<'a> {
1580        VerificationObligation::default()
1581    }
1582
1583    /// Add a new message to the list of messages to be verified.
1584    /// Returns the index of the message.
1585    pub fn add_message<T>(&mut self, message_value: &T, epoch: EpochId, intent: Intent) -> usize
1586    where
1587        T: Serialize,
1588    {
1589        let intent_msg = IntentMessage::new(intent, message_value);
1590        let mut intent_msg_bytes =
1591            bcs::to_bytes(&intent_msg).expect("Message serialization should not fail");
1592        epoch.write(&mut intent_msg_bytes);
1593        self.signatures.push(AggregateAuthoritySignature::default());
1594        self.public_keys.push(Vec::new());
1595        self.messages.push(intent_msg_bytes);
1596        self.messages.len() - 1
1597    }
1598
1599    // Attempts to add signature and public key to the obligation. If this fails, ensure to call `verify` manually.
1600    pub fn add_signature_and_public_key(
1601        &mut self,
1602        signature: &AuthoritySignature,
1603        public_key: &'a AuthorityPublicKey,
1604        idx: usize,
1605    ) -> SuiResult<()> {
1606        self.public_keys
1607            .get_mut(idx)
1608            .ok_or(SuiErrorKind::InvalidAuthenticator)?
1609            .push(public_key);
1610        self.signatures
1611            .get_mut(idx)
1612            .ok_or(SuiErrorKind::InvalidAuthenticator)?
1613            .add_signature(signature.clone())
1614            .map_err(|_| SuiErrorKind::InvalidSignature {
1615                error: "Failed to add signature to obligation".to_string(),
1616            })?;
1617        Ok(())
1618    }
1619
1620    pub fn verify_all(self) -> SuiResult<()> {
1621        let mut pks = Vec::with_capacity(self.public_keys.len());
1622        for pk in self.public_keys.clone() {
1623            pks.push(pk.into_iter());
1624        }
1625        AggregateAuthoritySignature::batch_verify(
1626            &self.signatures.iter().collect::<Vec<_>>()[..],
1627            pks,
1628            &self.messages.iter().map(|x| &x[..]).collect::<Vec<_>>()[..],
1629        )
1630        .map_err(|e| {
1631            let message = format!(
1632                "pks: {:?}, messages: {:?}, sigs: {:?}",
1633                &self.public_keys,
1634                self.messages
1635                    .iter()
1636                    .map(Base64::encode)
1637                    .collect::<Vec<String>>(),
1638                &self
1639                    .signatures
1640                    .iter()
1641                    .map(|s| Base64::encode(s.as_ref()))
1642                    .collect::<Vec<String>>()
1643            );
1644
1645            let chunk_size = 2048;
1646
1647            // This error message may be very long, so we print out the error in chunks of to avoid
1648            // hitting a max log line length on the system.
1649            for (i, chunk) in message
1650                .as_bytes()
1651                .chunks(chunk_size)
1652                .map(std::str::from_utf8)
1653                .enumerate()
1654            {
1655                warn!(
1656                    "Failed to batch verify aggregated auth sig: {} (chunk {}): {}",
1657                    e,
1658                    i,
1659                    chunk.unwrap()
1660                );
1661            }
1662
1663            SuiErrorKind::InvalidSignature {
1664                error: format!("Failed to batch verify aggregated auth sig: {}", e),
1665            }
1666        })?;
1667        Ok(())
1668    }
1669}
1670
1671pub mod bcs_signable_test {
1672    use serde::{Deserialize, Serialize};
1673
1674    #[derive(Clone, Serialize, Deserialize)]
1675    pub struct Foo(pub String);
1676
1677    #[cfg(test)]
1678    #[derive(Serialize, Deserialize)]
1679    pub struct Bar(pub String);
1680
1681    #[cfg(test)]
1682    use super::VerificationObligation;
1683
1684    #[cfg(test)]
1685    pub fn get_obligation_input<T>(value: &T) -> (VerificationObligation<'_>, usize)
1686    where
1687        T: super::bcs_signable::BcsSignable,
1688    {
1689        use shared_crypto::intent::{Intent, IntentScope};
1690
1691        let mut obligation = VerificationObligation::default();
1692        // Add the obligation of the authority signature verifications.
1693        let idx = obligation.add_message(
1694            value,
1695            0,
1696            Intent::sui_app(IntentScope::SenderSignedTransaction),
1697        );
1698        (obligation, idx)
1699    }
1700}
1701
1702#[derive(
1703    Clone,
1704    Copy,
1705    Deserialize,
1706    Serialize,
1707    JsonSchema,
1708    Debug,
1709    EnumString,
1710    strum_macros::Display,
1711    PartialEq,
1712    Eq,
1713)]
1714#[strum(serialize_all = "lowercase")]
1715pub enum SignatureScheme {
1716    ED25519,
1717    Secp256k1,
1718    Secp256r1,
1719    BLS12381, // This is currently not supported for user Sui Address.
1720    MultiSig,
1721    ZkLoginAuthenticator,
1722    PasskeyAuthenticator,
1723}
1724
1725impl SignatureScheme {
1726    pub fn flag(&self) -> u8 {
1727        match self {
1728            SignatureScheme::ED25519 => 0x00,
1729            SignatureScheme::Secp256k1 => 0x01,
1730            SignatureScheme::Secp256r1 => 0x02,
1731            SignatureScheme::MultiSig => 0x03,
1732            SignatureScheme::BLS12381 => 0x04, // This is currently not supported for user Sui Address.
1733            SignatureScheme::ZkLoginAuthenticator => 0x05,
1734            SignatureScheme::PasskeyAuthenticator => 0x06,
1735        }
1736    }
1737
1738    pub fn from_flag(flag: &str) -> Result<SignatureScheme, SuiError> {
1739        let byte_int = flag
1740            .parse::<u8>()
1741            .map_err(|_| SuiErrorKind::KeyConversionError("Invalid key scheme".to_string()))?;
1742        Self::from_flag_byte(&byte_int)
1743    }
1744
1745    pub fn from_flag_byte(byte_int: &u8) -> Result<SignatureScheme, SuiError> {
1746        match byte_int {
1747            0x00 => Ok(SignatureScheme::ED25519),
1748            0x01 => Ok(SignatureScheme::Secp256k1),
1749            0x02 => Ok(SignatureScheme::Secp256r1),
1750            0x03 => Ok(SignatureScheme::MultiSig),
1751            0x04 => Ok(SignatureScheme::BLS12381),
1752            0x05 => Ok(SignatureScheme::ZkLoginAuthenticator),
1753            0x06 => Ok(SignatureScheme::PasskeyAuthenticator),
1754            _ => Err(SuiErrorKind::KeyConversionError("Invalid key scheme".to_string()).into()),
1755        }
1756    }
1757}
1758/// Unlike [enum Signature], [enum CompressedSignature] does not contain public key.
1759#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1760pub enum CompressedSignature {
1761    Ed25519(Ed25519SignatureAsBytes),
1762    Secp256k1(Secp256k1SignatureAsBytes),
1763    Secp256r1(Secp256r1SignatureAsBytes),
1764    ZkLogin(ZkLoginAuthenticatorAsBytes),
1765    Passkey(PasskeyAuthenticatorAsBytes),
1766}
1767
1768#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1769pub struct ZkLoginAuthenticatorAsBytes(#[schemars(with = "Base64")] pub Vec<u8>);
1770
1771#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1772pub struct PasskeyAuthenticatorAsBytes(#[schemars(with = "Base64")] pub Vec<u8>);
1773
1774impl AsRef<[u8]> for CompressedSignature {
1775    fn as_ref(&self) -> &[u8] {
1776        match self {
1777            CompressedSignature::Ed25519(sig) => &sig.0,
1778            CompressedSignature::Secp256k1(sig) => &sig.0,
1779            CompressedSignature::Secp256r1(sig) => &sig.0,
1780            CompressedSignature::ZkLogin(sig) => &sig.0,
1781            CompressedSignature::Passkey(sig) => &sig.0,
1782        }
1783    }
1784}
1785
1786impl FromStr for Signature {
1787    type Err = eyre::Report;
1788    fn from_str(s: &str) -> Result<Self, Self::Err> {
1789        Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
1790    }
1791}
1792
1793impl FromStr for PublicKey {
1794    type Err = eyre::Report;
1795    fn from_str(s: &str) -> Result<Self, Self::Err> {
1796        Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
1797    }
1798}
1799
1800impl FromStr for GenericSignature {
1801    type Err = eyre::Report;
1802    fn from_str(s: &str) -> Result<Self, Self::Err> {
1803        Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
1804    }
1805}
1806
1807//
1808// Types for randomness generation
1809//
1810pub type RandomnessSignature = fastcrypto_tbls::types::Signature;
1811pub type RandomnessPartialSignature = fastcrypto_tbls::tbls::PartialSignature<RandomnessSignature>;
1812pub type RandomnessPrivateKey =
1813    fastcrypto_tbls::ecies_v1::PrivateKey<fastcrypto::groups::bls12381::G2Element>;
1814
1815/// Round number of generated randomness.
1816#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
1817pub struct RandomnessRound(pub u64);
1818
1819impl Display for RandomnessRound {
1820    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1821        write!(f, "{}", self.0)
1822    }
1823}
1824
1825impl std::ops::Add for RandomnessRound {
1826    type Output = Self;
1827    fn add(self, other: Self) -> Self {
1828        Self(self.0 + other.0)
1829    }
1830}
1831
1832impl std::ops::Add<u64> for RandomnessRound {
1833    type Output = Self;
1834    fn add(self, other: u64) -> Self {
1835        Self(self.0 + other)
1836    }
1837}
1838
1839impl std::ops::Sub for RandomnessRound {
1840    type Output = Self;
1841    fn sub(self, other: Self) -> Self {
1842        Self(self.0 - other.0)
1843    }
1844}
1845
1846impl std::ops::Sub<u64> for RandomnessRound {
1847    type Output = Self;
1848    fn sub(self, other: u64) -> Self {
1849        Self(self.0 - other)
1850    }
1851}
1852
1853impl RandomnessRound {
1854    pub fn new(round: u64) -> Self {
1855        Self(round)
1856    }
1857
1858    pub fn checked_add(self, rhs: u64) -> Option<Self> {
1859        self.0.checked_add(rhs).map(Self)
1860    }
1861
1862    pub fn signature_message(&self) -> Vec<u8> {
1863        "random_beacon round "
1864            .as_bytes()
1865            .iter()
1866            .cloned()
1867            .chain(bcs::to_bytes(&self.0).expect("serialization should not fail"))
1868            .collect()
1869    }
1870}