前言

区块链太复杂,那我们就讲点简单的。用JS来构建你自己的区块链系统,寥寥几行代码就可以说明区块链的底层数据结构、POW挖矿思想和交易过程等。当然了,真实的场景远远远比这复杂。本文的目的仅限于让大家初步了解、初步认识区块链。
文章内容主要参考视频:Building a blockchain with Javascript (https://www.youtube.com/playlist?list=PLzvRQMJ9HDiTqZmbtFisdXFxul5k0F-Q4)
感谢原作者,本文在原视频基础上做了修改补充,并加入了个人理解。

认识区块链

区块链顾名思义是由区块连接而成的链,因此最基本的数据结构是Block。每个Block都含有timestamp、data、hash、previousHash等信息。其中data用来存储数据,previousHash是前一个区块的hash值。示意如下:
hash是对区块信息的摘要存储,hash的好处是任意长度的信息经过hash都可以映射成固定长度的字符串,如可用sha256:
  1. calculateHash() {
  2. return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
  3. }

Block的数据结构

Block的最基本数据结构如下:
  1. class Block {
  2. constructor(timestamp, data, previousHash = '') {
  3. this.timestamp = timestamp;
  4. this.data = data;
  5. this.previousHash = previousHash;
  6. //对hash的计算必须放在最后,保证所有数据赋值正确后再计算
  7. this.hash = this.calculateHash();
  8. }
  9. calculateHash() {
  10. return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
  11. }
  12. }

BlockChain的数据结构

多个Block链接而成BlockChain,显然可用用数组或链表来表示,如:
  1. class BlockChain {
  2. constructor() {
  3. this.chain = [];
  4. }
  5. }

创世区块

正所谓万物始于一,区块链的第一个区块总是需要人为来手动创建,这个区块的previousHash为空,如:
  1. createGenesisBlock() {
  2. return new Block("2018-11-11 00:00:00", "Genesis block of simple chain", "");
  3. }
区块链的构造方法也应改为:
  1. class BlockChain {
  2. constructor() {
  3. this.chain = [this.createGenesisBlock()];
  4. }
  5. }

添加区块

每新加一个区块,必须保证与原有区块链连接起来,即:
  1. class BlockChain {
  2. getLatestBlock() {
  3. return this.chain[this.chain.length - 1];
  4. }
  5. addBlock(newBlock) {
  6. //新区块的前一个hash值是现有区块链的最后一个区块的hash值;
  7. newBlock.previousHash = this.getLatestBlock().hash;
  8. //重新计算新区块的hash值(因为指定了previousHash);
  9. newBlock.hash = newBlock.calculateHash();
  10. //把新区块加入到链中;
  11. this.chain.push(newBlock);
  12. }
  13. ...
  14. }

校验区块链

区块链数据结构的核心是保证前后链接、无法篡改,但是如果有人真的篡改了某个区块,我们该如何校验发现呢?最笨也是最自然是想法就是遍历所有情况,逐一校验,如:
  1. isChainValid() {
  2. //遍历所有区块
  3. for (let i = 1; i < this.chain.length; i++) {
  4. const currentBlock = this.chain[i];
  5. const previousBlock = this.chain[i - 1];
  6. //重新计算当前区块的hash值,若发现hash值对不上,说明该区块有数据被篡改,hash值未重新计算
  7. if (currentBlock.hash !== currentBlock.calculateHash()) {
  8. console.error("hash not equal: " + JSON.stringify(currentBlock));
  9. return false;
  10. }
  11. //判断当前区块的previousHash是否真的等于前一个区块的hash,若不等,说明前一个区块被篡改,虽然hash值被重新计算正确,但是后续区块的hash值并未重新计算,导致整个链断裂
  12. if (currentBlock.previousHash !== previousBlock.calculateHash) {
  13. console.error("previous hash not right: " + JSON.stringify(currentBlock));
  14. return false;
  15. }
  16. }
  17. return true;
  18. }

Just run it

跑起来看看,即:
  1. let simpleChain = new BlockChain();
  2. simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
  3. simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
  4. console.log(JSON.stringify(simpleChain, null, 4));
  5. console.log("is the chain valid? " + simpleChain.isChainValid());
结果如下:
  1. ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
  2. {
  3. "chain": [
  4. {
  5. "timestamp": "2018-11-11 00:00:00",
  6. "data": "Genesis block of simple chain",
  7. "previousHash": "",
  8. "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
  9. },
  10. {
  11. "timestamp": "2018-11-11 00:00:01",
  12. "data": {
  13. "amount": 10
  14. },
  15. "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
  16. "hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"
  17. },
  18. {
  19. "timestamp": "2018-11-11 00:00:02",
  20. "data": {
  21. "amount": 20
  22. },
  23. "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
  24. "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
  25. }
  26. ]
  27. }
  28. is the chain valid? true
注意看其中的previousHash与hash,确实是当前区块的previousHash指向前一个区块的hash。

篡改下试试

都说区块链不可篡改,是真的吗?让我们篡改第2个区块试试,如:
  1. let simpleChain = new BlockChain();
  2. simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
  3. simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
  4. console.log("is the chain valid? " + simpleChain.isChainValid());
  5. //将第2个区块的数据,由10改为15
  6. simpleChain.chain[1].data = {amount: 15};
  7. console.log("is the chain still valid? " + simpleChain.isChainValid());
  8. console.log(JSON.stringify(simpleChain, null, 4));
结果如下:
  1. ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
  2. is the chain valid? true
  3. hash not equal: {"timestamp":"2018-11-11 00:00:01","data":{"amount":15},"previousHash":"fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89","hash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"}
  4. is the chain still valid? false
  5. {
  6. "chain": [
  7. {
  8. "timestamp": "2018-11-11 00:00:00",
  9. "data": "Genesis block of simple chain",
  10. "previousHash": "",
  11. "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
  12. },
  13. {
  14. "timestamp": "2018-11-11 00:00:01",
  15. "data": {
  16. "amount": 15
  17. },
  18. "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
  19. "hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"
  20. },
  21. {
  22. "timestamp": "2018-11-11 00:00:02",
  23. "data": {
  24. "amount": 20
  25. },
  26. "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
  27. "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
  28. }
  29. ]
  30. }
显然,篡改了数据之后,hash值并未重新计算,导致该区块的hash值对不上。

再篡改下试试

那么,如果我们聪明点,篡改后把hash值也重新计算会如何?
  1. let simpleChain = new BlockChain();
  2. simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
  3. simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
  4. console.log("is the chain valid? " + simpleChain.isChainValid());
  5. //篡改后重新计算hash值
  6. simpleChain.chain[1].data = {amount: 15};
  7. simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
  8. console.log("is the chain still valid? " + simpleChain.isChainValid());
  9. console.log(JSON.stringify(simpleChain, null, 4));
结果如下:
  1. ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
  2. is the chain valid? true
  3. previous hash not right: {"timestamp":"2018-11-11 00:00:02","data":{"amount":20},"previousHash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529","hash":"274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"}
  4. is the chain still valid? false
  5. {
  6. "chain": [
  7. {
  8. "timestamp": "2018-11-11 00:00:00",
  9. "data": "Genesis block of simple chain",
  10. "previousHash": "",
  11. "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
  12. },
  13. {
  14. "timestamp": "2018-11-11 00:00:01",
  15. "data": {
  16. "amount": 15
  17. },
  18. "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
  19. "hash": "74d139274fb692495b7c805dd5822faa0c5b5e6058b6beef96e87e18ab83a6b1"
  20. },
  21. {
  22. "timestamp": "2018-11-11 00:00:02",
  23. "data": {
  24. "amount": 20
  25. },
  26. "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
  27. "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
  28. }
  29. ]
  30. }
显然,第3个区块的previousHash并未指向第2个区块的hash。

是真的无法篡改吗

其实并不是,如果我们再聪明一点,把后续区块的hash值也重新计算一下,不就OK了吗? 确实如此,如:
  1. let simpleChain = new BlockChain();
  2. simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
  3. simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
  4. console.log("is the chain valid? " + simpleChain.isChainValid());
  5. //篡改第2个区块
  6. simpleChain.chain[1].data = {amount: 15};
  7. simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
  8. //并把第3个区块也重新计算
  9. simpleChain.chain[2].previousHash = simpleChain.chain[1].hash;
  10. simpleChain.chain[2].hash = simpleChain.chain[2].calculateHash();
  11. console.log("is the chain still valid? " + simpleChain.isChainValid());
  12. console.log(JSON.stringify(simpleChain, null, 4

原文链接
本文为云栖社区原创内容,未经允许不得转载。

初识区块链——用JS构建你自己的区块链的更多相关文章

  1. 用Hyperledger Fabric(超级账本)来构建Java语言开发区块链的环境

    面向 Java 开发人员的链代码简介 您或许听说过区块链,但可能不确定它对 Java™ 开发人员有何用.本教程将帮助大家解惑.我将分步展示如何使用 Hyperledger Fabric v0.6 来构 ...

  2. 用 Python 构建一个极小的区块链

    虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录 ...

  3. js构建ui的统一异常处理方案(一)

    从早期从事基于java的服务器端开发,再到之后从事基于web和js的ui开发,总体感觉基于web页面的ui开发远不如服务器端健壮.主要是早期ie浏览器功能太弱小,很多业务被迫放到服务器端去实现,浏览器 ...

  4. js基础篇——原型与原型链的详细理解

    js中的对象分为两种:普通对象object和函数对象function. function fn1(){}; var fn2 = function(){}; var fn3 = new Function ...

  5. 全国11省市出台区块链专项政策,Panda Global发现 "区块链+政务"被寄予厚望!

    2020年已经过半,回顾2020年的上半年,不难发现其实区块链的变化非常大,今天Panda Global就给大家回顾下上半年全国关于区块链政策的发布情况.今年上半年,全国已有11个省市出台区块链专项政 ...

  6. 区块链开发学习第三章:私有链上部署helloBlockchain简单合约

    前面讲了部署私有链以及新增账户,现在进行到了部署合约了,此操作真是踩了无数无数无数的坑,到写文章为止确实是已经部署好了,但是还有些坑是还没有解决的! 一.Solidity编译器 开始的时候用的http ...

  7. NativeScript - JS 构建跨平台的原生 APP

    使用 NativeScript,你可以用现有的 JavaScript 和 CSS 技术来编写 iOS.Android 和 Windows Phone 原生移动应用程序.由原生平台的呈现引擎呈现界面而不 ...

  8. 通过Web Api 和 Angular.js 构建单页面的web 程序

    通过Web Api 和 Angular.js 构建单页面的web 程序 在传统的web 应用程序中,浏览器端通过向服务器端发送请求,然后服务器端根据这个请求发送HTML到浏览器,这个响应将会影响整个的 ...

  9. 第十九篇 js高级知识---词法分析和AO 链

    上面一篇文章说了js的作用域链,这一节算是对上面的延申,有一个典型的例子,首先看原来的一段代码: var name = "test"; function t() { var b = ...

随机推荐

  1. Catalan 数列的性质及其应用(转载)

    转自:http://lanqi.org/skills/10939/ 卡特兰数 — 计数的映射方法的伟大胜利 发表于2015年11月8日由意琦行 卡特兰(Catalan)数来源于卡特兰解决凸$n+2$边 ...

  2. myeclipse中配置自己安装的Tomcat

    myeclipse中配置自己安装的Tomcat 在myeclipse中配置好jdk,Tomcat之后,如果是web项目需要部署运行,但在部署时发现配置好的Tomcat没有显示,只显示了myeclips ...

  3. pom string

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  4. JVM服务进程挂掉问题定位查询思路

    昨天有朋友咨询了个RegionServer宕机找不到日志无法定位原因的问题,干脆就系统整理下JVM服务宕机的可能原因,方便按照思路去找真正的宕机原因. 1. abort()/halt()/exit() ...

  5. mysql数据库内容相关操作

    第一:介绍 mysql数据内容的操作主要是: INSERT实现数据的插入 UPDATE实现数据的更新 DLETE实现数据的删除 SELECT实现数据的查询. 第二:增(insert) 1.插入完整的数 ...

  6. 【技术分享】BurpSuite 代理设置的小技巧

    作者:三思之旅 预估稿费:300RMB 投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿 在Web渗透测试过程中,BurpSuite是不可或缺的神器之一.BurpSuite的核心是 ...

  7. js-day03-事件响应和练习题

    DOM事件编程 事件驱动编程:所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数).当然事件不仅限于用户的操作. 当对象处于某种状态时,可以发出一个消息通知,然 ...

  8. C语言面试题分类->链表

    链表的创建,清空,插入,删除 typedef int (* __compfunc)(const void *, const void *); //Traverse list. Fast macro t ...

  9. 依赖注入[4]: 创建一个简易版的DI框架[上篇]

    本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度 ...

  10. Atlas实现MySQL大表部署读写分离

    序章 Atlas是360团队弄出来的一套基于MySQL-Proxy基础之上的代理,修改了MySQL-Proxy的一些BUG,并且优化了很多东西.而且安装方便.配置的注释写的蛮详细的,都是中文.英文不好 ...