Compare and contrast a public blockchain vs a private blockchain and discuss methods for cross-chain functionality.
Build from scratch a simple private blockchain that creates new blocks, store data in blocks, and secure blocks with a digital signature using a one-way hash.
To Build a Simple Private Blockchain, we’ll work through 6 different parts that, once completed, will have you up and running with the core ideas behind building a simple private blockchain.
Part 1: Block Data Model
Part 2: Create New Blocks
Part 3: Store Blocks
Part 4: Linking Blocks
Part 5: Block Height and Timestamp
Part 6: LevelDB to Persist Data
stores up to 40 bytes
#npm init
//Block data model
{
"hash": "",
"height": 0,
"body": [
],
"time": 0,
"previousblockhash": ""
}
#touch simpleChain.js
#node
class Block{
constructor(data){
//Block data model
this.hash = "",
this.height = 0,
this.body = data,
this.time = 0,
this.previousblockhash = ""
}
}
class Blockchain{
constructor(){
// new chain array
this.chain = [];
}
// addBlock method
addBlock(newBlock){
this.chain.push(newBlock);
}
}
> let blockchain = new Blockchain()
> blockchain.addBlocknode
(new Block('test data'))
> blockchain.chain
#npm install crypto-js -save
In simpleChain.js
,
/* ===== SHA256 with Crypto-js ===================================
| Learn more: Crypto-js: https://github.com/brix/crypto-js |
| =============================================================*/
const SHA256 = require('crypto-js/sha256');
/* ===== Block Class ===================================
| Class with a constructor for block data model |
| ====================================================*/
class Block {
constructor(data){
this.height = '';
this.timeStamp = '';
this.data = data;
this.previousHash = '0x';
this.hash = '';
}
}
/* ===== Blockchain ===================================
| Class with a constructor for blockchain data model |
| with functions to support: |
| - createGenesisBlock() |
| - getLatestBlock() |
| - addBlock() |
| - getBlock() |
| - validateBlock() |
| - validateChain() |
| ====================================================*/
class Blockchain{
constructor(){
// new chain array
this.chain = [];
// add first genesis block
this.addBlock(this.createGenesisBlock());
}
createGenesisBlock(){
return new Block("First block in the chain - Genesis block");
}
// addBlock method
addBlock(newBlock){
if (this.chain.length>0) {
// previous block hash
newBlock.previousHash = this.chain[this.chain.length-1].hash;
}
// SHA256 requires a string of data
newBlock.hash = SHA256(JSON.stringify(newBlock)).toString();
// add block to chain
this.chain.push(newBlock);
}
}
Testing:
> let blockchain = new Blockchain()
> blockchain.addBlock(new Block('test'))
> blockchain.chain
/* ===== SHA256 with Crypto-js ===================================
| Learn more: Crypto-js: https://github.com/brix/crypto-js |
| =============================================================*/
const SHA256 = require('crypto-js/sha256');
/* ===== Block Class ===================================
| Class with a constructor for block data model |
| ====================================================*/
class Block {
constructor(data){
this.height = '';
this.timeStamp = '';
this.data = data;
this.previousHash = '0x';
this.hash = '';
}
}
/* ===== Blockchain ===================================
| Class with a constructor for blockchain data model |
| with functions to support: |
| - createGenesisBlock() |
| - getLatestBlock() |
| - addBlock() |
| - getBlock() |
| - validateBlock() |
| - validateChain() |
| ====================================================*/
class Blockchain{
constructor(){
// new chain array
this.chain = [];
// add first genesis block
this.addBlock(this.createGenesisBlock());
}
createGenesisBlock(){
return new Block("First block in the chain - Genesis block");
}
// getLatest block method
getLatestBlock(){
return this.chain[this.chain.length -1];
}
// addBlock method
addBlock(newBlock){
// block height
newBlock.height = this.chain.length;
// UTC timestamp
newBlock.timeStamp = new Date().getTime().toString().slice(0,-3);
if (this.chain.length>0) {
// previous block hash
newBlock.previousHash = this.chain(this.chain.length-1.hash;
}
// SHA256 requires a string of data
newBlock.hash = SHA256(JSON.stringify(newBlock)).toString();
console.log(JSON.stringify(newBlock));
// add block to chain
this.chain.push(newBlock);
}
We modified the code to include comments for good practice ?
Then, we installed leveldb by running this command in the terminal:
npm install level --save
So far, you’ve created a simple blockchain. The problem with this model is that the data stored in it is not persisted (not very useful for a blockchain). To solve, we will need to find a simple storage method to help us store the data. We also shouldn’t use a centralized database because the purpose of the blockchain is to have decentralized data. A good solution would be to use LevelDB.
To prep you for integrating your private blockchain with LevelDB, I created the levelSandbox.js
to demonstrate specific functionality you will build upon later.
InlevelSandbox.js
we:
addLevelDBData()
which adds data to levelDB with a key/value pairgetLevelDBData()
which gets a value from a keyaddDataToLevelDB()
which adds a new blocktheLoop()
which tests leveldb using a self invoking function to add blocks to the chainCurrently levelSandbox.js
is not tied to our simpleChain.js
just yet, that’s what you’ll get to work on in the project.
/* ===== Persist data with LevelDB ===================================
| Learn more: level: https://github.com/Level/level |
| =============================================================*/
let level = require('level');
let chainDB = './chaindata';
let db = level(chainDB);
// Add data to levelDB with key/value pair
function addLevelDBData(key,value){
db.put(key, value, function(err) {
if (err) return console.log('Block ' + key + ' submission failed', err);
})
}
// Get data from levelDB with key
function getLevelDBData(key){
db.get(key, function(err, value) {
if (err) return console.log('Not found!', err);
console.log('Value = ' + value);
})
}
// Add data to levelDB with value
function addDataToLevelDB(value) {
let i = 0;
db.createReadStream().on('data', function(data) {
i++;
}).on('error', function(err) {
return console.log('Unable to read data stream!', err)
}).on('close', function() {
console.log('Block #' + i);
addLevelDBData(i, value);
});
}
/* ===== Testing ==============================================================|
| - Self-invoking function to add blocks to chain |
| - Learn more: |
| https://scottiestech.info/2014/07/01/javascript-fun-looping-with-a-delay/ |
| |
| * 100 Milliseconds loop = 36,000 blocks per hour |
| (13.89 hours for 500,000 blocks) |
| Bitcoin blockchain adds 8640 blocks per day |
| ( new block every 10 minutes ) |
| ===========================================================================*/
(function theLoop (i) {
setTimeout(function () {
addDataToLevelDB('Testing data');
if (--i) theLoop(i);
}, 100);
})(10);
Step 1 Analyze LevelSandbox.js methods Step 2 Let’s talk about the getBlocksCount()
algorithmStep 3 Run the application
Each step you’ll take to complete this exercise is called out in comments throughout the code. You can find more details and guidance about each of these steps in the text provided below the code.
Let’s get started!
In the previous lesson, you saw us implement some methods from LevelDB using callbacks. In this practice, we will be using Promises. Promises allow us to work with the database in asynchronous way, but is very important to understand how to transform the methods to return Promises.
The fist method implemented is getLevelDBData(key)
. This method allow you to get the value corresponding to the key.
The second method implemented is addLevelDBData(key, value)
. This method allow you to insert data into the LevelDB.
The third method getBlocksCount()
is the method that you need to implement, this is a very useful method because it will help you to get the how many objects you have inserted in your DB, for your future project this method can be use as way to calculate the height
.
getBlocksCount()
algorithmThis is the method that we need to implement and it will be very useful for your next project. The simple way to describe this functionality is to find a method that allows us to count all the key
s inserted in LevelDB right?
This method exists in LevelDB and it is createReadStream()
db.createReadStream()
.on('data', function (data) {
// Count each object inserted
})
.on('error', function (err) {
// reject with error
})
.on('close', function () {
//resolve with the count value
});
Read more about this in the documentation for LevelDB.
In the workspace below, open the app.js
file. In there you have code that allows the application to insert objects in the database.
To run the application:
cd Project
node app.js
Note: If you need to run your application several times, remember to delete the folder chaindata
every time.