广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的。这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链。
通过本文,我们将可以:
创建自己的区块链
理解 hash 函数是如何保持区块链的完整性的
如何创造并添加新的块
多个节点如何竞争生成块
通过浏览器来查看整个链
所有其他关于区块链的基础知识
环境准备
因为大多数区块链的产品都是用gradle作为构建工具,而且springboot也渐渐将重心转移到gradle,所以在此使用gradle作为构建部署工具
依赖jar包
jar包名 | 版本 | 用途 |
---|---|---|
spring-boot-starter-web | 2.3.0.RELEASE | 提供web端展示界面 |
knife4j-spring-boot-starter | 2.0.4 | 提供方便快捷的调试页面 |
commons-codec | 1.14 | 编解码工具 |
lombok | 1.18.12 | 简化类结构 |
UML类图
数据模型
Block
我们来定义一个Block类,它代表组成区块链的每一个块的数据模型:
@Getter
@NoArgsConstructor
@ToString
@Slf4j
public class Block implements Serializable{
//当前块下标
private int index;
//块生成时间戳
private long timestamp;
//数据
private String data;
//是这个块
private String hash;
//上一个块通过 SHA256 算法生成的散列值
private String prevHash;
//块生成的构造函数
public Block(BlockList blockList, String data){
this.index = blockList.getLastBlock().getIndex() + 1;
this.timestamp = System.currentTimeMillis();
this.data = data;
this.prevHash = blockList.getLastBlock().hash;
this.hash = calculateHash(this);
}
//创建创世区块
public static Block generateGenesisBlock(){
Block block = new Block();
block.index = 0;
block.timestamp = System.currentTimeMillis();
block.data = "genesis block is generated";
block.prevHash = "";
block.hash = calculateHash(block);
return block;
}
//生成区块hash值
public static String calculateHash(Block block){
String record = block.index
+ block.timestamp
+ block.getData().toString()
+ block.prevHash;
log.info("calculate hash record is {}",record);
MessageDigest messageDigest = DigestUtils.getSha256Digest();
byte[] bytes = messageDigest.digest(record.getBytes(Charset.forName("utf-8")));
return Hex.encodeHexString(bytes);
}
//块数据校验
public boolean validBlock(Block oldBlock) {
//判断旧块的下标是否与新区块的下表对应
if (oldBlock.getIndex() + 1 != this.getIndex()) {
return false;
}
//判断新块是否和旧块的hash值是否匹配
if (oldBlock.getHash() != this.getPrevHash()) {
return false;
}
//校验当前块hash与数据是否匹配
if(!calculateHash(this).equals(this.hash)){
return false;
}
return true;
}
}
我们使用散列算法(SHA256)来确定和维护链中块和块正确的顺序,确保每一个块的 PrevHash 值等于前一个块中的 Hash 值,这样就以正确的块顺序构建出链:[ index:0| hash:"xxxw"| preHash:""] - [ index:1| hash:"xxxx"| preHash:"xxxw"] - [ index2| hash:"xxxy"| preHash:"xxxx"] 使用散列的理由:
在节省空间的前提下去唯一标识数据。散列是用整个块的数据计算得出,在我们的例子中,将整个块的数据通过 SHA256 计算成一个定长不可伪造的字符串。
维持链的完整性。通过存储前一个块的散列值,我们就能够确保每个块在链中的正确顺序。任何对数据的篡改都将改变散列值,同时也就破坏了链。以我们从事的医疗健康领域为例,比如有一个恶意的第三方为了调整“人寿险”的价格,而修改了一个或若干个块中的代表不健康的 VAC 值,那么整个链都变得不可信了。
BlockList
接下来我们建立一个链表结构保存区块的数据,将具体的链封装进我们自定义的BlockList数据结构中,仅开放我们需要的接口,保证链的安全性。
public class BlockList implements Serializable {
@Getter
private List<Block> chain;
public BlockList() {
chain = new LinkedList<>();
generateGenesisBlock();
}
public void add(Block block){
chain.add(block);
}
@JsonIgnore
public Block getLastBlock(){
return chain.get(chain.size()-1);
}
public int size(){
return chain.size();
}
private void generateGenesisBlock(){
//todo:补充完成创世块生成操作
if(chain.size() > 0){
throw new BlockException("已存在数据,无法创建创世区块");
}
chain.add(Block.generateGenesisBlock());
}
}
构建教程
现在我们有了单节点区块链的核心,区块和链,那么我们接下来就要将它运行在我们的springboot框架里。
建立链管理器
@Slf4j
public class BlockManager {
private BlockManager(){}
private static BlockList blockChain = new BlockList();
public static void addBlock(Block block){
blockChain.add(block);
}
public static BlockList getAll(){
return blockChain;
}
//替换较长区块
private static void replaceChain(BlockList newBlocks) {
if (newBlocks.size() > blockChain.size()) {
blockChain = newBlocks;
}
}
}
链管理器的作用是负责区块链的初始化,区块链和业务层的交互,在下一步建立节点通信中,也将担负起节点同步的责任。
创建业务接口
@RestController
@RequestMapping("block")
@Api
@Slf4j
public class BlockChainController {
@ApiOperation("获取链信息")
@GetMapping("getBlockChain")
public ResponseEntity<BlockList> getBlockChain(){
BlockList blockList = BlockManager.getAll();
return ResponseEntity.ok(blockList);
}
@PostMapping("addBlock")
@ApiOperation("添加新区块")
public ResponseEntity<BlockList> addBlock(String data){
Block newBlock = new Block(BlockManager.getAll(), data);
log.info("generate new block, {}",newBlock.toString());
if(!newBlock.validBlock(BlockManager.getAll().getLastBlock())){
log.error("generate new block failed, new block is not match old block");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
BlockManager.addBlock(newBlock);
return ResponseEntity.ok(BlockManager.getAll());
}
}
测试一下
总结
好了,单节点的区块链就这么构建完成了,虽然很简单很简陋,但它具备块生成、散列计算、块校验等基本能力。接下来我们要学习比如tcp网络通信,去中心化的p2p连接,evm虚拟机的原理,共识算法,一步一步理解区块链的原理。
WE BUILD VALUABLE PRODUCTS
www.andlinks.com 联系方式: contact@andlinks.com