Big fat disclaimer: This is Part 1 of an experiment. It is not yet secure, it does not yet share the blockchain between nodes, it still needs a proof-of-work or proof-of-stake algorithm.

With that out of the way, let us begin.

Redux is great because it does immutability right?



Now imagjne this: Blockchain backed Redux. Every state change gets recorded to the blockchain. Immutable forever. Pure as dew 👌 — Swizec Teller (@Swizec) December 8, 2017

Redux is great because it keeps a chain of immutable data forever. What if we backed that chain with a blockchain? What would happen? 🤔

So I fired up this amazing Medium post on building a blockchain in 200 lines of code and built a blockchain-backed Redux clone in about 2 hours.

Mine is just 105 lines tho 😛

You can see the code on Github. It's not quite useful yet. The blockchain doesn't get shared between nodes, and there's no proof-of-stake or proof-of-work algorithm.

That's all coming soon. First, I wanted to make sure I understood both blockchains and Redux enough to make this work.

Turns out blockchains are pretty simple. It's just a reversed linked list where every node holds some data, a hash, and points to the previous node.

Image borrowed from Lauri Hartikka's post

We use the hashes to verify our chain. Verification is important when we start sharing chains between nodes.

Our blocks are JavaScript objects.

class Block { constructor ( { previousBlock , data } ) { this . index = previousBlock . index + 1 ; this . previousHash = previousBlock . hash . toString ( ) ; this . timestamp = new Date ( ) . getTime ( ) / 1000 ; this . data = data ; this . hash = Block . calculateHash ( this ) ; } set data ( data ) { this . _data = JSON . stringify ( data ) ; } get data ( ) { return JSON . parse ( this . _data ) ; } static calculateHash ( block ) { return CryptoJS . SHA256 ( block . index + block . previousHash + block . timestamp + block . _data ) . toString ( ) ; } }

The constructor doubles as our block generator. It takes the previousBlock and some data , adds some metadata, and calculates a hash .

A data setter and getter help us JSONify data transparently. Users can pass simple objects as data, and the blockchain will handle it.

The calculateHash function right now just uses SHA256, but I think this is where a proof of work or a proof of stake algorithm would come into play in a real blockchain. Haven't figured that part out yet :)

Kind of surprisingly, that's all we need to build a blockchain. You could build it like this new Block({ previousBlock: new Block({ previousBlock: genesisBlock, data: {hai: "world"}}), data: {hai2: "world2"}})

But that's cumbersome, so let's add a Redux.

Redux, if you don't know, is a state management approach based on the functional concept of a reducer . At each change, we take the current state and a change descriptor, and produce the next state.

A tiny Redux implementation that maintains history would look something like this 👇

function createStore ( initialState , reducer ) { let state = [ initialState ] ; function getState ( ) { return state [ state . length - 1 ] ; } function dispatch ( action ) { state . push ( reducer ( getState ( ) , action ) ) ; } return { getState : getState , dispatch : dispatch , } ; }

👆 That's Redux in a nutshell. Unlike the real Redux, this one maintains history. We'll expand that into a blockchain.

getState returns the latest state and dispatch takes an action and calculates the new state using the reducer function.

Using this Redux to implement a counter looks like this 👇

const store = createStore ( { counter : 0 } , rootReducer ) ; function rootReducer ( state , action ) { switch ( action . type ) { case "inc" : return { counter : state . counter + 1 } ; case "dec" : return { counter : state . counter - 1 } ; default : return state ; } }

We can use this to verify that it works. Using tape in this case because LukeEd05 on the livestream suggested it.

test ( "count to 5" , ( t ) => { for ( let i = 0 ; i < 5 ; i ++ ) { store . dispatch ( { type : "inc" } ) ; } t . equal ( store . getState ( ) . counter , 5 ) ; t . end ( ) ; } ) ;

Works 👌

Now, how do we add the blockchain? 🤔

Well, instead of putting initialState directly into our state, we put a genesis Block with that state. And instead of pushing new state calculations directly, we add blocks.

function createStore ( initialState , reducer ) { let blockchain = [ new Block ( { previousBlock : { index : 0 , hash : "0" , timestamp : new Date ( ) . getTime ( ) , } , data : initialState , } ) , ] ; function getLastBlock ( ) { return blockchain [ blockchain . length - 1 ] ; } function dispatch ( action ) { const lastBlock = getLastBlock ( ) ; const nextData = reducer ( lastBlock . data , action ) ; addBlock ( new Block ( { previousBlock : lastBlock , data : nextData } ) ) ; } function addBlock ( newBlock ) { if ( isValidNewBlock ( newBlock , getLastBlock ( ) ) ) { blockchain . push ( newBlock ) ; } } function isValidNewBlock ( newBlock , previousBlock ) { if ( previousBlock . index + 1 !== newBlock . index ) { console . log ( "invalid index" ) ; return false ; } else if ( previousBlock . hash !== newBlock . previousHash ) { console . log ( "invalid previoushash" ) ; return false ; } else if ( Block . calculateHash ( newBlock ) !== newBlock . hash ) { console . log ( "invalid hash: " , Block . calculateHash ( newBlock ) , newBlock . hash ) ; return false ; } return true ; } function isValidChain ( blockchain ) { for ( let i = 0 ; i < blockchain . length - 1 ; i ++ ) { if ( ! isValidNewBlock ( blockchain [ i + 1 ] , blockchain [ i ] ) ) { return false ; } } return true ; } function replaceChain ( newBlocks ) { if ( isValidChain ( newBlocks ) && newBlocks . length > blockchain . length ) { blockchain = newBlocks ; } } return { getState : ( ) => getLastBlock ( ) . data , getLastBlock : getLastBlock , dispatch : dispatch , addBlock : addBlock , replaceChain : replaceChain , _blockchain : blockchain , } ; }

Okay that's plenty of code to spring on you. Let's go through it function by function.

function getLastBlock ( ) { return blockchain [ blockchain . length - 1 ] ; }

Takes last block from the blockchain and returns it. That's our current state. We export a helper getState that does getLastBlock().data to avoid changing external APIs.

function dispatch ( action ) { const lastBlock = getLastBlock ( ) ; const nextData = reducer ( lastBlock . data , action ) ; addBlock ( new Block ( { previousBlock : lastBlock , data : nextData } ) ) ; }

Same as dispatch before. Takes current state and builds new state with the reducer . Then it uses addBlock to insert a newly generated block into the chain.

function isValidNewBlock ( newBlock , previousBlock ) { if ( previousBlock . index + 1 !== newBlock . index ) { console . log ( "invalid index" ) ; return false ; } else if ( previousBlock . hash !== newBlock . previousHash ) { console . log ( "invalid previoushash" ) ; return false ; } else if ( Block . calculateHash ( newBlock ) !== newBlock . hash ) { console . log ( "invalid hash: " , Block . calculateHash ( newBlock ) , newBlock . hash ) ; return false ; } return true ; }

Every new block has to be validated. Especially because they can come from other nodes.

This checks that indexes and hashes all match up so we can avoid conflicts.

function isValidChain ( blockchain ) { for ( let i = 0 ; i < blockchain . length - 1 ; i ++ ) { if ( ! isValidNewBlock ( blockchain [ i + 1 ] , blockchain [ i ] ) ) { return false ; } } return true ; }

This will be used later on in conflict resolution. It goes through an entire proposed chain and validates every block.

function replaceChain ( newBlocks ) { if ( isValidChain ( newBlocks ) && newBlocks . length > blockchain . length ) { blockchain = newBlocks ; } }

Another part of conflict resolution and node communication is replacing the whole chain. If a new valid chain comes in that's longer than what we already have, we replace our internal state with the new chain.

This will be particularly useful when we boot up a new client and it needs to get the whole chain.

That's pretty much it. A naive blockchain implementation that gives you a Redux-like API to store and manipulate data.

Next I'm going to add communication between nodes, probably through Firebase, and a proof-of-stake algorithm. Then we can start building something interesting.

And maybe you now understand blockchains a little better 🤓

Did you enjoy this article? 👎 👍

Published on December 18th, 2017 in blockchain, Livecoding, redux, Technical

Learned something new?

Want to become a high value JavaScript expert? Here's how it works 👇 Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet 📖right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths. Start with an interactive cheatsheet 📖 Then get thoughtful letters 💌 on mindsets, tactics, and technical skills for your career. "Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌" ~ Ashish Kumar Your Name Your Email Your Address Subscribe & Become an expert 💌 Join over 10,000 engineers just like you already improving their JS careers with my letters, workshops, courses, and talks. ✌️

Have a burning question that you think I can answer? I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first 💰 on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️