[译]区块链民主 - 如何开发通过投票运行的合约
- 译文出自:登链翻译计划[1]
- 译者:Tiny熊[2]
当你为某事投赞成票时,你如何知道实际上会完成什么事情?你怎么知道承诺会兑现?
在本文中,我将简要介绍区块链如何改变民主。如何通过区块链民主程序,把承诺变成了行动。
我并不是要说我们可以或应该废除政治并建立技术专家制,但是我将展示如何运行一个投票系统,如果投票通过,该系统将自动制定执行。
你可以称之为不可阻挡的民主。
概念设计
首先,请让我以两种智能合约来设置场景:
- 智能合约是一个不可变的程序。智能合约中编码好的规则无法更改。部署后,也无法停止。
- 智能合约还可以触发其他智能合约的操作。例如,智能合约可以触发另一个合约以将资金释放到某个帐户,或授予某人执行某些交易的权限。
根据这些概念,我们可以编写运行公平投票程序的智能合约。每个人都能看到的明确规则, 在该智能合约中,我们可以包含一个提案,该提案是对另一个智能合约中的功能的调用。
无论如何,投票都会进行。如果投票通过,无论如何都将执行该提案。
以太坊和民主
投票是民主的支柱之一,也是以太坊的核心组成部分之一。
通常认为Vitalik Buterin[3]突破了比特币,提议以太坊上创建一个平台,允许在该平台上使用我们上面描述的原则来实施民主组织。
这些基于区块链的民主组织被称为去中心化自治组织[4],简称DAO(Decentralized Autonomous Organizations)。DAO由其利益相关者指导,规则编码在区块链智能合约中,没有中心控制。
当你对某事投票时,你如何知道实际上会完成什么事情?你怎么知道承诺会兑现?
阅读DAO的维基百科文章[5]非常有趣。它揭示了早期DAO的概念是如何构思的,以及它是多么的强大。Daniel Larimer[6](他由BitShares,Steem和EOS.IO项目成名)提出并 最早于2013年在BitShares 实现了该概念。然后你也知道The DAO(Đ)[7],该组织在遭到黑客入侵[8]前吸引到了所有流通中的14%的以太币投资到该组织中。
但是, “The DAO” 的消亡并不意味着“ DAO组织”的消亡。去中心化自治组织还活着并且很好[9],因为死亡的 The DAO的漏洞已广为人知,而且很容易避免。
Vlad Farcas[10]和我开始了一个玩具DAO项目[11],因为我们想学习如何应用民主模式。通过编写DAO,我了解了区块链中民主流程的可能性,这让我大吃一惊。这就是为什么我要写这个。
介绍结束,让我们深入研究代码。我们应该怎么做?
制定智能合约提案
考虑以下合约:
contract Proposal {
address public targetContract;
bytes public targetCall;
/// @param targetContract_ 执行提案的目标合约
/// @param targetCall_ 执行提案的目标函数(abi 编码及参数)
constructor(
address targetContract_,
bytes memory targetCall_,
) public {
targetContract = targetContract_;
proposalData = targetCall_;
}
/// @dev 执行投票的提案
function enact() external virtual {
(bool success, ) = targetContract.call(targetCall);
require(success, “Failed to enact proposal.”);
}
}
该合约具有一些底层的魔法,但解释起来并不难。在部署时,它需要另一个合约的地址和一个函数调用。调用enact()
时,它将在目标合约上执行函数调用。
可以使用web3.js[12]对提案进行编码。在javascript中,下面的示例中部署了一个提案
,该提案在执行时将铸造一个ERC20
代币。
token = await ERC20Mintable.new(‘Token’, ‘TKN’, 18);
proposalData = web3.eth.abi.encodeFunctionCall(
{
type: ‘function’,
name: ‘mint’,
payable: false,
inputs: [
{name: ‘account’, type: ‘address’},
{name: ‘amount’, type: ‘uint256’},
],
},
[owner, ‘1’]
);
proposal = await Proposal.new(token.address, proposalData);
web3.eth.abi.encodeFunctionCall
有点冗长,但实际上唯一做的就是将函数签名和参数包装在32个字节的数据中(参考文档[13])。
它的第一个参数表示函数签名,称为mint
(铸币函数),是非付费的函数,其address
参数称为account
,另一个uint256
参数称为amount
。第二个参数则是为参数赋值,使用在其他地方定义的owner
帐户,而铸币的数量为1。
有更简单的方法可以让一个合约调用在另一个合约上的函数。现在也许很难理解为什么我们会以这种复杂的方式来做这件事情。
继续阅读,现在我们将使该提案民主化。让我们看看制定提案的合约,它需要经过成功的投票之后执行。
一币一票
你可以在HQ20代码库中找到此合约[14]代码,请随意使用它,但不要不加修改的用于现实产品中。为了易于理解,我们忽略了许多对漏洞的处理,例如针对闪贷攻击的漏洞。
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../math/DecimalMath.sol";
contract OneTokenOneVote is Ownable {
using DecimalMath for uint256;
event VotingCreated();
event VotingValidated();
event ProposalEnacted();
event VoteCasted(address voter, uint256 votes);
event VoteCanceled(address voter, uint256 votes);
IERC20 public votingToken;
mapping(address => uint256) public votes;
address public targetContract;
bytes public proposalData;
uint256 public threshold;
bool public passed;
constructor(
address _votingToken,
address _targetContract,
bytes memory _proposalData,
uint256 _threshold
) public Ownable() {
votingToken = IERC20(_votingToken);
threshold = _threshold;
targetContract = _targetContract;
proposalData = _proposalData;
emit VotingCreated();
}
modifier proposalPassed() {
require(passed == true, "Cannot execute until vote passes.");
_;
}
/// @dev Function to enact one proposal of this voting.
function enact() external virtual proposalPassed {
// solium-disable-next-line security/no-low-level-calls
(bool success, ) = targetContract.call(proposalData);
require(success, "Failed to enact proposal.");
emit ProposalEnacted();
}
/// @dev 用这个函数投票,需要先授权
/// (from the frontend) to spend _votes of votingToken tokens.
/// @param _votes The amount of votingToken tokens that will be casted.
function vote(uint256 _votes) external virtual {
votingToken.transferFrom(msg.sender, address(this), _votes);
votes[msg.sender] = votes[msg.sender].addd(_votes);
emit VoteCasted(msg.sender, _votes);
}
/// @dev Use this function to retrieve your votingToken votes in case you changed your mind or the voting has passed
function cancel() external virtual {
uint256 count = votes[msg.sender];
delete votes[msg.sender];
votingToken.transfer(msg.sender, count);
emit VoteCanceled(msg.sender, count);
}
/// @dev Number of votes casted in favour of the proposal.
function inFavour() public virtual view returns (uint256) {
return votingToken.balanceOf(address(this));
}
/// @dev Number of votes needed to pass the proposal.
function thresholdVotes() public virtual view returns (uint256) {
return votingToken.totalSupply().muld(threshold, 4);
}
/// @dev Function to validate the threshold
function validate() public virtual {
require(
inFavour() >= thresholdVotes(),
"Not enough votes to pass."
);
passed = true;
emit VotingValidated();
}
}
在 OneTokenOneVote.sol 文件里:
- 投票的代币,是来自部署时选择的ERC20合约。
- 投票意味着使用
vote()
将代币转移到OneTokenOneVote
合约中。 - 如果在任何时候
OneTokenOneVote
持有的比例高于threshold
(相比于流通量),则通过提案。 - 提案通过后,它将永远保持在
passed
状态。 - 选民可以随时取消投票(调用
cancel
)并取回其代币,但如果他们希望提案通过,则应在提案通过后再进行取回。 - 任何人都可以通过调用
validate()
来触发计票。如果达到threshold
,这将使投票通过。
实现投票有几种方法[15]。有更安全的投票方式,包括要求达到法定人数。OneTokenOneVote.sol
是我们能想到的最简单的示例,但这足以显示区块链民主的原理。
在部署投票合约时,它接受带有参数编码的 targetContract
和targetFunction
作为提案。如果投票通过,任何人都可以调用 enact()
函数来执行提案。
这意味着投票合约包括如果表决通过将要采取的行动。不可能忽略投票结果。在区块链之前这是不可能的,请思考一下。
合约民主
我们可以为这种区块链民主概念提供另一种转折。到目前为止,我们知道如何部署需要执行表决过程然后在执行投票结果结果的合约。
我们可以编写一份合约,其中所有的功能如果经过表决就才能被执行。这就是DAO的精神,它比听起来容易。
在代码库中,我们包含了第三份合约Democratic.sol[16],我发现使用起来真的很令人兴奋。它允许任何合约对是否执行其任何功能进行表决。
-
Democratic.sol
被设计为可被其他合约继承,仅当它们经过投票后才允许将其中函数可执行。你可以通过使用onlyProposal
来修饰要执行的函数来做到这一点。 -
Democratic.sol
允许任何人提出提案进行投票。propose()
函数可以被任何人使用,目标函数使用web3.eth.abi.encodeFunctionCall
编码。 - 所有提案的投票代币都将相同,从而在MakerDAO[17]中创建一个带有MKR代币的社区。
Democratic.sol
的实现是基于代币的投票,但可以轻松更改为基于帐户的投票。 - 全部提案存储在
proposals
变量中,并且只有由同一合约创建的提案才能执行标记为onlyProposal
的函数。
如果考虑到这一点,则可以使用Democratic.sol
和OneTokenOneVote.sol
作为[完整民主系统的基础](https://github.com/HQ20/contracts/blob/master/contracts/access /Democracy.sol "完整民主系统的基础")。如果你没有发现这是多么的令人兴奋,我不知道还能告诉你什么。
结论
区块链有可能以我们一生中从未有过的程度改变民主进程。
使用区块链可以实现不可阻挡的投票,一旦投票通过,任何人都无法避免被执行。随着越来越多的世界可以从区块链访问,民主的力量将会增长。
在本文中,我们展示了如何实现智能合约执行的投票程序,并对其进行了改进,以生成只能由民主进程执行的智能合约函数。
自从以太坊诞生以来,这些概念在区块链生态系统中都不是新事物。但是,在这些合约中,我们提供的基础模块往前民主又迈出了一步。
本翻译由 Cell Network[18] 赞助支持。
原文链接:https://medium.com/swlh/blockchain-democracy-932b969d1cc5
作者:Alberto Cuesta Cañada[19]
参考资料
[1]
登链翻译计划: https://github.com/lbc-team/Pioneer
[2]
Tiny熊: https://learnblockchain.cn/people/15
[3]
Vitalik Buterin: https://about.me/vitalik_buterin
[4]
去中心化自治组织: https://en.wikipedia.org/wiki/Decentralized_autonomous_organization
[5]
DAO的维基百科文章: https://en.wikipedia.org/wiki/Decentralized_autonomous_organization
[6]
Daniel Larimer: https://twitter.com/bytemaster7
[7]
The DAO(Đ): https://en.wikipedia.org/wiki/The_DAO_%28organization%29
[8]
遭到黑客入侵: https://medium.com/swlh/the-story-of-the-dao-its-history-and-consequences-71e6a8a551ee
[9]
还活着并且很好: https://defirate.com/daos/
[10]
Vlad Farcas: https://twitter.com/uivlis
[11]
玩具DAO项目: https://github.com/HQ20/contracts/tree/master/contracts/examples/dao
[12]
web3.js: https://learnblockchain.cn/docs/web3.js/web3-eth-abi.html#encodefunctioncall
[13]
参考文档: https://learnblockchain.cn/docs/web3.js/web3-eth-abi.html#encodefunctioncall
[14]
HQ20代码库中找到此合约: https://github.com/HQ20/contracts/blob/master/contracts/voting/OneTokenOneVote.sol
[15]
几种方法: https://github.com/HQ20/contracts/blob/master/contracts/voting/OneManOneVote.sol
[16]
Democratic.sol: https://github.com/HQ20/contracts/blob/master/contracts/voting/Democratic.sol
[17]
MakerDAO: http://makerdao.com/
[18]
Cell Network: https://www.cellnetwork.io/?utm_souce=learnblockchain
[19]
Alberto Cuesta Cañada: https://medium.com/@albertocuestacanada?source=post_page-----932b969d1cc5--------------------------------
- UiTextField对输入的长度进行限制并提示用户还可输入的长度
- Spring Cloud实战小贴士:turbine如何聚合设置了context-path的hystrix数据
- iOS 查找字符串 相同 子字符串的位置 range
- 自然语言处理指南(第四部分)
- 史上最强Spring mvc入门
- 上边半透明的效果并且显示的是上一页的内容
- Spring Cloud构建微服务架构:Hystrix监控数据聚合【Dalston版】
- android自定义view实现公章效果
- ios app url scheme跳转到淘宝商品详情页 唤醒app
- ThreadPoolExecutor运行机制
- Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)【Dalston版】
- UIPickView的简单使用
- java开发中几种常见的线程池
- 传统多线程之前如何共享数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 「 Map最佳实践」什么时候适合使用 Map 而不是 Object
- ES2017 异步函数的最佳实践(`async` /`await`)
- React Hooks中这样写HTTP请求可以避免内存泄漏
- 用了这 7 个 VS Code 插件,想写一辈子代码
- 听说你还不知道Promise的allSettled()和all()的区别?
- React 条件渲染最佳实践(7 种方法)
- 一文带你层层解锁「文件下载」的奥秘
- Android实现简单C/S聊天室应用
- Android仿IOS回弹效果 支持任何控件
- 送你一道字节前端原题(Add sumOf)|文末送红宝书
- xadmin使用formfield_for_dbfield函数过滤下拉表单实例
- Python3之外部文件调用Django程序操作model等文件实现方式
- python 在右键菜单中加入复制目标文件的有效存放路径(单斜杠或者双反斜杠)
- 前端多线程大文件下载实践,提速10倍(拿捏百度云盘)
- docker 中的mysql 经常重启优化