Welcome!




A blockchain is, at it's heart, just a glorified database. It keeps track of stuff. In this book we'll explore how that works and why anyone should care.

There's a lot of data in the world. More and more everyday. While data can be useful, it's most useful to those who have access to it. Those In today's world that means banks, social networks, and governements. Take a look at their market caps as well as their influence over the state of things. There's a direct correlation between access to data and power and wealth. It's a zero sum game. If you're not a bank, social network, or government this sucks.

Good news though! There's this cool thing called Free Open Source Software, and there's a subset of that called P2P decentralized technology, and there's a subset of that called blockchains. Why do these matter? Well because silly... they're free for anyone to use and the networks are P2P so the users control the networks and data, and thus, keep the value they create. The magic of blockchains specifically is that they align incentives in a way that gets people to do stuff that benefits them individually as well as the network as a whole. It's a positive sum win/win game. The is achieved in a variety of ways depending on the blockchain, but common stakeholders include:

  • people who use the network to do stuff,
  • people who develop the code that the network runs on,
  • and people who contribute to the security of the network by verifying and creating blocks of transactions.

These stakeholders are incentivized to contribute to the health and growth network because they derive value from it. This can come in the form of direct monetary incentivization for miners to verify and create blocks, developers wanting to build things that people actually use so that their dApp tokens are valued highly, or users signalling their preferences by using things they like and dropping things that don't work. If this system is balanced, secure, and correctly designed all parties involved will benefit more than they would otherwise. This magic alchemy is often referred to as cryptoeconomics. Why? Well the network is built out of code, and code can verify things and keep them secure (crypto). Also, people who use and participate in the network recieve value for doing so (economics).

This tutorial will explore the basic components that go into building cryptoeconomic systems. This will start with the traditional centralize payment processors we know and hate, and will progressively move towards more and more decentralized systems secured and maintained through cryptoeconomics rather than corporate interests. Let the games begin!





If you're looking for a whimsical yet accessible intro to some of the concepts in this book, look no further than...

(WIP)




Ch1: Centralized Database Managers

They're not as boring when you roll your own!


This chapter will explore data as we know it: in the form of a centralized financial database like PayPal or your bank. The purpose is to highlight the important working parts of the system: state, accounts, transactions, and most of all... trust. In future chapters we'll show how we can get those same properties, but in a way where everyone can verify the authenticity of the state rather than trusting the word of a for profit business. After that we'll explore the new and exciting possibilities enabled by this divergent architecture :)

Let's get started...



State

All the things...




Some people say data is the new oil. Why? Probably because people like metaphors, but also... they both allow you to do stuff. Our world is becoming more and more digitized, and this means that data = access to:

  • money,
  • social connections,
  • and identity.

This data is most often stored in centralized databases. The "state" of these databases is what we look at to determine what is "true" or not. Ever gone to a restaurant and been told they can't find your reservation, or tried to register to vote and been told that you're not in the system? Data.

In this example we'll explore some of the common data structures that banks keep track of, and how they might go about maintaining and updating the state of that data.




Cryptoeconomics - 1.5 - Properties of Centralized Systems

Cryptoeconomics - 1.2 - State Transitions & Payment Processor Implementation.




// This is live editable Rust code. That means 
// that you can push the play button (little white triangle)
// in the top right of this box to see run it, or 
// you can edit whatever you like to see how that
// changes things. Explore!

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


// In Rust, structs are a way to organize data. You can 
// learn more about them here:
// https://doc.rust-lang.org/book/ch05-00-structs.html

// This structure keeps track of all the bank's data.
// The state is simply a record of what's what, and when
// things change, like users doing stuff or the bank doing
// stuff, the state will (hopefully )change to reflect that.
// Theoretically it's in everyone's best interest to make
// sure that the state accurate.
#[derive(Debug)]
struct State {

    // Accounts are stored in a HashMap 
    // with the account ID String the key
    // and the Account Struct as the value.
    accounts: HashMap<String, Account>,
    // Frozen accounts are decoupled from the main system
    // so that they're non-funcitonal, but they are not
    // deleted so that you can unfreeze them if needed.
    // This can be useful for regulatory compliance or 
    // savings accounts.
    frozen_accounts: HashMap<String, Account>,
    // This is just a Vec that stores all account IDs.
    // This is useful to look up account data in the 
    // accounts HashMap.
    account_ids: Vec<String>,
    // This is a churning pool of TX that have been submitted
    // by users, but not verified and processed by the bank. 
    pending_tx: Vec<TX>,
    // This is a history of all TX the bank has processed
    // correctly.
    history: Vec<TX>,
    // This is a history of all the moneys the bank has
    // created via fractional reserve banking. The bank can
    // sell this history to collectors or other investors.
    debt_history: Vec<TX>,
    // This is the amount of capital the bank has as debt on
    // it's balance sheet. While this is a liability for the
    // users who owe money, it's an asset for the bank that
    // they can trade at a discount to other parties like
    // collectors or investment funds.
    debt_pool: i32,
}

// This structure keeps track of the information in a user
// account.
#[derive(Debug, Clone)]
struct Account {

    // The password is how the user authorizes TX and proves
    // that they were sent by the user and not another party
    // like the receiver creating fake TX. This data is 
    // stored in the bank's centralized account database, 
    // and the bank (or anyone who gains access to it) can
    // change it at any time. This can be on purpose due to
    // compliance, regular operations, or intentional fraud.
    // This can also occur if the bank is hacked or if the user
    // reuses a password from another site for their bank
    // account, and that site then gets hacked.
    password: i32,
    // This number is incrimented every time an account
    // creates a valid TX. This is to prevent accidental 
    // glitches that might replay TX if the pending_tx pool
    // is not cleared properly.
    nonce: i32,
    // This is the users balance. Funny how so much of a 
    // person's access to resources, opportunities, and
    // survival depend on this number being accurate...
    balance: i32,
}

// This structure keeps track of all the TX information
// the bank cares about from users.
#[derive(Debug, Clone)]
struct TX {
    // Account to take money from.
    sender: String,
    // Add a password so we know the account to take money
    // from is the one that submitted the TX.
    sender_password: i32,
    // Check to make sure we're not processing duplicate TX.
    sender_nonce: i32,
    // Account to add money to.
    receiver: String,
    // Amount of moneys to "move around". Actually, the money
    // doesn't exist because the bank makes it up when they
    // "loan" money. It's just a number in a database. The only
    // thing verifying it's existance is the banks internal 
    // ledger, and maybe the ledgers of other banks. Good 
    // thing those banks are all secure, honest, and don't
    // collude 👍
    amount: i32,
}


// An implimentation is a structure that links functions
// together. You can learn more about them here:
// https://doc.rust-lang.org/book/ch05-03-method-syntax.html
impl State {
    
    // This function create a new state for the bank.
    pub fn new_state() -> State {
    
        // Ah... a blank canvas. So clean. So pure. So beautiful.
        let mut new = State {
            accounts: HashMap::new(),
            frozen_accounts: HashMap::new(),
            account_ids: Vec::new(),
            pending_tx: Vec::new(),
            history: Vec::new(),
            debt_history: Vec::new(),
            debt_pool: 0,
        };
        
        new
    }
}

// In Rust the main() function is where the program runs.
// You can store functions and stuff anywhere, but main()
// is the function has it's own state that keeps track of
// variables and computation. You can learn more about it
// here: https://doc.rust-lang.org/book/
fn main() {
    
    // Let's roll our own "bank"!
    let mut bank = State::new_state();
    println!("{:#?}", bank);
    
    // Let the games begin...
}




But wait... there's more

  • https://en.wikipedia.org/wiki/Record_(computer_science)
  • https://en.wikipedia.org/wiki/Database
  • https://www.coindesk.com/information/what-is-the-difference-blockchain-and-database
  • https://github.com/rust-unofficial/awesome-rust#database





Accounts

You and your stuff.




An account is just a way to store data to make it more useful. As we said before, he who controls the data controls the world. Let's put all our eggs in one basket via a centralized database and see how that works.

Some people like centralized databases and services because they're fast and there's someone to blame if/when things go wrong. This makes users feel safe because it's familiar, they don't have to think too much, and someone seems responsible. Time has told us however that this is merely a mirage to make us feel good and in reality centralized operators have most of the upside but limited downside, while for users it's reversed. Sound fun? Great! Let's go...




Cryptoeconomics - 1.0 - Chapter 1 Overview

Cryptoeconomics - 1.0 - Chapter 1 Overview.




// This is Rust code.
// I haven't figured out how to get rand to play
// nicely with mdBook, so you'll have to copypasta
// this into the Rust Playground. Have fun!
// https://play.rust-lang.org

extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


#[derive(Debug)]
struct State {
    accounts: HashMap<String, Account>,
    frozen_accounts: HashMap<String, Account>,
    account_ids: Vec<String>,
    pending_tx: Vec<TX>,
    history: Vec<TX>,
    debt_history: Vec<TX>,
    debt_pool: i32,
}

#[derive(Debug, Clone)]
struct Account {
    password: i32,
    nonce: i32,
    balance: i32,
}

#[derive(Debug, Clone)]
struct TX {
    sender: String,
    sender_password: i32,
    sender_nonce: i32,
    receiver: String,
    amount: i32,
}


// Central Payment Processor
impl State {
    
    
    /// GENERALLY USEFUL FUNCTIONS ///
    
    // Turn stuff into &[u8] slice
    pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }

    // Hash &[u8] slice into a hex String
    pub fn hash_u8(stuff: &[u8]) -> String {
        
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff);
        let digest = hasher.finish();
        let hex_digest = format!("{:#X}", digest);
            
        hex_digest
    }    
    
    // Hash stuff into a hex string
    pub fn hash<T>(stuff: &T) -> String {
        
        let u8_stuff = unsafe {
            State::any_as_u8_slice(stuff)
        };
        let hash_of_stuff = State::hash_u8(u8_stuff);
        
        hash_of_stuff
    }
    
    
    /// FUNCTION TO INIT THE STATE ///
    
    // Create a new state
    pub fn new_state() -> State {
    
        // Ah... a blank canvas. So clean. So pure. So beautiful.
        // Let the games begin.
    
        let mut new = State {
            accounts: HashMap::new(),
            frozen_accounts: HashMap::new(),
            account_ids: Vec::new(),
            pending_tx: Vec::new(),
            history: Vec::new(),
            debt_history: Vec::new(),
            debt_pool: 0,
        };
        
        new
    }
    
    
    /// ACCOUNT FUNCTIONS ///
    
    // Create a new account
    pub fn new_account(&mut self) {
        
        // Notice how the only thing tying the account_id to the password
        // is that the bank stores them in the same database. If the bank
        // were to change this by accident, or a hacker were to get access to
        // that data via hacking the bank directly or a relevant 3rd party...
        // well... life would get very interesting very fast. Mostly for you 
        // though because the banks are insured so for them it's a write-off
        // that affects them minimally. 
        // https://en.wikipedia.org/wiki/Write-off
        // https://en.wikipedia.org/wiki/Equifax
        
        let account_id = State::hash(&thread_rng().gen_range(0, 1000000));
        let account_data = Account {
            password: thread_rng().gen_range(0, 1000000),
            nonce: 0,
            balance: 0,
        };
        
        self.account_ids.push(account_id.clone());
        self.accounts.insert(account_id, account_data);
    }
    
    // Create multiple new accounts
    pub fn new_accounts(&mut self,
                        num_accounts: i32) {
        
        // Sock puppets ahoy!
        // Good thing banks are honest and would never create accounts to
        // simulate activity when there was none. Even better that crypto
        // exchanges are even more honest because, well... crypto! It's 
        // different this time right?
        // https://en.wikipedia.org/wiki/Sockpuppet_(Internet)
        // https://en.wikipedia.org/wiki/Wash_trade
        // https://medium.com/@bitfinexed/wash-trading-bitcoin-how-bitfinex-benefits-from-fraudulent-trading-8bd66be73215
        // https://medium.com/@bitfinexed/the-tether-truth-machine-the-wheels-of-justice-turn-slowly-but-grind-exceedingly-finely-8e3bd72ad011
        
        for i in 0..num_accounts {
            self.new_account()
        }
    }
    
    // Print account info
    pub fn print_account_info(&mut self,
                         account_id: String) {
        
        // If it's written down it must be true.
        
        if let Some(x) = self.accounts.get(&account_id) {
            println!("Your Account:\n{:#?}", self.accounts.get(&account_id).unwrap());
        }
        println!("Account not found");
    }
    
    // Print account history
    pub fn print_account_history(&mut self,
                                 account_id: String,) {
        
        // Assuming the bank's records are accurate and up to date, which
        // we assume they are, probably, but we don't know ¯\_(ツ)_/¯ 
        // https://www.bbc.com/news/business-43985233
        // https://www.cnet.com/news/commonwealth-bank-of-australia-financial-data-breach-20-million-accounts/
        
        let mut account_history = Vec::new();
        let list = self.history.clone();
        for i in list {
            if i.sender == account_id {
                account_history.push(i.clone());
            }
            if i.receiver == account_id {
                account_history.push(i.clone());
            }
        }
        println!("\n/// Getting Account History ///");
        println!("Account {} ", account_id);
        println!("{:#?}", self.accounts.get(&account_id).unwrap());
        println!("History:\n{:#?}", account_history);
    }
    
    // "Freeze" an account
    pub fn freeze_account(&mut self,
                          account_id: String) {
        
        // The end of your life savings are just a click away...
        
        let account = self.accounts.remove_entry(&account_id).unwrap();
    
        self.frozen_accounts.insert(account.0, account.1);
    }
}

fn main() {

    // Init bank state
    let mut bank = State::new_state();
    println!("\n/// Initialized Bank State ///");
    println!("{:#?}", &bank);
    
    // Create some new accounts
    bank.new_accounts(10);
    println!("\n/// Created Some Accounts ///");
    println!("{:#?}", bank);
}




But wait... there's more

  • https://en.wikipedia.org/wiki/User_(computing)
  • https://en.wikipedia.org/wiki/Bank_account





TX

Making stuff happen.




Core Concept: tx is a way to request a state change, but...

  • the state transition function is how we make that state change
  • the central operator controls the state and thus all state changes including freezing accounts or printing money




Cryptoeconomics - 1.5 - Properties of Centralized Systems

Cryptoeconomics - 1.2 - State Transitions & Payment Processor Implementation.

Cryptoeconomics - 1.3 - Replay Protection

Cryptoeconomics - 1.3 - Replay Protection.





extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


#[derive(Debug)]
struct State {
    accounts: HashMap<String, Account>,
    frozen_accounts: HashMap<String, Account>,
    account_ids: Vec<String>,
    pending_tx: Vec<TX>,
    history: Vec<TX>,
    debt_history: Vec<TX>,
    debt_pool: i32,
}

#[derive(Debug, Clone)]
struct Account {
    password: i32,
    nonce: i32,
    balance: i32,
}

#[derive(Debug, Clone)]
struct TX {
    sender: String,
    sender_password: i32,
    sender_nonce: i32,
    receiver: String,
    amount: i32,
}


// Central Payment Processor
impl State {
    
    
    /// GENERALLY USEFUL FUNCTIONS ///
    
    // Turn stuff into &[u8] slice
    pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }

    // Hash &[u8] slice into a hex String
    pub fn hash_u8(stuff: &[u8]) -> String {
        
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff);
        let digest = hasher.finish();
        let hex_digest = format!("{:#X}", digest);
            
        hex_digest
    }    
    
    // Hash stuff into a hex string
    pub fn hash<T>(stuff: &T) -> String {
        
        let u8_stuff = unsafe {
            State::any_as_u8_slice(stuff)
        };
        let hash_of_stuff = State::hash_u8(u8_stuff);
        
        hash_of_stuff
    }
    
    
    /// FUNCTION TO INIT THE STATE ///
    
    // Create a new state
    pub fn new_state() -> State {
    
        let mut new = State {
            accounts: HashMap::new(),
            frozen_accounts: HashMap::new(),
            account_ids: Vec::new(),
            pending_tx: Vec::new(),
            history: Vec::new(),
            debt_history: Vec::new(),
            debt_pool: 0,
        };
        
        new.accounts.insert(String::from("bank"), Account { password: 0, nonce: 0, balance: 1000000 });
        
        new
    }
    
    
    /// ACCOUNT FUNCTIONS ///
    
    // Create a new account
    pub fn new_account(&mut self) {
        
        let account_id = State::hash(&thread_rng().gen_range(0, 1000000));
        let account_data = Account {
            password: thread_rng().gen_range(0, 1000000),
            nonce: 0,
            balance: 0,
        };
        
        self.account_ids.push(account_id.clone());
        self.accounts.insert(account_id, account_data);
    }
    
    // Create multiple new accounts
    pub fn new_accounts(&mut self,
                        num_accounts: i32) {

        for i in 0..num_accounts {
            self.new_account()
        }
    }
    
    // Print account info
    pub fn print_account_info(&mut self,
                         account_id: String) {
        
        if let Some(x) = self.accounts.get(&account_id) {
            println!("Your Account:\n{:#?}", self.accounts.get(&account_id).unwrap());
        }
        println!("Account not found");
    }
    
    // Print account history
    pub fn print_account_history(&mut self,
                                 account_id: String,) {
        
        let mut account_history = Vec::new();
        let list = self.history.clone();
        for i in list {
            if i.sender == account_id {
                account_history.push(i.clone());
            }
            if i.receiver == account_id {
                account_history.push(i.clone());
            }
        }
        println!("\n/// Getting Account History ///");
        println!("Account {} ", account_id);
        println!("{:#?}", self.accounts.get(&account_id).unwrap());
        println!("History:\n{:#?}", account_history);
    }
    
    // "Freeze" an account
    pub fn freeze_account(&mut self,
                          account_id: String) {
        
        // The end of your life savings are just a click away...
        
        let account = self.accounts.remove_entry(&account_id).unwrap();
    
        self.frozen_accounts.insert(account.0, account.1);
    }


    /// TX FUNCTIONS ///
    
    // Create a new TX for the bank
    pub fn new_bank_tx(&mut self,
                       receiver: String,
                       amount: i32) {
        
        // When banks give people loans or credit it's actually processed
        // as debt which banks can then trade amongst each other at a market
        // rate based on how likely the debtor is likely to pay back in full
        // Yes you heard this right, they print money and profit from doing so.
        // Carpenters make cabinets, comedians make jokes, banks make money,
        // literaly...
        // Fun Fact: debt on a banks balance sheet is an ASSET to the bank and
        // not a liability. It's a liability to users, but banks can buy, sell, 
        // and trade this debt as a financial product. One of a banks primary 
        // products is loans, but as a user of a bank you're actually the product 
        // they're selling to other banks and investment funds. Kind of like how 
        // with social media platforms access to the users attention is the 
        // product that they sell to 3rd party advertisers.
        // https://en.wikipedia.org/wiki/Fractional-reserve_banking
        let tx = TX {
            sender: String::from("bank"),
            sender_password: 0,
            sender_nonce: self.accounts.get("bank").unwrap().nonce,
            receiver: receiver,
            amount: amount,
        };

        // Tx is legit by default because it's from the bank so let's process it.
        // decrease the balance from sender's account
        self.accounts
            .get_mut(&tx.sender)
            .unwrap()
            .balance -= tx.amount;
        // increase sender's nonce to prevent replay glitches
        self.accounts
            .get_mut(&tx.sender)
            .unwrap()
            .nonce += 1;
        // increase the balance of the reciever's account
        self.accounts
            .get_mut(&tx.receiver)
            .unwrap()
            .balance += tx.amount;
            
        // add processed TX to history
        self.history.push(tx.clone());        
    }
    
    // Create a new TX for a bank user
    pub fn new_user_tx(&mut self,
                       sender: String,
                       sender_password: i32,
                       sender_nonce: i32,
                       receiver: String,
                       amount: i32) {
        
        // This is really more of a TX request, but that's ok
        
        let tx = TX {
            sender: sender,
            sender_password: sender_password,
            sender_nonce: sender_nonce,
            receiver: receiver,
            amount: amount,
        };
        
        self.pending_tx.push(tx);
    }
}


fn main() {

    // Init bank state
    let mut bank = State::new_state();
    println!("\n/// Initialized Bank State ///");
    println!("{:#?}", &bank);
    
    // Create some new accounts
    bank.new_accounts(10);
    println!("\n/// Created Some Accounts ///");
    println!("{:#?}", bank);

    // Init some variables for testing accounts
    let test_account0 = bank.account_ids[0].clone();
    let test_account1 = bank.account_ids[1].clone();
    let test_account2 = bank.account_ids[2].clone();

    // Add some funds to those accounts
    for i in bank.accounts.values_mut() {
        i.balance += 10000;
    }
    println!("\n/// Added Funds To Accounts ///");
    println!("{:#?}", bank);

    // Let's make some TX requests
    for i in 0..10 {
        
        let sender = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        let receiver = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        
        if sender != receiver {
            bank.new_user_tx(sender.to_string(),
                        bank.accounts.get(sender).unwrap().password,
                        bank.accounts.get(sender).unwrap().nonce,
                        receiver.to_string(),
                        thread_rng().gen_range(100, 1000))
        }
    }    

    println!("\n/// Created Pending TX ///");
    println!("{:#?}", bank);
}




But wait... there's more

  • https://en.wikipedia.org/wiki/Database_transaction





State Transitions

Getting from A to B.




For every database that has a state, there's a way to change that state. Every time you're in state s and then make a change, you're now in state s'. The history of state transitions is the history of the database. When multiple people are using this database it's important that they all agree on the current state. If they don't, in meatspace we get wars and in cyberspace we get forks.

Since in this example we have a centralized database controlled by a single operator, it's their database so they make the rules. Because they control the state they also control the state changes. Users can submit TX for consideration, but ultimately the bank decides what goes through or not.

Let's try it out and become kings of our own little hills!




Cryptoeconomics - 1.5 - Properties of Centralized Systems

Cryptoeconomics - 1.2 - State Transitions & Payment Processor Implementation.





extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


#[derive(Debug)]
struct State {
    accounts: HashMap<String, Account>,
    frozen_accounts: HashMap<String, Account>,
    account_ids: Vec<String>,
    pending_tx: Vec<TX>,
    history: Vec<TX>,
    debt_history: Vec<TX>,
    debt_pool: i32,
}

#[derive(Debug, Clone)]
struct Account {
    password: i32,
    nonce: i32,
    balance: i32,
}

#[derive(Debug, Clone)]
struct TX {
    sender: String,
    sender_password: i32,
    sender_nonce: i32,
    receiver: String,
    amount: i32,
}


// Central Payment Processor
impl State {
    
    
    /// GENERALLY USEFUL FUNCTIONS ///
    
    // Turn stuff into &[u8] slice
    pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }

    // Hash &[u8] slice into a hex String
    pub fn hash_u8(stuff: &[u8]) -> String {
        
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff);
        let digest = hasher.finish();
        let hex_digest = format!("{:#X}", digest);
            
        hex_digest
    }    
    
    // Hash stuff into a hex string
    pub fn hash<T>(stuff: &T) -> String {
        
        let u8_stuff = unsafe {
            State::any_as_u8_slice(stuff)
        };
        let hash_of_stuff = State::hash_u8(u8_stuff);
        
        hash_of_stuff
    }
    
    
    /// FUNCTION TO INIT THE STATE ///
    
    // Create a new state
    pub fn new_state() -> State {
    
        let mut new = State {
            accounts: HashMap::new(),
            frozen_accounts: HashMap::new(),
            account_ids: Vec::new(),
            pending_tx: Vec::new(),
            history: Vec::new(),
            debt_history: Vec::new(),
            debt_pool: 0,
        };
        
        new.accounts.insert(String::from("bank"), Account { password: 0, nonce: 0, balance: 1000000 });
        
        new
    }
    
    
    /// ACCOUNT FUNCTIONS ///
    
    // Create a new account
    pub fn new_account(&mut self) {
        
        let account_id = State::hash(&thread_rng().gen_range(0, 1000000));
        let account_data = Account {
            password: thread_rng().gen_range(0, 1000000),
            nonce: 0,
            balance: 0,
        };
        
        self.account_ids.push(account_id.clone());
        self.accounts.insert(account_id, account_data);
    }
    
    // Create multiple new accounts
    pub fn new_accounts(&mut self,
                        num_accounts: i32) {

        for i in 0..num_accounts {
            self.new_account()
        }
    }
    
    // Print account info
    pub fn print_account_info(&mut self,
                         account_id: String) {
        
        if let Some(x) = self.accounts.get(&account_id) {
            println!("Your Account:\n{:#?}", self.accounts.get(&account_id).unwrap());
        }
        println!("Account not found");
    }
    
    // Print account history
    pub fn print_account_history(&mut self,
                                 account_id: String,) {
        
        let mut account_history = Vec::new();
        let list = self.history.clone();
        for i in list {
            if i.sender == account_id {
                account_history.push(i.clone());
            }
            if i.receiver == account_id {
                account_history.push(i.clone());
            }
        }
        println!("\n/// Getting Account History ///");
        println!("Account {} ", account_id);
        println!("{:#?}", self.accounts.get(&account_id).unwrap());
        println!("History:\n{:#?}", account_history);
    }
    
    // "Freeze" an account
    pub fn freeze_account(&mut self,
                          account_id: String) {
        
        // The end of your life savings are just a click away...
        
        let account = self.accounts.remove_entry(&account_id).unwrap();
    
        self.frozen_accounts.insert(account.0, account.1);
    }
    
    
    /// TX FUNCTIONS ///
    
    // Create a new TX
    pub fn new_user_tx(&mut self,
                       sender: String,
                       sender_password: i32,
                       sender_nonce: i32,
                       receiver: String,
                       amount: i32) {
        
        let tx = TX {
            sender: sender,
            sender_password: sender_password,
            sender_nonce: sender_nonce,
            receiver: receiver,
            amount: amount,
        };
        
        self.pending_tx.push(tx);
    }
    
    // Create a new bank TX
    pub fn new_bank_tx(&mut self,
                       receiver: String,
                       amount: i32) {

        // Tx is legit by default because it's from the bank so let's just process it.
        let tx = TX {
            sender: "bank".to_string(),
            sender_password: 0,
            sender_nonce: self.accounts.get("bank").unwrap().nonce,
            receiver: receiver,
            amount: amount, 
        };
        // decrease the balance in the bank's debt account
        self.debt_pool -= tx.amount;
        // increase the balance of the reciever's account
        self.accounts
            .get_mut(&tx.receiver)
            .unwrap()
            .balance += tx.amount;
            
        // add processed TX to history
        self.history.push(tx.clone());        
    }
    
    
    /// STATE TRANSITION FUNCTIONS ///
    
    // Verify pending user TX
    // - notice how the bank (or any hacker) gets to bypass this check
    //   using a bank tx rather than a user tx
    pub fn process_pending_tx(&mut self) {
        
        // check pending tx
        for i in & self.pending_tx {
            
            // check that sender is legit
            if !(self.accounts.contains_key(&i.sender)) {
                println!("TX ERROR: sender not found.");
                continue;
            }
 
            // check that receiver is legit
            if !(self.accounts.contains_key(&i.receiver)) {
                println!("TX ERROR: receiver not found.");
                continue;
            }           
            
            // check that tx is signed by sender password
            if !(i.sender_password == self.accounts
                                     .get(&i.sender)
                                     .unwrap()
                                     .password) {
                println!("TX ERROR: tx and sender passwords do not match.");
                continue;
            }
            
            // check that the TX nonce matches the sender nonce
            if !(i.sender_nonce == self.accounts
                                  .get(&i.sender)
                                  .unwrap()
                                  .nonce) {
                println!("TX ERROR: tx and sender nonces do not match.");
                continue;
            }

            // check that the TX amount is >= the sender's balance 
            if !(i.amount <= self.accounts
                                    .get(&i.sender)
                                    .unwrap()
                                    .balance) {
                println!("TX ERROR: sender has insufficient balance");
                continue;
            } 
            
            // Tx is legit so let's process it
            // decrease the balance from sender's account
            self.accounts
                .get_mut(&i.sender)
                .unwrap()
                .balance -= i.amount;
            // increase sender's nonce to prevent replay glitches
            self.accounts
                .get_mut(&i.sender)
                .unwrap()
                .nonce += 1;
            // increase the balance of the reciever's account
            self.accounts
                .get_mut(&i.receiver)
                .unwrap()
                .balance += i.amount;
                
            // add processed TX to history
            self.history.push(i.clone());
        }
        
        // clear pending tx
        self.pending_tx = Vec::new();
    }
    
    // Arbitrary function to add funds to an account
    pub fn add_funds(&mut self,
                     account_id: String,
                     amount: i32) {
        
        // A very important function for any private and seldom audited
        // for-profit enterprise. Convenient because it
        // just directly changes the state of the ledger
        // without going through any checks or being recorded
        // in the hisroty. What could go wrong?
        // https://en.wikipedia.org/wiki/Enron_scandal
        
        if let Some(x) = self.accounts.get_mut(&account_id) {
            x.balance += amount;
        }
    }

}


fn main() {
    
    // Note that every function that starts with
    // bank.function() is a function that only the
    // centralized operator can perform. Users can
    // submit TX for review, but ultimately they 
    // have no control. Same for viewing their 
    // account history or controlling if/when
    // their funds are accessible. Users can
    // make requests, but the central operator
    // makes the rules.
    
    // Init bank state
    let mut bank = State::new_state();
    println!("\n/// Initialized Bank State ///");
    println!("{:#?}", &bank);
    
    // Create some new accounts
    bank.new_accounts(10);
    println!("\n/// Created Some Accounts ///");
    println!("{:#?}", bank);

    // Init some variables for testing accounts
    let test_account0 = bank.account_ids[0].clone();
    let test_account1 = bank.account_ids[1].clone();
    let test_account2 = bank.account_ids[2].clone();

    // Add some funds to those accounts
    for i in bank.accounts.values_mut() {
        i.balance += 10000;
    }
    println!("\n/// Added Funds To Accounts ///");
    println!("{:#?}", bank);

    // Let's make some TX requests
    for i in 0..10 {
        
        let sender = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        let receiver = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        
        if sender != receiver {
            bank.new_user_tx(sender.to_string(),
                        bank.accounts.get(sender).unwrap().password,
                        bank.accounts.get(sender).unwrap().nonce,
                        receiver.to_string(),
                        thread_rng().gen_range(100, 1000))
        }
    } 

    // Let's print some moneys!
    for i in 0..10 {
        
        let sender = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        let receiver = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        
        if sender != receiver {
            bank.new_bank_tx(receiver.to_string(),
                             thread_rng().gen_range(100, 1000))
        }
    }
    println!("\n/// Simulated Some TX ///");
    println!("{:#?}", bank);
    
    // Process and approve or decline pending TX
    bank.process_pending_tx();
    println!("\n/// Processed Pending TX ///");
    println!("{:#?}", bank);
    
    // Get the TX history for an account
    bank.print_account_history(test_account0.clone());
}




But wait... there's more

  • https://en.wikipedia.org/wiki/Transition_system





Centralized Payment Processor

Feel the power.




So to recap...

Centralized database managers:

  • gets to create new data and accounts out of thin air (or money and debt)
  • controls read/write access to that data (ability to move/create money)
  • has minimal downside when things go wrong due to bailouts and insurance (what is regulatory capture?)

Users:

  • trust the central operator is showing them the correct state for their data or money
  • are only allowed as much money or control as they can figure out how to convince the bank or anyone else to give them
  • get their lives turned upside down if things go wrong




Cryptoeconomics - 1.5 - Properties of Centralized Systems

Cryptoeconomics - 1.5 - Properties of Centralized Systems.




Here is the code without extra commentary so you can explore and see how all the parts interact. Go ahead and try to modify or play with it :)

extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


#[derive(Debug)]
struct State {
    accounts: HashMap<String, Account>,
    frozen_accounts: HashMap<String, Account>,
    account_ids: Vec<String>,
    pending_tx: Vec<TX>,
    history: Vec<TX>,
    debt_history: Vec<TX>,
    debt_pool: i32,
}

#[derive(Debug, Clone)]
struct Account {
    password: i32,
    nonce: i32,
    balance: i32,
}

#[derive(Debug, Clone)]
struct TX {
    sender: String,
    sender_password: i32,
    sender_nonce: i32,
    receiver: String,
    amount: i32,
}


// Central Payment Processor
impl State {
    
    
    /// GENERALLY USEFUL FUNCTIONS ///
    
    // Turn stuff into &[u8] slice
    pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }

    // Hash &[u8] slice into a hex String
    pub fn hash_u8(stuff: &[u8]) -> String {
        
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff);
        let digest = hasher.finish();
        let hex_digest = format!("{:#X}", digest);
            
        hex_digest
    }    
    
    // Hash stuff into a hex string
    pub fn hash<T>(stuff: &T) -> String {
        
        let u8_stuff = unsafe {
            State::any_as_u8_slice(stuff)
        };
        let hash_of_stuff = State::hash_u8(u8_stuff);
        
        hash_of_stuff
    }
    
    
    /// FUNCTION TO INIT THE STATE ///
    
    // Create a new state
    pub fn new_state() -> State {
    
        let mut new = State {
            accounts: HashMap::new(),
            frozen_accounts: HashMap::new(),
            account_ids: Vec::new(),
            pending_tx: Vec::new(),
            history: Vec::new(),
            debt_history: Vec::new(),
            debt_pool: 0,
        };
        
        new
    }
    
    
    /// ACCOUNT FUNCTIONS ///
    
    // Create a new account
    pub fn new_account(&mut self) {
        
        let account_id = State::hash(&thread_rng().gen_range(0, 1000000));
        let account_data = Account {
            password: thread_rng().gen_range(0, 1000000),
            nonce: 0,
            balance: 0,
        };
        
        self.account_ids.push(account_id.clone());
        self.accounts.insert(account_id, account_data);
    }
    
    // Create multiple new accounts
    pub fn new_accounts(&mut self,
                        num_accounts: i32) {

        for i in 0..num_accounts {
            self.new_account()
        }
    }
    
    // Print account info
    pub fn print_account_info(&mut self,
                         account_id: String) {
        
        if let Some(x) = self.accounts.get(&account_id) {
            println!("Your Account:\n{:#?}", self.accounts.get(&account_id).unwrap());
        }
        println!("Account not found");
    }
    
    // Print account history
    pub fn print_account_history(&mut self,
                                 account_id: String,) {
        
        let mut account_history = Vec::new();
        let list = self.history.clone();
        for i in list {
            if i.sender == account_id {
                account_history.push(i.clone());
            }
            if i.receiver == account_id {
                account_history.push(i.clone());
            }
        }
        println!("\n/// Getting Account History ///");
        println!("Account {} ", account_id);
        println!("{:#?}", self.accounts.get(&account_id).unwrap());
        println!("History:\n{:#?}", account_history);
    }
    
    // "Freeze" an account
    pub fn freeze_account(&mut self,
                          account_id: String) {
        
        let account = self.accounts.remove_entry(&account_id).unwrap();
    
        self.frozen_accounts.insert(account.0, account.1);
    }
    
    
    /// TX FUNCTIONS ///
        
    // Create a new bank TX
    pub fn new_bank_tx(&mut self,
                       receiver: String,
                       amount: i32) {

        // Tx is legit by default because it's from the bank so let's just process it.
        let tx = TX {
            sender: "bank".to_string(),
            sender_password: 0,
            sender_nonce: self.accounts.get("bank").unwrap().nonce,
            receiver: receiver,
            amount: amount, 
        };
        // decrease the balance in the bank's debt account
        self.debt_pool -= tx.amount;
        // increase the balance of the reciever's account
        self.accounts
            .get_mut(&tx.receiver)
            .unwrap()
            .balance += tx.amount;
            
        // add processed TX to history
        self.history.push(tx.clone());        
    }
    
    // Create a new TX
    pub fn new_user_tx(&mut self,
                       sender: String,
                       sender_password: i32,
                       sender_nonce: i32,
                       receiver: String,
                       amount: i32) {
        
        let tx = TX {
            sender: sender,
            sender_password: sender_password,
            sender_nonce: sender_nonce,
            receiver: receiver,
            amount: amount,
        };
        
        self.pending_tx.push(tx);
    }

    
    /// STATE TRANSITION FUNCTIONS ///

    // Verify pending user TX
    pub fn process_pending_tx(&mut self) {
        
        // check pending tx
        for i in & self.pending_tx {
            
            // check that sender is legit
            if !(self.accounts.contains_key(&i.sender)) {
                println!("TX ERROR: sender not found.");
                continue;
            }
 
            // check that receiver is legit
            if !(self.accounts.contains_key(&i.receiver)) {
                println!("TX ERROR: receiver not found.");
                continue;
            }           
            
            // check that tx is signed by sender password
            if !(i.sender_password == self.accounts
                                     .get(&i.sender)
                                     .unwrap()
                                     .password) {
                println!("TX ERROR: tx and sender passwords do not match.");
                continue;
            }
            
            // check that the TX nonce matches the sender nonce
            if !(i.sender_nonce == self.accounts
                                  .get(&i.sender)
                                  .unwrap()
                                  .nonce) {
                println!("TX ERROR: tx and sender nonces do not match.");
                continue;
            }

            // check that the TX amount is >= the sender's balance 
            if !(i.amount <= self.accounts
                                    .get(&i.sender)
                                    .unwrap()
                                    .balance) {
                println!("TX ERROR: sender has insufficient balance");
                continue;
            } 
            
            // Tx is legit so let's process it
            // decrease the balance from sender's account
            self.accounts
                .get_mut(&i.sender)
                .unwrap()
                .balance -= i.amount;
            // increase sender's nonce to prevent replay glitches
            self.accounts
                .get_mut(&i.sender)
                .unwrap()
                .nonce += 1;
            // increase the balance of the reciever's account
            self.accounts
                .get_mut(&i.receiver)
                .unwrap()
                .balance += i.amount;
                
            // add processed TX to history
            self.history.push(i.clone());
        }
        
        // clear pending tx
        self.pending_tx = Vec::new();
    }
    
    // Add funds to an account
    pub fn add_funds(&mut self,
                     account_id: String,
                     amount: i32) {
        
        if let Some(x) = self.accounts.get_mut(&account_id) {
            x.balance += amount;
        }
    }
}


fn main() {
    
    // Init bank state
    let mut bank = State::new_state();
    println!("\n/// Initialized Bank State ///");
    println!("{:#?}", &bank);
    
    // Create some new accounts
    bank.new_accounts(10);
    println!("\n/// Created Some Accounts ///");
    println!("{:#?}", bank);

    // Init some variables for testing accounts
    let test_account0 = bank.account_ids[0].clone();
    let test_account1 = bank.account_ids[1].clone();
    let test_account2 = bank.account_ids[2].clone();

    // Add some funds to those accounts
    for i in bank.accounts.values_mut() {
        i.balance += 10000;
    }
    println!("\n/// Added Funds To Accounts ///");
    println!("{:#?}", bank);

    // Let's make some TX requests
    for i in 0..10 {
        
        let sender = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        let receiver = &bank.account_ids[thread_rng().gen_range(0, bank.account_ids.len())];
        
        if sender != receiver {
            bank.new_user_tx(sender.to_string(),
                        bank.accounts.get(sender).unwrap().password,
                        bank.accounts.get(sender).unwrap().nonce,
                        receiver.to_string(),
                        thread_rng().gen_range(100, 1000))
        }
    } 
    println!("\n/// Simulated Some TX ///");
    println!("{:#?}", bank);
    
    // Process pending TX
    bank.process_pending_tx();
    println!("\n/// Processed Pending TX ///");
    println!("{:#?}", bank);
    
    // Get the history for an account
    bank.print_account_history(test_account0.clone());
    
    // Freeze an account
    bank.freeze_account(test_account0.clone());
    println!("\n/// Froze Account {} ///", &test_account0);
    println!("{:#?}", bank);
    
    // Try checking the balance of a frozen account
    println!("\n/// Checking Frozen Account ///");
    bank.print_account_info(test_account0.clone().to_string());
}




Resources To Learn More

  • TBD




Seems a little odd that such an essential piece of modern living is so opaque and fragile... 🤔

Can we do better? Maybe! Some people have some ideas on how to at least make this process a little more secure. In the next chapter we'll explore how we can change the architecture of the system to make it better for users. This includes:

  • giving everyone on the network the option to verify TX to make sure no one is cheating
  • creating account IDs and passwords via cryptography so that they are not all located in a centralized database
  • creating costs and rewards for managing state transitions to create reliability and security

Let's go to chapter 2 and see how that works!





Minimal Viable Proof of Work "Blockchain"

If you love your data, set it free.




Welcome!

As we saw, centralized databases can be a drag. In this chapter we'll explore how we can move towards a decentralized p2p database. This will involve some crypto and some imagination, but mainly crypto. We'll keep the general structure of our database from chapter 1, but add and change a few things. Let's get started :)



State

All the things...




So as we mentioned, a blockchain is a glorified database. As such, at it's heart is some data. Like any database that changes, the current state of things is literally called the "state". As we saw with a centralized database the owners of that database can change the state however and whenever they want. In a decentralized system where there is no centralized database manager, the state is agreed upon by a distributed group of verifiers based on some consensus rules that they all agree to.

In a Proof of Work blockchain the state is divided by blocks; each new block representing a new world state. Users submit requests to change the state in the form of transactions (tx) and verifiers check those transactions and aggregate them into blocks. In order to publish a block that is accepted as the next state, the verifier has to perform computation to solve a puzzle. This is where the term "proof of work" comes in, because you can only solve the puzzle if you did the work. Whoever solves the puzzle gets to publish the next block, and there is a reward for doing so called the "block reward". This does a few things:

  • it creates new tokens to incentivize people to spend resources to check and secure blocks
  • it prevents anyone from just publishing spam blocks arbitrarily
  • because there is a reward for publishing valid blocks and every block can be checked by the entire network, anyone who publishes a fake block will be ignored, thus preventing fraud on the network

All in all this accomplishes something that was not possible before: a shared state that people can trust to be a legitimate source of information. Before you had to trust a centralized operator because it was to complicated for a large network to process and manage a complex state asynchronously. With blockchain technology however, this is possible. In this chapter we'll explore the simplest version of this: a financial transaction network that uses proof of work to solve a simple puzzle.




use std::collections::HashMap;

// First we're going to create a struct that will hold the important 
// state data we want to keep our database functioning:
//  - accounts: this is where people's money and addresses live
//  - pending_tx: a pool of pending tx that have not yet been verified 
//    as legit or not
//  - chain: this is where TX that have been verified and processed are 
//    stored. Think of it as the history, but rather than a bank telling
//    you what your balance is, you can check the history to make sure 
//    everything is legit. 

#[derive(Debug)]
struct State {
    modulo: i32,
    accounts: HashMap<i32, Account>,
    pending_tx: Vec<SignedTX>,
    chain: Vec<Block>,
}

// As promised, accounts have a balance.
// Accounts also have a nonce which is a number that
// gets incremented with every valid transaction sent.
// The nonce is added to every tx and this helps prevent
// duplicates from being processed. 
#[derive(Debug, Clone)]
struct Account {
    balance: f32,
    nonce: i32,
}

// TX stands for transaction.
// Sender, receiver, and amount are what they say they are.
// Like we said, the nonce is a unique value added to every
// TX to prevent duplicates from being processed.
#[derive(Debug, Clone)]
struct TX {
    sender: i32,
    receiver: i32,
    amount: f32,
    nonce: i32,
}

// The sender of every TX signs it to verify that it came
// from them and not someone else. This is done via hashing
// and public key crypto, which we'll explore soon. 
#[derive(Debug, Clone)]
struct SignedTX {
    tx: TX,
    signature: Vec<i32>,
}

// The blockheader is attached to every block to provide
// information such as when the block was produced (timestamp),
// a unique identifier (nonce), the hash of the previous block
// to verify the correct ordering of blocks, and a merkle 
// hash that acts as a signature of this block that the next
// block can reference. Hashing and linking these blocks 
// together like this is what leads to the term "blockchain".
// (because it's a chain of blocks)
#[derive(Debug, Clone)]
pub struct Blockheader {
    timestamp: i64,
    nonce: i32, 
    previous_block_hash: String,  
    merkle: String,  
}

// Each block holds the information in the block header
// as well as all the TX that were processed in that block.
// Because of this anyone can verify the authenticity of the
// history, and you can't rewrite the history without 
// rewriting every block that comes after it. When there's
// a cost for creating blocks this is very difficult, and
// this is what leads people to trust that blockchains are
// secure. With a centralized operator though, this does not
// apply. For this reason, in this chapter we'll refer to the
// database as a "blockchain" because it's architecture is
// inspired by blockchains, but has none of the security
// guarantees of decntralized P2P blockchains.
#[derive(Debug, Clone)]
pub struct Block {
    header: Blockheader,
    transactions: Vec<SignedTX>
}

// This function initializes a new "blockchain" inspired
// data structure.
impl State {

    // Initialize A "Blockchain"
    pub fn new_blockchain() -> State {
        let mut state = State {
            modulo: 0,
            accounts: HashMap::new(),
            pending_tx: Vec::new(),
            chain: Vec::new(),
        };
    
        state
    }  
}

// Let's try it out. If you push play you'll see that the
// program creates a new state. It's empty, but soon we'll
// start filling it with data.
fn main() {

  let state = State::new_blockchain();
  println!("{:#?}", state);

}




The Ethereum Whitepaper: state machine sections

  • https://github.com/ethereum/wiki/wiki/White-Paper#bitcoin-as-a-state-transition-system
  • https://github.com/ethereum/wiki/wiki/White-Paper#ethereum-state-transition-function

The Ethereum Yellowpaper: 2. The Blockchain Paradigm

  • "Ethereum, taken as a whole, can be viewed as a transaction-based state machine: we begin with a genesis state and incrementally execute transactions to morph it into some final state. It is this final state which we accept as the canonical “version” of the world of Ethereum. The state can include such information as account balances, reputations, trust arrangements, data pertaining to information of the physical world; in short, anything that can currently be represented by a computer is admissible. Transactions thus represent a valid arc between two states; the ‘valid’ part is important—there exist far more invalid state changes than valid state changes. Invalid state changes might, e.g., be things such as reducing an account balance without an equal and opposite increase elsewhere."
  • https://ethereum.github.io/yellowpaper/paper.pdf

A history of state machines

  • https://en.wikipedia.org/wiki/Analytical_Engine
  • https://en.wikipedia.org/wiki/Turing_machine#The_%22state%22
  • https://en.wikipedia.org/wiki/Distributed_ledger
  • https://en.wikipedia.org/wiki/Transition_system
  • https://en.wikipedia.org/wiki/Blockchain


Crypto & Trust

How do we go from trusting a b2c centralized database to trusting a p2p decentralized database?
and why would we want to?




In chapter 1 we explored how a generic database might work and how that database would be managed if we trusted a central operator to run it. What if we wanted to create a shared database though, where we could all verify things for ourselves?

One option might be to keep the central operator, but make the database transparent. All participants would agree to a set of rules that they would all play by and they could all verify changes of state. If anything happened that was not in line with the agreed upon rules they could switch to a new network. This has a few downsides though... mainly that switching and coordination costs are high. If you catch someone cheating (either the central operator or another user), at what threshold is it worth raising an issue or switching to another network? What if that network is already integrated into other apps and services and you'd have to switch those over too? What if your identity, social connections, or reputation are tied to this network and starting on a new one would mean losing all that? At what point would a transgressino of the network validate the cost of switching? If it's anything similar to our current banking or social networks, the answer is pretty damn high. There must be a better way...

It turns out, there is! It's called cryptography. Roughly speaking, cryptography is the process of using mathematic functions to conceal or verify information. In this chapter we'll focus mainly on the later as a way to replace trust in a central operator with trust in mathematics. This means that we can define in code all the functions that the central operator performed:

  • creating accounts
  • verifying tx
  • managing state transitions

In this section we'll cover some of the basic concepts in cryptography that allow us to do this, then throughout the chapter we'll apply those concepts to create the mechanisms that do that, and then in the chapter summary we'll have our own working network that we can use to simulate these things.

Let's start with a few foundational concepts that will help us get started :)


Computational Infeasibility (aka cryptographic hardness)

  • A process is computationally infeasible if it would take an impracticably long time (eg. billions of years) to do it for anyone who might conceivably have an interest in carrying it out.
  • Why should we care? Well, without knowing the computational feasibility or infeasibility of a problem we cannot make any claims that people can't cheat on our network by overwriting data or creating data without following the rules. Say for example I know the password to my account and I think that it's secure. If it takes you 3 days to randomly guess all the possible combinations of characters that my password might be, then it's really only secure as long as someone doesn't decide to take the time to figure it out. If it takes you 3 billion years to guess all the possible combinations though, then the story is quite different and I can rest assured that I'm not going to wake up one day and have all my data changed or stolen.
// For example, pick any Ethereum address and try to guess the private key that unlocks it. Go ahead! 
// https://www.myetherwallet.com/#send-transaction
  • https://en.wikipedia.org/wiki/Computational_hardness_assumption


Randomness

  • Random data does not have a pattern. If one were to take a series of random data and cut it in half, then ask someone to guess the second half given the first, any possible values would be equally likely and no one could do better than a wild guess. In computer science and information theory, we often say that if a random string of data is long enough it is computationally infeasable to guess it, and thus it is secure against attacks.
  • Why should we care? Well we're actually going to use random data to create accounts in such a way that only the person who created the account can use it to send transactions or sign data. We're also going to use random data to create a random string of characters that people have to guess in order to earn a reward and create the next state transition. The longest chain of valid state transitions is the agreed upon valid state, and since people are competing to earn the rewards that come with processing valid state transitions, anyone who wants to spam or overwrite the network would have to solve more puzzles faster than everyone else competing to do so, which on networks like Bitcoin or Ethereum is many.
// Note: if you want to run this you'll currently need to copypasta to the Rust Playground.
// - https://play.rust-lang.org

extern crate rand;
use rand::prelude::*;

// generate a large random number
fn key_gen() -> String {
    
    let rn: i32 = thread_rng().gen_range(100000, 1000000);
    rn.to_string()
}

fn main() {

    let key = key_gen();
    println!("key: {}", key);
}

// if you want to learn more about how the rand crate works in Rust, they've got a great book and docs
// - https://crates.io/crates/rand
// - https://rust-random.github.io/book/intro.html

Ethereum Specific Randomness

  • https://en.ethereum.wiki/Alternative-blockchains,-randomness,-economics,-and-other-research-topics
  • https://vitalik.ca/files/randomness.html
  • https://ethresear.ch/t/posw-random-beacon/1814

Random Randomness

  • https://blog.cloudflare.com/why-randomness-matters/
  • https://en.wikipedia.org/wiki/Entropy_(information_theory)
  • https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Security_and_practical_considerations
  • https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator
  • https://www.quantamagazine.org/a-unified-theory-of-randomness-20160802/
  • https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API


Hash Functions

  • A hash function (or hash algorithm) is a process by which a piece of data of arbitrary size (could be anything; a piece of text, a picture, or even a list of other hashes) is processed into a small piece of data (usually 32 bytes) which looks completely random, and from which no meaningful data can be recovered about the document, but which has the important property that the result of hashing one particular document is always the same. Additionally, it is crucially important that it is computationally infeasible to find two documents that have the same hash. Generally, changing even one letter in a document will completely randomize the hash; for example, the SHA3 hash of "Saturday" is c38bbc8e93c09f6ed3fe39b5135da91ad1a99d397ef16948606cdcbd14929f9d, whereas the SHA3 hash of Caturday is b4013c0eed56d5a0b448b02ec1d10dd18c1b3832068fbbdc65b98fa9b14b6dbf. Hashes are usually used as a way of creating a globally agreed-upon identifier for a particular document that cannot be forged. In the biz they call the input to a hash function the "preimage" and the output is called the "digest".
  • Why should we care? Well we're acually going us a hash function to create the random string of characters that need to be guessed in order to earn a reward and create the next state transition. We're also going to use a hash function to store the history of all those state transitions. Since every hash is completely deterministic, if someone were to change 1 datapoint in the past, it would literally change every piece of data that came after it. This means that in order to cheat and arbitrarily rewrite the state someone would have to re-solve all the puzzles after that change, and do so faster than everyone else working on the current puzzles. The more people are working to solve puzzles on the network the harder someone who wants to cheat would have to work as well. This means that the security of the network is tied to the amount of people working to earn rewards by creating valid state transitions, which is often tied to the monetary value of those rewards, which for networks like Bitcoin or Ethereum is generally quite high.
// Note: if you want to run this you'll currently need to copypasta to the Rust Playground.
// - https://play.rust-lang.org

use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

// Turn Arbitrary Stuff Into &[u8] Slice
pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

// Hash &[u8] Into Hex String
pub fn hash_u8(stuff: &[u8]) -> String {
    
    let mut hasher = DefaultHasher::new();
    hasher.write(stuff);
    let digest = hasher.finish();
    let hex_digest = format!("{:#X}", digest);
        
    hex_digest
}    

// Takes in stuff
// Turns it to a u8 slice
// Hashes that slice into a hex string
pub fn hash<T>(stuff: &T) -> String {
    
    let u8_stuff = unsafe {
        any_as_u8_slice(stuff)
    };
    let hash_of_stuff = hash_u8(u8_stuff);
    
    hash_of_stuff
}

fn main() {
    
    
    // Let's hash something!
    
    let my_data = String::from("stuff and stuff");
    println!("my data: {}", my_data);
    
    let my_hash = hash(&my_data);
    println!("my hash: {}", my_hash);
    
    
    // Let's try to find a hash that has an arbitrary amount of 0s
    
    // this is the number that we will hash
    let mut random_number = 0;
    
    // difficulty = the amount of 0s we want in our hash
    // - note: in the Rust Playground a difficulty > 8 
    // - will often fail or simply spin and do nothing
    let difficulty = 7; 
    
    // amount of times to guess
    for i in 0..100000 {
        
        // let's hash our random number to create a guess
        let guess = hash(&random_number);
        
        // count the amount of 0s in the hash of your guess
        let mut count = 0;
        for i in guess.chars() {
                if i == '0' {
                    count += 1;
                }
            }
        
        // if your guess has enough 0s print the results
        if count > difficulty {
            println!("\n/// WINNING ///");
            println!("Difficulty: {}", difficulty);
            println!("Winning Number: {}", random_number);
            println!("Winning Hash: {}", guess);
            break
        }
        
        // if you were incorrect augment the number by 1
        // and try again
        random_number += 1;
    }
}
  • https://en.wikipedia.org/wiki/Hash_function
  • https://en.wikipedia.org/wiki/Cryptographic_hash_function
  • https://ethereum.stackexchange.com/questions/550/which-cryptographic-hash-function-does-ethereum-use


Hash Trees (aka Merkle trees)

  • The concept of a hash tree is often referred to as a "Merkle Tree", named after Ralph Merkle who patented the idea in 1979. A merkle tree is a hash of a hash of a hash of a hash, etc... Essentially, you can take arbitrary data and hash it, then add data and hash it, and so on which results in the "root" or the latest hash being a hash of all the previous data. The only way to get that hash is to have all the hashes or data that came before it. You can imagine this like a "tree" in that the "root" is a single hash, but there's lots of things ("leaves") that get hashed together ("branches"), and then hashed together again and again until it's all been hashed together and there's a single hash "root". The benefit of this is that if you know that a certain set of data will hash down to a single root, then if anyone changes any piece of data it'll change every hash after that. This helps with quickly verifying that data is or isn't in a set, or that the data someone is providing you with is the same as everyone else without checking every piece (because you only have to check the single hash).
  • Why should we care? This is how we're going to store the history of all state transitions. Everytime someone earns the right to create a new valid state transition they're going to look at all the pending transactions that were requested between the last state change. They'll then check that all the transaction requests are valid and update the state accordingly. They then publish the new state along with the transactions that were processed to get to that state, the hash of the previous state, and a hash of the new state combined with the previous state's hash. This is called a block, because it's a bunch of data that gets processed together in batches, or "blocks". Every time there's a state transition a new block is created, and since every block comes published with a hash of the previous blocks, they're all chained together such that if you change something in a past block you also change all the hashes of any blocks after that. This is why our p2p decentralized database is called a "blockchain". It's also called a merkle tree because the only way to get to that latest hash is to include all the previous data. The "root" is the latest hash, and the branches are all the data that went into that hash, and all the data that went into those hashes, and so on...
// Note: if you want to run this you'll currently need to copypasta to the Rust Playground.
// - https://play.rust-lang.org

use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

// Turn Arbitrary Stuff Into &[u8] Slice
pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

// Hash &[u8] Into Hex String
pub fn hash_u8(stuff: &[u8]) -> String {
    
    let mut hasher = DefaultHasher::new();
    hasher.write(stuff);
    let digest = hasher.finish();
    let hex_digest = format!("{:#X}", digest);
        
    hex_digest
}    

// Takes in stuff
// Turns it to a u8 slice
// Hashes that slice into a hex string
pub fn hash<T>(stuff: &T) -> String {
    
    let u8_stuff = unsafe {
        any_as_u8_slice(stuff)
    };
    let hash_of_stuff = hash_u8(u8_stuff);
    
    hash_of_stuff
}

fn main() {
    
    // a short chain of hashed data
    let my_data = String::from("stuff and stuff");
    let my_hash_preimage = my_data.clone();
    let my_hash = hash(&my_hash_preimage);
    println!("\nmy hash: {}", my_hash);
    let my_data2 = String::from("stuff that came after the first stuff");
    let my_hash_preimage2 = my_hash + &my_data2;
    let my_hash2 = hash(&my_hash_preimage);
    println!("my hash 2: {}", my_hash2);
    let my_data3 = String::from("more stuff");
    let my_hash_preimage3 = my_hash2 + &my_data3;
    let my_hash3 = hash(&my_hash_preimage3);
    println!("my hash 3: {}", my_hash3);
    
    // a very similar short chain of data
    // with very different hashes
    let my_data = String::from("stuff or stuff");
    let my_hash_preimage = my_data.clone();
    let my_hash = hash(&my_hash_preimage);
    println!("\nV2 of my hash: {}", my_hash);
    let my_data2 = String::from("stuff that came after the first stuff");
    let my_hash_preimage2 = my_hash + &my_data2;
    let my_hash2 = hash(&my_hash_preimage);
    println!("V2 of my hash 2: {}", my_hash2);
    let my_data3 = String::from("more stuff");
    let my_hash_preimage3 = my_hash2 + &my_data3;
    let my_hash3 = hash(&my_hash_preimage3);
    println!("V2 of my hash 3: {}", my_hash3);
    
}

An example of a binary merkle tree. Hashes 0-0 and 0-1 are the hash values of data blocks L1 and L2, respectively, and hash 0 is the hash of the concatenation of hashes 0-0 and 0-1.

  • https://en.wikipedia.org/wiki/Hash_trie
  • https://en.wikipedia.org/wiki/Merkle_tree
  • https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/
  • https://ethereum.stackexchange.com/questions/2100/what-is-a-block-hash
  • Merklize this! Merkle Trees & Patricia Tries: https://www.zeroknowledge.fm/57
  • https://medium.com/building-blocks-on-the-chain/learn-merkle-trees-by-programming-your-own-4f0438d40063




General Resources & References

  • https://en.wikipedia.org/wiki/Cryptography
  • https://github.com/ethereum/wiki/wiki/Glossary




PROBLEM / TODO

  • Overall these explanations are a good high level explanation of blockchain basics and state transitions, but don't do a very good job explaining the cryptographic functions being used. Either add more robust explanations and terminology so that after this someone can ~read other crypto tutorials, or maybe move this to the State Transitions section?


Accounts

Crypto in action.




Core Concepts:

  • trusting math enforced via code vs an opaque centralized b2c database operator
  • creating accounts
  • creating signatures

Other Concepts

  • how you can create a pair of numbers such that it's easy to find one from the other, but not the other way around
  • how you can sign stuff with your private key, and others can verify that signature with your public key
  • this is grounded in the hardness of cryptography, not the choice of a central operator

Weak randomness can lead to hacked keys and crypto systems. This isn't a concern with our toy demo, but for any live working blockchain it's mission critical.

  • https://en.wikipedia.org/wiki/RSA_(cryptosystem)

TODO

  • add info about the data structure of accounts (nonces, balances, keys, etc)

Account nonce:

A transaction counter in each account. This prevents replay attacks where a transaction sending eg. 20 coins from A to B can be replayed by B over and over to continually drain A's balance.

Computational infeasibility:

A process is computationally infeasible if it would take an impracticably long time (eg. billions of years) to do it for anyone who might conceivably have an interest in carrying it out. Generally, 280 computational steps is considered the lower bound for computational infeasibility.

Encryption:

Encryption is a process by which a document (plaintext) is combined with a shorter string of data, called a key (eg. c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4), to produce an output (ciphertext) which can be "decrypted" back into the original plaintext by someone else who has the key, but which is incomprehensible and computationally infeasible to decrypt for anyone who does not have the key.

Public key encryption:

A special kind of encryption where there is a process for generating two keys at the same time (typically called a private key and a public key), such that documents encrypted using one key can be decrypted with the other. Generally, as suggested by the name, individuals publish their public keys and keep their private keys to themselves.

Digital signature:

A digital signing algorithm is a process by which a user can produce a short string of data called a "signature" of a document using a private key such that anyone with the corresponding public key, the signature and the document can verify that (1) the document was "signed" by the owner of that particular private key, and (2) the document was not changed after it was signed. Note that this differs from traditional signatures where you can scribble extra text onto a document after you sign it and there's no way to tell the difference; in a digital signature any change to the document will render the signature invalid.




This recreates the RSA style protocol explained in the RSA wiki page, randomly generating primes for p and q and demonstrating that the protocol works for various inputs :)


extern crate rand;
use rand::prelude::*;

// variable names based off Euclidean divison equation: a = b · q + r
// https://crates.io/crates/gcd
// https://en.wikipedia.org/wiki/Greatest_common_divisor
fn gcd(a: i32,
       b: i32) -> i32 {
    
    let (mut a, mut b) = if a > b {
        (a, b)
    } else {
        (b, a)
    };

    while b != 0 {
        let r = a % b;
        a = b;
        b = r;
    }

    a
}

// lowest common multiple
// https://en.wikipedia.org/wiki/Least_common_multiple
fn lcm(a: i32,
       b: i32) -> i32 {
    
    let lcm = (a * b) / gcd(a, b);
    
    lcm
}

// Carmichael's totient function
// https://en.wikipedia.org/wiki/Carmichael_function
fn ctf(a: i32,
       b: i32) -> i32 {
    
    lcm((a - 1), (b - 1))
}

// slowly check if a number is prime
fn slow_prime_check(num: i32) -> bool {
    
    if num < 0 {
        println!("number must be greater than 0");
    }
    
    if num > 1000000 {
        println!("number cannot be greater than 1000000");
    }
    
    for i in 2..num{
        if num % i == 0 {
            return false
        }
    }
    true
}

// slowly yet randomly generate a prime number within a range
fn prime_gen(low: i32,
             high: i32) -> i32 {
    
    for i in 0..1000000 {
        let p = thread_rng().gen_range(low, high);
        if slow_prime_check(p) {
            return p
        }
    }
    0
}

// generate a public key within a range
fn pub_key_gen(min: i32,
               max: i32) -> i32 {
    
    let pub_key = prime_gen(min, max);
    assert!(max % pub_key != 0);
    
    pub_key
}

// slowly find the modular multiplicative inverse of a prime 
fn slow_mmi(ctf_pq: i32,
            pub_key: i32,
            max: i32)-> i32 {
    
    for i in 2..max {
        if (i * pub_key) % ctf_pq == 1 {
            return i
        }
    }
    println!("Try larger search?");
    0
}

// create a private key from a public key and other data
fn priv_key_gen(ctf_pq: i32,
                pub_key: i32) -> i32 {
    
    let priv_key = slow_mmi(ctf_pq, pub_key, 100000);
    
    priv_key
}

// Because... Rust.
// exp_mod() is like pow() with a mod option
// (like python does natively, but not Rust)
// https://docs.python.org/3/library/functions.html#pow
// https://doc.rust-lang.org/nightly/std/primitive.i32.html#method.pow
// https://en.wikipedia.org/wiki/Modular_exponentiation
fn exp_mod(input: i32,
           power: i32,
           modulo: i32) -> i32 {
    
    let mut out = (input * input) % modulo;
    // because the first iter of out took 2 off the base
    for i in 0..power-2 {
        out = (out * input) % modulo;
    }
    
    out
}

// toy RSA function
fn toy_rsa(input: Vec<i32>,
           key: i32,
           modulo: i32) -> Vec<i32> {
    
    let output = input.iter()
                      .map(|x| exp_mod(*x, key, modulo))
                      .collect();
    output
}

// convert string to Vec<i32>
fn s2v(input: String) -> Vec<i32> {
    
    let output: Vec<i32> = input.as_bytes()
                                .iter()
                                .map(|x| *x as i32)
                                .collect();
    
    output
}

// convert Vec<i32> to string
fn v2s(input: Vec<i32>) -> String {
    
    let output_u8: Vec<u8> = input.iter()
                                  .map(|x| *x as u8)
                                  .collect();
    let output_string = String::from_utf8(output_u8).unwrap();
    
    output_string
}



// Rollin, rollin, rollin... !
fn main() {

    /*
    // the numbers from wiki work
    // https://en.wikipedia.org/wiki/RSA_(cryptosystem)
    let p = 61;
    let q = 53;
    let m = 3233;
    let ctf_pq = 780;
    let pub_key = 17;
    let priv_key = 413;
    */

    // usually works on Rust Playground when p and q are < 500
    let p = prime_gen(5, 100);
    let q = prime_gen(5, 100);
    let m = p * q; 
    let ctf_pq = ctf(p, q);
    let pub_key = pub_key_gen(1, ctf_pq);
    let priv_key = priv_key_gen(ctf_pq, pub_key);
    println!("\n// Params //");
    assert!(p > 0);
    assert!(q > 0);
    println!("p: {}", &p);
    println!("q: {}", &q);
    println!("m: {}", &m);
    println!("ctf_pq: {}", &ctf_pq);
    println!("pub_key: {}", &pub_key);
    println!("priv_key: {}", &priv_key);
    
    let message = "thepasswordispassword".to_string();
    let m2nums = s2v(message.clone());
    let ciphertext = toy_rsa(m2nums.clone(), pub_key, m);
    let decrypted = toy_rsa(ciphertext.clone(), priv_key, m);
    let message2 = v2s(decrypted.clone());
    assert_eq!(message, message2);
    println!("\n// Testing //");
    println!("message: {:?}", &message);
    println!("message as nums: {:?}", &m2nums);
    println!("ciphertext: {:?}", &ciphertext);
    println!("decrypted nums: {:?}", &decrypted);
    println!("decrypted message: {}", &message2);
    
    println!("DONE!");

}




Always Good To Get A Second Opinion

Ethereum Wiki Glossary

  • https://github.com/ethereum/wiki/wiki/Glossary

RSA Pub Key Stuff

  • https://en.wikipedia.org/wiki/Public-key_cryptography
  • https://en.wikipedia.org/wiki/RSA_(cryptosystem)
  • https://en.wikipedia.org/wiki/Least_common_multiple
  • https://en.wikipedia.org/wiki/Modular_exponentiation

Ethereum Key Stuff

  • Appendix F of the Yellow Paper: https://ethereum.github.io/yellowpaper/paper.pdf
  • Parity Ethereum Client (Rust): ethkey library: https://github.com/paritytech/parity-ethereum/tree/master/accounts/ethkey



TX

Doing stuff.




Core Concepts:

  • when you sign/do stuff with a public key crypto system you prove that it came from your address.
  • you also have assurance that your key or a 51% attack are needed to compromise that assurance

TODO / Ideas:

  • the part where thread messages come in at different times could be good to show why nonces are important: https://doc.rust-lang.org/book/ch16-02-message-passing.html

Transaction (TX):

A transaction is a digitally signed message authorizing some particular action associated with the blockchain. In a currency, the dominant transaction type is sending currency units or tokens to someone else; in a database actions like registering domain names, sending messages, or posting to a social feed would also be valid transaction types. Essentially, a state is an ordering of data, a transaction is a request to change the state of that data, and the state transition funciton determines how that happens.

3.1. The Transaction

The basic method for Ethereum accounts to interact with each other. The transaction is a single cryptographically signed instruction sent to the Ethereum network. There are two types of transactions: message calls and contract creations. Transactions lie at the heart of Ethereum, and are entirely responsible for the dynamism and flexibility of the platform. Transactions are the bread and butter of state transitions, that is of block additions, which contain all of the computation performed in one block. Each transaction applies the execution changes to the machine state, a temporary state which consists of all the required changes in computation that must be made before a block is finalized and added to the world state.

  • https://github.com/chronaeon/beigepaper/blob/master/beigepaper.pdf

TX Fees

Every transaction is required to include a tx fee. Miners have the choice of including the transaction and collecting the fee or not. This is to prevent someone from spamming the network or creating a DoS attack.




Code

extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

/// RSA STUFF ///

// greatest common divisor
fn gcd(a: i32,
       b: i32) -> i32 {
    
    let (mut a, mut b) = if a > b {
        (a, b)
    } else {
        (b, a)
    };

    while b != 0 {
        let r = a % b;
        a = b;
        b = r;
    }

    a
}

// lowest common multiple
fn lcm(a: i32,
       b: i32) -> i32 {
    
    let lcm = (a * b) / gcd(a, b);
    
    lcm
}

// Carmichael's totient function
// https://en.wikipedia.org/wiki/Carmichael_function
fn ctf(a: i32,
       b: i32) -> i32 {
    
    lcm((a - 1), (b - 1))
}

// slowly check if a number is prime
fn slow_prime_check(num: i32) -> bool {
    
    if num < 0 {
        println!("number must be greater than 0");
    }
    
    if num > 1000000 {
        println!("number cannot be greater than 1000000");
    }
    
    for i in 2..num{
        if num % i == 0 {
            return false
        }
    }
    true
}

// slowly yet randomly generate a prime number within a range
fn prime_gen(low: i32,
             high: i32) -> i32 {
    
    for i in 0..1000000 {
        let p = thread_rng().gen_range(low, high);
        if slow_prime_check(p) {
            return p
        }
    }
    0
}

// generate a public key within a range
fn pub_key_gen(min: i32,
               max: i32) -> i32 {
    
    let pub_key = prime_gen(min, max);
    assert!(max % pub_key != 0);
    
    pub_key
}

// slowly find the modular multiplicative inverse of a prime 
fn slow_mmi(ctf_pq: i32,
            pub_key: i32,
            max: i32)-> i32 {
    
    for i in 2..max {
        if (i * pub_key) % ctf_pq == 1 {
            return i
        }
    }
    println!("Try larger search?");
    0
}

// create a private key from a public key and other data
fn priv_key_gen(ctf_pq: i32,
                pub_key: i32) -> i32 {
    
    let priv_key = slow_mmi(ctf_pq, pub_key, 100000);
    
    priv_key
}

// Because... Rust.
fn exp_mod(input: i32,
           power: i32,
           modulo: i32) -> i32 {
    
    let mut out = (input * input) % modulo;
    // because the first iter of out took 2 off the base
    for i in 0..power-2 {
        out = (out * input) % modulo;
    }
    
    out
}

// toy RSA function
fn toy_rsa(input: Vec<i32>,
           key: i32,
           modulo: i32) -> Vec<i32> {
    
    let output = input.iter()
                      .map(|x| exp_mod(*x, key, modulo))
                      .collect();
    output
}

// convert string to Vec<i32>
fn s2v(input: String) -> Vec<i32> {
    
    let output: Vec<i32> = input.as_bytes()
                                .iter()
                                .map(|x| *x as i32)
                                .collect();
    
    output
}

// convert Vec<i32> to string
fn v2s(input: Vec<i32>) -> String {
    
    let output_u8: Vec<u8> = input.iter()
                                  .map(|x| *x as u8)
                                  .collect();

    let output_string = String::from_utf8(output_u8).unwrap();

    output_string
}

/// END RSA STUFF ///


// turn stuff into a &[u8] slice
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

// hash &[u8] slices into hex Strings
fn hash_u8(stuff: &[u8]) -> String {
    
    let mut hasher = DefaultHasher::new();
    hasher.write(stuff);
    let digest = hasher.finish();
    let hex_digest = format!("{:#X}", digest);
        
    hex_digest
}

#[derive(Debug, Clone)]
struct TX {
    sender: i32,
    receiver: i32,
    tx_amount: f32,
    nonce: i32,
}

#[derive(Debug, Clone)]
struct SignedTX {
    tx: TX,
    signature: Vec<i32>,
}

// NOTE: if the tx uses an invalid signature
// there is a high likelihood that it will produce
// invalid utf8, and thus this function will crash
// when v2s() tries to turn the Vec<i32> into a String
fn check_signed_tx(signed_tx: SignedTX,
                   modulo: i32) -> bool {
    
    let tx_as_bytes = unsafe {
        any_as_u8_slice(&signed_tx.tx)
    };
    let tx_hash = hash_u8(tx_as_bytes);
    println!("tx hash: {}", tx_hash);
    
    let decrypted_tx_hash_sig = toy_rsa(signed_tx.signature,
                                        signed_tx.tx.sender,
                                        modulo);
    let decrypted_tx_hash = v2s(decrypted_tx_hash_sig);
    println!("decrypted tx hash: {}", decrypted_tx_hash);
    
    match tx_hash == decrypted_tx_hash {
        true => true,
        false => {
            println!("not valid tx");
            return false
        },
    }
}


// Rollin, rollin, rollin...
fn main() {

    // Setup RSA To Create Test Accounts
    // fix p and q to generate keys
    let p = 61; //prime_gen(5, 100);
    let q = 53; //prime_gen(5, 100);
    assert!(p > 0);
    assert!(q > 0);
    // m is now a constant we can use for all keys
    // that share the same p and q setup
    let m = p * q; //3233
    ///////////////////////////////////////
    // Generated Keys from fixed p and q //
    // - SENDER: pub: 773, priv: 557     //
    // - RECEIVER: pub: 179, priv 719    //
    ///////////////////////////////////////
    let account1_pub_key = 773;
    let account1_priv_key = 557;
    let account2_pub_key = 179;
    let account2_priv_key = 719;
    // if you want to generate more keys
    //let ctf_pq = ctf(p, q);
    //let pub_key = pub_key_gen(1, ctf_pq);
    //let priv_key = priv_key_gen(ctf_pq, pub_key);
    /*
    // print RSA params to capture keys that work
    println!("p: {}", &p);
    println!("q: {}", &q);
    println!("m: {}", &m);
    println!("ctf_pq: {}", &ctf_pq);
    println!("pub_key: {}", &pub_key);
    println!("priv_key: {}", &priv_key);
    */
    
    
    // TX Hashing
    let tx = TX {
        sender: 86, //account1_pub_key,
        receiver: account2_pub_key,
        tx_amount: 1000.0,
        nonce: 345,
    };
    
    let tx_bytes: &[u8] = unsafe {
        any_as_u8_slice(&tx)
    };
    println!("tx: {:?}", &tx_bytes);
    
    let tx_hash = hash_u8(tx_bytes);
    println!("tx hash: {:#?}", &tx_hash);
    
    
    // TX Signing
    let message = tx_hash.clone();
    let m2nums = s2v(message.clone());
    let ciphertext = toy_rsa(m2nums.clone(), account1_priv_key, m);
    println!("\n/// Testing ///");
    println!("message: {:?}", &message);
    println!("message as nums: {:?}", &m2nums);
    println!("ciphertext: {:?}", &ciphertext);
    
    // Recover the TX hash from the signed ciphertext
    //let decrypted = toy_rsa(ciphertext.clone(), priv_key, m);
    //let message2 = v2s(decrypted.clone());
    //assert_eq!(message, message2);
    //println!("decrypted nums: {:?}", &decrypted);
    //println!("decrypted message: {}", &message2);
    
    
    // Add signed TX hash to a SignedTX
    let signed_tx = SignedTX {
        tx: tx,
        signature: ciphertext,
    };
    println!("\n{:#?}", &signed_tx);


    // Check that the SignedTX is valid 
    // NOTE: if the tx uses an invalid signature
    // there is a high likelihood that it will produce
    // invalid utf8, and thus this function will crash
    // when v2s() tries to turn the Vec<i32> into a String
    let check_tx = check_signed_tx(signed_tx, m);
    println!("is the signature authentic?\n{}", check_tx);

}




But wait... there' smore

  • https://en.wikipedia.org/wiki/Digital_signature
  • https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
  • https://github.com/ethereum/wiki/wiki/Glossary





State Transitions

The proof is in the pudding.




Goal

  • Upgrade our state transition function from a consensus model of "because the central operator says so" to "because someone did the work and proved that they earned the right to make these changes for the community".

Core Concepts:

  • pending_tx pool => blocks (nonces, block headers, etc...)
  • ledger history => hashed merkle tries
  • anti-spam protection with proof of work on each block

Open Questions

  • do people generally refer to the "consensus model" and the "state transition function" as the same thing, or are they different, or are they mostly the same but slightly different?
  • How do block nonces differ from TX nonces?

Idea To Add

  • because accounts are not in a centralized database the user controls the account, and as long as the network is operational, the user's account is too. Just like if your bank went under (https://en.wikipedia.org/wiki/Lehman_Brothers), your account would be gone, if the P2P network you're on goes under same deal.

Block:

A block is a package of data that contains zero or more transactions, the hash of the previous block ("parent"), and optionally other data. Because each block (except for the initial "genesis block") points to the previous block, the data structure that they form is called a "blockchain".

Proof of work:

One important property of a block in Bitcoin, Ethereum and many other crypto-ledgers is that the hash of the block must be smaller than some target value. The reason this is necessary is that in a decentralized system anyone can produce blocks, so in order to prevent the network from being flooded with blocks, and to provide a way of measuring how much consensus there is behind a particular version of the blockchain, it must in some way be hard to produce a block. Because hashes are pseudorandom, finding a block whose hash is less than 0000000100000000000000000000000000000000000000000000000000000000 takes an average of 4.3 billion attempts. In all such systems, the target value self-adjusts so that on average one node in the network finds a block every N minutes (eg. N = 10 for Bitcoin and 1 for Ethereum).

Proof of work nonce:

A meaningless value in a block which can be adjusted in order to try to satisfy the proof of work condition

Mining:

Mining is the process of repeatedly aggregating transactions, constructing a block and trying different nonces until a nonce is found that satisfies the proof of work condition. If a miner gets lucky and produces a valid block, they are granted a certain number of coins as a reward as well as all of the transaction fees in the block, and all miners start trying to create a new block containing the hash of the newly generated block as their parent.

Stale:

A stale is a block that is created when there is already another block with the same parent out there; stales typically get discarded and are wasted effort.

Fork:

A situation where two blocks are generated pointing to the same block as their parent, and some portion of miners see one block first and some see the other. This may lead to two blockchains growing at the same time. Generally, it is mathematically near-certain that a fork will resolve itself within four blocks as miners on one chain will eventually get lucky and that chain will grow longer and all miners switch to it; however, forks may last longer if miners disagree on whether or not a particular block is valid.




Videos

Cryptoeconomics - 1.1 - Hashes and Signatures

Cryptoeconomics - 1.1 - Hashes and Signatures.




Code Resources:

  • https://github.com/cryptoeconomics-study/code/blob/master/c3_ProofOfWork/proofOfWork.js
  • https://github.com/tensor-programming/Rust_block_chain/blob/master/src/blockchain.rs
// https://github.com/tensor-programming/Rust_block_chain/blob/master/src/blockchain.rs

impl Chain {

    pub fn proof_of_work(header: &mut Blockheader) {
        loop {
            let hash = Chain::hash(header);
            let slice = &hash[..header.difficulty as usize];
            match slice.parse::<u32>() {
                Ok(val) => {
                    if val != 0 {
                        header.nonce += 1;
                    } else {
                        println!("Block hash: {}", hash);
                        break;
                    }
                },
                Err(_) => {
                    header.nonce += 1;
                    continue;
                }
            };
        }
    }
}

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;


struct MyStruct {
    id: u8,
    data: String,
}

#[derive(Debug, Clone)]
struct TX {
    sender: String,
    receiver: String,
    tx_amount: f32,
    nonce: i32,
}

#[derive(Debug, Clone, Hash)]
struct AltTX {
    sender: String,
    receiver: String,
    tx_amount: i32,
    nonce: i32,
}


unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

fn hash(stuff: &[u8]) -> String {
    
    let mut hasher = DefaultHasher::new();
    hasher.write(stuff);
    let digest = hasher.finish();
    let hex_digest = format!("{:#X}", digest);
        
    hex_digest
}


fn main() {

    // Using Example Struct
    let my_struct = MyStruct {
        id: 98,
        data: "Hello World".to_string(),
    };
    
    let bytes: &[u8] = unsafe { 
        any_as_u8_slice(&my_struct)
    };

    println!("{:?}", &bytes);
    
    
    // Using TX Struct
    let tx = TX {
        sender: "Your Mom".to_string(),
        receiver: "Yours truly".to_string(),
        tx_amount: 1000.0,
        nonce: 345,
    };
    
    let tx_bytes: &[u8] = unsafe {
        any_as_u8_slice(&tx)
    };
    println!("tx: {:?}", &tx_bytes);
    
    let tx_hash = hash(tx_bytes);
    println!("tx hash: {:#?}", tx_hash);
}




But wait... there's more

Hashing and Merkle Trees

  • https://en.wikipedia.org/wiki/Merkle_tree
  • https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/
  • https://ethereum.stackexchange.com/questions/2100/what-is-a-block-hash
  • Merklize this! Merkle Trees & Patricia Tries: https://www.zeroknowledge.fm/57

PoW & Blocks

  • https://github.com/ethereum/wiki/wiki/White-Paper#blockchain-and-mining
  • https://ethereum.stackexchange.com/questions/5833/why-do-we-need-both-nonce-and-mixhash-values-in-a-block

Terms

  • https://github.com/ethereum/wiki/wiki/Glossary



Roll Your Own PoW "Blockchain"

I love the smell of progress in the morning.




Permalink to the Rust Playground

  • https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=703530237531ef005e811b13e31b6533
extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

/* "Blockchain" Sketch
A modular architecture where you can change any of the modules,
say changing PoW to PoS, and it still runs. This is the PoW version.
*/


/* ARCHITECTURE SKETCH
Functions
- State Transition Function
- Data Encoding Function
- Hash Function
- Key Generation Function
- Account Data
- Transaction Data
- State Data: a user defined configuration of the various blockchain modules
State Transition Function
 - determines what is a valid state transition by verifying tx
 - determines who is authorized to create a state change via PoA, PoW, PoS, etc...
 - impliments the state change
 - this needs to contain all params out of the box including the difficulty level
   and/or any functions needed to upgrade/modify those params
Data Encoding Function
 - takes in arbitrary data and encodes it in a specific way
 - the entire "blockchain" uses this in order to allow any function
   to process arbitrary data inputs as well as sharing data between functions
 - standard for now, but may become upgradable as Ethreum and Substrate data is explored
Hash Function
 - takes in arbitrary data and returns a string
 - the way that data is hashes or the encoding of the string can be changed
Key Generation Function
 - the method to generate public and private key pairs
 - can be a centralized system, RSA, elliptic curves, etc...
 - contains all parmas neccessary to work out of the box
Account Data
 - these will ALWAYS be a key/value pair in a HashMap
 - what you can change is the data that the account struct holds
 - UTXOs TBD
TX Data
 - standard for now
State Data
 - accounts: HashMap<i32, Account>
 - pending_tx: Vec<TX>
 - history: Vec<Block>
 - data encoding: user defined
 - State transition function: user defined
 - hash function: user defined
 - key gen function: user defined
STANDARD STRUCTS
These will keep the same name throughout the program, but their underlying
logic can be changed/upgraded.
- Account
- TX
- BlockHeader
- Block
- State
STANDARD FUNCTIONS
These will keep the same name throughout the program, but their underlying
logic can be changed/upgraded.
- data_encode()
- key_gen()
- hash()
- new_account()
- new_tx()
- new_state_transition() (checks pending tx and produces new block)
- check_state_transition() (checks the most recently produced block)
*/



pub struct DataEncoding;

impl DataEncoding {
    
    // TODO
    //
    // - Upgrade to something like what Substrate uses
    //   https://github.com/paritytech/substrate/tree/master/core/serializer
    // - Also, does it need it's own struct/impl or does it
    //   make sense to have it in the State impl?
    //
    // Turn stuff into an &[u8] slice
    pub unsafe fn to_u8<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }    

    // i32 -> String
    // https://doc.rust-lang.org/nightly/std/string/trait.ToString.html
    pub fn i2s(input: i32) -> String {
        
        let output = input.to_string();
        
        output
    }
    
    // String -> i32
    // https://stackoverflow.com/questions/27043268/convert-a-string-to-int-in-rust
    pub fn s2i(input: String) -> i32 {
        
        let output = input.parse::<i32>().unwrap();
        
        output
    }

    // string -> Vec<i32>
    pub fn s2v(input: String) -> Vec<i32> {
        
        let output: Vec<i32> = input.as_bytes()
                                    .iter()
                                    .map(|x| *x as i32)
                                    .collect();
        
        output
    }
 
    // Vec<i32> -> String
    // https://doc.rust-lang.org/nightly/std/string/trait.ToString.html
    pub fn v2s(input: Vec<i32>) -> String {
        
        let mut output_vec = Vec::new();
        for i in input {
            output_vec.push(i.to_string())
        }
        let output_string = output_vec.join("");
        
        output_string
    }
}


pub struct Hash;

impl Hash {
    
    // Takes a preimage ("preimage" = fancy word for input to a hash function)
    // Encodes it via the data_encode() function
    // Hashes that data into a hex or an integer (you choose)
    fn hash<T>(preimage: &T) -> String {
        
        // convert to u8
        let stuff_as_u8 = unsafe {
            DataEncoding::to_u8(preimage)
        };
        
        // hash u8 to u64
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff_as_u8);
        
        // format u64 hash as String
        let digest = hasher.finish();
        let string_digest = format!("{}", hasher.finish());
        string_digest
        
        // hex String
        //let digest = hasher.finish();
        //let hex_digest = format!("{:#X}", digest);
        //hex_digest
        
        // i32
        //let digest = hasher.finish() as i32;
        //digest 
        
        // f64
        //let digest = hasher.finish() as f64;
        //digest 
     
        // u64
        //let digest = hasher.finish();
        //digest
    }   
    
    // Create A Merkle Tree Of All TX In A Vec
    pub fn hash_tree<T>(stuff: Vec<T>) -> String {
        
        let mut v = Vec::new();

        for i in &stuff {
            let hashed = Hash::hash(&i);
            v.push(hashed);
        }

        if v.len() % 2 == 1 {
            let last = v.last().cloned().unwrap();
            v.push(last);
        }

        while v.len() > 1 {
            let mut h1 = v.remove(0);
            let mut h2 = v.remove(0);
            h1.push_str(&mut h2);
            let nh = Hash::hash(&h1);
            v.push(nh);
        }
        
        v.pop().unwrap()
    }
    
}


// This struct holds all the data for the key generation
// and signing. If you want to use a different key
// protocol, change the data in the Keys struct as well
// as the functions in the Keys impl
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Keys {
    min: i32,
    max: i32,
    p: i32,
    q: i32,
    modulo: i32,
    ctf_pq: i32, 
}

/// "RSA" Key Generation and Signing ///
impl Keys {
    
    // These functionsare not needed as we have hard coded
    // the modulo and ctf_pq values
    /*
    // greatest common divisor
    pub fn gcd(a: i32,
               b: i32) -> i32 {
        
        let (mut a, mut b) = if a > b {
            (a, b)
        } else {
            (b, a)
        };
    
        while b != 0 {
            let r = a % b;
            a = b;
            b = r;
        }
    
        a
    }
    
    // lowest common multiple
    pub fn lcm(a: i32,
               b: i32) -> i32 {
        
        let lcm = (a * b) / Keys::gcd(a, b);
        
        lcm
    }
    
    // Carmichael's totient function
    pub fn ctf(a: i32,
               b: i32) -> i32 {
        
        Keys::lcm(a - 1, b - 1)
    }
    */
    
    // slowly check if a number is prime
    pub fn slow_prime_check(self,
                            num: i32) -> bool {
        
        if num < self.min {
            println!("number must be greater than {}", self.min);
        }
        
        if num > self.max {
            println!("number cannot be greater than {}", self.max);
        }
        
        for i in 2..num{
            if num % i == 0 {
                return false
            }
        }
        
        true
    }

    // slowly, yet randomly, generate a prime number within a range
    pub fn prime_gen(self) -> i32 {
        
        for _i in 0..self.max {
            let p = thread_rng().gen_range(self.min, self.max);
            if Keys::slow_prime_check(self, p) {
                return p
            }
        }
        
        0
    }

    // generate a private key within a range
    pub fn priv_key_gen(self) -> i32 {
        
        let priv_key = Keys::prime_gen(self);
        assert!(self.max % priv_key != 0);
        
        priv_key
    }
    
    // slowly find the modular multiplicative inverse of a prime 
    pub fn slow_mmi(self,
                    priv_key: i32)-> i32 {
        
        for i in 2..self.max {
            if (i * priv_key) % self.ctf_pq == 1 {
                return i
            }
        }
        println!("Try larger search?");
        
        0
    }
    
    // create a public key from a pricate key and RSA param data
    pub fn pub_key_gen(self,
                       priv_key: i32) -> i32 {
        
        let pub_key = Keys::slow_mmi(self, priv_key);
        
        pub_key
    }
    
    // generate a private/public key pair
    pub fn generate_keypair(self) -> (i32, i32){
        let priv_key = Keys::priv_key_gen(self);
        let pub_key = Keys::pub_key_gen(self, priv_key);
        (priv_key, pub_key)
    }
    
    // Because... Rust.
    pub fn exp_mod(self,
                   input: i32,
                   power: i32) -> i32 {
        
        let mut out = (input * input) % self.modulo;
        // because the first iter of out took 2 off the base
        for _i in 0..power-2 {
            out = (out * input) % self.modulo;
        }
        
        out
    }
    
    // Sign a TX with a toy RSA function
    pub fn sign<T>(self,
                   thing_to_be_signed: &T,
                   signing_key: i32) -> Vec<i32> {
        
        let hashed_thing = Hash::hash(thing_to_be_signed);
        
        let mut hashed_thing_vec = Vec::new();
        for i in hashed_thing.chars() {
            hashed_thing_vec.push(i.to_string().parse::<i32>().unwrap())
        }
        
        let mut signed_vec = Vec::new();
        for i in hashed_thing_vec {
            signed_vec.push(Keys::exp_mod(self, i, signing_key,));
        }

        signed_vec
    }
    
    // Check signature on a TX
    pub fn check_tx_signature(self,
                              tx: TX) -> bool {
        
        let mut tx_sig_check: Vec<i32> = tx.clone().signature;
        
        let mut tx_sig_check_pub_signed = Vec::new();
        for i in tx_sig_check {
            tx_sig_check_pub_signed.push(Keys::exp_mod(self, i, tx.data.sender))
        }
        
        let mut tx_sig_check_string = String::new();
        for i in tx_sig_check_pub_signed {
            tx_sig_check_string.push_str(&i.to_string())
        }
        
        let hashed_tx = Hash::hash(&tx.data);
        
        if tx_sig_check_string == hashed_tx {
            return true
        } else {
            return false
        }
    }
}


// This struct holds all the data needed for 
// the chosen state transition protocol.
// In this case we're doign PoW, but if you
// wanted to impliment PoS you would write a new
// STF struct and new verify_pending_tx and proof
// functions.
#[derive(Debug)]
pub struct STF {
    version: String, // PoA, PoW, PoS, etc...
    difficulty: i32, // currently PoW difficulty
    max: i32, // max time/tries for valid proof
}

impl STF {
    
    // This function encodes the rules of what qualifies as a "valid tx"
    pub fn verify_pending_tx(state: &mut State) -> Vec<TX> {
        
        let mut verified_tx = Vec::new();
        
        for i in &state.pending_tx {
        
            if !(state.accounts.contains_key(&i.data.sender)) {
                println!("Invalid TX: sender not found.");
                continue
            }
            
            if !(state.accounts.contains_key(&i.data.receiver)) {
                println!("Invalid TX: receiver not found.");
                continue
            }
            
            if !(i.data.amount > 0) {
                println!("Invalid TX: negative amount error.");
                println!("{} cannot send {} to {}", i.data.sender, i.data.amount, i.data.receiver);
                continue
            }
            
            if !(state.accounts.get(&i.data.sender).unwrap().balance > i.data.amount) {
                println!("Invalid TX: insufficient funds.");
                println!("{} cannot send {} to {}", i.data.sender, i.data.amount, i.data.receiver);
                continue         
            }
            
            if !(i.data.sender_nonce == state.accounts.get(&i.data.sender).unwrap().nonce) {
                println!("Invalid TX: potential replay tx.");
                println!("{} has nonce {}, but submitted a tx with nonce {}", i.data.sender, state.accounts.get(&i.data.sender).unwrap().nonce, i.data.sender_nonce);
                continue
            }
            
            if !(Keys::check_tx_signature(state.keys, i.clone())) {
                println!("Invalid TX: signature check failed");
                continue
            }
            
            verified_tx.push(i.clone());
        }
        
        verified_tx
    }

    // This function creates a proof that authorizes the state transition
    // This is a variation of PoW that's easy enough that it runs in the Rust Playground 
    // You could change the logic of this function to satisfy PoS or PoA as well.
    pub fn proof(state: &State,
                 mut block_data: BlockData) -> (BlockData, String) {
    
        for i in 0..state.stf.max {
        
            let mut count = 0;
            let hash = Hash::hash(&block_data);

            for i in hash.chars() {
                if i == '0' {
                    count += 1;
                }
            }
            
            if count > state.stf.difficulty {
                // success
                return (block_data, hash);
            }
            
            block_data.header.nonce += 1;
        }
        
        // failure
        return (block_data, String::from("ERROR: proof failed."))
    }
    
    // Create A New Block With Valid Transactions
    pub fn create_block(state: &mut State) -> Block {
    
        let verified_tx = STF::verify_pending_tx(state);
        
        let mut naive_header = BlockHeader {
            nonce: 0,
            timestamp: time::now().to_timespec().sec as i32,
            block_number: state.history.last().unwrap().data.header.block_number + 1,
            previous_block_hash: Hash::hash(&state.history.last().unwrap().data.header.current_block_hash),
            current_block_hash: Hash::hash_tree(verified_tx.clone()),
        };
        
        let naive_data = BlockData {
            header: naive_header,
            transactions: verified_tx, 
        };
        
        let (data, proof) = STF::proof(state, naive_data);
        let block = Block {
            proof: proof,
            data: data,
        };
        
        block
    }
    
    // function to transition the state
    pub fn check_block(state: &State,
                       block: &mut Block) -> bool {
        
        // proof to check
        let submitted_proof = &block.proof;
        
        // check proof difficulty is achieved
        let mut count = 0;
        for i in submitted_proof.chars() {
            if i == '0' {
                count += 1;
            }
        }
        if !(count > state.stf.difficulty) {
            println!("ERROR: block proof does not meet difficulty requirements.");
            return false
        }
        
        // check proof matches block
        let hash_check = Hash::hash(&block.data);
        if &hash_check != submitted_proof {
            println!("\nPoW Error: Invalid PoW Hash.");
            return false
        }
        
        // if tests are passed, return true
        true
    }
    
}


#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Account {
    balance: i32,
    nonce: i32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TxData {
    sender: i32,
    sender_nonce: i32,
    amount: i32,
    receiver: i32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TX {
    data: TxData,
    signature: Vec<i32>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct BlockHeader {
    nonce: i32,
    timestamp: i32,
    block_number: i32,
    previous_block_hash: String,  
    current_block_hash: String,  
}

#[derive(Debug, Clone, PartialEq)]
pub struct BlockData {
    header: BlockHeader,
    transactions: Vec<TX>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Block {
    proof: String,
    data: BlockData,
}

// TODO
// - does it make sense to add more data to the State?
//   STF (type, difficulty, etc...)
//   KEY_PARAMS (type, p, q, modulo, etc..)
//   or maybe CRYPTO (KEY_PARAMS, hash function, hash tree function, etc...)
#[derive(Debug)]
pub struct State {
    keys: Keys,
    stf: STF,
    accounts: HashMap<i32, Account>,
    pending_tx: Vec<TX>,
    history: Vec<Block>,
}

impl State {

    // Create a new state
    pub fn create_state() -> State {
        
        let rsa_params = Keys {
            min: 0,
            max: 1000000,
            p: 61,
            q: 53,
            modulo: 3233,
            ctf_pq: 780,
        };

        let stf_data = STF {
            version: String::from("PoW"),
            difficulty: 5,
            max: 1000000,
        };

        let genesis_block = Block {
                proof: String::from("GENESIS BLOCK"),
                data: BlockData {
                    header: BlockHeader {
                        nonce: 0,
                        timestamp: time::now().to_timespec().sec as i32,
                        block_number: 0,
                        previous_block_hash: String::from("N/A"),  
                        current_block_hash: Hash::hash(&String::from("")),  
                    },
                    transactions: Vec::new(),
                }
            };
        
        let new_state = State {
            keys: rsa_params,
            stf: stf_data,
            accounts: HashMap::new(),
            pending_tx: Vec::new(),
            history: vec![genesis_block],
        };
        
        new_state
    }

    // Create a new account
    pub fn create_account(&mut self) {
        
        // TODO
        // - How can I make Keys::generator_keypair() not
        //   take in anything as input and have all the params
        //   stored within the Keys library?
        let (priv_key, pub_key) = Keys::generate_keypair(self.keys);
        let new_account = Account {
            balance: 0,
            nonce: 0,
        };
        
        if self.accounts.contains_key(&pub_key) {
            println!("Bummer... account collision.");
            return
        }
        
        self.accounts.insert(pub_key, new_account);
        //println!("\nThis is your public key: {:#?}", &pub_key);
        //println!("This is your private key: {:#?}", &priv_key);
        //println!("This is your account: {:#?}", self.accounts.get(&pub_key).unwrap());
    }
    
    // Create a new TX
    pub fn create_tx(&mut self,
                     sender_pub_key: i32,
                     sender_priv_key: i32,
                     receiver_pub_key: i32,
                     amount: i32) {
        
        
        let data = TxData {
            sender: sender_pub_key,
            sender_nonce: self.accounts.get(&sender_pub_key).unwrap().nonce,
            receiver: receiver_pub_key,
            amount: amount,
        };
        
        let signature = Keys::sign(self.keys, &data, sender_priv_key);
        
        let tx = TX {
            data: data,
            signature: signature,
        };
        
        self.pending_tx.push(tx);
    }

    // function to transition the state to a new state
    pub fn create_new_state(&mut self) {
        
        // check tx and put valid ones into a block
        let mut block = STF::create_block(self);
        
        // check that the block proof is valid
        if !(STF::check_block(&self, &mut block)) {
            println!("\nERROR: block not valid.");
            return
        }
        
        // transition the state by incorporating the
        // information in the new block
        for i in &block.data.transactions {
            self.accounts.get_mut(&i.data.sender).unwrap().balance -= i.data.amount;
            self.accounts.get_mut(&i.data.receiver).unwrap().balance += i.data.amount;
            self.accounts.get_mut(&i.data.sender).unwrap().nonce += 1;
        }
        
        // add the block to the history
        self.history.push(block);
    }
}



fn main() {
    
    // Init "blockchain"
    let mut blockchain = State::create_state();
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Create random accounts
    for _i in 0..3 {
        blockchain.create_account();
    }
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Manually create testing account 0
    let acc_0_pub_key = 773;
    let acc_0_priv_key = 557;
    let acc_0 = Account {
        balance: 10000,
        nonce: 0,
    };
    blockchain.accounts.insert(acc_0_pub_key.clone(), acc_0);
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Manually create testing account 1
    let acc_1_pub_key = 179;
    let acc_1_priv_key = 719;
    let acc_1 = Account {
        balance: 10000,
        nonce: 0,        
    };
    blockchain.accounts.insert(acc_1_pub_key.clone(), acc_1);
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // test a tx
    blockchain.create_tx(acc_0_pub_key,
                        acc_0_priv_key,
                        acc_1_pub_key,
                        50);
    //println!("blockchain:\n{:#?}", blockchain);
    
    // process the tx
    blockchain.create_new_state();
    println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
}

Want even more?

Cryptoeconomics.Study

  • https://cryptoeconomics.study/overview.html
  • https://cryptoeconomics.study/lectures/
  • https://github.com/cryptoeconomics-study/code/tree/master/c3_ProofOfWork

Rust Blockchain Tutorial

  • https://steemit.com/technology/@tensor/rust-project-cli-toy-blockchain
  • https://github.com/tensor-programming/Rust_block_chain

Minimal Viable Proof of Stake "Blockchain"

Because you can't eat money.


Words

In this chapter we'll transition our PoW "blockchain" towards a PoS "blockchain". This has many benefits:

  • we don't consume physical resources for digital security
  • we can create more compelling incentive mechanisms
  • we can destroy an attacker's ability to make repeated attacks

The future is near. Let's get started!



State

Are we there yet?




Words




Videos




// code




Resources To Learn More

  • TBD




State Transitions

Marching forward.


Words

TBD








Core Concepts

  • PoW => PoS
  • adding in support for uncles, ommers, and mixhash in addition to nonces (https://ethereum.stackexchange.com/questions/5833/why-do-we-need-both-nonce-and-mixhash-values-in-a-block)

Uncle: See Ommer, the gender-neutral alternative to aunt/uncle.

Ommer: a child of a parent of a parent of a block that is not the parent, or more generally a child of an ancestor that is not itself an ancestor. If A is an ommer of B, B is a nibling (niece/nephew) of A.

Uncle inclusion mechanism: Ethereum has a mechanism where a block may include its uncles; this ensures that miners that create blocks that do not quite get included into the main chain can still get rewarded.


Videos


Code


# #![allow(unused_variables)]

#fn main() {
#}


Resources

https://github.com/ethereum/wiki/wiki/Glossary#casper-and-scaling-research


PoS "Blockchain"

You get a stake! You get a stake! You get a Stake!


Words

Core Concepts

  • RIP the Lorax: save the trees
  • PoW is doomed to specialized ASIIC domination whereas PoS allows for more democratized access to blockchain security
  • PoS also allows you to create much more interesting and compelling incentivization mechanisms such as slashing and correlated slashing whereas with PoW the attacker doesn't ever lose their compute power and they can launch multiple attacks at little to no increased cost
  • PoS also lowers the barriers to entry of becomming a validator and contributing to the network :)


Videos


Code

Permalink to the Rust Playground

  • https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6df734fdee7fac411d9378f17f7978ca
extern crate rand;
use rand::prelude::*;

use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

/* GOAL
A modular architecture where you can change any of the modules,
say changing PoW to PoS, and it still runs.
*/

/* QUESTIONS
Why does this panic?
*/


pub struct DataEncoding;

impl DataEncoding {
    
    // TODO
    //
    // - Upgrade to something like what Substrate uses
    //   https://github.com/paritytech/substrate/tree/master/core/serializer
    // - Also, does it need it's own struct/impl or does it
    //   make sense to have it in the State impl?
    //
    // Turn stuff into an &[u8] slice
    pub unsafe fn to_u8<T: Sized>(p: &T) -> &[u8] {
        ::std::slice::from_raw_parts(
            (p as *const T) as *const u8,
            ::std::mem::size_of::<T>(),
        )
    }    

    // i32 -> String
    // https://doc.rust-lang.org/nightly/std/string/trait.ToString.html
    pub fn i2s(input: i32) -> String {
        
        let output = input.to_string();
        
        output
    }
    
    // String -> i32
    // https://stackoverflow.com/questions/27043268/convert-a-string-to-int-in-rust
    pub fn s2i(input: String) -> i32 {
        
        let output = input.parse::<i32>().unwrap();
        
        output
    }

    // string -> Vec<i32>
    pub fn s2v(input: String) -> Vec<i32> {
        
        let output: Vec<i32> = input.as_bytes()
                                    .iter()
                                    .map(|x| *x as i32)
                                    .collect();
        
        output
    }
 
    // Vec<i32> -> String
    // https://doc.rust-lang.org/nightly/std/string/trait.ToString.html
    pub fn v2s(input: Vec<i32>) -> String {
        
        let mut output_vec = Vec::new();
        for i in input {
            output_vec.push(i.to_string())
        }
        let output_string = output_vec.join("");
        
        output_string
    }
}


pub struct Hash;

impl Hash {
    
    // Takes a preimage ("preimage" = fancy word for input to a hash function)
    // Encodes it via the data_encode() function
    // Hashes that data into a hex or an integer (you choose)
    fn hash<T>(preimage: &T) -> String {
        
        // convert to u8
        let stuff_as_u8 = unsafe {
            DataEncoding::to_u8(preimage)
        };
        
        // hash u8 to u64
        let mut hasher = DefaultHasher::new();
        hasher.write(stuff_as_u8);
        
        // format u64 hash as String
        let string_digest = format!("{}", hasher.finish());
        string_digest
        
        // hex String
        //let digest = hasher.finish();
        //let hex_digest = format!("{:#X}", digest);
        //hex_digest
        
        // i32
        //let digest = hasher.finish() as i32;
        //digest 
        
        // f64
        //let digest = hasher.finish() as f64;
        //digest 
     
        // u64
        //let digest = hasher.finish();
        //digest
    }   
    
    // Create A Merkle Tree Of All TX In A Vec
    pub fn hash_tree<T>(stuff: Vec<T>) -> String {
        
        let mut v = Vec::new();

        for i in &stuff {
            let hashed = Hash::hash(&i);
            v.push(hashed);
        }

        if v.len() % 2 == 1 {
            let last = v.last().cloned().unwrap();
            v.push(last);
        }

        while v.len() > 1 {
            let mut h1 = v.remove(0);
            let mut h2 = v.remove(0);
            h1.push_str(&mut h2);
            let nh = Hash::hash(&h1);
            v.push(nh);
        }
        
        v.pop().unwrap()
    }
    
}


// This struct holds all the data for the key generation
// and signing. If you want to use a different key
// protocol, change the data in the Keys struct as well
// as the functions in the Keys impl
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Keys {
    min: i32,
    max: i32,
    p: i32,
    q: i32,
    modulo: i32,
    ctf_pq: i32, 
}

/// "RSA" Key Generation and Signing ///
impl Keys {
    
    // These functionsare not needed as we have hard coded
    // the modulo and ctf_pq values
    /*
    // greatest common divisor
    pub fn gcd(a: i32,
               b: i32) -> i32 {
        
        let (mut a, mut b) = if a > b {
            (a, b)
        } else {
            (b, a)
        };
    
        while b != 0 {
            let r = a % b;
            a = b;
            b = r;
        }
    
        a
    }
    
    // lowest common multiple
    pub fn lcm(a: i32,
               b: i32) -> i32 {
        
        let lcm = (a * b) / Keys::gcd(a, b);
        
        lcm
    }
    
    // Carmichael's totient function
    pub fn ctf(a: i32,
               b: i32) -> i32 {
        
        Keys::lcm(a - 1, b - 1)
    }
    */
    
    // slowly check if a number is prime
    pub fn slow_prime_check(self,
                            num: i32) -> bool {
        
        if num < self.min {
            println!("number must be greater than {}", self.min);
        }
        
        if num > self.max {
            println!("number cannot be greater than {}", self.max);
        }
        
        for i in 2..num{
            if num % i == 0 {
                return false
            }
        }
        
        true
    }

    // slowly, yet randomly, generate a prime number within a range
    pub fn prime_gen(self) -> i32 {
        
        for _i in 0..self.max {
            let p = thread_rng().gen_range(self.min, self.max);
            if Keys::slow_prime_check(self, p) {
                return p
            }
        }
        
        0
    }

    // generate a private key within a range
    pub fn priv_key_gen(self) -> i32 {
        
        let priv_key = Keys::prime_gen(self);
        assert!(self.max % priv_key != 0);
        
        priv_key
    }
    
    // slowly find the modular multiplicative inverse of a prime 
    pub fn slow_mmi(self,
                    priv_key: i32)-> i32 {
        
        for i in 2..self.max {
            if (i * priv_key) % self.ctf_pq == 1 {
                return i
            }
        }
        println!("Try larger search?");
        
        0
    }
    
    // create a public key from a pricate key and RSA param data
    pub fn pub_key_gen(self,
                       priv_key: i32) -> i32 {
        
        let pub_key = Keys::slow_mmi(self, priv_key);
        
        pub_key
    }
    
    // generate a private/public key pair
    pub fn generate_keypair(self) -> (i32, i32){
        let priv_key = Keys::priv_key_gen(self);
        let pub_key = Keys::pub_key_gen(self, priv_key);
        (priv_key, pub_key)
    }
    
    // Because... Rust.
    pub fn exp_mod(self,
                   input: i32,
                   power: i32) -> i32 {
        
        let mut out = (input * input) % self.modulo;
        // because the first iter of out took 2 off the base
        for _i in 0..power-2 {
            out = (out * input) % self.modulo;
        }
        
        out
    }
    
    // Sign a TX with a toy RSA function
    pub fn sign<T>(self,
                   thing_to_be_signed: &T,
                   signing_key: i32) -> Vec<i32> {
        
        let hashed_thing = Hash::hash(thing_to_be_signed);
        
        let mut hashed_thing_vec = Vec::new();
        for i in hashed_thing.chars() {
            hashed_thing_vec.push(i.to_string().parse::<i32>().unwrap())
        }
        
        let mut signed_vec = Vec::new();
        for i in hashed_thing_vec {
            signed_vec.push(Keys::exp_mod(self, i, signing_key,));
        }

        signed_vec
    }
    
    // Check signature on a TX
    pub fn check_tx_signature(self,
                              tx: TX) -> bool {
        
        let tx_sig_check: Vec<i32> = tx.clone().signature;
        
        let mut tx_sig_check_pub_signed = Vec::new();
        for i in tx_sig_check {
            tx_sig_check_pub_signed.push(Keys::exp_mod(self, i, tx.data.sender))
        }
        
        let mut tx_sig_check_string = String::new();
        for i in tx_sig_check_pub_signed {
            tx_sig_check_string.push_str(&i.to_string())
        }
        
        let hashed_tx = Hash::hash(&tx.data);
        
        if tx_sig_check_string == hashed_tx {
            return true
        } else {
            return false
        }
    }
}


// This struct holds all the data needed for 
// the chosen state transition protocol.
// In this case we're doign PoS, but if you
// wanted to impliment PoW you would write a new
// STF struct and new verify_pending_tx and proof
// functions.
#[derive(Debug, Clone)]
pub struct STF {
    version: String, // PoA, PoW, PoS, etc...
    difficulty: i32,
    validator: i32,
}

impl STF {
    
    // Standin for a "random beacon"
    pub fn random_validator_selection(state: &mut State) {
        
        // kick out any validators who don't meet the 
        // difficulty requirements
        // TODO: find a more "rust like" way to do this
        // without clone()
        let state_copy = state.clone();
        let mut count = 0;
        for i in state_copy.validators {
            if state.accounts.get(&i).unwrap().balance < state.stf.difficulty {
                state.validators.remove(count as usize);
            }
            count += 1;
        }
        
        // check that there are validators
        if state.validators.len() <= 0 {
            println!("ERROR: no known validators.");
            return
        }
        
        // randomly select a validator from the validator Vec
        let validator_num = thread_rng().gen_range(0, state.validators.len());
        state.stf.validator = state.validators[validator_num];
    }
    
    // This function encodes the rules of what qualifies as a "valid tx"
    pub fn verify_pending_tx(state: &mut State) -> Vec<TX> {
        
        let mut verified_tx = Vec::new();
        
        for i in &state.pending_tx {
        
            if !(state.accounts.contains_key(&i.data.sender)) {
                println!("Invalid TX: sender not found.");
                continue
            }
            
            if !(state.accounts.contains_key(&i.data.receiver)) {
                println!("Invalid TX: receiver not found.");
                continue
            }
            
            if !(i.data.amount > 0) {
                println!("Invalid TX: negative amount error.");
                println!("{} cannot send {} to {}", i.data.sender, i.data.amount, i.data.receiver);
                continue
            }
            
            if !(state.accounts.get(&i.data.sender).unwrap().balance > i.data.amount) {
                println!("Invalid TX: insufficient funds.");
                println!("{} cannot send {} to {}", i.data.sender, i.data.amount, i.data.receiver);
                continue         
            }
            
            if !(i.data.sender_nonce == state.accounts.get(&i.data.sender).unwrap().nonce) {
                println!("Invalid TX: potential replay tx.");
                println!("{} has nonce {}, but submitted a tx with nonce {}", i.data.sender, state.accounts.get(&i.data.sender).unwrap().nonce, i.data.sender_nonce);
                continue
            }
            
            if !(Keys::check_tx_signature(state.keys, i.clone())) {
                println!("Invalid TX: signature check failed");
                continue
            }
            
            verified_tx.push(i.clone());
        }
        
        verified_tx
    }

    // This function creates a proof that authorizes the state transition
    // This can be as complex as desired such as in a PoW setting 
    // Or it can simply hash the publicly announced validator address like
    // it does here :)
    pub fn proof(state: &State) -> String {
    
        let hash = Hash::hash(&state.stf.validator);
        
        hash
    }
    
    // Create A New Block With Valid Transactions
    pub fn create_block(state: &mut State) -> Block {
    
        let verified_tx = STF::verify_pending_tx(state);
        let header = BlockHeader {
            nonce: 0,
            timestamp: time::now().to_timespec().sec as i32,
            block_number: state.history.last().unwrap().data.header.block_number + 1,
            previous_block_hash: Hash::hash(&state.history.last().unwrap().data.header.current_block_hash),
            current_block_hash: Hash::hash_tree(verified_tx.clone()),
        };
        
        let data = BlockData {
            header: header,
            transactions: verified_tx, 
        };
        let proof = STF::proof(state);
        
        let block = Block {
            proof: proof,
            data: data,
        };
        
        block
    }
    
    // function to transition the state
    pub fn check_block(state: &State,
                       block: &Block) -> bool {
        
        // proof to check
        let submitted_proof = &block.proof;
        
        // check that validator matches randomly chosen STF validator
        if submitted_proof != &Hash::hash(&state.stf.validator) {
            println!("\nProof Error: invalid PoS validator.");
            return false
        }
        
        // check validator account has enough state
        let validator_balance = state.accounts.get(&state.stf.validator).unwrap().balance;
        if !(validator_balance > state.stf.difficulty) {
            println!("ERROR: block proof does not meet difficulty requirements.");
            return false
        }
        
        // if tests are passed, return true
        true
    }
    
}


#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Account {
    balance: i32,
    nonce: i32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TxData {
    sender: i32,
    sender_nonce: i32,
    amount: i32,
    receiver: i32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TX {
    data: TxData,
    signature: Vec<i32>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct BlockHeader {
    nonce: i32,
    timestamp: i32,
    block_number: i32,
    previous_block_hash: String,  
    current_block_hash: String,  
}

#[derive(Debug, Clone, PartialEq)]
pub struct BlockData {
    header: BlockHeader,
    transactions: Vec<TX>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Block {
    proof: String,
    data: BlockData,
}

// TODO
// - does it make sense to add more data to the State?
//   STF (type, difficulty, etc...)
//   KEY_PARAMS (type, p, q, modulo, etc..)
//   or maybe CRYPTO (KEY_PARAMS, hash function, hash tree function, etc...)
#[derive(Debug, Clone)]
pub struct State {
    keys: Keys,
    stf: STF,
    accounts: HashMap<i32, Account>,
    validators: Vec<i32>,
    pending_tx: Vec<TX>,
    history: Vec<Block>,
}

impl State {

    // Create a new state
    pub fn create_state() -> State {
        
        let rsa_params = Keys {
            min: 0,
            max: 1000000,
            p: 61,
            q: 53,
            modulo: 3233,
            ctf_pq: 780,
        };

        let stf_data = STF {
            version: String::from("PoS"),
            difficulty: 100,
            validator: 0,
        };

        let genesis_block = Block {
                proof: String::from("GENESIS BLOCK"),
                data: BlockData {
                    header: BlockHeader {
                        nonce: 0,
                        timestamp: time::now().to_timespec().sec as i32,
                        block_number: 0,
                        previous_block_hash: String::from("N/A"),  
                        current_block_hash: Hash::hash(&String::from("")),  
                    },
                    transactions: Vec::new(),
                }
            };
        
        let new_state = State {
            keys: rsa_params,
            stf: stf_data,
            accounts: HashMap::new(),
            validators: Vec::new(),
            pending_tx: Vec::new(),
            history: vec![genesis_block],
        };
        
        new_state
    }

    // Create a new account
    pub fn create_account(&mut self) {
        
        // TODO
        // - How can I make Keys::generator_keypair() not
        //   take in anything as input and have all the params
        //   stored within the Keys library?
        let (priv_key, pub_key) = Keys::generate_keypair(self.keys);
        let new_account = Account {
            balance: 0,
            nonce: 0,
        };
        
        if self.accounts.contains_key(&pub_key) {
            println!("Bummer... account collision.");
            return
        }
        
        self.accounts.insert(pub_key, new_account);
        //println!("\nThis is your public key: {:#?}", &pub_key);
        //println!("This is your private key: {:#?}", &priv_key);
        //println!("This is your account: {:#?}", self.accounts.get(&pub_key).unwrap());
    }
    
    // Create a new TX
    pub fn create_tx(&mut self,
                     sender_pub_key: i32,
                     sender_priv_key: i32,
                     receiver_pub_key: i32,
                     amount: i32) {
        
        
        let data = TxData {
            sender: sender_pub_key,
            sender_nonce: self.accounts.get(&sender_pub_key).unwrap().nonce,
            receiver: receiver_pub_key,
            amount: amount,
        };
        
        let signature = Keys::sign(self.keys, &data, sender_priv_key);
        
        let tx = TX {
            data: data,
            signature: signature,
        };
        
        self.pending_tx.push(tx);
    }
    
    // function to add an account to the validator Vec
    pub fn create_validator(&mut self,
                                account: i32) {
        
        self.validators.push(account);
    }
    
    // function to transition the state to a new state
    pub fn create_new_state(&mut self) {
        
        // "publicly" select a random validator
        STF::random_validator_selection(self);
        
        // check tx and put valid ones into a block
        let mut block = STF::create_block(self);
        
        // check that the block proof is valid
        if !(STF::check_block(&self, &block)) {
            // if block is not valid slash validator's funds
            println!("\nERROR: block not valid.");
            self.accounts.get_mut(&self.stf.validator).unwrap().balance -= self.stf.difficulty;
            return
        }
        
        // if block is valid add reward to validator's balance
        self.accounts.get_mut(&self.stf.validator).unwrap().balance += self.stf.difficulty;
        
        // transition the state by incorporating the
        // information in the new block
        for i in &block.data.transactions {
            self.accounts.get_mut(&i.data.sender).unwrap().balance -= i.data.amount;
            self.accounts.get_mut(&i.data.receiver).unwrap().balance += i.data.amount;
            self.accounts.get_mut(&i.data.sender).unwrap().nonce += 1;
        }
        
        // add the block to the history
        self.history.push(block);
    }
}



fn main() {
    
    // Init "blockchain"
    let mut blockchain = State::create_state();
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Create random accounts
    for _i in 0..3 {
        blockchain.create_account();
    }
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Manually create testing account 0
    let acc_0_pub_key = 773;
    let acc_0_priv_key = 557;
    let acc_0 = Account {
        balance: 10000,
        nonce: 0,
    };
    blockchain.accounts.insert(acc_0_pub_key.clone(), acc_0);
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // Manually create testing account 1
    let acc_1_pub_key = 179;
    let acc_1_priv_key = 719;
    let acc_1 = Account {
        balance: 10000,
        nonce: 0,        
    };
    blockchain.accounts.insert(acc_1_pub_key.clone(), acc_1);
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // add testing account 0 and 1 to the validator pool
    blockchain.create_validator(acc_0_pub_key);
    blockchain.create_validator(acc_1_pub_key);
    //println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
    
    // test a tx
    blockchain.create_tx(acc_0_pub_key,
                        acc_0_priv_key,
                        acc_1_pub_key,
                        50);
    //println!("blockchain:\n{:#?}", blockchain);
    
    // process the tx
    blockchain.create_new_state();
    println!("\nBLOCKCHAIN:\n{:#?}", blockchain);
}



Resources

Ethereum PoS FAQ

  • https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQs


Earth

A compendium of the resources presented in this book and more :)



Knowledge Is Power

So let's learn some stuff!




Cryptoeconomics

  • Cryptoeconomic Primitives and Staking: carrots, sticks, and attack vectors for PoS: https://www.zeroknowledge.fm/49
  • Chat with Richard Craib from Numerai on using staking in applications to prevent sybil attacks and filter signal from noise: https://www.zeroknowledge.fm/47
  • Awesome Cryptoeconomics jpantunes: https://github.com/jpantunes/awesome-cryptoeconomics
  • Awesome Cryptoeconomics L4ventures: https://github.com/L4ventures/awesome-cryptoeconomics
  • https://www.reddit.com/r/cryptoeconomics/
  • https://theethereum.wiki/w/index.php/Cryptoeconomics

Cryptoeconomics.Study

  • Overview: https://cryptoeconomics.study/overview.html
  • Lectures: https://cryptoeconomics.study/lectures/
  • Code: https://github.com/cryptoeconomics-study/code
  • Forum: http://forum.cryptoeconomics.study

Crypto

  • Awesome Crypto: https://github.com/sobolevn/awesome-cryptography
  • Reddit Crypto Community: https://www.reddit.com/r/crypto

General Blockchain Resources

  • https://en.wikipedia.org/wiki/Blockchain
  • https://github.com/reiver/blockchain-reading-list
  • https://a16z.com/2018/02/10/crypto-readings-resources/
  • https://github.com/tensor-programming/Rust_block_chain
  • https://steemit.com/technology/@tensor/rust-project-cli-toy-blockchain

Bitcoin

  • Bitcoin Whitepaper: https://bitcoin.org/bitcoin.pdf
  • Mastering Bitcoin: https://github.com/bitcoinbook/bitcoinbook
  • Yuge compendium of resources: https://lopp.net/bitcoin.html
  • M Nielsen explains Bitcoin: http://www.michaelnielsen.org/ddi/how-the-bitcoin-protocol-actually-works/
  • Awesome Bitcoin: https://github.com/igorbarinov/awesome-bitcoin
  • Wikipedia: https://en.wikipedia.org/wiki/Bitcoin

Mimblewimble

  • Mimblewimble Whitepaper: https://scalingbitcoin.org/papers/mimblewimble.txt
  • Mimblewimble Paper Update via Andrew Poelstra: https://scalingbitcoin.org/papers/mimblewimble.pdf
  • Grin: https://grin-tech.org/

Ethereum

  • white: https://github.com/ethereum/wiki/wiki/White-Paper#ethereum
  • beige: https://github.com/chronaeon/beigepaper/blob/master/beigepaper.pdf
  • yellow: https://ethereum.github.io/yellowpaper/paper.pdf
  • Mastering Ethereum: https://github.com/ethereumbook
  • Ethereum Research: https://ethresear.ch
  • Ethereum Improvement Research: https://ethereum-magicians.org/
  • Reddit: https://www.reddit.com/r/ethereum/
  • Awesome Ethereum: https://github.com/btomashvili/awesome-ethereum
  • Wikipdedia: https://en.wikipedia.org/wiki/Ethereum

Substrate / Polkadot

  • Polkadot Whitepaper: https://polkadot.network/PolkaDotPaper.pdf
  • Substrate Docs: https://substrate.readme.io/

Podcasts

  • https://www.zeroknowledge.fm/

Other

  • Awesome Awesome Lists: https://github.com/sindresorhus/awesome

Let's make things better by making better things.




Cryptoeconomic Security / Game Theory Stuff

  • https://ethresear.ch/c/economics
  • http://tokenengineering.net/building-blocks

Rust Dev

  • all the Rust docs and books! https://doc.rust-lang.org/
  • all the Rust Crates! https://crates.io/
  • this book is built with mdBook

Rust Crypto / Blockchain Stuff

  • Parity's Ethereum Client: https://github.com/paritytech/parity-ethereum
  • Parity Substrate: https://www.parity.io/substrate/
  • Parity Polkadot: https://github.com/paritytech/polkadot
  • Parity WASM: https://github.com/paritytech/parity-wasm
  • POA's Honey Badger Byzantine Fault Tolerant (BFT) consensus algorithm: https://github.com/poanetwork/hbbft
  • POA's Threshold Crypto: https://github.com/poanetwork/threshold_crypto
  • Grin: https://github.com/mimblewimble/grin
  • Zcash: https://z.cash/blog/bellman-zksnarks-in-rust/
  • Bellman zk-SNARKS in Rust: https://z.cash/blog/bellman-zksnarks-in-rust/
  • Zero-knowledge Cryptography in Rust: https://github.com/zkcrypto
  • Zokrates: A toolbox for zkSNARKS on Ethereum
    • https://github.com/Zokrates/ZoKrates
  • Dalek Crypto: https://dalek.rs
  • (WIP) Libp2p: https://github.com/libp2p/rust-libp2p
  • and these:
    • https://github.com/rust-unofficial/awesome-rust#cryptocurrencies
    • https://github.com/rust-unofficial/awesome-rust#cryptography

Ethereum Core Dev

  • TBD

General Ethereum Dev

  • https://blog.0xproject.com/new-ethereum-dev-tools-from-0x-db80ee9e802
  • https://github.com/trailofbits/awesome-ethereum-security
  • https://github.com/ConsenSys/ethereum-developer-tools-list
  • https://media.consensys.net/everything-you-possibly-need-to-develop-on-ethereum-1bef0c23c7c6

Ethereum Security Stuff

  • MythX:
    • https://mythx.io/
  • SECURIFY:
    • scanner for Ethereum smart contracts: https://securify.chainsecurity.com/
  • Trail Of Bits:
    • https://www.trailofbits.com/
    • https://github.com/trailofbits
    • Manticore: https://github.com/trailofbits/manticore
    • Echidna: https://github.com/trailofbits/echidna
    • Awesome Ethereum Security: https://github.com/trailofbits/awesome-ethereum-security
  • Sigma Prime:
    • https://sigmaprime.io/
  • formal verification?
  • this stuff: https://slideslive.com/38911605/smart-contract-security-incentives-beyond-the-launch

Ethereum L2 Dev

  • plasma
  • state channels
  • other?




Need some inspiration on what to build?

  • Ethteruem Research Applications: https://ethresear.ch/c/applications
  • ETHSingapore hackathon wishlist: https://youtu.be/egC2F_JKuhc?t=829

Yay people...

because sharing is caring

Reddit Communities

  • https://www.reddit.com/r/crypto
  • https://www.reddit.com/r/Bitcoin/
  • https://www.reddit.com/r/ethereum/
  • https://www.reddit.com/r/cryptoeconomics/

Cryptoeconomics.Study Forum

  • http://forum.cryptoeconomics.study

Ethereum Research Forum

  • https://ethresear.ch





I sincerely hoped you enjoyed this book and got some value out of it. That being said, it's a WIP and needs to get better. What did you like? What sucked? Please let me know!

  • rustycryptoeconomics@protonmail.com

Thanks!





The best things in life are free, but for everthing else...




I hope you liked this book/tutorial and got some value out of it! I'll keep building learning tools and resources as long as I can, but it takes a lot of work and I'm doing it for free. If you feel so inspired please consider donating to support the ongoing maintenance and development of this and future books :)

Bitcoin:

  • bc1qarnsad7ulp8gnh6v5r9n5qj6rvqx4m7ap4d9zc

Bitcoin Cash:

  • qqsvc8x8qd8g02pqtqwf5kpquuxk4vy6vgt8c24k8c

Litecoin:

  • ltc1qxyxjuszc765e8eszr8yj0v5cgnd9rf9ujjh6yy

Doge:

  • DQ4eMuxEQi49GBeRNGEYXpaLLhftvnaC36

Ethereum:

  • 0xAB30757feDDc162C788d748f6F89AbeC4bB78cAD

Ethereum Classic:

  • 0x0e2140b372Db8D0a042F87e24C0779eA9A8F559E

Monero:

  • 46TtrtJ2Vef89npxpGSaLBBTKzuBmqAAhSj8Y7aEpD6qcSaHMtSGY6w2cn4LkCicZGCCgaXAqL5YxQmKbMSME87RDHaf5Qz

Zcash:

  • t1Ri2Pb63Ajo8avbcqMQBUPh9z2cdpeQytw

PIVX:

  • D9VEfQi9XynPxwPGYRspG5Yg44m2o3cYPo

and of courrse if you'd like to donate with a token that is not represented here just let me know and I'll add it.




Also, if you know of a grant program that you think would be a good fit for this project, please reach out and let me know!

  • rustycryptoeconomics@protonmail.com