斗地主吗?能学区块链那种! | 原力计划
作者 | CharlesGuo2017
责编 | 王晓曼
出品 | CSDN博客
“If you can not explain it simply,you don’t understand it。”
)。它是一种单向密码体制,即一个从明文到密文的不可逆映射,只有加密过程,没有解密过程。
(备注:《人民日报》发布于2020年3月25日)
面向对象的编码基本知识。
Java编程,编译及运行
加密的相关知识
data-当前区块的说明信息,第几局。
xiaozhang - 记录小张在当前局中的输赢数量
xiaowang - 记录小王在当前局中的输赢数量
xiaoli - 记录小李在当前局中的输赢数量
strDateTime - 记录当前交易发生的时间
nonce - 记录当前区块的工作量(即通过多少次hash运算最后得到了符合条件的当前区块的哈希值) hash - 当前区块的哈希值
previousHash- 前一个区块的hash值
/**
* @Description: 简单的区块链示例:扑克牌斗地主记账。
*
* @author Charles (yonglin_guo@hotmail.com)
* @version V1.0
* @Date 03/19/2020
*/
package com.janny.pokerblockchain;
import com.janny.pokerblockchain.JannyUtil;
/**
PokerBlock 区块类
“区块类”中存放每次区块的信息(记账信息,解密信息,链接信息等)
*/
public class PokerBlock {
//每个区块存放的数据信息,这里我们存放的是第几局,以及三个人的在当前牌局的输赢数量。
private String data;
private int xiaozhang;
private int xiaowang;
private int xiaoli;
//时间字符串
private String strDateTime;
/* 挖矿者的工作量证明PoW。
* 在这里指需要经过多少次哈希运算才能得到满足条件的哈希值
*
*/
private int nonce;
// 当前区块的哈希值;
public String hash;
// 前一个区块的hash值;
public String previousHash;
//构造方法
public PokerBlock(String data,int xiaozhang, int xiaowang, int xiaoli, String previousHash ) {
this.data = data;
this.xiaozhang = xiaozhang;
this.xiaowang = xiaowang;
this.xiaoli = xiaoli;
this.previousHash = previousHash;
this.strDateTime = (new JannyUtil()).getCurrentDateStr();
this.hash = calculateHash();
}
//根据当前区块的信息内容计算新的哈希值
public String calculateHash() {
String calculatedhash = (new JannyUtil()).applySha256(
data +
Integer.toString(xiaozhang) +
Integer.toString(xiaowang) +
Integer.toString(xiaoli) +
strDateTime +
Integer.toString(nonce) +
previousHash
);
return calculatedhash;
}
/* “挖矿”过程
在这里指需要经过多少次哈希运算才能得到满足条件的哈希值
条件有参数 difficulty 设定
例如:如果difficulty 值是2,则要求计算出来的哈希值前2位字节为“00”
如果计算出来不是,则nonce 值加1,继续计算
直到算出来的哈希值满足条件,则结束
*/
public void mineBlock(int difficulty) {
//难度值,difficulty越大,计算量越大
String target = (new JannyUtil()).getDificultyString(difficulty);
//difficulty如果为4,那么target则为 0000
while(!hash.substring( 0, difficulty).equals(target)) {
nonce ++;
hash = calculateHash();
System.out.println("!PoW工作中..." + "nonce:"+ nonce + ":hash: "+ hash);
}
System.out.println("!!!当前节点PoW完成,新的区块被创建:" + hash);
}
}
/**
* @Description: 简单的区块链示例:扑克牌斗地主记账。
*
* @author Charles (yonglin_guo@hotmail.com)
* @version V1.0
* @Date 03/18/2020
*/
package com.janny.pokerblockchain;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
import java.util.Base64;
import java.security.Key;
import java.security.MessageDigest;
//需要下载并确保 gson-2.8.6.jar 在类路径中
import com.google.gson.GsonBuilder;
/**
* 工具类
* 获得当前时间的字符串格式,对于字符串的数字签名、将一个对象装换为JSON格式数据、返回难度字符串目标
* @author Charles
*
*/
public class JannyUtil {
//返回系统当前时间的字符串格式
public static String getCurrentDateStr() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(new Date());
return dateString;
}
/*计算一个字符串的的Hash值
* MessageDigest.getInstance("MD5"); MD5 产生一个128位(16字节)的散列值(hash value)
* MessageDigest.getInstance("SHA-1"); SHA-1 产生一个160(20字节)位字符串
* MessageDigest.getInstance("SHA-256"); SHA-256 产生一个256位(32字节)字符串
*/
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);
}
}
//返回JSON格式数据,供打印用。
public static String getJson(Object o) {
return new GsonBuilder().setPrettyPrinting().create().toJson(o);
}
//返回难度字符串目标,例如难度5将返回“00000”
public static String getDificultyString(int difficulty) {
return new String(new char[difficulty]).replace('\0', '0');
}
//返回难度字符串目标,例如难度5将返回“00000”
public static String getStringFromKey(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
public static void main(String[] args) {
System.out.println("Datetime....... "+getCurrentDateStr());
}
}
/**
* @Description: 简单的区块链示例:扑克牌斗地主记账。
*
* @author Charles (yonglin_guo@hotmail.com)
* @version V1.0
* @Date 03/19/2020
*/
package com.janny.pokerblockchain;
import java.util.ArrayList;
import java.util.Base64;
import java.security.Security;
//需要下载并确保 gson-2.8.6.jar 在类路径中
import com.google.gson.GsonBuilder;
/**
* 区块链类
* @author Charles
*
*/
public class PokerBlockChain{
//存放所有的区块集合
public static ArrayList<PokerBlock> blockchain = new ArrayList<PokerBlock>();
//挖矿的难度,数字越大越难
public static int difficulty = 4;
public static void main(String[] args) {
String title = "创世块";
int xiaozhang = 0;
int xiaowang = 0;
int xiaoli = 0;
System.out.println("开始创建第0个区块链:创世块....... ");
addBlock(new PokerBlock("第0个区块",xiaozhang,xiaowang,xiaoli,"0"));//创世块
title = "第1局";
xiaozhang = 1;
xiaowang = 1;
xiaoli = -2;
System.out.println("正在创建第1个区块....... ");
addBlock(new PokerBlock("第1个区块",xiaozhang,xiaowang,xiaoli,blockchain.get(blockchain.size()-1).hash));
title = "第2局";
xiaozhang = 6;
xiaowang = -3;
xiaoli = -3;
System.out.println("正在创建第2个区块....... ");
addBlock(new PokerBlock("第2个区块",xiaozhang,xiaowang,xiaoli,blockchain.get(blockchain.size()-1).hash));
title = "第3局";
xiaozhang = -4;
xiaowang = 2;
xiaoli = 2;
System.out.println("正在创建第3个区块....... ");
addBlock(new PokerBlock("第3个区块",xiaozhang,xiaowang,xiaoli,blockchain.get(blockchain.size()-1).hash));
System.out.println("区块链是否有效的: " + isChainValid());
String blockchainJson = (new JannyUtil()).getJson(blockchain);
System.out.println(blockchainJson);
}
/**
* 检查区块链的完整性
* 从第一个区块开始逐次检查两个内容:
1,当前的区块哈希值是否正确?(通过重新计算结果,和区块中保存的区块比较)
2,当前区块中保存的“前区块哈希值” 是不是和前一个区块中的实际的哈希值相等
*/
public static Boolean isChainValid() {
PokerBlock currentBlock;
PokerBlock previousBlock;
String hashTarget = new String(new char[difficulty]).replace('\0', '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("当前区块哈希值校验错误...");
return false;
}
//当前区块中保存的“前区块哈希值” 是不是和前一个区块中的实际的哈希值是否相等?
if(!previousBlock.hash.equals(currentBlock.previousHash) ) {
System.out.println("当前区块的‘前哈希值’校验错误...");
return false;
}
//检查当前区块是否已经加到区块链上?
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("这个区块还没有被加到区块链上...");
return false;
}
}
return true;
}
/**
* 在区块链上增加一个新的区块
* @param newBlock
*/
public static void addBlock(PokerBlock newBlock) {
newBlock.mineBlock(difficulty);
blockchain.add(newBlock);
}
}
c40a984d0d31b85640cd67decf7dbe3ab684765b6bf36b38e243dc428b2d3fb5
!PoW工作中…nonce:2#️⃣
0eef49b73b88396e474899b69baeaf6f8089c4c6f07417446b8894d720c6d2a4
!PoW工作中…nonce:3#️⃣
e0d6bfb8f222fc886802047f62217c0683570f403ca1c6ae1409ac8ba359f150 …
!PoW工作中…nonce💯hash:
b9ee001c09cc9dfa8884fb4f1b9447f75e30d7fcea09edf75d71eabaa4b97827
!PoW工作中…nonce:101#️⃣
00719d3db941650ef3ccdace527c3deed836ddb0cf6304e759cab4ba237db6ee
!!!当前节点PoW完成,新的区块被创建:00719d3db941650ef3ccdace527c3deed836ddb0cf6304e759cab4ba237db6ee
正在创建第1个区块… !PoW工作中…nonce:1#️⃣
18215996f7b6f7041d20b8ea5067040af4eeb2370773de9713a1eca1206a5d3e
!PoW工作中…nonce:2#️⃣
01987723af672357a6f6f50da35c32c996c01b6349c860417776144e0b4c6e91
!PoW工作中…nonce:3#️⃣
f12614d3c9745d00a5631e3a620cbb472454a92214f27eb3f40a8cb0e9faee88 …
!PoW工作中…nonce:58#️⃣
6929737b2d514807aad983b020112699491c82fda46bb6000903046d6607050e
!PoW工作中…nonce:59#️⃣
005f83c135d7ae782fe1812706d0f08d27ef9e4aa4ac36103f0a38b3a6aa4779
!!!当前节点PoW完成,新的区块被创建:005f83c135d7ae782fe1812706d0f08d27ef9e4aa4ac36103f0a38b3a6aa4779
正在创建第2个区块… !PoW工作中…nonce:1#️⃣
3f45344b335827d278643dd7eaa43bb255c2095200b27dabdc288470e2ded533
!PoW工作中…nonce:2#️⃣
cc32db0f15df81d0eb95780175540d45ab20676375ce29e7f8779500819be1f1
!PoW工作中…nonce:3#️⃣
f505f201630e52e19807d08337b65142f9679854f72fed7657b8266a64e5e254 …
!PoW工作中…nonce:59#️⃣
f3b29379d64e8b6c467efb7aca05df5b0e0dcff4eae2c281ba24a5775ac27ef3
!PoW工作中…nonce:60#️⃣
0081ba9c9c548cea03c5b685637d5c1b3bc2808f59671a32b2a20a1eabfb608b
!!!当前节点PoW完成,新的区块被创建:0081ba9c9c548cea03c5b685637d5c1b3bc2808f59671a32b2a20a1eabfb608b
正在创建第3个区块… !PoW工作中…nonce:1#️⃣
eda9b63fcd8e3a72d604cc9dda9ebc26e8eaaa314a9999ac44afc725b919c7c7
!PoW工作中…nonce:2#️⃣
061b3386cdb975311cf48a899ac931371da683bac276823e1726741da4a964db
!PoW工作中…nonce:3#️⃣
5a9b65e96f470cfcfec5466fd6f5a687c0a9e59d697ad0fb24d723f7c6b3584c …
!PoW工作中…nonce:347#️⃣
42727f99cbb4c90e622f9c1e73663555a8adff4b4bcdb13fa4375a4b5184a255
!PoW工作中…nonce:348#️⃣
00de147c874e856c212145af5302b48a47143e8ee9a185d7e8a7b437b5b6e1e4
!!!当前节点PoW完成,新的区块被创建:00de147c874e856c212145af5302b48a47143e8ee9a185d7e8a7b437b5b6e1e4
区块链是否有效的: true [ {
“data”: “第0个区块”,
“xiaozhang”: 0,
“xiaowang”: 0,
“xiaoli”: 0,
“strDateTime”: “2020-03-26 16:42:07”,
“nonce”: 101,
“hash”: “00719d3db941650ef3ccdace527c3deed836ddb0cf6304e759cab4ba237db6ee”,
“previousHash”: “0” }, {
“data”: “第1个区块”,
“xiaozhang”: 1,
“xiaowang”: 1,
“xiaoli”: -2,
“strDateTime”: “2020-03-26 16:42:08”,
“nonce”: 59,
“hash”: “005f83c135d7ae782fe1812706d0f08d27ef9e4aa4ac36103f0a38b3a6aa4779”,
“previousHash”: “00719d3db941650ef3ccdace527c3deed836ddb0cf6304e759cab4ba237db6ee”
}, {
“data”: “第2个区块”,
“xiaozhang”: 6,
“xiaowang”: -3,
“xiaoli”: -3,
“strDateTime”: “2020-03-26 16:42:08”,
“nonce”: 60,
“hash”: “0081ba9c9c548cea03c5b685637d5c1b3bc2808f59671a32b2a20a1eabfb608b”,
“previousHash”: “005f83c135d7ae782fe1812706d0f08d27ef9e4aa4ac36103f0a38b3a6aa4779”
}, {
“data”: “第3个区块”,
“xiaozhang”: -4,
“xiaowang”: 2,
“xiaoli”: 2,
“strDateTime”: “2020-03-26 16:42:09”,
“nonce”: 348,
“hash”: “00de147c874e856c212145af5302b48a47143e8ee9a185d7e8a7b437b5b6e1e4”,
“previousHash”: “0081ba9c9c548cea03c5b685637d5c1b3bc2808f59671a32b2a20a1eabfb608b” }
]
老铁们在看签个到! 👇