• 初识区块链——用JS构建你自己的区块链


    前言

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

    认识区块链

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

    Block的数据结构

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

    BlockChain的数据结构

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

    创世区块

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

    添加区块

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

    校验区块链

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

    Just run it

    跑起来看看,即:
    let simpleChain = new BlockChain();
    simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
    simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
    
    
    console.log(JSON.stringify(simpleChain, null, 4));
    
    console.log("is the chain valid? " + simpleChain.isChainValid());
    
    结果如下:
    ali-186590cc4a7f:simple-chain shanyao$ node main_1.js 
    {
        "chain": [
            {
                "timestamp": "2018-11-11 00:00:00",
                "data": "Genesis block of simple chain",
                "previousHash": "",
                "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
            },
            {
                "timestamp": "2018-11-11 00:00:01",
                "data": {
                    "amount": 10
                },
                "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
                "hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"
            },
            {
                "timestamp": "2018-11-11 00:00:02",
                "data": {
                    "amount": 20
                },
                "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
                "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
            }
        ]
    }
    is the chain valid? true
    
    
    注意看其中的previousHash与hash,确实是当前区块的previousHash指向前一个区块的hash。

    篡改下试试

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

    再篡改下试试

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

    是真的无法篡改吗

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

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

  • 相关阅读:
    为什么我要迁移 SpringBoot 到函数计算
    PolarDBX源码解读系列:DML之Insert流程
    转载 | 从云计算到函数计算
    通过部署流行Web框架掌握Serverless技术
    阿里云云原生一体化数仓 — 湖仓一体新能力解读
    EasyNLP中文文图生成模型带你秒变艺术家
    转载 | 基于函数计算自定义运行时快速部署一个 springboot 项目
    PolarDBX 源码解读:事务的一生
    【20220319】回老家给父亲庆生
    【20220321】连岳摘抄
  • 原文地址:https://www.cnblogs.com/yunqishequ/p/10063447.html
Copyright © 2020-2023  润新知