文章目录
-
- 前言
- 摘要
- 一、从比特币转账说起
- 二、区块链结构
-
- 2.1 区块结构
- 三、交易与签名
-
- 3.1 交易结构
- 四、工作量证明
-
- 4.1 挖矿算法
- 五、共识机制
-
- 5.1 最长链原则
- 六、应用示例
-
- 6.1 完整示例
- 七、总结
-
- 7.1 核心技术
前言
区块链是分布式账本的革命性技术。比特币如何防止篡改?挖矿是在做什么?为什么51%攻击很难? 理解哈希指针链式结构、工作量证明挖矿机制、共识算法保证一致性、UTXO交易模型、Merkle树验证交易、智能合约可编程逻辑,才能掌握区块链的本质。
摘要
从"比特币转账"场景出发,剖析区块链的核心技术。通过哈希指针链接区块防篡改、工作量证明争夺记账权、最长链原则达成共识、非对称加密保证安全、Merkle树高效验证、UTXO模型管理资产、智能合约自动执行,揭秘区块链的完整原理。配合详细实现和安全分析,给出区块链的透彻理解。
一、从比特币转账说起
哈吉米给南北绿豆转账比特币:
场景:BTC转账
操作:
哈吉米钱包地址:1A1zP1…
南北绿豆地址:1BvBMSE…
转账金额:0.5 BTC
手续费:0.0001 BTC
流程:
1. 哈吉米发起交易
2. 签名验证(私钥签名)
3. 广播到网络
4. 矿工打包交易
5. 挖矿(工作量证明)
6. 区块加入链
7. 6个确认后到账
特点:
✓ 去中心化(无需银行)
✓ 不可篡改(哈希链)
✓ 公开透明(所有人可查)
✓ 匿名性(地址不关联身份)
南北绿豆:“不需要银行,直接点对点转账。”
阿西噶阿西:“区块链保证了不可篡改。”
核心问题:
问题1:如何防止交易被篡改?
问题2:谁有权记账?如何选出记账者?
问题3:如何保证所有节点数据一致?
问题4:如何防止双花(重复支付)?
二、区块链结构
2.1 区块结构
区块组成:
区块结构:
+———————————-+
| 区块头(Header) |
+———————————-+
| 版本号(Version) |
| 前一区块哈希(PrevHash) |
| Merkle根(MerkleRoot) |
| 时间戳(Timestamp) |
| 难度目标(Difficulty) |
| 随机数(Nonce) |
+———————————-+
| 区块体(Body) |
+———————————-+
| 交易1(Transaction 1) |
| 交易2(Transaction 2) |
| 交易3(Transaction 3) |
| … |
| 交易N(Transaction N) |
+———————————-+
哈希链:
创世区块 → 区块1 → 区块2 → 区块3 → …
每个区块包含前一区块的哈希
修改任何历史区块,后续所有区块哈希都会变化
区块链实现:
// Block.java – 区块
@Data
public class Block {
// 区块头
private int version; // 版本号
private String previousHash; // 前一区块哈希
private String merkleRoot; // Merkle根
private long timestamp; // 时间戳
private int difficulty; // 难度
private long nonce; // 随机数
// 区块体
private List<Transaction> transactions; // 交易列表
// 当前区块哈希(不存储在区块中,动态计算)
private String hash;
/**
* 计算区块哈希
*/
public String calculateHash() {
String data = version + previousHash + merkleRoot +
timestamp + difficulty + nonce;
return SHA256.hash(data);
}
/**
* 挖矿(工作量证明)
*/
public void mine(int difficulty) {
// 目标:找到一个nonce,使得区块哈希前N位为0
String target = new String(new char[difficulty]).replace('\\0', '0');
nonce = 0;
do {
nonce++;
hash = calculateHash();
} while (!hash.substring(0, difficulty).equals(target));
System.out.println("挖矿成功! Hash: " + hash + ", Nonce: " + nonce);
}
}
// Blockchain.java – 区块链
@Component
@Slf4j
public class Blockchain {
// 区块链
private List<Block> chain = new ArrayList<>();
// 待打包交易
private List<Transaction> pendingTransactions = new ArrayList<>();
// 挖矿难度
private int difficulty = 4;
// 挖矿奖励
private BigDecimal miningReward = new BigDecimal("50");
/**
* 创建创世区块
*/
@PostConstruct
public void init() {
Block genesisBlock = createGenesisBlock();
chain.add(genesisBlock);
log.info("创世区块已创建: hash={}", genesisBlock.getHash());
}
/**
* 创建创世区块
*/
private Block createGenesisBlock() {
Block block = new Block();
block.setVersion(1);
block.setPreviousHash("0");
block.setMerkleRoot("");
block.setTimestamp(System.currentTimeMillis());
block.setDifficulty(difficulty);
block.setTransactions(new ArrayList<>());
block.mine(difficulty);
return block;
}
/**
* 获取最新区块
*/
public Block getLatestBlock() {
return chain.get(chain.size() – 1);
}
/**
* 添加交易
*/
public void addTransaction(Transaction transaction) {
// 验证交易
if (!transaction.isValid()) {
throw new IllegalArgumentException("无效的交易");
}
pendingTransactions.add(transaction);
log.info("交易已添加到待打包列表: from={}, to={}, amount={}",
transaction.getFromAddress(),
transaction.getToAddress(),
transaction.getAmount());
}
/**
* 挖矿(打包交易)
*/
public void minePendingTransactions(String minerAddress) {
log.info("开始挖矿: miner={}, pending={}", minerAddress, pendingTransactions.size());
long startTime = System.currentTimeMillis();
// 1. 创建新区块
Block block = new Block();
block.setVersion(1);
block.setPreviousHash(getLatestBlock().getHash());
block.setTimestamp(System.currentTimeMillis());
block.setDifficulty(difficulty);
block.setTransactions(new ArrayList<>(pendingTransactions));
// 2. 计算Merkle根
block.setMerkleRoot(calculateMerkleRoot(block.getTransactions()));
// 3. 挖矿(工作量证明)
block.mine(difficulty);
// 4. 添加到链
chain.add(block);
long elapsed = System.currentTimeMillis() – startTime;
log.info("挖矿成功: hash={}, transactions={}, time={}ms",
block.getHash(), block.getTransactions().size(), elapsed);
// 5. 重置待打包交易,添加挖矿奖励交易
pendingTransactions.clear();
Transaction rewardTx = new Transaction(null, minerAddress, miningReward);
pendingTransactions.add(rewardTx);
}
/**
* 计算Merkle根
*/
private String calculateMerkleRoot(List<Transaction> transactions) {
if (transactions.isEmpty()) {
return "";
}
List<String> hashes = transactions.stream()
.map(Transaction::calculateHash)
.collect(Collectors.toList());
return buildMerkleTree(hashes);
}
/**
* 构建Merkle树
*/
private String buildMerkleTree(List<String> hashes) {
if (hashes.size() == 1) {
return hashes.get(0);
}
List<String> newLevel = new ArrayList<>();
for (int i = 0; i < hashes.size(); i += 2) {
String left = hashes.get(i);
String right = (i + 1 < hashes.size()) ? hashes.get(i + 1) : left;
String combined = SHA256.hash(left + right);
newLevel.add(combined);
}
return buildMerkleTree(newLevel);
}
/**
* 验证区块链完整性
*/
public boolean isValid() {
for (int i = 1; i < chain.size(); i++) {
Block currentBlock = chain.get(i);
Block previousBlock = chain.get(i – 1);
// 验证当前区块哈希
if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
log.error("区块哈希不匹配: index={}", i);
return false;
}
// 验证与前一区块的链接
if (!currentBlock.getPreviousHash().equals(previousBlock.getHash())) {
log.error("区块链断裂: index={}", i);
return false;
}
// 验证工作量证明
String target = new String(new char[difficulty]).replace('\\0', '0');
if (!currentBlock.getHash().substring(0, difficulty).equals(target)) {
log.error("工作量证明无效: index={}", i);
return false;
}
}
return true;
}
/**
* 获取余额
*/
public BigDecimal getBalance(String address) {
BigDecimal balance = BigDecimal.ZERO;
for (Block block : chain) {
for (Transaction tx : block.getTransactions()) {
if (address.equals(tx.getFromAddress())) {
balance = balance.subtract(tx.getAmount());
}
if (address.equals(tx.getToAddress())) {
balance = balance.add(tx.getAmount());
}
}
}
return balance;
}
}
哈吉米:“哈希指针把区块串成链,任何篡改都会被发现。”
三、交易与签名
3.1 交易结构
交易实现:
// Transaction.java – 交易
@Data
public class Transaction {
private String fromAddress; // 发送方地址
private String toAddress; // 接收方地址
private BigDecimal amount; // 金额
private long timestamp; // 时间戳
private String signature; // 签名
public Transaction(String from, String to, BigDecimal amount) {
this.fromAddress = from;
this.toAddress = to;
this.amount = amount;
this.timestamp = System.currentTimeMillis();
}
/**
* 计算交易哈希
*/
public String calculateHash() {
return SHA256.hash(fromAddress + toAddress + amount + timestamp);
}
/**
* 签名交易
*/
public void sign(PrivateKey privateKey) throws Exception {
if (fromAddress == null) {
// 挖矿奖励交易无需签名
return;
}
String data = calculateHash();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update(data.getBytes());
byte[] signatureBytes = sig.sign();
this.signature = Base64.getEncoder().encodeToString(signatureBytes);
}
/**
* 验证签名
*/
public boolean isValid() {
if (fromAddress == null) {
// 挖矿奖励交易
return true;
}
if (signature == null || signature.isEmpty()) {
return false;
}
try {
String data = calculateHash();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(getPublicKeyFromAddress(fromAddress));
sig.update(data.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return sig.verify(signatureBytes);
} catch (Exception e) {
return false;
}
}
private PublicKey getPublicKeyFromAddress(String address) {
// 从地址恢复公钥(简化处理)
return null;
}
}
// Wallet.java – 钱包
@Slf4j
public class Wallet {
private PrivateKey privateKey;
private PublicKey publicKey;
private String address;
/**
* 生成钱包
*/
public Wallet() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
this.address = generateAddress(publicKey);
log.info("钱包已创建: address={}", address);
}
/**
* 生成地址
*/
private String generateAddress(PublicKey publicKey) {
byte[] publicKeyBytes = publicKey.getEncoded();
// SHA256哈希
String sha256 = SHA256.hash(new String(publicKeyBytes));
// 取前20字节作为地址
return "1" + sha256.substring(0, 33);
}
/**
* 发送交易
*/
public Transaction sendTransaction(String toAddress, BigDecimal amount,
Blockchain blockchain) throws Exception {
// 1. 检查余额
BigDecimal balance = blockchain.getBalance(address);
if (balance.compareTo(amount) < 0) {
throw new IllegalArgumentException("余额不足: " + balance);
}
// 2. 创建交易
Transaction tx = new Transaction(address, toAddress, amount);
// 3. 签名
tx.sign(privateKey);
// 4. 添加到区块链
blockchain.addTransaction(tx);
log.info("交易已发送: from={}, to={}, amount={}", address, toAddress, amount);
return tx;
}
public String getAddress() {
return address;
}
}
南北绿豆:“私钥签名,公钥验证,保证交易安全。”
四、工作量证明
4.1 挖矿算法
工作量证明实现:
// ProofOfWork.java – 工作量证明
@Slf4j
public class ProofOfWork {
private Block block;
private int difficulty;
private String target;
public ProofOfWork(Block block, int difficulty) {
this.block = block;
this.difficulty = difficulty;
this.target = new String(new char[difficulty]).replace('\\0', '0');
}
/**
* 挖矿
*/
public MiningResult mine() {
log.info("开始挖矿: difficulty={}", difficulty);
long startTime = System.currentTimeMillis();
long nonce = 0;
String hash = "";
long attempts = 0;
while (true) {
attempts++;
// 构造数据
String data = block.getVersion() +
block.getPreviousHash() +
block.getMerkleRoot() +
block.getTimestamp() +
block.getDifficulty() +
nonce;
// 计算哈希
hash = SHA256.hash(data);
// 检查是否满足难度要求
if (hash.substring(0, difficulty).equals(target)) {
long elapsed = System.currentTimeMillis() – startTime;
double hashRate = attempts / (elapsed / 1000.0);
log.info("挖矿成功! hash={}, nonce={}, attempts={}, time={}ms, hashRate={} H/s",
hash, nonce, attempts, elapsed, hashRate);
MiningResult result = new MiningResult();
result.setHash(hash);
result.setNonce(nonce);
result.setAttempts(attempts);
result.setElapsedTime(elapsed);
result.setHashRate(hashRate);
return result;
}
nonce++;
// 每1000000次输出进度
if (attempts % 1000000 == 0) {
log.debug("挖矿进度: attempts={}, current hash={}", attempts, hash);
}
}
}
/**
* 验证工作量证明
*/
public boolean validate(String hash, long nonce) {
String data = block.getVersion() +
block.getPreviousHash() +
block.getMerkleRoot() +
block.getTimestamp() +
block.getDifficulty() +
nonce;
String calculatedHash = SHA256.hash(data);
return calculatedHash.equals(hash) &&
hash.substring(0, difficulty).equals(target);
}
}
// MiningResult.java – 挖矿结果
@Data
class MiningResult {
private String hash;
private long nonce;
private long attempts;
private long elapsedTime;
private double hashRate;
}
// SHA256.java – SHA256工具类
public class SHA256 {
public static String hash(String data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
// 挖矿难度测试
public class MiningDifficultyTest {
public static void main(String[] args) {
System.out.println("难度对比测试:");
for (int difficulty = 1; difficulty <= 6; difficulty++) {
Block block = new Block();
block.setVersion(1);
block.setPreviousHash("0000000000000000");
block.setMerkleRoot("merkle_root");
block.setTimestamp(System.currentTimeMillis());
block.setDifficulty(difficulty);
ProofOfWork pow = new ProofOfWork(block, difficulty);
MiningResult result = pow.mine();
System.out.println("难度" + difficulty + ": " +
"尝试次数=" + result.getAttempts() +
", 耗时=" + result.getElapsedTime() + "ms" +
", 算力=" + String.format("%.2f", result.getHashRate()) + " H/s");
}
}
}
难度与算力:
难度对比:
难度1(前1位为0):
平均尝试次数:16次
耗时:<1ms
难度2(前2位为0):
平均尝试次数:256次
耗时:~1ms
难度3(前3位为0):
平均尝试次数:4096次
耗时:~10ms
难度4(前4位为0):
平均尝试次数:65536次
耗时:~100ms
难度5(前5位为0):
平均尝试次数:1048576次
耗时:~2秒
难度6(前6位为0):
平均尝试次数:16777216次
耗时:~30秒
比特币实际难度:
前19位为0
全网算力:~300 EH/s
出块时间:~10分钟
阿西噶阿西:“工作量证明让攻击者付出巨大算力成本。”
五、共识机制
5.1 最长链原则
共识实现:
// Consensus.java – 共识机制
@Slf4j
public class Consensus {
/**
* 选择最长链
*/
public List<Block> selectLongestChain(List<List<Block>> chains) {
log.info("选择最长链: 候选链数={}", chains.size());
List<Block> longestChain = null;
int maxLength = 0;
for (List<Block> chain : chains) {
if (isValidChain(chain) && chain.size() > maxLength) {
longestChain = chain;
maxLength = chain.size();
}
}
log.info("最长链选择完成: length={}", maxLength);
return longestChain;
}
/**
* 验证链的有效性
*/
private boolean isValidChain(List<Block> chain) {
if (chain.isEmpty()) {
return false;
}
for (int i = 1; i < chain.size(); i++) {
Block currentBlock = chain.get(i);
Block previousBlock = chain.get(i – 1);
// 验证哈希
if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
return false;
}
// 验证链接
if (!currentBlock.getPreviousHash().equals(previousBlock.getHash())) {
return false;
}
}
return true;
}
/**
* 处理分叉
*/
public void handleFork(Blockchain mainChain, List<Block> newChain) {
log.info("检测到分叉: main={}, new={}",
mainChain.getChain().size(), newChain.size());
if (newChain.size() > mainChain.getChain().size() && isValidChain(newChain)) {
log.info("新链更长,切换到新链");
// 回滚主链
rollback(mainChain, newChain);
// 切换到新链
mainChain.setChain(new ArrayList<>(newChain));
} else {
log.info("保持当前主链");
}
}
/**
* 回滚主链
*/
private void rollback(Blockchain mainChain, List<Block> newChain) {
// 找到分叉点
int forkPoint = findForkPoint(mainChain.getChain(), newChain);
log.info("分叉点: index={}", forkPoint);
// 回滚分叉点之后的交易
for (int i = mainChain.getChain().size() – 1; i > forkPoint; i—) {
Block block = mainChain.getChain().get(i);
for (Transaction tx : block.getTransactions()) {
// 将交易放回待打包列表
mainChain.getPendingTransactions().add(tx);
}
}
}
/**
* 找到分叉点
*/
private int findForkPoint(List<Block> chain1, List<Block> chain2) {
int minLength = Math.min(chain1.size(), chain2.size());
for (int i = 0; i < minLength; i++) {
if (!chain1.get(i).getHash().equals(chain2.get(i).getHash())) {
return i – 1;
}
}
return minLength – 1;
}
}
哈吉米:“最长链原则保证了全网达成共识。”
六、应用示例
6.1 完整示例
区块链应用:
// BlockchainDemo.java – 区块链演示
@Slf4j
public class BlockchainDemo {
public static void main(String[] args) throws Exception {
log.info("===== 区块链演示 =====");
// 1. 创建区块链
Blockchain blockchain = new Blockchain();
blockchain.init();
// 2. 创建钱包
Wallet alice = new Wallet();
Wallet bob = new Wallet();
Wallet miner = new Wallet();
log.info("Alice地址: {}", alice.getAddress());
log.info("Bob地址: {}", bob.getAddress());
log.info("Miner地址: {}", miner.getAddress());
// 3. 挖矿获得初始代币
log.info("\\n===== 第一次挖矿 =====");
blockchain.minePendingTransactions(alice.getAddress());
log.info("Alice余额: {}", blockchain.getBalance(alice.getAddress()));
// 4. Alice转账给Bob
log.info("\\n===== Alice转账给Bob =====");
alice.sendTransaction(bob.getAddress(), new BigDecimal("20"), blockchain);
// 5. 挖矿打包交易
log.info("\\n===== 第二次挖矿 =====");
blockchain.minePendingTransactions(miner.getAddress());
log.info("Alice余额: {}", blockchain.getBalance(alice.getAddress()));
log.info("Bob余额: {}", blockchain.getBalance(bob.getAddress()));
log.info("Miner余额: {}", blockchain.getBalance(miner.getAddress()));
// 6. Bob转账给Alice
log.info("\\n===== Bob转账给Alice =====");
bob.sendTransaction(alice.getAddress(), new BigDecimal("5"), blockchain);
// 7. 挖矿打包交易
log.info("\\n===== 第三次挖矿 =====");
blockchain.minePendingTransactions(miner.getAddress());
log.info("Alice余额: {}", blockchain.getBalance(alice.getAddress()));
log.info("Bob余额: {}", blockchain.getBalance(bob.getAddress()));
log.info("Miner余额: {}", blockchain.getBalance(miner.getAddress()));
// 8. 验证区块链
log.info("\\n===== 验证区块链 =====");
boolean isValid = blockchain.isValid();
log.info("区块链有效性: {}", isValid);
// 9. 打印区块链
log.info("\\n===== 区块链内容 =====");
printBlockchain(blockchain);
// 10. 尝试篡改
log.info("\\n===== 尝试篡改第2个区块 =====");
Block block2 = blockchain.getChain().get(1);
block2.getTransactions().get(0).setAmount(new BigDecimal("100"));
boolean isValidAfterTamper = blockchain.isValid();
log.info("篡改后区块链有效性: {}", isValidAfterTamper);
}
private static void printBlockchain(Blockchain blockchain) {
for (int i = 0; i < blockchain.getChain().size(); i++) {
Block block = blockchain.getChain().get(i);
log.info("区块 #{}", i);
log.info(" 哈希: {}", block.getHash());
log.info(" 前一哈希: {}", block.getPreviousHash());
log.info(" 时间戳: {}", new Date(block.getTimestamp()));
log.info(" Nonce: {}", block.getNonce());
log.info(" 交易数: {}", block.getTransactions().size());
for (Transaction tx : block.getTransactions()) {
log.info(" 交易: {} -> {}, 金额: {}",
tx.getFromAddress(), tx.getToAddress(), tx.getAmount());
}
}
}
}
南北绿豆:“完整的区块链实现,包含挖矿、交易、验证。”
七、总结
7.1 核心技术
阿西噶阿西总结:
区块链核心:
✓ 哈希链:防篡改
✓ 工作量证明:选择记账者
✓ 最长链原则:达成共识
✓ 非对称加密:保证安全
✓ Merkle树:高效验证
✓ 分布式:去中心化
工作量证明:
✓ 寻找满足难度的Nonce
✓ 难度动态调整
✓ 算力竞争
✓ 51%攻击困难
区块链特性:
✓ 去中心化
✓ 不可篡改
✓ 公开透明
✓ 匿名性
✓ 可追溯
应用场景:
✓ 数字货币(比特币)
✓ 智能合约(以太坊)
✓ 供应链溯源
✓ 数字身份
✓ 版权保护
哈吉米:“区块链通过技术手段实现了信任。”
南北绿豆:“从哈希到挖矿到共识,环环相扣。”
参考资料:
- 《精通比特币》
- 比特币白皮书
- 《区块链技术指南》
- 以太坊黄皮书


![[C++]算法双指针 复写0-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260910013601-6aa2098179e1b-220x150.png)

