zombienet_orchestrator/generators/
jam_config.rs1use configuration::types::JamNodeMode;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 generators::{chain_spec::merge, errors::GeneratorError},
6 network_spec::jamchain::JamchainSpec,
7};
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
49pub struct GenesisValidator {
50 pub peer_id: String,
51 pub bandersnatch: String,
52 pub net_addr: String,
53}
54
55#[derive(Debug, Clone, Default, Serialize, Deserialize)]
56pub struct GenesisConfig {
57 pub id: String,
58 pub genesis_validators: Vec<GenesisValidator>,
59 #[serde(flatten)]
61 pub overrides: serde_json::Map<String, serde_json::Value>,
62}
63
64pub fn generate(jam_spec: &JamchainSpec) -> Result<GenesisConfig, GeneratorError> {
65 let genesis_validators = jam_spec
66 .nodes
67 .iter()
68 .filter_map(|n| {
69 if let JamNodeMode::Validator = n.mode {
70 Some(GenesisValidator {
71 peer_id: n.peer_id.clone(),
72 bandersnatch: n
73 .accounts
74 .accounts
75 .get("bandersnatch")
76 .expect("bandersnatch should be present.")
77 .public_key
78 .clone(),
79 net_addr: format!("127.0.0.1:{}", n.port.0),
83 })
84 } else {
85 None
86 }
87 })
88 .collect();
89
90 let gen_config = GenesisConfig {
91 id: jam_spec.id.as_str().into(),
92 genesis_validators,
93 overrides: Default::default(),
94 };
95
96 let Some(overrides) = &jam_spec.genesis_overrides else {
97 return Ok(gen_config);
98 };
99 let mut merged = serde_json::to_value(gen_config).map_err(encode_error)?;
101 merge(&mut merged, overrides);
102 serde_json::from_value(merged).map_err(encode_error)
103}
104
105fn encode_error(error: serde_json::Error) -> GeneratorError {
106 GeneratorError::EncodeDecodeError(error.to_string())
107}
108
109#[cfg(test)]
110mod tests {
111 use configuration::types::Chain;
112
113 use super::*;
114 use crate::{
115 network_spec::jamnode::JamNodeSpec,
116 shared::types::{NodeAccount, NodeAccounts, ParkedPort},
117 };
118
119 fn jam_node(name: &str, mode: JamNodeMode, p2p_port: u16, rpc_port: u16) -> JamNodeSpec {
120 JamNodeSpec {
121 name: name.to_string(),
122 mode,
123 peer_id: format!("peer-{name}"),
124 accounts: NodeAccounts {
125 seed: name.to_string(),
126 accounts: [(
127 "bandersnatch".to_string(),
128 NodeAccount::new(format!("addr-{name}"), format!("pub-{name}")),
129 )]
130 .into(),
131 },
132 port: ParkedPort(p2p_port, Default::default()),
133 rpc_port: ParkedPort(rpc_port, Default::default()),
134 ..Default::default()
135 }
136 }
137
138 fn jam_spec(nodes: Vec<JamNodeSpec>) -> JamchainSpec {
139 JamchainSpec {
140 id: Chain::try_from("dev").unwrap(),
141 default_command: None,
142 default_image: None,
143 default_resources: None,
144 default_args: vec![],
145 chain_spec_command: String::new(),
146 nodes,
147 genesis_overrides: None,
148 }
149 }
150
151 const ALICE_ALONE: &str = r#"{
154 "id": "dev",
155 "genesis_validators": [
156 {
157 "peer_id": "peer-alice",
158 "bandersnatch": "pub-alice",
159 "net_addr": "127.0.0.1:40001"
160 }
161 ]
162}"#;
163
164 fn alice_alone(genesis_overrides: Option<serde_json::Value>) -> JamchainSpec {
165 JamchainSpec {
166 genesis_overrides,
167 ..jam_spec(vec![jam_node("alice", JamNodeMode::Validator, 40001, 9944)])
168 }
169 }
170
171 fn written(spec: &JamchainSpec) -> String {
172 serde_json::to_string_pretty(&generate(spec).unwrap()).unwrap()
173 }
174
175 #[test]
179 fn genesis_overrides_are_added_to_the_generated_config() {
180 let overrides = serde_json::json!({
181 "services": [{
182 "id": 5,
183 "code": "/blobs/parasim-service.jam",
184 "balance": "18446744073709551615",
185 }],
186 "auth_queues": { "0": "2bbda8cb" },
187 "assigners": { "0": 5 },
188 "privileges": { "bless": 0, "assign": { "0": 5 } },
189 });
190
191 let config: serde_json::Value =
192 serde_json::from_str(&written(&alice_alone(Some(overrides.clone())))).unwrap();
193
194 for key in ["services", "auth_queues", "assigners", "privileges"] {
195 assert_eq!(
196 config[key], overrides[key],
197 "{key} did not reach the config"
198 );
199 }
200 assert_eq!(config["id"], "dev");
201 assert_eq!(
202 config["genesis_validators"][0]["net_addr"],
203 "127.0.0.1:40001"
204 );
205 }
206
207 #[test]
210 fn without_overrides_the_config_is_written_exactly_as_before() {
211 assert_eq!(written(&alice_alone(None)), ALICE_ALONE);
212 assert_eq!(
213 written(&alice_alone(Some(serde_json::json!({})))),
214 ALICE_ALONE
215 );
216 }
217
218 #[test]
221 fn genesis_validators_are_addressed_by_their_p2p_port() {
222 let spec = jam_spec(vec![
223 jam_node("alice", JamNodeMode::Validator, 40001, 9944),
224 jam_node("bob", JamNodeMode::Validator, 40002, 9945),
225 ]);
226
227 let config = generate(&spec).unwrap();
228
229 let addrs: Vec<&str> = config
230 .genesis_validators
231 .iter()
232 .map(|v| v.net_addr.as_str())
233 .collect();
234 assert_eq!(addrs, vec!["127.0.0.1:40001", "127.0.0.1:40002"]);
235 }
236
237 #[test]
238 fn only_validators_are_included_in_genesis() {
239 let spec = jam_spec(vec![
240 jam_node("alice", JamNodeMode::Validator, 40001, 9944),
241 jam_node("dave", JamNodeMode::Ordinary, 40002, 9945),
242 jam_node("eve", JamNodeMode::Proxy, 40003, 9946),
243 ]);
244
245 let config = generate(&spec).unwrap();
246
247 assert_eq!(config.id, "dev");
248 assert_eq!(config.genesis_validators.len(), 1);
249 assert_eq!(config.genesis_validators[0].peer_id, "peer-alice");
250 }
251}