Skip to content
lib.rs 80.5 KiB
Newer Older
			System::deposit_event(32u16);
			System::note_finished_initialize();
			System::deposit_event(42u16);
			System::note_applied_extrinsic(&Ok(()), 0, Default::default());
			System::note_applied_extrinsic(&Err(DispatchError::BadOrigin), 0, Default::default());
			System::note_finished_extrinsics();
			System::deposit_event(3u16);
			System::finalize();
			assert_eq!(
				System::events(),
				vec![
					EventRecord { phase: Phase::Initialization, event: 32u16, topics: vec![] },
					EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16, topics: vec![] },
					EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16, topics: vec![] },
					EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16, topics: vec![] },
					EventRecord { phase: Phase::Finalization, event: 3u16, topics: vec![] }
				]
			);

	#[test]
	fn deposit_event_topics() {
		new_test_ext().execute_with(|| {
			const BLOCK_NUMBER: u64 = 1;

			System::initialize(
				&BLOCK_NUMBER,
				&[0u8; 32].into(),
				&[0u8; 32].into(),
				&Default::default(),
			System::note_finished_extrinsics();

			let topics = vec![
				H256::repeat_byte(1),
				H256::repeat_byte(2),
				H256::repeat_byte(3),
			];

			// We deposit a few events with different sets of topics.
			System::deposit_event_indexed(&topics[0..3], 1u16);
			System::deposit_event_indexed(&topics[0..1], 2u16);
			System::deposit_event_indexed(&topics[1..2], 3u16);

			System::finalize();

			// Check that topics are reflected in the event record.
			assert_eq!(
				System::events(),
				vec![
					EventRecord {
						phase: Phase::Finalization,
						event: 1u16,
						topics: topics[0..3].to_vec(),
					},
					EventRecord {
						phase: Phase::Finalization,
						event: 2u16,
						topics: topics[0..1].to_vec(),
					},
					EventRecord {
						phase: Phase::Finalization,
						event: 3u16,
						topics: topics[1..2].to_vec(),
					}
				]
			);

			// Check that the topic-events mapping reflects the deposited topics.
			// Note that these are indexes of the events.
			assert_eq!(
				System::event_topics(&topics[0]),
				vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 1)],
			);
			assert_eq!(
				System::event_topics(&topics[1]),
				vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 2)],
			);
			assert_eq!(
				System::event_topics(&topics[2]),
				vec![(BLOCK_NUMBER, 0)],
			);
		});
	}

	#[test]
	fn prunes_block_hash_mappings() {
		new_test_ext().execute_with(|| {
			// simulate import of 15 blocks
			for n in 1..=15 {
				System::initialize(
					&n,
					&[n as u8 - 1; 32].into(),
					&[0u8; 32].into(),
					&Default::default(),
				);

				System::finalize();
			}

			// first 5 block hashes are pruned
			for n in 0..5 {
				assert_eq!(
					System::block_hash(n),
					H256::zero(),
				);
			}

			// the remaining 10 are kept
			for n in 5..15 {
				assert_eq!(
					System::block_hash(n),
					[n as u8; 32].into(),
				);
			}
		})
	}

	#[test]
	fn signed_ext_check_nonce_works() {
		new_test_ext().execute_with(|| {
Gavin Wood's avatar
Gavin Wood committed
			Account::<Test>::insert(1, AccountInfo { nonce: 1, refcount: 0, data: 0 });
			let info = DispatchInfo::default();
			let len = 0_usize;
			// stale
			assert!(CheckNonce::<Test>(0).validate(&1, CALL, &info, len).is_err());
			assert!(CheckNonce::<Test>(0).pre_dispatch(&1, CALL, &info, len).is_err());
			assert!(CheckNonce::<Test>(1).validate(&1, CALL, &info, len).is_ok());
			assert!(CheckNonce::<Test>(1).pre_dispatch(&1, CALL, &info, len).is_ok());
			assert!(CheckNonce::<Test>(5).validate(&1, CALL, &info, len).is_ok());
			assert!(CheckNonce::<Test>(5).pre_dispatch(&1, CALL, &info, len).is_err());
Kian Peymani's avatar
Kian Peymani committed
	fn signed_ext_check_weight_works_normal_tx() {
		new_test_ext().execute_with(|| {
Kian Peymani's avatar
Kian Peymani committed
			let normal_limit = normal_weight_limit();
			let small = DispatchInfo { weight: 100, ..Default::default() };
			let medium = DispatchInfo {
				weight: normal_limit - <Test as Trait>::ExtrinsicBaseWeight::get(),
				..Default::default()
			};
			let big = DispatchInfo {
				weight: normal_limit - <Test as Trait>::ExtrinsicBaseWeight::get() + 1,
				..Default::default()
			};
			let len = 0_usize;

			let reset_check_weight = |i, f, s| {
				AllExtrinsicsWeight::mutate(|current_weight| {
					current_weight.put(s, DispatchClass::Normal)
				});
				let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, i, len);
				if f { assert!(r.is_err()) } else { assert!(r.is_ok()) }
			};

			reset_check_weight(&small, false, 0);
			reset_check_weight(&medium, false, 0);
			reset_check_weight(&big, true, 1);
	#[test]
	fn signed_ext_check_weight_refund_works() {
		new_test_ext().execute_with(|| {
			// This is half of the max block weight
			let info = DispatchInfo { weight: 512, ..Default::default() };
			let post_info = PostDispatchInfo { actual_weight: Some(128), };
			let len = 0_usize;

			// We allow 75% for normal transaction, so we put 25% - extrinsic base weight
			AllExtrinsicsWeight::mutate(|current_weight| {
				current_weight.put(256 - <Test as Trait>::ExtrinsicBaseWeight::get(), DispatchClass::Normal)
			});

			let pre = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap();
			assert_eq!(AllExtrinsicsWeight::get().total(), info.weight + 256);

			assert!(
				CheckWeight::<Test>::post_dispatch(pre, &info, &post_info, len, &Ok(()))
				.is_ok()
			);
				AllExtrinsicsWeight::get().total(),
				post_info.actual_weight.unwrap() + 256,
			);
		})
	}

	#[test]
	fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() {
		new_test_ext().execute_with(|| {
			let info = DispatchInfo { weight: 512, ..Default::default() };
			let post_info = PostDispatchInfo { actual_weight: Some(700), };
			let len = 0_usize;

			AllExtrinsicsWeight::mutate(|current_weight| {
				current_weight.put(128, DispatchClass::Normal)
			});

			let pre = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap();
				AllExtrinsicsWeight::get().total(),
				info.weight + 128 + <Test as Trait>::ExtrinsicBaseWeight::get(),
			);

			assert!(
				CheckWeight::<Test>::post_dispatch(pre, &info, &post_info, len, &Ok(()))
				.is_ok()
			);
				AllExtrinsicsWeight::get().total(),
				info.weight + 128 + <Test as Trait>::ExtrinsicBaseWeight::get(),
			);
	fn zero_weight_extrinsic_still_has_base_weight() {
		new_test_ext().execute_with(|| {
			let free = DispatchInfo { weight: 0, ..Default::default() };
			let len = 0_usize;

			// Initial weight from `BlockExecutionWeight`
			assert_eq!(System::all_extrinsics_weight().total(), <Test as Trait>::BlockExecutionWeight::get());
			let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &free, len);
			assert!(r.is_ok());
				System::all_extrinsics_weight().total(),
				<Test as Trait>::ExtrinsicBaseWeight::get() + <Test as Trait>::BlockExecutionWeight::get()
			);
	#[test]
	fn mandatory_extrinsic_doesnt_care_about_limits() {
		new_test_ext().execute_with(|| {
			let max = DispatchInfo {
				weight: Weight::max_value(),
				class: DispatchClass::Mandatory,
				..Default::default()
			};
			let len = 0_usize;

			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max, len));
			assert_eq!(System::all_extrinsics_weight().total(), Weight::max_value());
			assert!(System::all_extrinsics_weight().total() > <Test as Trait>::MaximumBlockWeight::get());
		});
	}

	#[test]
	fn register_extra_weight_unchecked_doesnt_care_about_limits() {
		new_test_ext().execute_with(|| {
			System::register_extra_weight_unchecked(Weight::max_value(), DispatchClass::Normal);
			assert_eq!(System::all_extrinsics_weight().total(), Weight::max_value());
			assert!(System::all_extrinsics_weight().total() > <Test as Trait>::MaximumBlockWeight::get());
		});
	}

	#[test]
	fn full_block_with_normal_and_operational() {
		new_test_ext().execute_with(|| {
			// Max block is 1024
			// Max normal is 768 (75%)
			// 10 is taken for block execution weight
			// So normal extrinsic can be 758 weight (-5 for base extrinsic weight)
			// And Operational can be 256 to produce a full block (-5 for base)
			let max_normal = DispatchInfo { weight: 753, ..Default::default() };
			let rest_operational = DispatchInfo { weight: 251, class: DispatchClass::Operational, ..Default::default() };

			let len = 0_usize;

			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max_normal, len));
			assert_eq!(System::all_extrinsics_weight().total(), 768);
			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&rest_operational, len));
			assert_eq!(<Test as Trait>::MaximumBlockWeight::get(), 1024);
			assert_eq!(System::all_extrinsics_weight().total(), <Test as Trait>::MaximumBlockWeight::get());
		});
	}

	#[test]
	fn dispatch_order_does_not_effect_weight_logic() {
		new_test_ext().execute_with(|| {
			// We switch the order of `full_block_with_normal_and_operational`
			let max_normal = DispatchInfo { weight: 753, ..Default::default() };
			let rest_operational = DispatchInfo { weight: 251, class: DispatchClass::Operational, ..Default::default() };

			let len = 0_usize;

			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&rest_operational, len));
			// Extra 15 here from block execution + base extrinsic weight
			assert_eq!(System::all_extrinsics_weight().total(), 266);
			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&max_normal, len));
			assert_eq!(<Test as Trait>::MaximumBlockWeight::get(), 1024);
			assert_eq!(System::all_extrinsics_weight().total(), <Test as Trait>::MaximumBlockWeight::get());
		});
	}

	#[test]
	fn operational_works_on_full_block() {
		new_test_ext().execute_with(|| {
			// An on_initialize takes up the whole block! (Every time!)
			System::register_extra_weight_unchecked(Weight::max_value(), DispatchClass::Mandatory);
			let dispatch_normal = DispatchInfo { weight: 251, class: DispatchClass::Normal, ..Default::default() };
			let dispatch_operational = DispatchInfo { weight: 251, class: DispatchClass::Operational, ..Default::default() };
			let len = 0_usize;

			assert_noop!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_normal, len), InvalidTransaction::ExhaustsResources);
			// Thank goodness we can still do an operational transaction to possibly save the blockchain.
			assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len));
			// Not too much though
			assert_noop!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len), InvalidTransaction::ExhaustsResources);
	}

	#[test]
	fn signed_ext_check_weight_works_operational_tx() {
		new_test_ext().execute_with(|| {
			let normal = DispatchInfo { weight: 100, ..Default::default() };
			let op = DispatchInfo { weight: 100, class: DispatchClass::Operational, pays_fee: Pays::Yes };
			let len = 0_usize;
Kian Peymani's avatar
Kian Peymani committed
			let normal_limit = normal_weight_limit();

			// given almost full block
			AllExtrinsicsWeight::mutate(|current_weight| {
				current_weight.put(normal_limit, DispatchClass::Normal)
			});
			// will not fit.
			assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &normal, len).is_err());
			assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &op, len).is_ok());

			// likewise for length limit.
			let len = 100_usize;
Kian Peymani's avatar
Kian Peymani committed
			AllExtrinsicsLen::put(normal_length_limit());
			assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &normal, len).is_err());
			assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, &op, len).is_ok());
		})
	}

	#[test]
	fn signed_ext_check_weight_priority_works() {
		new_test_ext().execute_with(|| {
			let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
			let op = DispatchInfo { weight: 100, class: DispatchClass::Operational, pays_fee: Pays::Yes };
			let len = 0_usize;

			let priority = CheckWeight::<Test>(PhantomData)
				.validate(&1, CALL, &normal, len)
				.unwrap()
				.priority;
			assert_eq!(priority, 100);

			let priority = CheckWeight::<Test>(PhantomData)
				.validate(&1, CALL, &op, len)
				.unwrap()
				.priority;
			assert_eq!(priority, u64::max_value());
		})
	}

	#[test]
	fn signed_ext_check_weight_block_size_works() {
		new_test_ext().execute_with(|| {
Kian Peymani's avatar
Kian Peymani committed
			let normal = DispatchInfo::default();
			let normal_limit = normal_weight_limit() as usize;
			let reset_check_weight = |tx, s, f| {
				AllExtrinsicsLen::put(0);
				let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, CALL, tx, s);
				if f { assert!(r.is_err()) } else { assert!(r.is_ok()) }
			};

			reset_check_weight(&normal, normal_limit - 1, false);
			reset_check_weight(&normal, normal_limit, false);
			reset_check_weight(&normal, normal_limit + 1, true);
Kian Peymani's avatar
Kian Peymani committed

			// Operational ones don't have this limit.
			let op = DispatchInfo { weight: 0, class: DispatchClass::Operational, pays_fee: Pays::Yes };
			reset_check_weight(&op, normal_limit, false);
			reset_check_weight(&op, normal_limit + 100, false);
			reset_check_weight(&op, 1024, false);
			reset_check_weight(&op, 1025, true);
		})
	}

	#[test]
	fn signed_ext_check_era_should_work() {
		new_test_ext().execute_with(|| {
			// future
			assert_eq!(
				CheckEra::<Test>::from(Era::mortal(4, 2)).additional_signed().err().unwrap(),
				InvalidTransaction::AncientBirthBlock.into(),
			);

			// correct
			System::set_block_number(13);
			<BlockHash<Test>>::insert(12, H256::repeat_byte(1));
			assert!(CheckEra::<Test>::from(Era::mortal(4, 12)).additional_signed().is_ok());
		})
	}

	#[test]
	fn signed_ext_check_era_should_change_longevity() {
		new_test_ext().execute_with(|| {
			let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
			let len = 0_usize;
			let ext = (
				CheckWeight::<Test>(PhantomData),
				CheckEra::<Test>::from(Era::mortal(16, 256)),
			);
			System::set_block_number(17);
			<BlockHash<Test>>::insert(16, H256::repeat_byte(1));

			assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15);


	#[test]
	fn set_code_checks_works() {
		struct CallInWasm(Vec<u8>);

		impl sp_core::traits::CallInWasm for CallInWasm {
			fn call_in_wasm(
				&self,
				_: &[u8],
				_: &str,
				_: &[u8],
				_: &mut dyn sp_externalities::Externalities,
				_: sp_core::traits::MissingHostFunctions,
			) -> Result<Vec<u8>, String> {
				Ok(self.0.clone())
			}
		}

		let test_data = vec![
			("test", 1, 2, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
			("test", 1, 1, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
			("test2", 1, 1, Err(Error::<Test>::InvalidSpecName)),
			("test", 2, 1, Ok(())),
			("test", 0, 1, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
			("test", 1, 0, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
		];

		for (spec_name, spec_version, impl_version, expected) in test_data.into_iter() {
			let version = RuntimeVersion {
				spec_name: spec_name.into(),
				spec_version,
				impl_version,
				..Default::default()
			};
			let call_in_wasm = CallInWasm(version.encode());

			let mut ext = new_test_ext();
			ext.register_extension(sp_core::traits::CallInWasmExt::new(call_in_wasm));
			ext.execute_with(|| {
				let res = System::set_code(
					RawOrigin::Root.into(),
					vec![1, 2, 3, 4],
				);

				assert_eq!(expected.map_err(DispatchError::from), res);
			});
		}
	}

	#[test]
	fn set_code_with_real_wasm_blob() {
		let executor = substrate_test_runtime_client::new_native_executor();
		let mut ext = new_test_ext();
		ext.register_extension(sp_core::traits::CallInWasmExt::new(executor));
		ext.execute_with(|| {
			System::set_code(
				RawOrigin::Root.into(),
				substrate_test_runtime_client::runtime::WASM_BINARY.to_vec(),
			).unwrap();
				vec![EventRecord { phase: Phase::Initialization, event: 102u16, topics: vec![] }],
		});
	}

	#[test]
	fn runtime_upgraded_with_set_storage() {
		let executor = substrate_test_runtime_client::new_native_executor();
		let mut ext = new_test_ext();
		ext.register_extension(sp_core::traits::CallInWasmExt::new(executor));
		ext.execute_with(|| {
			System::set_storage(
				RawOrigin::Root.into(),
				vec![(
					well_known_keys::CODE.to_vec(),
					substrate_test_runtime_client::runtime::WASM_BINARY.to_vec()
				)],
			).unwrap();

	#[test]
	fn events_not_emitted_during_genesis() {
		new_test_ext().execute_with(|| {
			// Block Number is zero at genesis
			assert!(System::block_number().is_zero());
			System::on_created_account(Default::default());
			assert!(System::events().is_empty());
			// Events will be emitted starting on block 1
			System::set_block_number(1);
			System::on_created_account(Default::default());
			assert!(System::events().len() == 1);
		});
	}