【区块链扩容】侧链技术 Plasma(Layer 2)

Plasma 作为以太坊的二层扩容框架,自从 2017 年被 Joseph Poon(Lightning Network 创始人)和 Vitalik Buterin (Ethereum 创始人)提出以来[1],一直是区块链从业人员关注的焦点[2]。首先需要明确的是,Plasma 实质上是一套框架,而不是一个单独的项目,它为各种不同的项目实际项目提供链下(off-chain)解决方案。这也是为什么很多人对 Plasma 感到疑惑的一个重要原因,因为在缺乏实际应用场景的情况下很难将 Plasma 解释清楚。
因此,理解 Plasma 是一套框架是理解 Plasma 的关键。

Plasma 框架

从区块链扩容谈起

在介绍 Plasma 之前,不得不先介绍区块链扩容。我们都知道,比特币(Bitcoin)和以太坊(Ethereum)作为目前最广泛使用的区块链平台,面临的最大问题就是可扩展性(Scalability)。这里需要注意的是,区块链中的可扩展性问题并不是单独特指某个问题,而是区块链想要实现 Web3.0[3] 的愿景,为亿万用户提供去中心化服务所要克服的一系列挑战。虽然以太坊号称是“世界计算机”,但这台“计算机”却是单线程的,每秒钟只能处理大约 15 条交易,与目前主流的 Visa 和 MasterCard 动辄每秒上万的吞吐量相比实在相形见绌。因此如何在保证区块链安全性的情况下,提高可扩展性是目前区块链发展亟待解决的问题之一。

目前关于区块链扩容的解决方案无外乎两个方向:一层(Layer 1)扩容和二层(Layer 2)扩容[4]。一层扩容也被称为链上(on-chain)扩容,顾名思义,这类扩容方案需要更改区块链底层协议。但同时也意味着需要将区块链硬分叉。这类扩容方案就像将原来的单核 CPU 改装成多核 CPU,从而可以多线程处理计算任务,提高整个网络的吞吐量。

目前最典型的一层扩容方案是 Vitalik 和他的研究团队提出的“Sharding(分片)”,也就是说将区块链划分成不同的部分(shards),每个部分独立处理交易。想要了解更多关于 Sharding 的信息,可以参考以太坊官方的 Wiki[5]

二层扩容也称链下(off-chain)扩容,同样非常好理解,这种扩容方案不需要修改区块链底层协议,而是通过将大量、频繁的计算工作转移到“链下”完成,并定期或在需要时将链下的计算结果提交到“链上”保证其最终性(finality)。二层扩容的核心思想是将底层区块链作为共识基础,使用智能合约或者其它手段作为链下和链上沟通的桥梁,当有欺诈行为发生时链下的用户仍然可以回到链上的某一状态。虽然将计算转移到链下会在一段时间内损失最终性,但这个代价是值得的,因为这样做不止可以极大提高区块链的灵活性和可扩展性,也极大降低了用户进行交易所需要的代价。将计算转移到链下也并不意味着完全放弃安全性,因为最终的安全性还是由底层所依赖的区块链来保证,因此二层扩容主要关注的问题就在于如何保证链上链下切换过程的安全性。这种思想最早被用在闪电网络(Lightning Network)当中作为比特币的其中一个扩容方案,并取得了很好的效果。

本文所要介绍的 Plasma 就属于基于以太坊二层扩容方案,类似的解决方案还有 State Channels 和 Trubit。这些方案虽然面向的问题有所区别,但基本思想都是将复杂的计算转移到链下进行。那么,接下来我们将进入 Plasma 的世界,一窥究竟!

理解 

在前文中我们已经明白 Plasma 是一种二层扩容框架,那么该如何进一步理解 Plasma 是什么?它区别于其它二层扩容方案的地方在哪呢?

Plasma 也被称为“链中链(blockchains in blockchains)”。任何人都可以在底层区块链之上创建不同的 Plasma 支持不同的业务需求,例如分布式交易所、社交网络、游戏等。

这里可以举一个例子来理解 Plasma。假如企鹅公司创建了一个 Plasma Chain 叫作 Game Chain。用户通过向 Game Chain 发送一些以太币换取 Token,用于购买皮肤等游戏内的增值商品。加入 Game Chain 的操作是在链上进行的,以太坊区块链将这部分资产锁定,转移到 Game Chain 上。之后每次我们购买虚拟商品的交易就会转移到链下进行,由企鹅公司记账。这种方式几乎跟我们现实生活中游戏内购的体验一样,不仅结算迅速,而且手续费低廉(相比于以太坊之上需要给矿工支付的手续费)。那么问题来了,如果企鹅公司从中作祟,修改账本,恶意占有我们的资产怎么办?这时我们就可以提交之前每次交易的凭证到以太坊区块链上,如果确实是企鹅恶意篡改了账本,那么我们不但能够成功取回自己的资产,还能获得之前企鹅公司创建 Game Chain 存入的部分或全部押金。

通过上面这个例子是不是已经明白 Plasma 大致是如何工作的了?但上面这个例子还是过于简单,只涉及用户自己和企鹅公司。下面我们使用区块链的语言对上面的例子进行解析。

首先,企鹅公司在以太坊主链之上创建了一系列智能合约作为主链和子链 Game Chain 通信的媒介。这些智能合约中不仅规定了子链中的一些基本的状态转换规则(例如如何惩罚作恶的节点),也记录了子链中的状态(子链中区块的哈希值)。之后企鹅公司可以搭建自己的子链(可以用以太坊搭建一套私链)。子链实际上是一个完全独立的区块链,可以拥有专门的矿工,使用不同于主链的共识算法,例如 PoS(Proof of Stake)等。

当子链创建完毕后,企鹅公司可以使用 ERC721 合约创建 token 作为游戏内的商品(就像 Cryptokitty)。但这里需要注意的是,所有数字资产必须在以太坊主链上创建,并通过 Plasma 子链的智能合约转移到子链中。用户也需要在主链上购买数字资产后转移到子链上。在上面这个例子中,Game Chain 的智能合约将主链上的资产锁定,之后在子链上生成等值的资产。之后用户就可以完全脱离主链,在子链上进行交易。企鹅公司在子链上扮演 operator 的角色,如果一切运行正常,子链中的矿工会正常打包区块,并在需要时由 operator 将区块的哈希值提交到主链作为子链的状态更新证明。在这个过程中,用户完全不需要和主链交互。

我们可以看到,将复杂的计算操作转移到链下确实使得整个交易过程变得简单。但没有强大的共识算法和庞大的参与者,资产在子链上是很不安全的。Plasma 给了我们一种避险机制,即使 operator 作恶,我们也能取回属于自己的资产。下图(来源自[1])简单说明了这个过程。图中,在第 4 个区块中的交易被篡改。由于 Alice 本地保存有 Plasma Chain 中所有的区块数据,因此她可以向主链提交一个含有“防伪证明(Fraud Proof)”的交易。如果证明生效,那么主链将状态从 4 号区块回滚到 3 号区块,一切恢复正常。Plasmas Chain 中的参与者也可以随时提交资产证明,返回到主链。

到这里我们应该已经理解了,Plasma 所要做的工作并不是保护子链的安全,而是当有安全事故发生时,保证用户可以安全地取回自己的资产,并返回到主链上。并且采用一系列经济激励的方式减少作恶情况的发生

Plasma细节

我们已经理解了什么是 Plasma 框架以及它是如何运行的,下面将对其运行过程中的一些关键部分,包括 Plasma 提交区块的过程,当有恶意行为发生时如何构建防伪证明以及如何退出 Plasma 子链等进行剖析。需要注意的是,由于 Plasma 是一套框架,因此本文只剖析 Plasma 项目的共性,每一部分的实现细则还是需要参考实际的项目,例如 Plasma MVP(Minimal-Viable-Plasma)和 Plasma Cash 等。

存款 (Deposit)

Plasma 的主要思想就是将大部分计算过程都转移到链下进行,用户只有在进入和退出 Plasma Chain 的时候需要跟主链上的智能合约交互,这也是所有 Plasma 应用的标准流程。

用户在将主链的资产(如以太币或者其它 ERC20 合约发布的 token)转移到 Plasma Chain 的过程称为存款(Deposit),具体做法是直接向主链上的 Plasma 合约发送以太币或 token。Plasma 合约收到 Deposit 交易后会在子链上创建跟 Deposit 数额一致的交易,并将其打包进区块中,作为存款确认的证明。这个过程如下图所示(来源自[1])。

当用户看到子链上自己之前存款的交易被确认后,就可以在子链上使用这笔资产(给子链上的其他用户发送交易或者退出子链等)。

状态确定(State Commmitment)

当大部分都转移到链下进行时,需要某种机制确保链下状态的更新得到确认,这样才能保证当有恶意行为发生时,主链可以保证用户不会受到损失。这就是为什么需要状态确认(State Commitment),即子链周期性地将状态更新情况提交到主链进行共识。

然而,将子链中的所有交易都同步到主链显然违反了 Plasma 的初衷,在 Plasma 中,实际向主链提交的是 Merkle Tree 的根哈希。因此子链中的实际交易情况被隐藏,在主链上只能看到子链区块的哈希值。

当有恶意行为发生时,子链网络中的所有用户都可以向主链提交防伪证明,证明成立后,含有恶意交易的区块将被回滚。

防伪证明(Fraud Proof)

Plasma 的一个关键设计之一就是允许用户构造防伪证明(Fraud Proof)。防伪证明的意义在于只要发布区块的节点构造了含有恶意交易的区块,那么就要承担被惩罚的风险。每当新的区块被提交到主链上时,会留出一段时间给用户提交防伪证明,如果在这段时间内没有证明被提交,则认为新的区块被验证合法。如果有防伪证明检测到区块中存在恶意交易,则该区块将被舍弃,回滚到上一个被验证合法的区块。Plasma 中的防伪证明主要有以下(但不限于)几种:

  • 资产可花费证明
  • 交易签名有效性证明
  • 存取款证明

至于每种防伪证明的具体形式,则依赖于实际 Plasma 应用的实现细则。

如下图所示(来源自[1]),子链中每个节点都存有 1-4 个区块的数据。假设区块 1-3 已经被验证合法,而区块 4 中存在恶意交易,那么每个节点都可以使用 1-4 个区块中的数据构造防伪证明提交到主链,主链验证后将子链中的状态回滚到区块 1-3。

防伪证明还可以使用零知识证明(zk-SNARKs 或者 STARKs)来构造,但由于目前通过零知识证明生成证明的时间和空间还有待优化,目前设计的 Plasma 并不依赖零知识证明。零知识证明在 Plasma 中的应用是一个很有前景的研究方向,感兴趣的读者可以参考以太坊研究团队关于这方面的研究[2])。

TODO:  这个是怎么判断防伪证明 Fraud Proof 就是对的呢?

取款(Withdrawal)

取款(Withdrawal),顾名思义,就是从子链中的资产取回到主链上,因此取款也被称为退出(Exit)。取款操作可以说是 Plasma 框架中最重要的一步,因为它确保用户可以安全地将子链上的资产转移到主链上。之前的存款以及状态确认步骤已经产生了一些交易数据,并在主链上得到同步,用户可以利用这些数据构造资产证明,之后执行简单取款(Simple Withdrawal)操作退出子链。当有扣留(Withholding)攻击发生(即子链上的矿工恶意扣留区块,意图双花攻击等)时,用户可能无法成功获取数据构造资产证明,这时需要执行批量取款(Mass Withdrawal)操作退出子链。

需要注意的是,当子链中有取款操作发生时,跟这个取款操作相关的账号或者交易都将被禁止。

简单取款(Simple Withdrawal)

执行简单取款的条件是所要取回的资产已经在主链和子链上确认。

一个简单取款的执行主要有以下几个步骤:

  • 向主链上的 Plasma 智能合约发送已签名的取款交易。取款的数额可以包含多个输出(UTXO模型),但所有输出必须在同一个子链当中,而且每个输出的余额必须全部取出,不能只取出一部分。取款数额的一部分还需要被当作押金,作为恶意行为的惩罚。
  • 当取款请求发送后,需要等待一段“争议期(Challenge Period)”,这期间其它节点可以构造证据证明该取款中的部分或全部数额已经被花费。如果争议期内有节点提供证明成功,那么取款被取消,而且押金被扣除。
  • 接下来可能还要等待一段时间,直到所有区块高度较低的取款操作都完成。这是为了保证所有取款操作按照“先来后到”的顺序执行。
  • 当争议期结束后,如果没有争议被提出,则认为取款操作成立,取款者将子链资产成功取回到主链。

TODO: 怎么保证争议期,会有人举报呢(激励机制?), 争议期长短设定怎么决定?

快速取款(Fast Withdrawal)

快速取款(Fast Withdrawal)跟简单取款相比的差别主要是引入一个中间人,白皮书上称为 Liquidity Provider,这里简称为 LP。如果一个用户不想等待很长的争议期(目前的实现至少要一周),那么它可以选择从 LP 这里直接取款,只需要消耗一个交易确认的时间,代价是需要支付给 LP 一定的费用。由于 Plasma 白皮书上关于快速取款的描述太过晦涩,这里主要参考 kfichter 提出的 Simple Fast Withdrawal[3] 来介绍快速取款是如何实现的。

为了实现快速取款,取款方和 LP 可以利用一个流动合约(liquidity contract)。假设取款方是 Alice,她想要执行快速取款将 10 以太币从子链转移到主链。她首先向流动合约发送 10 以太币,注意这里的交易是在子链上进行的。当这个交易被子链打包成区块后,Alice 可以调用合约中的某个退出函数,这时 Alice 将获取一个代表她这笔资产的 token。Bob 作为 LP,他检查了子链上数据之后证明 Alice 的取款没有问题之后愿意以 9 以太币的价格购买这个 token。Alice 将 token 卖给 Bob,获得了 9 以太币,Bob 赚取了 1 以太币。

需要注意的是,实现快速取款的前提条件是没有拜占庭行为发生,即没有扣留区块攻击发生,因为 LP 需要验证取款方的交易历史。

TODO:  类似与中介(中间商)了吧。很像买卖二手车

批量取款(Mass Withdrawal)

当子链中有拜占庭行为(例如,区块扣留攻击)发生时,将会影响以后生成防伪证明,因此网络中的每个用户都有责任快速退出子链。虽然批量取款(Mass Withdrawal)操作不是必要选择,但当大量用户执行取款时很可能会造成主链拥塞,也会消耗更多的 gas,因此批量取款是当子链受到攻击时更好的选择。

批量取款操作由于所采用的模型(UTXO 模型或者账户模型)不同会有较大的差别,而且目前关于批量取款的操作细节也正在研讨当中,因此这里只对批量取款做简单介绍,想要了解目前研究状态可以参考[4]

当子链中有拜占庭行为发生时,用户之间可以共同协作执行批量取款。这时会有一个节点扮演取款处理人(Exit Processor)的角色,简称为 EP,负责当前某个批量操作(可以同时有多个批量取款操作发生,但同一个取款申请不能存在于多个批量取款),并且可以收取服务费作为报酬。EP 将构造一个位图(bitmap,即一串0/1)记录哪些资产要执行取款。之后 EP 将利用现有的区块数据检查每个取款是否合法,之后将构造一个批量退出初始化交易(Mass Exit Initiation Transaction,MEIT),并将其发送到主链上。在 MEIT 被主链确认之前,每个用户都可以对这个交易提出异议。当争议期结束,MEIT 被主链确认,批量取款成功。

Plasma MVP(验证实现)

本节将详细介绍 Plama 的第一个项目 Plasma MVP(Minimal Viable Plasma),即在 Plasma 框架下的最基础的实现。Plasma MVP 是 Vitalic 和他的团队在 2018 年初提出的基于 UTXO 模型实现的 Plasma 设计标准[1],它以最简单的方式实现了链下交易,但无法支持复杂的计算,例如脚本(Script)和智能合约。

整个 Plasma MVP 的生命周期可以通过下面这幅图表现出来:

Plasma 合约

首先需要将 Plasma 合约部署到主链(以太坊)上作为主链和子链沟通的媒介。Plasma 合约会处理由子链提交的区块,并且将区块的哈希值存在主链上。除此之外,还会处理用户的存款(deposit)、取款(withdrawal/exit)以及争议(challenge)操作。

Plasma 合约中主要包括的数据结构有:

  • Owner:合约的拥有者(即部署合约交易的发送者)的地址,即部署合约交易的发送者;
  • Plasma 区块列表:每个 Plasma 区块中存储了(1)区块的 Merkle root(2)区块提交的时间;

    Plasma 区块或许叫区块记录更准确,因为它是指在合约的区块记录。

  • 退出列表:即提交了退出申请的列表,每个退出申请存储了(1)申请者的地址(2)申请退出的 UTXO 的位置。

Plasma 合约中主要包括的函数有:

  • submitBlock(bytes32 root):向主链提交一个区块,仅仅提交区块中所有交易的 Merkle root;
  • deposit():生成一个只包含一个交易的区块,这个交易中包含与 msg.value 值相等的 UTXO;
  • startExit():执行给定 UTXO 的退出操作;
  • challengeExit():向某个正在执行的退出提出争议。

Operator

在前面的文章中我们已经知道 Plasma 子链是一个独立的区块链,那么也就有独立的共识机制。在 Plasma MVP 中采用的共识机制就是 PoA(Proof of Authority),即参与共识的只有唯一一个矿工,称为 Operator。Operator 负责处理所有子链上发生的交易,将其打包成区块存储在子链上,并且周期性地向 Plasma 合约提交区块,将子链上的状态(区块的哈希值)提交到主链共识。那么,既然 Operator 是唯一的矿工,这不就意味着 Plasma 违背了去中心化的初衷了吗?其实,这是去中心化向执行效率的妥协。在之前的文章中也提到过,Plasma 的安全基础依赖于底层的区块链,只要底层的区块链能够保证安全,那么在 Plasma 子链上发生的最差结果也只是迫使用户退出子链,而不会造成资产损失。

Operator 可以采用最简单的 REST API 方式实现,子链中的用户可以通过调用简单的 API 获取到子链中区块的数据。

存款 (deposit)

用户 Alice 通过存款(deposit)操作向 Plasma 合约发送带有一定数额的以太币或 ERC20 token 加入 Plasma Chain,这时 Plasma 合约会执行 deposit() 函数,生成一个只包含一个交易的区块,这个交易的 UTXO 记录了 Alice 从主链转移到子链的数额。当这个区块被主链确认后,Alice 就可以使用新生成的 UTXO 向其它用户发送交易了。

交易(transaction)

在 Plasma MVP 中,所有用户发送的交易都是直接发送给 Operator,当积累了一定数量的交易后,由 Operator 将交易打包成区块。这里需要注意的是,由于 Plasma MVP 采用的是 UTXO 模型,所以即使交易的收款方不存在,交易也是成立的。

在子链上 Alice 向 Bob 发送一个交易的流程如下:

  1. Alice 首先需要得到 Bob 在子链上的地址;
  2. Alice 将一个或多个 UTXO 作为输入构造交易发送到 Bob 的地址,并对交易签名;
  3. 等待该交易被打包到区块中;
  4. Alice 向 Bob 发送确认消息,并且使用相同的私钥签名。

生成 Plasma区块

在 Plasma MVP 中,一个 Plasma 区块 产生的情况只有两种:一种是 Operator 打包生成区块,向Plasma合约提交;另外一种是当用户执行 deposit 操作时,由 Plasma 合约直接生成一个只包含一个交易的 Plasma区块

Plasma 区块 含义参考前面 Plasma 合约包含的数据结构。

监视子链

为了保证子链上资产的安全,用户需要周期性地检查子链上的数据,保证没有恶意交易产生。用户需要运行一种自动化的软件(例如钱包),每隔一段时间下载子链中的区块数据,检查每个区块中的交易,如果有恶意交易产生,立即退出子链。

取款/退出(withdrawal/exit)

当 Alice 想要退出子链时,需要向 Plasma 合约发送一个 exit 交易,申请中需要包含:
(1)所要退出的 UTXO 的位置,包括区块号(blknum)、区块内交易号(txindex)以及交易内输出号(outindex)
(2)包含该 UTXO 的交易
(3)该交易的 Merkle proof
(4)用于生成该 UTXO 所涉及的之前一系列交易的确认签名。除此之外,exit 交易中还要包含“退出押金(exit bond)”。如果这个 exit 被 challenge 成功,那么取款的操作将被取消,而且退出押金将被发送给提出 challenge 的用户。

之后这个申请会被放入一个优先队列中,通过这个公式计算优先级:

Priority = blknum * 1000000000 + txindex * 10000 + oindex

之所以采用这种优先队列的方式处理取款顺序的原因是保证旧的 UTXO 总能优先于新的 UTXO 被取出。也就是说,当有恶意交易(例如双花等)产生时,所有在恶意交易发生之前的交易都可以被优先取出。那么如何解决在恶意交易之后被确认的交易的取款问题呢?Plasma MVP 采用了“确认签名(Confirmation Signatures)”的机制。

确认签名(Confirmation Signatures)

在 Plasma MVP 中,用户的退出顺序以所要退出的 UTXO 所在的交易的位置为准。假如 operator 作恶,在一个合法的交易之前插入一个非法的交易,那么当用户执行取款时,由于非法交易可以先被取出,因此当执行到该用户的交易时,可能 Plasma 合约中的资产已经被取空。为了解决这个问题,Plasma MVP 采用了“确认签名”机制,例如当 Alice 产生一个交易时,她首先会对交易签名。当该交易被打包入区块后,Alice 还需要对该交易进行一次签名,即“确认签名”。

引入确认签名机制后,当 Alice 发现在一个区块中自己的合法交易之前存在非法交易时,可以拒绝对自己的交易进行“确认签名”,同时申请取款。这样可以使得当前的交易失效,保证自己之前“确认签名”后的交易可以优先于非法交易之前取出。

这种确认签名机制极大地破坏了用户体验,用户每产生一个交易都要经历签名->等待确认->确认签名。而且由于确认签名也需要占据 Plasma 区块的空间,因此也降低了子链的可扩展性。为了解决这个问题,Plasma 的研究人员提出了扩展版本 More Viable Plasma 移除了确认签名的要求[2]

争议(Challenge)

每个取款操作都会经历一个争议期。例如在 Alice 的某个 UTXO 退出子链的过程中,如果 Bob 在争议期内发现有恶意行为发生,他可以提出一个争议(challenge)。一个争议需要给出针对的 UTXO 的位置,以及该 UTXO 被花费的证明,即该 UTXO 已经存在于某个交易中,且这个交易已经被打包到区块。

合约通过调用 challengeExit() 函数执行一个争议,争议成功后会取消正在执行的取款操作,并将提交取款申请所冻结的押金发送给 Bob。

攻击场景

在 Plasma 子链中主要存在两种攻击场景:

  1. Alice 试图忽视在子链中转移给 Bob 的资产,使用最初加入 Plasma 子链时的交易证明向主链提出取款申请。
  2. Operator 生成一个恶意交易,占有其他用户的资产,并且尝试退出子链。

下面对这两个攻击场景进行分析,观察 Plasma MVP 如何保证资产的安全性:

攻击场景1:

  1. Alice 使用最初加入子链时生成的交易作为证据向主链提出取款申请;
  2. Bob(或者其他任意用户)拥有 Alice 申请退出的 UTXO 被花费的交易证明,并将此作为证据向主链提出一个争议;
  3. 争议生效,Alice 的退出申请被驳回,同时将 Alice 申请退出的押金发送给 Bob;
  4. Alice 的攻击失效。

攻击场景2:

  1. Operator 创建了一个非法交易,并且将其打包生成区块之后在主链得到确认;
  2. Operator 提交取款申请,打算将 Alice 的资产取走;
  3. 在争议期内,Alice 发现了 Operator 的恶意行为,立即提出取款申请,退出子链;
  4. 由于 Alice 的申请优先级较高,因此会在 Operator 之前退出;
  5. Operator 的攻击失效。

相关Plasma MVP 实现项目

Talk is cheap, show me your code.

目前已经有许多机构和公司已经实现了 Plasma MVP,但实现的语言和细节有所不同:

总结

本文介绍了 Plasma 的最小实现版本 Plasma MVP,虽然采用最简单的 UTXO 模型,但已经足够体现出 Plasma 的核心思想。在 Plasma MVP 中,用户资产的安全主要依赖于用户及时发现恶意行为,并退出子链。接下来的文章将会介绍另外一个稍微复杂一点的项目,Plasma Cash。

Plasma Cash

由于 Plasma MVP 本身过于简单,并不能用于实际的生产环境中。2018 年 3 月,在巴黎举行的以太坊开发者大会上,Vitalik 发布了 Plasma Cash 模型[1],可以视为对 Plasma MVP 的改进。Plasma Cash 与 Plasma MVP 的主要区别是每次存款操作都会产生一个唯一的 coin ID 对应转移到侧链上的资产,并使用一种称为稀疏梅克尔树(Sparse Merkle Tree)的数据结构存储交易历史。由此带来的好处是用户不需要关注子链上的每个动态,只需要关注跟自己的 token 有关的动态。在下文中将介绍具体细节。

存款(Deposits)

Plasma Cash 中的每次存款操作都会对应产生一个 NFT(non-fungible token)[2]。NFT 可以简单理解为“不可互换的 token”,即每个 token 都是独一无二的,由唯一的 ID 标记。以太坊官方为 NFT 提供了 ERC721 标准[3],在之前火爆到阻塞以太坊的 CryptoKitties 就是由 ERC721 合约实现的。

在 Plasma Cash 中,当用户向 Plasma 合约发送存款交易时,合约会生成一个与存款等值的 token,并给这个 token 分配一个唯一的 ID。如果一个用户分别执行两次存款操作,且每次存款都是 5 ETH,那么他将得到相等价值的两个完全不同的 token。和 Plasma MVP 一样,每次存款操作都会使得 Plasma 合约产生一个只包含这个存款交易的区块。

这里的区块及下文的区块指的是子链区块对应在主链合约中的记录。

Plasma Cash 区块

Plasma Cash 中的每个 token 都被分配唯一的 ID,因此可以按 ID 的顺序存储每个 token 的交易历史。Plasma Cash 的区块按 token ID 的顺序给每个 token 分配了一个插槽(slot),每个插槽会记录这个 token 是否被交易的信息。例如在下图(来源[4])的区块中,包含 4 个 token,id 分别是 #1,#2,#3,#4。其中 #1,#2,#3 被标记为没有被花费,而 #4 由用户 A 发送给用户 B。

从上面这个例子中我们可以看到,每个插槽记录了其所对应的 token 在当前区块中的交易状态,所有存储了某个 token 的交易状态的区块按时间顺序连在一起就构成了这个 token 的全部交易历史。每当一个 token 被分配了一个 id,之后的所有交易状态都会被保存在每个区块相同的插槽中,也不会被其它 token 取代。因此,用户只需要关注每个区块中存储属于自己的 token 的状态,完全不用关心别的插槽存储的内容。

交易与验证

由于 Plasma Cash 中的节点只追踪属于自己的 token 的交易历史,因此当有交易发生时,token 的发送者要向接收者提供关于这个 token 所有的交易历史(从存款交易开始)以便接收者验证。从下图(来源[4])的例子中可以看到 4 个区块中所记录的 4 个 token 的交易历史。

截止到区块 #4,可以看到token #1 和 token #3 始终没有被交易。token #2 在区块 #2 被 E 发送给了 F,在区块 #4 被 F 发送给了 G,在其它区块没有发生交易,token #2 的最终所有权归 G。token #4 在区块 #1 被 A 发送给了 B,在区块 #3 被 B 发送给了 C,在其它区块没有发生交易,token #4 的最终所有权归 C。F 为了向 G 证明 token #2 的合法性,需要向 G 提供 token #2 在前 4 个区块中的所有交易历史,也就是说不仅需要包括区块 #2 中 E => F 的交易证明、区块 #4中 F => G 的交易证明,还要包括在区块 #1 和 #3 中没有被交易的证明。到这里可能感觉有点奇怪,为什么还要包括没有被交易的证明?这是为了防止双花,因为 G 并不知道在区块 #1 和 #3 中 token #2 是否被交易给了其它人。假如 F 在区块 #3 中将 token #2 发送给了 H,并且对 G 隐瞒了这个交易,那么发生在区块 #4 中的 F => G 就是非法(双花)的。因此,在 Plasma Cash 中,完整且合法的交易历史是一个 token 被安全交易的前提。

没有被交易的证明?那怎么才能证明没有被交易呢。

稀疏梅克尔树(Sparse Merkle Tree)

在上文中我们已经了解到一个交易的成功的前提是需要发送方提供关于一个 token 的完整交易历史。完整的交易历史既包括这个 token 在哪些区块被交易的信息,也包括这个 token 在哪些区块没有被交易的信息。我们都知道,在区块链中,使用梅克尔树(Merkle Tree,MT)构造梅克尔证明(Merkel Proof, MP)可以在 O(logN)的时间复杂度验证一个交易是否存在一个区块中。但想要证明一个交易没有存在一个区块中,使用标准的梅克尔树却没那么容易。因此,Plasma Cash 中使用了一种称为稀疏梅克尔树(Sparse Merkle Tree,SMT)的数据结构存储交易数据,能够在O(logN)的时间复杂度验证一个交易不存在。

SMT 实际上一点都不复杂,它的叶子节点是按数据集中的元素序号顺序排列的。如果某个叶子节点对应的元素为空,那么该叶子节点将存储一个特定的值(例如 0 的哈希值)。一个简单的 SMT 示例如下图(来源[5])所示。

扩展到 Plasma Cash 中,SMT 的叶子节点对应了区块中给每个 token 分配的插槽,按照每个 token 的 ID 排序。每个叶子节点存储对应的 token 的交易信息,如果 token 在这个区块中没有被交易,则相应的叶子节点存储的值为 null

以上图为例,如果需要证明交易 A 存在,就像在标准的 MT 中一样,需要构造 MP:H(null) 和 H(H(null) + H(D))。如果需要证明 B 不存在,同样很简单,我们已经知道 B 的位置是第二个叶子节点,如果 B 不存在,那么该节点存储的值应该为 null。因此就像在标准的 MT 中证明存在的 MP 一样,只不过需要加上 H(null) 作为 MP 的一部分,即 MP:H(null)、H(A)和 H(H(null)+H(D))。

取款/退出(Withdrawl/Exit)

Plasma Cash 中的取款操作在流程上跟 Plasma MVP 大体相同,都要从提交取款申请开始,经历争议期之后才能完成。由于 Plasma Cash 中采用的数据结构不同,在取款时需要提交的 token 所有权证明不同,因此当争议发生时需要提交的争议证明也不同。

提交取款申请

在向 Plasma 合约提交关于某个 token 的取款申请时,需要提供关于这个 token 最近的两次交易证明。例如,在上图中,假如 G 想要取走 token #2 到主链,那么他需要提交关于 F => G 以及 E => F 的 Merkle Proof。

提交争议

取款者在提交了取款申请之后同样需要支付一定的保证金,并等待一段时间的争议期。在这期间如果有其它节点提交了有效的争议证明,那么取款者不但无法完成取款操作,也会损失全部或部分的保证金。

目前 Plasma Cash 支持三种争议证明,分别应对三种不同的攻击场景(具体会在后文分析):

  1. 已花费证明。如果能证明正在取款的 token 已经被花费,那么取款立即被取消;
  2. 双花证明。如果能证明取款申请中提供的两次交易证明中间还有别的交易,即发生了双花,那么取款立即被取消;
  3. 非法交易历史证明。用户还可以对正在取款的 token 的其它交易历史提出争议。这种争议不会立刻阻断取款,而是强制取款者提交其它交易证明来反驳争议,如果没有在规定时间内反驳,则取款被取消。

攻击场景

在这一节将讨论已有的 3 种攻击场景以及如何构造争议分别应对这些攻击[6]。在这里假设 Plasma Cash 中存在不可信的 operator 接收所有的交易并构造区块。

发送交易后立即退出

如下图(来源[7])所示,假设攻击者 Alice 向 Bob 发送了一个 token A,且 Bob 已经验证了 A 的交易历史没有问题,交易在区块 N+X 得到确认。在这之后,Alice 立即提交取款申请,企图将 token A 取回主链,并提交 A 在区块 N 以及之前的交易证明。为了应对这种情况,Bob 必须及时发现 Alice 的取款行为,并且在争议期结束前提交在区块 N+X 中 token A 被 Alice 发送给 Bob 的证明。这里需要注意的是,如果 Bob 在区块 N+Y 将 token A 发送给 Charlie 的交易是不能被当做争议证明的,只有最接近被争议的交易的下一个交易证明有效。

双花攻击

双花攻击需要 operator 配合,将含有已经被花费的 token 的交易打包入下一个区块中。如下图所示(来源[7]),攻击者 Alice 和 Charlie 是同谋,Alice 向 Bob 发送一个 token A 在区块 N+X 被确认,之后 Alice 又将 token A 发送给 Charlie,并在区块 N+Y 被确认。这时在主链看来,Bob 和 Charlie 都是 token A 的合法拥有者。接下来,Charlie 立即提交取款申请,企图取走 token A。Bob 为了防止自己的 token 被盗,可以在争议期内提交在区块 N+X 被确认的交易,表明自己在 Charlie 之前已经拥有了 token A。

取款包含非法交易历史

这种攻击需要联合比较多的同谋者。如下图所示,Alice 在区块 N 拥有 token A。Bob 联合 operator、Charlie 以及 Dylan 企图盗走 Alice 的 token。首先,operator 伪造 Alice 将 token A 发送给 Bob 的交易,并在区块 N+X 得到确认,之后 Bob 将 token 发送给 Charlie,在区块 N+Y 确认。同样地,Charlie 接着将 token 发送给 Dylan,在区块 N+Z 确认。这是,Dylan 提出取款申请,企图取走 token A。Dylan 用于取款申请的两个交易证明 Charlie => Dylan 和 Bob => Charlie 都是合法的,但 token A 的交易历史中有一部分是伪造的。Alice 为了证明自己是 token A 的最新合法拥有者,可以提出争议,要求 Dylan 提供 Alice => Bob 的交易证明,同时 Alice 需要提交一部分保证金(否则任何人都可以随便提出争议)。Dylan 必须在一定的时间内提供合法的交易证明,否则取款失效。

相关项目

Talk is cheap, show me your code.

目前已经有许多机构和公司已经实现了 Plasma Cash,但实现的语言和细节有所不同:

总结

本节介绍了 Plasma 框架下的基于 NFT 的项目 Plasma Cash。Plasma Cash 给每个新转移的 token 分配一个唯一的 token ID,并且用稀疏梅克尔树存储交易,使得用户可以只关注跟自己的 token 有关的动态,而不需要关注其它 token。Plasma Cash 可以被看作 Plasma 逐渐迈向成熟的一步,已经有很多公司使用 Plasma Cash 搭建自己的平台和应用,例如 Loomnetwork 公司搭建了自己的 Plasma Cash 子链并且编写了 SDK 支撑开发者在上面开发新的应用。然而 Plasma Cash 本身仍然存在较多的问题,例如 token 无法被分隔合并、需要提交的证明过长等。

以太坊扩容技术1 · 区块链技术导航-开发资源整理 (learnblockchain.cn)

Plasma学习资源

Plasma 以太坊的二层扩容框架

猜你喜欢

转载自blog.csdn.net/RenLJ1895/article/details/124370250