盲拍智慧合約solidity程式碼
阿新 • • 發佈:2022-03-30
盲拍:
盲拍分為競價階段和最終競價比較階段,在競價期間,競價者並不傳送他們的真實競價,而是一個雜湊資料(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