热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

solidity智能合约秘密拍卖合约讲解

前言:所谓秘密拍卖,就是在拍卖期间别人无法知道你的出价,因此拍卖的所有出价都是由自己决定,不受他人影响。在一定程度上

前言:
所谓秘密拍卖,就是在拍卖期间别人无法知道你的出价,因此拍卖的所有出价都是由自己决定,不受他人影响。在一定程度上,这样的秘密拍卖更能体现商品的真实价值,因为它避免了真实拍卖中抬价的现象。

秘密拍卖合约:
在solidity官方文档中,给出了一个秘密拍卖合约的代码。由于拍卖的过程需要进行交易,而交易的记录是死死记录在链上的,这不就无法秘密拍卖了吗?

它这里把交易的钱和承诺拍卖的钱进行了区分,也就是说,光从交易的钱中是无法看出真实的叫价。而真实的叫价,连同一个bool值和一个bytes32的秘密值以keccak的形式输入到合约中。因此,外人是无法知道你的真实叫价的。当且仅当拍卖结束,进入公布阶段时,所有人的拍卖才能被揭示。

需要注意的是,所有给了交易钱的人必须执行reveal函数,否则它的钱将永远无法被退回。当所有的人都执行了reveal(揭示)函数,合约会锁定最高价,然后将其他合法的叫价如数退还。倘若你忘记了你的一组(真实叫价,bool,bytes32 secret)值,那你该次的交易金额将永远锁定在合约中。揭示时间一旦过去,佛祖也回天乏力。

由于代码的复杂程度太高,我这里就不做测试了,代码如下:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >&#61;0.7.0 <0.9.0;contract BlindAuction {struct Bid {bytes32 blindedBid;uint deposit;}address payable public beneficiary;uint public biddingEnd;uint public revealEnd;bool public ended;mapping(address &#61;> Bid[]) public bids;address public highestBidder;uint public highestBid;// 可以取回的之前的出价mapping(address &#61;> uint) pendingReturns;event AuctionEnded(address winner, uint highestBid);/// 使用 modifier 可以更便捷的校验函数的入参。/// &#96;onlyBefore&#96; 会被用于后面的 &#96;bid&#96; 函数&#xff1a;/// 新的函数体是由 modifier 本身的函数体&#xff0c;并用原函数体替换 &#96;_;&#96; 语句来组成的。modifier onlyBefore(uint _time) { require(block.timestamp < _time); _; }modifier onlyAfter(uint _time) { require(block.timestamp > _time); _; }constructor(uint _biddingTime,uint _revealTime,address payable _beneficiary) public {beneficiary &#61; _beneficiary;biddingEnd &#61; block.timestamp &#43; _biddingTime;revealEnd &#61; biddingEnd &#43; _revealTime;}/// 可以通过 &#96;_blindedBid&#96; &#61; keccak256(value, fake, secret)/// 设置一个秘密竞拍。/// 只有在出价披露阶段被正确披露&#xff0c;已发送的以太币才会被退还。/// 如果与出价一起发送的以太币至少为 “value” 且 “fake” 不为真&#xff0c;则出价有效。/// 将 “fake” 设置为 true &#xff0c;然后发送满足订金金额但又不与出价相同的金额是隐藏实际出价的方法。/// 同一个地址可以放置多个出价。function bid(bytes32 _blindedBid)publicpayableonlyBefore(biddingEnd){bids[msg.sender].push(Bid({blindedBid: _blindedBid,deposit: msg.value}));}/// 披露你的秘密竞拍出价。/// 对于所有正确披露的无效出价以及除最高出价以外的所有出价&#xff0c;你都将获得退款。function reveal(uint[] memory _values,bool[] memory _fake,bytes32[] memory _secret)publiconlyAfter(biddingEnd)onlyBefore(revealEnd){uint length &#61; bids[msg.sender].length;require(_values.length &#61;&#61; length);require(_fake.length &#61;&#61; length);require(_secret.length &#61;&#61; length);uint refund;for (uint i &#61; 0; i < length; i&#43;&#43;) {Bid storage bid &#61; bids[msg.sender][i];(uint value, bool fake, bytes32 secret) &#61;(_values[i], _fake[i], _secret[i]);if (bid.blindedBid !&#61; keccak256(abi.encodePacked(value, fake, secret))){// 出价未能正确披露// 不返还订金continue;}refund &#43;&#61; bid.deposit;if (!fake && bid.deposit >&#61; value) {if (placeBid(msg.sender, value))refund -&#61; value;}// 使发送者不可能再次认领同一笔订金bid.blindedBid &#61; bytes32(0);}msg.sender.transfer(refund);}// 这是一个 "internal" 函数&#xff0c; 意味着它只能在本合约&#xff08;或继承合约&#xff09;内被调用function placeBid(address bidder, uint value) internalreturns (bool success){if (value <&#61; highestBid) {return false;}if (highestBidder !&#61; address(0)) {// 返还之前的最高出价pendingReturns[highestBidder] &#43;&#61; highestBid;}highestBid &#61; value;highestBidder &#61; bidder;return true;}/// 取回出价&#xff08;当该出价已被超越&#xff09;function withdraw() public {uint amount &#61; pendingReturns[msg.sender];if (amount > 0) {// 这里很重要&#xff0c;首先要设零值。// 因为&#xff0c;作为接收调用的一部分&#xff0c;// 接收者可以在 &#96;transfer&#96; 返回之前重新调用该函数。&#xff08;可查看上面关于‘条件 -> 影响 -> 交互’的标注&#xff09;pendingReturns[msg.sender] &#61; 0;msg.sender.transfer(amount);}}/// 结束拍卖&#xff0c;并把最高的出价发送给受益人function auctionEnd()publiconlyAfter(revealEnd){require(!ended);emit AuctionEnded(highestBidder, highestBid);ended &#61; true;beneficiary.transfer(highestBid);}
}

如上图&#xff0c;其实大家可能在很多地方都见过这段代码&#xff0c;但真的理解了吗&#xff1f;如果理解了&#xff0c;真的在remix上进行过测试了吗&#xff1f;只有亲手测试&#xff0c;才能看出一些问题。

我在这里提醒几个点
1. 在本地进行keccak256(value, fake, secret)测试时&#xff0c;要知道这里的value是以wei为单位的&#xff0c;我们一般都用ether进行测试&#xff0c;因为这样可以忽略掉gas。因此这里的value若以ether为单位&#xff0c;则要进行换算&#xff1a;1ether &#61; 10^18 wei

2. 在本地做测试的时候&#xff0c;biddingtime和revealtime一定要调整好&#xff0c;如果太短则没有充足的时间测试&#xff0c;如果太长则需要等待一大段时间出结果

接下来&#xff0c;我们还是说一下这个合约中函数的功能

constructor&#xff1a;
构造函数&#xff0c;设定竞拍时间和揭示时间

bid:
规定在竞拍结束前&#xff0c;每个人可以多次竞拍&#xff0c;每次竞拍存入一个哈希和交易值

reveal&#xff1a;
规定在竞拍结束后&#xff0c;揭示结束前&#xff0c;每个人一般情况下必须进行揭示。倘若不进行揭示非但自己的叫价不会被记录&#xff0c;更重要的是自己的钱绝对拿不回来&#xff01;

你需要输入的是你n次竞拍的真实信息&#xff0c;每次竞拍包括value&#xff0c;fake&#xff0c;secret&#xff0c;当且仅当你的这三个信息的keccak256与你之前输入的keccak256完全一致&#xff0c;你才可能会有退钱。同时&#xff0c;倘若你出的是最高价&#xff0c;将不会退钱。

这里其实是有点问题的&#xff0c;这就需要在所有人执行reveal两次。第一次是选取最高价&#xff0c;第二次是保证所有人都能退钱&#xff0c;可是&#xff0c;倘若有节点一直等待到最后一时刻才进行第一次reveal&#xff0c;那么它这种行为可能就会坑到人。

比如说&#xff0c;当前的最高价为99&#xff0c;出价人为A。然后&#xff0c;等到揭示的最后一时刻&#xff0c;出价人B第一次执行reveal&#xff0c;它的出价为100&#xff0c;那么B将成为最高出价人&#xff0c;同时&#xff0c;揭示阶段结束。这时候&#xff0c;A欲哭无泪&#xff0c;因为它既没有拍到&#xff0c;也取不出钱了。因此最稳妥的做法就是在reveal期间不停地调用reveal函数&#xff0c;防止被人坑。然而&#xff0c;这是很浪费资源的。

placeBid&#xff1a;
reveal调用地internal函数&#xff0c;负责记录了最高价以及出价人&#xff0c;同时将非最高价的钱转移到pendingReturn三种

withdraw&#xff1a;
无时间限制&#xff0c;可以任何时候取回自己失效的资金。前提是要通过reveal进行。并且&#xff0c;一定是曾经做过最高价的人才会有机会出动pendingRuturns&#xff0c;否则它的钱在reveal阶段就已经返还了。

autionEnd&#xff1a;
当揭示阶段结束&#xff0c;该函数负责将最高价者出的钱转移到受益人的账户上

总结&#xff1a;
这个办法的确做到了隐藏竞拍价格的目的&#xff0c;但reveal阶段非常麻烦&#xff0c;需要频繁reveal保证自己的曾经的最高资金在被别人盖掉后还有机会落入pendingreturns。这点可能是开发团队疏忽的。

大家如果有什么想法或者问题&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流&#xff0c;共同进步&#xff01;


推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • Summarize function is doing alignment without timezone ?
    Hi.Imtryingtogetsummarizefrom00:00otfirstdayofthismonthametric, ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 本文介绍了一种图的存储和遍历方法——链式前向星法,该方法在存储带边权的图时时间效率比vector略高且节省空间。然而,链式前向星法存图的最大问题是对一个点的出边进行排序去重不容易,但在平行边无所谓的情况下选择这个方法是非常明智的。文章还提及了图中搜索树的父子关系一般不是很重要,同时给出了相应的代码示例。 ... [详细]
author-avatar
温蚊童鞋_612
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有