Interacting with UTXO

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

Now that we have established the basics of our UTXO module, let's add ways to interact with it.

Reading Materials

Dispatchable / Call / Extrinsic functions:

[!Note] Note that we'll implement UTXO pallet in dev_mode, it is not necessary to specify weight and call_index. However, feel free to do it if you want.

Implement for Pallet UTXO

Implement spend extrinsic. Highly recommend that keep the function simple and move all logics to intrinsics named with prefix do_..., execute_... (or whatever prefix you want 👀).

#![allow(unused)]
fn main() {
pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
    // 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(())
}
}

Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.

Just is for demo usage, feel free to add logic of using additional salt yourself 😉.

#![allow(unused)]
fn main() {
fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
    let mut trx = transaction.clone();
    for input in trx.inputs.iter_mut() {
        input.sigscript = H512::zero();
    }

    trx.encode()
}
}

Implement validate_transaction intrinsic with criteria:

  • Inputs and outputs are not empty
  • Each input is used exactly once
  • All inputs match to existing, unspent and unlocked outputs
  • Sum of input values does not overflow
  • Each output is defined exactly once and has nonzero value
  • Sum of output values does not overflow
  • Total output value must not exceed total input value
#![allow(unused)]
fn main() {
/// 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> {
    // 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,
    })
}
}

Implement do_spend intrinsic for spend extrinsic

#![allow(unused)]
fn main() {
/// 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 {
    // 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);
        // Optional, this event I used for log 
        Self::deposit_event(Event::TransactionOutputProcessed(hash));
    }

    Ok(())
}
}

Implement store_utxo for storage mutation

#![allow(unused)]
fn main() {
 fn store_utxo(utxo: &TransactionOutput, hash: H256) {
    // TODO [3-spend-utxo]
    UtxoStore::<T>::insert(hash, utxo);
    
    // further update 😉
}
}

Build the code

cargo build --release

Awesome work completing this step! 🚀 You're doing great. Now, let's advance 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,
    }


    /// [2-data-structure]
    /// Define extrinsics / dispatchable function
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
            // TODO remove this
            todo!()

            // TODO [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 {
            // TODO remove this
            todo!();

            // TODO [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!();
        }

        /// Mutate storage, insert / update new UTXOs
        fn store_utxo(utxo: &TransactionOutput, hash: H256) {
            // TODO remove this
            todo!();

            // // TODO [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> {
            // TODO remove this
            todo!();

            // // TODO [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> {
            // TODO remove this
            todo!();
            
            // TODO [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,
    }


    /// [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!();
        }

        /// 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 69a9535..a36637d 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -16,10 +16,10 @@ use super::{block_author::BlockAuthor, issuance::Issuance};
 
 pub use pallet::*;
 
-///  [2-data-structure]
+/// [2-data-structure]
 pub type Value = u128;
 
-///  [2-data-structure]
+/// [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)]
@@ -30,7 +30,7 @@ pub struct Transaction {
     pub outputs: Vec<TransactionOutput>,
 }
 
-///  [2-data-structure]
+/// [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)]
@@ -43,7 +43,7 @@ pub struct TransactionInput {
     pub sigscript: H512,
 }
 
-///  [2-data-structure]
+/// [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)]
@@ -64,7 +64,7 @@ pub mod pallet {
 
     use super::*;
 
-    ///  [2-data-structure]
+    /// [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.
@@ -72,9 +72,6 @@ pub mod pallet {
         type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 
         /// A source to determine the block author
-        /// Read more: `runtime/src/block_author.rs`
-        /// Pallet loosely coupling
-        /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets
         type BlockAuthor: BlockAuthor;
 
         /// A source to determine the issuance portion of the block reward
@@ -85,7 +82,7 @@ pub mod pallet {
     pub struct Pallet<T>(_);
 
 
-    ///  [2-data-structure]
+    /// [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.
@@ -93,7 +90,7 @@ pub mod pallet {
     #[pallet::getter(fn total_reward)]
     pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;
 
-    ///  [2-data-structure]
+    /// [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
@@ -109,7 +106,7 @@ pub mod pallet {
     >;
 
 
-    ///  [2-data-structure]
+    /// [2-data-structure]
     /// Pallets use events to inform users when important changes are made.
     #[pallet::event]
     #[pallet::generate_deposit(pub(super) fn deposit_event)]
@@ -127,7 +124,7 @@ pub mod pallet {
     }
 
 
-    ///  [2-data-structure]
+    /// [2-data-structure]
     /// Errors inform users that something went wrong.
     #[pallet::error]
     pub enum Error<T> {
@@ -158,23 +155,65 @@ pub mod pallet {
     }
 
 
-    ///  [2-data-structure]
+    /// [2-data-structure]
     /// Define extrinsics / dispatchable function
     #[pallet::call]
     impl<T: Config> Pallet<T> {
         pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
-            todo!();
-        }
+            // TODO remove this
+            todo!()
+
+            // TODO [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]
+    /// [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 {
+            // TODO remove this
             todo!();
+
+            // TODO [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
@@ -184,12 +223,27 @@ pub mod pallet {
 
         /// Mutate storage, insert / update new UTXOs
         fn store_utxo(utxo: &TransactionOutput, hash: H256) {
+            // TODO remove this
             todo!();
+
+            // // TODO [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> {
+            // TODO remove this
             todo!();
+
+            // // TODO [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
@@ -197,7 +251,100 @@ pub mod pallet {
         pub fn validate_transaction(
             transaction: &Transaction,
         ) -> Result<ValidTransaction, &'static str> {
+            // TODO remove this
             todo!();
+            
+            // TODO [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,
+            // })
         }
     }
 }
\ No newline at end of file
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index a36637d..00433df 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -160,23 +160,20 @@ pub mod pallet {
     #[pallet::call]
     impl<T: Config> Pallet<T> {
         pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
-            // TODO remove this
-            todo!()
-
-            // TODO [3-spend-utxo]
+            // [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(())
+            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(())
         }
     }
 
@@ -186,34 +183,31 @@ pub mod pallet {
         /// 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 {
-            // TODO remove this
-            todo!();
-
-            // TODO [3-spend-utxo]
+            // [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(())
+            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
@@ -223,27 +217,21 @@ pub mod pallet {
 
         /// Mutate storage, insert / update new UTXOs
         fn store_utxo(utxo: &TransactionOutput, hash: H256) {
-            // TODO remove this
-            todo!();
-
-            // // TODO [3-spend-utxo]
-            // UtxoStore::<T>::insert(hash, utxo);
+            // [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> {
-            // TODO remove this
-            todo!();
-
-            // // TODO [3-spend-utxo]
-            // let mut trx = transaction.clone();
-            // for input in trx.inputs.iter_mut() {
-            //     input.sigscript = H512::zero();
-            // }
+            // [3-spend-utxo]
+            let mut trx = transaction.clone();
+            for input in trx.inputs.iter_mut() {
+                input.sigscript = H512::zero();
+            }
 
-            // trx.encode()
+            trx.encode()
         }
 
         /// Check transaction for validity, errors, & race conditions
@@ -251,100 +239,97 @@ pub mod pallet {
         pub fn validate_transaction(
             transaction: &Transaction,
         ) -> Result<ValidTransaction, &'static str> {
-            // TODO remove this
-            todo!();
-            
-            // TODO [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,
-            // })
+            // [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,
+            })
         }
     }
 }
\ No newline at end of file