以太坊共享内存(智能合约的并行视角)
3。1 引用
Sergey, Ilya, and Aquinas Hobor。 "A concurrent perspective on smart contracts。" International Conference on Financial Cryptography and Data Security。 Springer, Cham, 2017。
3。2 摘要
在本文中,我们探讨了加密货币中智能合约的多事务行为(如以太坊)与共享内存并发的经典问题之间的显著相似性。 我们从以太坊区块链中检查了两个真实世界的例子,并分析了它们如何容易受到与传统并发程序中经常出现的错误相关的错误的影响。然后,我们详细阐述了可观察的合同行为与经过充分研究的并发主题之间的关系,例如原子性、干扰、同步和资源所有权。文中所描述的合同 - 并发 - 对象类比提供了对智能合同的潜在威胁的更深入理解,表明了更好的工程实践,并且能够应用现有的最先进的形式验证技术。
3。3 技术介绍
尽管区块链上的所有计算都是确定性的,但由于交易本身之间的 竞争(即矿工为给定区块选择了哪些交易),仍然会发生一定量的非确定性。我们将证明,非决定论可以被对抗方利用,区块链平台并使合同行为变得不再确定,让人联想到传统并发编程中涉及的已知挑战。 在本文中,我们概述了一个强调其并发执行属性的智能合约模型。 这样的执行可以跨越多个区块链交易(在同一个区块内或多个区块内)从而违反了仅使用合同的实施和本地状态无法说明的所需安全属性,正是现有验证方法所关注的。 为了便于重用常见的编程直觉,我们提出以下类比: 在区块链中使用智能合约的账户就像在共享内存中使用并发对象的线程。
在共享内存中使用并发对象的线程。对于并发对象,我们指的是广泛 数据结构类,用于在多个线程(进程)并发运行之间交换数据并管 理它们之间的交互。并发对象的典型示例是锁,队列和原子计数器,通常通过流行的库(如 java。util。concurrent)使用。在运行时, 这些并发对象分配在正在运行的线程可访问的共享内存块中。线程同时访问对象所产生的行为可能非常难以预测,因此极难推理。 并发对象的实现没有利用正确的同步(例如,使用锁或障碍),可以在干扰下显示数据争用,从而导致内存完整性的损失。即使对于无竞争对象,从一个或多个客户端的角度来看,在干扰下观察到的行为可能是错误的。例如,特定线程可能不“预见”其他线程对共享对象采取的动作,因此可能不期望该对象以其在干扰下确实改变的所有方式改变。智能合约类似于并发对象。他们存在区块链中,而不是在共享内存中;而不是由线程使用它们由帐户(用户或其他合同)调用。与并发对象一样,它们具有内部可变状态,管理资源(例如资金),并且可以由块内和多个块中的多方访问。 与传统的并发对象不同,由于计算的事务模型,智能合约的方法是原子的。也就是说,对合同的单次调用(或对一系列合同调用的一系列 调用)将按顺序执行,没有中断,并在成功更新区块链或中止之后终止并回滚到之前的配置之前。 然而,“免费原子性”的概念具有欺骗性,因为在区块链的层面上仍然可以观察到并发行为:包含在块中的事务的顺序不是在事务执行时确定的,因此,结果在很大程度上取决于相对于其他事务的排序。
在过去三十年中进行的并发和分布式编程研究为编码,指定,推理和正式验证并发对象及其实现提 了大量理论和应用框架。因此,本文的目标是双重的。首先,我们将简要概述智能合约中可能出现的一些已知并发问题,并根据更传统的并发抽象来描述问题。其次,我们的目标是建立一种“好”和 “坏”合同行为的直觉,可以使用为推理并发而开发的现有形式方法,相应地识别和验证/检测。
在这里,我们讨论已经部署在以太坊区块链上的两个合同,每个合同都说明了并发类型行为的不同方面。与今天以太坊区块链上的许多其他人一样,BlockKing合同实现了一个简单的赌博游戏。虽然 BlockKing 没有被大量使用,但我们研究它是因为它展示了 Oraclize 服务的潜在用途,这是一种允许契约与区块链之外的世界进行通信的服务,从而实现真正的并发。由于 Oraclize 服务的早期采用者将其 作为服务的演示并将其源代码免费提供,因此许多其他希望使用 Oraclize 的合同可能会在其实现中反映出来。 我们讨论的第二个例子是 DAO 合同中广泛研究的错误。DAO 成立了一个业主管理的风险投资基金,超过 18,000 名投资者;在它的高度,它吸引了当时存在的所有以太币的 14%以上。随后的攻击使投资者损失了大约 360 万欧元,当时价值约为 5000 万美元。DAO 采用了我们所谓的“不合作多任务处理”,因为当 DAO 向收件人汇款时,该收件人能够运行代码,干扰 (通过重入)DAO 的合同状态,DAO 假设在呼叫期间不会改变。
3。3。1 BlockKing 合约
BlockKing 的赌博工作原理如下。在任何给定时间都有一个指定的“Block King”(最初是合同的作者)。当发送者 s 将钱发送给合同时,在 1 和 9 之间产生随机数 j。如果模 10 的当前块号等于 j,则 s 成为新的块王。之 后,Block King 在合同中获得一定比例的资金(从 50%到 90%,具体取决 于各种参数),合同的作者将获得余额。 在确定性系统中,通常很难生成高质量的随机数,特别是在所有数据都是公开存储的情况下,并且其中存在针对攻击者的经济激励。因此, BlockKing 利用受信任方 Wolfram Alpha 的服务,使用 Oraclize 服务生成其随机数。假设 Oraclize 表现良好,那么随机数选择策略应该是攻击者难以预测的。 BlockKing 的代码长度为 365 行,但特别感兴趣的行如图 1 所示;这里的行号是指 Etherscan给出的合同的实际源代码。当钱被发送到合同时调用 enter 函数。它设置了一些合约变量(第 299-301 行),然后向 Oraclize 服务发送查询(第 303 行)。 oraclize_query 函数在返回其调用者之前引发在“真实世界”中可见的 事件,然后该调用者退出(行 304)。在现实世界中,Oraclize 服务器监视事件日志,为请求提供服务(在这种情况下通过联系 Wolfram Alpha Web 服务),然后在指定的回调点(BlockKing 中的第 306 行)重新调用原始合同。在事件和它的回调之间,很多事情都可能发生,因为区块链可以在 oraclize_query 调用和回调控制恢复之间推进几个块。在此期间,区块链的状态,甚至 BlockKing 合同本身的状态都会发生巨大变化。换句话说, 这是区块链上的真正并发行为。 什么可能出错?假设多个赌徒希望在短时间内(即使在同一个区 块内)试试运气。合同不会尝试跟踪此行为。因此,每个新参与者将在第 299-301 行覆盖前一个数据(关键 warriorBlock 和 warrior 变量)。当回调确实最终发生时,批次中的最后一个参赛者将有多 次机会赢得那批支付其他回调的早期参赛者的宝座!罪魁祸首是来自 process_payment 函数的第 339-347 行,称为最后一行第 309 行中的回调函数。
图1 BlockKing 代码片段
每次调用 process_payment 函数时,计算 warriorBlock 的最低有效位并将其存储到变量 singleDigitBlock 中。6 每次通过回调调用 process_payment 函数时,他都有新机会匹配第 339 行中的随机数。 如果数字确实匹配,然后最终的选手在 345 号线上加冕。
3。3。2 DAO合约
DAO 的源代码是 1,239 行,明显比 BlockKing 复杂[23]。由于已经 有很多关于这个 bug 的文章(例如[9,27]),我们在图 2 中只给出了关键线。问题是第 1012 行的顺序(通过一系列进一步的函数调用) 将 Ether 发送到 msg。sender,第 1014 行将 msg。sender 帐户的余额 清零。 在顺序程序中,重新排序两个独立的操作对程序的最终行为没有影响。但是,在并发程序中,顺序无害的重新排序的效果会产生显著的影响,因为操作发生的顺序会影响线程的干扰方式。在 DAO 中, 在某些多任务处理中,将 1010 行中的以太网“发送”控制到位于 msg。sender 的任意(因此可能是恶意的)合同。
图2 DAO代码片段
不幸的是,DAO 内部状态仍然表明该帐户已获得资金,因为其帐户余额尚未在第 1014 行归零。因此,恶意的 msg。sender 可以通过回 调 DAO 合同来启动第二次撤销,该合同将在当控制再次到达第 1012 行时,转向发送第二笔付款。实际上,恶意的 msg。sender 然后可以 发起第三次,第四次等撤销,所有这些都将导致付款。只有在结束时,他的账户在支付了原始余额的许多倍后才归零。 以前对此错误的分析表明问题是由于递归或意外的重入。从狭义上讲,这是事实,但从更广泛的意义上说,正在发生的事情是顺序代码在许多意义上的并发环境中运行。
3。4 本文主要贡献
我们相信,我们在智能合约和并发对象之间的类比可以提供新视角,激发研究,并允许有效地重用现有结果,工具和见解,以便理解,调试和验证分布式分类帐中的复杂合同行为。任何类比,我们不应该逐字逐句: 一方面,确实存在并发问题,这在合同编程中似乎难以观察到;另一方面,智能合约实施者也应该注意在并发领域没有直接对应物的概念,例如Gas限制执行和资金管理。
","content_hash"!"e513d0d9
评论