• Java实现简单区块链


    参考地址:Creating Your First Blockchain with Java

    准备

    开发环境

    1. java1.8~
    2. maven
    3. 任选IDE

    区块链概述

    顾名思义,区块链就是很多“区块”形成的“链”。
    每个“区块”上包含的数据有:

    • 它自身的数字指纹(digital fingerprint)
    • 上一个区块的数字指纹
    • 一些额外信息,如交易信息(tansaction infomation)等等

    数字指纹一般是一个哈希值
    当前区块的数字指纹计算方式:会根据上一个区块的指纹,以及当前区块的信息来计算
    故当前区块的信息发生改变,影响这条链上之后的所有区块的数字指纹

    所以,区块链的一般结构为:

    编码

    区块Block

    import java.util.Date;
    
    public class Block {
        public String hash;
        public String previousHash;
        private String data; 
        private long timeStamp; 
    
        public Block(String data,String previousHash ) {
    	this.data = data;
    	this.previousHash = previousHash;
    	this.timeStamp = new Date().getTime();
        }
    }
    

    “数字指纹”生成

    采用sha256哈希算法生成哈希值(String表示)

    import java.security.MessageDigest;
    
    public class StringUtil {
    
        /**
         * 对输入input使用sha256算法进行哈希,
         * 返回哈希值的16进制字符串
         * @param input
         * @return
         */
        public static String applySha256(String input) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
                byte[] hash = digest.digest(input.getBytes("UTF-8"));
                StringBuffer hexString = new StringBuffer();
                for (int i = 0; i < hash.length; i++) {
                    String hex = Integer.toHexString(0xff & hash[i]);
                    if (hex.length() == 1)
                        hexString.append('0');
                    hexString.append(hex);
                }
                return hexString.toString();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    接下来可以修改Block类,增加一个生成数字指纹的方法
    在Block类的构造函数中,给变量hash赋初值

    public class Block {
        // ... 省略成员变量 
        
        public Block(String data, String previousHash) {
            this.data = data;
            this.previousHash = previousHash;
            this.timeStamp = new Date().getTime();
            this.hash = calculateHash(); // 给hash赋初值
        }
    
        public String calculateHash() {
            String calculatedhash = StringUtil.applySha256(
                                previousHash +              // 上一区块的数字指纹
                                Long.toString(timeStamp) +  // 当前区块的时间戳
                                data);                      // 当前区块的额外信息
            return calculatedhash;
        }
    }
    

    区块链NoobChain

    Noob是“菜鸟,新手”的意思。

    新建NoobChain类
    先把主函数放入这个类中,进行测试:
    创世区块是指区块链的第一个区块
    创世区块没有上一个区块,所以把该区块的“上一个区块数字指纹”设为0
    有了创世区块,就可以在这条区块链上继续新增区块

    public class NoobChain {
        
        // 测试
        public static void main(String[] args) {
            // 创世区块
            Block genesisBlock = new Block("Hi im the first block", "0");
            System.out.println("Hash for block 1 : " + genesisBlock.hash);
    
            Block secondBlock = new Block("Yo im the second block", genesisBlock.hash);
            System.out.println("Hash for block 2 : " + secondBlock.hash);
    
            Block thirdBlock = new Block("Hey im the third block", secondBlock.hash);
            System.out.println("Hash for block 3 : " + thirdBlock.hash);
    
        }
    }
    

    输出为:

    修改pom.xml文件,添加Gson依赖

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.9.0</version>
    </dependency>
    

    修改NoobChain类,用链表ArrayList保存区块

    public class NoobChain {
    
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
        
        //测试
        public static void main(String[] args) {
            // 往区块链上添加区块:
            blockchain.add(new Block("Hi im the first block", "0"));
            blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size() - 1).hash));
            blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size() - 1).hash));
            // 按json格式输出
            String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
            System.out.println(blockchainJson);
        }
    }
    

    区块链完整性校验

    之前提到,在一个区块链上,
    如果当前区块的信息发生变化,
    后序所有区块的数字指纹都要重新计算,
    所以需要校验是否所有区块都满足前后依赖关系。
    这里在NoobChain类中设计一个实例函数isChainValid()

    public class NoobChain {
        // 区块链
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
        
        // 区块链完整性校验
        public static Boolean isChainValid() {
            Block currentBlock;
            Block previousBlock;
    
            // 从前往后遍历所有区块
            for (int i = 1; i < blockchain.size(); i++) {
                currentBlock = blockchain.get(i);
                previousBlock = blockchain.get(i - 1);
                // 重新计算当前区块的数字指纹(实例变量hash)
                if (!currentBlock.hash.equals(currentBlock.calculateHash())) {
                    System.out.println("Current Hashes not equal");
                    return false;
                }
                // 判断当前区块的中上一区块的数字指纹是否正确
                if (!previousBlock.hash.equals(currentBlock.previousHash)) {
                    System.out.println("Previous Hashes not equal");
                    return false;
                }
            }
            return true;
        }
    
    }
    

    “区块链上线”

    区块链系统是典型的分布式系统
    每个机器(节点)都能运行区块链程序
    每个节点都能运行上述NoobChain类中的主函数
    在主函数中,主要执行的任务是什么呢?就是往区块链后添加区块
    例如,比特币(bitcoin)系统的每个节点是共享区块链的,
    而所有节点中的最长区块链会被整个系统接受。
    为了防止某个节点随意创建“最长的区块链”,比特币有工作量证明机制(Proof of work),
    也就是在“添加区块至区块链”时会消耗很多时间和算力

    攻击者的攻击需要有超过整个系统中所有剩余节点的算力之和

    “挖矿”

    挖矿:简单理解,挖矿等价于“添加区块至区块链”;
    由于存在工作量证明机制,添加区块至区块链时会很难;
    下面模拟一种工作量证明机制(使得“添加区块至区块链”变难):

    在区块Block类中新增一个nonce属性,nonce的作用是:

    • 在计算数字指纹(hash实例变量)时也需加上这个nonce值;
    • 只有当新建区块的数字指纹以一定数量的0开头,才能把新建区块添加入区块链
    public class Block {
        public String hash;
        public String previousHash;
        private String data; 
        private long timeStamp;
        private int nonce; 
    
        public Block(String data, String previousHash) {
            this.data = data;
            this.previousHash = previousHash;
            this.timeStamp = new Date().getTime();
            this.hash = calculateHash(); // 给hash赋初值
        }
    
        public String calculateHash() {
            String calculatedhash = StringUtil.applySha256(
                                previousHash +              // 上一区块的数字指纹
                                Long.toString(timeStamp) +  // 当前区块的时间戳
                                Integer.toString(nonce) +   // 随机值
                                data);                      // 当前区块的额外信息
            return calculatedhash;
        }
      
        /**
         * 模拟:只有当hash数字指纹 以difficulty个0开头, 
         * 才能结束while循环,
         * 然后去把已经创建好的区块添加至区块链
         * 
         * @param difficulty
         */
        public void mineBlock(int difficulty) {
            String target = new String(new char[difficulty]).replace('\0', '0');
            while (!hash.substring(0, difficulty).equals(target)) {
                nonce++;
                hash = calculateHash();
            }
            System.out.println("Block Mined!!! : " + hash);
        }
    }
    

    更新NoobChain类中的main函数,实现:只有满足工作量证明后,才创建区块

    public class NoobChain {
        // 区块链
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
        public static int difficulty = 1;
        
        // 模拟挖矿
        public static void main(String[] args) {
            // add our blocks to the blockchain ArrayList:
            blockchain.add(new Block("Hi im the first block", "0"));
            System.out.println("Trying to Mine block 1... ");
            // 得完成工作量证明,才能算成功把新建区块1添加区块链
            blockchain.get(0).mineBlock(difficulty); 
    
            blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size() - 1).hash));
            System.out.println("Trying to Mine block 2... ");
            // 得完成工作量证明,才能算成功把新建区块2添加区块链
            blockchain.get(1).mineBlock(difficulty);
    
            blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size() - 1).hash));
            System.out.println("Trying to Mine block 3... ");
            // 得完成工作量证明,才能算成功把新建区块3添加区块链
            blockchain.get(2).mineBlock(difficulty); 
    
            System.out.println("\nBlockchain is Valid: " + isChainValid()); // 检验区块链的完整性
    
            String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain); 
            System.out.println("\nThe block chain: ");
            System.out.println(blockchainJson);
        }
    
        // ... 其他代码省略
    }
    

    个人想法

    如果某人想篡改区块链中已经确定好的区块,那么这个人就必须得赶在其他所有人的前头,把他自己节点上的那条区块链变得更长,他所需要的算力必须比其他人加起来还要多,不然其他人中就会出现至少一个人把未经篡改的区块链变长,继而把他那条篡改过的区块链淘汰。

  • 相关阅读:
    【调侃】IOC前世今生
    经典 makefile 教程
    Win7  CMD大全
    关于验证码识别3
    关于验证码识别2
    关于验证码识别 1
    DirectUI的初步分析-转
    winform软件版本检测自动升级开发流程(转)
    如何让在panel里的子窗体随panel的大小改变而变化?(转)
    C#如何控制panel加载的字窗体中控件的位置随着显示器分辨率大小而改变
  • 原文地址:https://www.cnblogs.com/bacmive/p/16299093.html
Copyright © 2020-2023  润新知