前言:
目前市面上大多数都是源于国外大神的两篇关于java区块链的教程的生硬翻译,这就很容易导致一个问题,就是你明明把他代码跑起来了,最后却还不知道区块链是个啥,比如是如何做到去中心化的?又是如何做到不可篡改行的,以及比特币为什么越挖越少这些问题依然得不到很好的解释,本篇文章呢,依然源用的是那篇教程的代码,除了英文注释我会手动翻译成中文之外,其他的变量名什么的都不会改,毕竟人家思路是对的,没有必要重新造轮子,改个变量就说代码是我自己写的,那我和网上那些抄袭转载的人也没什么区别了。本篇文章并不是对作者源论文的机械翻译,只是借用了相关代码,希望大家看过之后会大致明白区块链具体是一项什么样的技术,我们为什么需要区块链等等。
什么是区块链:
区块链(英语:blockchain或block chain)借由密码学与公识机制等技术创建与存储庞大交易资料区块链的点对点网络系统。——维基百科
区块链是什么,区块链就是谈恋爱
明天就是小民和小红在一起100天了,小民想要在明天给小红一个大大的惊喜,就是把他辛辛苦苦研发的机器人送给小红,他能想象到小红看到机器人兴奋的样子,他一想觉得太他妈浪漫了。
但是那天出现了意外。
小黑:你们不能在一起,其实小红是我的女朋友。
小民:难道我被绿了,@(#(*#(@。
小黑:不不不,其实小红是我的女朋友。
重点来啰:注意看。
小民:去你的,我们谈恋爱的第一天,我送了小红一个鼠标,第二天,我送了她一个键盘,第三天,我送了她一个屏幕,第四天..... 第一百天,我送了她一个我辛苦开发的机器人,这些,就是证据!
小黑:啊啊啊啊,我输了,小红是你的女朋友。
小黑由于故意篡改小红是小民女朋友的事实,被拉入黑名单,从此再也没有找到女朋友。
在这个例子中,小红
,小民
,和小黑
就是区块链中的链,而之前小民
和小红
从相识,到相知,再到相爱期间每发生一个故事就会形成一个区块。而且小民和小红之间发生的所有故事都会以直播的形式被区块链中的所有链知道(太狠了吧),所以小黑说小红是他的女朋友自然就不可能是真的了,因为整个区块链所有的链都见证了小民和小红是情侣这个事实,如果小黑要篡改事实说小红是他的女朋友,那么他要修改整个区块链中所有链对于小民和小红这对情侣的记忆,这是几乎无法做到的。
这就是区块链的不可篡改性。
同时,如果有一天小民不爱小红了,爱上了小绿,于是把手机上和小红有关的东西全部删掉,告诉小绿,小绿是自己的初恋,小民只爱小绿一个人。这样做有用吗?没用,因为整个区块链中的链都观看过小民和小红的直播,记录着小民和小红曾经在一起过的证据。
这就是区块链中的不可修改性。
大家认真想一想,如果区块链应用于金融会怎么样?
之前我们的钱都是存在银行的账户管理系统里面,如果有人侵入银行的账户管理系统,只需要把他账号下代表余额的那串数字改了就可以决定自己有多少钱了,而在区块链中,每个人都是银行,每个人都是账户管理系统,如果需要修改自己账户的余额,则需要修改全网所有节点的信息才行,这几乎是不可能实现的,所以大大的提高了安全性。更不要说应用于其他领域了。那下面就来交你怎么实现一个简单的区块链。
用java实现一个简单的区块链:
public class Block {
/**
* 当前区块的hash
*/
public String hash;
/**
* 前一个区块的hash,本例中,靠这个实现链的
*/
public String previousHash;
/**
* 当前区块的数据,比如交易信息啊等等,在谈恋爱例子中代表小红和小民具体发生的事件
*/
private String data;
/**
* 时间戳
*/
private long timeStamp;
private int nonce;
public Block(String hash, String previousHash, String data) {
this.hash = hash;
this.previousHash = previousHash;
this.data = data;
}
public Block(String data, String previousHash) {
this.previousHash = previousHash;
this.data = data;
this.timeStamp = new Date().getTime();
this.hash = calculateHash();
}
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash +
Long.toString(timeStamp) +
Integer.toString(nonce) +
data);
return calculatedhash;
}
public void mineBlock(int difficulty) {
//Create a string with difficulty * "0"
String target = new String(new char[difficulty]).replace('\0', '0');
while(!hash.substring( 0, difficulty).equals(target)) {
nonce ++;
hash = calculateHash();
}
}
}
变量解释:
- hash :当前区块的哈希值
- previousHash :上一个区块的哈希值,就是靠这个实现链的,怎么有点像链表(嘘)
- data:当前区块所存储的信息,比如小民给小红买口红这一件事。
- timeStamp:时间戳, 比如小民给小红买口红这一件事发生的时间信息。
- nonce: 只是一个普通的基数变量。
这个类中需要大家去理解的可能就是mineBlock ()
和calculateHash()
这个两个方法了,而nonce
就是关键变量。
在mineBlock()
方法中会不停的执行hash运算,直到算出符合条件的区块,而nonce
就是算出来改区块所需要的次数,在calculateHash()
中,我们加上nonce
次数,就可以一下子计算出来这个hash
了。
而且大家看这个calculateHash()
这个方法,我们在执行hash计算的时候,是以上一个区块的hash
为参数进行的,一旦上一个区块的hash
是个假的,或者被篡改了,那么无论怎么计算,calculateHash()
方法返回的hash值,和该区块本身的hash值是几乎不可能一样的,也就很容易发现区块被人篡改了。
而加密算法呢,作者选用的是SHA-256, 也是比特币所采用的加密算法,被公认为最安全最先进的算法之一 .
StringUtil
类的代码如下所示,由于这个和我们今天要讲的主题关系不大,所以就不过多阐述了。
public class StringUtil {
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);
}
}
}
JsonUtil
的代码如下:(用这个包记得导入JAR包)
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonUtil {
public static String toJson(Object object){
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
return gson.toJson(object);
}
}
既然最基础的区块已经写好了,那么我们就来简单地测试一下,看看是不是真的能挖到矿。
新建三个Block区块,把mineBlock()
方法的参数设置为5.
测试类代码如下:
public class BlockChainTest {
public static void main(String[] args) {
//第1个区块
Block firstBlock = new Block("我是第1个区块", "0");
firstBlock.mineBlock(5);
System.out.println("第1个区块hash: " + firstBlock.hash);
//第2个区块
Block secondBlock = new Block("我是第2个区块", firstBlock.hash);
secondBlock.mineBlock(5);
System.out.println("第2个区块hash: " + secondBlock.hash);
//第3个区块
Block thirdBlock = new Block("我是第3个区块", secondBlock.hash);
thirdBlock.mineBlock(5);
System.out.println("第3个区块hash: " + thirdBlock.hash);
}
}
运行结果如下:
第1个区块hash: 00000a6787b74a7ca69f3fb905f17bb09503ff26a736793b582fef13f503247f
第2个区块hash: 00000f5aacb3d9c5d1eeb466472949e6a22aa83c3a5ea4b576661a6895fb26b4
第3个区块hash: 0000007ef526d4a4bbeb33b7a9077e74d3e2b32c4bc4c9c0678e26e430044bd4
发现什么了吗?即我们挖到的三个区块,前五个数字都是0诶,好神奇哦
0000052659276b
所以挖矿的本质其实就是通过哈希计算得到符合条件的hash的过程。
所以知道比特币为什么越挖越少了吧。
因为在所有计算结果之中,符合条件的hash是有限的,而且越算越少,刚开始计算的时候,由于符合条件的hash实在是太多了,所以所需的算力比较小,很容易就计算出来了,而越往后,未被计算出来过的符合条件的hash值就越少,算出来所需要的算力越大,当最后只剩一个符合条件的hash的时候,那时候就真的无异于大海捞针了。这可能就是为什么十年前随便一台电脑都能轻轻松松地挖出来比特币,而现在却需要几千台矿机的矿场才能挖出来的原因吧
这个时候可能有人会提出疑问了,我怎么知道你这个区块是不是合法的,看着是这样,万一他不合法我也不知道啊。被改了我也不知道。别慌我们慢慢来。
public class BlockChainListTest {
//这玩意就是我们的区块链,存储我们所有的区块信息。(简陋版)
public static ArrayList<Block> blockChain = new ArrayList();
//挖矿的难度,就是计算出来的hash前几个字符是0才是合法的。
public static int difficulty = 5;
public static void main(String[] args) {
blockChain.add(new Block("我是第1个区块", "0"));
blockChain.get(0).mineBlock(difficulty);
blockChain.add(new Block("我是第2个区块", blockChain.get(blockChain.size() - 1).hash));
blockChain.get(1).mineBlock(difficulty);
blockChain.add(new Block("我是第3个区块", blockChain.get(blockChain.size() - 1).hash));
blockChain.get(2).mineBlock(difficulty);
System.out.println("区块链是否合法: " + isChainValid());
System.out.println(JsonUtil.toJson(blockChain));
}
public static Boolean isChainValid(){
Block currentBlock;
Block previousBlock;
boolean flag = true;
String hashTarget = new String(new char[difficulty]).replace('\0', '0');
//循环遍历列表检验hash
for(int i=1;i<blockChain.size();i++){
currentBlock = blockChain.get(i);
previousBlock = blockChain.get(i-1);
//比较注册的hash和计算的hash
if(!currentBlock.hash.equals(currentBlock.calculateHash())){
System.out.println("当前hash不相等");
flag=false;
}
//比较当前的前一个hash与注册的前一个hash
if(!previousBlock.hash.equals(currentBlock.previousHash)){
System.out.println("前一个hash不相等");
flag=false;
}
//检查该区块是不是已经被算出来过了。
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("这个区块还没有被开采,也就是你这个区块他不是合格的");
flag=false;
}
}
return flag;
}
}
谢谢关注和点赞