Skip to main content

zombienet_orchestrator/generators/
session_0_overrides.rs

1use array_bytes::bytes2hex;
2use codec::{Decode, Encode};
3use serde_json::json;
4use sp_core::crypto::AccountId32;
5use support::substorage::storage_value_key;
6use tracing::warn;
7
8use crate::generators::errors::GeneratorError;
9
10// Extracted and simplified from polkadot-sdk
11
12/// Index of the validator is used as a lightweight replacement of the `ValidatorId` when
13/// appropriate.
14#[derive(PartialEq, Clone, Encode, Decode, Debug)]
15pub struct ValidatorIndex(pub u32);
16
17/// Simple index type with which we can count sessions.
18pub type SessionIndex = u32;
19
20/// The unique (during session) index of a validator group.
21#[derive(Encode, Decode, Default, Clone, Debug, PartialEq)]
22pub struct GroupIndex(pub u32);
23
24#[derive(Clone, Encode, Decode, Debug, PartialEq)]
25pub struct SessionInfo {
26    /// **** New in v2 ******
27    /// All the validators actively participating in parachain consensus.
28    /// Indices are into the broader validator set.
29    pub active_validator_indices: Vec<ValidatorIndex>,
30    /// A secure random seed for the session, gathered from BABE.
31    pub random_seed: [u8; 32],
32    /// The amount of sessions to keep for disputes.
33    pub dispute_period: SessionIndex,
34
35    /// **** Old fields *****
36    /// Validators in canonical ordering.
37    ///
38    /// NOTE: There might be more authorities in the current session, than `validators`
39    /// participating in parachain consensus. See
40    /// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
41    ///
42    /// `SessionInfo::validators` will be limited to `max_validators` when set.
43    pub validators: Vec<AccountId32>,
44    /// Validators' authority discovery keys for the session in canonical ordering.
45    ///
46    /// NOTE: The first `validators.len()` entries will match the corresponding validators in
47    /// `validators`, afterwards any remaining authorities can be found. This is any authorities
48    /// not participating in parachain consensus - see
49    /// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148)
50    pub discovery_keys: Vec<AccountId32>,
51    /// The assignment keys for validators.
52    ///
53    /// NOTE: There might be more authorities in the current session, than validators participating
54    /// in parachain consensus. See
55    /// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
56    pub assignment_keys: Vec<AccountId32>,
57    /// Validators in shuffled ordering - these are the validator groups as produced
58    /// by the `Scheduler` module for the session and are typically referred to by
59    /// `GroupIndex`.
60    pub validator_groups: Vec<Vec<ValidatorIndex>>,
61    /// The number of availability cores used by the protocol during this session.
62    pub n_cores: u32,
63    /// The zeroth delay tranche width.
64    pub zeroth_delay_tranche_width: u32,
65    /// The number of samples we do of `relay_vrf_modulo`.
66    pub relay_vrf_modulo_samples: u32,
67    /// The number of delay tranches in total.
68    pub n_delay_tranches: u32,
69    /// How many slots (BABE / SASSAFRAS) must pass before an assignment is considered a
70    /// no-show.
71    pub no_show_slots: u32,
72    /// The number of validators needed to approve a block.
73    pub needed_approvals: u32,
74}
75
76pub fn generate_session_0_overrides(
77    raw_spec: &serde_json::Value,
78    num_genesis_cores: u32,
79) -> Result<serde_json::Value, GeneratorError> {
80    let mut overrides = json!({});
81    if num_genesis_cores == 0 {
82        warn!("'num_genesis_cores' is 0, means that we can not override session 0. Please check your config to ensure you have paras to register in chain-spec.");
83        return Ok(overrides);
84    }
85    // get current session 0
86    let sessions_prefix = storage_value_key(&b"ParaSessionInfo"[..], b"Sessions");
87    let session_0_key = format!(
88        "{}{}",
89        bytes2hex("0x", &sessions_prefix),
90        bytes2hex("", 0_u32.encode())
91    );
92
93    let current_value = &raw_spec["genesis"]["raw"]["top"][&session_0_key];
94    let Some(current_value_inner) = current_value.as_str() else {
95        return Err(GeneratorError::OverridingRawSpec(format!(
96            "Session_0 keys {} is missing (in genesis.raw.top)",
97            session_0_key
98        )));
99    };
100
101    let encoded = hex::decode(&current_value_inner[2..]).map_err(|e| {
102        GeneratorError::EncodeDecodeError(format!(
103            "Error decoding hex: {}, err: {e}",
104            current_value_inner
105        ))
106    })?;
107    let mut session: SessionInfo = SessionInfo::decode(&mut encoded.as_slice()).map_err(|e| {
108        GeneratorError::EncodeDecodeError(format!("Error decoding scale: {:?}, err: {e}", encoded))
109    })?;
110
111    // clone keys
112    session.assignment_keys = session.validators.clone();
113    session.discovery_keys = session.validators.clone();
114
115    // generate validator groups
116
117    // some checks first
118    if num_genesis_cores > session.validators.len() as u32 {
119        return Err(GeneratorError::InvariantError(format!("Num cores in genesis {num_genesis_cores} should be less than or equal to the num of validators ({})", session.validators.len())));
120    }
121
122    let groups = genetate_groups(session.validators.len() as u32, num_genesis_cores);
123    session.validator_groups = groups.clone();
124    session.n_cores = num_genesis_cores;
125
126    // done with session
127    let session_0_value = bytes2hex("0x", session.encode());
128    overrides[session_0_key] = json!(session_0_value);
129
130    // paraScheduler.validatorGroups: Vec<Vec<u32>>
131    let para_scheduler_validator_groups_key = bytes2hex(
132        "0x",
133        storage_value_key(&b"ParaScheduler"[..], b"ValidatorGroups"),
134    );
135
136    overrides[para_scheduler_validator_groups_key] = json!(bytes2hex("0x", groups.encode()));
137
138    Ok(overrides)
139}
140
141fn genetate_groups(num_validators: u32, num_cores: u32) -> Vec<Vec<ValidatorIndex>> {
142    let iter = std::iter::repeat_n(vec![], num_cores as usize);
143    let mut groups: Vec<Vec<ValidatorIndex>> = Vec::from_iter(iter);
144    for i in 0..num_validators {
145        let index = i % num_cores;
146        let group = groups.get_mut(index as usize).expect(&format!(
147            "Group index {index} should be part of groups. qed"
148        ));
149        group.push(ValidatorIndex(i));
150    }
151
152    groups
153}
154
155#[cfg(test)]
156mod test {
157    use std::assert_eq;
158
159    use tracing::debug;
160
161    use super::*;
162
163    #[test]
164    fn decode_encode_should_work() {
165        use support::substorage::storage_value_key;
166
167        let k = storage_value_key(&b"ParaSessionInfo"[..], b"Sessions");
168        debug!("k: {}{}", bytes2hex("", &k), bytes2hex("", 0_u32.encode()));
169
170        let encoded = hex::decode( "1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a06000000108eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000000000000000000000000000000010000000100000000000000").unwrap();
171        let mut session: SessionInfo = SessionInfo::decode(&mut encoded.as_slice()).unwrap();
172
173        debug!("{session:?}");
174
175        session.assignment_keys = session.validators.clone();
176        session.discovery_keys = session.validators.clone();
177
178        let encoded = session.encode();
179        debug!("{}", bytes2hex("", &encoded));
180
181        let session_modified = SessionInfo::decode(&mut &encoded[..]).unwrap();
182
183        debug!("{session_modified:?}");
184    }
185
186    #[test]
187    fn val_groups() {
188        let num_cores = 3_u32;
189        let validators = ["abc", "cds", "qwe", "eds"];
190        let groups = genetate_groups(validators.len() as u32, num_cores);
191
192        debug!("{:?}", groups);
193        assert_eq!(groups.len(), num_cores as usize);
194    }
195
196    #[test]
197    fn generate_should_work() {
198        let sessions_prefix = storage_value_key(&b"ParaSessionInfo"[..], b"Sessions");
199        let session_0_key = format!(
200            "{}{}",
201            bytes2hex("0x", &sessions_prefix),
202            bytes2hex("", 0_u32.encode())
203        );
204
205        let session_value = "0x1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a06000000108eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000000000000000000000000000000010000000100000000000000";
206        let mock_spec = json!({
207            "genesis": {
208                "raw": {
209                    "top": {
210                        session_0_key: session_value
211                    }
212                }
213            }
214        });
215
216        debug!("mock {:?}", mock_spec);
217
218        let overrides = generate_session_0_overrides(&mock_spec, 3).unwrap();
219        debug!("{:?}", overrides);
220        // ensure we have 2 keys to override
221        assert_eq!(overrides.as_object().unwrap().keys().len(), 2);
222    }
223
224    #[test]
225    fn generate_should_work_without_cores() {
226        let sessions_prefix = storage_value_key(&b"ParaSessionInfo"[..], b"Sessions");
227        let session_0_key = format!(
228            "{}{}",
229            bytes2hex("0x", &sessions_prefix),
230            bytes2hex("", 0_u32.encode())
231        );
232
233        let session_value = "0x1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a06000000108eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20000000000000000000000000000000010000000100000000000000";
234        let mock_spec = json!({
235            "genesis": {
236                "raw": {
237                    "top": {
238                        session_0_key: session_value
239                    }
240                }
241            }
242        });
243
244        debug!("mock {:?}", mock_spec);
245
246        let overrides = generate_session_0_overrides(&mock_spec, 0).unwrap();
247        debug!("{:?}", overrides);
248        assert_eq!(overrides.as_object().unwrap().keys().len(), 0);
249    }
250}