Add Proof of Existence Dispatch

We have already established the nested dispatch pipeline for Pallets in the Runtime.

Let's build Pallet level dispatch logic for the Proof of Existence to take advantage of that.

Create Pallet Level Dispatch

There is nothing new here, but we have left more for you to fill out than before.

  1. Create the variants for CreateClaim and RevokeClaim for your Call enum.
  2. Implement the Dispatch trait for your Pallet.

If you get stuck, try not to look at the solution provided here, but instead look at what you did in the Balances Pallet. Everything we have done here, we have already done in the past. This is an opportunity to catch where you may have outstanding questions or misunderstandings.

Don't worry about compiler warnings like "never used/constructed".

#![allow(unused)]
fn main() {
use crate::support::DispatchResult;
use core::fmt::Debug;
use std::collections::BTreeMap;

pub trait Config: crate::system::Config {
	/// The type which represents the content that can be claimed using this pallet.
	/// Could be the content directly as bytes, or better yet the hash of that content.
	/// We leave that decision to the runtime developer.
	type Content: Debug + Ord;
}

/// This is the Proof of Existence Module.
/// It is a simple module that allows accounts to claim existence of some data.
#[derive(Debug)]
pub struct Pallet<T: Config> {
	/// A simple storage map from content to the owner of that content.
	/// Accounts can make multiple different claims, but each claim can only have one owner.
	claims: BTreeMap<T::Content, T::AccountId>,
}

impl<T: Config> Pallet<T> {
	/// Create a new instance of the Proof of Existence Module.
	pub fn new() -> Self {
		Self { claims: BTreeMap::new() }
	}

	/// Get the owner (if any) of a claim.
	pub fn get_claim(&self, claim: &T::Content) -> Option<&T::AccountId> {
		self.claims.get(claim)
	}

	/// Create a new claim on behalf of the `caller`.
	/// This function will return an error if someone already has claimed that content.
	pub fn create_claim(&mut self, caller: T::AccountId, claim: T::Content) -> DispatchResult {
		if self.claims.contains_key(&claim) {
			return Err("this content is already claimed");
		}
		self.claims.insert(claim, caller);
		Ok(())
	}

	/// Revoke an existing claim on some content.
	/// This function should only succeed if the caller is the owner of an existing claim.
	/// It will return an error if the claim does not exist, or if the caller is not the owner.
	pub fn revoke_claim(&mut self, caller: T::AccountId, claim: T::Content) -> DispatchResult {
		let owner = self.get_claim(&claim).ok_or("claim does not exist")?;
		if caller != *owner {
			return Err("this content is owned by someone else");
		}
		self.claims.remove(&claim);
		Ok(())
	}
}

// A public enum which describes the calls we want to expose to the dispatcher.
// We should expect that the caller of each call will be provided by the dispatcher,
// and not included as a parameter of the call.
pub enum Call<T: Config> {
	/*
		TODO:
		Create variants for:
		- `CreateClaim`
		- `RevokeClaim`

		Remember that you only need to pass in the `claim` data, as `caller` information is passed
		in through the `dispatch` logic.
	*/
	RemoveMe(core::marker::PhantomData<T>),
}

/// Implementation of the dispatch logic, mapping from `POECall` to the appropriate underlying
/// function we want to execute.
/*
	TODO:
	Implement `crate::support::Dispatch` for `Pallet<T>`.

	In your `dispatch` logic, match on `call` and forward the `caller` and `claim` data to the
	appropriate function.
*/

#[cfg(test)]
mod test {
	struct TestConfig;

	impl super::Config for TestConfig {
		type Content = &'static str;
	}

	impl crate::system::Config for TestConfig {
		type AccountId = &'static str;
		type BlockNumber = u32;
		type Nonce = u32;
	}

	#[test]
	fn basic_proof_of_existence() {
		let mut poe = super::Pallet::<TestConfig>::new();
		assert_eq!(poe.get_claim(&"Hello, world!"), None);
		assert_eq!(poe.create_claim("alice", "Hello, world!"), Ok(()));
		assert_eq!(poe.get_claim(&"Hello, world!"), Some(&"alice"));
		assert_eq!(
			poe.create_claim("bob", "Hello, world!"),
			Err("this content is already claimed")
		);
		assert_eq!(poe.revoke_claim("alice", "Hello, world!"), Ok(()));
		assert_eq!(poe.create_claim("bob", "Hello, world!"), Ok(()));
	}
}
}
#![allow(unused)]
fn main() {
use crate::support::DispatchResult;
use core::fmt::Debug;
use std::collections::BTreeMap;

pub trait Config: crate::system::Config {
	/// The type which represents the content that can be claimed using this pallet.
	/// Could be the content directly as bytes, or better yet the hash of that content.
	/// We leave that decision to the runtime developer.
	type Content: Debug + Ord;
}

/// This is the Proof of Existence Module.
/// It is a simple module that allows accounts to claim existence of some data.
#[derive(Debug)]
pub struct Pallet<T: Config> {
	/// A simple storage map from content to the owner of that content.
	/// Accounts can make multiple different claims, but each claim can only have one owner.
	claims: BTreeMap<T::Content, T::AccountId>,
}

impl<T: Config> Pallet<T> {
	/// Create a new instance of the Proof of Existence Module.
	pub fn new() -> Self {
		Self { claims: BTreeMap::new() }
	}

	/// Get the owner (if any) of a claim.
	pub fn get_claim(&self, claim: &T::Content) -> Option<&T::AccountId> {
		self.claims.get(claim)
	}

	/// Create a new claim on behalf of the `caller`.
	/// This function will return an error if someone already has claimed that content.
	pub fn create_claim(&mut self, caller: T::AccountId, claim: T::Content) -> DispatchResult {
		if self.claims.contains_key(&claim) {
			return Err("this content is already claimed");
		}
		self.claims.insert(claim, caller);
		Ok(())
	}

	/// Revoke an existing claim on some content.
	/// This function should only succeed if the caller is the owner of an existing claim.
	/// It will return an error if the claim does not exist, or if the caller is not the owner.
	pub fn revoke_claim(&mut self, caller: T::AccountId, claim: T::Content) -> DispatchResult {
		let owner = self.get_claim(&claim).ok_or("claim does not exist")?;
		if caller != *owner {
			return Err("this content is owned by someone else");
		}
		self.claims.remove(&claim);
		Ok(())
	}
}

// A public enum which describes the calls we want to expose to the dispatcher.
// We should expect that the caller of each call will be provided by the dispatcher,
// and not included as a parameter of the call.
pub enum Call<T: Config> {
	CreateClaim { claim: T::Content },
	RevokeClaim { claim: T::Content },
}

/// Implementation of the dispatch logic, mapping from `POECall` to the appropriate underlying
/// function we want to execute.
impl<T: Config> crate::support::Dispatch for Pallet<T> {
	type Caller = T::AccountId;
	type Call = Call<T>;

	fn dispatch(
		&mut self,
		caller: Self::Caller,
		call: Self::Call,
	) -> crate::support::DispatchResult {
		match call {
			Call::CreateClaim { claim } => {
				self.create_claim(caller, claim)?;
			},
			Call::RevokeClaim { claim } => {
				self.revoke_claim(caller, claim)?;
			},
		}
		Ok(())
	}
}

#[cfg(test)]
mod test {
	struct TestConfig;

	impl super::Config for TestConfig {
		type Content = &'static str;
	}

	impl crate::system::Config for TestConfig {
		type AccountId = &'static str;
		type BlockNumber = u32;
		type Nonce = u32;
	}

	#[test]
	fn basic_proof_of_existence() {
		let mut poe = super::Pallet::<TestConfig>::new();
		assert_eq!(poe.get_claim(&"Hello, world!"), None);
		assert_eq!(poe.create_claim("alice", "Hello, world!"), Ok(()));
		assert_eq!(poe.get_claim(&"Hello, world!"), Some(&"alice"));
		assert_eq!(
			poe.create_claim("bob", "Hello, world!"),
			Err("this content is already claimed")
		);
		assert_eq!(poe.revoke_claim("alice", "Hello, world!"), Ok(()));
		assert_eq!(poe.create_claim("bob", "Hello, world!"), Ok(()));
	}
}
}
diff --git a/src/proof_of_existence.rs b/src/proof_of_existence.rs
index cd75fa24..a1755b34 100644
--- a/src/proof_of_existence.rs
+++ b/src/proof_of_existence.rs
@@ -52,6 +52,32 @@ impl<T: Config> Pallet<T> {
 	}
 }
 
+// A public enum which describes the calls we want to expose to the dispatcher.
+// We should expect that the caller of each call will be provided by the dispatcher,
+// and not included as a parameter of the call.
+pub enum Call<T: Config> {
+	/*
+		TODO:
+		Create variants for:
+		- `CreateClaim`
+		- `RevokeClaim`
+
+		Remember that you only need to pass in the `claim` data, as `caller` information is passed
+		in through the `dispatch` logic.
+	*/
+	RemoveMe(core::marker::PhantomData<T>),
+}
+
+/// Implementation of the dispatch logic, mapping from `POECall` to the appropriate underlying
+/// function we want to execute.
+/*
+	TODO:
+	Implement `crate::support::Dispatch` for `Pallet<T>`.
+
+	In your `dispatch` logic, match on `call` and forward the `caller` and `claim` data to the
+	appropriate function.
+*/
+
 #[cfg(test)]
 mod test {
 	struct TestConfig;
diff --git a/src/proof_of_existence.rs b/src/proof_of_existence.rs
index a1755b34..4e2e0ac7 100644
--- a/src/proof_of_existence.rs
+++ b/src/proof_of_existence.rs
@@ -56,27 +56,32 @@ impl<T: Config> Pallet<T> {
 // We should expect that the caller of each call will be provided by the dispatcher,
 // and not included as a parameter of the call.
 pub enum Call<T: Config> {
-	/*
-		TODO:
-		Create variants for:
-		- `CreateClaim`
-		- `RevokeClaim`
-
-		Remember that you only need to pass in the `claim` data, as `caller` information is passed
-		in through the `dispatch` logic.
-	*/
-	RemoveMe(core::marker::PhantomData<T>),
+	CreateClaim { claim: T::Content },
+	RevokeClaim { claim: T::Content },
 }
 
 /// Implementation of the dispatch logic, mapping from `POECall` to the appropriate underlying
 /// function we want to execute.
-/*
-	TODO:
-	Implement `crate::support::Dispatch` for `Pallet<T>`.
+impl<T: Config> crate::support::Dispatch for Pallet<T> {
+	type Caller = T::AccountId;
+	type Call = Call<T>;
 
-	In your `dispatch` logic, match on `call` and forward the `caller` and `claim` data to the
-	appropriate function.
-*/
+	fn dispatch(
+		&mut self,
+		caller: Self::Caller,
+		call: Self::Call,
+	) -> crate::support::DispatchResult {
+		match call {
+			Call::CreateClaim { claim } => {
+				self.create_claim(caller, claim)?;
+			},
+			Call::RevokeClaim { claim } => {
+				self.revoke_claim(caller, claim)?;
+			},
+		}
+		Ok(())
+	}
+}
 
 #[cfg(test)]
 mod test {