如何用Solidity写一个能够升级(改版)的智能合约

2019-01-06 12:58 栏目:经验之谈 来源: 查看()
总结

Solidity(或者更一般地说,EVM)在软件工程师的生产力和语言技能方面还有很长的路要走。如果你曾经在以太坊上签订过智能合约,你现在应该注意到它,Solidity真的是绑架的一种语言。

特别是如果你从Swift或Javascript转向开发Solidity。开发一个具有Solidity的程序,该语言允许软件工程师做什么,以及表达整个语言的能力,让人们有倒退的感觉。

这种感觉有时会让人感到尴尬。

但为什么它是受限制的语言

可以编译为在EVM(以太网虚拟机)上运行的Solidity和其他字节码受到某些限制,因为:

·如果要执行程序,程序将在整个系统的每个节点上运行。当节点接收到新块时,该节点将验证块的完整性。在以太坊中,这些验证包括此块中包含的所有计算都是正确的,并且合同的每个状态都是正确计算的。

·即使EVM是图灵完备的(甚至直接因为超过了气体限制),这导致大量计算非常昂贵,因为每个节点都需要计算这些计算。结果,整个以太网网络变慢了。

·标准库尚未开发。特别是数组和字符串很难使用。我自己也实现了操作字符串库,只是为了一些我们过去认为理所当然的基本功能。

·除非通过事务(Oracle),否则您编写的合同无法从外部(EVM外部)获取任何数据。当在网络上部署事务时,他没有升级它(除非是通过迁移或纯存储合同)。

这些限制是以太坊必须具备的一些限制(就像你永远无法在Google照片上存储照片备份,或者只是使用链上的计算资源进行图像识别,但这并不重要但是,其他限制将会存在,只是因为它是一种非常新的技术(虽然它真的很快),但技术将继续改进。

好的,那我该怎么解决这个问题呢?

当我们开发项目时,我们可能会在未来对合同进行更改。我们可以通过在不同合同之间超越信息来间接解决这个问题。在继续实施可升级智能合约之前,让我们首先了解它的局限性。

什么是图书馆,为什么我们需要图书馆?

在Solidity中,库是一种特殊的契约,它不存储任何数据,也不存在任何以太坊。有时我们可以尝试将库视为以太坊虚拟机(EVM)的单例(Essence)。这个单例是一段可以被其他契约调用的代码,并且在调用时不需要重新部署该代码。此功能解决了我们面临的一些重大问题,例如:

·所需燃料成本:由于相同的代码不需要一次又一次地部署,一个明显的优点是能够节省大量燃料。不同的合同可以依赖于已部署的同一个库。

·在区块链中重复繁琐的代码:这显然是上述点的好处,部署数量较少,区块链上的记录较少。

·代码升级:在以前的情况下,如果你需要修改程序错误或帮助修改合同,你需要重新部署一个新的,所以合同独立于以前的合同(更糟糕的是,情况与之前相同)执行硬分配)。因此这个问题已经解决了
你是否开始认为图书馆是一个非常强大的东西?不幸的是,图书馆有一些限制。以下是我们必须了解的关于图书馆的一些重要事项:

·无存储容量
·图书馆可以操纵其他合同的存储
·图书馆不能有任何应付职能。
·库不能具有任何回退功能。
·库不能有事件日志。
库可用于为使用它的合同触发事件日志。
·图书馆不是继承的
·虽然库不能直接继承,但库可以链接到其他库,并且库可以像普通合同一样使用。使用单个库。限制仍将存在。

这些要点最初可能听起来令人困惑,但不要气馁。有一个很好的资源可以帮助您理解库。

但至少在下一步中,我们只会使用一些,因为理解和实施升级智能合约必须理解的部分是很好的。

图书馆如何运作?

图书馆是一种特殊的合同。这种合同不具备任何应付功能,也不具备任何后备功能。 (这些限制在编制期间被强制限制,因此您可以使图书馆成为合同。无法持有任何资金)。库由库密钥(库L {})定义,就像合同将由(合同C {})定义一样。

图书馆L {
  函数a()返回(地址){     返回地址(this);
  }
}

合同C {
  函数a()常量返回(地址){    //这将表现为库代码是在本合同中编写的
    返回L.a();
  }
}
要调用库中的函数,必须使用特殊指令(DELEGATECALL),它将调用上下文传递给库,因此它几乎就像是在同一个合同中。合同处理自己的指示。我非常喜欢Solidity文件中的观点。

图书馆可以被认为是其他智能合约的内在基础合同。

从上面的代码中可以看出,如果调用契约C的函数a(),a()将返回契约C的地址而不是库L的地址。这也适用于所有msg属性:msg.sender ,msg.value,msg.sig,msg.data和msg.gas(相反的是写在Solidity文件中,但经过一些实验,看起来像我这样的解释是正确的)。

我们在这里要注意的一件事是我们仍然不知道C类(C类)和库L(库L)是如何连接在一起的,所以我们将尝试理解这个问题。

库是如何连接的?

与显式继承不同,契约C是B {},它链接到库的方式不太清楚。在上面的示例中,Contract C在其自己的函数a()中使用库L.但是,在上面的简短代码中,我们没有提到在库中使用什么地址,并且库L没有出现在编译的块代码(byecode)中。

库的连接发生在位组代码级别。编制合同C时,合同C将为图书馆的地址提供如下形式的保留位置:0073__L_____________________________________ 630dbe671f(0dbe671f是()的功能签名),如果我们不在所有合同中更改合同C.部署了C,这将导致部署失败,因为块代码是非法的。

简单的解释是库连接只是将合同组代码中的所有位置保留到库中,所有这些位置都填充了库的地址。这样完成的位代码可以成功部署到区块链。

好了,现在我们已经介绍了库的基本概念,我们可以理解如何使用库来开发可扩展的智能合约。

图书馆本身无法升级

无法升级库,也无法升级合同。正如我们在本文前面提到的,对库的引用是字节码级别,而不是存储级别。部署合同后,无法修改合同的块代码。因此,对图书馆的引用将与合同一起保留。

所以我们不得不问这个问题,“为什么还有人可以升级这个功能?”

最后,他是如何工作的?

在这里我们将使用一些提示,让我们仔细看看。

如何用Solidity写一个能够升级(改版)的智能合约

我们不直接将用户使用的主合同C连接到部署所需的库,而是将用户使用的主合同C连接到Dispatcher合同。在编译时和部署期间,由于调度程序合同没有在库中实现任何功能,所以一切都不会有问题。这意味着调度程序合同不使用库中的任何代码。调度程序合同只是一个块代码(就像我们在上面的合同C中看到的位组代码一样),在调度程序中。位组代码不需要包含库的地址。我们没有在字节码级别硬编码任何地址,因此我们总是可以用我们需要的任何库替换库。

但是,如果我们不在调度程序合同中使用任何库代码,我们如何在库中实现这些功能?

当事务进入时,主合同(令牌合同)发现事务需要从连接到主合同的库(TokenLib1)调用delegatecall。但是,调用delegatecall并不直接询问该函数。库直接返回,而是通过调度程序合同调用delegatecall。

接下来的事情会变得非常有趣。一旦调度程序合同在其自己的回退函数中收到委托调用,调度程序合同将确定回退函数中需要哪个正确的版本库,然后指示请求将代理调用到正确的库。当库返回数据时,数据将一直返回到主合同。

尽管这种解决方案效果很好,但他仍有一些局限性。

限制

调度程序合同必须知道调用库的内存大小和库返回的值。现在这个问题可以通过映射来解决,函数签名将用于获取返回值的大小。为了简化说明,我们有意避免更多地解释这方面。

上述方法可以在以太坊虚拟机中成功运行,但是如果不同合同之间的存储占用空间相同,我们只能使用它。由于库没有任何存储空间,因此我们必须在调度程序合同中没有存储空间。这就是为什么你需要另一个单独的Dispatcher Storage(见下面的方框)来存储调度程序合同所需的数据。此外,Dispatcher Storage的地址必须写入调度程序合同的位组代码中。

可以发现,用户使用的合同不使用任何特殊技术。唯一的区别是您无法像以前那样连接到特定版本的库,而是连接到计划。合同。

微信二维码
售前客服二维码

文章均源于网络收集编辑侵删

提示:仅接受技术开发咨询!

郑重申明:资讯文章为网络收集整理,官方公告以外的资讯内容与本站无关!
NFT开发,NFT交易所开发,DAPP开发 Keywords: NFT开发 NFT交易所开发 DAPP开发