Making Your System Functional
We have again established the basis of a new Pallet.
Let's add functions which make it useful.
Block Number
Your blockchain's blocknumber is stored in the System Pallet, and the System Pallet needs to expose functions which allow us to access and modify the block number.
For this we need two simple functions:
fn block_number
- a function that returns the currently stored blocknumber.fn inc_block_number
- a function that increments the current block number by one.
This should be everything that a basic blockchain needs to function.
Nonce
The nonce
represents "a number used once".
In this context, each user on your blockchain has a nonce
which gives a unique value to each transaction the user submits to the blockchain.
Remember that blockchains are decentralized and distributed systems, and transactions do not inherently have a deterministic order. For a user, we can assign an order to different transactions by using this nonce to keep track of how many transactions the user has executed on the blockchain.
For this, we again use a BTreeMap
to give each user their own nonce
counter.
Our simple blockchain won't use this value, but for the sake of example, we will keep track of it by creating an inc_nonce
function. If you were creating a more complex blockchain, the user nonce
would become an important part of your system.
Safe Math?
We just explained the importance of using safe math when writing the Balances Pallet.
In that context, it is easy to see how a user could provide malicious inputs, and cause simple underflows or overflows if our system did not check the math.
However, you will see in the templates provided, that these new functions in the System Pallet do not return a result, and thus do not provide error handling.
Is this okay?
As you will notice, the blocknumber
and nonce
storage items only provide APIs to increment by one. In our System, both of these numbers are represented by u32
, which means that over 4.2 billion calls to those functions need to occur before an overflow would happen.
Assuming a user does one transaction every block, and a new block is generated every 6 seconds, it would take over 800 years for an overflow to occur. So in this situation, we are preferring an API which requires no error handling rather than one which does.
End of the day, this is a design decision and a preference which is left to the developer. This tutorial chooses this API because this is exactly the API exposed by Substrate and the Polkadot SDK. There is nothing wrong with making these functions handle errors, so feel free to do this if you choose.
Build Your System Pallet
Follow the instructions in the template to complete:
fn block_number
fn inc_block_number
fn inc_nonce
Then write tests which verify that these functions work as expected, and that your state is correctly updated. There should be no compiler warnings after this step!
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the System Pallet. /// It handles low level state needed for your blockchain. pub struct Pallet { /// The current block number. block_number: u32, /// A map from an account to their nonce. nonce: BTreeMap<String, u32>, } impl Pallet { /// Create a new instance of the System Pallet. pub fn new() -> Self { Self { block_number: 0, nonce: BTreeMap::new() } } /// Get the current block number. pub fn block_number(&self) -> u32 { /* TODO: Return the current block number. */ unimplemented!() } // This function can be used to increment the block number. // Increases the block number by one. pub fn inc_block_number(&mut self) { /* TODO: Increment the current block number by one. */ unimplemented!() } // Increment the nonce of an account. This helps us keep track of how many transactions each // account has made. pub fn inc_nonce(&mut self, who: &String) { /* TODO: Get the current nonce of `who`, and increment it by one. */ unimplemented!() } } #[cfg(test)] mod test { #[test] fn init_system() { /* TODO: Create a test which checks the following: - Increment the current block number. - Increment the nonce of `alice`. - Check the block number is what we expect. - Check the nonce of `alice` is what we expect. - Check the nonce of `bob` is what we expect. */ } } }
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the System Pallet. /// It handles low level state needed for your blockchain. pub struct Pallet { /// The current block number. block_number: u32, /// A map from an account to their nonce. nonce: BTreeMap<String, u32>, } impl Pallet { /// Create a new instance of the System Pallet. pub fn new() -> Self { Self { block_number: 0, nonce: BTreeMap::new() } } /// Get the current block number. pub fn block_number(&self) -> u32 { self.block_number } // This function can be used to increment the block number. // Increases the block number by one. pub fn inc_block_number(&mut self) { self.block_number += 1; } // Increment the nonce of an account. This helps us keep track of how many transactions each // account has made. pub fn inc_nonce(&mut self, who: &String) { let nonce: u32 = *self.nonce.get(who).unwrap_or(&0); let new_nonce = nonce + 1; self.nonce.insert(who.clone(), new_nonce); } } #[cfg(test)] mod test { #[test] fn init_system() { let mut system = super::Pallet::new(); system.inc_block_number(); system.inc_nonce(&"alice".to_string()); assert_eq!(system.block_number(), 1); assert_eq!(system.nonce.get("alice"), Some(&1)); assert_eq!(system.nonce.get("bob"), None); } } }
diff --git a/src/system.rs b/src/system.rs
index 99d537c8..b46402c3 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -14,4 +14,39 @@ impl Pallet {
pub fn new() -> Self {
Self { block_number: 0, nonce: BTreeMap::new() }
}
+
+ /// Get the current block number.
+ pub fn block_number(&self) -> u32 {
+ /* TODO: Return the current block number. */
+ unimplemented!()
+ }
+
+ // This function can be used to increment the block number.
+ // Increases the block number by one.
+ pub fn inc_block_number(&mut self) {
+ /* TODO: Increment the current block number by one. */
+ unimplemented!()
+ }
+
+ // Increment the nonce of an account. This helps us keep track of how many transactions each
+ // account has made.
+ pub fn inc_nonce(&mut self, who: &String) {
+ /* TODO: Get the current nonce of `who`, and increment it by one. */
+ unimplemented!()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn init_system() {
+ /* TODO: Create a test which checks the following:
+ - Increment the current block number.
+ - Increment the nonce of `alice`.
+
+ - Check the block number is what we expect.
+ - Check the nonce of `alice` is what we expect.
+ - Check the nonce of `bob` is what we expect.
+ */
+ }
}
diff --git a/src/system.rs b/src/system.rs
index b46402c3..967578d0 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -17,22 +17,21 @@ impl Pallet {
/// Get the current block number.
pub fn block_number(&self) -> u32 {
- /* TODO: Return the current block number. */
- unimplemented!()
+ self.block_number
}
// This function can be used to increment the block number.
// Increases the block number by one.
pub fn inc_block_number(&mut self) {
- /* TODO: Increment the current block number by one. */
- unimplemented!()
+ self.block_number += 1;
}
// Increment the nonce of an account. This helps us keep track of how many transactions each
// account has made.
pub fn inc_nonce(&mut self, who: &String) {
- /* TODO: Get the current nonce of `who`, and increment it by one. */
- unimplemented!()
+ let nonce: u32 = *self.nonce.get(who).unwrap_or(&0);
+ let new_nonce = nonce + 1;
+ self.nonce.insert(who.clone(), new_nonce);
}
}
@@ -40,13 +39,12 @@ impl Pallet {
mod test {
#[test]
fn init_system() {
- /* TODO: Create a test which checks the following:
- - Increment the current block number.
- - Increment the nonce of `alice`.
+ let mut system = super::Pallet::new();
+ system.inc_block_number();
+ system.inc_nonce(&"alice".to_string());
- - Check the block number is what we expect.
- - Check the nonce of `alice` is what we expect.
- - Check the nonce of `bob` is what we expect.
- */
+ assert_eq!(system.block_number(), 1);
+ assert_eq!(system.nonce.get("alice"), Some(&1));
+ assert_eq!(system.nonce.get("bob"), None);
}
}