JavaScript implements a simple blockchain

Implement a simple blockchain in JavaScript. Through the implementation process, you will understand what a blockchain is: a blockchain is a distributed database, and the storage structure is an ever-growing linked list that contains many ordered records.

However, in general, when we talk about the blockchain, we also talk about the problems solved by the blockchain, and it is easy to confuse the two.

This is the case with blockchain-based projects like the popular Bitcoin and Ethereum. The term "blockchain" is often closely associated with concepts like transactions, smart contracts, and cryptocurrencies.

This makes understanding the blockchain unnecessarily complicated, especially when you want to understand the source code. Below I will help you understand it through a super simple blockchain implemented in 200 lines of JS, I named this code NaiveChain. You can check more technical details at [Github] https://github.com/lhartikk/naivechain ).

block structure

The first logical step is to decide on the block structure. To keep things as simple as possible, we only choose the most necessary parts: index (subscript), timestamp (timestamp), data (data), hash (hash value) and previous hash (previous hash value).

blockchain

The hash of the previous block must be found in this block to ensure the integrity of the entire chain.

class Block {
    constructor(index, previousHash, timestamp, data, hash) {
        this.index = index;
        this.previousHash = previousHash.toString();
        this.timestamp = timestamp;
        this.data = data;
        this.hash = hash.toString();
    }
}

block hash

In order to preserve the complete data, the block must be hashed. SHA-256 will encrypt the content of the block, and recording this value should have nothing to do with "mining", because there is no need to solve the problem of proof-of-work here.

var calculateHash = (index, previousHash, timestamp, data) => {
    return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};

generation of blocks

To generate a block, one must know the hash of the previous block, and then create the rest of the required content (= index, hash, data and timestamp).

The data part of the block is provided by the end user.

var generateNextBlock = (blockData) => {
    var previousBlock = getLatestBlock();
    var nextIndex = previousBlock.index + 1;
    var nextTimestamp = new Date().getTime() / 1000;
    var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);
    return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
};

block storage

In-memory Javascript arrays are used to store the blockchain. The first block of the blockchain is often referred to as the "origin block" and is hardcoded.

var getGenesisBlock = () => {
    return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");
};
 
var blockchain = [getGenesisBlock()];

Confirm the integrity of the block

At all times, it must be possible to confirm whether a block or an entire chain of blocks is complete. This is especially important when we receive new blocks from other nodes and need to decide to accept or reject them.

var 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 (calculateHashForBlock(newBlock) !== newBlock.hash) {
        console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);
        return false;
    }
    return true;
};

Choose the longest chain

There should only be a defined set of blocks in the chain at any one time. In case of conflict (for example: when both nodes generate block 72), the chain with the largest number of blocks will be selected.

blockchain

var replaceChain = (newBlocks) => {
    if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
        console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');
        blockchain = newBlocks;
        broadcast(responseLatestMsg());
    } else {
        console.log('Received blockchain invalid');
    }
};

Communication with other nodes

The essence of a node is to share and synchronize the blockchain with other nodes. The following rules can ensure network synchronization.

  • When a node generates a new block, it spreads the block across the network.
  • When a node connects to a new peer, it queries the latest block.
  • When a node encounters a block whose index is greater than the indexes of all current blocks, it will add the block to its current chain, or query the block in the entire blockchain.

blockchain

I don't have a tool for auto-discovering peers. The location (URL) of peers must be added manually.

Node control

The user must be able to control the node to some extent. This can be achieved by building an HTTP server.

var initHttpServer = () => {
    var app = express();
    app.use(bodyParser.json());

    app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
    app.post('/mineBlock', (req, res) => {
        var newBlock = generateNextBlock(req.body.data);
        addBlock(newBlock);
        broadcast(responseLatestMsg());
        console.log('block added: ' + JSON.stringify(newBlock));
        res.send();
    });
    app.get('/peers', (req, res) => {
        res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
    });
    app.post('/addPeer', (req, res) => {
        connectToPeers([req.body.peer]);
        res.send();
    });
    app.listen(http_port, () => console.log('Listening http on port: ' + http_port));
};

Users can interact with nodes in the following ways:

  • list all blocks
  • Create a new block with user-provided content
  • List or add peers

The following Curl example is the most direct way to control nodes:

#get all blocks from the node
curl http://localhost:3001/blocks

system structure

It should be pointed out that the node actually presents two web servers: one (HTTP server) that lets the user control the node, and the other (Websocket HTTP server).

Summarize

The purpose of creating NaiveChain is for demonstration and learning, because it does not have a "mining" algorithm (PoS or PoW) and cannot be used in public networks, but it implements the basic characteristics of blockchain operation.

If you want to learn Ethereum DApp development efficiently , you can visit the most popular online interactive tutorials provided by Huizhi.com:

More other content is also available on this Ethereum blog .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325314132&siteId=291194637