Solidity 投票案例
2019-01-08 本文已影响7人
黄靠谱
参考
https://solidity.readthedocs.io/en/latest/solidity-by-example.html
https://ethfans.org/wikis/Solidity-%E6%96%87%E6%A1%A3--%E7%AC%AC%E4%B8%89%E7%AB%A0
Voting 投票
演示的是一个投票结果对外公布,但是投票人信息、投票人投给哪个议案的信息,对外是匿名。
因为votes是public的,知道address的人都可以查询到Voter信息,所以chairperson可以查询,投票人可以查询自己的投票结果。
电子选举的主要问题:如何赋予投票权给准确的人(授权到投票人提供的地址),并防止操纵(投票发起人篡改投票结果)。
我们会展示如何委托投票可以同时做到投票统计是自动和完全透明。
思路:
- 为每张选票创建一个合约,每个投票选项提供一个短名称
- 合约创建者作为会长将会给每个投票参与人各自的地址投票权
- 地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票
- 在投票结束后,winningProposal()将会返回获得票数最多的提案
合约的好处:
- 投票的结果是透明的:任何人都有权限查询,最新投票结果
- 投票人的信息是匿名的:外部无法知道投票人是谁。但是chairperson知道,因为他必须给投票人的地址授权
- 投票的过程是匿名的:外部不知道是谁投给了哪个议案,可以很好的保护投票人的匿名投票权。但是chairperson知道,可以通过address去votes里面获取votes的投票信息
可以改进的地方:
- 把voters设置为非public的话,投票可以实现完全的匿名,即使是chairperson也没有对应的入口去查询 谁投票给了哪个议案,但是要防止chairperson顶替投票
- 同时要把vote函数返回一个结果值,这样避免 chairperson 替 voter投票,因为chairperson知道voters的地址。如果voter投票发现无效,那么就是chairperson作弊了
代码逻辑:
- 设计好必要的状态变量:
-
Voter
-
voters(所有参与的投票人信息,包括合约创建者和参与投票者),只有合约的创造者才有权限执行这个操作
-
Proposal
-
proposals:不定长数组,用来存储所有的合约创建者提供的议案,同时统计该议案的累计得票数
-
chairperson(Address),作为状态变量,用来限定身份,在合约的构造函数中指定chairperson,只有chairperson才有权限授权 投票人
contract Ballot
{
// 这里声明了复杂类型
// 将会在被后面的参数使用
// 代表一个独立的投票人。
struct Voter
{
uint weight; // 累积的权重。
bool voted; // 如果为真,则表示该投票人已经投票。
address delegate; // 委托的投票代表
uint vote; // 投票选择的提案索引号
}
// 这是一个独立提案的类型
struct Proposal
{
bytes32 name; // 短名称(32字节)
uint voteCount; // 累计获得的票数
}
address public chairperson;
//这里声明一个状态变量,保存每个独立地址的`Voter` 结构
mapping(address => Voter) public voters;
//一个存储`Proposal`结构的动态数组
Proposal[] public proposals;
//合约的构造函数
//创建一个新的投票用于选出一个提案名`proposalNames`.
function Ballot(bytes32[] proposalNames)
{
chairperson = msg.sender;
voters[chairperson].weight = 1;
//对提供的每一个提案名称,创建一个新的提案
//对象添加到数组末尾
for (uint i = 0; i < proposalNames.length; i++)
//`Proposal({...})` 创建了一个临时的提案对象,
//`proposal.push(...)`添加到了提案数组`proposals`末尾。
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
//给投票人`voter`参加投票的投票权,
//只能由投票主持人`chairperson`调用。
function giveRightToVote(address voter)
{
if (msg.sender != chairperson || voters[voter].voted)
//`throw`会终止和撤销所有的状态和以太改变。
//如果函数调用无效,这通常是一个好的选择。
//但是需要注意,这会消耗提供的所有gas。
throw;
voters[voter].weight = 1;
}
// 委托你的投票权到一个投票代表 `to`
//先判断受托人账户是否存在,再check 受托人的最终的受托人不能是指回自己
//再把发起人的投票使用掉,给受托人的权重+1,如果受托人已经投票,则修改投票结果的权重+1
function delegate(address to)
{
// 指定引用
Voter sender = voters[msg.sender];
if (sender.voted)
throw;
//当投票代表`to`也委托给别人时,寻找到最终的投票代表
//如果代理人存在 而且代理人的代理不等于请求发起人,则进行代理授权
while (voters[to].delegate != address(0) &&voters[to].delegate != msg.sender)
to = voters[to].delegate;
// 当最终投票代表等于调用者,是不被允许的。
if (to == msg.sender)
throw;
//因为`sender`是一个引用,
//这里实际修改了`voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter delegate = voters[to];
if (delegate.voted)
//如果委托的投票代表已经投票了,直接修改票数
proposals[delegate.vote].voteCount += sender.weight;
else
//如果投票代表还没有投票,则修改其投票权重。
delegate.weight += sender.weight;
}
//投出你的选票(包括委托给你的选票)给 `proposals[proposal].name`。
//防止你重复投票
//给对应的议案的voteCount 增加weight
function vote(uint proposal)
{
Voter sender = voters[msg.sender];
if (sender.voted) throw;
sender.voted = true;
sender.vote = proposal;
//如果`proposal`索引超出了给定的提案数组范围
//将会自动抛出异常,并撤销所有的改变。
proposals[proposal].voteCount += sender.weight;
}
//根据当前所有的投票计算出当前的胜出提案
function winningProposal() constant returns (uint winningProposal)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++)
{
if (proposals[p].voteCount > winningVoteCount)
{
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
}