找回密码
 立即注册

扫一扫,登录网站

首页 自媒体 查看内容
  • 6539
  • 0
  • 分享到

如何在Solidity中设计模块化智能合约

2019-8-30 08:12

来源: bc-tech-lab


在本文中,我将描述如何使用称为目标模式的东西来模块化智能合约。使用标准的solidity,您将学习如何使用abi.encodeSelector和target.call(data)重写脆弱、耦合的调用,以清除模块和分离关注点。为此,我们将以HumanityDAO为例 - 以及他们如何使用目标模式将治理与注册表分开。

HumanityDAO:介绍
HumanityDAO是一个DAO,其目的是人类验证。维护一个说“你是真的”的注册表。它既能让人安心,又在对抗Sybil攻击方面也非常有用。在我目前的工作中,我正在 使用注册表构建像分散式reddit这样的东西,你可以捎带其他DAO所做的工作来打击垃圾邮件(作为一个基本的例子)。一旦我们开始采用这些模式,您就可以看到整个生态系统如何蓬勃发展。

但除了HumanityDAO是一个伟大的dapp,它也是我看到的模块化Solidity设计的第一个例子。但是在这种情况下模块化指的是什么?我们如何实现它?

HumanityDAO中的模块

HumanityDAO维护一个您添加和查询的人的注册表,称为HumanityRegology。注册表本身仅对单个映射(address=>bool)公共人员上操作。以下是添加条目的主要功能:


function add(address whopublic {
    require(msg.sender == governance, 

   "HumanityRegistry::add: Only governance can add an identity");
    require(humans[who] == false

    "HumanityRegistry::add: Address is already on the registry");

    _reward(who);
    humans[who] = true;
}


治理是我们将要研究的下一个模块,它涉及提案和投票。这是基本流程:


  1. Propose(address target, bytes memory data)开始一个新的提议。它会创建一个Proposal,将其添加到列表中,然后发出一个事件。
  2. 用户调用voteyes(uint proposalid)和voteno(uint proposalid)在通过/失败提案时使用其令牌。
  3. 投票期过后,可以调用finalize(uint proposalId)。如果proposal.yesCount> proposal.noCount,则使用数据调用target

为了简单起见,最后一步我将在本指南中引用为目标模式。这不是一种新的编程模式.

EVM:调用合同,发送消息

简要说明 - 以太坊合约在EVM中执行,这是一个消息传递执行环境。因此,当您向地址发送10个wei时,您发送的消息显示{from:me,to:0x123456789,value:10}。当我们在合同上执行调用时,除了附加数据之外我们也是这样做的 - 所以现在消息看起来像{from:me,to:0x123456789,data:“0xcafebabe”,value:10}。这是什么数据?嗯,这是根据Solidity应用程序二进制接口(或简称ABI)编码的调用。


消息构造很好,因为它意味着发送以太和调用智能合约没有什么不同。将钱汇入用户的钱包地址,并将钱汇入合同,看起来一样。唯一的区别是,合同的地址不是从公钥生成的(而是从nonce生成)。


因为我们在区块链中,而且我们更愿意以不变的方式进行引用,所以智能合约中的方法由它们的选择器引用。选择器只是函数签名哈希的一部分。


target模式

目标模式非常简单,但尚未被广泛使用,因为Solidity具有这些特殊名称。它也可以称为动态调度(Ruby,Obj-C),回调(JS),无论你想要什么; 


它基本上包括:


  • 你正在构建的模块,例如治理

  • 你正在实施的事情,例如注册表

  • 目标整合点- 即传递提案将添加到注册表


我们将从HumanityDAO的例子中学习如何实现这一目标。


综上所述,我们的目标是:在通过治理投票之后,在注册表中添加一个条目。


在我们将它们组合在一起之前,下面是两个难题,治理和注册模块:


contract Registry {
    mapping (address => bool) public humans

    function add(address whopublic {
        require(msg.sender == governance, "HumanityRegistry::add: Only governance can add an identity");
        humans[who] = true;
    }
}


contract Governance {
    function propose(address target, bytes memory data

     public returns (uint{

        uint proposalId = proposals.length;
        Proposal memory proposal;
        proposal.target = target;
        proposal.data = data;
        proposals.push(proposal);
    }

    function vote(public /* ... */ }

    function finalize(uint proposalIdpublic {
        Proposal storage proposal = proposals[proposalId];
        require(proposal.result == Result.Pending, 

       "Governance::finalize: Proposal is already finalized");

        if (proposal.yesCount > proposal.noCount) {
            // Call the target contract with the data
            proposal.target.call(proposal.data);
        }
    }
}



我们可以使用Registry.add添加到注册表中。治理的基础是提交一份提案,对提案进行投票,然后调用finalize并通过/失败。如果提案通过,我们将数据中的calldata调用target合同。


那么我们如何构建这个calldata呢? 你会认为这是非常复杂的,即Solidity


不! 我们可以通过一个名为abi.encodeWithSelector的东西来做到这一点:


bytes memory data = abi.encodeWithSelector(registry.add.selector, who);
governance.propose(address(registry), data)


就像那样,目标被设置为注册表合同,并且看起来像Registry.add(who)的调用被编码并存储在提议中。 这真的很简单。

使用wrapper
还有一件事 - 因为我们已经在谈论良好的设计和可组合性 - 我建议在一个单独的合同中用一种方法wrapper这个调用。 这个wrapper智能合约是一个可以在前端调用的简单方法。

我们将建议的代码流命名为什么,并可能将某人添加到注册表中? HumanityDAO称之为HumanityApplicant:


contract HumanityApplicant {
    Governance governance;
    Registry registry;

    constructor(Governance _Governance, Registry _Registry) {
        governance = Governance(_Governance);
        registry = Registry(_Registry);
    }

    function addToRegistry(address who{
        bytes memory data = abi.encodeWithSelector(registry.add.selector, who);
        return governance.propose(msg.sender, address(registry), data);
    }
}


结论

就是这样! 使用此功能,您可以轻松获取治理合同并在您自己的设计中使用它。


版权申明:本内容来自于互联网,属第三方汇集推荐平台。本文的版权归原作者所有,文章言论不代表链门户的观点,链门户不承担任何法律责任。如有侵权请联系QQ:3341927519进行反馈。
相关新闻
发表评论

请先 注册/登录 后参与评论

    回顶部