盲拍智能合约solidity代码
盲拍:
盲拍分为竞价阶段和最终竞价比较阶段,在竞价期间,竞价者并不发送他们的真实竞价,而是一个哈希数据(bytes32 blindedBid)。
竞价期结束后,竞价者必须出示他们的竞价,即多个真假竞价信息数组组合,哈希数据用于帮助合约检查哈希值是否与竞价期提供的哈希值相同。
此外,为防止竞价者在赢得拍卖后不给钱,唯一方法是让竞价者把钱和出价一起寄出去,由于ether转账在以太坊中不能被加密,任何人都可以看到竞价。
下文的contract通过接受任何大于最高竞价来解决这个问题。由于只能在披露阶段进行检查,一些出价可能是无效的,投标者可以通过放置几个高或低的无效出价来混淆竞争。
下文代码在solidity官方英文文档基础上添加很多注释用于帮助加深理解。
pragma solidity ^0.8.4; //盲拍,Remix编译 contract BlindAuction{ struct Bid{ bytes32 blindedBid; uint deposit; } address payable public beneficiary;//竞价受益人 uint public biddingEnd;//盲拍规定周期 uint public revealEnd; bool public ended;//竞价状态,默认值为false //一个竞价者对应有多个竞价信息,用bids保存,其中只有一个为真,其他都为假 mapping(address => Bid[]) public bids; address public highestBidder;//最高竞价者地址 uint public highestBid;//最高竞价 //mapping类似于散列表和字典,只能声明为状态变量,不支持迭代,支持嵌套 mapping(address => uint) pendingReturns;//保存所有最高竞价信息 //调用event可以将合约中某些内容的更改记录到日志(记录在区块链上),用关键字emit修饰调用 //事件(event)和日志不能在合约内部访问,可以被子合约调用 event AuctionEnded(address winner, uint highestBid);//记录当前最高竞价者 error TooEarly(uint time);//标识盲拍周期还未开始 error TooLate(uint time);//标识盲拍周期已结束 error AuctionEndAlreadyCalled();//标识盲拍已进行 //modifier类似于一个可以通用的函数供其他function重复调用,减少代码量 //_;可以放在modifier结构体{}内的任何位置来运行调用modifier的function代码 //当前时间已错过盲拍周期 modifier onlyBefore(uint time){ if(block.timestamp >= time) revert TooLate(time); _; } //盲拍还未开始 modifier onlyAfter(uint time){ if(block.timestamp <= time) revert TooEarly(time); _; } //构造函数,仅在部署合约时调用一次,初始化盲拍受益者地址、盲拍竞价周期、 constructor(uint biddingTime,uint revealTime,address payable beneficiaryAddress){ beneficiary = beneficiaryAddress; biddingEnd = block.timestamp + biddingTime; revealEnd = biddingEnd + revealEnd; } //先判断当前时间是否在竞拍周期内,再将竞拍信息写入bids function bid(bytes32 blindedBid) external payable onlyBefore(biddingEnd){ //注:msg.sender指调用该函数的地址,而不是调用整个合约的地址,.push是动态数组方法 bids[msg.sender].push(Bid({ blindedBid:blindedBid, deposit:msg.value })); } //calldata用于存储函数参数,是不可修改、非持久的函数参数存储区域 //先判断该function是否处于正确的盲拍周期内,values[]存储所有真假竞价信息 //fakes[]标记values[]中对应信息的真假,secrets[]仅用于辅助计算加密信息,无实际意义 //reveal()用于从多个真假竞拍信息中找出真的并进行转账 function reveal(uint[] calldata values,bool[] calldata fakes,bytes32[] calldata secrets) external onlyAfter(biddingEnd) onlyBefore(revealEnd){ uint length = bids[msg.sender].length;//记录调用该function的地址的竞拍次数 //require()中判断条件为true则继续,为false则退出该function,回退该function内所有更改 require(values.length == length);//values[]、fakes[]、secrets[]的长度均相等 require(fakes.length == length);//否则退出该function require(secrets.length == length); uint refund;//用于保存真的那个竞价信息 //keccak256是SHA-3成熟的一种加密算法 //遍历调用者的所有竞拍信息,可有多个真的竞价信息 for(uint i=0;i<length;i++){ Bid storage bidToCheck = bids[msg.sender][i];//取出调用者的第i条竞拍信息 (uint value,bool fake,bytes32 secret) = (values[i],fakes[i],secrets[i]);//calldata类型数据只读,不能修改,故需赋值 //abi.encodePacked(...) returns (bytes memory) //abi.encodePacked()是solidity中数据打包、连接方法 //元素之间用逗号连接,支持多种不同数据类型(struct和嵌套数组除外) //keccak256(bytes memory) returns (bytes32)用于加密数据 if(bidToCheck.blindedBid != keccak256(abi.encodePacked(value,fake,secret))){ continue;//跳出本次循环继续下一次循环 } refund += bidToCheck.deposit;//找到真的竞价信息就将竞价金额deposit取出 if(!fake && bidToCheck.deposit >= value){ if(placeBid(msg.sender,value)) refund -= value; }//若该条竞价信息为真且该竞价者的钱足够支付,则调用placeBid()转钱并置空refund bidToCheck.blindedBid = bytes32(0);//将此条竞价标识置为0x00 } //运行到此处refund==0,为防止for循环出错,返还调用者竞价金额 payable(msg.sender).transfer(refund);//给调用此function的地址转账 } //重置盲拍状态 function auctionEnd() external onlyAfter(revealEnd){ if(ended) revert AuctionEndAlreadyCalled();//若成立则退出函数,回退已更改状态 emit AuctionEnded(highestBidder,highestBid);//记录最高竞价者、最高竞价为事件于日志上 ended = true; beneficiary.transfer(highestBid);//给盲拍受益人转账 } //退钱 function withdraw() external{ uint amount = pendingReturns[msg.sender]; if(amount>0){ pendingReturns[msg.sender] = 0; payable(msg.sender).transfer(amount); } } //用于转帐 function placeBid(address bidder,uint value) internal returns(bool success){ if(value <= highestBid){ return false;//当前竞价并非最高,竞价失败 } if(highestBidder != address(0)){ pendingReturns[highestBidder] += highestBid; }//若竞价者地址有效,则添加竞价信息 highestBid = value;//重置最高竞价 highestBidder = bidder;//重置最高竞价者地址 return true; } }
来源(solidity官方英文文档0.8.13):https://docs.soliditylang.org/en/v0.8.13/solidity-by-example.html#id2
solidity官方中文文档0.8.0:https://learnblockchain.cn/docs/solidity/solidity-by-example.html#id5
keccak256()官方英文文档介绍:https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode
abi.encodePacked()官方英文文档介绍:https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode