# 区块链相关概念

概念不说人话,感觉不如下边实例更好理解 qwq

# 定义

说点人话,就是一种技术,能够实现极高信用保证的东西 qwq

区块链相关技术:密码学、控制论、博弈论、分布式系统、数字货币

主要用于去中心化分布式记账本技术,应用:金融、贸易、征信、物联网、共享经济 blabla...

区块链常见定义方式:

  • 不依赖于第三方,通过自身分布式节点进行网络数据存储、验证、传递、交流

  • 分布式开放性的大型网络记账簿,任何人都可以在任何时间进行操作

  • 分布式、去中心化的计算与存储架构

区块链的特征:

  • 去中心化

  • 开放透明

  • 安全性

  • 匿名性

举例,比特币交易流程:

  1. 发起事务请求

  2. 请求事务广播到网络 p2p 节点

  3. 节点使用加密算法验证

  4. 验证成功,该事务被标识为一个新区块

  5. 新区块添加到现有区块中

# 分类

# 公有链

  • 所有人均可参与

  • 完全去中心化

  • 效率很低

全世界都可以访问,比如比特币的区块链,不过区块链维护要钱,所以公有链不会太多 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):

img

(放一下题解)

img

对于一个转账行为,用户持有公私密钥对,可以通过公钥来确定是哪个用户(这里为了简化流程,我们省去了签名的过程)。

# 代码实现

定义一个事务,即一个转账行为,包含转账相关信息,这里发送接收者实际上就是存储用户的公钥。

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