// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
/**
BlindAuction合約是基于上一份SampleAuction的擴(kuò)展,在公開(kāi)競(jìng)標(biāo)的基礎(chǔ),增加隱藏競(jìng)標(biāo)價(jià)格的功能
由于合約是公開(kāi)透明的,并且每個(gè)人每次發(fā)送競(jìng)標(biāo)‘金額’都可以通過(guò)區(qū)塊鏈直接查看value金額,那怎么做到可以隱藏大家的‘競(jìng)標(biāo)價(jià)’?
隱藏競(jìng)標(biāo)價(jià),則代表隱藏了調(diào)用合約競(jìng)標(biāo)時(shí)發(fā)送的value,那么怎么確保他們贏得拍賣之后付款?
解決此問(wèn)題使用了“加密付款金額”和“訂金”的模式
調(diào)用者發(fā)起競(jìng)拍時(shí),先把自己的實(shí)際競(jìng)投“金額”使用hash加密,用blindedBid參數(shù)記錄,然后實(shí)際發(fā)送到合約的eth則作為“訂金”
在競(jìng)投結(jié)束時(shí),用戶則發(fā)送解密的valueString、和密鑰,如果blindedBid = hash('valueString','密鑰')則代表valueString是真實(shí)的‘競(jìng)拍價(jià)‘
最后,如果贏得拍賣,可退回金額 = 訂金 - 競(jìng)拍價(jià),競(jìng)拍失敗的用戶,則全款可退回
部署地址 https://rinkeby.etherscan.io/tx/0x9c023537bb1038ba2f7e639ec4184010c9e32067ca4734d2e33bf5c063d3fff9
*/
contract BlindAuction {
// 用戶的競(jìng)標(biāo)結(jié)構(gòu)體
struct Bid {
bytes32 blindedBid; // 加密后的競(jìng)標(biāo)價(jià)
uint deposit; // 訂金
}
address payable public beneficiary; // 受益人
uint public biddingEnd; // 拍賣結(jié)束時(shí)間
uint public revealEnd; // 拍賣結(jié)束后,公開(kāi)實(shí)際競(jìng)標(biāo)金額的時(shí)間
bool public ended; // 拍賣時(shí)候結(jié)束
mapping(address => Bid[]) public bids; // 通過(guò)用戶address映射他的出價(jià),出價(jià)用數(shù)組表示,可以出價(jià)多次
address public highestBidder; // 最高出價(jià)人的address
uint public highestBid; // 最高出價(jià)
mapping(address => uint) pendingReturns; // 通過(guò)用戶address映射他的可退金額
event AuctionEnded(address winner, uint amount); // 通知外部,整場(chǎng)競(jìng)拍活動(dòng)結(jié)束
// 通過(guò)下列Error返回,告知調(diào)用者執(zhí)行出錯(cuò)
/// 函數(shù)末開(kāi)放調(diào)用,請(qǐng)?jiān)赻time`之后在嘗試
error TooEarly(uint time);
/// 函數(shù)不允許在`time`之后調(diào)用
error TooLate(uint time);
/// 拍賣已結(jié)束
error AuctionEndAlreadyCalled();
// Modify修飾符是驗(yàn)證函數(shù)輸入的便捷方法
// 可以理解為先驗(yàn)證Modify里面的條件,通過(guò)之后,再執(zhí)行舊函數(shù)的代碼塊,’_‘就代表舊函數(shù)的代碼塊
// 檢查block.timestamp當(dāng)前時(shí)間是否在`time`之前
modifier onlyBefore(uint time) {
if (block.timestamp >= time) revert TooLate(time);
_;
}
// 檢查block.timestamp當(dāng)前時(shí)間是否在`time`之后
modifier onlyAfter(uint time) {
if (block.timestamp <= time) revert TooEarly(time);
_;
}
//
constructor (
uint biddingTime, // 拍賣持續(xù)時(shí)間(秒為單位)
uint revealTime, // 拍賣結(jié)束后,有多小時(shí)間可以解謎自己的實(shí)際競(jìng)投金額(秒為單位)
address payable beneficiaryAddress // 結(jié)束后,受益人地址
) {
beneficiary = beneficiaryAddress;
biddingEnd = block.timestamp + biddingTime;
revealEnd = biddingEnd + revealTime;
}
/// 競(jìng)拍時(shí)使用加密后的盲價(jià),`blindedBid` = keccak256(abi.encodePacked(value, fake, secret)).
/// 只有在拍賣公開(kāi)大家競(jìng)標(biāo)價(jià)的reveal階段,正確的輸入以上加密的值(包括value, fake, secret),解謎成功,才能發(fā)起’提現(xiàn)‘退款
/// 由于每個(gè)競(jìng)拍者都可以發(fā)起多次競(jìng)拍,只有當(dāng)fake是true的時(shí)候,才會(huì)當(dāng)作有效出價(jià)
/// fake作用是給調(diào)用者多次出價(jià),從而可以隱藏自己的真實(shí)出價(jià)
function bid(bytes32 blindedBid)
external
payable
onlyBefore(biddingEnd) // Modify修飾符
{
bids[msg.sender].push(Bid({
blindedBid: blindedBid, // 加密后的實(shí)際競(jìng)投金額
deposit: msg.value // 實(shí)際收到的ETH則當(dāng)做訂金
}));
}
/// 公開(kāi)你的真實(shí)競(jìng)拍價(jià),對(duì)于所有正確驗(yàn)證,但無(wú)效的出價(jià),都會(huì)獲得退款
function revael(
uint[] calldata values, // 多次出價(jià)的實(shí)際value數(shù)組
bool[] calldata fakes, // 多次出價(jià)的fake數(shù)組,fake表示那次出價(jià)有效
bytes32[] calldata secrets // 解謎密鑰數(shù)組
)
external
onlyAfter(biddingEnd) // Modify修飾符 biddingEnd時(shí)間之后
onlyBefore(revealEnd) // Modify修飾符 revealEnd時(shí)間之前
{
// length是用戶出價(jià)次數(shù)
uint length = bids[msg.sender].length;
// 校驗(yàn)數(shù)組和出價(jià)次數(shù)是否匹配
require(values.length == length);
require(fakes.length == length);
require(secrets.length == length);
// 可退回金額
uint refund;
// 遍歷,校驗(yàn)拍賣階段加密過(guò)的出價(jià),和解密后的值是否匹配,匹配則出價(jià)有效
// 并且計(jì)算可退回金額refund
for (uint i =0; i < length; i++) {
// 獲取用戶在bids數(shù)組的歷史競(jìng)價(jià)bid對(duì)象出來(lái)
Bid storage bidToCheck = bids[msg.sender][i];
// 獲取對(duì)應(yīng)index的值
(uint value, bool fake, bytes32 secret) = (values[i], fakes[i], secrets[i]);
// 校驗(yàn)加密的blindedBid和用戶提供的元素加密后是否匹配
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
// 如果不正確則無(wú)法退回訂金,進(jìn)行下一輪校驗(yàn)
continue;
}
// 來(lái)到這里 - 代表校驗(yàn)通過(guò)
// 記錄退款額度為當(dāng)初預(yù)繳的訂金
refund += bidToCheck.deposit;
// 判斷預(yù)繳訂金,是否比value出價(jià)高,并且fake為true,則代表真實(shí)出價(jià)
if (!fake && bidToCheck.deposit > value) {
// 判斷是否最高出價(jià)
if (placeBid(msg.sender, value)) {
// 則訂金 - 投標(biāo)價(jià)
refund -= value;
}
}
// 把盲拍金額數(shù)據(jù)清0,防止重復(fù)提款
bidToCheck.blindedBid = bytes32(0);
}
// 退回?zé)o效出價(jià)
payable(msg.sender).transfer(refund);
}
/// 曾經(jīng)成為最高價(jià)的競(jìng)拍者,但是最后落選,可以通過(guò)此函數(shù)取出競(jìng)拍金額
function withdraw() external {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
// 先將可回退金額設(shè)置為0,防止在send()過(guò)程中,用戶重復(fù)提款,又進(jìn)入到此提款判斷
// 記住應(yīng)該遵從 1.條件判斷 -> 2.修改狀態(tài) 3.與合約交互or轉(zhuǎn)錢
pendingReturns[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
}
/// 結(jié)束整個(gè)競(jìng)拍活動(dòng),并向受益人轉(zhuǎn)賬
function auctionEnd()
external
onlyAfter(revealEnd)
{
// 判斷整個(gè)競(jìng)拍活動(dòng)是否已結(jié)束
if (ended) revert AuctionEndAlreadyCalled();
// 告知獲勝競(jìng)拍人address和amount
emit AuctionEnded(highestBidder, highestBid);
// 修改狀態(tài)
ended = true;
// 向受益人轉(zhuǎn)賬
beneficiary.transfer(highestBid);
}
// ’internal’關(guān)鍵字
// 意思是,這個(gè)函數(shù)屬于內(nèi)部可以訪問(wèn)的函數(shù),即只能本合約(或繼承的合約)調(diào)用
// 此函數(shù)是對(duì)比當(dāng)前最高價(jià),并記錄
function placeBid(address bidder, uint value) internal returns (bool success){
// 對(duì)比最高價(jià)
if (value < highestBid) {
return false;
}
// 之前知否有最高出價(jià)人
if (highestBidder != address(0)) {
// 有則把之前最高出價(jià)人的address和amount記錄在pendingReturns,到時(shí)“提現(xiàn)“時(shí)使用
pendingReturns[highestBidder] += highestBid;
}
// 記錄最高價(jià)和最高出價(jià)人address
highestBidder = bidder;
highestBid = value;
return true;
}
}
[solidity]盲拍合約
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 1 場(chǎng)景 在盲拍的應(yīng)用場(chǎng)景中,我們定義如下幾個(gè)關(guān)鍵要素: 受益人,最終的錢款接收方 競(jìng)拍者,任意持有合法賬戶的人都...
- 4.揭標(biāo) 揭標(biāo)的過(guò)程應(yīng)該是本智能合約中最復(fù)雜且具有靈魂的關(guān)鍵步驟。當(dāng)每個(gè)發(fā)起過(guò)競(jìng)標(biāo)的用戶,利用該標(biāo)的隱式價(jià)格和密碼...
- 1.功能描述 該合約主要功能描述如下:用戶A發(fā)起盲拍項(xiàng)目,即提出拍賣申請(qǐng)。 競(jìng)標(biāo)原則 1.所有人都可以對(duì)任何一個(gè)盲...
- 1.功能描述 該合約主要功能描述如下:用戶A發(fā)起盲拍項(xiàng)目,即提出拍賣申請(qǐng)。 競(jìng)標(biāo)原則 1.所有人都可以對(duì)任何一個(gè)盲...
- 1、solodity簡(jiǎn)介 Solidity語(yǔ)言是一種以太坊智能合約高級(jí)編程語(yǔ)言,運(yùn)行在以太坊(Ethereum)虛...