声明:本系列文章是自己在http://solidity-cn.readthedoc... 学习solidity时,因为英语水平不够,被迫用谷歌粗略翻译的。仅为了方便学习,中间肯定有很多错误的地方。请勘误。
本文原地址:http://solidity-cn.readthedoc...
以下是翻译正文:
投票:
以下合同非常复杂,但展示了很多Solidity的功能。 它执行投票合同。 当然,电子投票的主要问题是如何为正确的人员分配投票权,以及如何防止操纵。 我们不会在这里解决所有问题,但至少我们会展示如何进行委派投票,以便计票自动且完全透明。
这个想法是为每个选票创建一个合同,为每个选项提供一个简称。 然后,担任主席的合同创建者将分别给予每个地址的投票权。
然后,地址背后的人可以选择自己投票,或者将他们的投票委托给他们信任的人。
在投票时间结束时,winningProposal()
将返回投票数最多的提案。
pragma solidity ^0.4.21
/// @title Voting with delegation.
contract Ballot {
//这将声明一个新的复合类型
//稍后用于变量。
//它会代表一个voter。
struct Voter {
uint weight; //weight is accumulated by delegation
bool voted; // 如果true,已经voted
address delegate; // 被授权的人
uint vote; // vote提案的索引
}
// a type for a single proposal.
struct Proposal {
bytes32 name;
uint voteCount;
}
address public chairperson;
//声明一个状态变量,为每个可能的地址存储一个`Voter`结构体。
mapping(address => Voter) public voters;
// Proposal结构的动态大小的数组。
Proposal[] public proposals;
///创建一个新ballot来选择`proposalNames`中的一个。
function Ballot(bytes32[] proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
//对于提供的每个提议名称,创建一个新的提议对象并将其添加到数组的末尾。
for (uint i = 0, i < proposalNames.length; i++) {
//`Proposal({...})`创建一个临时的Proposal对象,
// `proposals.push(...)`将它附加到`proposals`的末尾
proposals.push(Proposal({
name:proposalNames[i];
voteCount:0
}));
}
}
//让`voter`有权对这张选票进行投票。
//只能由`chairperson`调用。
function giveRightToVote(address voter) public {
//如果`require`的参数评估为'false',
//它会终止并恢复对状态和以太平衡的所有更改。 如果函数被错误地调用,
//通常使用它是一个好方法。 但要小心,这也将消耗所有提供的gas(这是计划在未来改变)。
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0))
voters[voter].weight = 1;
}
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
require(to != msg.sender);
// 一般来说,这样的循环是非常危险的,因为如果它们运行时间太长,
// 它们可能需要比块中可用的更多的气体。在这种情况下,委托将不会被执行,
// 但在其他情况下,这样的循环可能导致合同 完全“卡住”。
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
require(to != msg.sender);
}
//因为`sender`是一个引用,所以这会修改`voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter storage delegate = voters[to];
if(delegate.voted) {
//如果委托人已投票,则直接添加投票数
proposals[delegate.vote].voteCount += sender.weight;
} else {
//如果代表尚未投票,增加
delegate.weight += sender.weight;
}
}
///将您的投票(包括授予您的投票)提交给提案[proposal] .name`。
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
sender.voted = true;
sender.vote = proposal;
//如果`proposal'超出了数组范围,这将自动抛出并恢复所有更改。
proposals[proposal].voteCount += sender.weight;
}
/// @dev计算以前所有投票的获胜建议。
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;
}
}
}
//调用winningProposal()函数获取提议数组中包含的获奖者的索引,然后返回获胜者的名字
function winnerName() public view
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}
可能的改进
目前,需要许多交易来将投票权分配给所有参与者。 你能想出更好的方法吗?
秘密竞价(盲拍)
在本节中,我们将展示在以太坊创建一个完全失明的拍卖合同是多么容易。 我们将从公开拍卖开始,每个人都可以看到所做的投标,然后将此合同扩展到盲目拍卖,在竞标期结束之前无法看到实际出价。
简单的公开拍卖
以下简单的拍卖合同的总体思路是每个人都可以在投标期内发送他们的出价。 出价已经包括发送金钱/以太币以使投标人与他们的出价相结合。 如果提高最高出价,以前出价最高的出价人可以拿回她的钱。 在投标期结束后,合同必须手动为受益人接收他的钱, 合同不能激活自己。
pragma solidity ^0.4.21
contract SimpleAuction {
//拍卖的参数。 时间是绝对的unix时间戳
//(自1970-01-01以来的秒数)或以秒为单位的时间段。
address public beneficiary;
uint public auctionEnd;
// 拍卖当前的状态
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
// 设置为true,禁止任何更改
bool ended;
// 将在更改中触发的事件。
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
//以下是所谓的natspec评论,可以通过三个斜杠来识别。
//当用户被要求确认交易时将显示。
///代表受益人地址`_beneficiary`以`_biddingTime`秒的投标时间创建一个简单的拍卖。
function SimpleAuction(
uint _biddingTime,
address _beneficiary
) public {
beneficiary = _beneficiary;
auctionEnd = now + _biddingTime;
}
///使用与此交易一起发送的价格拍卖拍卖品。 如果拍卖没成功,价值只会被退还。
function bid() public payable {
//不需要参数,所有信息已经是交易的一部分。 为了能够接收以太网,功能需要关键字。
//如果结束,请恢复通话。
require(now <= auctionEnd);
// 如果出价不高,将钱退回。
require(msg.value > highestBid);
if (highestBid != 0) {
//通过使用highestBidder.send(highestBid)发送回款有安全风险,
// 因为它可以执行不可信的合同。
// 让收款人自己收回钱会比较安全。
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
HighestBidIncreased(msg.sender, msg.value);
}
///撤销高出价的出价。
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为收件人可以在`send`返回
// 之前再次调用此函数作为接收调用的一部分。
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
//不需要在这里呼叫,只需重新设置欠款额
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd() public {
//将与其他合约交互的函数(即它们调用函数或发送Ether)结构化为三个阶段是一个很好的指导:
// 1.检查条件
// 2.执行操作(潜在的变化条件)
// 3.与其他合同交互
//如果这些阶段混淆在一起,另一个合约可以回拨到当前合约中,并修改多次执行的状态或原因效果(以太付款).
//如果内部调用的函数包含与外部合同的交互,则必须将它们视为与外部合同的交互。
// 1.条件
require(now >= auctionEnd); //拍卖还没有结束
require(!ended); //此功能已被调用
// 2.效果
ended = true;
AuctionEnded(highestBidder, highestBid);
// 3.交互
beneficiary.transfer(highestBid);
}
}
盲拍
以前的公开拍卖会延伸到以下的盲拍。盲拍的优势在于投标期结束时没有时间压力。在一个透明的计算平台上创建一个盲目拍卖可能听起来像是一个矛盾,但是密码学可以解决这个问题。
在投标期间,投标人实际上并没有发出她的投标,而只是一个散列版本。由于目前认为实际上不可能找到两个(足够长)的哈希值相等的值,因此投标人承诺通过该投标。投标结束后,投标人必须公开他们的投标:他们将他们的价值未加密并且合同检查散列值与投标期间提供的散列值相同。
另一个挑战是如何在同一时间使拍卖具有约束力和盲目性:在赢得拍卖后,防止投标人不发送货币的唯一方法是让她在拍卖中一并发送。由于价值转移不能在以太坊蒙蔽,任何人都可以看到价值。
以下合同通过接受任何大于最高出价的值来解决此问题。因为这当然只能在披露阶段进行检查,所以有些出价可能是无效的,这是有意的(它甚至提供了一个明确的标记,用高价值转让放置无效出价):投标人可以通过放置几个较高的低无效出价。
pragma solidity ^0.4.21
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
address public beneficiary;
uint public biddingEnd;
uint public revealEnd;
bool public ended;
mapping(address => Bid[]) public bids;
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
event AuctionEnded(address winnder, uint highestBid);
///修饰符是验证函数输入的便捷方式。 `onlyBefore`应用于下面的`bid`:
// 新函数体是修饰符的主体,其中`_`被旧函数体替换。
modifier onlyBefore(uint _time) { require(now < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; }
function BlindAuction {
uint _biddingTime,
uint _revealTime,
address _beneficiary
} public {
beneficiary = _beneficiary;
biddingEnd = now + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
///用`_blindedBid` = keccak256(value,fake,secret)放置一个不知情的出价。
///如果投标在披露阶段正确显示,则只会退还已发送的以太币。
//如果与投标一起发送的以太币至少“value”和“fake”不是true,则投标有效。
//将“fake”设置为true,并发送不确切的金额是隐藏实际出价但仍然需要存款的方法。 同一个地址可以放置多个出价。
function bid(bytes32 _blindedBid) public payable onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
///揭示你不知情的投标。 对于所有正确无视的无效出价以及除最高出价以外的所有出价,您都将获得退款。
function reveal (
uint[] _values,
bool[] _fake,
bytes32[] _secret
) public onlyAfter(biddingEnd) onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(_values.length == length);
require(_fake.length == length);
require(_secret.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
var bid = bids[msg.sender][i];
var (value, fake, secret) = (_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != keccak256(value,fake,secret)) {
//投标并未实际显示。
//不要押金。
continue;
}
refund += bid.deposit;
if (!fake && bid.deposit > value) {
if (placeBid(msg.sender, value)) refund -= value;
}
//使发件人无法重新申请相同的存款。
bid.blindedBid = bytes32(0);
}
msg.sender.transfer(refund);
}
//这是一个“内部”功能,这意味着它只能从合同本身(或衍生合同)中调用。
function placeBid(address bidder, uint value) internal return (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != 0) {
//退还先前出价最高的出价者。
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
///撤销高出价的出价。
function withdraw() public {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为接收者可以在`transfer`返回之前再次调用
//此函数作为接收调用的一部分(请参阅上面关于条件 - >效果 - >交互的注释)。
pendingReturns[msg.sender] = 0;
msg.sender.transfer(amount);
}
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd () public onlyAfter(revealEnd)
{
require(!ended);
AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
}
安全的远程购买
pragma solidity ^0.4.21
contract Purchase {
uint public value;
address public seller;
address public buyer;
enum State { Created, Locked, Inactive}
State public state;
//确保`msg.value`是一个偶数。
//如果它是一个奇数,则它将被截断。
//通过乘法检查它不是奇数。
function Purchase() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value);
}
modifier condition(bool _condition) {
require(_condition);
_;
}
modifier onlyBuyer() {
require(msg.sender == buyer);
_;
}
modifier onlySeller() {
require(msg.sender == seller);
_;
}
modifier inState(State _state) {
require(state == _state);
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
///中止购买并回收以太。
///只能在合同被锁定之前由卖家调用。
function abort() public onlySeller inState(State.Created)
{
Aborted();
state = State.Inactive;
seller.transfer(this.balance);
}
///将购买确认为买家。
/// Transaction必须包含`2 * value`以太。
///以太会被锁定,直到confirmReceived被调用。
function confirmPurchase() public inState(State.Created) condition(msg.value == (2 * value)) payable
{
PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
///确认您(买家)收到该物品。
///这将释放锁定的以太。
function confirmReceived() public onlyBuyer inState(State.Locked)
{
ItemReceived();
//先改变状态很重要,否则使用下面的`send`调用的合约可以在这里再次调用。
state = State.Inactive;
//NOTE:这实际上允许买家和卖家阻止退款 - 应该使用退款模式。
buyer.transfer(value);
seller.transfer(this.balance);
}
}
微支付通道
To be written.