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, 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#[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)]
49pub enum DomainRuntimeInfo {
50 Evm {
51 chain_id: EVMChainId,
53 domain_runtime_config: EvmDomainRuntimeConfig,
55 },
56 AutoId {
57 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 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 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 pub at_hash: Hash,
119 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: 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
150pub 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
225pub(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
232pub(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
251pub(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
290pub(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
330pub(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 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 DomainRuntimeUpgrades::<T>::mutate(|upgrades| upgrades.push(runtime_id));
383
384 frame_system::Pallet::<T>::deposit_log(DigestItem::domain_runtime_upgrade(runtime_id));
386
387 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 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 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}