跳至主要内容

用 R 编码实现一个区块链,理解其原理

在 R 中实现一个小型区块链,进一步了解区块链的结构以及背后的一些核心概念!
· 10分钟 阅读

使用 AI 探索

如今人人都在谈论加密货币——这要归功于比特币的热潮。比起“是否存在比特币泡沫”,更有趣的问题是这项技术究竟如何运作,以及它能做什么。比特币的故事始于 2008 年的一篇白皮书,题为“Bitcoin: A Peer-to-Peer Electronic Cash System”。在这篇论文中,署名为中本聪(关于中本聪真实身份的争论至今仍在继续)的作者提出了一项全新的革命性技术与理念:一种运行在所谓区块链之上的去中心化电子货币。由此出发,这一理念现已扩展到许多其他领域。

我第一次接触区块链是在牛津的 BlockchainSociety 活动上。那里的人们都对这项新技术的潜力感到兴奋。实话说,这听起来确实很吸引人:一个去中心化、不可篡改的数据库,可以记录货币交易、合约,或您喜欢的任何内容(例如不可被入侵的投票机)。区块链向加密社区许下的承诺是:它将以全新的方式重塑信任,从而颠覆整个行业——我们将不再需要银行或律师等第三方,只需依靠密码学的力量。

虽然基本思想相当直观,但从技术层面理解区块链的工作原理就没那么容易了。上周我在 R bloggers 上看到一篇文章,BigData Doc 用纯 R 构建了一个区块链。用 R 搭建区块链可能不是世界上最高效、最实用的做法,但却是理解其背后编程与密码学原理的好方式。我也想弄明白,于是用 R 实现了一个更小的区块链版本:“如果您能把它写出来,您就一定理解了它。”

什么是区块链?

假设您的目标是以安全的方式存储一些数据。为此,您首先将数据存放在一个容器里——我们称之为区块。以比特币为例,每个区块包含若干笔金融交易。当有新的交易(或新数据)产生时,就会创建一个新区块,并与之前的区块相连,形成一条链——即区块链。

我们来看看区块链如何利用密码学实现几乎不可被入侵。

1. 区块

    block_example <- list(index = 1,
                     timestamp = "2018-01-05 17.00 MST",
                     data = "some data",
                     previous_hash = 0,
                     proof = 9,
                     new_hash = NULL)

在开始构建区块链(也就是把不同的数据容器串联起来)之前,您需要先了解另外两个概念:哈希与工作量证明(Proof-of-Work)算法。

2. 哈希

哈希通过将区块与链中的其他区块连接起来,帮助确保区块的完整性。哈希函数接收某个输入并生成唯一的加密输出。举个例子:您给朋友出个谜题“哪个统计软件更好:Stata 还是 R?”,同时把正确答案的哈希值给她:“71ec0b920622cf4358bbc21d6a8b41f903584808db53ec07a8aa79119304ce86”。她只需把自己的答案输入哈希函数(此处为 SHA256 算法)就能自行验证是否正确:

    library("digest")

    digest("Stata" ,"sha256") # first try
    ## [1] "3ac273f00d52dc9caf89cbd71e73e5915229a588117ca3441630089409ddb7bc"
    digest("R", "sha256") # second try
    ## [1] "71ec0b920622cf4358bbc21d6a8b41f903584808db53ec07a8aa79119304ce86"

这对您有什么帮助?

在这里,您输入哈希函数的不仅是关于区块的信息(index、timestamp、data),还有上一个区块的哈希。这意味着,只有在知道上一个区块的哈希时,您才能计算出当前区块的有效哈希;而上一个区块的哈希又由再上一个区块的哈希生成,如此顺延。这就形成了一条不可变的、按序链接的区块链。如果您事后篡改了某个区块,就必须重新计算其后所有区块的哈希。

    #Function that creates a hashed "block"
    hash_block <- function(block){
      block$new_hash <- digest(c(block$index,
                                 block$timestamp,
                                 block$data,
                                 block$previous_hash), "sha256")
      return(block)
    }


3. 工作量证明

如果有大量信息需要存储在区块链上,您就需要创建很多新区块。在许多情况下,您希望控制新区块的生成速度。以加密货币为例,如果每秒都能无限量地产生新币,币就会失去价值。

因此,我们加入所谓的“工作量证明”(PoW)算法,用以控制创建新区块的难度。“证明”意味着计算机完成了一定量的工作。实践中,目标是构造一种“难做但易验”的任务。本文将使用如下“任务”作为 PoW:找到一个同时能被 99 和上一个区块的 proof 数整除的下一个数字。

    ### Simple Proof of Work Alogrithm
    proof_of_work <- function(last_proof){
      proof <- last_proof + 1

      # Increment the proof number until a number is found that is divisable by 99 and by the proof of the previous block
      while (!(proof %% 99 == 0 & proof %% last_proof == 0 )){
        proof <- proof + 1
      }

      return(proof)
    }

像比特币或以太坊这样的区块链,创建新区块的工作由所谓的矿工完成。当需要创建新区块时,一个计算难题会被广播到网络。第一个解决 PoW 问题的矿工将创建新区块,并获得比特币奖励(新比特币正是这样被创造出来的)。这种“抽奖”式地寻找正确 proof 的机制,确保了创建新区块的权力是去中心化的。新区块被挖出后会广播给所有人,使网络中的每个节点都拥有最新区块链的副本。网络中最长的区块链(即“投入工作最多”的那条)被视为有效版本,这一理念被称为“去中心化共识”。

在比特币的实现中,PoW 问题涉及寻找能生成具有特定数量前导零的哈希的数字(我找到的最佳解释来自Savjee的视频)。为了适应计算速度的提升和网络中矿工数量的波动,PoW 的难度可以调整,从而将出块时间稳定在约 10 分钟。

4. 添加新区块

现在您已经知道区块的结构、区块如何通过哈希链接,以及 PoW 如何调节出块节奏。让我们把这些整合进一个函数:

    #A function that takes the previous block and normally some data (in our case the data is a string indicating which block in the chain it is)
    gen_new_block <- function(previous_block){

      #Proof-of-Work
      new_proof <- proof_of_work(previous_block$proof)

      #Create new Block
      new_block <- list(index = previous_block$index + 1,
                        timestamp = Sys.time(),
                        data = paste0("this is block ", previous_block$index +1),
                        previous_hash = previous_block$new_hash,
                        proof = new_proof)

      #Hash the new Block
      new_block_hashed <- hash_block(new_block)

      return(new_block_hashed)
    }

在开始构建区块链之前,您需要有一个起点,这就是所谓的创世区块。它不包含数据,并为 proof 和 previous hash 赋予任意值(因为不存在前一个区块)。

    # Define Genesis Block (index 1 and arbitrary previous hash)
    block_genesis <-  list(index = 1,
                           timestamp = Sys.time(),
                           data = "Genesis Block",
                           previous_hash = "0",
                           proof = 1)


5. 构建区块链

现在可以开始构建区块链了。以创世区块为起点,然后通过循环添加几个区块。

    blockchain <- list(block_genesis)
    previous_block <- blockchain[[1]]

      # How many blocks should we add to the chain after the genesis block
      num_of_blocks_to_add <- 5

      # Add blocks to the chain
      for (i in 1: num_of_blocks_to_add){
        block_to_add <- gen_new_block(previous_block) 
        blockchain[i+1] <- list(block_to_add)
        previous_block <- block_to_add

        print(cat(paste0("Block ", block_to_add$index, " has been added", "\n",
                   "\t", "Proof: ", block_to_add$proof, "\n",
                   "\t", "Hash: ", block_to_add$new_hash)))
      }
    ## Block 2 has been added
    ##  Proof: 99
    ##  Hash: 7d3dfbb58b410838769f6080dbc62a44a4c5d411a41048c4e597d26dccd1cd38NULL
    ## Block 3 has been added
    ##  Proof: 198
    ##  Hash: 4f8bdd79d751f9e9829c14c52a737f257285b61e54b29531dd59bf1a530f1097NULL
    ## Block 4 has been added
    ##  Proof: 396
    ##  Hash: 512b877c4ff92605d9fe10ac73ced20f748742964f306c211c4691f15425a26eNULL
    ## Block 5 has been added
    ##  Proof: 792
    ##  Hash: a3baaa025186c5bcb2238a888bc65e705ffe73c17dc5c8f26ee337fd62867993NULL
    ## Block 6 has been added
    ##  Proof: 1584
    ##  Hash: d12c3f54c14f3287a9f31ab542271197ba6a658cee561a9289819dd563fe4991NULL

如果您想继续添加更多区块,会发现所需时间越来越长。原因在于 proof 数会呈指数增长。

blockchain R

对于比特币这类加密货币来说,这是个问题,因为出块时间理应大体恒定(比特币约为 10 分钟)。因此需要持续调整 PoW 的难度,以适应计算能力的提升以及任意时刻网络中矿工数量的变化。

最后,链中的一个区块看起来大致如下:

    blockchain[[5]]
    ## $index
    ## [1] 5
    ## 
    ## $timestamp
    ## [1] "2018-02-08 12:02:56 GMT"
    ## 
    ## $data
    ## [1] "this is block 5"
    ## 
    ## $previous_hash
    ## [1] "26cdc16a4560df5fa2fd521dbca22670e2475c35d3dd90781872bee98a164eef"
    ## 
    ## $proof
    ## [1] 792
    ## 
    ## $new_hash
    ## [1] "2eef25bf0bc4ee81e8c7cd1dfda65855b4ba32aba218d8c525a03a72b3454d74"


总结

在这篇小短文中,您构建了一个极简区块链。主要目的是介绍区块链的样子,并讲解其背后的一些核心概念。理解密码学的巧妙应用,的确能帮助我们理解为什么人们会对区块链的可能性感到如此兴奋。

要将区块链投入生产,还需要做更多工作:搭建 API、创建钱包、使用公私钥对进行数字签名等。本文对区块链网络核心的去中心化共识也只是一笔带过。

本文的灵感来自Gerald Nash 的博客文章,他用 Python 实现了“最小的区块链”。我用 R 进行了实现,并加入了Daniel van Flymen 的 PoW 实现。

如果想更深入地了解区块链,我推荐中本聪那篇出色而原创的比特币白皮书以及 BigData Doc 的博文。

如果您对这篇文章有任何想法或反馈,欢迎在 Twitter 上联系我:您可以找到我 @jj_mllr,或关注 @CorrelAid,这是一个致力于以更具包容性、融合性与创新性的方法通过数据分析改变世界的青年数据分析师网络。

主题