以太坊DAO之时间锁定Multisig

2023-10-29

Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。

有时候,时间也可以用作一种很好的安全机制。以下代码基于DAO区块链大会,但有不同的变化。不是每个操作需要X个成员批准,而是任何交易都可以由单个成员发起,但它们在执行之前都需要最少的延迟,这取决于交易的支持。提案的批准越多,就越早执行。会员可以对交易进行投票,这意味着它将取消其他一个已批准的签名。

时间锁定Multisig

这意味着如果你没有紧急程度,则执行任何交易可能只需要一个或两个签名。但是,如果单个密钥被泄露,其他密钥可以将该交易延迟数月或数年,甚至可以阻止其执行。

这个怎么运作

所有密钥都已批准的交易可以在十分钟后执行(此金额是可配置的),并且每5%未投票的成员每次需要的时间加倍(如果他们主动投票,则为四倍)反对)。如果它是一个简单的ether交易,只要支持投票将其置于所需的时间内,就会执行交易,但更复杂的交易将要求使用正确的字节码手动执行交易。这些是默认值,但在创建合约时可以设置不同的值:

批准交易的成员数量:近似时间延迟

  • 100%批准:10分钟(最低默认值)
  • 90%批准:40分钟
  • 80%:2小时40分钟
  • 50%:大约一周
  • 40%:1个月
  • 30%:4个月
  • 20%:超过一年
  • 10%或更少:5年或从不
    一旦最短的时间过去,任何人都可以执行交易(参见“国会”以获得更完整的步行)。这是故意的,因为它允许某人安排交易或雇用其他人来执行交易。

代码:

pragma solidity >=0.4.22 <0.6.0;

contract owned {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address newOwner) onlyOwner public {
        owner = newOwner;
    }
}

contract tokenRecipient {
    event receivedEther(address sender, uint amount);
    event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData);

    function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public {
        Token t = Token(_token);
        require(t.transferFrom(_from, address(this), _value));
        emit receivedTokens(_from, _value, _token, _extraData);
    }

    function () payable external {
        emit receivedEther(msg.sender, msg.value);
    }
}

interface Token {
    function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
}

contract TimeLockMultisig is owned, tokenRecipient {

    Proposal[] public proposals;
    uint public numProposals;
    mapping (address => uint) public memberId;
    Member[] public members;
    uint minimumTime = 10;

    event ProposalAdded(uint proposalID, address recipient, uint amount, string description);
    event Voted(uint proposalID, bool position, address voter, string justification);
    event ProposalExecuted(uint proposalID, int result, uint deadline);
    event MembershipChanged(address member, bool isMember);

    struct Proposal {
        address recipient;
        uint amount;
        string description;
        bool executed;
        int currentResult;
        bytes32 proposalHash;
        uint creationDate;
        Vote[] votes;
        mapping (address => bool) voted;
    }

    struct Member {
        address member;
        string name;
        uint memberSince;
    }

    struct Vote {
        bool inSupport;
        address voter;
        string justification;
    }

    // Modifier that allows only shareholders to vote and create new proposals
    modifier onlyMembers {
        require(memberId[msg.sender] != 0);
        _;
    }

    /**
     * Constructor
     *
     * First time setup
     */
    constructor(
        address founder, 
        address[] memory initialMembers, 
        uint minimumAmountOfMinutes
    ) payable public {
        if (founder != address(0)) owner = founder;
        if (minimumAmountOfMinutes !=0) minimumTime = minimumAmountOfMinutes;
        // It’s necessary to add an empty first member
        addMember(address(0), '');
        // and let's add the founder, to save a step later
        addMember(owner, 'founder');
        changeMembers(initialMembers, true);
    }

    /**
     * Add member
     *
     * @param targetMember address to add as a member
     * @param memberName label to give this member address
     */
    function addMember(address targetMember, string memory memberName) onlyOwner public
    {
        uint id;
        if (memberId[targetMember] == 0) {
            memberId[targetMember] = members.length;
            id = members.length++;
        } else {
            id = memberId[targetMember];
        }

        members[id] = Member({member: targetMember, memberSince: now, name: memberName});
        emit MembershipChanged(targetMember, true);
    }

    /**
     * Remove member
     *
     * @param targetMember the member to remove
     */
    function removeMember(address targetMember) onlyOwner public {
        require(memberId[targetMember] != 0);

        for (uint i = memberId[targetMember]; i<members.length-1; i++){
            members[i] = members[i+1];
            memberId[members[i].member] = i;
        }
        memberId[targetMember] = 0;
        delete members[members.length-1];
        members.length--;
    }

    /**
     * Edit existing members
     *
     * @param newMembers array of addresses to update
     * @param canVote new voting value that all the values should be set to
     */
    function changeMembers(address[] memory newMembers, bool canVote) public {
        for (uint i = 0; i < newMembers.length; i++) {
            if (canVote)
                addMember(newMembers[i], '');
            else
                removeMember(newMembers[i]);
        }
    }

    /**
     * Add Proposal
     *
     * Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code.
     *
     * @param beneficiary who to send the ether to
     * @param weiAmount amount of ether to send, in wei
     * @param jobDescription Description of job
     * @param transactionBytecode bytecode of transaction
     */
    function newProposal(
        address beneficiary,
        uint weiAmount,
        string memory jobDescription,
        bytes memory transactionBytecode
    )
        onlyMembers public
        returns (uint proposalID)
    {
        proposalID = proposals.length++;
        Proposal storage p = proposals[proposalID];
        p.recipient = beneficiary;
        p.amount = weiAmount;
        p.description = jobDescription;
        p.proposalHash = keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode));
        p.executed = false;
        p.creationDate = now;
        emit ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription);
        numProposals = proposalID+1;
        vote(proposalID, true, '');

        return proposalID;
    }

    /**
     * Add proposal in Ether
     *
     * Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code.
     * This is a convenience function to use if the amount to be given is in round number of ether units.
     *
     * @param beneficiary who to send the ether to
     * @param etherAmount amount of ether to send
     * @param jobDescription Description of job
     * @param transactionBytecode bytecode of transaction
     */
    function newProposalInEther(
        address beneficiary,
        uint etherAmount,
        string memory jobDescription,
        bytes memory transactionBytecode
    )
        onlyMembers public
        returns (uint proposalID)
    {
        return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode);
    }

    /**
     * Check if a proposal code matches
     *
     * @param proposalNumber ID number of the proposal to query
     * @param beneficiary who to send the ether to
     * @param weiAmount amount of ether to send
     * @param transactionBytecode bytecode of transaction
     */
    function checkProposalCode(
        uint proposalNumber,
        address beneficiary,
        uint weiAmount,
        bytes memory transactionBytecode
    )
        view public
        returns (bool codeChecksOut)
    {
        Proposal storage p = proposals[proposalNumber];
        return p.proposalHash == keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode));
    }

    /**
     * Log a vote for a proposal
     *
     * Vote `supportsProposal? in support of : against` proposal #`proposalNumber`
     *
     * @param proposalNumber number of proposal
     * @param supportsProposal either in favor or against it
     * @param justificationText optional justification text
     */
    function vote(
        uint proposalNumber,
        bool supportsProposal,
        string memory justificationText
    )
        onlyMembers public
    {
        Proposal storage p = proposals[proposalNumber]; // Get the proposal
        require(p.voted[msg.sender] != true);           // If has already voted, cancel
        p.voted[msg.sender] = true;                     // Set this voter as having voted
        if (supportsProposal) {                         // If they support the proposal
            p.currentResult++;                          // Increase score
        } else {                                        // If they don't
            p.currentResult--;                          // Decrease the score
        }

        // Create a log of this event
        emit Voted(proposalNumber,  supportsProposal, msg.sender, justificationText);

        // If you can execute it now, do it
        if ( now > proposalDeadline(proposalNumber)
            && p.currentResult > 0
            && p.proposalHash == keccak256(abi.encodePacked(p.recipient, p.amount, ''))
            && supportsProposal) {
            executeProposal(proposalNumber, '');
        }
    }

    function proposalDeadline(uint proposalNumber) public view returns(uint deadline) {
        Proposal storage p = proposals[proposalNumber];
        uint factor = calculateFactor(uint(p.currentResult), (members.length - 1));
        return p.creationDate + uint(factor * minimumTime *  1 minutes);
    }

    function calculateFactor(uint a, uint b) public pure returns (uint factor) {
        return 2**(20 - (20 * a)/b);
    }

    /**
     * Finish vote
     *
     * Count the votes proposal #`proposalNumber` and execute it if approved
     *
     * @param proposalNumber proposal number
     * @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it
     */
    function executeProposal(uint proposalNumber, bytes memory transactionBytecode) public {
        Proposal storage p = proposals[proposalNumber];

        require(now >= proposalDeadline(proposalNumber)                                         // If it is past the voting deadline
            && p.currentResult > 0                                                              // and a minimum quorum has been reached
            && !p.executed                                                                      // and it is not currently being executed
            && checkProposalCode(proposalNumber, p.recipient, p.amount, transactionBytecode));  // and the supplied code matches the proposal...


        p.executed = true;
        (bool success, ) = p.recipient.call.value(p.amount)(transactionBytecode);
        require(success);

        // Fire Events
        emit ProposalExecuted(proposalNumber, p.currentResult, proposalDeadline(proposalNumber));
    }
}
部署和使用

像以前一样在这些教程上部署该代码。在部署参数上,将最小时间留空将默认为30分钟,如果你想要更快的锁定时间,则放1分钟。上传后,执行“添加成员”功能以添加组的新成员,他们可以是你认识的其他人,也可以是不同计算机上的帐户或离线存储。

设置为所有者owner的帐户非常强大,因为它可以随意添加或删除成员。因此,在添加主成员后,我们建议你通过执行Transfer Membership功能将owner设置为另一个帐户。如果你希望对所有成员的添加或删除进行投票,则将其设置为multisig本身,就像任何其他交易一样。另一种方法是将其设置为另一个受信任的multisig钱包,如果你希望永久修复成员数,则可以设置为0x000。请记住,此合约上的资金仅与“所有者”帐户一样安全。

与上述任何DAO一样,此合约可以持有以太币,任何基于以太坊的代币并执行任何合约。为此,请检查如何在国会DAO上执行复杂的提案。

警告和改进

为简单起见,对提案的投票仅仅算得少一点支持。如果你愿意,你可以玩弄负面投票更重要的想法,但这意味着少数成员可以对任何提议的交易拥有有效的否决权!

你怎么能改善这个合约?

我们去探索吧!

你已经到了本教程的末尾,但这只是一次伟大冒险的开始。回顾一下,看看你取得了多少成就:你创造了一个活生生的,有说服力的机器人,你自己的加密货币,通过无信息的众筹筹集资金,并用它来启动你自己的个人民主组织。

接下来会发生什么?

  • 你仍然控制的代币可以在分散的交易所出售,或者交易商品和服务,以资助第一份合约的进一步发展和发展组织。
  • 你的DAO可以在名称注册商处拥有自己的名称,然后更改它重定向的位置,以便在代币持有者批准时自行更新。
  • 该组织不仅可以拥有醚类,还可以拥有在以太坊上创造的任何其他类型的硬币,包括其价值与比特币或美元相关的资产。
  • 可以对DAO进行编程,以允许具有多个交易的提案,其中一些预定在未来。它还可以拥有其他DAO的股份,这意味着它可以对更大的组织投票或成为DAO联盟的一部分。
  • 代币合约可以重新编程为持有以太或持有其他代币并将其分发给代币持有者。这会将代币的价值与其他资产的价值联系起来,因此只需将资金转移到代币地址即可实现支付股息。

这一切都意味着你创造的这个小社会可以成长,从第三方获得资金,支付经常性工资,拥有任何类型的加密资产,甚至使用众筹为其活动提供资金。所有这些都具有完全透明,完全的问责制和完全免受任何人为干扰。当网络存在时,合约将完全执行它们被创建的代码,而没有任何例外,永远执行。

那么你的合约是什么?它会是一个国家,一个公司,一个非营利组织吗?你的代码会做什么?

随你,由你决定。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是原文以太坊DAO之时间锁定的Multisig

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以太坊DAO之时间锁定Multisig 的相关文章

  • 对抗验证概述

    了解如何实施对抗性验证 以建立分类器来确定您的数据是来自训练还是测试集 如果可以这样做 则您的数据有问题 并且对抗验证模型可以帮助您诊断问题 如果您要在Kaggle上研究一些获胜的解决方案 则可能会注意到对 对抗性验证 的引用 像这样 它是
  • QT5无法输入中文,如何解决???

    环境 UBUNTU 12 04 LTS 最近在Ubuntu下安装了QT5 0 2版本 在尝试完跑实例程序后 自己想做一个Socket的TCP IP通信例子 但是悲剧的发现QT5中竟然不能切换输入法到中文 所做尝试包括 1 换输入法fctix
  • 分页组件封装

    scss 分页 el pagination text align right margin top 20px span el pagination total position absolute left 0 is background e

随机推荐

  • 白盒测试基本方法

    白盒测试的概述 由于逻辑错误和不正确假设与一条程序路径被运行的可能性成反比 由于我们经常相信某逻辑路径不可能被执行 而事实上 它可能在正常的情况下被执行 由于代码中的笔误是随机且无法杜绝的 因此我们要进行白盒测试 白盒测试又称结构测试 透明
  • Maven : has broken classes path unknown

    1 美图 2 背景 windows下 因为要整理Maven 的pom文件 我加入了一个
  • Java提高篇——equals()方法和“==”运算符

    equals 超类Object中有这个equals 方法 该方法主要用于比较两个对象是否相等 该方法的源码如下 public boolean equals Object obj return this obj 我们知道所有的对象都拥有标识
  • wsl子系统Ubuntu18.04,cuDNN安装

    如果觉得本篇文章对您的学习起到帮助作用 请 点赞 关注 评论 留下您的足迹 本文主要wls子系统Ubuntu18 04安装cuDNN 安装cudnn坑巨多 因此记录以备日后查看 同时 如果能够帮助到更多人 也不胜荣幸 文章目录 一 下载安装
  • Xcopy 复制文件和目录,包括子目录

    Xcopy 复制文件和目录 包括子目录 语法 xcopy Source Destination w p c v q f l g d mm dd yyyy u i s e t k r h a m n o x exclude file1 fil
  • Spring FeignClient 遇到的参数问题(RequestParam.value() was empty on parameter 0)

    报错 Caused by java lang IllegalStateException RequestParam value was empty on parameter 0 代码 PostMapping value org getOrg
  • 下载徐小明新浪博客全部博文链接

    利用爬虫把徐小明新浪博客里的所有博文链接爬下来 保存到脚本所在路径的csv文件中 python2 7代码 把起始博文目录链接换成其他的也是完全可以的 详细内容请关注微信公众号 岛城窝窝 代码如下 usr bin env python cod
  • sklearn分类任务模型评价指标汇总与AUC-ROC曲线及代码示例

    文章目录 1 分类指标 2 整合工具函数 3 使用示例 1 分类指标 二分类可以参考下图 准确率 预测对的样本数 全部样本数 精确率 被预测出的正例样本数 预测为正例的样本数 召回率 预测且真的是正例的样本数 正例的样本数 二分类F1 2
  • 《Spring Boot实战》之二:开发第一个应用程序

    本章使用Spring Boot实现一个简单的例子 主要包括两个知识点 使用Spring Boot的起步依赖 使用Spring Boot自动进行Spring的配置 2 1 运用Spring Boot 下面将使用Spring Boot创建一个简
  • [力扣]只出现一次的数字

    给定一个非空整数数组 除了某个元素只出现一次以外 其余每个元素均出现两次 找出那个只出现了一次的元素 说明 你的算法应该具有线性时间复杂度 你可以不使用额外空间来实现吗 作者 力扣 LeetCode 链接 https leetcode cn
  • Linux中级实战讲解--Mysql-galera集群

    Galera Cluster 介绍 Galera是一个MySQL 也支持MariaDB Percona 的同步多主集群软件 主要功能 同步复制 Active active的多主拓扑结构 真正的multi master 即所有节点可以同时读写
  • Android 10.0 禁用插入耳机时弹出的保护听力对话框

    1 前言 在10 0的系统开发中 在某些产品中会对耳机音量调节过高限制 在调高到最大音量的70 的时候 会弹出音量过高弹出警告 所以产品 开发的需要要求去掉这个音量弹窗警告功能 2 禁用插入耳机时弹出的保护听力对话框的核心类 framewo
  • 什么是物模型?阿里小米京东物模型规则对比

    什么是物模型 物模型是产品数字化的描述 定义了产品的功能 物模型将不同品牌不同品类的产品功能抽象归纳 形成 标准物模型 便于各方用统一的语言描述 控制 理解产品功能 物模型由若干条 参数 组成 参数按描述的功能类型不同 又分为属性 方法和事
  • shc gzexe 将shell脚本转换为二进制的可执行文件

    第一种方法 gzexe 这种加密方式不是非常保险的方法 但是能够满足一般的加密用途 可以隐蔽脚本中的密码等信息 它是使用系统自带的gzexe程序 它不但加密 同时压缩文件 使用方法 gzexe file sh它会把原来没有加密的文件备份为
  • Netty源码解析系列三:Netty与Tomcat的区别

    Netty与Tomcat的区别 作用不同 Tomcat 是 Servlet 容器 可以视为 Web 服务器 而 Netty 是异步事件驱动的网络应用程序框架和工具用于简化网络编程 例如TCP和UDP套接字服务器 协议不同 Tomcat 是基
  • Random.Range 范围

    第一 Random Range随机数问题 时间20141212 今天在开发中用到随机数 差点就错过了 没注意这个问题 for int ooi 0 ooi lt 5000 ooi int hhh Random Range 0 3 if hhh
  • latex 参考文献没有显示_LaTeX 中的参考文献

    LaTeX 的对参考文献的处理实在是非常的方便 我用过几次 有些体会 写出来供大家 参考 当然 自己的功力还不够深 有些地方问题一解决就罢手了 没有细究 LaTeX 对参考文献的处理有这么一些优点 1 可以维护一个 bib 文件 在你的整个
  • 系统架构技能之设计模式-组合模式

    一 上篇回顾 我们上篇主要讲述了结构型模式中的外观模式 外观模式作为结构型模式中的一个简单又实用的模式 外观模式通过封装细节来提供大粒度的调用 直接的好处就是 封装细节 提供了应用写程序的可维护性和易用性 外观模式一般应用在系统架构的服务层
  • 【转】Vector与ArrayList区别

    the following is from http blessed24 javaeye com blog 751336 1 Vector ArrayList 1 Vector的方法都是同步的 Synchronized 是线程安全的 thr
  • 以太坊DAO之时间锁定Multisig

    Decentralized Autonomous Organization 简称DAO 以太坊中重要的概念 一般翻译为去中心化的自治组织 有时候 时间也可以用作一种很好的安全机制 以下代码基于DAO区块链大会 但有不同的变化 不是每个操作需