不知从什么时候起,区块链在网上一下子就火了。

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

我个人觉得,同区块链本身的实现技术相比,更难的事情是如何找到一个合适的业务场景,把区块链集成到SAP产品中去,让它发挥出作用。

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

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

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

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

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

每个区块的哈希值是由该区块所有其他字段的值作为输入,通过SHA1算法计算出来,存储到字段mv_hash里。

链的实现:ZCL_BLOCKCHAIN

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

第一版的所有代码在我的github上。

执行测试程序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的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

300行ABAP代码实现一个最简单的区块链原型的更多相关文章

  1. 50行ruby代码开发一个区块链

    区块链是什么?作为一个Ruby开发者,理解区块链的最好办法,就是亲自动手实现一个.只需要50行Ruby代码你就能彻底理解区块链的核心原理! 区块链 = 区块组成的链表? blockchain.ruby ...

  2. 只用120行Java代码写一个自己的区块链

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区 ...

  3. 40多行python代码开发一个区块链。

    40多行python代码开发一个区块链?可信吗?我们将通过Python 2动手开发实现一个迷你区块链来帮你真正理解区块链技术的核心原理.python开发区块链的源代码保存在Github. 尽管有人认为 ...

  4. 只用200行Go代码写一个自己的区块链!

    Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大 ...

  5. 只用120行Java代码写一个自己的区块链-3挖矿算法

    在本系列前两篇文章中,我们向大家展示了如何通过精炼的Java代码实现一个简单的区块链.包括生成块,验证块数据,广播通信等等,这一篇让我们聚焦在如何实现 PoW算法. 大家都无不惊呼比特币.以太坊及其他 ...

  6. 只用200行Go代码写一个自己的区块链!(转)

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链 ...

  7. Python创建一个简单的区块链

    区块链(Blockchain)是一种分布式账本(listributed ledger),它是一种仅供增加(append-only),内容不可变(immutable)的有序(ordered)链式数据结构 ...

  8. [区块链\理解BTCD源码]GO语言实现一个区块链原型

    摘要 本文构建了一个使用工作量证明机制(POW)的类BTC的区块链.将区块链持久化到一个Bolt数据库中,然后会提供一个简单的命令行接口,用来完成一些与区块链的交互操作.这篇文章目的是希望帮助大家理解 ...

  9. 用Java实现简单的区块链

    用 Java 实现简单的区块链 1. 概述 本文中,我们将学习区块链技术的基本概念.也将根据概念使用 Java 来实现一个基本的应用程序. 进一步,我们将讨论一些先进的概念以及该技术的实际应用. 2. ...

随机推荐

  1. C# 写 LeetCode easy #28 Implement strStr()

    28.Implement strStr() Implement strStr(). Return the index of the first occurrence of needle in hays ...

  2. .NET Core 3.0 可回收程序集加载上下文

    一.前世今生 .NET诞生以来,程序集的动态加载和卸载都是一个Hack的技术,之前的NetFx都是使用AppDomain的方式去加载程序集,然而AppDomain并没有提供直接卸载一个程序集的API, ...

  3. 【原创】智能合约安全事故回顾分析(1):The Dao事件

    首先需要说明的一点是,这个世界上没有绝对安全的技术.在区块链发展的十年里,各种基于区块链的数字货币引发的安全事故层出不穷,这些安全威胁主要来源有三个方面: 自身安全机制的问题,类似智能合约. 生态安全 ...

  4. git分支主干

    ~/Desktop/work/movies/movie(apps) $ git status  //先查看是否有需要提交的东西# On branch appsnothing to commit (wo ...

  5. BestCoder Round #86 1002

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5805 题意:删除数列中一个数,找出相邻之差绝对值最大,求依次删除最大值的和 解法:删除边缘位置的数字需要注 ...

  6. jQuery中ready和load的区别

    <span style="white-space:pre">        </span>//document ready $(document).read ...

  7. PullToRefreshListView

    @Override protected void onRefreshing(final boolean doScroll) { /** * If we're not showing the Refre ...

  8. SPOJ 3267: DQUERY 树状数组,离线算法

    给出q个询问,询问一段区间里面的不同元素的个数有多少个. 离线做,用树状数组. 设树状数组的意义是:1--pos这个段区间的不用元素的种类数.怎么做?就是add(pos,1);在这个位置中+1,就是说 ...

  9. c/c++ socket发送http请求访问网站

    这几天课比较少,校园网上网要认证才能上网,每次必须输入学号密码,为了方便,写了一个自动登录以及如果在线,登录自服务系统强制下线的小工具. 强制下线思路:获取sessionID----------> ...

  10. php 转码函数 你还在用iconv吗?-- 解决sqlserver插入中文失败问题

    文章来源 :http://www.veryhuo.com/a/view/41348.html 这次给客户同步sqlserver数据,临时搭的 PHP Query Analyzer 插入某些中文一直有些 ...