Disperse Reward

[!Note] Resolve all TODO in runtime/src/utxo.rs to complete this step.

Reading Materials

The block reward is a combination of the block subsidy and all transaction fees paid by transactions in a specific block. When miners mine a block, they receive a block subsidy in the form of newly minted bitcoin. All transactions also include a fee, which miners collect. The block reward is the sum of these two amounts. As block subsidies are cut in half every four years, fees will slowly become a greater portion of the block reward. The term block reward and block subsidy are occasionally used interchangeably.

The block reward is paid out in the blockchain transaction of each block. This special transaction is the first transaction in every block, and it has no inputs.

So the problem in this step is that we've to distribute the block reward to the miners who successfully solve the puzzle to mine new blocks.

Before go to this tutorial, please read these documents to understand transaction lifecycle in Substrate:

graph LR
    subgraph AfterTransactions
    direction LR
        OnIdle --> OnFinalize
    end

    subgraph OnChain
    direction LR
        Optional --> BeforeExtrinsics
        BeforeExtrinsics --> Inherents
        Inherents --> Poll
        Poll --> Transactions
        Transactions --> AfterTransactions
    end

    subgraph Optional
        OnRuntimeUpgrade
    end


    subgraph BeforeExtrinsics
        OnInitialize
    end

    subgraph Transactions
        Transaction1 --> UnsignedTransaction2 --> Transaction3
    end

    subgraph Inherents
        Inherent1 --> Inherent2
    end

Implement

To disperse block reward to block author, we'll use on_finalize hook.

To implement the Hooks trait provides logic that is executed at the end of each block.

  • Start with #[pallet::hooks] macro.
  • Implements the Hooks trait for the pallet. This trait is generic over the BlockNumberFor<T>, which represents the type of block number defined in the runtime configuration.
  • fn on_finalize(_n: BlockNumberFor<T>): This function is triggered automatically at the end of each block.
#![allow(unused)]
fn main() {
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    fn on_finalize(_n: BlockNumberFor<T>) {
        match T::BlockAuthor::block_author() {
            // Block author did not provide key to claim reward
            None => Self::deposit_event(Event::RewardWasted),
            // Block author did provide key, so issue thir reward
            Some(author) => Self::disperse_reward(&author),
        }
    }
}
}

Implement logic of function disperse_reward

#![allow(unused)]
fn main() {
/// Redistribute combined reward value to block Author
fn disperse_reward(author: &Public) {
    // take the cumulative reward in storage
    // plus issuance of blocknumber
    let reward = TotalReward::<T>::take()
        + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());

    // create new UTXO
    let utxo = TransactionOutput {
        value: reward,
        pubkey: H256::from_slice(author.as_slice()),
    };

    // hash UTXO to get the key
    let hash = BlakeTwo256::hash_of(&(
        &utxo,
        frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
    ));

    // mutate storage
    Self::store_utxo(&utxo, hash);
    
    // emit event `RewardDistributed`
    Self::deposit_event(Event::RewardDistributed(reward, hash));
}
}

Build the code

cargo build --release

Fantastic job completing a new step! 🌟 You're almost there. Let's move forward to next step.

#![allow(unused)]
fn main() {
// We make sure this pallet uses `no_std` for compiling to Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_core::{
    sr25519::{Public, Signature},
    ByteArray, H256, H512,
};
use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};

use super::{block_author::BlockAuthor, issuance::Issuance};

pub use pallet::*;

/// [2-data-structure]
pub type Value = u128;

/// [2-data-structure]
/// Single transaction to be dispatched
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct Transaction {
    /// UTXOs to be used as inputs for current transaction
    pub inputs: Vec<TransactionInput>,
    /// UTXOs to be created as a result of current transaction dispatch
    pub outputs: Vec<TransactionOutput>,
}

/// [2-data-structure]
/// Single transaction input that refers to one UTXO
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct TransactionInput {
    /// Reference to an UTXO to be spent
    pub outpoint: H256,

    /// Proof that transaction owner is authorized to spend referred UTXO &
    /// that the entire transaction is untampered
    pub sigscript: H512,
}

/// [2-data-structure]
/// Single transaction output to create upon transaction dispatch
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct TransactionOutput {
    /// Value associated with this output
    pub value: Value,

    /// Public key associated with this output. In order to spend this output
    /// owner must provide a proof by hashing the whole `Transaction` and
    /// signing it with a corresponding private key.
    pub pubkey: H256,
}

#[frame_support::pallet(dev_mode)]
pub mod pallet {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    use super::*;

    /// [2-data-structure]
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

        /// A source to determine the block author
        type BlockAuthor: BlockAuthor;

        /// A source to determine the issuance portion of the block reward
        type Issuance: Issuance<BlockNumberFor<Self>, Value>;
    }

    #[pallet::pallet]
    pub struct Pallet<T>(_);


    /// [2-data-structure]
    /// Total reward value to be redistributed among authorities.
    /// It is accumulated from transactions during block execution
    /// and then dispersed to validators on block finalization.
    #[pallet::storage]
    #[pallet::getter(fn total_reward)]
    pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;

    /// [2-data-structure]
    /// All valid unspent transaction outputs are stored in this map.
    /// Initial set of UTXO is populated from the list stored in genesis.
    /// We use the identity hasher here because the cryptographic hashing is
    /// done explicitly.
    /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput`
    #[pallet::storage]
    #[pallet::getter(fn utxo_store)]
    pub type UtxoStore<T: Config> = StorageMap<
        Hasher = Identity,
        Key = H256,
        Value = TransactionOutput,
        QueryKind = OptionQuery,
    >;


    /// [2-data-structure]
    /// Pallets use events to inform users when important changes are made.
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// Dispatch transaction successful
        TransactionSuccess(Transaction),
        /// UTXO out processed
        TransactionOutputProcessed(H256),
        /// Reward distributed to `BlockAuthor`
        RewardDistributed(Value, H256),
        /// Faucet to `To`
        Faucet(Value, H256),
        /// No one get reward
        RewardWasted,
    }


    /// [2-data-structure]
    /// Errors inform users that something went wrong.
    #[pallet::error]
    pub enum Error<T> {
        /// Missing `Transaction` Input
        MissingInput,
        /// Reward overflow
        RewardOverflow,
        /// Maximum transaction depth
        MaximumTransactionDepth,
        /// Empty input
        EmptyInput,
        /// Empty output
        EmptyOutput,
        /// Each input must only be used once
        DuplicatedInput,
        /// Each output must be defined only once
        DuplicatedOutput,
        /// Input value is overflow
        InputOverflow,
        /// Output value is overflow
        OutputOverflow,
        /// Output spent must lte than Input spent
        OutputOverInput,
        /// Zero amount spent
        ZeroAmount,
        /// Invalid signature
        InvalidSignature,
    }

    /// TODO [4-dispersed-reward]
    // #[pallet::hooks]
    // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    //     fn on_finalize(_n: BlockNumberFor<T>) {
    //         match T::BlockAuthor::block_author() {
    //             // Block author did not provide key to claim reward
    //             None => Self::deposit_event(Event::RewardWasted),
    //             // Block author did provide key, so issue thir reward
    //             Some(author) => Self::disperse_reward(&author),
    //         }
    //     }
    // }

    /// [2-data-structure]
    /// Define extrinsics / dispatchable function
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
            // [3-spend-utxo]
            // validate transaction
            let transaction_validity = Self::validate_transaction(&transaction)?;
            ensure!(
                transaction_validity.requires.is_empty(),
                Error::<T>::MissingInput
            );

            // implement logic
            Self::do_spend(&transaction, transaction_validity.priority as Value)?;
            // emit event
            Self::deposit_event(Event::<T>::TransactionSuccess(transaction));

            Ok(())
        }
    }

    /// [2-data-structure]
    /// Define intrinsics
    impl<T: Config> Pallet<T> {
        /// Implement spend logic, update storage to reflect changes made by transaction
        /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
        fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
            // [3-spend-utxo]
            // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward.
            let new_total = TotalReward::<T>::get()
                .checked_add(reward)
                .ok_or(Error::<T>::RewardOverflow)?;
            TotalReward::<T>::put(new_total);

            // Removing spent UTXOs
            for input in &transaction.inputs {
                UtxoStore::<T>::remove(input.outpoint);
            }

            let mut index: u64 = 0;
            for output in &transaction.outputs {
                let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index));
                // validated before, this is safe
                index = index
                    .checked_add(1)
                    .ok_or(Error::<T>::MaximumTransactionDepth)
                    .unwrap();
                Self::store_utxo(output, hash);
                Self::deposit_event(Event::TransactionOutputProcessed(hash));
            }

            Ok(())
        }

        /// Redistribute combined reward value to block Author
        fn disperse_reward(author: &Public) {
            // TODO remove this
            todo!();

            // TODO [4-dispersed-reward]

            // take the rest of reward
            // plus issuance reward of current block number
            // let reward = TotalReward::<T>::take()
            // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());

            // let utxo = TransactionOutput {
            //     value: reward,
            //     pubkey: H256::from_slice(author.as_slice()),
            // };

            // let hash = BlakeTwo256::hash_of(&(
            //     &utxo,
            //     frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
            // ));

            // Self::store_utxo(&utxo, hash);
            // Self::deposit_event(Event::RewardDistributed(reward, hash));  
        }

        /// Mutate storage, insert / update new UTXOs
        fn store_utxo(utxo: &TransactionOutput, hash: H256) {
            // [3-spend-utxo]
            UtxoStore::<T>::insert(hash, utxo);
            
            // // further update 😉
        }

        /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
        fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
            // [3-spend-utxo]
            let mut trx = transaction.clone();
            for input in trx.inputs.iter_mut() {
                input.sigscript = H512::zero();
            }

            trx.encode()
        }

        /// Check transaction for validity, errors, & race conditions
        /// Called by both transaction pool and runtime execution
        pub fn validate_transaction(
            transaction: &Transaction,
        ) -> Result<ValidTransaction, &'static str> {
            // [3-spend-utxo]
            // Check inputs and outputs are not empty
            ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput);
            ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput);

            // Check each input is used exactly once
            {
                let input_set: BTreeMap<_, ()> =
                    transaction.inputs.iter().map(|input| (input, ())).collect();
                ensure!(
                    input_set.len() == transaction.inputs.len(),
                    Error::<T>::DuplicatedInput
                );
            }
            {
                let output_set: BTreeMap<_, ()> = transaction
                    .outputs
                    .iter()
                    .map(|output| (output, ()))
                    .collect();
                ensure!(
                    output_set.len() == transaction.outputs.len(),
                    Error::<T>::DuplicatedOutput
                );
            }

            let mut total_input: Value = 0;
            let mut total_output: Value = 0;
            let mut output_index: u64 = 0;
            let simple_transaction = Self::get_simple_transaction(transaction);

            // Variables sent to transaction pool
            let mut missing_utxos = Vec::new();
            let mut new_utxos = Vec::new();
            let mut reward = 0;

            for input in transaction.inputs.iter() {
                // Check all inputs match to existing, unspent and unlocked outputs
                if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) {
                    // Check provided signatures are valid
                    let is_valid_sig = sp_io::crypto::sr25519_verify(
                        &Signature::from_raw(*input.sigscript.as_fixed_bytes()),
                        &simple_transaction,
                        &Public::from_h256(input_utxo.pubkey),
                    );
                    ensure!(is_valid_sig, Error::<T>::InvalidSignature);
                    // Check sum of input values does not overflow
                    total_input = total_input
                        .checked_add(input_utxo.value)
                        .ok_or(Error::<T>::InputOverflow)?;
                } else {
                    missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec());
                }
            }

            // Check each output is defined exactly once and has nonzero value
            for output in transaction.outputs.iter() {
                ensure!(output.value > 0, Error::<T>::ZeroAmount);
                let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index));
                output_index = output_index
                    .checked_add(1)
                    .ok_or(Error::<T>::MaximumTransactionDepth)?;
                // Check new outputs do not collide with existing ones
                ensure!(
                    !UtxoStore::<T>::contains_key(hash),
                    Error::<T>::DuplicatedOutput
                );
                // Check sum of output values does not overflow
                total_output = total_output
                    .checked_add(output.value)
                    .ok_or(Error::<T>::OutputOverflow)?;
                new_utxos.push(hash.as_fixed_bytes().to_vec());
            }

            // If no race condition, check the math
            if missing_utxos.is_empty() {
                // Check total output value must not exceed total input value
                ensure!(total_input >= total_output, Error::<T>::OutputOverInput);
                reward = total_input
                    .checked_sub(total_output)
                    .ok_or(Error::<T>::RewardOverflow)?;
            }

            // Returns transaction details
            Ok(ValidTransaction {
                requires: missing_utxos,
                provides: new_utxos,
                priority: reward as u64,
                longevity: TransactionLongevity::max_value(),
                propagate: true,
            })
        }
    }
}
}
#![allow(unused)]
fn main() {
// We make sure this pallet uses `no_std` for compiling to Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_core::{
    sr25519::{Public, Signature},
    ByteArray, H256, H512,
};
use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};

use super::{block_author::BlockAuthor, issuance::Issuance};

pub use pallet::*;

/// [2-data-structure]
pub type Value = u128;

/// [2-data-structure]
/// Single transaction to be dispatched
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct Transaction {
    /// UTXOs to be used as inputs for current transaction
    pub inputs: Vec<TransactionInput>,
    /// UTXOs to be created as a result of current transaction dispatch
    pub outputs: Vec<TransactionOutput>,
}

/// [2-data-structure]
/// Single transaction input that refers to one UTXO
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct TransactionInput {
    /// Reference to an UTXO to be spent
    pub outpoint: H256,

    /// Proof that transaction owner is authorized to spend referred UTXO &
    /// that the entire transaction is untampered
    pub sigscript: H512,
}

/// [2-data-structure]
/// Single transaction output to create upon transaction dispatch
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
pub struct TransactionOutput {
    /// Value associated with this output
    pub value: Value,

    /// Public key associated with this output. In order to spend this output
    /// owner must provide a proof by hashing the whole `Transaction` and
    /// signing it with a corresponding private key.
    pub pubkey: H256,
}

#[frame_support::pallet(dev_mode)]
pub mod pallet {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    use super::*;

    /// [2-data-structure]
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

        /// A source to determine the block author
        type BlockAuthor: BlockAuthor;

        /// A source to determine the issuance portion of the block reward
        type Issuance: Issuance<BlockNumberFor<Self>, Value>;
    }

    #[pallet::pallet]
    pub struct Pallet<T>(_);


    /// [2-data-structure]
    /// Total reward value to be redistributed among authorities.
    /// It is accumulated from transactions during block execution
    /// and then dispersed to validators on block finalization.
    #[pallet::storage]
    #[pallet::getter(fn total_reward)]
    pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;

    /// [2-data-structure]
    /// All valid unspent transaction outputs are stored in this map.
    /// Initial set of UTXO is populated from the list stored in genesis.
    /// We use the identity hasher here because the cryptographic hashing is
    /// done explicitly.
    /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput`
    #[pallet::storage]
    #[pallet::getter(fn utxo_store)]
    pub type UtxoStore<T: Config> = StorageMap<
        Hasher = Identity,
        Key = H256,
        Value = TransactionOutput,
        QueryKind = OptionQuery,
    >;


    /// [2-data-structure]
    /// Pallets use events to inform users when important changes are made.
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// Dispatch transaction successful
        TransactionSuccess(Transaction),
        /// UTXO out processed
        TransactionOutputProcessed(H256),
        /// Reward distributed to `BlockAuthor`
        RewardDistributed(Value, H256),
        /// Faucet to `To`
        Faucet(Value, H256),
        /// No one get reward
        RewardWasted,
    }


    /// [2-data-structure]
    /// Errors inform users that something went wrong.
    #[pallet::error]
    pub enum Error<T> {
        /// Missing `Transaction` Input
        MissingInput,
        /// Reward overflow
        RewardOverflow,
        /// Maximum transaction depth
        MaximumTransactionDepth,
        /// Empty input
        EmptyInput,
        /// Empty output
        EmptyOutput,
        /// Each input must only be used once
        DuplicatedInput,
        /// Each output must be defined only once
        DuplicatedOutput,
        /// Input value is overflow
        InputOverflow,
        /// Output value is overflow
        OutputOverflow,
        /// Output spent must lte than Input spent
        OutputOverInput,
        /// Zero amount spent
        ZeroAmount,
        /// Invalid signature
        InvalidSignature,
    }

    /// [4-dispersed-reward]
    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
        fn on_finalize(_n: BlockNumberFor<T>) {
            match T::BlockAuthor::block_author() {
                // Block author did not provide key to claim reward
                None => Self::deposit_event(Event::RewardWasted),
                // Block author did provide key, so issue thir reward
                Some(author) => Self::disperse_reward(&author),
            }
        }
    }

    /// [2-data-structure]
    /// Define extrinsics / dispatchable function
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
            // [3-spend-utxo]
            // validate transaction
            let transaction_validity = Self::validate_transaction(&transaction)?;
            ensure!(
                transaction_validity.requires.is_empty(),
                Error::<T>::MissingInput
            );

            // implement logic
            Self::do_spend(&transaction, transaction_validity.priority as Value)?;
            // emit event
            Self::deposit_event(Event::<T>::TransactionSuccess(transaction));

            Ok(())
        }
    }

    /// [2-data-structure]
    /// Define intrinsics
    impl<T: Config> Pallet<T> {
        /// Implement spend logic, update storage to reflect changes made by transaction
        /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
        fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
            // [3-spend-utxo]
            // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward.
            let new_total = TotalReward::<T>::get()
                .checked_add(reward)
                .ok_or(Error::<T>::RewardOverflow)?;
            TotalReward::<T>::put(new_total);

            // Removing spent UTXOs
            for input in &transaction.inputs {
                UtxoStore::<T>::remove(input.outpoint);
            }

            let mut index: u64 = 0;
            for output in &transaction.outputs {
                let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index));
                // validated before, this is safe
                index = index
                    .checked_add(1)
                    .ok_or(Error::<T>::MaximumTransactionDepth)
                    .unwrap();
                Self::store_utxo(output, hash);
                Self::deposit_event(Event::TransactionOutputProcessed(hash));
            }

            Ok(())
        }

        /// Redistribute combined reward value to block Author
        fn disperse_reward(author: &Public) {
            // [4-dispersed-reward]

            // take the rest of reward
            // plus issuance reward of current block number
            let reward = TotalReward::<T>::take()
            + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());

            let utxo = TransactionOutput {
                value: reward,
                pubkey: H256::from_slice(author.as_slice()),
            };

            let hash = BlakeTwo256::hash_of(&(
                &utxo,
                frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
            ));

            Self::store_utxo(&utxo, hash);
            Self::deposit_event(Event::RewardDistributed(reward, hash));  
        }

        /// Mutate storage, insert / update new UTXOs
        fn store_utxo(utxo: &TransactionOutput, hash: H256) {
            // [3-spend-utxo]
            UtxoStore::<T>::insert(hash, utxo);
            
            // // further update 😉
        }

        /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
        fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
            // [3-spend-utxo]
            let mut trx = transaction.clone();
            for input in trx.inputs.iter_mut() {
                input.sigscript = H512::zero();
            }

            trx.encode()
        }

        /// Check transaction for validity, errors, & race conditions
        /// Called by both transaction pool and runtime execution
        pub fn validate_transaction(
            transaction: &Transaction,
        ) -> Result<ValidTransaction, &'static str> {
            // [3-spend-utxo]
            // Check inputs and outputs are not empty
            ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput);
            ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput);

            // Check each input is used exactly once
            {
                let input_set: BTreeMap<_, ()> =
                    transaction.inputs.iter().map(|input| (input, ())).collect();
                ensure!(
                    input_set.len() == transaction.inputs.len(),
                    Error::<T>::DuplicatedInput
                );
            }
            {
                let output_set: BTreeMap<_, ()> = transaction
                    .outputs
                    .iter()
                    .map(|output| (output, ()))
                    .collect();
                ensure!(
                    output_set.len() == transaction.outputs.len(),
                    Error::<T>::DuplicatedOutput
                );
            }

            let mut total_input: Value = 0;
            let mut total_output: Value = 0;
            let mut output_index: u64 = 0;
            let simple_transaction = Self::get_simple_transaction(transaction);

            // Variables sent to transaction pool
            let mut missing_utxos = Vec::new();
            let mut new_utxos = Vec::new();
            let mut reward = 0;

            for input in transaction.inputs.iter() {
                // Check all inputs match to existing, unspent and unlocked outputs
                if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) {
                    // Check provided signatures are valid
                    let is_valid_sig = sp_io::crypto::sr25519_verify(
                        &Signature::from_raw(*input.sigscript.as_fixed_bytes()),
                        &simple_transaction,
                        &Public::from_h256(input_utxo.pubkey),
                    );
                    ensure!(is_valid_sig, Error::<T>::InvalidSignature);
                    // Check sum of input values does not overflow
                    total_input = total_input
                        .checked_add(input_utxo.value)
                        .ok_or(Error::<T>::InputOverflow)?;
                } else {
                    missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec());
                }
            }

            // Check each output is defined exactly once and has nonzero value
            for output in transaction.outputs.iter() {
                ensure!(output.value > 0, Error::<T>::ZeroAmount);
                let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index));
                output_index = output_index
                    .checked_add(1)
                    .ok_or(Error::<T>::MaximumTransactionDepth)?;
                // Check new outputs do not collide with existing ones
                ensure!(
                    !UtxoStore::<T>::contains_key(hash),
                    Error::<T>::DuplicatedOutput
                );
                // Check sum of output values does not overflow
                total_output = total_output
                    .checked_add(output.value)
                    .ok_or(Error::<T>::OutputOverflow)?;
                new_utxos.push(hash.as_fixed_bytes().to_vec());
            }

            // If no race condition, check the math
            if missing_utxos.is_empty() {
                // Check total output value must not exceed total input value
                ensure!(total_input >= total_output, Error::<T>::OutputOverInput);
                reward = total_input
                    .checked_sub(total_output)
                    .ok_or(Error::<T>::RewardOverflow)?;
            }

            // Returns transaction details
            Ok(ValidTransaction {
                requires: missing_utxos,
                provides: new_utxos,
                priority: reward as u64,
                longevity: TransactionLongevity::max_value(),
                propagate: true,
            })
        }
    }
}
}
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 00433df..1c51039 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -154,6 +154,18 @@ pub mod pallet {
         InvalidSignature,
     }
 
+    /// TODO [4-dispersed-reward]
+    // #[pallet::hooks]
+    // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+    //     fn on_finalize(_n: BlockNumberFor<T>) {
+    //         match T::BlockAuthor::block_author() {
+    //             // Block author did not provide key to claim reward
+    //             None => Self::deposit_event(Event::RewardWasted),
+    //             // Block author did provide key, so issue thir reward
+    //             Some(author) => Self::disperse_reward(&author),
+    //         }
+    //     }
+    // }
 
     /// [2-data-structure]
     /// Define extrinsics / dispatchable function
@@ -212,7 +224,28 @@ pub mod pallet {
 
         /// Redistribute combined reward value to block Author
         fn disperse_reward(author: &Public) {
+            // TODO remove this
             todo!();
+
+            // TODO [4-dispersed-reward]
+
+            // take the rest of reward
+            // plus issuance reward of current block number
+            // let reward = TotalReward::<T>::take()
+            // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
+
+            // let utxo = TransactionOutput {
+            //     value: reward,
+            //     pubkey: H256::from_slice(author.as_slice()),
+            // };
+
+            // let hash = BlakeTwo256::hash_of(&(
+            //     &utxo,
+            //     frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
+            // ));
+
+            // Self::store_utxo(&utxo, hash);
+            // Self::deposit_event(Event::RewardDistributed(reward, hash));  
         }
 
         /// Mutate storage, insert / update new UTXOs
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 1c51039..ed8ec6a 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -154,18 +154,18 @@ pub mod pallet {
         InvalidSignature,
     }
 
-    /// TODO [4-dispersed-reward]
-    // #[pallet::hooks]
-    // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
-    //     fn on_finalize(_n: BlockNumberFor<T>) {
-    //         match T::BlockAuthor::block_author() {
-    //             // Block author did not provide key to claim reward
-    //             None => Self::deposit_event(Event::RewardWasted),
-    //             // Block author did provide key, so issue thir reward
-    //             Some(author) => Self::disperse_reward(&author),
-    //         }
-    //     }
-    // }
+    /// [4-dispersed-reward]
+    #[pallet::hooks]
+    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+        fn on_finalize(_n: BlockNumberFor<T>) {
+            match T::BlockAuthor::block_author() {
+                // Block author did not provide key to claim reward
+                None => Self::deposit_event(Event::RewardWasted),
+                // Block author did provide key, so issue thir reward
+                Some(author) => Self::disperse_reward(&author),
+            }
+        }
+    }
 
     /// [2-data-structure]
     /// Define extrinsics / dispatchable function
@@ -224,28 +224,25 @@ pub mod pallet {
 
         /// Redistribute combined reward value to block Author
         fn disperse_reward(author: &Public) {
-            // TODO remove this
-            todo!();
-
-            // TODO [4-dispersed-reward]
+            // [4-dispersed-reward]
 
             // take the rest of reward
             // plus issuance reward of current block number
-            // let reward = TotalReward::<T>::take()
-            // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
+            let reward = TotalReward::<T>::take()
+            + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
 
-            // let utxo = TransactionOutput {
-            //     value: reward,
-            //     pubkey: H256::from_slice(author.as_slice()),
-            // };
+            let utxo = TransactionOutput {
+                value: reward,
+                pubkey: H256::from_slice(author.as_slice()),
+            };
 
-            // let hash = BlakeTwo256::hash_of(&(
-            //     &utxo,
-            //     frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
-            // ));
+            let hash = BlakeTwo256::hash_of(&(
+                &utxo,
+                frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
+            ));
 
-            // Self::store_utxo(&utxo, hash);
-            // Self::deposit_event(Event::RewardDistributed(reward, hash));  
+            Self::store_utxo(&utxo, hash);
+            Self::deposit_event(Event::RewardDistributed(reward, hash));  
         }
 
         /// Mutate storage, insert / update new UTXOs