前言
随着区块链技能的发展,智能合约在各种场景中的应用越来越广泛。盲拍合约作为一种新兴的智能合约情势,利用暗码学原理为参与者提供了隐私掩护和安全保障。这种合约不仅增强了竞拍的公平性,还消除了时间压力,让参与者能够在更为放松的环境中进行投标。本文将深入探讨盲拍合约的定义、上风、工作原理以及代码实现,旨在为读者提供一个全面的明白。
一、什么是盲拍合约?
盲拍合约是一种智能合约,答应参与者在不公开其出价的环境下进行竞拍。参与者提交一个“盲出价”,其中包含出价金额、一个虚假的标志以及一个机密值。只有在竞拍结束时,参与者才气披露这些信息,从而验证他们的出价。
二、盲拍合约的上风
盲拍合约的利益在于,参与者在投标结束前不会感受到时间压力。在透明的计算平台上进行机密竞拍听起来似乎抵牾,但暗码学的应用使这统统成为可能。
1.时间压力的缓解
在投标期间,投标人实际上并没有发送真实出价,而只是发送出价的哈希版本。由于几乎不可能找到两个(足够长的)值,其哈希值相等,投标人可以通过这种方式提交出价。投标结束后,投标人必须公开他们的出价,合约会检查披露的出价是否与之前提交的哈希值相同。
2.绑定与机密的寻衅
另一个寻衅是怎样使拍卖同时做到绑定与机密。唯一能阻止投标者在赢得拍卖后不付款的方式是让她将钱连同出价一起发送。但由于以太坊中资金转移不可隐藏,任何人都可以看到转移的资金。
合约通过接受任何大于当前最高出价的值来办理这个标题。虽然在披露阶段才进行检查,有些出价可能是无效的,但这也是故意的。投标人可以通过设置几个高或低的无效出价来迷惑竞争对手。
三、盲拍合约的工作原理
1.提交盲出价
参与者通过 bid 函数提交盲出价,计算方式为:
在这里,value 是实际出价金额,fake 是一个布尔值,用于隐藏真实出价,secret 是一个32字节的机密字符串,用于防止加密前过于简朴而导致轻易暴力破解的环境。此计算使得盲出价在未披露前无法被识别
2.披露出价
在竞拍结束后,参与者利用 reveal 函数披露出价。只有正确披露的出价会被验证:
假如出价有效且未标志为假,合约会将其视为有效出价并处理
3.结束拍卖
在竞拍结束后,auctionEnd 函数将确定最高出价并将其转移给部署合约时设置的三个参数受益人:
4.退款机制
对于无效出价或低于最高出价的出价,合约会将存入的包管金退还给参与者。这通过 pendingReturns 映射来实现
四、代码示例
以下是完备的盲拍合约代码:
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.27;
- contract BlindAuction {
- struct Bid {
- bytes32 blindedBid;
- uint deposit;
- }
- address payable public beneficiary;
- uint public biddingEnd;
- uint public revealEnd;
- bool public ended;
- mapping(address => Bid[]) public bids;
- address public highestBidder;
- uint public highestBid;
- // 可以取回的之前的出价
- mapping(address => uint) public pendingReturns;
- event AuctionEnded(address winner, uint highestBid);
- // 定义错误
- error TooEarly(uint currentTime, uint endTime);
- error TooLate(uint currentTime, uint endTime);
- error AuctionAlreadyEnded();
- /// 使用 modifier 可以更便捷的校验函数的入参。
- /// `onlyBefore` 会被用于后面的 `bid` 函数:
- /// 新的函数体是由 modifier 本身的函数体,并用原函数体替换 `_;` 语句来组成的。
- // modifier onlyBefore(uint _time) { require(block.timestamp < _time); _; }
- // modifier onlyAfter(uint _time) { require(block.timestamp > _time); _; }
- modifier onlyBefore(uint _time) {
- if (block.timestamp >= _time) revert TooLate(block.timestamp, _time);
- _;
- }
- modifier onlyAfter(uint _time) {
- if (block.timestamp <= _time) revert TooEarly(block.timestamp, _time);
- _;
- }
- constructor(
- uint _biddingTime,
- uint _revealTime,
- address payable _beneficiary
- ) {
- beneficiary = _beneficiary;
- biddingEnd = block.timestamp + _biddingTime;
- revealEnd = biddingEnd + _revealTime;
- }
- /// 可以通过 `_blindedBid` = keccak256(value, fake, secret)
- /// 设置一个秘密竞拍。
- /// 只有在出价披露阶段被正确披露,已发送的以太币才会被退还。
- /// 如果与出价一起发送的以太币至少为 “value” 且 “fake” 不为真,则出价有效。
- /// 将 “fake” 设置为 true ,然后发送满足订金金额但又不与出价相同的金额是隐藏实际出价的方法。
- /// 同一个地址可以放置多个出价。
- // function bid(bytes32 _blindedBid)
- // external
- // payable
- // onlyBefore(biddingEnd)
- // {
- // bids[msg.sender].push(Bid({
- // blindedBid: _blindedBid,
- // deposit: msg.value
- // }));
- // }
- function bid(uint value, bool fake, bytes32 secret)
- external
- payable
- onlyBefore(biddingEnd)
- {
- // 计算 blindedBid 内部使用,仅供存储或其他用途
- bytes32 blindedBid = keccak256(abi.encodePacked(value, fake, secret));
- bids[msg.sender].push(Bid({
- blindedBid: blindedBid,
- deposit: msg.value
- }));
- }
- /// 披露你的秘密竞拍出价。
- /// 对于所有正确披露的无效出价以及除最高出价以外的所有出价,你都将获得退款。
- function reveal(
- uint[] memory _values,
- bool[] memory _fake,
- bytes32[] memory _secret
- )
- external
- payable
- onlyAfter(biddingEnd)
- onlyBefore(revealEnd)
- {
- uint length = bids[msg.sender].length;
- require(_values.length == length, "Mismatched values length");
- require(_fake.length == length, "Mismatched fake flags length");
- require(_secret.length == length, "Mismatched secrets length");
- uint refund;
- for (uint i = 0; i < length; i++) {
- Bid storage bidInfo = bids[msg.sender][i];
- (uint value, bool fake, bytes32 secret) =
- (_values[i], _fake[i], _secret[i]);
- if (bidInfo.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
- // 出价未能正确披露
- // 不返还订金
- continue;
- }
- refund += bidInfo.deposit;
- if (!fake && bidInfo.deposit >= value) {
- if (placeBid(msg.sender, value))
- refund -= value;
- }
- // 使发送者不可能再次认领同一笔订金
- bidInfo.blindedBid = bytes32(0);
- }
- // Cast msg.sender to address payable
- address payable sender = payable(msg.sender);
- sender.transfer(refund);
- }
- // 这是一个 "internal" 函数, 意味着它只能在本合约(或继承合约)内被调用
- 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;
- }
- /// 取回出价(当该出价已被超越)
- function withdraw() public payable {
- uint amount = pendingReturns[msg.sender];
- if (amount > 0) {
- // 这里很重要,首先要设零值。
- // 因为,作为接收调用的一部分,
- // 接收者可以在 `transfer` 返回之前重新调用该函数。(可查看上面关于‘条件 -> 影响 -> 交互’的标注)
- pendingReturns[msg.sender] = 0;
- // Cast msg.sender to address payable
- address payable sender = payable(msg.sender);
- sender.transfer(amount);
- }
- }
- /// 结束拍卖,并把最高的出价发送给受益人
- function auctionEnd()
- public
- payable
- onlyAfter(revealEnd)
- {
- // require(!ended);
- if (ended) revert AuctionAlreadyEnded();
- emit AuctionEnded(highestBidder, highestBid);
- ended = true;
- beneficiary.transfer(highestBid);
- }
- }
复制代码 总结
通过本文,我们详细介绍了盲拍合约的定义、上风、工作原理及其代码实现。盲拍合约利用暗码学原理为参与者提供隐私掩护,减轻时间压力,并确保出价的绑定与机密。我们解说了参与者怎样提交盲出价、披露出价、结束拍卖及退款机制。希望这篇文章能帮助你深入明白盲拍合约及其在区块链中的应用。假如你有任何疑问或建议,欢迎在批评区留言讨论 |