Solidity 编程实例

Voting 投票

接下来的合约非常复杂,但展示了很多Solidity的特性。它实现了一个投票合约。当然,电子选举的主要问题是如何赋予投票权给准确的人,并防止操纵。我们不能解决所有的问题,但至少我们会展示如何委托投票可以同时做到投票统计是自动和完全透明。

思路是为每张选票创建一个合约,每个投票选项提供一个短名称。合约创建者作为会长将会给每个投票参与人各自的地址投票权。

地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票。在投票结束后,winningProposal()将会返回获得票数最多的提案。

/// @title Voting with delegation.
 /// @title 授权投票
    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`。
    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`。
    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;
    }

   ///@dev 根据当前所有的投票计算出当前的胜出提案
    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;
            }
        }
    }
}

可能的改进

现在,指派投票权到所有的投票参加者需要许多的交易。你能想出更好的方法么?

盲拍

这一节,我们将展示在以太上创建一个完整的盲拍合约是多么简单。我们从一个所有人都能看到出价的公开拍卖开始,接着扩展合约成为一个在拍卖结束以前不能看到实际出价的盲拍。

简单的公开拍卖

通常简单的公开拍卖合约,是每个人可以在拍卖期间发送他们的竞拍出价。为了实现绑定竞拍人的到他们的拍卖,竞拍包括发送金额/ether。如果产生了新的最高竞拍价,前一个最高价竞拍人将会拿回他的钱。在竞拍阶段结束后,受益人人需要手动调用合约收取他的钱 — — 合约不会激活自己。

contract SimpleAuction {
    // 拍卖的参数。
   // 时间要么为unix绝对时间戳(自1970-01-01以来的秒数),
   // 或者是以秒为单位的出块时间
    address public beneficiary;
    uint public auctionStart;
    uint public biddingTime;

    //当前的拍卖状态
    address public highestBidder;
    uint public highestBid;

   //在结束时设置为true来拒绝任何改变
    bool ended;

   //当改变时将会触发的Event
    event HighestBidIncreased(address bidder, uint amount);
    event AuctionEnded(address winner, uint amount);

    //下面是一个叫做natspec的特殊注释,
    //由3个连续的斜杠标记,当询问用户确认交易事务时将显示。

    ///创建一个简单的合约使用`_biddingTime`表示的竞拍时间,
   /// 地址`_beneficiary`.代表实际的拍卖者
    function SimpleAuction(uint _biddingTime,
                           address _beneficiary) {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingTime = _biddingTime;
    }

    ///对拍卖的竞拍保证金会随着交易事务一起发送,
   ///只有在竞拍失败的时候才会退回
    function bid() {

       //不需要任何参数,所有的信息已经是交易事务的一部分
        if (now > auctionStart + biddingTime)
           //当竞拍结束时撤销此调用
            throw;
        if (msg.value <= highestBid)
           //如果出价不是最高的,发回竞拍保证金。
            throw;
        if (highestBidder != 0)
            highestBidder.send(highestBid);
        highestBidder = msg.sender;
        highestBid = msg.value;
        HighestBidIncreased(msg.sender, msg.value);
    }

   ///拍卖结束后发送最高的竞价到拍卖人
    function auctionEnd() {
        if (now <= auctionStart + biddingTime)
            throw;
            //拍卖还没有结束
        if (ended)
            throw;
     //这个收款函数已经被调用了
        AuctionEnded(highestBidder, highestBid);
        //发送合约拥有所有的钱,因为有一些保证金可能退回失败了。

        beneficiary.send(this.balance);
        ended = true;
    }

    function () {
        //这个函数将会在发送到合约的交易事务包含无效数据
        //或无数据的时执行,这里撤销所有的发送,
        //所以没有人会在使用合约时因为意外而丢钱。
        throw;
    }
}

Blind Auction 盲拍

接下来扩展前面的公开拍卖成为一个盲拍。盲拍的特点是拍卖结束以前没有时间压力。在一个透明的计算平台上创建盲拍系统听起来可能有些矛盾,但是加密算法能让你脱离困境。

在拍卖阶段, 竞拍人不需要发送实际的出价,仅仅只需要发送一个它的散列值。因为目前几乎不可能找到两个值(足够长)的散列值相等,竞拍者提交他们的出价散列值。在拍卖结束后,竞拍人重新发送未加密的竞拍出价,合约将检查其散列值是否和拍卖阶段发送的一样。 另一个挑战是如何让拍卖同时实现绑定和致盲 :防止竞拍人竞拍成功后不付钱的唯一的办法是,在竞拍出价的同时发送保证金。但是在Ethereum上发送保证金是无法致盲,所有人都能看到保证金。下面的合约通过接受任何尽量大的出价来解决这个问题。当然这可以在最后的揭拍阶段进行复核,一些竞拍出价可能是无效的,这样做的目的是(它提供一个显式的标志指出是无效的竞拍,同时包含高额保证金):竞拍人可以通过放置几个无效的高价和低价竞拍来混淆竞争对手。

contract BlindAuction
{
    struct Bid
    {
        bytes32 blindedBid;
        uint deposit;
    }
    address public beneficiary;
    uint public auctionStart;
    uint public biddingEnd;
    uint public revealEnd;
    bool public ended;

    mapping(address => Bid[]) public bids;

    address public highestBidder;
    uint public highestBid;

    event AuctionEnded(address winner, uint highestBid);

   ///修饰器(Modifier)是一个简便的途径用来验证函数输入的有效性。
   ///`onlyBefore` 应用于下面的 `bid`函数,其旧的函数体替换修饰器主体中 `_`后就是其新的函数体
    modifier onlyBefore(uint _time) { if (now >= _time) throw; _ }
    modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }

    function BlindAuction(uint _biddingTime,
                            uint _revealTime,
                            address _beneficiary)
    {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingEnd = now + _biddingTime;
        revealEnd = biddingEnd + _revealTime;
    }

   ///放置一个盲拍出价使用`_blindedBid`=sha3(value,fake,secret).
   ///仅仅在竞拍结束正常揭拍后退还发送的以太。当随同发送的以太至少
   ///等于 "value"指定的保证金并且 "fake"不为true的时候才是有效的竞拍
   ///出价。设置 "fake"为true或发送不合适的金额将会掩没真正的竞拍出
   ///价,但是仍然需要抵押保证金。同一个地址可以放置多个竞拍。
    function bid(bytes32 _blindedBid)
        onlyBefore(biddingEnd)
    {
        bids[msg.sender].push(Bid({
            blindedBid: _blindedBid,
            deposit: msg.value
        }));
    }

   ///揭开你的盲拍竞价。你将会拿回除了最高出价外的所有竞拍保证金
   ///以及正常的无效盲拍保证金。
    function reveal(uint[] _values, bool[] _fake,
                    bytes32[] _secret)
        onlyAfter(biddingEnd)
        onlyBefore(revealEnd)
    {
        uint length = bids[msg.sender].length;
        if (_values.length != length || _fake.length != length ||
                    _secret.length != length)
            throw;
        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 != sha3(value, fake, secret))
                //出价未被正常揭拍,不能取回保证金。
                continue;
            refund += bid.deposit;
            if (!fake && bid.deposit >= value)
                if (placeBid(msg.sender, value))
                    refund -= value;
            //保证发送者绝不可能重复取回保证金
            bid.blindedBid = 0;
        }
        msg.sender.send(refund);
    }

    //这是一个内部 (internal)函数,
   //意味着仅仅只有合约(或者从其继承的合约)可以调用
    function placeBid(address bidder, uint value) internal
            returns (bool success)
    {
        if (value <= highestBid)
            return false;
        if (highestBidder != 0)
            //退还前一个最高竞拍出价
            highestBidder.send(highestBid);
        highestBid = value;
        highestBidder = bidder;
        return true;
    }

   ///竞拍结束后发送最高出价到竞拍人
    function auctionEnd()
        onlyAfter(revealEnd)
    {
        if (ended) throw;
        AuctionEnded(highestBidder, highestBid);
        //发送合约拥有所有的钱,因为有一些保证金退回可能失败了。
        beneficiary.send(this.balance);
        ended = true;
    }

    function () { throw; }
}
Safe Remote Purchase 安全的远程购物

contract Purchase
{
    uint public value;
    address public seller;
    address public buyer;
    enum State { Created, Locked, Inactive }
    State public state;
    function Purchase()
    {
        seller = msg.sender;
        value = msg.value / 2;
        if (2 * value != msg.value) throw;
    }
    modifier require(bool _condition)
    {
        if (!_condition) throw;
        _
    }
    modifier onlyBuyer()
    {
        if (msg.sender != buyer) throw;
        _
    }
    modifier onlySeller()
    {
        if (msg.sender != seller) throw;
        _
    }
    modifier inState(State _state)
    {
        if (state != _state) throw;
        _
    }
    event aborted();
    event purchaseConfirmed();
    event itemReceived();

   ///终止购物并收回以太。仅仅可以在合约未锁定时被卖家调用。
    function abort()
        onlySeller
        inState(State.Created)
    {
        aborted();
        seller.send(this.balance);
        state = State.Inactive;
    }

   ///买家确认购买。交易包含两倍价值的(`2 * value`)以太。
   ///这些以太会一直锁定到收货确认(confirmReceived)被调用。
    function confirmPurchase()
        inState(State.Created)
        require(msg.value == 2 * value)
    {
        purchaseConfirmed();
        buyer = msg.sender;
        state = State.Locked;
    }

    ///确认你(买家)收到了货物,这将释放锁定的以太。
    function confirmReceived()
        onlyBuyer
        inState(State.Locked)
    {
        itemReceived();
        buyer.send(value);//我们有意忽略了返回值。
        seller.send(this.balance);
        state = State.Inactive;
    }
    function() { throw; }
}

小额支付通道

待补

Solidity 文档--第三章:Solidity 编程实例的更多相关文章

  1. Prism 文档 第三章 管理组件之间的依赖关系

                                                                          第3章:管理组件之间的依赖关系 基于Prism库的复合应用程 ...

  2. Solidity 文档--目录

    Solidity是一种语法类似JavaScript的高级语言.它被设计成以编译的方式生成以太坊虚拟机代码.在后续内容中你将会发现,使用它很容易创建用于投票.众筹.封闭拍卖.多重签名钱包等等的合约. 注 ...

  3. Ext JS 6学习文档-第8章-主题和响应式设计

    Ext JS 6学习文档-第8章-主题和响应式设计 主题和响应式设计 本章重点在 ExtJS 应用的主题和响应式设计.主要有以下几点内容: SASS 介绍和入门 主题 响应式设计 SASS 介绍和入门 ...

  4. Ext JS 6学习文档-第7章-图表

    Ext JS 6学习文档-第7章-图表 使用图表 本章中将探索在 ExtJS 中使用不同类型的图表并使用一个名为费用分析的示例项目结束本章所学.以下是将要所学的内容: 图表类型 条形图 和 柱形图 图 ...

  5. Ext JS 6学习文档-第6章-高级组件

    Ext JS 6学习文档-第6章-高级组件 高级组件 本章涵盖了高级组件,比如 tree 和 data view.它将为读者呈现一个示例项目为 图片浏览器,它使用 tree 和 data view 组 ...

  6. Ext JS 6学习文档-第5章-表格组件(grid)

    Ext JS 6学习文档-第5章-表格组件(grid) 使用 Grid 本章将探索 Ext JS 的高级组件 grid .还将使用它帮助读者建立一个功能齐全的公司目录.本章介绍下列几点主题: 基本的 ...

  7. Ext JS 6学习文档-第4章-数据包

    Ext JS 6学习文档-第4章-数据包 数据包 本章探索 Ext JS 中处理数据可用的工具以及服务器和客户端之间的通信.在本章结束时将写一个调用 RESTful 服务的例子.下面是本章的内容: 模 ...

  8. Ext JS 6学习文档-第3章-基础组件

    Ext JS 6学习文档-第3章-基础组件 基础组件 在本章中,你将学习到一些 Ext JS 基础组件的使用.同时我们会结合所学创建一个小项目.这一章我们将学习以下知识点: 熟悉基本的组件 – 按钮, ...

  9. Objective-C 基础教程第三章,面向对象编程基础知

    目录 Objective-C 基础教程第三章,面向对象编程基础知 0x00 前言 0x01 间接(indirection) 0x02 面向对象编程中使用间接 面向过程编程 面向对象编程 0x03 OC ...

随机推荐

  1. 第一课:SVN代码管理

    SVN:是一个跨平台的开源的版本控制系统.svn版本管理工具管理着随时间改变的各种数据.这些数据放置在一个中央资料档案库中.svn会备份并记录每个文件每次的修改更新变动.svn的工作流程:1.在中央库 ...

  2. No node available for block: blk

    刚才利用hadoop和mahout运行kmean是算法,一开始利用了10个节点,一个master,9个slave,运行了7分钟,我为了看速度的变化,就改用伪分布的形式,但是一开始运行就报错了: 17/ ...

  3. 小C的记事本(栈记录字符串)

    链接:https://www.nowcoder.com/acm/contest/122/D来源:牛客网 题目描述 小C最近学会了java小程序的开发,他很开心,于是想做一个简单的记事本程序练练手. 他 ...

  4. [洛谷P2602][ZJOI2010]数字计数

    题目大意:求区间$[l,r]$中数字$0\sim9$出现个数 题解:数位$DP$ 卡点:无 C++ Code: #include <cstdio> #include <iostrea ...

  5. [洛谷P1801]黑匣子_NOI导刊2010提高(06)

    题目大意:两个操作:向一个可重集中加入一个元素:询问第$k$大的数($k$为之前询问的个数加一) 题解:离散化,权值线段树直接查询 卡点:无 C++ Code: #include <cstdio ...

  6. 2018宁夏邀请赛K Vertex Covers

    题目链接:https://nanti.jisuanke.com/t/28411 题意: 给出n(n<=36)个点的一个图.求点覆盖集数. 题解: 将n个点折半为L和R两部分.对于R内部的边,枚举 ...

  7. 安徽师大附中%你赛day9 T3 贵 解题报告

    贵 问题描述 苟先生的狼狗大军没有追上富先生, 所以他把它们都解雇了, 决定去雇佣一些更好的狗, 不过狗可是很贵的.苟先生有 \(w\) 元钱, 有 \(n\) 条狗可以雇佣, 第 \(i\) 条狗有 ...

  8. CVPR2014 Objectness 源码转换(完整版) VS2012 X64 –>win32

    一.版本转换  1.将源码中vs2012 X64版本转换为vs2012 win32版本. 2.源码下载及其相关资料下载http://mmcheng.net/zh/bing/ 3.需要下载源码(Pape ...

  9. [hdu 1067]bfs+hash

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1067 queue里面果然不能放vector,还是自己写的struct比较省内存…… #include& ...

  10. 用angular.element实现jquery的一些功能的简单示例

    下面实现了在每个p元素后面自动添加hello world. 在这里我要说的是jquery中的 $document.ready()相当于angualr 中的angualr.element(documen ...