区块链怎么实现以太坊通证的多签合约

发布时间:2022-01-19 10:03:38 作者:iii
来源:亿速云 阅读:150

本篇内容介绍了“区块链怎么实现以太坊通证的多签合约”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

需求描述

有一天,老板给辉哥提了一个需求,希望能够实现一个安全的代币支出多签功能,便于基金会治理审核。

汇总而言,产品需求描述如下:

(1)治理委员会由4人组成,一项CLB代币支出需要所有4人同意才可以转账打出; (2)为了避嫌中心化作恶,治理委员会的退出只能由委员本人账号操作有效; (3)当治理委员会无法正常运作时,老板有权撤回治理委员会管理的剩余代币。

多签钱包市场调研信息

辉哥首先完成市场上商用硬件钱包的调研工作。

结论:

目前(2018.08.24)世面上暂时没有体验好的多签硬钱包,不过等1个月后比特派硬件钱包更新会支持ETH和ERC20代币的多重签名功能。

具体信息:

多签智能合约代码及解析

多签合约核心代码如下:

contract ColorbayMultiSign {

   using SafeMath for uint256;

   uint256 public MAX_OWNER_COUNT = 50;

   event Confirmation(address indexed sender, uint256 indexed transactionId);
   event Revocation(address indexed sender, uint256 indexed transactionId);
   event Submission(uint256 indexed transactionId);
   event Execution(uint256 indexed transactionId); 
   event ExecutionSuccess(uint256 indexed transactionId);
   event ExecutionFailure(uint256 indexed transactionId);
   event OwnerAddition(address indexed owner);
   event OwnerRemoval(address indexed owner);
   event RequirementChange(uint256 required);

   mapping (uint256 => Transaction) public transactions;
   mapping (uint256 => mapping(address => bool)) public confirmations;
   mapping (address => bool) public isOwner;
   address[] public owners;
   uint256 public required;
   uint256 public transactionCount;
   address public creator;

   ERC20 public token;

   struct Transaction {
       address destination;
       uint256 value;
       bool executed;
   }   /*omit some modifier function*/

   /**
    * @dev Contract constructor sets initial owners and required number of confirmations.
    * @param _owners List of initial owners.
    * @param _required Number of required confirmations.
    */
   constructor(address _token, address[] _owners, uint256 _required) public validRequirement(_owners.length, _required)
   {
       token = ERC20(_token);        require(_owners.length <= 100);        for (uint256 i=0; i<_owners.length; i++) {  
           require(!isOwner[_owners[i]] && _owners[i] != address(0));             
           isOwner[_owners[i]] = true;
       }
       owners = _owners;
       required = _required;
       creator = msg.sender;
   }   

   /** 
    * @dev Allows to remove an owner. Transaction has to be sent by wallet.
    * @param owner Address of owner.
    */
   function removeOwner(address owner) public ownerExists(owner)
   {        /*only owner can delete itself*/
       require(owner == msg.sender);

       isOwner[owner] = false;        for (uint256 i=0; i<owners.length.sub(1); i++) {            if (owners[i] == owner) {
               owners[i] = owners[owners.length.sub(1)];                break;
           }
       }            
       owners.length = owners.length.sub(1);        if (required > owners.length) {
           changeRequirement(owners.length);
       }            
       emit OwnerRemoval(owner);
   }    
   /** 
    * @dev Withdraw the token remained to the constructor address.
    */
   function withdrawToken() public onlyCreator{        if( 0 < token.balanceOf(address(this))) {
          token.transfer(creator, token.balanceOf(address(this)));
       }
   }    

   /** 
    * @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
    * @param _required Number of required confirmations.
    */
   function changeRequirement(uint256 _required) private validRequirement(owners.length, _required)
   {
       required = _required;
       emit RequirementChange(_required);
   }    /** 
    * @dev Allows an owner to submit and confirm a transaction.
    * @param destination Transaction target address.
    * @param value Transaction ether value.
    * @return Returns transaction ID.
    */
   function submitTransaction(address destination, uint256 value) public returns (uint256 transactionId)
   {
       transactionId = addTransaction(destination, value);
       confirmTransaction(transactionId);
   }    /** 
    * @dev Allows an owner to confirm a transaction.
    * @param transactionId Transaction ID.
    */
   function confirmTransaction(uint256 transactionId) public ownerExists(msg.sender) transactionExists(transactionId) notConfirmed(transactionId, msg.sender)
   {
       confirmations[transactionId][msg.sender] = true;
       emit Confirmation(msg.sender, transactionId);
       executeTransaction(transactionId);
   }    /**  
    * @dev Allows an owner to revoke a confirmation for a transaction.
    * @param transactionId Transaction ID.
    */
   function revokeConfirmation(uint256 transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId)
   {
       confirmations[transactionId][msg.sender] = false;
       emit Revocation(msg.sender, transactionId);
   }    /** 
    * @dev Allows anyone to execute a confirmed transaction.
    * @param transactionId Transaction ID.
    */
   function executeTransaction(uint256 transactionId) public notExecuted(transactionId)
   {        if (isConfirmed(transactionId)) {
           Transaction storage ta = transactions[transactionId];
           ta.executed = true;            if(token.transfer(ta.destination, ta.value)) {
               emit ExecutionSuccess(transactionId);
           } else {
               emit ExecutionFailure(transactionId);
               ta.executed = false;
           }
       }
   }    /** 
    * @dev Returns the confirmation status of a transaction.
    * @param transactionId Transaction ID.
    * @return Confirmation status.
    */
   function isConfirmed(uint256 transactionId) public view returns (bool)
   {
       uint256 count = 0;        for (uint256 i=0; i<owners.length; i++) {            if (confirmations[transactionId][owners[i]]) {
               count = count.add(1);
           }                
           if (count == required) {                return true;
           }                
       }
   }    /** 
    * @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
    * @param destination Transaction target address.
    * @param value Transaction ether value.
    * @return Returns transaction ID.
    */
   function addTransaction(address destination, uint256 value) internal notNull(destination) returns (uint256 transactionId)
   {
       transactionId = transactionCount;
       transactions[transactionId] = Transaction({
           destination: destination,
           value: value,
           executed: false
       });
       transactionCount = transactionCount.add(1);
       emit Submission(transactionId);
   }    /** 
    * Web3 call functions
    * @dev Returns number of confirmations of a transaction.
    * @param transactionId Transaction ID.
    * @return Number of confirmations.
    */
   function getConfirmationCount(uint256 transactionId) public view returns (uint256 count)
   {        for (uint256 i=0; i<owners.length; i++) {            if (confirmations[transactionId][owners[i]]) {
               count = count.add(1);
           }
       }            

   }    /** 
    * @dev Returns total number of transactions after filers are applied.
    * @param pending Include pending transactions.
    * @param executed Include executed transactions.
    * @return Total number of transactions after filters are applied.
    */
   function getTransactionCount(bool pending, bool executed) public view returns (uint256 count)
   {        for (uint256 i=0; i<transactionCount; i++) {            if (pending && !transactions[i].executed || executed && transactions[i].executed) {
               count = count.add(1);
           }
       }           

   }    /** 
    * @dev Returns list of owners.
    * @return List of owner addresses.
    */
   function getOwners() public view returns (address[])
   {        return owners;
   }    /** 
    * @dev Returns array with owner addresses, which confirmed transaction.
    * @param transactionId Transaction ID.
    * @return Returns array of owner addresses.
    */
   function getConfirmations(uint256 transactionId) public view returns (address[] _confirmations)
   {
       address[] memory confirmationsTemp = new address[](owners.length);
       uint256 count = 0;        for (uint256 i=0; i<owners.length; i++) {            if (confirmations[transactionId][owners[i]]) {
               confirmationsTemp[count] = owners[i]; 
               count = count.add(1);
           }
       }            
       _confirmations = new address[](count);        for (i=0; i<count; i++) {
           _confirmations[i] = confirmationsTemp[i];
       }

   }    
   /** 
    * @dev Returns list of transaction IDs in defined range.
    * @param from Index start position of transaction array.
    * @param to Index end position of transaction array.
    * @param pending Include pending transactions.
    * @param executed Include executed transactions.
    * @return Returns array of transaction IDs.
    */
   function getTransactionIds(uint256 from, uint256 to, bool pending, bool executed) public view returns (uint256[] _transactionIds)
   {
       uint256[] memory transactionIdsTemp = new uint256[](transactionCount);
       uint256 count = 0;        for (uint256 i=0; i<transactionCount; i++) {            if (pending && !transactions[i].executed || executed && transactions[i].executed)
           {
               transactionIdsTemp[count] = i;
               count = count.add(1);
           }
       }            
       _transactionIds = new uint256[](to.sub(from));        for (i=from; i<to; i++) {
           _transactionIds[i.sub(from)] = transactionIdsTemp[i];
       }

   }

}

各个函数的定义说明参考类图:

区块链怎么实现以太坊通证的多签合约

多签合约类图

核心函数说明:

多签智能合约场景测试

编译通过后,按照实际业务场景,辉哥做了一下完整测试。测试流程如下:

区块链怎么实现以太坊通证的多签合约

业务流程

具体的操作流程如下,均达到预期目标,本多签合约具备商用能力。

测试数据

代币

账号

申请人:

1. 创建多签合约

1) 创建代币合约Colorbay

切换到管理员账号0xca3...a733c下,创建代币合约Colorbay,创建完成后,复制合约地址备用。试用者也可以创建自己的ERCC20合约,记住地址即可。

0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

2)创建多签合约

切换到管理员账号0xca3...a733c下,创建多签合约。输入CLB合约地址,将审批委员会名单导入(管理员、辉哥、欧阳哥哥、ELLA),配置需要4个确认个数(也称必签数,即需要审批4次才能通过)

constructor("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", ["0xca35b7d915458ef540ade6068dfe2f44e8fa733c","0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db","0x583031d1113ad414f02576bd6afabfb302140225"], "4")

如果必签数<委员会成员数,将不能完成任何事务的审批,必须通过新增委员会成员(见用例8)才能完成

将多签合约地址复制下来备用:

0xbbf289d846208c16edc8474705c748aff07732db

3)给多签合约地址充值CLB

回到CLB合约中,切换到管理员账号0xca3...a733c下,给多签合约地址充值2000000CLB

transfer("0xbbf289d846208c16edc8474705c748aff07732db", "2000000,000000000000000000") #约定:运行时,去除数额中的逗号,否则会出错

4)查询多签合约地址代币余额

balanceOf("0xbbf289d846208c16edc8474705c748aff07732db")

5)查询剩余发行总量

balanceOf("0xca35b7d915458ef540ade6068dfe2f44e8fa733c")

2、发起待审批事务

仅审批委员会成员可操作

1)作为申请人阿汤哥0xdd8...92148,向管理员提出申请,需要10000个CLB做为活动运营激励。切换到管理员账号0xca3...a733c下(可以是任意审批委员会账号)

submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,000000000000000000")

编号为0的事务申请提交完成后,即管理员已经审批通过了这个事务,待其他3个审批通过。

2)作为申请人阿汤哥0xdd8...92148,直接自己发起待审批事务。切换到阿汤哥账号0xca3...a733c下(任意非审批委员会账号),将会报错,因为普通用户不能发起待审批事务

submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,0000000000000000000000")

3)查询当前编号为0的事务有几人审批确认了

getConfirmationCount(0)

4)查询当前编号为0的事务有哪些委员做审批确认了

getConfirmations(0)

3、 进入审批流程

1)编号为0的事务还需要3个审批确认,现在辉哥、欧阳哥哥开始审批编号为0的事务,分别切换到辉哥账号、欧阳哥哥账号0xca3...a733c下,

confirmTransaction(0)

辉哥再次审批将会报错,因为已经审批过了

重复用例3第3、4步

2)辉哥后悔了,想要撤销审批,切换到辉哥账号0xca3...a733c下:

revokeConfirmation(0)

重复用例3第3、4步

3)审批委员会说服了辉哥,切换到辉哥账号0xca3...a733c下,辉哥再次做审批通过操作:

重复用例5第1步

4、完成最后1次审批,同时执行转账,结束事务

1)切换到ELLA账号0x583...40225下,完成最后1次审批确认,同时将执行转账操作(已经满足4次确认)

2)查询阿汤哥到账情况

回到CLB合约,查询阿汤哥账号0xdd8...92148余额

balanceOf("0xdd870fa1b7c4700f2bd7f44238821c26f7392148")

3)查询发行总量

重复用例1第5条

4)查询编号为0的事务是否已经处于完成状态

isConfirmed(0)

5、 删除审批委员会成员

说明:不管是在审批流程中操作,还是在审批结束后操作,移除成员时,遵循以下规则:

1)辉哥由于个人原因,要退出治理审批委员会。他在自己账号下调用多签合约的函数 0x147...c160c

removeOwner("0x14723a09acff6d2a60dcdf7aa4aff308fddc160c")

6、 取回合约代币

后来,该治理委员会开始无法有效运作,部分人员审批并不及时,严重影响了业务进展。老板决策把剩余代币打回到管理账号,结束该审计委员会工作和审批权限。

“区块链怎么实现以太坊通证的多签合约”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

推荐阅读:
  1. 以太坊智能合约怎么写
  2. 以太坊通证标准ERC有哪些

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

区块链 以太坊

上一篇:SAP MM Reset预留单上的Deletion Flag是怎样的

下一篇:html5中有哪些常用框架

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》