合约内容预览
例子实现了一个投票智能合约即电子投票系统。解决的主要问题是如何分配合理的权限给正确的人,并且要防止被篡改。这个例子实现了如何去委托投票,整个投票计数过程是自动而且完全透明。
功能上它首先为投票创建一个合约,发起者作为所谓的 chairperson 姑且叫主席来给每一个独立的地址分配相应权限。每一个参与投票者可以自己投票或者委托自己信任的人。这段代码最后运行结果会返回得票数最多的那个议案或者叫倡议。
实现
pragma solidity ^0.4.22;
// 一个有委托功能的投票系统
contract Ballot {
// 一个投票人
struct Voter {
uint weight; // 代表投票过程中会累积
bool voted; // 如果值为 true,代表这个投票人已经投过票
address delegate; // 委托投票人地址
uint vote; // 当前投票的索引
}
// 一份议案的数据结构
struct Proposal {
bytes32 name; // 议案的名称
uint voteCount; // 议案得到的投票数
}
address public chairperson; // 定义投票发起人
mapping(address => Voter) public voters; // 所有潜在投票人
Proposal[] public proposals; // 定义动态数组存储议案
// 构造函数,传入议案名称来定义一个投票对象。注意需要传入一个bytes32数组,需要将 string 转为 bytes 传进来。
function Ballot(bytes32[] proposalNames) public {
chairperson = msg.sender; // 发起者才有权力分配选票
voters[chairperson].weight = 1; // 发起者自己有一票
// 按传入的议案名称创建一个议案,并加入到前面定义的议案数组
for (uint i = 0; i < proposalNames.length; i++) {
// 创建一个临时议案对象,加入议案数组
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// 给投票人分配投票权限,这个操作只有主席才可以
function giveRightToVote(address voter) public {
require(msg.sender == chairperson, "Only chairperson can give right to vote."); // 不是主席不允许分配选票
require(!voters[voter].voted, "The voter already voted."); // 投过票的不允许再分配选票了
require(voters[voter].weight == 0); // 已分配选票的不需要再给选票了
voters[voter].weight = 1;
}
// 委托投票给另外一个投票人
function delegate(address to) public {
// 找出委托发起人,如果已经投票,终止程序
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
// 发起人、委托人不能是同一个,否则终止程序
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
require(to != msg.sender, "Found loop in delegation.");
}
// 标识发起人已经投过票
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
proposals[delegate_.vote].voteCount += sender.weight; // 投票成功,投票总数加上相应的weight
} else {
delegate_.weight += sender.weight; // 如果还没投票,发起人weight赋值给委托人
}
}
// 投票给某个议案
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
proposals[proposal].voteCount += sender.weight;
}
// 找出投票数最多的议案
function winningProposal() public view 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;
}
}
}
// 返回投票最多的议案
function winnerName() public view returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
其他
议案需要将 string 转为 bytes32,比如 string alice
转为 bytes32 则为 0x616c696365000000000000000000000000000000000000000000000000000000
。一份转换代码如下:
var convert = function hexToStr(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2) {
var v = parseInt(hex.substr(i, 2), 16);
if (v) str += String.fromCharCode(v);
}
return str;
}