300行ABAP代码实现一个最简单的区块链原型

不知从何时起,区块链在网上一会儿就火了。git

这里Jerry就不班门弄斧了,网上有太多的区块链介绍文章。个人这篇文章没有任何高大上的术语,就是300行ABAP代码,实现一个最简单的区块链原型。github

我我的以为,同区块链自己的实现技术相比,更难的事情是如何找到一个合适的业务场景,把区块链集成到SAP产品中去,让它发挥出做用。算法

这篇文章包含三个版本,每一个版本在前一版本基础上增添了一些新的功能。数据结构

版本1:区块和链这两个数据结构的实现

区块链,顾名思义,由区块组成的一条链。函数

下图和咱们在大学计算机专业课《数据结构》里学到的单链表很像。在这个版本里,每一个区块包含了最基本的字段:块索引,块的建立时间戳,当前块的哈希值(hash)和前一块的哈希值。每一个区块的pHash字段存储了前一块的哈希值,这样就构成了一个链表。链表的第一个节点,就是下图最左边红色抬头的区块为创世块,其索引为0,pHash字段为空。区块链

区块的ABAP实现:ZCL_BLOCK。上图所示的字段都建模在这个类里,出于简单起见,大部分字段设置为public。测试

每一个区块的哈希值是由该区块全部其余字段的值做为输入,经过SHA1算法计算出来,存储到字段mv_hash里。编码

链的实现:ZCL_BLOCKCHAIN3d

  • ADD_BLOCK: 接受一个区块的实例做为输入参数,将该实例的pHash指向当前链表尾部的区块,这样该实例成为链表新的尾部区块。
  • CONSTRUCTOR: 构造函数,执行链的初始化操做,建立创世块区块。
  • GET_BLOCK_BY_INDEX: 根据索引访问指定的区块。
  • GET_SIZE: 返回链里包含的区块数量。
  • IS_VALID: 检查该区块链是否有效。具体检查逻辑在后续介绍。

初版的全部代码在个人github上。blog

执行测试程序ZBLOCKCHAIN_V1,输入您想建立的区块个数,会看到以下输出:(我选择的个数是5)

由于SAP GUI没有链表的UI控件,所以我用树控件模拟。

下图第21行,我把区块链里索引为1的区块内容篡改成"Change by Jerry", 而后再执行第23行的is_valid方法进行检查:

由于第21行set_data方法修改了第一个区块的内容后,会触发其哈希值的从新计算。这样第一个区块的哈希发生了变化(假设从YYY变到了CCC),而第二个区块的pHash仍然指向第一个区块变化以前的旧哈希值YYY,所以这个区块链被断定为无效。

上图的输出来自校验方法is_valid: 遍历链里每一个区块,比较区块里存储前一区块哈希值的字段pHash和位于该区块前一个位置的区块的哈希值是否一致。

版本2:增长挖矿成本,增长对工做量证实(Proof of Work,缩写为POW)的支持

第二版代码的地址在个人github上。

这一版的链实现类ZCL_BLOCKCHAIN_V2的构造函数增长了一个输入参数:iv_difficulty。这个参数有什么用?

仔细观察初版测试程序的树状输出,能够看到每一个区块的哈希值没有任何规律。而第二版的这个输入参数就是为了提升哈希值的计算成本,即只有当计算出来的哈希值知足必定规则时,该哈希值才能被区块链所接受。参数iv_difficulty定义了可以被接受的哈希值的前导零个数。

例如我指定前导零个数为3:

执行结果:能看到全部的哈希值的前三位都为零。

这里有两个问题:

  • 下图最后一列Nonce的含义是什么?
  • iv_difficulty这个参数是如何参与哈希值计算的呢?

首先在区块的实现类ZCL_BLOCK里增长了一个新的成员字段mv_nonce:

在将一个区块实例添加到链里的方法add_block里,增长了一个方法mine。

这个方法里是一个循环,在循环体内计算出一个哈希值,而后检查其是否包含指定位数的前导零。若是没有,将mv_nonce加1,而后继续循环。mv_nonce也会做为输入的一部分参与哈希计算。也就是说,最终区块字段mv_nonce的值表明了表明了在获得符合前导零位数要求的合法哈希值以前,一共通过了多少次计算。而经过在循环里不断尝试最终获得一个合法的哈希值的这一过程,就是区块链圈内俗称的“挖矿”。

在个人测试系统里,建立10个区块,前导零个数为4,总共花费了10秒钟。

从这个花费的时间能体会出,POW实际上是一种机制,经过引入须要必定工做量的哈希计算来避免区块链被垃圾填充或者区块内容被篡改。

版本3:使用区块链记录交易明细,增长挖矿奖励

这一版的源代码:

https://github.com/i042416/KnowlegeRepository/tree/master/ABAP/blockchain/v3

使用ZCL_TRANSACTION来表明一笔交易,包含三个字段:mv_from_address(支付方),mv_to_address(收款方)和mv_amount(交易金额)。

在这一版本里,首先被加强的是ZCL_BLOCK3: 去掉了前两个版本使用的mv_index和mv_data字段,增长了一个字段mt_transaction, 存储的是交易的集合。

在计算哈希值时,交易类的每个字段也要参与计算:

类ZCL_BLOCKCHAIN_V3增长了一个新的成员变量mt_pending_trans。每次调用方法create_transaction,并不会建立一个新的区块用于记录该条交易,只是简单地把该条交易添加到待处理任务队列mt_pending_trans里。

字段mv_mine_reward存储了挖矿的奖励,硬编码成100。

这个待处理任务队列仅当方法mine_pending_trans被调用时才会获得处理。

第6行的区块实例的mine方法调用以后,计算出一个符合前导零规范的哈希值。接着待处理任务队列被清空,而后一个新的交易记录在第13行被建立出来,做为挖矿的奖励,奖励方的帐号信息由输入参数iv_award_address定义。

既然如今交易信息存储在了每一个区块里,那么简单遍历这些区块,就能得出某个帐号最后的余额是多少。采用的逻辑是,遍历每一个区块记录的每笔交易,若是某账号出如今交易记录的支付方,则余额减去当前这笔交易的交易金额(第5行), 反之帐号若是出如今发送方,则余额加上交易金额(第9行)。

测试程序以下。由于Tom转了100元给Jerry,Jerry又转了10元给Tom,而后第15行挖矿设置的奖励帐号是Jerry,故最后Jerry的余额是100-10+100 = 190元。

但愿您读完本文以后,对区块链的工做原理有一个最基本的认识。感谢阅读。

要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

相关文章
相关标签/搜索