阅读对象

本文阅读对象,主要是希望和即将从事区块链开发的项目架构师,开发工程师,项目设计或产品设计师。要求阅读者具备一定的“区块链”基础知识、概念和以及相关的技术知识。

如果你只需要对区块链应用做一个更深更直观的了解,通过本文的例子更清晰了解区块链是怎么回事,大概是怎么开发出来的,怎么使用,那么知道一些区块链相关知识即可。

如果你是一个想从事这方面工作的人,尤其是从事开发和架构的技术人员,那么需要的技术知识就相当的多了,无法一一列举,大概一个高级网络开发工程师和中级以上的架构师水平,是需要的。

 

前言

很多新的概念,人们在刚接触的时候,会让人感到非常困惑,学了好多次都稀里糊涂,并不仅仅是技术,很多地方都是如此。对于比较难理解的概念,用什么办法去学习最高效呢?我一直认为,就是找一个例子,或者写一个Demo,世上无难事,for一个example即可。。。

 

最近一段时间,AI人工智能方兴未艾,区块链技术又迎面走来,对数字货币的崛起感到新奇的我们,估计很想知道其背后的技术--区块链是怎样的一个东西。但是完全搞懂区块链并非易事,在实践中学习方为上策。

 

我喜欢把程序当成诗歌来写,理所当然要通过写代码来实现并运行一个例子,来理解并学习这门技术。这里,就通过用JAVA语言实现和构建一个区块链来探讨对区块链的理解。

 

本文,我们要用区块链来实现这么一个例子,名字叫“区块链成语接龙”。用户通过这个例子,可以不断的接龙前面一个用户的成语,规则和普通游戏一样,前面一个用户的成语的最后一个字,作为后面一个成语的第一个字。

 

单单作为一个应用,这个很简单呐,问题是,我们要用区块链的原理和相关技术实现它。

 

OKlet’s go… …

 

准备工作

这个时候其实我很想大喊一声:“区块链”其实不是某一种单纯的技术,而是基于某一种思想的多种技术的结合。

需要的技术包括分布式存储,分布式计算,P2P数据同步,加密解密,安全传输,一些语言,一些开发工具。。。还有一些新型的概念分布式节点,工作量证明,共识算法。。。等等

基于哪一种思想呢,大家都说是“去中心化”,我觉得就是“反垄断”“反。。。”,敏感话题,不展开了。但是我事实上是一个喜欢民主的人,所以区块链的“去中心化”对我吸引力颇大。。。all men are created equal…J

 

本文里面涉及到的一些例子,来源是网上,有些是Python版本,有些是Java版本,C版本,考虑到JAVA更为通用和易读写(其实我是认为JAVA语言更像诗歌了),就把他们改成了JAVA版,所以要求读者对诗歌..哦,不是,是对JAVA非常了解,能读写基本的语法框架和逻辑,并且因为这个DEMO的特殊性,需要对网络框架和HTTP请求有基本的了解。

 

我们知道区块链是数据块和链的存储方式的组合,是由N多个数据区块按照链的组织和记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,同时它们是通过哈希值(hashes)链接起来的。所以,在阅读本文之前,一定要阅读几篇关于区块链的文章,了解里面块,链,数据,工作量证明等等概念,最好也了解一些相关技术比如Hash,分布式存储等等的概念。如果你还不是很了解这些,请找度娘。

        

         如果在了解这些概念的过程中,一不小心接触到了比特币,请一定要明白,比特币只是区块链技术的一个产品实现。如果还接触到其它一些诸如以太网等等的名词,那么需要知道,这些都是区块链技术下实现的一些框架和产品。

 

 

环境准备

理论上来说,可以用任何一种语言来创建任何一种技术的例子,包括区块链的例子,我们这里选用了JAVA,所以要在自己的电脑上,准备JAVA的相关开发环境。

 

确保你的电脑上已经安装了较新版本的JDKTomcat和某一种你熟悉的最好能整合TomcatJAVA IDE,本例用的是EclipseTomcat在我们这个例子中间,是一个WEB工具,因为我们的项目,需要基于WEB  HTTP 发布和运行。

 

如何安装Java和各种工具,包括如何用Eclipse创建Dynamic web项目并发布到Tomcat等等步骤,既然你已经如此熟悉JAVA了,这里就不再多讲。

 

 

开始创建区块链

 

区块或称数据块Block

区块链中每个区块包含以下基本内容:索引(index),Unix时间戳(timestamp),数据块(data)(包括交易,文字,申明,奖励等任何和合适的内容),证明或工作量证明(proof稍后解释)以及前一个区块的Hash值,Hash 用来链接数据块,同时确保数据块不被非法修改。

以下是一个区块的结构:

public class Block {

 

   int iIndex;              //索引

   String sProof;           //工作量证明,在这个例子里面,其实就是一个经过验证的正确的成语

   String sPreviousHash;    //前一个区块的Hash

   Timestamp tsCreateTime//区块创建时间戳

  

  

   /*数据块

    *

    * 用户每接上一个成语,会得到系统10元钱的奖励,同时会赢得前面一个用户的2元钱

    * 数据区同时需要记录自己的用户名和回答出上一个成语的用户名

    *

    * */

   String sSender;           //回答出上一个成语的用户名

   String sRecipient;        //回答出当前这个成语的用户名

   final int iMoneyAward=10; //系统奖励,数额固定

   final int iMoneyWin=2;    //赢取奖励,数额固定

  

  

   public Block(){

     

   }

  

  

}

 

 

区块链实现Blockchain

 

import java.util.*;

import blockchain.Block;

 

public class BlockChain {

   //用来存储区块

private List<Block> lBlockchain=new ArrayList<>[];   

        

  

   public BlockChain(){

     

   }

  

  

   //创建新块

   public Block NewBlock(){

      Block bRet=null;

     

      //在这里创建一个新块

     

      return bRet;

   }

  

   //Hash 一个块

   public String Hash(Block block){

      String sHash=null;

     

      //在这里Hash 一个块

     

      return sHash;

   }

  

   //其他方法

   //....

}

 

 

 

Blockchain类用来管理链条,它能存储和更新链数据,加入新块等等,下面我们来进一步增加和完善里面的一些方法

 

创建新块和创世块

 

当一个用户按照成语接龙的规则,对上上一个成语,并且系统验证这成语正确(工作量被证明)。这个时候我们就可以创建一个新块,并且加到链里面。

 

一旦工作量证明确认,并且上一个块hash 生成后,就可以简单调用函数创建一个新块了。

 

创建新块方法如下:

 

 

   //创建新块

   public Block NewBlock(int i,String proof,String hash,Timestamp c,String sender,String recipient){

      Block bRet=null;

     

      //在这里创建一个新块

      bRet = new Block(i,proof,hash,c,sender,recipient);

     

      return bRet;

   }

 

 

这里,我们需要提一下创世块的概念,创世块是区块链的第一个区块,它是没有前区块的。逻辑上只在第一个用户第一次启动系统的时候,才需要创建创世块,后面的都是通过同步获得。创世块索引是0,同样需要给它加上一个工作量证明,我们这里就是初始成语“海阔天空”,因为没有前面的块,所以hash=””,同时给一个固定的创建时间。方法如下:

 

   //创始块的创建,创世块是一个块,必须是固定的信息

   //逻辑上来说,只有在区块链产品的第一个用户第一次启动的时候,才会需要创建创世块

   public Block CreateFirstBlock(){

      try{

         Timestamp t=new Timestamp((new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).parse("2018-01-01 01:01:01").getTime());

         return NewBlock(0,"海阔天空","",t,"","");

      }catch(Exception e){

         return null;

      }

   }

 

 

 

这里顺便贴上生成块的Hash字符串的方法:

   //Hash 一个块

   public static String Hash(Block block){

      String sHash=null;

     

      //在这里Hash 一个块

      String s=block.sPreviousHash+block.sProof+block.sRecipient+block.sSender+block.tsCreateTime.toString();

     

      sHash = MD5(s);

      

      return sHash;

   }

  

   public static String MD5(String key) {

        char hexDigits[] = {

                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'

        };

        try {

            byte[] btInput = key.getBytes();

            // 获得MD5摘要算法的 MessageDigest 对象

            java.security.MessageDigest mdInst = java.security.MessageDigest.getInstance("MD5");

            // 使用指定的字节更新摘要

            mdInst.update(btInput);

            // 获得密文

            byte[] md = mdInst.digest();

            // 把密文转换成十六进制的字符串形式

            int j = md.length;

            char str[] = new char[j * 2];

            int k = 0;

            for (int i = 0; i < j; i++) {

                byte byte0 = md[i];

                str[k++] = hexDigits[byte0 >>> 4 & 0xf];

                str[k++] = hexDigits[byte0 & 0xf];

            }

            return new String(str);

        } catch (Exception e) {

            return null;

        }

    }

 

 

 

理解工作量证明

 

新的区块生成,必须依赖工作量证明算法(PoW)来构造。

 

PoW的目标是找出一个能被公认的方法,来证明你所从事工作的正确性。

 

在当前区块链应用中,PoW经常是被设计成通过寻找某一个符合特定条件的数字来证明,这个数字可能很难计算出来,但很容易验证

 

这就是工作量证明的核心思想。

为了方便理解,举个例子:

假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以两个零 ‘00’ 结尾,即 hash(x * y) = xxxxxx...00。设变量 x = 5,求 y 的值?

Java实现如下:

      int x=5;

      int y=0;

      while(true){

         String md5=BlockChain.MD5(""+(x*y));

         if(md5.charAt(md5.length()-1)=='0'){

            break;

         }

         y+=1;

      }

 

最后得出 y=69

因为MD5(5*69)=D81F9C1BE2E08964BF9F24B15F0E4900

 

随着区块链应用的扩展,应该有更多合理的工作量证明算法。

 

在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果,甚至动用N台电脑,这就是称之为挖矿。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。当然,一旦计算出来,会非常容易验证这个结果。

 

实现我们的工作量证明

 

我们的工作量证明,那就比较简单,当用户输入下一个成语的时候,我们打开成语词典或者百度,一查。。。。就可以知道它接的对还是错了。

 

从系统上来说,我们可以通过调用外部开发查询接口,或者自带成语词库,来实现这个工作量证明,代码如下(此处引用了一个外部成语真实性查询接口):

 

   //验证当前的成语是否符合规则

   //pre 前一个成语

   //cur 这一个成语

   public static boolean ValidProof(String pre,String cur){

     

      //验证这个成语的头一个字是不是上一个成语的最后一个字

      if(cur.charAt(0)!=pre.charAt(pre.length()-1)){

         return false;

      }

     

      //验证是否是成语

      //http://chengyu.t086.com/chaxun.php?q=%B9%E2%C3%F7%D5%FD%B4%F3&t=ChengYu

      String content=httpRequest("http://chengyu.t086.com/chaxun.php?q="+cur+"&t=ChengYu");

      if(content.indexOf("没有找到与您搜索相关的成语")!=-1 || content.indexOf("搜索词太长")!=-1){

         return false;

      }

     

      return true;

   }

 

 

 

 

小结:通过上面的讲解,描述了区块链涉及到的一些新概念,区块,创世块,链式存储,工作量证明,主数据形式和内容,Hash加密等等。同时我们通过“成语接龙”的例子,在例子中用Java构造了相关的类和方法。

 

具备了区块链项目的基础组件后,下一步就是将这些组件运行起来,构造一个可以真正运行起来的区块链项目。

 

 

启动一个Blockchain项目


blob.png


 

我们都知道,区块链是一个分布式存储和分布式计算互相结合的技术和产品框架。其核心是去中心化存储。这个意味着,所有区块链的数据都应该存储在用户本地而不是某个中心服务器。所以,一个真正的区块链产品,需要在没有中心服务器支持情况下,解决如下几个基本的问题:

1、  每个用户端启动区块链产品时,如何将区块数据的初始下载到本地;

2、  新增加区块时,区块链内容的更新发布以及不同用户新增区块时间冲突的处理;

3、  日常运行过程中如何保证每个用户端(节点)数据的最新和及时同步;

 

 

创建节点

        

         区块链项目中,一个正在运行着区块链项目的用户终端,称为一个节点。

 

         每个用户第一次启动某个区块链项目,就相当于创建了一个节点。而以后每次启动项目,都意味着启动了这个节点。

 

         为什么说是创建一个节点?原因很简单,区块链的基本思想是去中心化,去中心化的换一种说法,中间任何一个终端,都可以是中心,各自是对等的组网模型,也就是P2P组网模型。因为任何启动的终端,除了需要完成数据下载,使用,运算之外,还需要完成对外提供数据,同步,验证等等功能。

 

区块链网络系统之所以选择P2P作为其组网模型,这是由于二者思想的契合度确定的;区块链的根本出发点之一是去中心化,中本聪在他的白皮书里,提到电子现金系统中,第三方系统是多余的,没有价值,意思就是整个系统不要依赖任何特殊的第三方来完成自身系统的运转;而P2P网络的天然属性,就是全网节点平等,无特殊节点;由于区块链和对等网络的建设思想,高度契合,再加上P2P网络已经是一个发展成熟的网络技术;二者走到一起,几乎是一种必然。

        

         所以我们在这个例子里面引入Tomcat,一方面是为了将一些用户操作转换到浏览器,演示方便,另外一方面,是为了方便发布一些对外接口,用来模拟在P2P对等网络之间数据同步。

 

首先我们使用Eclipse建立一个动态WEB 项目,将上面相关类导入,同时新建一个类似下面的 index.jsp 欢迎页面

 

<%@ page language="java" contentType="text/html; charset=gb2312"

    pageEncoding="gb2312"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>区块链测试</title>

</head>

<body>

 

<% java.util.Date d = new java.util.Date(); %>

<h1>

欢迎使用区块链成语接龙。 <br>

</h1>

 

区块数据同步中,请稍等。。。<br>

 

<%= d.toString() %> !

 

</body>

</html>

 

发布项目到你安装的Tomcat,启动Tomcat,输入如下地址,当看到如下图所示页面,即创建区块链项目“成语接龙”的节点准备工作已经完成。

 

blob.png


 

启动区块链应用,创建节点,包括以后每次启动应用,都需要完成的第一个工作,是同步区块链数据到本地或者更新本地区块数据。碰到的第一个问题是,我怎么知道到哪里去同步数据呢,或者说我怎么知道其它可以同步数据的节点的地址呢?

        

         找遍百度,很多都说的很模糊,大致是需要 node-seed 也就是种子节点支持,最后找到了区块链师祖比特币的开发者的一段说明如下:

blob.png



翻译成中文,简而言之就是在代码里面写死了一个域名数组和IP地址数组,程序启动的时候就去连这些域名和IP。

 

         当然,觉得这种方法可以解决问题,但是,这或多或少使得“去中心化”蒙上了一层淡淡的阴影。

 

那么根据我这个例子的模拟环境,我就把公司局域网可能的IP hard-code 在程序里,逐个扫描查找最长链,作为下载区块链数据的扫描地址吧。下面是Java代码:

 

   static final String  sIPPre="10.70.5.";                

//对局域网内的电脑进行扫描,找到最长的链,下载到本地

 

   static final String  sDataFileDir="c://blockchain";     //本地存储路径

  

   //从网络读取区块链数据到本地文件

   public static void DowloadData(){

 

      //检查数据文件目录,不存在就创建

        File dirFile = new File(sDataFileDir);

        boolean bFile   = dirFile.exists();

      if(!bFile ){

         bFile = dirFile.mkdir();

         //往新创建的本地文件里面写一个创世块

         try{

            FileOutputStream out = new FileOutputStream(new File(dirFile+"//data.txt"));

            out.write((BlockChain.CreateFirstBlock().toInfoString()+"\r\n").getBytes());

            out.close();

         }catch(Exception e){ 

         }

      }

     

     

      //扫描周边的节点,找到最长的链,下载到本地

      int iLastLen=0;

      String sLastChain="";

      for(int i=0;i<255;i+=1){

         String sThisURL="http://"+sIPPre+i+":8080/blockchain/chain.jsp";

        

         System.out.println(sThisURL);

        

         String sChain=httpRequest(sThisURL);

        

         if(sChain!=""){

            System.out.println(sChain);

            String sTemp[]=sChain.split("##");

            if(sTemp.length>iLastLen){

                iLastLen=sTemp.length;

                sLastChain=sChain;

            }

         }

      }

     

      try{

         if(sLastChain!=""){

            FileOutputStream out = new FileOutputStream(new File(dirFile+"//data.txt"));

            //System.out.println("before:"+sLastChain);

            sLastChain=sLastChain.replace("##", "\r\n");

            //System.out.println("after:"+sLastChain);

            out.write((sLastChain+"\r\n").getBytes());

            out.close();

         }

      }catch(Exception e){ 

      }

     

   }

 

 

DowloadData()这个方法,应该在应用启动的时候被调用。也可能在应用执行过程中,后台间隔性被调用。

 

你可能马上会发现,多了一个外部接口:

http://"+IP+":8080/blockchain/chain.jsp

 

这个接口是每个节点都需要实现的,用来输出自己节点最新的区块链数据,在平时各个节点彼此互相调用,初始化和同步区块链数据。在本例中,简单实现方法如下:

 

建立一个 chain.jsp 页面,放到Tomcat 路径下,贴上里面主要代码

 

<%@ page language="java" contentType="text/html; charset=gb2312"

    pageEncoding="gb2312"%><%

//java.util.Date d = new java.util.Date();

blockchain.BlockChain.LoadData();

%>

<%= blockchain.BlockChain.StringBlockchain() %>

 

 

用户怎么挖矿

到了这一步,可以把我们例子程序稍微完善一下,为后面的“挖矿”概念做准备。

 

完善后的产品界面如下

 

blob.png


 

启动应用,首先是一个数据同步的欢迎页面。此页面用ajax方式实现后台数据同步。

同步完成后,输入手机号码,进入下一个页面。


blob.png


blob.png

注意页面下方,增加了一个自动更新区块链功能,也通过ajax实现。


blob.png


 

所谓“挖矿”和“矿工”,只不过是借用了比特币里面的相关说法。“矿工”就是用户了,“挖矿”就是用户在这个区块链应用上面做的事情,一般带有一定的价值和目的,目前很多区块链应用,“挖矿”就是完成某个工作,获得系统一些奖励。

在我们这个演示应用里面,挖矿就是用户根据上一个成语,遵守成语接龙的规则,想出并输入下一个成语,系统验证无误,就会为你在区块链增加一条记录,同时把奖励也给你记录。

其实把名字取得那么神奇,事实却很简单,系统就是做了以下三件事:

1、计算工作量证明PoW,验证用户递交的工作;

2、对于“挖矿”成功的用户,通过新增一个区块授予矿工即用户一些分数或者虚拟币奖励;

3、构造新区块并将其添加到链中,同步到每个节点。

 

代码如下:

if(request.getParameter("answer")!=null){   //读取到用户的输入

  

   //加载区块链数据

   blockchain.BlockChain.LoadData();

  

   //获取区块链中最后一个成语

   String sPre=blockchain.BlockChain.lBlockchain.get(blockchain.BlockChain.lBlockchain.size()-1).sProof;

  

   //用户输入的成语

   String sCur=new String(request.getParameter("answer").getBytes("ISO8859-1"),"gb2312");

  

   //判断是不是正确答案,如果是,就要创建新块并添加到区块链中

   if(blockchain.BlockChain.ValidProof(sPre,sCur)){

     

      blockchain.Block bPre=blockchain.BlockChain.lBlockchain.get(blockchain.BlockChain.lBlockchain.size()-1);

     

      //获取前一个块的Hash

      String sHash=blockchain.BlockChain.Hash(bPre);

     

      //创建新块

      blockchain.Block bCur=blockchain.BlockChain.NewBlock(bPre.iIndex+1, sCur, sHash, new java.sql.Timestamp(System.currentTimeMillis()), bPre.sRecipient, (String)session.getAttribute("mobile"));

     

      //加入区块链并保存到本地文件

      blockchain.BlockChain.lBlockchain.add(bCur);

      blockchain.BlockChain.WriteData();

      。。。

   }else{

         。。。。

   }

}

 

下面是经过几个接龙后的页面:


blob.png



这是可以打开本地区块链数据文件 c:/blockchain/data.txt”可以看到如下内容,为了查看简单,数据文件用明文txt存储。



blob.png


其中第一条,是创世块的数据,在区块链中,它是根区块。

在实际项目中,数据文件一般都会以某种较复杂的方式存储,比如采用某种加密方式。

 

 

节点数据一致性(共识算法/一致性算法)

到目前为止我们已经有了一个基本的区块链产品和界面,并且可以做简单的“挖矿”,在应用启动的时候,我们也看到了第一次数据同步和后面每隔一段时间数据同步的实现方法,在一个节点上运行没有问题。

但是,区块链系统应该是分布式的。既然是分布式的,那么我们就需要考虑究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有很多个节点并且他们的数据保持一致,那就必须实现一个一致性的算法。

 

实现共识算法(一致性算法)

 

单个节点分布在不同的计算机或手机等等终端,各自工作,必然有一些冲突产生。冲突的产生,主要来自两方面,一是链长的不一致,二是链分叉的产生。

 

用本例来说明。第一种情况是,每个节点同步后,各自做各自的题目,各自的链数据都在扩展,导致每个人的链长度不一致。还有一种情况是,在某个区块下面,存在不同的都正确的答案,那么,即使长度一致,但链的内容不一致。比如在本例中“海阔天空”后面可以接的成语有很多,比如“空中楼阁”、“空前绝后”、“空口无凭”,他们都是正确的。这种情况下,系统必须定义一个唯一性的算法。

 

解决上面问题的算法,称之为“共识算法”或“一致性算法”。

 

通过上面的描述,我们知道“共识算法”其实是解决不同用户(或称不同节点)在完成被“工作量证明”算法承认的工作之后,如何公平合理的奖励问题。他必须合理,体现公平、公正。在不同的区块链应用中,在不同的“工作量证明”基础下,其实现方法是不同的,在基于某些复杂的“工作量证明”的区块链应用中,可以想象,这些复杂的“工作量证明”,会导致需要非常复杂的“共识算法”去解决冲突。

 

如果每个节点在增加一个新区块的时候,都可以实时同步到每个节点,并且在同步过程中,能锁定其它节点,当然这是最理想的。但事实上,在现实场景中,这很难做到。网上有很多共识性算法讨论,大家可以查询参考,不过很多都存在一个问题,需要中心节点支持,我觉得这个是“去中心化”不可接受的,所以非常不以为然。

 

就本例子而言,可以设计一个相对简单的“共识算法”,我们称之为最长链法,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。具体实现如下:

 

当一个用户完成一个成语接龙并被系统判断答案正确的时候,他将计划新增一个区块,并且希望添加到区块链中,但这之前,系统会先去扫描当前所有节点目前的链,如果发现存在长度比当前节点加一还要长的链,那么系统认为,当前节点虽然完成了题目,但已经不是最快的那个人了,因此不再承认这次工作的有效性。并且同时同步最长链到本地,提示用户重新做成语接龙。

 

这个算法在本例中,理论上是合理的,因为不能说一个用户花了一分钟就回答出来的答案,另外一个花了一天才回答出来,当然后面的就不能说有效了。没有更仔细的想过这个“共识算法”的合理性,作为演示,暂时就那么用一下了。下面说一下在本例中实现需要的两个材料:

 

1、  每个节点需要一个能被扫描后提供它自己的区块链数据的接口,这个其实接口已经有了,就是http:/…/blockchain/chain.jsp,回顾前面,在启动节点的时候用到过;

2、  实现一个共识算法方法,在每次“挖矿”完成后,判断工作的有效性。

 

共识算法如下:

 

   //共识算法

   //返回 true 表示当前节点工作可以被认可

   public static boolean Unanimous(){

      boolean bRet=true;

     

      //扫描周边的节点,找到最长的链,下载到本地

      int iLastLen=0;

      String sLastChain="";

      for(int i=0;i<255;i+=1){

         String sThisURL="http://"+sIPPre+i+":8080/blockchain/chain.jsp";

        

         System.out.println(sThisURL);

        

         String sChain=httpRequest(sThisURL);

        

         if(sChain!=""){

            System.out.println(sChain);

            String sTemp[]=sChain.split("##");

            if(sTemp.length>iLastLen){

                iLastLen=sTemp.length;

                sLastChain=sChain;

            }

         }

      }

     

      BlockChain.LoadData();

      try{

         //如果其它节点存在长度大于本地节+1的链,则本次挖矿无效

         if(sLastChain!="" && iLastLen >= BlockChain.lBlockchain.size()+1){

            bRet=false;

            FileOutputStream out = new FileOutputStream(new File(sDataFileDir+"//data.txt"));

            sLastChain=sLastChain.replace("##", "\r\n");

            out.write((sLastChain+"\r\n").getBytes());

            out.close();

         }

      }catch(Exception e){ 

      }

      return bRet;

   }

 

 

加入“共识算法”后的界面变成如下:



blob.png



当用户递交一个成语后,系统会通过Ajax首先检测区块链的一致性,一致性通过了,用户递交的这个新的成语,才会加入到区块链。如果发现有更长的链条存在,则系统会提示你慢了一步,已经被抢答了,必须重新开始。

页面大致如下:


blob.png



发布和运行区块链成语接龙

 

到这里,这个“区块链成语接龙”游戏就基本搭建和测试完成。

 

接下来的事情,你可以将此项目打包war,然后发布到你所在局域网不同的节点的机器上,因为环境的支持问题,所以节点必须有Windows+JDK+Tomcat运行环境,将项目打包成 blockchain.war,放到你的Tomcat目录 webapps下面,然后启动Tomcat,打开你的浏览器,输入:http://localhost:8080/blockchain/

过程不再累述。如果局域网的环境和本地目录结构有所改变,则需要对项目的部分地方稍作修改,比如:

                  

//对局域网内的电脑进行扫描,找到最长的链,下载到本地

static final String  sIPPre="10.70.5.";

//本地存储路径

   static final String  sDataFileDir="c://blockchain";    




好啦,你可以邀请你的同事和朋友们一起来测试你的区块链成语接龙!

 

 

总结:

 

通过搭建一个区块链项目,我们可以了解一个区块链项目启动所需要的所有知识,环境和其基本算法所代表的概念,在例子中,我们一一讲解并且实现,我们可以非常具体地体会到“去中心化”的真实含义。事实上,可以在测试过程中,加入很多终端,然后,关闭中间任何一台终端,看这个产品是否还是可以正常运行。

 

         虽然如此,在这个过程中,也可以看到有些问题并没有很好和彻底的解决方案,区块链作为一个新起的概念和技术,这个当然也可以理解,同样,这个可以作为我们研究人员进一步仔细考虑的点。

 

1、 关于种子节点的问题

在任何一个节点启动的时候,如何获得其它节点的位置信息,从而下载和初始化区块链呢?种子节点的存在,的确可以解决这个问题,但种子节点本身,就带有“中心化”这个概念,所以,如果没有种子节点会更加好,但如何实现呢?

 

2、 关于“工作量证明”

一个技术是否能普及和商业化,或者说对用户对社会是否有真正的价值,就在于它能不能解决我们具体生活场景的问题。区块链里面具体所做的事情,都在“工作量证明”里面,当前很多区块链项目,比如“比特币”,都是用一个计算机化的方式,来描述和做某一种工作,但这类工作的意义究竟有多大,或者其适用性有多大,真的需要思考。这种方式用来产生“虚拟币”是不错,但“虚拟币”有真的价值么?还有就是,真的所有工作,都可以不需要“中心节点”支持么?所以,设计更多的符合人们生活场景的“工作量证明”算法,将大大拓展这个技术的前景。

 

3、 关于“共识算法”

“共识算法”和“工作量证明”戚戚相关,但所有“共识算法”都需要考虑解决起码两个问题,当各个节点独自运作的时候,不可避免会产生每个节点区块链不同步的问题,不同步可能在时间上,也可能在“工作量证明”的多重结果上,这两种,需要最终达成共识,使得系统中始终只有一条正确的链,应该说是相当困难。网络有延迟,终端有差异,不同的结果都合理,怎么公平取舍。。。等等。实在不是那么简单的。

 

4、  关于巨量区块链数据同步速度

即使解决了上面123 提出的问题,也存在数据同步速度问题。

 

理论上来说,区块链每个节点,应该是数据的全备份。在我们这个例子中,同步数据量比较少,同步节点也不多,所以每次同步时间很短。但是在商用系统中,如果出现大量的节点,大量的数据,这个同步肯定会有时间。一个是节点存储容量问题,一个是同步带宽问题,两者都是不可预料的。目前基于区块链的智能合约开发框架以太访(Ethereum)需要同步的数据已经接近了惊人的100G,还在不断增长,我下载安装一个同步了一个星期。在如此大的数据两下,简单的同步和拷贝,肯定是不合理的,既会带来巨大的资源浪费,也会带来应用的可使用性及差。所以这里面需要解决的问题还很多。

 

 

以上这些,任何一点,仔细研究都是一个巨大的课题,做一个简单那的区块链项目不难,要做好并且做成社会真正有用而非炒作的产品,估计还有很远的路,具体想来都是需要好好解决的问题。让我们一起努力吧。