pallet_domains/
runtime_registry.rs

1//! Runtime registry for domains
2
3#[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, EVMChainId, 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, DomainRuntimeConfig, DomainsDigestItem,
25    EvmDomainRuntimeConfig, RuntimeId, RuntimeObject, RuntimeType,
26};
27use sp_runtime::DigestItem;
28use sp_runtime::traits::{CheckedAdd, Zero};
29use sp_std::vec;
30use sp_version::RuntimeVersion;
31
32/// Runtime specific errors
33#[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/// Domain runtime specific information to create domain raw genesis.
48#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
49pub enum DomainRuntimeInfo {
50    Evm {
51        /// The dynamic EVM chain id for this domain.
52        chain_id: EVMChainId,
53        /// The EVM-specific domain runtime config.
54        domain_runtime_config: EvmDomainRuntimeConfig,
55    },
56    AutoId {
57        /// The AutoId-specific domain runtime config.
58        domain_runtime_config: AutoIdDomainRuntimeConfig,
59    },
60}
61
62impl Default for DomainRuntimeInfo {
63    fn default() -> Self {
64        Self::Evm {
65            chain_id: 0,
66            domain_runtime_config: EvmDomainRuntimeConfig::default(),
67        }
68    }
69}
70
71impl DomainRuntimeInfo {
72    /// Returns the inner config as a `DomainRuntimeConfig`.
73    pub fn domain_runtime_config(&self) -> DomainRuntimeConfig {
74        match self {
75            Self::Evm {
76                domain_runtime_config,
77                ..
78            } => DomainRuntimeConfig::Evm(domain_runtime_config.clone()),
79            Self::AutoId {
80                domain_runtime_config,
81                ..
82            } => DomainRuntimeConfig::AutoId(domain_runtime_config.clone()),
83        }
84    }
85
86    /// If this is an EVM runtime, returns the chain id.
87    pub fn evm_chain_id(&self) -> Option<EVMChainId> {
88        match self {
89            Self::Evm { chain_id, .. } => Some(*chain_id),
90            _ => None,
91        }
92    }
93
94    pub fn is_evm_domain(&self) -> bool {
95        matches!(self, Self::Evm { .. })
96    }
97
98    pub fn is_private_evm_domain(&self) -> bool {
99        if let Self::Evm {
100            domain_runtime_config,
101            ..
102        } = self
103        {
104            domain_runtime_config.evm_type.is_private_evm_domain()
105        } else {
106            false
107        }
108    }
109
110    pub fn is_auto_id(&self) -> bool {
111        matches!(self, Self::AutoId { .. })
112    }
113}
114
115#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
116pub struct DomainRuntimeUpgradeEntry<Hash> {
117    // The consensus block hash at which the upgrade happened
118    pub at_hash: Hash,
119    // The expected number of ER (from differnt domains) that derive from the consensus
120    // block `at_hash`, the `reference_count` will decrease by one as one such ER is
121    // confirmed and the whole entry will remove from the state after it become zero.
122    pub reference_count: u32,
123}
124
125fn derive_initial_balances_storages<T: Config, AccountId: Encode>(
126    total_issuance: BalanceOf<T>,
127    balances: Vec<(AccountId, BalanceOf<T>)>,
128) -> Vec<(StorageKey, StorageData)> {
129    let total_issuance_key = sp_domains::domain_total_issuance_storage_key();
130    let mut initial_storages = vec![(total_issuance_key, StorageData(total_issuance.encode()))];
131    for (account_id, balance) in balances {
132        let account_storage_key = sp_domains::domain_account_storage_key(account_id);
133        let account_info = AccountInfo {
134            nonce: domain_runtime_primitives::Nonce::zero(),
135            consumers: 0,
136            // providers are set to 1 for new accounts
137            providers: 1,
138            sufficients: 0,
139            data: pallet_balances::AccountData {
140                free: balance,
141                ..Default::default()
142            },
143        };
144        initial_storages.push((account_storage_key, StorageData(account_info.encode())))
145    }
146
147    initial_storages
148}
149
150// Return a complete raw genesis with runtime code and domain id set properly
151pub fn into_complete_raw_genesis<T: Config>(
152    runtime_obj: RuntimeObject<BlockNumberFor<T>, T::Hash>,
153    domain_id: DomainId,
154    domain_runtime_info: &DomainRuntimeInfo,
155    total_issuance: BalanceOf<T>,
156    initial_balances: Vec<(MultiAccountId, BalanceOf<T>)>,
157) -> Result<RawGenesis, Error> {
158    let RuntimeObject {
159        mut raw_genesis, ..
160    } = runtime_obj;
161    raw_genesis.set_domain_id(domain_id);
162    match domain_runtime_info {
163        DomainRuntimeInfo::Evm {
164            chain_id,
165            domain_runtime_config,
166        } => {
167            raw_genesis.set_evm_chain_id(*chain_id);
168            if let Some(initial_contract_creation_allow_list) = domain_runtime_config
169                .evm_type
170                .initial_contract_creation_allow_list()
171            {
172                raw_genesis
173                    .set_evm_contract_creation_allowed_by(initial_contract_creation_allow_list);
174            }
175
176            let initial_balances = initial_balances.into_iter().try_fold(
177                Vec::<(AccountId20, BalanceOf<T>)>::new(),
178                |mut balances, (account_id, balance)| {
179                    let account_id =
180                        domain_runtime_primitives::AccountId20Converter::try_convert_back(
181                            account_id,
182                        )
183                        .ok_or(Error::InvalidAccountIdType)?;
184
185                    balances.push((account_id, balance));
186                    Ok(balances)
187                },
188            )?;
189            raw_genesis.set_top_storages(derive_initial_balances_storages::<T, _>(
190                total_issuance,
191                initial_balances,
192            ));
193        }
194        DomainRuntimeInfo::AutoId {
195            domain_runtime_config: AutoIdDomainRuntimeConfig {},
196        } => {
197            let initial_balances = initial_balances.into_iter().try_fold(
198                Vec::<(AccountId32, BalanceOf<T>)>::new(),
199                |mut balances, (account_id, balance)| {
200                    let account_id =
201                        domain_runtime_primitives::AccountIdConverter::try_convert_back(account_id)
202                            .ok_or(Error::InvalidAccountIdType)?;
203
204                    balances.push((account_id, balance));
205                    Ok(balances)
206                },
207            )?;
208            raw_genesis.set_top_storages(derive_initial_balances_storages::<T, _>(
209                total_issuance,
210                initial_balances,
211            ));
212        }
213    }
214
215    Ok(raw_genesis)
216}
217
218#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
219pub struct ScheduledRuntimeUpgrade<Hash> {
220    pub raw_genesis: RawGenesis,
221    pub version: RuntimeVersion,
222    pub hash: Hash,
223}
224
225/// Extracts the runtime version of the provided code.
226pub(crate) fn runtime_version(code: &[u8]) -> Result<RuntimeVersion, Error> {
227    sp_io::misc::runtime_version(code)
228        .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok())
229        .ok_or(Error::FailedToExtractRuntimeVersion)
230}
231
232/// Upgrades current runtime with new runtime.
233// TODO: we can use upstream's `can_set_code` after some adjustments
234pub(crate) fn can_upgrade_code(
235    current_version: &RuntimeVersion,
236    update_code: &[u8],
237) -> Result<RuntimeVersion, Error> {
238    let new_version = runtime_version(update_code)?;
239
240    if new_version.spec_name != current_version.spec_name {
241        return Err(Error::InvalidSpecName);
242    }
243
244    if new_version.spec_version <= current_version.spec_version {
245        return Err(Error::SpecVersionNeedsToIncrease);
246    }
247
248    Ok(new_version)
249}
250
251/// Registers a new domain runtime..
252pub(crate) fn do_register_runtime<T: Config>(
253    runtime_name: String,
254    runtime_type: RuntimeType,
255    raw_genesis_storage: Vec<u8>,
256    at: BlockNumberFor<T>,
257) -> Result<RuntimeId, Error> {
258    let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
259        .map_err(|_| Error::FailedToDecodeRawGenesis)?;
260
261    let code = raw_genesis
262        .get_runtime_code()
263        .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
264
265    let version = runtime_version(code)?;
266    let runtime_hash = T::Hashing::hash(code);
267    let runtime_id = NextRuntimeId::<T>::get();
268
269    RuntimeRegistry::<T>::insert(
270        runtime_id,
271        RuntimeObject {
272            runtime_name,
273            runtime_type,
274            hash: runtime_hash,
275            raw_genesis,
276            version,
277            created_at: at,
278            updated_at: at,
279            runtime_upgrades: 0u32,
280            instance_count: 0,
281        },
282    );
283
284    let next_runtime_id = runtime_id.checked_add(1).ok_or(Error::MaxRuntimeId)?;
285    NextRuntimeId::<T>::set(next_runtime_id);
286
287    Ok(runtime_id)
288}
289
290// TODO: Remove once `do_register_runtime` works at genesis.
291/// Registers a new domain runtime at genesis.
292pub(crate) fn register_runtime_at_genesis<T: Config>(
293    runtime_name: String,
294    runtime_type: RuntimeType,
295    runtime_version: RuntimeVersion,
296    raw_genesis_storage: Vec<u8>,
297    at: BlockNumberFor<T>,
298) -> Result<RuntimeId, Error> {
299    let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
300        .map_err(|_| Error::FailedToDecodeRawGenesis)?;
301
302    let code = raw_genesis
303        .get_runtime_code()
304        .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
305
306    let runtime_hash = T::Hashing::hash(code);
307    let runtime_id = NextRuntimeId::<T>::get();
308
309    RuntimeRegistry::<T>::insert(
310        runtime_id,
311        RuntimeObject {
312            runtime_name,
313            runtime_type,
314            hash: runtime_hash,
315            raw_genesis,
316            version: runtime_version,
317            created_at: at,
318            updated_at: at,
319            runtime_upgrades: 0u32,
320            instance_count: 0,
321        },
322    );
323
324    let next_runtime_id = runtime_id.checked_add(1).ok_or(Error::MaxRuntimeId)?;
325    NextRuntimeId::<T>::set(next_runtime_id);
326
327    Ok(runtime_id)
328}
329
330/// Schedules a runtime upgrade after `DomainRuntimeUpgradeDelay` from current block number.
331pub(crate) fn do_schedule_runtime_upgrade<T: Config>(
332    runtime_id: RuntimeId,
333    raw_genesis_storage: Vec<u8>,
334    current_block_number: BlockNumberFor<T>,
335) -> Result<BlockNumberFor<T>, Error> {
336    let runtime_obj = RuntimeRegistry::<T>::get(runtime_id).ok_or(Error::MissingRuntimeObject)?;
337
338    let new_raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice())
339        .map_err(|_| Error::FailedToDecodeRawGenesis)?;
340
341    let new_code = new_raw_genesis
342        .get_runtime_code()
343        .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?;
344
345    let new_runtime_version = can_upgrade_code(&runtime_obj.version, new_code)?;
346    let new_runtime_hash = T::Hashing::hash(new_code);
347    let scheduled_upgrade = ScheduledRuntimeUpgrade {
348        raw_genesis: new_raw_genesis,
349        version: new_runtime_version,
350        hash: new_runtime_hash,
351    };
352    // we schedule it in the next consensus block
353    let scheduled_at = current_block_number
354        .checked_add(&BlockNumberFor::<T>::from(1u32))
355        .ok_or(Error::MaxScheduledBlockNumber)?;
356
357    ensure!(
358        !ScheduledRuntimeUpgrades::<T>::contains_key(scheduled_at, runtime_id),
359        Error::RuntimeUpgradeAlreadyScheduled
360    );
361
362    ScheduledRuntimeUpgrades::<T>::insert(scheduled_at, runtime_id, scheduled_upgrade);
363
364    Ok(scheduled_at)
365}
366
367pub(crate) fn do_upgrade_runtimes<T: Config>(at: BlockNumberFor<T>) {
368    for (runtime_id, scheduled_update) in ScheduledRuntimeUpgrades::<T>::drain_prefix(at) {
369        RuntimeRegistry::<T>::mutate(runtime_id, |maybe_runtime_object| {
370            let runtime_obj = maybe_runtime_object
371                .as_mut()
372                .expect("Runtime object exists since an upgrade is scheduled after verification");
373
374            runtime_obj.raw_genesis = scheduled_update.raw_genesis;
375            runtime_obj.version = scheduled_update.version;
376            runtime_obj.hash = scheduled_update.hash;
377            runtime_obj.runtime_upgrades = runtime_obj.runtime_upgrades.saturating_add(1);
378            runtime_obj.updated_at = at;
379        });
380
381        // Record the runtime upgrade
382        DomainRuntimeUpgrades::<T>::mutate(|upgrades| upgrades.push(runtime_id));
383
384        // deposit digest log for light clients
385        frame_system::Pallet::<T>::deposit_log(DigestItem::domain_runtime_upgrade(runtime_id));
386
387        // deposit event to signal runtime upgrade is complete
388        frame_system::Pallet::<T>::deposit_event(<T as Config>::RuntimeEvent::from(
389            Event::DomainRuntimeUpgraded { runtime_id },
390        ));
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use crate::Error;
397    use crate::pallet::{NextRuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades};
398    use crate::runtime_registry::Error as RuntimeRegistryError;
399    use crate::tests::{
400        Domains, ReadRuntimeVersion, System, TEST_RUNTIME_APIS, Test, new_test_ext,
401    };
402    use domain_runtime_primitives::Hash;
403    use frame_support::dispatch::RawOrigin;
404    use frame_support::traits::OnInitialize;
405    use frame_support::{assert_err, assert_ok};
406    use parity_scale_codec::{Decode, Encode};
407    use sp_domains::storage::RawGenesis;
408    use sp_domains::{DomainsDigestItem, RuntimeId, RuntimeObject, RuntimeType};
409    use sp_runtime::traits::BlockNumberProvider;
410    use sp_runtime::{Digest, DispatchError};
411    use sp_version::{RuntimeVersion, create_apis_vec};
412
413    #[test]
414    fn create_domain_runtime() {
415        let version = RuntimeVersion {
416            spec_name: "test".into(),
417            impl_name: Default::default(),
418            authoring_version: 0,
419            spec_version: 1,
420            impl_version: 1,
421            apis: Default::default(),
422            transaction_version: 1,
423            system_version: 0,
424        };
425        let read_runtime_version = ReadRuntimeVersion(version.encode());
426
427        let mut ext = new_test_ext();
428        ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
429            read_runtime_version,
430        ));
431        ext.execute_with(|| {
432            let raw_genesis_storage = RawGenesis::dummy(vec![1, 2, 3, 4]).encode();
433            let res = crate::Pallet::<Test>::register_domain_runtime(
434                RawOrigin::Root.into(),
435                "evm".to_owned(),
436                RuntimeType::Evm,
437                raw_genesis_storage,
438            );
439
440            assert_ok!(res);
441            let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
442            assert_eq!(runtime_obj.version, version);
443            assert_eq!(NextRuntimeId::<Test>::get(), 1)
444        })
445    }
446
447    #[test]
448    fn schedule_domain_runtime_upgrade() {
449        let mut ext = new_test_ext();
450        ext.execute_with(|| {
451            RuntimeRegistry::<Test>::insert(
452                0,
453                RuntimeObject {
454                    runtime_name: "evm".to_owned(),
455                    runtime_type: Default::default(),
456                    runtime_upgrades: 0,
457                    hash: Default::default(),
458                    raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
459                    version: RuntimeVersion {
460                        spec_name: "test".into(),
461                        spec_version: 1,
462                        impl_version: 1,
463                        transaction_version: 1,
464                        apis: create_apis_vec!(TEST_RUNTIME_APIS),
465                        ..Default::default()
466                    },
467                    created_at: Default::default(),
468                    updated_at: Default::default(),
469                    instance_count: 0,
470                },
471            );
472
473            NextRuntimeId::<Test>::set(1);
474        });
475
476        let test_data = vec![
477            (
478                "test1",
479                1,
480                Err(Error::<Test>::RuntimeRegistry(
481                    RuntimeRegistryError::InvalidSpecName,
482                )),
483            ),
484            (
485                "test",
486                1,
487                Err(Error::<Test>::RuntimeRegistry(
488                    RuntimeRegistryError::SpecVersionNeedsToIncrease,
489                )),
490            ),
491            ("test", 2, Ok(())),
492        ];
493
494        for (spec_name, spec_version, expected) in test_data.into_iter() {
495            let version = RuntimeVersion {
496                spec_name: spec_name.into(),
497                spec_version,
498                impl_version: 1,
499                transaction_version: 1,
500                apis: create_apis_vec!(TEST_RUNTIME_APIS),
501                ..Default::default()
502            };
503            let read_runtime_version = ReadRuntimeVersion(version.encode());
504            ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
505                read_runtime_version,
506            ));
507
508            ext.execute_with(|| {
509                frame_system::Pallet::<Test>::set_block_number(100u32);
510                let res = crate::Pallet::<Test>::upgrade_domain_runtime(
511                    RawOrigin::Root.into(),
512                    0,
513                    RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
514                );
515
516                assert_eq!(res, expected.map_err(DispatchError::from))
517            })
518        }
519
520        // will not be able to override an already scheduled upgrade
521        ext.execute_with(|| {
522            frame_system::Pallet::<Test>::set_block_number(100u32);
523            let res = crate::Pallet::<Test>::upgrade_domain_runtime(
524                RawOrigin::Root.into(),
525                0,
526                RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
527            );
528
529            assert_err!(
530                res,
531                Error::<Test>::RuntimeRegistry(
532                    RuntimeRegistryError::RuntimeUpgradeAlreadyScheduled
533                )
534            );
535        });
536
537        // verify upgrade
538        ext.execute_with(|| {
539            let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
540            assert_eq!(
541                runtime_obj.version,
542                RuntimeVersion {
543                    spec_name: "test".into(),
544                    spec_version: 1,
545                    impl_version: 1,
546                    transaction_version: 1,
547                    apis: create_apis_vec!(TEST_RUNTIME_APIS),
548                    ..Default::default()
549                }
550            );
551            assert_eq!(runtime_obj.runtime_upgrades, 0);
552            assert_eq!(runtime_obj.raw_genesis, RawGenesis::dummy(vec![1, 2, 3, 4]),);
553
554            let block_number = frame_system::Pallet::<Test>::current_block_number();
555            let scheduled_block_number = block_number.checked_add(1).unwrap();
556            let scheduled_upgrade =
557                ScheduledRuntimeUpgrades::<Test>::get(scheduled_block_number, 0).unwrap();
558            assert_eq!(
559                scheduled_upgrade.version,
560                RuntimeVersion {
561                    spec_name: "test".into(),
562                    spec_version: 2,
563                    impl_version: 1,
564                    transaction_version: 1,
565                    apis: create_apis_vec!(TEST_RUNTIME_APIS),
566                    ..Default::default()
567                }
568            )
569        })
570    }
571
572    fn go_to_block(block: u32) {
573        for i in System::block_number() + 1..=block {
574            let parent_hash = if System::block_number() > 1 {
575                let header = System::finalize();
576                header.hash()
577            } else {
578                System::parent_hash()
579            };
580
581            System::reset_events();
582            let digest = sp_runtime::testing::Digest { logs: vec![] };
583            System::initialize(&i, &parent_hash, &digest);
584            Domains::on_initialize(i);
585        }
586    }
587
588    fn fetch_upgraded_runtime_from_digest(digest: Digest) -> Option<RuntimeId> {
589        for log in digest.logs {
590            match log.as_domain_runtime_upgrade() {
591                None => continue,
592                Some(runtime_id) => return Some(runtime_id),
593            }
594        }
595
596        None
597    }
598
599    #[test]
600    fn upgrade_scheduled_domain_runtime() {
601        let mut ext = new_test_ext();
602        let mut version = RuntimeVersion {
603            spec_name: "test".into(),
604            impl_name: Default::default(),
605            authoring_version: 0,
606            spec_version: 1,
607            impl_version: 1,
608            apis: create_apis_vec!(TEST_RUNTIME_APIS),
609            transaction_version: 1,
610            system_version: 0,
611        };
612
613        ext.execute_with(|| {
614            RuntimeRegistry::<Test>::insert(
615                0,
616                RuntimeObject {
617                    runtime_name: "evm".to_owned(),
618                    runtime_type: Default::default(),
619                    runtime_upgrades: 0,
620                    hash: Default::default(),
621                    raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
622                    version: version.clone(),
623                    created_at: Default::default(),
624                    updated_at: Default::default(),
625                    instance_count: 0,
626                },
627            );
628
629            NextRuntimeId::<Test>::set(1);
630        });
631
632        version.spec_version = 2;
633        let read_runtime_version = ReadRuntimeVersion(version.encode());
634        ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(
635            read_runtime_version,
636        ));
637
638        ext.execute_with(|| {
639            let res = crate::Pallet::<Test>::upgrade_domain_runtime(
640                RawOrigin::Root.into(),
641                0,
642                RawGenesis::dummy(vec![6, 7, 8, 9]).encode(),
643            );
644            assert_ok!(res);
645
646            let current_block = frame_system::Pallet::<Test>::current_block_number();
647            let scheduled_block_number = current_block.checked_add(1).unwrap();
648
649            go_to_block(scheduled_block_number);
650            assert_eq!(
651                ScheduledRuntimeUpgrades::<Test>::get(scheduled_block_number, 0),
652                None
653            );
654
655            let runtime_obj = RuntimeRegistry::<Test>::get(0).unwrap();
656            assert_eq!(runtime_obj.version, version);
657            assert_eq!(runtime_obj.created_at, 0);
658            assert_eq!(runtime_obj.updated_at, 1);
659
660            let digest = System::digest();
661            assert_eq!(Some(0), fetch_upgraded_runtime_from_digest(digest))
662        });
663    }
664
665    #[test]
666    fn test_runtime_version_encode_decode_with_core_api() {
667        let runtime_obj = RuntimeObject {
668            runtime_name: "evm".to_owned(),
669            runtime_type: Default::default(),
670            runtime_upgrades: 100,
671            hash: Default::default(),
672            raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
673            version: RuntimeVersion {
674                spec_name: "test".into(),
675                spec_version: 100,
676                impl_version: 34,
677                transaction_version: 256,
678                apis: create_apis_vec!(TEST_RUNTIME_APIS),
679                ..Default::default()
680            },
681            created_at: 100,
682            updated_at: 200,
683            instance_count: 500,
684        };
685
686        let encoded = runtime_obj.encode();
687        let decoded = RuntimeObject::<u32, Hash>::decode(&mut &encoded[..]).unwrap();
688        assert_eq!(decoded, runtime_obj);
689    }
690
691    #[test]
692    fn test_runtime_version_encode_decode_without_core_api() {
693        let runtime_obj = RuntimeObject {
694            runtime_name: "evm".to_owned(),
695            runtime_type: Default::default(),
696            runtime_upgrades: 100,
697            hash: Default::default(),
698            raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
699            version: RuntimeVersion {
700                spec_name: "test".into(),
701                spec_version: 100,
702                impl_version: 34,
703                transaction_version: 256,
704                ..Default::default()
705            },
706            created_at: 100,
707            updated_at: 200,
708            instance_count: 500,
709        };
710
711        let encoded = runtime_obj.encode();
712        let decoded = RuntimeObject::<u32, Hash>::decode(&mut &encoded[..]).unwrap();
713        assert_ne!(decoded, runtime_obj);
714    }
715}