recall_ipc_api/
checkpoint.rs

1// Copyright 2022-2024 Protocol Labs
2// SPDX-License-Identifier: MIT
3//! Cross network messages related struct and utility functions.
4
5use crate::cross::IpcEnvelope;
6use crate::subnet_id::SubnetID;
7use crate::HumanReadable;
8use cid::multihash::Code;
9use cid::multihash::MultihashDigest;
10use cid::Cid;
11use ethers::utils::hex;
12use fvm_ipld_encoding::DAG_CBOR;
13use fvm_shared::address::Address;
14use fvm_shared::clock::ChainEpoch;
15use fvm_shared::econ::TokenAmount;
16use lazy_static::lazy_static;
17use serde::ser::SerializeSeq;
18use serde::{Deserialize, Serialize, Serializer};
19use serde_with::serde_as;
20use std::fmt::{Display, Formatter};
21
22lazy_static! {
23    // Default CID used for the genesis checkpoint. Using
24    // TCid::default() leads to corrupting the fvm datastore
25    // for storing the cid of an inaccessible HAMT.
26    pub static ref CHECKPOINT_GENESIS_CID: Cid =
27        Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest("genesis".as_bytes()));
28    /// ABI types of the Merkle tree which contains validator addresses and their voting power.
29    pub static ref VALIDATOR_REWARD_FIELDS: Vec<String> = vec!["address".to_owned(), "uint64".to_owned()];
30}
31
32pub type Signature = Vec<u8>;
33
34/// The event emitted
35#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
36pub struct QuorumReachedEvent {
37    pub obj_kind: u8,
38    pub height: ChainEpoch,
39    /// The checkpoint hash
40    pub obj_hash: Vec<u8>,
41    pub quorum_weight: TokenAmount,
42}
43
44impl Display for QuorumReachedEvent {
45    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46        write!(
47            f,
48            "QuorumReachedEvent<height: {}, checkpoint: {}, quorum_weight: {}>",
49            self.height,
50            hex::encode(&self.obj_hash),
51            self.quorum_weight
52        )
53    }
54}
55
56/// The collection of items for the bottom up checkpoint submission
57#[serde_as]
58#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
59pub struct BottomUpCheckpointBundle {
60    pub checkpoint: BottomUpCheckpoint,
61    /// The list of signatures that have signed the checkpoint hash
62    #[serde_as(as = "Vec<HumanReadable>")]
63    pub signatures: Vec<Signature>,
64    /// The list of addresses that have signed the checkpoint hash
65    pub signatories: Vec<Address>,
66}
67
68/// The collection of items for the bottom up checkpoint submission
69#[serde_as]
70#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
71pub struct BottomUpMsgBatch {
72    /// Child subnet ID, for replay protection from other subnets where the exact same validators operate
73    #[serde_as(as = "HumanReadable")]
74    pub subnet_id: SubnetID,
75    /// The height of the child subnet at which the batch was cut
76    pub block_height: ChainEpoch,
77    /// Batch of messages to execute
78    pub msgs: Vec<IpcEnvelope>,
79}
80
81/// Compressed representation of the activity summary that can be embedded in checkpoints to propagate up the hierarchy.
82#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
83pub struct CompressedActivityRollup {
84    pub consensus: consensus::CompressedSummary,
85}
86
87/// Namespace for consensus-level activity summaries.
88/// XYZ(raulk) move to activity module
89pub mod consensus {
90    use fvm_shared::address::Address;
91    use serde::{Deserialize, Serialize};
92
93    /// Aggregated stats for consensus-level activity.
94    #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
95    pub struct AggregatedStats {
96        /// The total number of unique validators that have mined within this period.
97        pub total_active_validators: u64,
98        /// The total number of blocks committed by all validators during this period.
99        pub total_num_blocks_committed: u64,
100    }
101
102    /// The commitments for the child subnet activities that should be submitted to the parent subnet
103    /// together with a bottom up checkpoint
104    #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
105    pub struct ValidatorData {
106        /// The validator address
107        pub validator: Address,
108        /// The number of blocks mined
109        pub blocks_committed: u64,
110    }
111
112    // The full activity summary for consensus-level activity.
113    #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
114    pub struct FullSummary {
115        pub stats: AggregatedStats,
116        /// The breakdown of activity per validator.
117        pub data: Vec<ValidatorData>,
118    }
119
120    /// The compresed representation of the activity summary for consensus-level activity suitable for embedding in a checkpoint.
121    #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
122    pub struct CompressedSummary {
123        pub stats: AggregatedStats,
124        /// The commitment for the validator details, so that we don't have to transmit them in full.
125        pub data_root_commitment: Vec<u8>,
126    }
127}
128
129#[serde_as]
130#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
131pub struct BottomUpCheckpoint {
132    /// Child subnet ID, for replay protection from other subnets where the exact same validators operate.
133    /// Alternatively it can be appended to the hash before signing, similar to how we use the chain ID.
134    pub subnet_id: SubnetID,
135    /// The height of the child subnet at which this checkpoint was cut.
136    /// Has to follow the previous checkpoint by checkpoint period.
137    pub block_height: ChainEpoch,
138    /// The hash of the block.
139    #[serde_as(as = "HumanReadable")]
140    pub block_hash: Vec<u8>,
141    /// The number of the membership (validator set) which is going to sign the next checkpoint.
142    /// This one expected to be signed by the validators from the membership reported in the previous checkpoint.
143    /// 0 could mean "no change".
144    pub next_configuration_number: u64,
145    /// The list of messages for execution
146    pub msgs: Vec<IpcEnvelope>,
147    /// The activity commitment from child subnet to parent subnet
148    pub activity_rollup: CompressedActivityRollup,
149}
150
151pub fn serialize_vec_bytes_to_vec_hex<T: AsRef<[u8]>, S>(
152    data: &[T],
153    s: S,
154) -> Result<S::Ok, S::Error>
155where
156    S: Serializer,
157{
158    let mut seq = s.serialize_seq(Some(data.len()))?;
159    for element in data {
160        seq.serialize_element(&hex::encode(element))?;
161    }
162    seq.end()
163}
164
165#[cfg(test)]
166mod tests {
167    use crate::address::IPCAddress;
168    use crate::checkpoint::Signature;
169    use crate::subnet_id::SubnetID;
170    use crate::HumanReadable;
171    use fvm_shared::address::Address;
172    use serde::{Deserialize, Serialize};
173    use serde_with::serde_as;
174    use std::str::FromStr;
175
176    #[test]
177    fn test_serialization_vec_vec_u8() {
178        #[serde_as]
179        #[derive(Debug, Serialize, Deserialize, PartialEq)]
180        struct T {
181            #[serde_as(as = "Vec<HumanReadable>")]
182            d: Vec<Signature>,
183            #[serde_as(as = "HumanReadable")]
184            subnet_id: SubnetID,
185            #[serde_as(as = "HumanReadable")]
186            ipc_address: IPCAddress,
187        }
188
189        let subnet_id =
190            SubnetID::from_str("/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq").unwrap();
191        let ipc_address = IPCAddress::new(&subnet_id, &Address::new_id(101)).unwrap();
192
193        let t = T {
194            d: vec![vec![1; 30], vec![2; 30]],
195            subnet_id,
196            ipc_address,
197        };
198        let s = serde_json::to_string(&t).unwrap();
199        assert_eq!(
200            s,
201            r#"{"d":["010101010101010101010101010101010101010101010101010101010101","020202020202020202020202020202020202020202020202020202020202"],"subnet_id":"/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq","ipc_address":"/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq:f0101"}"#
202        );
203
204        let r: T = serde_json::from_str(&s).unwrap();
205
206        assert_eq!(r, t);
207    }
208}