1#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6use crate::pallet::{
7 DomainRuntimeUpgrades, NextRuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades,
8};
9use crate::{BalanceOf, Config, Event};
10#[cfg(not(feature = "std"))]
11use alloc::string::String;
12#[cfg(not(feature = "std"))]
13use alloc::vec::Vec;
14use domain_runtime_primitives::{AccountId20, MultiAccountId, TryConvertBack};
15use frame_support::{PalletError, ensure};
16use frame_system::AccountInfo;
17use frame_system::pallet_prelude::*;
18use parity_scale_codec::{Decode, Encode};
19use scale_info::TypeInfo;
20use sp_core::Hasher;
21use sp_core::crypto::AccountId32;
22use sp_domains::storage::{RawGenesis, StorageData, StorageKey};
23use sp_domains::{
24 AutoIdDomainRuntimeConfig, DomainId, DomainRuntimeInfo, DomainsDigestItem, RuntimeId,
25 RuntimeObject, RuntimeType,
26};
27use sp_runtime::DigestItem;
28use sp_runtime::traits::{CheckedAdd, Zero};
29use sp_std::vec;
30use sp_version::RuntimeVersion;
31
32#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)]
34pub enum Error {
35 FailedToExtractRuntimeVersion,
36 InvalidSpecName,
37 SpecVersionNeedsToIncrease,
38 MaxRuntimeId,
39 MissingRuntimeObject,
40 RuntimeUpgradeAlreadyScheduled,
41 MaxScheduledBlockNumber,
42 FailedToDecodeRawGenesis,
43 RuntimeCodeNotFoundInRawGenesis,
44 InvalidAccountIdType,
45}
46
47#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
48pub struct DomainRuntimeUpgradeEntry<Hash> {
49 pub at_hash: Hash,
51 pub reference_count: u32,
55}
56
57fn derive_initial_balances_storages<T: Config, AccountId: Encode>(
58 total_issuance: BalanceOf<T>,
59 balances: Vec<(AccountId, BalanceOf<T>)>,
60) -> Vec<(StorageKey, StorageData)> {
61 let total_issuance_key = sp_domains::domain_total_issuance_storage_key();
62 let mut initial_storages = vec![(total_issuance_key, StorageData(total_issuance.encode()))];
63 for (account_id, balance) in balances {
64 let account_storage_key = sp_domains::domain_account_storage_key(account_id);
65 let account_info = AccountInfo {
66 nonce: domain_runtime_primitives::Nonce::zero(),
67 consumers: 0,
68 providers: 1,
70 sufficients: 0,
71 data: pallet_balances::AccountData {
72 free: balance,
73 ..Default::default()
74 },
75 };
76 initial_storages.push((account_storage_key, StorageData(account_info.encode())))
77 }
78
79 initial_storages
80}
81
82pub fn into_complete_raw_genesis<T: Config>(
84 runtime_obj: RuntimeObject<BlockNumberFor<T>, T::Hash>,
85 domain_id: DomainId,
86 domain_runtime_info: &DomainRuntimeInfo,
87 total_issuance: BalanceOf<T>,
88 initial_balances: Vec<(MultiAccountId, BalanceOf<T>)>,
89) -> Result<RawGenesis, Error> {
90 let RuntimeObject {
91 mut raw_genesis, ..
92 } = runtime_obj;
93 raw_genesis.set_domain_id(domain_id);
94 match domain_runtime_info {
95 DomainRuntimeInfo::Evm {
96 chain_id,
97 domain_runtime_config,
98 } => {
99 raw_genesis.set_evm_chain_id(*chain_id);
100 if let Some(initial_contract_creation_allow_list) = domain_runtime_config
101 .evm_type
102 .initial_contract_creation_allow_list()
103 {
104 raw_genesis
105 .set_evm_contract_creation_allowed_by(initial_contract_creation_allow_list);
106 }
107
108 let initial_balances = initial_balances.into_iter().try_fold(
109 Vec::<(AccountId20, BalanceOf<T>)>::new(),
110 |mut balances, (account_id, balance)| {
111 let account_id =
112 domain_runtime_primitives::AccountId20Converter::try_convert_back(
113 account_id,
114 )
115 .ok_or(Error::InvalidAccountIdType)?;
116
117 balances.push((account_id, balance));
118 Ok(balances)
119 },
120 )?;
121 raw_genesis.set_top_storages(derive_initial_balances_storages::<T, _>(
122 total_issuance,
123 initial_balances,
124 ));
125 }
126 DomainRuntimeInfo::AutoId {
127 domain_runtime_config: AutoIdDomainRuntimeConfig {},
128 } => {
129 let initial_balances = initial_balances.into_iter().try_fold(
130 Vec::<(AccountId32, BalanceOf<T>)>::new(),
131 |mut balances, (account_id, balance)| {
132 let account_id =
133 domain_runtime_primitives::AccountIdConverter::try_convert_back(account_id)
134 .ok_or(Error::InvalidAccountIdType)?;
135
136 balances.push((account_id, balance));
137 Ok(balances)
138 },
139 )?;
140 raw_genesis.set_top_storages(derive_initial_balances_storages::<T, _>(
141 total_issuance,
142 initial_balances,
143 ));
144 }
145 }
146
147 Ok(raw_genesis)
148}
149
150#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
151pub struct ScheduledRuntimeUpgrade<Hash> {
152 pub raw_genesis: RawGenesis,
153 pub version: RuntimeVersion,
154 pub hash: Hash,
155}
156
157pub(crate) fn runtime_version(code: &[u8]) -> Result<RuntimeVersion, Error> {
159 sp_io::misc::runtime_version(code)
160 .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok())
161 .ok_or(Error::FailedToExtractRuntimeVersion)
162}
163
164pub(crate) fn can_upgrade_code(
167 current_version: &RuntimeVersion,
168 update_code: &[u8],
169) -> Result<RuntimeVersion, Error> {
170 let new_version = runtime_version(update_code)?;
171
172 if new_version.spec_name != current_version.spec_name {
173 return Err(Error::InvalidSpecName);
174 }
175
176 if new_version.spec_version <= current_version.spec_version {
177 return Err(Error::SpecVersionNeedsToIncrease);
178 }
179
180 Ok(new_version)
181}
182
183pub(crate) fn do_register_runtime<T: Config>(
185 runtime_name: String,
186 runtime_type: RuntimeType,
187 raw_genesis_storage: Vec<u8>,
188 at: BlockNumberFor<T>,
189) -> Result<RuntimeId, Error> {
190 let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
191 .map_err(|_| Error::FailedToDecodeRawGenesis)?;
192
193 let code = raw_genesis
194 .get_runtime_code()
195 .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
196
197 let version = runtime_version(code)?;
198 let runtime_hash = T::Hashing::hash(code);
199 let runtime_id = NextRuntimeId::<T>::get();
200
201 RuntimeRegistry::<T>::insert(
202 runtime_id,
203 RuntimeObject {
204 runtime_name,
205 runtime_type,
206 hash: runtime_hash,
207 raw_genesis,
208 version,
209 created_at: at,
210 updated_at: at,
211 runtime_upgrades: 0u32,
212 instance_count: 0,
213 },
214 );
215
216 let next_runtime_id = runtime_id.checked_add(1).ok_or(Error::MaxRuntimeId)?;
217 NextRuntimeId::<T>::set(next_runtime_id);
218
219 Ok(runtime_id)
220}
221
222pub(crate) fn register_runtime_at_genesis<T: Config>(
225 runtime_name: String,
226 runtime_type: RuntimeType,
227 runtime_version: RuntimeVersion,
228 raw_genesis_storage: Vec<u8>,
229 at: BlockNumberFor<T>,
230) -> Result<RuntimeId, Error> {
231 let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
232 .map_err(|_| Error::FailedToDecodeRawGenesis)?;
233
234 let code = raw_genesis
235 .get_runtime_code()
236 .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
237
238 let runtime_hash = T::Hashing::hash(code);
239 let runtime_id = NextRuntimeId::<T>::get();
240
241 RuntimeRegistry::<T>::insert(
242 runtime_id,
243 RuntimeObject {
244 runtime_name,
245 runtime_type,
246 hash: runtime_hash,
247 raw_genesis,
248 version: runtime_version,
249 created_at: at,
250 updated_at: at,
251 runtime_upgrades: 0u32,
252 instance_count: 0,
253 },
254 );
255
256 let next_runtime_id = runtime_id.checked_add(1).ok_or(Error::MaxRuntimeId)?;
257 NextRuntimeId::<T>::set(next_runtime_id);
258
259 Ok(runtime_id)
260}
261
262pub(crate) fn do_schedule_runtime_upgrade<T: Config>(
264 runtime_id: RuntimeId,
265 raw_genesis_storage: Vec<u8>,
266 current_block_number: BlockNumberFor<T>,
267) -> Result<BlockNumberFor<T>, Error> {
268 let runtime_obj = RuntimeRegistry::<T>::get(runtime_id).ok_or(Error::MissingRuntimeObject)?;
269
270 let new_raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
271 .map_err(|_| Error::FailedToDecodeRawGenesis)?;
272
273 let new_code = new_raw_genesis
274 .get_runtime_code()
275 .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
276
277 let new_runtime_version = can_upgrade_code(&runtime_obj.version, new_code)?;
278 let new_runtime_hash = T::Hashing::hash(new_code);
279 let scheduled_upgrade = ScheduledRuntimeUpgrade {
280 raw_genesis: new_raw_genesis,
281 version: new_runtime_version,
282 hash: new_runtime_hash,
283 };
284 let scheduled_at = current_block_number
286 .checked_add(&BlockNumberFor::<T>::from(1u32))
287 .ok_or(Error::MaxScheduledBlockNumber)?;
288
289 ensure!(
290 !ScheduledRuntimeUpgrades::<T>::contains_key(scheduled_at, runtime_id),
291 Error::RuntimeUpgradeAlreadyScheduled
292 );
293
294 ScheduledRuntimeUpgrades::<T>::insert(scheduled_at, runtime_id, scheduled_upgrade);
295
296 Ok(scheduled_at)
297}
298
299pub(crate) fn do_upgrade_runtimes<T: Config>(at: BlockNumberFor<T>) {
300 for (runtime_id, scheduled_update) in ScheduledRuntimeUpgrades::<T>::drain_prefix(at) {
301 RuntimeRegistry::<T>::mutate(runtime_id, |maybe_runtime_object| {
302 let runtime_obj = maybe_runtime_object
303 .as_mut()
304 .expect("Runtime object exists since an upgrade is scheduled after verification");
305
306 runtime_obj.raw_genesis = scheduled_update.raw_genesis;
307 runtime_obj.version = scheduled_update.version;
308 runtime_obj.hash = scheduled_update.hash;
309 runtime_obj.runtime_upgrades = runtime_obj.runtime_upgrades.saturating_add(1);
310 runtime_obj.updated_at = at;
311 });
312
313 DomainRuntimeUpgrades::<T>::mutate(|upgrades| upgrades.push(runtime_id));
315
316 frame_system::Pallet::<T>::deposit_log(DigestItem::domain_runtime_upgrade(runtime_id));
318
319 frame_system::Pallet::<T>::deposit_event(<T as Config>::RuntimeEvent::from(
321 Event::DomainRuntimeUpgraded { runtime_id },
322 ));
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use crate::Error;
329 use crate::mock::{Domains, System, Test};
330 use crate::pallet::{NextRuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades};
331 use crate::runtime_registry::Error as RuntimeRegistryError;
332 use crate::tests::{ReadRuntimeVersion, TEST_RUNTIME_APIS, new_test_ext};
333 use domain_runtime_primitives::Hash;
334 use frame_support::dispatch::RawOrigin;
335 use frame_support::traits::OnInitialize;
336 use frame_support::{assert_err, assert_ok};
337 use parity_scale_codec::{Decode, Encode};
338 use sp_domains::storage::RawGenesis;
339 use sp_domains::{DomainsDigestItem, RuntimeId, RuntimeObject, RuntimeType};
340 use sp_runtime::traits::BlockNumberProvider;
341 use sp_runtime::{Digest, DispatchError};
342 use sp_version::{RuntimeVersion, create_apis_vec};
343
344 #[test]
345 fn create_domain_runtime() {
346 let version = RuntimeVersion {
347 spec_name: "test".into(),
348 impl_name: Default::default(),
349 authoring_version: 0,
350 spec_version: 1,
351 impl_version: 1,
352 apis: Default::default(),
353 transaction_version: 1,
354 system_version: 0,
355 };
356 let read_runtime_version = ReadRuntimeVersion(version.encode());
357
358 let mut ext = new_test_ext();
359 ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
360 read_runtime_version,
361 ));
362 ext.execute_with(|| {
363 let raw_genesis_storage = RawGenesis::dummy(vec![1, 2, 3, 4]).encode();
364 let res = crate::Pallet::<Test>::register_domain_runtime(
365 RawOrigin::Root.into(),
366 "evm".to_owned(),
367 RuntimeType::Evm,
368 raw_genesis_storage,
369 );
370
371 assert_ok!(res);
372 let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
373 assert_eq!(runtime_obj.version, version);
374 assert_eq!(NextRuntimeId::<Test>::get(), 1)
375 })
376 }
377
378 #[test]
379 fn schedule_domain_runtime_upgrade() {
380 let mut ext = new_test_ext();
381 ext.execute_with(|| {
382 RuntimeRegistry::<Test>::insert(
383 0,
384 RuntimeObject {
385 runtime_name: "evm".to_owned(),
386 runtime_type: Default::default(),
387 runtime_upgrades: 0,
388 hash: Default::default(),
389 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
390 version: RuntimeVersion {
391 spec_name: "test".into(),
392 spec_version: 1,
393 impl_version: 1,
394 transaction_version: 1,
395 apis: create_apis_vec!(TEST_RUNTIME_APIS),
396 ..Default::default()
397 },
398 created_at: Default::default(),
399 updated_at: Default::default(),
400 instance_count: 0,
401 },
402 );
403
404 NextRuntimeId::<Test>::set(1);
405 });
406
407 let test_data = vec![
408 (
409 "test1",
410 1,
411 Err(Error::<Test>::RuntimeRegistry(
412 RuntimeRegistryError::InvalidSpecName,
413 )),
414 ),
415 (
416 "test",
417 1,
418 Err(Error::<Test>::RuntimeRegistry(
419 RuntimeRegistryError::SpecVersionNeedsToIncrease,
420 )),
421 ),
422 ("test", 2, Ok(())),
423 ];
424
425 for (spec_name, spec_version, expected) in test_data.into_iter() {
426 let version = RuntimeVersion {
427 spec_name: spec_name.into(),
428 spec_version,
429 impl_version: 1,
430 transaction_version: 1,
431 apis: create_apis_vec!(TEST_RUNTIME_APIS),
432 ..Default::default()
433 };
434 let read_runtime_version = ReadRuntimeVersion(version.encode());
435 ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
436 read_runtime_version,
437 ));
438
439 ext.execute_with(|| {
440 frame_system::Pallet::<Test>::set_block_number(100u32);
441 let res = crate::Pallet::<Test>::upgrade_domain_runtime(
442 RawOrigin::Root.into(),
443 0,
444 RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
445 );
446
447 assert_eq!(res, expected.map_err(DispatchError::from))
448 })
449 }
450
451 ext.execute_with(|| {
453 frame_system::Pallet::<Test>::set_block_number(100u32);
454 let res = crate::Pallet::<Test>::upgrade_domain_runtime(
455 RawOrigin::Root.into(),
456 0,
457 RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
458 );
459
460 assert_err!(
461 res,
462 Error::<Test>::RuntimeRegistry(
463 RuntimeRegistryError::RuntimeUpgradeAlreadyScheduled
464 )
465 );
466 });
467
468 ext.execute_with(|| {
470 let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
471 assert_eq!(
472 runtime_obj.version,
473 RuntimeVersion {
474 spec_name: "test".into(),
475 spec_version: 1,
476 impl_version: 1,
477 transaction_version: 1,
478 apis: create_apis_vec!(TEST_RUNTIME_APIS),
479 ..Default::default()
480 }
481 );
482 assert_eq!(runtime_obj.runtime_upgrades, 0);
483 assert_eq!(runtime_obj.raw_genesis, RawGenesis::dummy(vec![1, 2, 3, 4]),);
484
485 let block_number = frame_system::Pallet::<Test>::current_block_number();
486 let scheduled_block_number = block_number.checked_add(1).unwrap();
487 let scheduled_upgrade =
488 ScheduledRuntimeUpgrades::<Test>::get(scheduled_block_number, 0).unwrap();
489 assert_eq!(
490 scheduled_upgrade.version,
491 RuntimeVersion {
492 spec_name: "test".into(),
493 spec_version: 2,
494 impl_version: 1,
495 transaction_version: 1,
496 apis: create_apis_vec!(TEST_RUNTIME_APIS),
497 ..Default::default()
498 }
499 )
500 })
501 }
502
503 fn go_to_block(block: u32) {
504 for i in System::block_number() + 1..=block {
505 let parent_hash = if System::block_number() > 1 {
506 let header = System::finalize();
507 header.hash()
508 } else {
509 System::parent_hash()
510 };
511
512 System::reset_events();
513 let digest = sp_runtime::testing::Digest { logs: vec![] };
514 System::initialize(&i, &parent_hash, &digest);
515 Domains::on_initialize(i);
516 }
517 }
518
519 fn fetch_upgraded_runtime_from_digest(digest: Digest) -> Option<RuntimeId> {
520 for log in digest.logs {
521 match log.as_domain_runtime_upgrade() {
522 None => continue,
523 Some(runtime_id) => return Some(runtime_id),
524 }
525 }
526
527 None
528 }
529
530 #[test]
531 fn upgrade_scheduled_domain_runtime() {
532 let mut ext = new_test_ext();
533 let mut version = RuntimeVersion {
534 spec_name: "test".into(),
535 impl_name: Default::default(),
536 authoring_version: 0,
537 spec_version: 1,
538 impl_version: 1,
539 apis: create_apis_vec!(TEST_RUNTIME_APIS),
540 transaction_version: 1,
541 system_version: 0,
542 };
543
544 ext.execute_with(|| {
545 RuntimeRegistry::<Test>::insert(
546 0,
547 RuntimeObject {
548 runtime_name: "evm".to_owned(),
549 runtime_type: Default::default(),
550 runtime_upgrades: 0,
551 hash: Default::default(),
552 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
553 version: version.clone(),
554 created_at: Default::default(),
555 updated_at: Default::default(),
556 instance_count: 0,
557 },
558 );
559
560 NextRuntimeId::<Test>::set(1);
561 });
562
563 version.spec_version = 2;
564 let read_runtime_version = ReadRuntimeVersion(version.encode());
565 ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
566 read_runtime_version,
567 ));
568
569 ext.execute_with(|| {
570 let res = crate::Pallet::<Test>::upgrade_domain_runtime(
571 RawOrigin::Root.into(),
572 0,
573 RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
574 );
575 assert_ok!(res);
576
577 let current_block = frame_system::Pallet::<Test>::current_block_number();
578 let scheduled_block_number = current_block.checked_add(1).unwrap();
579
580 go_to_block(scheduled_block_number);
581 assert_eq!(
582 ScheduledRuntimeUpgrades::<Test>::get(scheduled_block_number, 0),
583 None
584 );
585
586 let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
587 assert_eq!(runtime_obj.version, version);
588 assert_eq!(runtime_obj.created_at, 0);
589 assert_eq!(runtime_obj.updated_at, 1);
590
591 let digest = System::digest();
592 assert_eq!(Some(0), fetch_upgraded_runtime_from_digest(digest))
593 });
594 }
595
596 #[test]
597 fn test_runtime_version_encode_decode_with_core_api() {
598 let runtime_obj = RuntimeObject {
599 runtime_name: "evm".to_owned(),
600 runtime_type: Default::default(),
601 runtime_upgrades: 100,
602 hash: Default::default(),
603 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
604 version: RuntimeVersion {
605 spec_name: "test".into(),
606 spec_version: 100,
607 impl_version: 34,
608 transaction_version: 256,
609 apis: create_apis_vec!(TEST_RUNTIME_APIS),
610 ..Default::default()
611 },
612 created_at: 100,
613 updated_at: 200,
614 instance_count: 500,
615 };
616
617 let encoded = runtime_obj.encode();
618 let decoded = RuntimeObject::<u32, Hash>::decode(&mut &encoded[..]).unwrap();
619 assert_eq!(decoded, runtime_obj);
620 }
621
622 #[test]
623 fn test_runtime_version_encode_decode_without_core_api() {
624 let runtime_obj = RuntimeObject {
625 runtime_name: "evm".to_owned(),
626 runtime_type: Default::default(),
627 runtime_upgrades: 100,
628 hash: Default::default(),
629 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
630 version: RuntimeVersion {
631 spec_name: "test".into(),
632 spec_version: 100,
633 impl_version: 34,
634 transaction_version: 256,
635 ..Default::default()
636 },
637 created_at: 100,
638 updated_at: 200,
639 instance_count: 500,
640 };
641
642 let encoded = runtime_obj.encode();
643 let decoded = RuntimeObject::<u32, Hash>::decode(&mut &encoded[..]).unwrap();
644 assert_ne!(decoded, runtime_obj);
645 }
646}