• 【原】用Java编写第一个区块链(一)


      写这篇随笔主要是尝试帮助自己了解如何学习区块链技术开发。

     【本文禁止任何形式的全文粘贴式转载,本文来自 zacky31 的随笔】

    目标:

    • 创建一个最基本的“区块链”
    • 实现一个简单的挖矿系统

    前提:

      对面向对象编程有一定的基础

    注意:

      值得注意的是,这不会是一个完整的功能,恰恰相反,这是一个概念证明的实例,可以帮助您进一步了解区块链。

    准备:

      我将会使用Java来实现,当然你也可以使用任何面向对象的语言。

    环境:

    • JDK 8
    • IDEA
    • Maven

    开始吧

      区块链就好比多个块连接起来。其中每一块都将拥有自己的签名,签名中包含其前面的块信息和一些数据(例如交易信息)。

      每个块不仅仅包含它之前的块信息,同时也包含自身。如果前面一块内容改变了,其 hash 值也会改变,将会导致其后面所有的块发生变化。通过计算和比较所得的 hash 值,我们可以判断区块链是否合法。换句话说,改变区块链中的任意内容,将会改变整个区块链的签名。

      根据上面的分析,我们先创建一个 Block 类。

    import java.util.Date;
    
    public class Block {
        public String hash; //存放数字签名
        public String preHash; //前面块的签名
        private String data;
        private long timeStamp;
    
        public Block(String data, String preHash) {
            this.data = data;
            this.preHash = preHash;
            this.timeStamp = new Date().getTime();
        }
    }

      接下来,我们需要一个生成签名的方法。有很多加密算法可供选择,这里使用 SHA256 刚刚好。

    import java.security.MessageDigest;
    
    public class StringUtil {
        public static String applySha256(String input) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
    
                byte[] hash = digest.digest(input.getBytes("UTF-8"));
                StringBuilder hexString = new StringBuilder();
                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 类中添加计算数字签名的方法,并修改一下其构造方法。

    public Block(String data, String preHash) {
            this.data = data;
            this.preHash = preHash;
            this.timeStamp = new Date().getTime();
            this.hash = calculateHash();
        }
    
        public String calculateHash() {
            String calculatedhash = StringUtil.applySha256(preHash + Long.toString(timeStamp) + data);
            return calculatedhash;
        }

      到这里,可以写个 Main 方法看一下效果。

    public class Main {
    
        public static void main(String[] args) {
            Block first = new Block("Hi i am the first block", "0");
            System.out.println("Hash for block 1 : " + first.hash);
            Block second = new Block("Hi i am the second block", first.hash);
            System.out.println("Hash for block 2 : " + second.hash);
            Block third = new Block("Hi i am the third block", second.hash);
            System.out.println("Hash for block 3 : " + third.hash);
        }
    }

      可以看见每个 Block 都有自己唯一的 数字签名,当然,现在还没有构成一个区块链,将这些块存放到一个 ArrayList 中吧。修改 Main 类后再次运行。

    import com.google.gson.GsonBuilder;
    
    import java.util.ArrayList;
    
    public class Main {
    
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
    
        public static void main(String[] args) {
            blockchain.add(new Block("Hi i am the first block", "0"));
            blockchain.add(new Block("Hi i am the second block", blockchain.get(blockchain.size() - 1).hash));
            blockchain.add(new Block("Hi i am the third block", blockchain.get(blockchain.size() - 1).hash));
    
            String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
            System.out.println(blockchainJson);
        }
    }

      现在,需要一种方法去验证创建的区块链。编写一段 isChainValid() 方法。任何块的改变将会导致这个方法失效。

    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);
    
                if (!currentBlock.hash.equals(currentBlock.calculateHash())) {
                    System.out.println("Current Hashes not equal!");
                    return false;
                }
    
                if (!previousBlock.hash.equals(currentBlock.preHash)) {
                    System.out.println("Previous Hashes not equal!");
                    return false
                }
            }
            return true;
        }

      接下来,尝试一下挖矿!

      

      在 Block 类中,新增一个变量 nonce,并且添加到 calculateHash() 这个方法中,同时需要 mineBlock() 这个方法。这个方法中的 difficulty 变量就是用来控制计算量的。当设置的值较低时,大部分计算机很快就能算出来。

    import java.util.Date;
    
    public class Block {
        public String hash;
        public String preHash;
        private String data;
        private long timeStamp;
        private int nonce;
    
        public Block(String data, String preHash) {
            this.data = data;
            this.preHash = preHash;
            this.timeStamp = new Date().getTime();
            this.hash = calculateHash();
        }
    
        public String calculateHash() {
            String calculatedhash = StringUtil.applySha256(preHash + Long.toString(timeStamp) + Integer.toString(nonce) +
                    data);
            return calculatedhash;
        }
    
        public void mineBlock(int difficulty) {
            String target = new String(new char[difficulty]).replace('', '0');
            while (!hash.substring(0, difficulty).equals(target)) {
                nonce++;
                hash = calculateHash();
            }
            System.out.println("Block Mined!!!" + hash);
        }
    }
    View Code

      我们可以在 Main 类中定义个静态变量。尝试在每次创建新块去调用 mineBlock() 方法。

    import com.google.gson.GsonBuilder;
    
    import java.util.ArrayList;
    import java.util.Date;
    
    public class Main {
    
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
        public static int difficulty = 5;
    
        public static void main(String[] args) {
            long beginTime1 = new Date().getTime();
            blockchain.add(new Block("Hi i am the first block", "0"));
            System.out.println("Trying to  mine block 1...");
            blockchain.get(0).mineBlock(difficulty);
            long endTime1 = new Date().getTime();
            System.out.println("Mining block 1 cost " + (endTime1 - beginTime1));
    
    
            long beginTime2 = new Date().getTime();
            blockchain.add(new Block("Hi i am the second block", blockchain.get(blockchain.size() - 1).hash));
            System.out.println("Trying to  mine block 2...");
            blockchain.get(1).mineBlock(difficulty);
            long endTime2 = new Date().getTime();
            System.out.println("Mining block 1 cost " + (endTime2 - beginTime2));
    
            long beginTime3 = new Date().getTime();
            blockchain.add(new Block("Hi i am the third block", blockchain.get(blockchain.size() - 1).hash));
            System.out.println("Trying to  mine block 3...");
            blockchain.get(2).mineBlock(difficulty);
            long endTime3 = new Date().getTime();
            System.out.println("Mining block 1 cost " + (endTime3 - beginTime3));
    
            System.out.println("
    Blockchain is Valid: " + isChainValid());
    
            String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
            System.out.println(blockchainJson);
        }
    
        public static Boolean isChainValid() {
            Block currentBlock;
            Block previousBlock;
            String hashTarget = new String(new char[difficulty]).replace('', '0');
    
            for (int i = 1; i < blockchain.size(); i++) {
                currentBlock = blockchain.get(i);
                previousBlock = blockchain.get(i - 1);
    
                if (!currentBlock.hash.equals(currentBlock.calculateHash())) {
                    System.out.println("Current Hashes not equal!");
                    return false;
                }
    
                if (!previousBlock.hash.equals(currentBlock.preHash)) {
                    System.out.println("Previous Hashes not equal!");
                    return false;
                }
    
                if (!currentBlock.hash.substring(0, difficulty).equals(hashTarget)) {
                    System.out.println("This block hasn't been mined");
                    return false;
                }
            }
            return true;
        }
    }

      运行发现,挖矿过程还是很费时间的。把计算量改成7,差不多每挖一个需要一分钟。。。

      如果在此过程中,有人篡改了数据,将会导致:

    • 区块链将会无效
    • 不能够创建一个更长的区块链
    • 网络中的诚实链将会比较长的区块链有时间上的优势

      不过如果篡改数据拥有更强的运算速度,可能成功篡改。

      这样,基本上简单实现了一个区块链了。

           

  • 相关阅读:
    git fetch 和git pull 的差别
    解决npm install安装慢的问题
    Git本地分支和远程分支关联
    phalapi 2.14 使用(一)增加顶级命名空间、调整返回结构字段
    vue-element-template实战(五) 获取后端路由表动态生成权限
    vue-element-template实战(四)使用mock数据,新增用户管理界面
    使用phalapi 2.14版本的问题及解决办法
    关于vue
    git详细操作
    三次握手四次挥手理解
  • 原文地址:https://www.cnblogs.com/zacky31/p/9057193.html
Copyright © 2020-2023  润新知