除了最简单的以太坊应用程序之外,所有其他应用程序都由几个智能合约组成。这是因为在任何已部署的智能合约中都会有24KB的硬限制,并且随着智能合约的复杂性增加,你的烦恼也会增加。
你可以将代码分解为可管理的智能合约,您肯定会发现一个智能合约具有仅应由另一个智能合约调用的函数。
例如在Uniswap v2中,只有智能合约因素应该初始化Uniswap对。
Uniswap团队通过一个简单的检查就解决了他们的问题,但是无论我在哪里,我都能找到更多为每个项目从头开始编写编排解决方案的例子。
在理解此问题并开发模式的过程中,我们更好地了解了如何从多个智能合约中构建应用程序,这使Yield更加健壮和安全。
在本文中,我将通过著名项目中的示例深入研究智能合约编排。当您读完它时,您将能够查看您自己项目的编排需求,并决定哪一种现有方法适合您。
我创建这个示例存储库是为了在您准备好时让您继续。
让我们准备开始操作把。
背景
我已经提出,您需要将您的项目分解为一系列智能合约,因为有两个限制,一个是技术上的限制,一个是精神上的限制。
技术限制是在2016年11月实施的。当时,以太坊主网(包括EIP-170)实施了Spurious Dragon硬叉。此更改将已部署的智能合约的大小限制为最大24576字节。
没有此限制,攻击者便可以部署智能合约,从而在部署期间进行无限量的计算。它不会影响存储在区块链中的任何数据,但可以用作对以太坊节点的拒绝服务攻击。
当时限制气体限制不允许使用这种规模的智能合约,因此这种变化被认为是非破坏性的:
“解决方案是对可以保存到区块链中的对象的大小设置硬上限,并通过将上限设置为略高于当前气体限制的可行值来进行无中断操作-案例智能合约可以使用470万个气体以〜23200字节创建,通常创建的智能合约可以达到〜18 kb。”
那是在DeFi爆炸之前。对于Yield,我们编码了2000行智能合约代码,部署后的总和将接近100 KB。我们的审计人员甚至不认为我们的项目非常复杂。
但是,我们仍然必须将项目分解为多个智能合约。
· 复杂性与面向对象程序设计
将区块链应用分解为多个智能合约的第二个原因与技术限制无关,而与人为限制无关。
我们在一个特定的时间只能在大脑中保存那么多信息。如果我们能处理以有限方式相互作用的小问题,我们的表现会比在一个单一的大问题中处理所有事物都能相互作用的问题要好。
可以说,面向对象编程使软件可以达到更高的复杂度。通过定义代表某种概念的“对象”,并将变量和函数定义为对象的属性,开发人员可以更好地从心理上解决他们要解决的问题。
Solidity使用面向对象编程,但在智能合约级别。您可以将智能合约视为具有变量和功能的对象。复杂的区块链应用程序将更容易在您的脑海中描绘为一组智能合约,每个智能合约代表一个实体。
例如在MakerDAO中,每种加密货币都有单独的智能合约,记录债务的另一个智能合约,代表债务库和外部世界之间网关的单独智能合约等。试图在单个智能合约中编写所有代码可能是不可能的。如果可以的话,那也是很难。
将大问题分解为以有限方式交互的小问题确实有帮助。
· 部署
在下一节中,我们将研究Uniswap,MakerDAO和Yield的业务流程实现。这会很有趣的。
· 简单的一个-Uniswap和Ownable.sol
我喜欢Uniswap v2,因为它太简单了。他们成功的在410行智能合约代码中建立了非常成功的去中心化交易所。它们只有两种部署的智能合约:一个factory和不限数量的交易对合约。
由于他们factory合约的设计方式,新的交易对合约的部署需要两个步骤。首先部署智能合约,然后使用将要交易的两个令牌对其进行初始化。
我不知道他们是如何保护自己不受攻击的,但他们需要确保只有创建配对交易合约的factory才能初始化该合约。为了解决这个问题,他们重新实现了Ownable模式。
如果你的案子和他们的一样简单,你也会成功的。如果您知道您的智能合约只需要授予对另一个智能合约的特权访问权,那么您可以使用Ownable.sol. 你甚至不需要使用像Uniswap这样的factory。您可以让一个用户部署两个智能合约(Boss和Minion,Minion继承自Ownable.sol),然后执行minion.transferOwnership(address(boss))。
· 最完整的一个示例-Yield
对于Yield,我们没有设法编写像Uniswap v2一样简单的解决方案。我们的核心是五个智能合约,特权访问关系不是一对一的。一些智能合约具有受限制的功能,我们需要将这些功能提供给核心组中的多个智能合约。
因此,我们只是将Ownable.sol扩展为具有两个访问层,其中之一具有多个成员:
智能合约所有者可以将任何地址添加到特权列表(业务流程)。继承合约可以包括onlyOrchestrated修饰符,该修饰符将限制对注册地址的访问。
作为附加的安全检查,每个地址都与功能签名一起注册,从而缩小了在Orchestrated合同中对单个功能的访问范围。检查示例存储库以获取有关此内容的详细信息。
没有一个函数可以撤消访问权,因为我们会在部署过程中对智能合约进行编排,然后所有者通过对所有智能合约调用transferOwnership(address(0))放弃对自己的特权访问。
我们自己的平台令牌yDai将从Orchestrated继承,并将mint限制为在部署期间设置的特定智能合约:
这种模式相对易于实现和调试,并允许我们实现仅应由我们控制的合约使用的函数。
· MakerDAO
MakerDAO因使用荒谬的术语而臭名昭著,这使其非常难以理解。直到我分解Yield问题之后,我才意识到他们使用的实现几乎完全相同。
1. 智能合约部署者是wards的原始成员。
2. wards可以依靠其他人(usr),以便他们也可以成为wards。
3. 函数可以被限制(auth),这样只有wards才能执行它们。
举例来说,MakerDAO的Vat.sol合约中的fold函数用于更新利率累加器,并且只能由其集合中的另一个合约调用(Jug.sol合约,drip函数)。如果您查看该函数,将看到auth修饰符,这是它们用于业务流程的内容:
在某种程度上,auth和其他业务流程实现是私有和内部功能概念的扩展,仅用于智能合约之间的访问控制。
MakerDAO的实现与我们自己想到的实现非常相似。
1. 智能合约部署者是 wards的原始成员。在收益率中它将是owner。
2. wards可以依靠其他人(usr),以便他们也可以成为wards。在Yield中,只有所有者可以将其他地址指定为特权。
3. 函数可以被限制(auth),这样只有wards才能执行它们。在收益率中,我们说只有经过onlyOrchestrated的地址才能调用标记的函数。我们进一步限制对单个函数的访问。
除了在Yield中我们使用了两个访问层(owner和authorized)和单个函数限制,实现是相同的。智能合约编排是一种通用模式,可以实现一次并经常重用。
为了使审核员和用户更加满意,我们还开发了一个脚本,该脚本可追踪区块链事件并描绘我们智能合约的所有权和业务流程。该脚本可从我们的网站上线获取,并证明除部署时设置的智能合约外,没有人拥有或曾经有特权访问过它们。
毕竟,这就是智能合约编排的重点。
结论
智能合约的编排是一个在大多数项目中都会重复出现的问题,并且大多数项目都是从头开始实施的。通常所实现的解决方案彼此几乎相同。