Origin

As we started to describe, the origin is the first parameter of every callable function.

It describes where the call is calling from, and allows us to perform simple access control logic based on that information.

Origin vs Sender

If you are familiar with smart contract development, for example in Ethereum, you will be familiar with msg.sender.

Origin is a superset of this idea. No longer do we need to assume that every call to a callable function is coming from an external account. We could have pallets call one another, or other internal logic trigger a callable function.

It is hard to explain the power of Origin when you are still learning the basics of Pallet development, but this is again, something worth exploring deeper at a later point.

Ensure Signed

In this tutorial, we will just use origin to check for signed messages.

For this, we can use the ensure_signed function:

#![allow(unused)]
fn main() {
let who: T::AccountId = ensure_signed(origin)?;
}

You can see this function takes the OriginFor<T> type, and will return a T::AccountId if the origin was an account, otherwise it will return the error BadOrigin.

This turns origin into exactly the same as msg.sender from Ethereum contract development.

With this, we are able to know who is calling our Pallet, and use that as authorization to make changes to our blockchain on their behalf.

Tests

We are introducing our first new test in this step, so let's spend a second to talk about it.

The test shows that you are able to successfully call create_kitty from the user ALICE, but not from none(). This validates the functionality of our ensure_signed check, and also shows how information about who is calling a function gets passed into our pallet (at least in a unit test).

Make sure to update your tests.rs file to include this latest test, and check that the test passes. Since you haven't written any code yet, everything should pass, but hopefully you can start to get comfortable with this pattern.

Deep Dive

As a note, you should know that ensure_signed is not actually doing signature checking.

Signature checking is very expensive, perhaps one of the most expensive things to perform when executing transactions.

So signature checking happens batched and parallelized at the beginning of executing a block.

By the time your callable function gets the origin, it is just:

#![allow(unused)]
fn main() {
let origin: OriginFor<T> = RawOrigin::Signed(account_id).into();
}

So it is simply an enum variant with the T::AccountId inside. So ensure_signed logic is as simple as:

#![allow(unused)]
fn main() {
pub fn ensure_signed(o: OriginFor<T>) -> Result<AccountId, BadOrigin> {
	match o {
		RawOrigin::Signed(t) => Ok(t),
		_ => Err(BadOrigin),
	}
}
}

The real ensure_signed function has more generic stuff, but the idea is the same.

#![allow(unused)]
fn main() {
use super::*;
use frame::prelude::*;

impl<T: Config> Pallet<T> {
	/* 🚧 TODO 🚧: Learn about `AccountId`. */
	pub fn mint(owner: T::AccountId) -> DispatchResult {
		Self::deposit_event(Event::<T>::Created { owner });
		Ok(())
	}
}
}
#![allow(unused)]
#![cfg_attr(not(feature = "std"), no_std)]

fn main() {
mod impls;
mod tests;

use frame::prelude::*;
pub use pallet::*;

#[frame::pallet(dev_mode)]
pub mod pallet {
	use super::*;

	#[pallet::pallet]
	pub struct Pallet<T>(core::marker::PhantomData<T>);

	#[pallet::config]
	pub trait Config: frame_system::Config {
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
	}

	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		Created { owner: T::AccountId },
	}

	#[pallet::error]
	pub enum Error<T> {}

	#[pallet::call]
	impl<T: Config> Pallet<T> {
		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
			/* 🚧 TODO 🚧: Learn about origin. */
			let who = ensure_signed(origin)?;
			Self::mint(who)?;
			Ok(())
		}
	}
}
}
#![allow(unused)]
fn main() {
// Tests for the Kitties Pallet.
//
// Normally this file would be split into two parts: `mock.rs` and `tests.rs`.
// The `mock.rs` file would contain all the setup code for our `TestRuntime`.
// Then `tests.rs` would only have the tests for our pallet.
// However, to minimize the project, these have been combined into this single file.
//
// Learn more about creating tests for Pallets:
// https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/your_first_pallet/index.html

// This flag tells rust to only run this file when running `cargo test`.
#![cfg(test)]

use crate as pallet_kitties;
use crate::*;
use frame::deps::sp_io;
use frame::runtime::prelude::*;
use frame::testing_prelude::*;
use frame::traits::fungible::*;

type Balance = u64;
type Block = frame_system::mocking::MockBlock<TestRuntime>;

// In our "test runtime", we represent a user `AccountId` with a `u64`.
// This is just a simplification so that we don't need to generate a bunch of proper cryptographic
// public keys when writing tests. It is just easier to say "user 1 transfers to user 2".
// We create the constants `ALICE` and `BOB` to make it clear when we are representing users below.
const ALICE: u64 = 1;
const BOB: u64 = 2;

// Our blockchain tests only need 3 Pallets:
// 1. System: Which is included with every FRAME runtime.
// 2. PalletBalances: Which is manages your blockchain's native currency. (i.e. DOT on Polkadot)
// 3. PalletKitties: The pallet you are building in this tutorial!
construct_runtime! {
	pub struct TestRuntime {
		System: frame_system,
		PalletBalances: pallet_balances,
		PalletKitties: pallet_kitties,
	}
}

// Normally `System` would have many more configurations, but you can see that we use some macro
// magic to automatically configure most of the pallet for a "default test configuration".
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for TestRuntime {
	type Block = Block;
	type AccountData = pallet_balances::AccountData<Balance>;
}

// Normally `pallet_balances` would have many more configurations, but you can see that we use some
// macro magic to automatically configure most of the pallet for a "default test configuration".
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for TestRuntime {
	type AccountStore = System;
	type Balance = Balance;
}

// This is the configuration of our Pallet! If you make changes to the pallet's `trait Config`, you
// will also need to update this configuration to represent that.
impl pallet_kitties::Config for TestRuntime {
	type RuntimeEvent = RuntimeEvent;
}

// We need to run most of our tests using this function: `new_test_ext().execute_with(|| { ... });`
// It simulates the blockchain database backend for our tests.
// If you forget to include this and try to access your Pallet storage, you will get an error like:
// "`get_version_1` called outside of an Externalities-provided environment."
pub fn new_test_ext() -> sp_io::TestExternalities {
	frame_system::GenesisConfig::<TestRuntime>::default()
		.build_storage()
		.unwrap()
		.into()
}

#[test]
fn starting_template_is_sane() {
	new_test_ext().execute_with(|| {
		let event = Event::<TestRuntime>::Created { owner: ALICE };
		let _runtime_event: RuntimeEvent = event.into();
		let _call = Call::<TestRuntime>::create_kitty {};
		let result = PalletKitties::create_kitty(RuntimeOrigin::signed(BOB));
		assert_ok!(result);
	});
}

#[test]
fn system_and_balances_work() {
	// This test will just sanity check that we can access `System` and `PalletBalances`.
	new_test_ext().execute_with(|| {
		// We often need to set `System` to block 1 so that we can see events.
		System::set_block_number(1);
		// We often need to add some balance to a user to test features which needs tokens.
		assert_ok!(PalletBalances::mint_into(&ALICE, 100));
		assert_ok!(PalletBalances::mint_into(&BOB, 100));
	});
}

#[test]
fn create_kitty_checks_signed() {
	new_test_ext().execute_with(|| {
		// The `create_kitty` extrinsic should work when being called by a user.
		assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE)));
		// The `create_kitty` extrinsic should fail when being called by an unsigned message.
		assert_noop!(PalletKitties::create_kitty(RuntimeOrigin::none()), DispatchError::BadOrigin);
	})
}
}
diff --git a/src/impls.rs b/src/impls.rs
index 7454df9..ecb2e7d 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -1,8 +1,8 @@
 use super::*;
 use frame::prelude::*;
 
-/* 🚧 TODO 🚧:  Learn about internal functions. */
 impl<T: Config> Pallet<T> {
+	/* 🚧 TODO 🚧: Learn about `AccountId`. */
 	pub fn mint(owner: T::AccountId) -> DispatchResult {
 		Self::deposit_event(Event::<T>::Created { owner });
 		Ok(())
diff --git a/src/lib.rs b/src/lib.rs
index 52544f5..76a7966 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,10 +27,10 @@ pub mod pallet {
 	#[pallet::error]
 	pub enum Error<T> {}
 
-	/* 🚧 TODO 🚧: Learn about callable functions and dispatch. */
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
 		pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
+			/* 🚧 TODO 🚧: Learn about origin. */
 			let who = ensure_signed(origin)?;
 			Self::mint(who)?;
 			Ok(())
diff --git a/src/tests.rs b/src/tests.rs
index 0901674..08649c3 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -95,3 +95,13 @@ fn system_and_balances_work() {
 		assert_ok!(PalletBalances::mint_into(&BOB, 100));
 	});
 }
+
+#[test]
+fn create_kitty_checks_signed() {
+	new_test_ext().execute_with(|| {
+		// The `create_kitty` extrinsic should work when being called by a user.
+		assert_ok!(PalletKitties::create_kitty(RuntimeOrigin::signed(ALICE)));
+		// The `create_kitty` extrinsic should fail when being called by an unsigned message.
+		assert_noop!(PalletKitties::create_kitty(RuntimeOrigin::none()), DispatchError::BadOrigin);
+	})
+}