Block Chain Note Write a simple block chain prototype with JS

Posted by nita on Mon, 27 May 2019 19:27:44 +0200

Introduces some concepts and mechanisms about Bitcoin. For a better understanding, this paper implements a simple block chain prototype based on JavaScript, and then enriches it.

1. Overview

Components of the block chain model described earlier include blocks, block chains made of blocks, and data persistence layers that hold block chains.A very simple UML class diagram follows:

Since I'm front-end, I've looked at the theory of block chains for such a long time in my spare time, or itchy thank you code to implement this kind of thing in JavaScript.After writing it, I find that it is still too simple for the block chain prototype at this stage, but if it is used for front-end interviews, it is a good idea to examine the following objects and knowledge points such as Proise.

2. Define the block data model

Extract the details of the bitcoin block and modify it to remove all the redundant information, leaving only the most basic information describing the block, declaring the block class as follows:

class Block {
    constructor(data) {
        // Attribute values for blocks
        this.hash = "";
        this.height = 0;
        this.body = data;
        this.time = 0;
        this.previousBlockHash = "";
    }
}

module.exports.Block = Block;

3. Data persistence layer

The simplest prototype is to implement block chains using arrays, but each time the arrays are restarted, the data is not persistent.So here, levelDB database is introduced as a persistence layer to store data, which can be referred to level .Because calling the API directly is too cumbersome for the application tier, declare here a data operation class, LevelSandbox, which does not have all the functions of adding, deleting, altering, searching like traditional relational databases. Because of the immutability of data on block chains, this class only contains operations of adding and searching.

3.1 Get data from database based on key

The following asynchronous implementations are implemented in a Promise fashion rather than a callback, and the benefits as front-end engineers are not covered here, but asynchronous implementations that need to be understood Introduction to Promise To expand your reading.

getLevelDBData(key) {
    let self = this;
    return new Promise(function(resolve, reject) {
        self.db.get(key)
            .then(value => {
                console.log('Value = ' + value);
                resolve(value)
            })
            .catch(err => {
                console.log('Not found!');
                reject(err)
            })
    });
}

3.2 Insert key/value data into database

Stored in a database as key/value, whose key is optional, consider using the height field declared in the block class, which identifies the order of a block in the chain and is unique and appropriate.

addLevelDBData(key, value) {
    let self = this;
    return new Promise(function(resolve, reject) {
        self.db.put(key, value)
            .then(() => resolve())
            .catch((err) => {
                console.log('Block ' + key + ' submission failed');
                reject(err)
            })
    });
}

3.3 Get the total number of blocks in the database

The createReadStream() method creates a stream that reads the database, where the purpose is to traverse the entire library to get the total number of blocks stored. It also sets the traversal order by passing parameters, which can be detailed in the documentation.

getBlocksCount() {
    let self = this;
    return new Promise(function(resolve, reject){
        let height = 0;
        self.db.createReadStream()
            .on('data', function () {
                height++;
            })
            .on('error', function (error) {
                reject('Unable to read data stream!', error);
            })
            .on('close', function () {
                resolve(height);
            });
    });
}

4. Block Chain Classes

This class is responsible for adding newly created blocks to the block chain and verifying the data integrity of the blocks in the chain.This process involves hashing block data, using third-party libraries for convenience crypto-js The SHA256 method implemented.

The main ways to conceive of this category include:

  • CreateGenesis Block (): Generate start block
  • getBlockHeight(): Get block chain length
  • getBlock(height): Gets the specified block
  • AddBlock: Add a new block to the block chain
  • ValidateBlock: Validate a block
  • validateChain(): Verify block chains

The main methods are as follows:

4.1 Add new blocks

Each block is linked through the previousBlockHash attribute, which in turn points to the previous block, except for the first block, which is empty.

addBlock(block) {
    return this.getBlockHeight()
        .then(height => {
            //block height
            block.height = height;
            // UTC timestamp
            block.time = new Date().getTime().toString().slice(0, -3);
            if (height > 0) {
                this.getBlock(height - 1)
                    .then(preBlock => {
                        // Hash value of previous block
                        block.previousBlockHash = preBlock.hash;
                        // Hash blocks
                        block.hash = SHA256(JSON.stringify(block)).toString();
                        // Quick Save New Zone in Library
                        this.bd.addLevelDBData(height, JSON.stringify(block));
                    })
                    .catch(error => console.log(error));
            } else {
                block.hash = SHA256(JSON.stringify(block)).toString();
                this.bd.addLevelDBData(height, JSON.stringify(block));
            }
        })
        .catch( error => console.log(error));
}

4.2 Verify single block integrity

The validation method is to apply the properties of the hash algorithm: the same data after hash generates the same hash value.

validateBlock(height) {
        // Get the value of the block
        return this.getBlock(height)
            .then(block => {
                const objBlock = JSON.parse(block);
                let blockHash = objBlock.hash;
                objBlock.hash = '';
                // Regenerate hash values for blocks
                let validBlockHash = SHA256(JSON.stringify(objBlock)).toString();
                objBlock.hash = blockHash;
                // Compare to verify integrity
                if (blockHash === validBlockHash) {
                    return Promise.resolve({isValidBlock: true, block: objBlock});
                } else {
                    console.log('Block #'+blockHeight+' invalid hash:\n'+blockHash+'<>'+validBlockHash);
                    return Promise.resolve({isValidBlock: false, block: objBlock});
                }
            })
    }

4.3 Verify the entire block chain

Verify the integrity of the entire chain by checking each block in turn.

validateChain() {
    let errorLog = [];
    let previousHash = '';
    this.getBlockHeight()
        .then(height => {
            for (let i = 0; i < height; i++) {
                this.getBlock(i)
                    .then(block => this.validateBlock(block.height))
                    .then(({isValidBlock, block}) => {
                        if (!isValidBlock) errorLog.push(i);
                        if (block.previousBlockHash !== previousHash) errorLog.push(i);
                        previousHash = block.hash;
                        if (i === height - 1) {
                            if (errorLog.length > 0) {
                                console.log(`Block errors = ${errorLog.length}`)
                                console.log(`Blocks: ${errorLog}`)
                            } else {
                                console.log('No errors detected')
                            }
                        }
                    })
            }
        })
}

5. Generate test data

(function theLoop (i) {
    setTimeout(function () {
        let blockTest = new Block.Block("Test Block - " + (i + 1));
        myBlockChain.addBlock(blockTest).then((result) => {
            console.log(result);
            i++;
            if (i < 10) theLoop(i);
        });
    }, 10000);
})(0);

The look of a block chain prototype is fairly primitive, but it's still very simple in terms of its current functionality. It's said that the prototype is elevated, but it's getting richer later.This is only a practical subsection of the previous one.

This article lists the main code snippets, the overall implementation is not difficult, not all the code posted is mainly to express ideas more clearly, if there are problems in the implementation process of a friend, you can leave a message to exchange.

Topics: Javascript Database JSON Attribute