引言
区块链:一种由密码学算法、共识机制、点对点传输、数学、计算机编程等多种技术为基础,而形成的分布式记账系统。
在过去的学习中,我们已经系统的学习了一下区块链的基本知识,如区块链的定义,智能合约,以及区块链的应用等。但我们貌似还没有实际的用代码编写出一条区块链出来(just一点点简单基础的功能👌)
一.哈希算法
StringUtil(计算哈希值)
java.security.MessageDigest的用法
import java.security.MessageDigest;
public class StringUtil {
public static String applySha256(String input) {
try {
// 获取 SHA-256 消息摘要实例
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// 将输入字符串转换为字节数组并进行哈希运算
byte[] hash = digest.digest(input.getBytes("UTF-8"));
// 创建一个 StringBuffer 来存储哈希值的十六进制表示
StringBuffer hexString = new StringBuffer();
// 遍历哈希字节数组,将每个字节转换为两位的十六进制字符串
for (byte b : hash) {
hexString.append(String.format("%02x",b));
// String hex = Integer.toHexString(0xff & b);
// if (hex.length() == 1) hexString.append('0'); // 如果十六进制字符串长度为1,则在前面补0
// hexString.append(hex);
}
// 返回十六进制字符串
return hexString.toString();
} catch (Exception e) {
// 捕获异常并抛出运行时异常
throw new RuntimeException(e);
}
}
}
二.区块链的共识机制
POW(工作量证明)
由于不同的节点接受数据有所区别,为了保证数据一致性,每个区块数据只能由一个节点进行记录。
BTC通过“工作量证明”(Proof of Work,PoW)来确认记账节点。每个节点如果想生成一个新的区块并写入区块链,必须解出比特币网络出的PoW问题。
其关键的要素是工作量证明函数、区块信息及难度值。
工作量证明函数是这道题的计算方式,区块决定了这道题的输入数据,难度值决定了这道题所需要的计算量。
可以简单理解成就是将不同的nonce值作为输入,尝试进行SHA256哈希运算,找出满足给定数量前导0的哈希值的过程。而要求的前导0的个数越多,代表难度越大。
difficult就是前导0的个数,如difficult=4,就是哈希的前四位为0
以下代码结合了前一章计算哈希的方法
public class ProofOfWork {
public static void main(String[] args) {
// 要进行哈希的数据
String data = "Some data to hash";
// 挖矿难度级别
int difficulty = 4;
// 调用挖矿方法并获取哈希值
String hash = mine(data, difficulty);
// 打印挖矿得到的哈希值
System.out.println("Mined Hash: " + hash);
}
/**
* 挖矿方法
* @param data 要进行哈希的数据
* @param difficulty 挖矿难度级别
* @return 满足难度要求的哈希值
*/
public static String mine(String data, int difficulty) {
// 初始化nonce值
long nonce = 0;
// 记录挖矿次数
int count = 0;
// 生成目标字符串,由difficulty个'0'组成
String target = new String(new char[difficulty]).replace('\0', '0');
// 计算初始哈希值
String hash = calculateHash(data, nonce);
// 循环直到找到满足条件的哈希值
while (!hash.substring(0, difficulty).equals(target)) {
count++;
nonce++;
// 重新计算哈希值
hash = calculateHash(data, nonce);
// 打印当前挖矿次数、哈希值和nonce值
System.out.println("第" + count + "次挖矿" + " 哈希值:" + hash + " nonce值:" + nonce + "\n");
}
// 返回满足条件的哈希值
return hash;
}
/**
* 计算哈希值
* @param data 要进行哈希的数据
* @param nonce 当前的nonce值
* @return 计算得到的SHA-256哈希值
*/
public static String calculateHash(String data, long nonce) {
// 使用SHA-256算法计算哈希值
return StringUtil.applySha256(data + nonce);
}
}
```
三.区块链与智能合约的结合运用
Block(定义区块)
public class Block {
private String hash;
private String previousHash;
private String data;
private long timeStamp;
private int nonce;
public Block(String data) {
this.data = data;
this.timeStamp = System.currentTimeMillis();
this.hash = calculateHash();
}
//计算哈希值
public String calculateHash() {
return StringUtil.applySha256(previousHash + timeStamp + nonce + data);
}
//挖矿
public void mineBlock(int difficulty) {
String target = new String(new char[difficulty]).replace('\0', '0');
while (!hash.substring(0, difficulty).equals(target)) {
nonce++;
hash = calculateHash();
}
System.out.println("Block Mined!!! : " + hash);
}
public String getHash() {
return hash;
}
public void setPreviousHash(String previousHash) {
this.previousHash = previousHash;
}
@Override
public String toString() {
return "Block{" +
"hash='" + hash + '\'' +
", previousHash='" + previousHash + '\'' +
", data='" + data + '\'' +
", timeStamp=" + timeStamp +
", nonce=" + nonce +
'}';
}
}
Blockchain(上链)
创世区块(Genesis Block)是一条区块链中的第一个区块,它在区块链网络创建之初被添加到区块链中。
创世区块的创建标志着一个新的区块链的诞生,它通常包含一些特定的数据和参数,为后续区块的添加提供了基础。
import java.util.ArrayList;
import java.util.List;
public class Blockchain {
private List<Block> chain;
public Blockchain() {
this.chain = new ArrayList<>();
// 创建创世区块
this.chain.add(createGenesisBlock());
}
//生成创世区块,前一区块哈希值为0,在这里我们定义data="genesisBlock"
private Block createGenesisBlock() {
return new Block("genesisBlock");
}
public Block getLatestBlock() {
return this.chain.get(this.chain.size() - 1);
}
public void addBlock(Block newBlock) {
newBlock.setPreviousHash(this.getLatestBlock().getHash());
newBlock.mineBlock(4); // 假设难度为4
this.chain.add(newBlock);
}
public List<Block> getChain() {
return this.chain;
}
}
SmartContract(智能合约)
智能合约就是存在于区块链上的计算机程序。它们仅在由用户(或其他合约)发出的交易触发时执行。
import java.util.HashMap;
import java.util.Map;
public class SmartContract {
private String owner;
private Map<String, Integer> balances;
private Blockchain blockchain;
public SmartContract(String owner, Blockchain blockchain) {
this.owner = owner;
this.blockchain = blockchain;
this.balances = new HashMap<>();
this.balances.put(owner, 1000); // 初始代币分配给合约所有者
}
// 获取账户余额
public int getBalance(String account) {
return balances.getOrDefault(account, 0);
}
// 转账方法
public boolean transfer(String from, String to, int amount) {
if (balances.getOrDefault(from, 0) >= amount) {
balances.put(from, balances.get(from) - amount);
balances.put(to, balances.getOrDefault(to, 0) + amount);
// 创建新的区块并添加到区块链
String transactionData = from + " transferred " + amount + " to " + to;
Block newBlock = new Block(blockchain.getLatestBlock().getHash(), transactionData);
//上链 需要进行挖矿,才能将账本上链,在addBlock里有挖矿函数
blockchain.addBlock(newBlock);
return true;
}
return false;
}
}
三.区块链中的加密算法
Ctrl+鼠标左键了解→RSA算法
RSA算法基于大数因子分解的数学难题,它使用一对密钥:公钥和私钥。
公钥用于加密数据,私钥用于解密数据。公钥可以公开分享给其他人,而私钥必须保密。
在BTC中,人们使用公私钥进行转账,在阅读上文后,可以对RSA算法怎么用Java实现有个基本了解
那么可以进一步完善一下我们的区块链系统,增加公私钥加密和数字签名
KeyPairGeneratorUtil(生成一个RSA密钥对)
import java.security.KeyPair; // 导入 KeyPair 类,用于表示公钥和私钥对
import java.security.KeyPairGenerator; // 导入 KeyPairGenerator 类,用于生成公钥和私钥对
import java.security.NoSuchAlgorithmException; // 导入 NoSuchAlgorithmException 类,用于处理算法不可用的异常
public class KeyPairGeneratorUtil {
/**
* 生成一个 RSA 密钥对
*
* @return KeyPair 包含生成的公钥和私钥
* @throws NoSuchAlgorithmException 如果指定的算法不可用时抛出异常
*/
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
// 获取一个 KeyPairGenerator 实例,指定使用 "RSA" 算法
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
// 初始化 KeyPairGenerator,指定密钥大小为 2048 位
keyGen.initialize(2048);
// 生成并返回密钥对
return keyGen.genKeyPair();
}
}
CryptoUtil(用于签名和验证数据)
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
/**
* CryptoUtil 是一个用于签名和验证数据的实用工具类。
*/
public class CryptoUtil {
/**
* 使用私钥对数据进行签名。
*
* @param data 要签名的数据
* @param privateKey 用于签名的私钥
* @return 签名后的字节数组
* @throws Exception 如果签名过程出错
*/
public static byte[] sign(String data, PrivateKey privateKey) throws Exception {
// 获取 SHA256withRSA 签名实例
Signature signature = Signature.getInstance("SHA256withRSA");
// 初始化签名对象,使用私钥
signature.initSign(privateKey);
// 更新签名对象,传入数据的字节形式
signature.update(data.getBytes());
// 执行签名操作并返回签名后的字节数组
return signature.sign();
}
/**
* 使用公钥验证数据的签名。
*
* @param data 原始数据
* @param signatureBytes 签名后的字节数组
* @param publicKey 用于验证的公钥
* @return 如果签名验证成功则返回 true,否则返回 false
* @throws Exception 如果验证过程出错
*/
public static boolean verify(String data, byte[] signatureBytes, PublicKey publicKey) throws Exception {
// 获取 SHA256withRSA 签名实例
Signature signature = Signature.getInstance("SHA256withRSA");
// 初始化验证对象,使用公钥
signature.initVerify(publicKey);
// 更新验证对象,传入数据的字节形式
signature.update(data.getBytes());
// 执行验证操作并返回验证结果
return signature.verify(signatureBytes);
}
}
在了解了公私钥加密和数字签名后,我们可以对前面的代码进行修改,使其可以运用上新功能
SmartContract(高级版)
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
public class SmartContract {
// 存储用户余额的映射
private Map<String, Integer> balances = new HashMap<>();
// 区块链实例
private Blockchain blockchain;
// 构造函数,初始化合约,设置初始用户和区块链
public SmartContract(String initialUser, Blockchain blockchain) {
this.blockchain = blockchain;
// 设置初始用户的余额为1000
balances.put(initialUser, 1000); // 初始余额
}
// 获取用户余额
public int getBalance(String user) {
// 如果用户不存在,返回0
return balances.getOrDefault(user, 0);
}
// 转账方法
public boolean transfer(PublicKey fromKey, PublicKey toKey, int amount, PrivateKey fromPrivateKey) {
// 将公钥转换为字符串表示
String from = fromKey.toString();
String to = toKey.toString();
// 检查发送方余额是否足够
if (getBalance(from) >= amount) {
try {
// 创建交易数据字符串
String data = from + to + amount;
// 使用发送方的私钥对数据进行签名
byte[] signature = CryptoUtil.sign(data, fromPrivateKey);
// 验证签名是否有效
if (CryptoUtil.verify(data, signature, fromKey)) {
// 更新发送方和接收方的余额
balances.put(from, getBalance(from) - amount);
balances.put(to, getBalance(to) + amount);
// 创建新的区块并添加到区块链
blockchain.addBlock(new Block("from:" + from + "\n" + "to:" + to + "\n"));
return true; // 转账成功
}
} catch (Exception e) {
e.printStackTrace(); // 捕获并打印异常
}
}
return false; // 转账失败
}
}
四.the last–Main
将前面的知识全部整合一下,我们可以编写出一个main来运用他们
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
public class Main {
public static void main(String[] args) throws NoSuchAlgorithmException {
// 创建一个新的区块链实例
Blockchain blockchain = new Blockchain();
// 生成 Alice 和 Bob 的密钥对
KeyPair keyPairAlice = KeyPairGeneratorUtil.generateKeyPair();
KeyPair keyPairBob = KeyPairGeneratorUtil.generateKeyPair();
// 获取 Alice 和 Bob 的公钥和私钥
PublicKey publicKeyAlice = keyPairAlice.getPublic();
PrivateKey privateKeyAlice = keyPairAlice.getPrivate();
PublicKey publicKeyBob = keyPairBob.getPublic();
// 创建一个智能合约实例,绑定 Alice 的公钥和区块链
SmartContract contract = new SmartContract(publicKeyAlice.toString(), blockchain);
// 打印 Alice 和 Bob 的初始余额
System.out.println("Alice's balance: " + contract.getBalance(publicKeyAlice.toString()));
System.out.println("Bob's balance: " + contract.getBalance(publicKeyBob.toString()));
// Alice 向 Bob 转账 100 代币
if (contract.transfer(publicKeyAlice, publicKeyBob, 100, privateKeyAlice)) {
System.out.println("Transfer successful!"); // 转账成功
} else {
System.out.println("Transfer failed!"); // 转账失败
}
// 打印 Alice 和 Bob 的余额
System.out.println("Alice's balance: " + contract.getBalance(publicKeyAlice.toString()));
System.out.println("Bob's balance: " + contract.getBalance(publicKeyBob.toString()));
// 打印区块链中的所有区块
for (Block block : blockchain.getChain()) {
System.out.println(block.toString());
}
}
}