The blockchain-redux experiment continues. State is now shared and preserved.

When I last wrote about this blockchain-backed Redux clone in 105 lines of code, it could:

build a local blockchain

add blocks on every action dispatch

verify the chain is valid

replace the chain with a longer chain

All the basic building blocks. But it didn't quite have feature parity with the real redux, and it lacked some way to share the blockchain between nodes. You started a new chain with every call to createStore() .

That has now been fixed.

We reached Redux feature parity… I think. We added the ability to subscribe to changes and to use middleware.

blockchain-redux looks and feels the same as real Redux when you're using it, and I think that means it's got feature parity. In theory, you should be able to drop-in any redux plugin or middleware and it's gonna work.

The concept here is simple:

A method adds functions to an array of listeners Dispatch calls all of them

let listeners = [ ] ; function subscribe ( listener ) { listeners . push ( listener ) ; return function unsubscribe ( ) { listeners . splice ( listeners . indexOf ( listener ) , 1 ) ; } ; }

We keep a listeners array. It starts as empty. Calling subscribe , which is exposed as an API, adds your listener to the list. In return you get a function that lets you unsubscribe.

Using listeners.splice to remove functions like that comes straight from Redux source. It's probably faster than building a new list using .filter .

Adding middleware support was trickier.

Middleware, in essence, wraps the Redux store and changes how its functions work. You can think of it like a higher-order-component or a function decorator. Whichever makes more sense to you.

Redux itself makes this easier with an applyMiddleware function, but I didn't want to build that just yet. I don't think it's strictly necessary, but maybe that breaks feature parity. 🤔

First part is to add a 3rd argument to createStore – the enhancer .

function createStore ( reducer , preloadedState , enhancer ) { if ( typeof preloadedState === "function" && typeof enhancer === "undefined" ) { enhancer = preloadedState ; preloadedState = undefined ; } if ( typeof enhancer === "function" ) { return enhancer ( createStore ) ( reducer , preloadedState ) ; }

If enhancer is a function, we pass the createstore function to it and say, "Ok, you do this.”

The enhancer, a middleware, then takes care of instantiating the store and changing its behavior. A simple middleware that prints out every call to dispatch would look like this.

export default function ( ) { return ( createStore ) => ( ... args ) => { const store = createStore ( ... args ) ; const _dispatch = store . dispatch ; function dispatch ( action ) { _dispatch ( action ) ; console . log ( "ACTION" , action ) ; } return Object . assign ( store , { dispatch , } ) ; } ; }

We're using some functional concepts here to do our thing. Currying for the most part.

Our logging middleware returns a function that takes createStore as its argument. This in turn returns a function that takes arguments, usually reducer and preloadedState .

It immediately calls createState to build the store. This time without passing a middleware.

Then our redux middleware overwrites the dispatch function with its own function that prints actions as well as calls the original dispatch.

In the end, we return the augmented store.

I borrowed this approach from Redux's applyMiddleware function, and it works great. Where I think my approach might suffer is applying multiple middlewares.

But we can solve that problem when we get to it 😇

We went to all that trouble of supporting middleware so that we can build different strategies for sharing our blockchain. blockchain-redux itself should focus on building the blockchain, dispatching actions, and keeping state.

How that gets shared between nodes lives in a separate layer.

For now, that layer is Firebase. This gives us an easy backend to work against and make sure nodes are communicating. Great way to iron out kinks, but it makes our blockchain centralized.

We'll solve the centralization problem next.

Here's how the Firebase middleware works 👇

Our middleware exports a function that takes an initialized FirebaseApp as its argument. This made it easier to have multiple instances of the store sharing the same Firebase when testing.

Just like before, the only function it augments is dispatch . It now tries to save new blocks to Firebase.

function dispatch ( action ) { _dispatch ( action ) ; const newBlock = store . getLastBlock ( ) ; return saveBlock ( newBlock ) ; }

Dispatch the action, get latest block from the store, save block to firebase. 👌

Saving that block is fraught with some difficulty, however.

function saveBlock ( block ) { block . _data = block . _data || { } ; return db . ref ( ` blockchain/ ${ block . index } ` ) . once ( "value" ) . then ( ( snapshot ) => { if ( ! snapshot . exists ( ) ) { db . ref ( ` blockchain/ ${ block . index } ` ) . set ( block ) ; return true ; } else { return false ; } } ) ; }

We save blocks at blockchain/<index> . On every save, we check if the blocks already exists. If it doesn’t, we save the block and everyone is happy.

However, if somebody else already created the same block successfully, we're in trouble. Right now, the middleware silently drops the save, and that's bad.

I think the easiest solution will be to sync with the Firebase blockchain before dispatching an action and making a new block. Or we could dispatch the action and run a save-dispatch-save cycle until it succeeds.

Neither of those approaches is very optimal from a network utilization perspective. Not sure yet what to do here.

Another interesting problem was initializing our store from the blockchain. How do you ensure you have the whole chain before building new blocks?

One approach I found was to read the whole thing from Firebase and replacing the local stub chain with the big one.

function initFromFirebase ( ) { return db . ref ( "blockchain" ) . orderByKey ( ) . once ( "value" ) . then ( ( snapshot ) => snapshot . val ( ) ) . then ( ( blockchain ) => { blockchain = Object . values ( blockchain ) . map ( ( block ) => { block . data = block . _data ? JSON . parse ( block . _data ) : { } ; return block ; } ) ; store . replaceChain ( blockchain ) ; return Object . assign ( store , { dispatch , } ) ; } ) ; } return Promise . all ( store . _blockchain . map ( saveBlock ) ) . then ( initFromFirebase ) ;

initFromFirebase reads our central blockchain from Firebase, goes through the blocks and makes sure data is in the right format, then runs replaceChain .

You'll notice this is now where we return the Redux store. That means createStore is asynchronous and wreaks havoc on just about everything we hold dear about using Redux.

But I'm not sure how to fix that. We need to have the chain before dispatching our first function. Maybe a waiting mechanism in dispatch 🤔

Oh, and that last part? That's so we create the blockchain stub on Firebase in case it doesn't exist yet. Goes through blocks in the default chain and makes sure they exist on Firebase.

This is still a rough experiment. The blockchain implementation works, it communicates through Firebase so it's distributed, but it has trouble with conflict resolution, has a centralized source of truth, and is very network and memory inefficient.

All of that is bad.

I want to move towards using Firebase as just a communication protocol between nodes so instead of saving the whole blockchain there, we just announce new blocks and other nodes pick them up. That would be more efficient.

I also want to figure out how to avoid saving the whole blockchain locally. But maybe that's fundamentally not possible 🤔

Got suggestions? Yell at me on Twitter.

Did you enjoy this article? 👎 👍

Published on January 4th, 2018 in Livecoding, 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 ❤️