120行左右的java程序_只用120行Java代码写一个自己的区块链-2网络

已经看完第一章的内容了吗,欢迎回来。

上一章我们介绍了关于怎么去编写自己的区块链,完成哈希和新块的校验。但是它只是在一个终端(结点)上跑。我们怎么样来连接其他结点以及贡献新的块呢,怎么样广播到其他结点告诉他们要更新区块了呢?

本章就是要告诉你这些。

// 区块链的核心部分

// 维护一个在启动时可以连接的对等节点列表。当一个完整的节点第一次启动时,它必须被自举(bootstrapped)到网络。

// 自举过程完成后,节点向其对等节点发送一个包含其自身IP地址的addr消息。其对等的每个节点向它们自己的对等节点转发这个信息,以便进一步扩大连接池。

// 块广播

// 在与对等节点建立连接后,双方互发包含最新块哈希值的getblocks消息。

// 如果某个节点坚信其拥有最新的块信息或者有更长的链,它将发送一个inv消息(invite),其中包含至多500个最新块的哈希值,以此来表明它的链更长。

// 收到的节点使用getdata来请求块的详细信息,而远程的节点通过命令block来发送这些信息。

// 在500个块的信息被处理完之后,节点可以通过getblocks请求更多的块信息。这些块在被接收节点认证之后得到确认。

// 新块的确认也可通过矿工挖到并发布的块来发现。其扩散过程和上述类似。

// 通过之前的连接,新块以inv消息发布出去,而接收节点可以通过getdata请求这些块的详细信息。

我们会做什么

1.建立第一个结点,作为tcp server监听

2.打开一个终端,连接到第一个结点上来,模拟新产生区块

3.结点将新的区块链以广播的形式传播到所有结点

我们不会做什么

与上一篇文章一样,本教程的目的是模拟节点网络,以便您可以直观的认识区块链网络。所以不会像真实的区块链网络一样去实现所有功能,对以上功能会进行简化。

让我们开始编码吧!

除了上一章讲过的区块内容,我们将去掉http的部分,今天讲的重点是TCP构建网络

TCP 和 HTTP 有什么差别?

这里我们不会详细讨论,但是您需要知道的是TCP是一个传输数据的基本协议。HTTP建立在TCP之上,以在web和浏览器上使用此数据传输。当您查看一个网站时,您使用的是HTTP,它是由一个名为TCP的底层数据传输协议支持的。

在本教程中,我们将使用TCP,因为我们不需要在浏览器中查看任何内容。

我们创建一个Node.java的文件,来开始我们的编写。

Imports

包声明我们需要的导入。

import java.net.ServerSocket;

import java.net.Socket;

Review

我们的块,以及块相关操作的内容都没有变,索性我们把这些内容放在一起,新建BlockUtils.java文件,内容如下:

public classBlockUtils {/*** 计算区块的hash值

*

*@paramblock

* 区块

*@return

*/

public staticString calculateHash(Block block) {

String record= (block.getIndex()) + block.getTimestamp() + (block.getVac()) +block.getPrevHash();

MessageDigest digest=DigestUtils.getSha256Digest();byte[] hash =digest.digest(StringUtils.getBytesUtf8(record));returnHex.encodeHexString(hash);

}/*** 区块的生成

*

*@paramoldBlock

*@paramvac

*@return

*/

public static Block generateBlock(Block oldBlock, intvac) {

Block newBlock= newBlock();

newBlock.setIndex(oldBlock.getIndex()+ 1);

newBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(newDate()));

newBlock.setVac(vac);

newBlock.setPrevHash(oldBlock.getHash());

newBlock.setHash(calculateHash(newBlock));returnnewBlock;

}/*** 校验区块的合法性(有效性)

*

*@paramnewBlock

*@paramoldBlock

*@return

*/

public static booleanisBlockValid(Block newBlock, Block oldBlock) {if (oldBlock.getIndex() + 1 !=newBlock.getIndex()) {return false;

}if (!oldBlock.getHash().equals(newBlock.getPrevHash())) {return false;

}if (!calculateHash(newBlock).equals(newBlock.getHash())) {return false;

}return true;

}}

好了!我们已经基本得到了所有的区块链相关函数,从第1章删除了所有HTTP相关的内容。我们现在可以进行联网了。

网络

在此之前,让我们声明一个名为bcServer的全局变量(blockchainServer的简称),它是接收传入块的队列。

我们从main函数开始,我们需要把结点作为TCP server启动,监听端口8333。

当网络中有链接进来时,我们接收链接,并开线程处理它。这样如果有很多结点连接过来,每个结点通讯都可以并行处理。

接着,我们模拟广播到全网。

//建立TCP监听8333

serverSocket = new ServerSocket(8333);

LOGGER.info("*** Node is started,waiting for others ***");//监听对等网络中的结点

for(;;) {final Socket socket =serverSocket.accept();//创建一个新的线程 ,和建立连接的结点通讯

newNodeThread(socket, blockChain).start();//模拟网络结点广播

newBroadcastThread(socket, blockChain).start();

}

让我们来写NodeThread的实现。当新的结点连上来,我们发出一条信息“请输入一个资产值”,新结点输入一个数值,创建一个新的块,发送回链,我们将这个新的块加入到链上。

代码如下

BufferedReader br = null;

PrintWriter pw= null;try{//提示结点输入

pw = newPrintWriter(socket.getOutputStream());

pw.write("please enter a new number(vac):\n");

pw.flush();

String info= null;//读取结点发送的信息

br = new BufferedReader(newInputStreamReader(socket.getInputStream()));while ((info = br.readLine()) != null) {try{int vac =Integer.parseInt(info);//根据vac创建区块

Block newBlock = BlockUtils.generateBlock(blockChain.get(blockChain.size() - 1), vac);if (BlockUtils.isBlockValid(newBlock, blockChain.get(blockChain.size() - 1))) {

blockChain.add(newBlock);

pw.write("Success!\n");

pw.write(gson.toJson(blockChain));

}else{

pw.write("HTTP 500: Invalid Block Error\n");

}

LOGGER.info("add new block with vac:" +vac);

}catch(Exception e) {

LOGGER.error("not a number:", e);

pw.write("not a number! \n");

}

pw.write("Please enter a new number(vac):" + "\n");//调用flush()方法将缓冲输出

pw.flush();

}

}catch(Exception e) {

LOGGER.error("TCP i/o error Or client closed", e);

}finally{

LOGGER.info("node closed:" + address.getHostAddress() + ",port:" +socket.getPort());//关闭资源

try{if (pw != null) {

pw.close();

}if (br != null) {

br.close();

}

}catch(IOException e) {

LOGGER.error("close error:", e);

}

}

模拟广播

我们需要把得到的新链告诉所有链接到我们TCPServer的结点,因此,我们将模拟数据如何传输到所有的其他结点。

如何做呢,设置一个时间,定时把blockchain用json化写入连接,通知所有连接结点。

代码如下:

for(;;) {

PrintWriter pw= null;try{

Thread.sleep(30000);

LOGGER.info("\n------------broadcast-------------\n");

LOGGER.info(gson.toJson(blockChain));

pw= newPrintWriter(socket.getOutputStream());//发送到其他结点

pw.write("------------broadcast-------------\n");

pw.write(gson.toJson(blockChain));

pw.flush();

}catch(InterruptedException e) {

LOGGER.error("error:", e);

}catch(IOException e) {

LOGGER.error("error:", e);

}

}

可以到我的github查看完整的代码

https://github.com/Mignet/blockchain

实验步骤:

启动第一个结点.Node

baae60aea2967b32e47aec8f0551d055.png

打开一个终端,执行命令nc localhost 8333

打开更多终端,执行命令nc localhost 8333

e7ef94ea2c7f8147e89e79fe957c7e57.png

在任意终端输入数字,看链的打印

每过30秒,看广播到的链。

如果你有真正的网络环境,想象一下,你的任意一台设备作为结点,都可能是第一个结点,它们直接通过tcp互相通讯,广播,是不是也一样类似上面的过程呢。

下一章,我们将谈一谈工作量证明算法(Proof Of Work)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值