资讯专栏INFORMATION COLUMN

区块链笔记(4)用JS写个简单的区块链原型

W_BinaryTree / 751人阅读

摘要:介绍了一些关于比特币的概念与机制,为了加深理解,本文基于来实现一个简单的区块链原型,后续再对其进行不断丰富。概述如前所述区块链模型的组成部分,包括区块,区块构成的区块链,以及保存区块链的数据持久层等。

介绍了一些关于比特币的概念与机制,为了加深理解,本文基于JavaScript来实现一个简单的区块链原型,后续再对其进行不断丰富。
1. 概述

如前所述区块链模型的组成部分,包括区块,区块构成的区块链,以及保存区块链的数据持久层等。一个超简单的UML类图如下:

由于我是前端的,业余看了这么久区块链的理论,还是手痒痒谢谢代码,把这个类用JavaScript实现一下。写完之后发现目前阶段,对于区块链原型来说还是太过简单,不过如果说用来做前端面试题,考察下面向对象和Promise等知识点倒是挺接洽。

2. 定义区块数据模型

摘取比特币区块的详情进行修改,去除所有多余信息,只留下能描述区块最基本的信息,声明区块类如下:

class Block {
    constructor(data) {
        // 区块的属性值
        this.hash = "";
        this.height = 0;
        this.body = data;
        this.time = 0;
        this.previousBlockHash = "";
    }
}

module.exports.Block = Block;
3. 数据持久层

其实用数组实现区块链是最简单的原型方案,但每次重启数组都会被清空,数据并不持久。所以这里引入levelDB数据库作为持久层来保存数据,相关操作可参考level。由于直接调用API,对于应用层来说过于麻烦,所以在此声明一个数据操作类LevelSandbox,该类不像传统的关系型数据库具有增、删、改、查等全部功能,由于区块链上数据的不可更改性,此类只包含增和查的操作。

3.1 根据key从数据库中获取数据

本文如下相关异步实现,都采用Promise的方式而非回调,其中好处作为前端工程师此处就不多介绍了,有需要了解的可异步Promise介绍,自行扩展阅读。

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 将key/value数据插入数据库中

key/value的方式在数据库中存储,其key值得选取,这里考虑使用区块类中声明的height字段,该字段标识一个区块在链中的位序,同时也具有唯一性,非常合适。

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 获取数据库中区块总数

createReadStream()方法创建一个读取数据库的流,这里的作用是为了遍历整库以获取存储的区块总数,另外此方法还可通过传参,设置遍历次序,详情可参阅文档。

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. 区块链类

该类主要负责将新创建的区块添加进区块链,并验证链中各个区块的数据完整性。这个过程中少不了对区块数据的哈希处理,为方便起见,采用第三方库crypto-js实现的SHA256方法。

构想该类中的主要方法包括:

createGenesisBlock():生成起始区块

getBlockHeight():获取区块链长度

getBlock(height):获取指定区块

addBlock(block):将一个新区块加入区块链中

validateBlock(block):验证某个区块

validateChain():验证区块链

如下便实现其中主要的几个方法:

4.1 增加新区块

各个区块通过previousBlockHash属性,依次指向前一个区块来连接成链的,除首区块该属性为空外。

addBlock(block) {
    return this.getBlockHeight()
        .then(height => {
            区块高度
            block.height = height;
            // UTC 时间戳
            block.time = new Date().getTime().toString().slice(0, -3);
            if (height > 0) {
                this.getBlock(height - 1)
                    .then(preBlock => {
                        // 前一个区块的哈希值
                        block.previousBlockHash = preBlock.hash;
                        // 对区块进行哈希处理
                        block.hash = SHA256(JSON.stringify(block)).toString();
                        // 将新区快存入库中
                        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 验证单个区块完整性

验证方法就是应用了hash算法的性质:相同的数据经过hash后会生成相同的hash值。

validateBlock(height) {
        // 获取区块的值
        return this.getBlock(height)
            .then(block => {
                const objBlock = JSON.parse(block);
                let blockHash = objBlock.hash;
                objBlock.hash = "";
                // 重新生成区块的哈希值
                let validBlockHash = SHA256(JSON.stringify(objBlock)).toString();
                objBlock.hash = blockHash;
                // 比较以验证完整性
                if (blockHash === validBlockHash) {
                    return Promise.resolve({isValidBlock: true, block: objBlock});
                } else {
                    console.log("Block #"+blockHeight+" invalid hash:
"+blockHash+"<>"+validBlockHash);
                    return Promise.resolve({isValidBlock: false, block: objBlock});
                }
            })
    }
4.3 验证整个区块链

通过依次校验每个区块以验证整条链的完整性。

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. 生成测试数据
(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);

作为一个区块链原型的样子算是初见端倪,但就目前的功能来说还非常简陋,说是原型都算抬举了,不过后面慢慢再丰富吧。这里也只算是对之前的一个实践性的小节。

文中以列出主要代码片段,整体实现其实不难,没贴出所有代码主要是为了表述思路更清晰些,若有朋友实现过程中有问题,可文下留言交流。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109909.html

相关文章

  • 区块笔记4JS写个简单区块原型

    摘要:介绍了一些关于比特币的概念与机制,为了加深理解,本文基于来实现一个简单的区块链原型,后续再对其进行不断丰富。概述如前所述区块链模型的组成部分,包括区块,区块构成的区块链,以及保存区块链的数据持久层等。 介绍了一些关于比特币的概念与机制,为了加深理解,本文基于JavaScript来实现一个简单的区块链原型,后续再对其进行不断丰富。 1. 概述 如前所述区块链模型的组成部分,包括区块,区块...

    binaryTree 评论0 收藏0
  • 区块上编程:DApp 开发实战——来写个竞猜游戏吧

    摘要:合约编写可以看出合约需要实现用户投注生成随机数发放奖励奖池余额查询的功能,接下来编写我们的合约代码。总结当前随机数的实现通过链上信息生成,这种生成随机数的方式容易受到不诚实的节点攻击。 导读:本文旨在引导对 DApp 开发感兴趣的开发者,构建一个基于以太坊去中心化应用,通过开发一款功能完备的竞猜游戏,迈出 DApp 开发的第一步,通过实例讲解 Solidity 语言的常用语法,以及前端...

    codecook 评论0 收藏0
  • 300行ABAP代码实现一个最简单区块原型

    摘要:我的这篇文章没有任何高大上的术语,就是行代码,实现一个最简单的区块链原型。检查该区块链是否有效。而通过在循环里不断尝试最终得到一个合法的哈希值的这一过程,就是区块链圈内俗称的挖矿。 不知从什么时候起,区块链在网上一下子就火了。 showImg(https://segmentfault.com/img/remote/1460000014744826); 这里Jerry就不班门弄斧了,网上...

    cikenerd 评论0 收藏0
  • 300行ABAP代码实现一个最简单区块原型

    摘要:我的这篇文章没有任何高大上的术语,就是行代码,实现一个最简单的区块链原型。检查该区块链是否有效。而通过在循环里不断尝试最终得到一个合法的哈希值的这一过程,就是区块链圈内俗称的挖矿。 不知从什么时候起,区块链在网上一下子就火了。 showImg(https://segmentfault.com/img/remote/1460000014744826); 这里Jerry就不班门弄斧了,网上...

    DangoSky 评论0 收藏0
  • 基于Java语言构建区块(一)—— 基本原型

    摘要:本文将基于语言构建简化版的,来实现数字货币。值用于确保的安全。计算是计算敏感的操作,即使在高性能电脑也需要花费一段时间来完成计算这也就是为什么人们购买高性能进行比特币挖矿的原因。资料源代码精通比特币第二版 showImg(https://segmentfault.com/img/remote/1460000013923206?w=1600&h=900); 最终内容请以原文为准:http...

    PiscesYE 评论0 收藏0

发表评论

0条评论

W_BinaryTree

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<