# 区块链相关概念
概念不说人话,感觉不如下边实例更好理解 qwq
# 定义
说点人话,就是一种技术,能够实现极高信用保证的东西 qwq
区块链相关技术:密码学、控制论、博弈论、分布式系统、数字货币
主要用于去中心化分布式记账本技术,应用:金融、贸易、征信、物联网、共享经济 blabla...
区块链常见定义方式:
不依赖于第三方,通过自身分布式节点进行网络数据存储、验证、传递、交流
分布式开放性的大型网络记账簿,任何人都可以在任何时间进行操作
分布式、去中心化的计算与存储架构
区块链的特征:
去中心化
开放透明
安全性
匿名性
举例,比特币交易流程:
发起事务请求
请求事务广播到网络 p2p 节点
节点使用加密算法验证
验证成功,该事务被标识为一个新区块
新区块添加到现有区块中
# 分类
# 公有链
所有人均可参与
完全去中心化
效率很低
全世界都可以访问,比如比特币的区块链,不过区块链维护要钱,所以公有链不会太多 qwq
# 联盟链
记账权由部分组织共享
不完全去中心化
效率较高
可以有几个主节点,这些主节点不挂就行。一般由一些组织成员共享(比如几家快递公司共享物流信息什么的),这样就可以每个成员出一部分钱来维护了,区块链最常见的是联盟链。
# 私有链
记账权集中到一点
完全中心化
效率最高
没什么意义,自己玩就没意思了 qwq
公有链 | 联盟链 | 私有链 | |
---|---|---|---|
参与者 | 所有人 | 联盟成员 | 所有者 |
记账人 | 所有人 | 成员协商确定 | 所有者 |
中心化程度 | 完全去中心化 | 部分中心节点 | 完全中心化 |
典型场景 | 加密货币 | 物流 | 大型组织机构 |
交易可视化 | 完全 | 不可视 | 视情况而定 |
# 结构
区块链,看名字就是块加上链表嘛 qwq
一些不说人话的概念:
交易(Transaction):一次操作导致账本状态的改变,如插入一条数据。
区块(Block):记录一段时间内的交易信息和结果,由头和主体组成,主体记录交易信息,头部记录链相关信息。
链(Chain):区块按照发生顺序串联,是整个状态变化的日志。
每个块(链表节点)包含结构:
timestamp: 区块产生时间戳
nonce: 随机值,与区块头的 hash 值共同证明计算量(工作量),用于计算哈希(详见代码实现)
data: 区块链上存储的数据(默克尔树存储)
previousHash: 上一个区块的 hash
currentHash: 本区块链的 hash,由上述几个属性进行哈希计算而得
# 挖矿是什么
这是一种基于 PoW (Proof of Work) (即工作量证明)机制的操作,知名货币比特币、以太币都使用了 PoW 机制。
每个区块链都运行在一个共识机制上,共识机制是该区块链网络上所有节点的协议,目的是证明在该区块链操作的真实性:
确保交易的真实性
防止有人恶意篡改帐本
确认交易
避免双重支付
我们拿比特币来举例:比特币网路中的人被称为矿工,使用算力、电力、时间来计算 PoW 的过程称为挖矿,借此开采新的区块,终极目标是争夺每个区块的记帐拥有权,并取得奖励 (获得比特币)。
PoW 机制认为,网络中有超过 51% 的节点认为操作是真实的,就可以证明某操作是真实的。它被设计为要消耗大量算力,可以有效防止数据被篡改,毕竟,不可能有人的算力能超过这个巨大的网络中 51% 的计算机。(换句话说,篡改要消耗的算力都超过正常挖矿需要的算力了,有那能力去改数据还不如老实去挖矿)。
PoW 缺点也是显而易见的,就是要消耗大量毫无意义的算力,以及,假如真的有人掌握超过全网半数以上的算力,还是可以随意篡改数据的(虽然可能性不大 qwq)。
当然,共识机制不只有 PoW 一种,比较出名的还有 PBFT,PoS,blabla... 就不细说了 qwq
# 代码实现简易区块链
# 基本结构
Java 代码实现简易的区块链,结构如上文所述(为什么是 java,因为是比着 java 的例子抄的嘤~)
# 哈希
我们要保证,当前区块的 previousHash 和前一个区块的 currentHash 始终保持一致,这样才能认为这个区块链是有效的。
将区块链所有的信息转换成一个哈希,通过这个哈希的值来验证当前块是否被篡改过。
# 挖矿过程的设计
我们要设定一种方法,它可以消耗一部分算力,使得篡改区块产生一定成本。(注意这个操作本身是没有意义的,目的就是让区块变得难以篡改)
还记得刚刚定义的随机数 nonce 吗,它的作用就是保证每次哈希计算值都不同。
我们设定一个难度 difficulty,不断计算区块的哈希(每次 nonce+1),直到区块 hash 值前 difficulty 位为 0 时才认为这个哈希是有效的。这个不断计算的过程就称为挖矿。
这样简单想一下,当我们篡改某个块的内容时,重新计算它的 hash 需要一个复杂的挖矿过程。由于它后一个块也有一个 previousHash 字段被改变,要保证链的有效性,我们也需要对后一个块进行挖矿,后一个块的后一个块也要挖矿... 这样篡改的成本就非常高了。
# 代码
首先我们按上文所述定义一个区块:
Block.java:
package blockchain; | |
import java.security.MessageDigest; | |
public class Block { | |
private static int currentId = 0; | |
public int id; // 唯一标识 | |
public long timestamp; // 时间戳 | |
public String currentHash; // 当前块 hash | |
public String previousHash; // 前一个块 hash | |
public String data; // 当前块信息 | |
public int nonce; // 用于计算 hash 的随机值 | |
public Block(String previousHash, String data) { | |
this.id = currentId++; | |
this.timestamp = System.currentTimeMillis(); | |
this.previousHash = previousHash; | |
this.data = data; | |
this.currentHash = calculateHash(); | |
} | |
/** | |
* 将头部信息转换成 16 进制哈希值 | |
*/ | |
public String calculateHash() { | |
StringBuffer hexString = new StringBuffer(); | |
try { | |
String inpuString = Integer.toString(id) + previousHash + Long.toString(timestamp) + data + Integer.toString(nonce); | |
MessageDigest digest = MessageDigest.getInstance("SHA-256"); | |
byte[] hash = digest.digest(inpuString.getBytes("UTF-8")); | |
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); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
return hexString.toString(); | |
} | |
/** | |
* 挖矿过程计算哈希,不断变换 nonce 值并计算哈希,直到哈希值第一位开始的 difficulty 个字符为 0 | |
* @param difficulty 挖矿难度,设定前几位为 0 | |
*/ | |
public void mineBlock(int difficulty) { | |
String target = new String(new char[difficulty]).replace('\0', '0'); | |
while (!currentHash.substring(0, difficulty).equals(target)) { | |
nonce++; | |
currentHash = calculateHash(); | |
} | |
System.out.println("Block mined: " + currentHash); | |
} | |
public String toString() { | |
return "Block #" + id + "\r\n" + | |
"Timestamp: " + timestamp + "\r\n" + | |
"Previous Hash: " + previousHash + "\r\n" + | |
"Current Hash: " + currentHash + "\r\n" + | |
"Data: " + data + "\r\n" + | |
"Nonce: " + nonce + "\r\n"; | |
} | |
} |
接下来我们通过这些区块组建一个区块链,并提供一些验证机制:
BlockChain.java:
package blockchain; | |
import java.util.ArrayList; | |
public class BlockChain { | |
public static int difficulty = 2; | |
public ArrayList<Block> chain = new ArrayList<Block>(); | |
public void genernateBlockChain() { | |
Block b1 = new Block(null, "First block."); | |
b1.mineBlock(difficulty); | |
chain.add(b1); | |
System.out.println(b1.toString()); | |
Block b2 = new Block(b1.currentHash, "Second block."); | |
b2.mineBlock(difficulty); | |
chain.add(b2); | |
System.out.println(b2.toString()); | |
System.out.println("Current BlockChain valid:" + validateChain(this.chain)); | |
} | |
/** | |
* 验证区块是否有效,防止被篡改 | |
* 1. 前后衔接是否合法 | |
* 2. 当前块是否有效 | |
* @param current 当前区块 | |
* @param previous 前一个区块 | |
* @return | |
*/ | |
public static Boolean validateBlock(Block current, Block previous) { | |
if (!current.previousHash.equals(previous.currentHash)) { | |
return false; | |
} | |
if (!current.currentHash.equals(current.calculateHash())) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* 验证链是否有效,验证所有区块有效性即可 | |
* @param chain 区块链 | |
* @return | |
*/ | |
public static Boolean validateChain(ArrayList<Block> chain) { | |
Block previous = chain.get(0); | |
for (int i = 1; i < chain.size(); i++) { | |
Block current = chain.get(i); | |
if (!validateBlock(current, previous)) { | |
return false; | |
} | |
previous = current; | |
} | |
return true; | |
} | |
} |
调用 genernateBlockChain 方法,控制台输出:
Block mined: 00e8eed71c98e68668f37e23d6e3af8891b59fc81ae3b7725b4f546f11192ae6
Block #0
Timestamp: 1656863294128
Previous Hash: null
Current Hash: 00e8eed71c98e68668f37e23d6e3af8891b59fc81ae3b7725b4f546f11192ae6
Data: First block.
Nonce: 90
Block mined: 00b02c57715baba6c263adf075d5fd01af0d2ce45f72a60b4161e1d715543b0c
Block #1
Timestamp: 1656863294149
Previous Hash: 00e8eed71c98e68668f37e23d6e3af8891b59fc81ae3b7725b4f546f11192ae6
Current Hash: 00b02c57715baba6c263adf075d5fd01af0d2ce45f72a60b4161e1d715543b0c
Data: Second block.
Nonce: 392
Current BlockChain valid:true
# 应用实例:简易转账系统
# RSA 加密简介
首先需要了解一下 RSA 加密,拿之前蓝桥杯一个题一图看懂(看不懂也没事,知道密钥对干啥的就行 qwq):
(放一下题解)
对于一个转账行为,用户持有公私密钥对,可以通过公钥来确定是哪个用户(这里为了简化流程,我们省去了签名的过程)。
# 代码实现
定义一个事务,即一个转账行为,包含转账相关信息,这里发送接收者实际上就是存储用户的公钥。
Transaction.java:
package blockchainApp; | |
public class Transaction { | |
public String sender; // 转账发出者 | |
public String recipient; // 转账接收者 | |
public float amount; // 转账金额 | |
public Transaction(String from, String to, float value) { | |
this.sender = from; | |
this.recipient = to; | |
this.amount = value; | |
} | |
} |
修改 Block,使其存储信息为记账本(即 Transection 数组):
public ArrayList<Transaction> transactions; // 账本信息 | |
// 另外在 calculateHash 方法中,把所有记账信息放入哈希计算即可,不贴详细代码了 | |
for (Transaction transaction : transactions) { | |
inpuString += transaction.recipient + transaction.amount + transaction.sender; | |
} |
定义用户(钱包?)存储密钥对和初始余额(实时余额应该通过区块链进行计算),具有向别人转账的方法。
Wallet.java:
package blockchainApp; | |
import java.util.ArrayList; | |
import java.security.KeyPair; | |
import java.security.KeyPairGenerator; | |
import java.security.NoSuchAlgorithmException; | |
public class Wallet { | |
public String publicKey; // 公钥 | |
private String privateKey; // 私钥 | |
public float balance; // 初始余额,实际余额会通过区块链计算得出 | |
public ArrayList<Block> blockchain; // 区块链 | |
public Wallet(float balance, ArrayList<Block> blockchain) { | |
generateRSAKey(); | |
this.balance = balance; | |
this.blockchain = blockchain; | |
} | |
/** | |
* 生成 RSA 密钥对 | |
*/ | |
private void generateRSAKey() { | |
KeyPair keyPair; | |
try { | |
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); | |
privateKey = keyPair.getPrivate().toString(); | |
publicKey = keyPair.getPublic().toString(); | |
} catch (NoSuchAlgorithmException e) { | |
e.printStackTrace(); | |
} | |
} | |
/** | |
* 从区块链计算并获取当前账户的总余额 | |
*/ | |
public float getBalance() { | |
float total = this.balance; | |
for (Block block : blockchain) { | |
for (Transaction transaction : block.transactions) { | |
if (transaction.sender.equals(this.publicKey)) { | |
total -= transaction.amount; | |
} | |
if (transaction.recipient.equals(this.publicKey)) { | |
total += transaction.amount; | |
} | |
} | |
} | |
return total; | |
} | |
/** | |
* 向 recipient 转账 amount 金额 | |
* @param recipient 接收者的 RSA 公钥 | |
* @param amount | |
* @return 这次交易的记录 | |
*/ | |
public Transaction send(String recipient, float amount) { | |
if (amount > this.getBalance()) { | |
System.out.println("余额不足"); | |
return null; | |
} | |
Transaction transaction = new Transaction(this.publicKey, recipient, amount); | |
// transaction.sign(privateKey); | |
return transaction; | |
} | |
} |
之后就可以在 BlockChain 上做一些神奇操作 BlockChainApp.java:
// 修改部分不多,粘部分代码 | |
public ArrayList<Transaction> transactions = new ArrayList<Transaction>(); | |
public void genernateBlockChain() { | |
Wallet Alice = new Wallet(100, this.chain); | |
Wallet Bob = new Wallet(100, this.chain); | |
System.out.println("Alice's initial balance: " + Alice.getBalance()); | |
System.out.println("Bob's initial balance: " + Bob.getBalance() + '\n'); | |
System.out.println("Alice is sending 10 coins to Bob..." + '\n'); | |
Transaction t1 = new Transaction(Alice.publicKey, Bob.publicKey, 10); | |
if (t1 != null) { | |
transactions.add(t1); | |
} | |
Block b1 = new Block(null, this.transactions); | |
b1.mineBlock(difficulty); | |
chain.add(b1); | |
System.out.println(b1.toString()); | |
System.out.println("Current BlockChain valid:" + validateChain(this.chain) + '\n'); | |
System.out.println("Alice's balance: " + Alice.getBalance()); | |
System.out.println("Bob's balance: " + Bob.getBalance() + '\n'); | |
System.out.println("Bob is sending 20 coins to Alice..." + '\n'); | |
// 踩坑记录:新建块时没有重置记账本 | |
this.transactions = new ArrayList<Transaction>(); | |
Transaction t2 = new Transaction(Bob.publicKey, Alice.publicKey, 20); | |
if (t2 != null) { | |
transactions.add(t2); | |
} | |
Block b2 = new Block(b1.currentHash, this.transactions); | |
b2.mineBlock(difficulty); | |
chain.add(b2); | |
System.out.println(b2.toString()); | |
System.out.println("Current BlockChain valid:" + validateChain(this.chain) + '\n'); | |
System.out.println("Alice's balance: " + Alice.getBalance()); | |
System.out.println("Bob's balance: " + Bob.getBalance()); | |
} |
调用 genernateBlockChain 方法,控制台输出:
Alice's initial balance: 100.0
Bob's initial balance: 100.0
Alice is sending 10 coins to Bob...
Block mined: 00015b8569eb2adb86fac6a967e0ade85b271a708a136d8c3e7bc2e9d691b97d
Block #0
Timestamp: 1656864082866
Previous Hash: null
Current Hash: 00015b8569eb2adb86fac6a967e0ade85b271a708a136d8c3e7bc2e9d691b97d
Data: null
Nonce: 1567
Current BlockChain valid:true
Alice's balance: 90.0
Bob's balance: 110.0
Bob is sending 20 coins to Alice...
Block mined: 0080d90b23bc72f028e167050b9b116991d40634b6acd2aa64406f6445410b8b
Block #1
Timestamp: 1656864082946
Previous Hash: 00015b8569eb2adb86fac6a967e0ade85b271a708a136d8c3e7bc2e9d691b97d
Current Hash: 0080d90b23bc72f028e167050b9b116991d40634b6acd2aa64406f6445410b8b
Data: null
Nonce: 99
Current BlockChain valid:true
Alice's balance: 110.0
Bob's balance: 90.0
# 区块链 demo
Github 上一个用 node 写的简易区块链,感觉非常棒 qwq
区块链 Demo (Code): https://github.com/anders94/blockchain-demo
Demo: Blockchain Demo