首页 手机兼职平台区块链正文

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池

网赚之家 区块链 2020-11-25 18:31:26 2922 0

MerkleHassanAbdel-Rahman

paymentpool-gas

付出池是一种通用机制,可用于模仿「一对多」或「多对多」付出通道。这个主意是,能够从各种来历将通证存放到池中,然后根据「规矩」(在链上或链外施行) 将池中的通证分配给许多接纳者。本质上,咱们的办法有才干就按很多小额付款汇总到一个结算中,然后节省了很多的 gas 。

考虑一个相似于 Spotify 的场景,在此事例中,每逢有人听一首歌,就向音乐家付出版税。流媒体服务能够用很多通证为音乐家的付出池供给资金。然后,当人们播映音乐时,这些播映日志将在每个付款周期中汇总,汇总的付款金额将被反应到付出池,然后以比在链上买卖(每次听歌时都触发买卖)少的 gas 消耗的办法来付出音乐家之间的版税。

先看看:根据数组的付出池

咱们开端对这种链上付出池智能合约的设想十分简略。

在咱们的 Tally 协议,协议经过「剖析」矿工奉献的 GPU 周期来核算奖赏,和流媒体场景很相似,开端的主意是付出池智能合约将从各种通证搜集智能合约接纳 ERC-20[3] 通证,经过剖析签名的应用程序运用日志和其他链上信号来承认付出池内的通证分配。剖析矿工迁就付出池中的付款金额达到一致,并经过提交一系列收款人地址和付款金额的方式提交给付出池,这些地址和付款金额将写入付出池办理的帐本中。

此处理方案最显着的缺陷是,付出池处理的是无限的收款人及其付款金额,这意味着这种买卖或许会超出 gas 限值。这将需求付出池能监控 gas 预算,并盯梢收款人列表,以便假如超出 gas 预算时,以便能够在后续买卖中持续履行。

咱们进行了一些试验,在当时的区块 gas limit 下,能够支撑 200 多名收款人,再多就会超出 gas 约束,假定按 gas 价格为 35 gwei , gas 限额为 10,000,000,需求消耗 0.35 个 ETH (当时大约 1000 块),相当于存储每个收款人付出的 gas 费是 5 块。

明显,这种办法无法扩展。

Merkle 树

在寻觅更好的办法时,咱们遭到这篇以太坊研讨文章 [4] 的启示

这个主意是,与其让付出池办理收款人及其付款金额的帐本,不如树立一个保存收款人及其付款金额的 Merkle 树[5],然后收款人经过提交 Merkle 证明来提取相应的金额。然后,Merkle 证明将成为仅适用于收款人的密钥,该密钥可在付出池中解锁收款人的通证。

Merkle 树办法的长处在于,咱们只需求向付出池中写入 32 字节的 Merkle 根,而且能够存在 Merkle 树中的收款人数量没有上限。不管 Merkle 树代表多少收款人,咱们都只需求为树写一个 32 字节的 Merkle 根:关于很多收款人, gas 费则能够分计。

咱们中许多人都知道:Merkle 树 [6] 是一种新颖的二叉树结构体,它使咱们能够轻松,廉价地承认树中是否的确存在节点。Merkle 树构成了以太坊的根底,并有助于以太坊节点无需区块链的完好前史即可验证区块的才干。

Merkle 树最重要的方面是:

  1. 每个节点是该节点的子级哈希值之和的哈希值
  2. 根节点的哈希受树中每个节点的影响
  3. 咱们能够经过将节点的哈希值及其「叔叔」节点加在一起,以承认它们是否与根节点匹配,来承认树中是否存在节点。

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 Merkle 根

咱们将关怀的数据放在 Merkle 树的叶节点中。有许多可用的代码库能够履行此操作,给库中供给一个数组,库将对数组进行排序,并运用供给的已排序数组构成 Merkle 树的叶节点来构建 Merkle 树结构体。库会供给 Merkle 树的根,它也能够为任何节点供给证明,其间证明是该节点的哈希与叔叔们 hash 列表,当与节点的哈希值加在一起时,便是默克尔根。

咱们能够验证节点是否的确存在于 Merkle 树中的办法是增加带有其证明的节点,然后检查该成果是否等于根节点。OpenZepplin 有一个 Solidity 库 [7] 用来验证。

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 在此示例中,检查树中是否存在 L2,咱们经过在 hash(L2) 上参加哈希 A 和哈希 B,来承认总和的哈希是否「根节点」的哈希值

Merkle 树付出池

咱们如安在付出池中运用 Merkle 树?

这种办法运用了需求链上和链下机制的办法。为了生成 Merkle 树,咱们能够运用链下程序 (例如 NodeJS 模块) 从收款人及其付款金额列表中构建 Merkle 树。选用这种办法,每个节点都是收款人地址及其付款金额的字符串衔接。

考虑以下收款人清单:

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 收款人清单

咱们能够将此列表转换为如下所示的数组:

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 列表

然后,咱们能够从该列表构建 Merkle 树,合约一切者能够将 Merkle 根提交到付出池合约。而且,咱们也能够在一些当地 (例如 IPFS) 发布这些叶节点及其证明数据,以便收款人能够拜访此数据。

得到如下所示的列表:

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 请注意,这些并不是这些节点的实践 Merkle 证明,而是一些随机的十六进制来传达主意

然后,收款人能够运用金额和证明作为函数的参数来调用付出池合约上的函数,以便提取其通证。

思路是 paymentPool.withdraw() 函数将经过 msg.sender 及通证数量重建叶子节点。withdraw 函数能够哈希该叶节点并将哈希的叶节点增加到证明 (这是构成证明的哈希的十六进制表明)。假如哈希叶节点和供给的证明之和的哈希值等于合约一切者提交的 Merkle 根,那么 paymentPool.withdraw() 函数能够答应将通证从付出池转移到 msg.sender

此外,咱们需求盯梢每个收款人的提款,以便 msg.sender 无法重复调用 paymentPool.withdraw() 函数。

因而,这种办法是一个好的开端。咱们现已解锁了从付出池中付出收款人所需数量通证的功用,而不用发生很多的 gas 费,此外,这意味着咱们付出池中收款人的数量和所需求的 gas 隐晦耦了。收款人的证明基本上就像钥匙相同,仅用于从收款人的地址建议的买卖,可用于从池中解锁该收款人的通证。

可是咱们依然面对一些应战。

  1. 假如收款人只想提取一部分通证怎么办?
  2. 怎么表明链上某个地址可用的通证数量及证明?
  3. 多个付款周期呢?Merkle 根更新后,咱们能够运用旧的证明吗?

改善

为了处理上述应战,咱们在每个收款人的证明中增加了元数据,并在付出池中引入了「付款周期」的概念。

付款周期

在付出池智能合约中,咱们盯梢每个 Merkle 根提交所描绘的付款周期。合约一切者向付出池提交 Merkle 根表明当时付款周期已完毕,新的付款周期开端。

在付出池智能合约中,咱们保护一个映射特点,该特点将付款周期映射到办理该付款周期中在该付出池的 Merkle 根。这样,当用证明调用 paymentPool.withdraw() 函数时,假如咱们知道生成证明所根据的付款周期,则能够运用正确的 Merkle 根来验证证明。

这使收款人能够运用旧的证明来提取付款。一起,这的确意味着证明与特定数量的通证相关。你无法提取超越在叶子节点的哈希值对应的通证数量。

只需付出池盯梢每个收款人已提取多少通证,就能够保证从分配给该收款人的累计通证中减去已提取的通证数量。

证明元数据

要战胜的另一个应战是怎么提取少于创立证明时的通证数量。此外,咱们怎么运用户更简略将证明与特定付款周期相关联,以便能够运用正确的 Merkle 根来验证提款恳求?

针对这些应战,咱们引入了将元数据附加到证明自身的主意。咱们能够将付款周期号和收款人可收到的累计通证数量 (用于在 Merkle 树中生成收款人的付款叶子节点) 合并到证明中。这样,收款人调用 paymentPool.withdraw() 函数指定提取最多金额,该金额不超越证明的通证数量。

简略吧?收款人正在调用 paymentPool.withdraw(),其间包含所需的通证数量以及一个特别的密钥,该密钥仅对他们有用,以从付出池中解锁这些通证。

这是它的工作办法:正如我在上面说到的,证明实践上仅仅一系列已被序列化为十六进制格局的姑妈及叔叔哈希的数组。为了将元数据包含在证明中,咱们需求向该证明数组增加几个额定的项。

具体来说,要增加与 Merkle 根相对应的付款周期号 (咱们能够在将 Merkle 根提交给合约之前,在 PaymentPool 智能合约上调用 paymentPool.numPaymentCycles() 来获得该值) 以及收款人可提取的累计通证数量。在 paymentPool.withdraw() 函数中,需求从证明中剥离元数据,以便 paymentPool.withdraw() 函数知道证明所触及的付出周期以及通证的数量是此收款人 Merkle 树中叶节点的一部分。

这样 paymentPool.withdraw() 函数才干查找到正确的 Merkle 根用作证明,相同经过 msg.sender 及在出现在证明元数据中的通证数量来正确结构叶节点哈希。

出现在 paymentPool.withdraw(amount,proof) 中的金额 (amount) 是收款人期望从所证明的通证总数中提取的通证数量。

以太坊怎么完成低成本多方付出?技能详解 Merkle 树付出池 图:Cardstack 经过元数据验证的 Merkle 树完成的付出池

这种办法还需求供给一个链上函数,答应任何人经过证明的收款人来检查可用于特定证明的通证数量。

重要注意事项

我曾在此处理方案中说到过几回,默克尔树需求盯梢通证的累计数量,这意味着收款人列表及其数量只能随时刻增加-咱们永久都不会看到收款人的累计数量在随后的付款周期中削减。

这是为什么呢?这是这种特定办法的细微差别:咱们为每个付款周期构建的 Merkle 树需求反映收款人的累计付款金额,而且应在付出池中保存取款额的映射,其差额是能够答应收款人供给的任何有用依据提取的部分 (当差额为负时,明显不答应提取)。

假如后续付款周期中的累计付款额削减了,则意味着核算可用提取的通证数量(已提取的金额与证明元数据中的累计总数之差)将是不正确的,并对可用余额发生影响导致他们无法提取一切通证。

该处理方案也很依赖于链下技能,尤其是需求发布收款人的证明 (IPFS 或许是不错的地坊)。你或许还期望发布收款人在付款周期中能够收到的通证数量,乃至或许需求供给 dApp 的链接,该链接能够显现付出池中证明可用的余额。

此外,值得注意的是,在本文中说到的一切处理方案,并未触及怎么保证付出池中的资金已悉数到位,然后使收款人能够接连进行提款。在咱们供给的代码示例中,咱们的保证证付出池有满足的资金,然后再测验将通证调用 payingPool.withdraw() 功用时将通证转移到收款人。这儿能够想到的一种办法是当付出池余额下降到特定阈值以下时宣布付出池通证余额正告事情。

你能够在咱们的 GitHub 代码库 [8] 中找到代码 (用于构建证明和元数据的合约和 javascript 库),代码库中的 README 文件和测验在代码等级演示了怎么运用这种办法。

假如你觉得此处理方案对你有用,欢迎在你自己的合约中运用它,改善它。

Phala-NetworkkUniswap

medium.com

版权声明

本文仅代表作者观点,不代表网赚之家本站立场。
本文系作者授权发表,未经许可,不得转载。

评论