Skip to main content

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, Eq, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
20#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
21#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
22pub struct ValidatorCommittee {
23    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
24    #[cfg_attr(feature = "bcs-schema", bcs_schema(as_type = "u64"))]
25    pub epoch: EpochId,
26    pub members: Vec<ValidatorCommitteeMember>,
27}
28
29impl crate::TreeDisplay for ValidatorCommittee {
30    fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
31        w.header("Validator Committee")?;
32        w.leaf("Epoch", &self.epoch, false)?;
33        w.children("Members", &self.members, true)
34    }
35}
36
37/// A member of a Validator Committee
38///
39/// # BCS
40///
41/// The BCS serialized form for this type is defined by the following ABNF:
42///
43/// ```text
44/// validator-committee-member = bls12381-public-key
45///                              u64 ; stake
46/// ```
47#[derive(Clone, Debug, Eq, PartialEq)]
48#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
49#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
50#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
51pub struct ValidatorCommitteeMember {
52    #[cfg_attr(feature = "serde", serde(with = "ValidatorPublicKeySerialization"))]
53    pub public_key: Bls12381PublicKey,
54    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
55    #[cfg_attr(feature = "bcs-schema", bcs_schema(as_type = "u64"))]
56    pub stake: StakeUnit,
57}
58
59impl crate::TreeDisplay for ValidatorCommitteeMember {
60    fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
61        w.header("Validator Committee Member")?;
62        w.leaf("Public Key", &self.public_key, false)?;
63        w.leaf("Stake", &self.stake, true)
64    }
65}
66
67/// An aggregated signature from multiple Validators.
68///
69/// # BCS
70///
71/// The BCS serialized form for this type is defined by the following ABNF:
72///
73/// ```text
74/// validator-aggregated-signature = u64                  ; epoch
75///                                  bls12381-signature   ; signature
76///                                  bytes                ; bitmap — contents of the bytes are
77///                                                       ; valid according to the serialized
78///                                                       ; spec for roaring bitmaps
79/// ```
80///
81/// See [here](https://github.com/RoaringBitmap/RoaringFormatSpec) for the specification for the
82/// serialized format of RoaringBitmaps.
83#[derive(Clone, Debug, PartialEq)]
84#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
85#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
86#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
87pub struct ValidatorAggregatedSignature {
88    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
89    #[cfg_attr(feature = "bcs-schema", bcs_schema(as_type = "u64"))]
90    pub epoch: EpochId,
91    pub signature: Bls12381Signature,
92    #[cfg_attr(feature = "serde", serde(with = "RoaringBitMapSerialization"))]
93    #[cfg_attr(
94        feature = "proptest",
95        strategy(proptest::strategy::Just(roaring::RoaringBitmap::default()))
96    )]
97    #[cfg_attr(feature = "bcs-schema", bcs_schema(as_type = "bytes"))]
98    pub bitmap: roaring::RoaringBitmap,
99}
100
101impl crate::TreeDisplay for ValidatorAggregatedSignature {
102    fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
103        w.header("Validator Aggregated Signature")?;
104        w.leaf("Epoch", &self.epoch, false)?;
105        w.leaf("Signature", &self.signature, false)?;
106        w.leaf("Bitmap", &format!("{:?}", self.bitmap), true)
107    }
108}
109
110#[cfg(feature = "serde")]
111type RoaringBitMapSerialization = ::serde_with::As<
112    ::serde_with::IfIsHumanReadable<
113        crate::_serde::Base64RoaringBitmap,
114        crate::_serde::BinaryRoaringBitmap,
115    >,
116>;
117
118// Similar to Digest...unfortunately validator's public key material is
119// serialized with the length (96) prefixed
120#[cfg(feature = "serde")]
121type ValidatorPublicKeySerialization = ::serde_with::As<
122    ::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr, BinaryValidatorPublicKey>,
123>;
124
125#[cfg(feature = "serde")]
126struct BinaryValidatorPublicKey;
127
128#[cfg(feature = "serde")]
129impl serde_with::SerializeAs<Bls12381PublicKey> for BinaryValidatorPublicKey {
130    fn serialize_as<S>(source: &Bls12381PublicKey, serializer: S) -> Result<S::Ok, S::Error>
131    where
132        S: serde::Serializer,
133    {
134        ::serde_with::Bytes::serialize_as(source.inner(), serializer)
135    }
136}
137
138#[cfg(feature = "serde")]
139impl<'de> serde_with::DeserializeAs<'de, Bls12381PublicKey> for BinaryValidatorPublicKey {
140    fn deserialize_as<D>(deserializer: D) -> Result<Bls12381PublicKey, D::Error>
141    where
142        D: serde::Deserializer<'de>,
143    {
144        let bytes: [u8; Bls12381PublicKey::LENGTH] =
145            ::serde_with::Bytes::deserialize_as(deserializer)?;
146        Ok(Bls12381PublicKey::new(bytes))
147    }
148}
149
150/// A signature from a Validator
151///
152/// # BCS
153///
154/// The BCS serialized form for this type is defined by the following ABNF:
155///
156/// ```text
157/// validator-signature = u64                  ; epoch
158///                       bls12381-public-key
159///                       bls12381-signature
160/// ```
161#[derive(Clone, Debug, Eq, PartialEq)]
162#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
163#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
164pub struct ValidatorSignature {
165    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
166    pub epoch: EpochId,
167    #[cfg_attr(feature = "serde", serde(with = "ValidatorPublicKeySerialization"))]
168    pub public_key: Bls12381PublicKey,
169    pub signature: Bls12381Signature,
170}
171
172impl crate::TreeDisplay for ValidatorSignature {
173    fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
174        w.header("Validator Signature")?;
175        w.leaf("Epoch", &self.epoch, false)?;
176        w.leaf("Public Key", &self.public_key, false)?;
177        w.leaf("Signature", &self.signature, true)
178    }
179}
180
181crate::impl_tree_display!(
182    ValidatorCommittee,
183    ValidatorCommitteeMember,
184    ValidatorAggregatedSignature,
185    ValidatorSignature
186);
187
188#[cfg(all(test, feature = "serde"))]
189mod tests {
190    #[cfg(target_arch = "wasm32")]
191    use wasm_bindgen_test::wasm_bindgen_test as test;
192
193    use super::*;
194
195    #[test]
196    fn aggregated_signature_fixture() {
197        use base64ct::{Base64, Encoding};
198
199        const FIXTURE: &str = "CgAAAAAAAACZrBcXiqa0ttztfwrBxKzQRzIRnZhbmsQV7tqNXwiZQrRC+dVDbdua1Ety9uy2pCUSOjAAAAEAAAAAAAAAEAAAAAAA";
200        let bcs = Base64::decode_vec(FIXTURE).unwrap();
201
202        let signature: ValidatorAggregatedSignature = bcs::from_bytes(&bcs).unwrap();
203        let bytes = bcs::to_bytes(&signature).unwrap();
204        assert_eq!(bcs, bytes);
205    }
206}