iota_sdk_types/
validator.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use super::{Bls12381PublicKey, Bls12381Signature};
6use crate::checkpoint::{EpochId, StakeUnit};
7
8/// The Validator Set for a particular epoch.
9///
10/// # BCS
11///
12/// The BCS serialized form for this type is defined by the following ABNF:
13///
14/// ```text
15/// validator-committee = u64 ; epoch
16///                       (vector validator-committee-member)
17/// ```
18#[derive(Clone, Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
21#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
22pub struct ValidatorCommittee {
23    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
24    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
25    pub epoch: EpochId,
26    pub members: Vec<ValidatorCommitteeMember>,
27}
28
29/// A member of a Validator Committee
30///
31/// # BCS
32///
33/// The BCS serialized form for this type is defined by the following ABNF:
34///
35/// ```text
36/// validator-committee-member = bls-public-key
37///                              u64 ; stake
38/// ```
39#[derive(Clone, Debug, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
42#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
43pub struct ValidatorCommitteeMember {
44    #[cfg_attr(feature = "serde", serde(with = "ValidatorPublicKeySerialization"))]
45    #[cfg_attr(feature = "schemars", schemars(with = "Bls12381PublicKey"))]
46    pub public_key: Bls12381PublicKey,
47    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
48    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
49    pub stake: StakeUnit,
50}
51
52/// An aggregated signature from multiple Validators.
53///
54/// # BCS
55///
56/// The BCS serialized form for this type is defined by the following ABNF:
57///
58/// ```text
59/// validator-aggregated-signature = u64               ; epoch
60///                                  bls-signature
61///                                  roaring-bitmap
62/// roaring-bitmap = bytes  ; where the contents of the bytes are valid
63///                         ; according to the serialized spec for
64///                         ; roaring bitmaps
65/// ```
66///
67/// See [here](https://github.com/RoaringBitmap/RoaringFormatSpec) for the specification for the
68/// serialized format of RoaringBitmaps.
69#[derive(Clone, Debug, PartialEq)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
72#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
73pub struct ValidatorAggregatedSignature {
74    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
75    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
76    pub epoch: EpochId,
77    pub signature: Bls12381Signature,
78    #[cfg_attr(feature = "serde", serde(with = "RoaringBitMapSerialization"))]
79    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))]
80    #[cfg_attr(
81        feature = "proptest",
82        strategy(proptest::strategy::Just(roaring::RoaringBitmap::default()))
83    )]
84    pub bitmap: roaring::RoaringBitmap,
85}
86
87#[cfg(feature = "serde")]
88type RoaringBitMapSerialization = ::serde_with::As<
89    ::serde_with::IfIsHumanReadable<
90        crate::_serde::Base64RoaringBitmap,
91        crate::_serde::BinaryRoaringBitmap,
92    >,
93>;
94
95// Similar to Digest...unfortunately validator's public key material is
96// serialized with the length (96) prefixed
97#[cfg(feature = "serde")]
98type ValidatorPublicKeySerialization = ::serde_with::As<
99    ::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr, BinaryValidatorPublicKey>,
100>;
101
102#[cfg(feature = "serde")]
103struct BinaryValidatorPublicKey;
104
105#[cfg(feature = "serde")]
106impl serde_with::SerializeAs<Bls12381PublicKey> for BinaryValidatorPublicKey {
107    fn serialize_as<S>(source: &Bls12381PublicKey, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: serde::Serializer,
110    {
111        ::serde_with::Bytes::serialize_as(source.inner(), serializer)
112    }
113}
114
115#[cfg(feature = "serde")]
116impl<'de> serde_with::DeserializeAs<'de, Bls12381PublicKey> for BinaryValidatorPublicKey {
117    fn deserialize_as<D>(deserializer: D) -> Result<Bls12381PublicKey, D::Error>
118    where
119        D: serde::Deserializer<'de>,
120    {
121        let bytes: [u8; Bls12381PublicKey::LENGTH] =
122            ::serde_with::Bytes::deserialize_as(deserializer)?;
123        Ok(Bls12381PublicKey::new(bytes))
124    }
125}
126
127/// A signature from a Validator
128///
129/// # BCS
130///
131/// The BCS serialized form for this type is defined by the following ABNF:
132///
133/// ```text
134/// validator-signature = u64               ; epoch
135///                       bls-public-key
136///                       bls-signature
137/// ```
138#[derive(Clone, Debug, PartialEq, Eq)]
139#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
140#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
141#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
142pub struct ValidatorSignature {
143    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
144    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
145    pub epoch: EpochId,
146    #[cfg_attr(feature = "serde", serde(with = "ValidatorPublicKeySerialization"))]
147    #[cfg_attr(feature = "schemars", schemars(with = "Bls12381PublicKey"))]
148    pub public_key: Bls12381PublicKey,
149    pub signature: Bls12381Signature,
150}
151
152#[cfg(test)]
153mod tests {
154    #[cfg(target_arch = "wasm32")]
155    use wasm_bindgen_test::wasm_bindgen_test as test;
156
157    use super::*;
158
159    #[cfg(feature = "serde")]
160    #[test]
161    fn aggregated_signature_fixture() {
162        use base64ct::{Base64, Encoding};
163
164        const FIXTURE: &str = "CgAAAAAAAACZrBcXiqa0ttztfwrBxKzQRzIRnZhbmsQV7tqNXwiZQrRC+dVDbdua1Ety9uy2pCUSOjAAAAEAAAAAAAAAEAAAAAAA";
165        let bcs = Base64::decode_vec(FIXTURE).unwrap();
166
167        let signature: ValidatorAggregatedSignature = bcs::from_bytes(&bcs).unwrap();
168        let bytes = bcs::to_bytes(&signature).unwrap();
169        assert_eq!(bcs, bytes);
170    }
171}