利用密码学技术(哈希函数)可以实现盲拍。
在规定时间内竞拍者给出他们的出价,但出价是不可见的,直至拍卖结束并且竞拍的价格被揭示(reveal),竞价最高者赢得拍卖,这样互相就不知道到底谁出价是多少,在拍卖结束后所有人才能得到最高出价。
我们可以其前准备好盲拍需要的值:
keccak函数的输入可以事先准备好,在这里我们通过调用合约来实现(直接在remix中)
pragma solidity 0.7.6;
contract testContract
{
uint value;
bool fake;
uint secret;
constructor
(
uint _value,
bool _fake,
uint _secret
)
public
{
value = _value;
fake = _fake;
secret = _secret;
}
function encode() public view
returns (bytes32 result)
{
result = keccak256(abi.encodePacked(value, fake, secret));
}
}
账户0是部署智能合约的人,同时也将其设为受益者。
账户1参与盲拍,10以太币,fake设为false(即设置为有效出价),秘密值为(101),则生成的keccak值为:0x07f3e45fc119cd1a168f25138fc30d83ffc2224377e7332fc277b82c86a322b3
账户2参与盲拍,20以太币,fake设为false(即设置为有效出价),秘密值为(201),则生成的keccak值为:0x829b2f3a3afdf58ee75d639b1f6ecef0e2fcf338cbfc87259738e3e92f9db2e1
账户3参与盲拍,15以太币,fake设为false(即设置为有效出价),秘密值为(301),则生成的keccak值为:0x02c27359bed673bba1d9728fcbe2b82e5691c032113a6977091fe5eb75ceb34c
暂时就先准备这么多。blindauction合约代码如下:
// SPDX-License-Identifier: ATU-1.0
// Verifier: king; wechat group: pkutoken
pragma solidity >=0.7.0 <0.9.0;
contract BlindAuction{
//VARIABLES
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) pendingReturns;
// EVENT
event AuctionEnded(address winner, uint highestBid);
//MODIFIERS
modifier onlyBefore(uint _time){require(block.timestamp< _time); _; }
modifier onlyAfter(uint _time){require(block.timestamp< _time); _;}
// FUNCTIONS
constructor(
uint _biddingTime,
uint _revealTime,
address payable _beneficiary
){
beneficiary = _beneficiary;
biddingEnd = block.timestamp + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
function generateBlindedBidBytess32(uint value, bool fake, uint secret) public view returns(bytes32){
return keccak256(abi.encodePacked(value,fake,secret));
}
function bid(bytes32 _blindedBid) public payable onlyBefore(biddingEnd) {
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
function reveal(
uint[] memory _values,
bool[] memory _fake,
uint[]memory _secret
)
public
onlyAfter(biddingEnd)
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(_values.length == length);
require(_fake.length == length);
require (_secret.length == length);
for (uint i=0;i < length; i++) {
Bid storage bidTocheck = bids[msg.sender][i];
(uint value,bool fake,uint secret) = (_values[i], _fake[i],_secret[i]);
if (bidTocheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))){
continue;
}
if (!fake && bidTocheck.deposit >= value){
if(!placeBid(msg.sender,value)) {
payable(msg.sender).transfer(bidTocheck.deposit * (1 ether));
}
}
bidTocheck.blindedBid = bytes32(0);
}
}
function auctionEnd() public payable onlyAfter(revealEnd){
require(!ended);
emit AuctionEnded(highestBidder,highestBid);
ended = true;
beneficiary.transfer(highestBid *(1 ether));
}
function withdraw() public {
uint amount = pendingReturns[msg.sender];
if (amount > 0){
pendingReturns[msg.sender] = 0;
payable(msg.sender).transfer(amount * (1 ether));
}
}
function placeBid(address bidder, uint value) internal returns(bool success){
if (value <= highestBid){
return false;
}
//ensure the address is not zero
if (highestBidder != address(0)){
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
}
预期的结果是,在reveal之前,各个竞拍者可以任意出价(不叠加)(以哈希的形式,别人不会从中了解对手的出价),竞拍者都会从账户中扣除相应账户余额,在reveal之后,就能查看最高出价以及最高出价者,但是这时候已经无法再出价了(过了时间)。
在到达拍卖结束后,任何用户可以调用auctionEnd函数来结束拍卖,这时候最高出价者的出价就会被转到受益人账户,其他失败的出价者可以调用withdraw函数来回收他们的出价。
最后的结果应该是,账户0中多了20以太币,账户2中少了20以太币,其余未变化(除了调用合约函数消耗的)。
文件位置什么的可以参考上一篇拍卖的文章:
Truffle + Ganache 实现简单的拍卖智能合约(Windows10)_以太坊智能合约小白的博客-CSDN博客
在这里更改2_deploy_contracts.js文件:
var BlindAuction = artifacts.require('./BlindAuction.sol');
module.exports = function(deployer) {
deployer.deploy(BlindAuction,200,200,"0x840E940c19E56d8D93e0Ca650D7AE7a252172939");
}
即bid的时间和reveal的时间都是200秒。
接着migrate......
web3.eth.getAccounts().then(function(acc){ accounts = acc })
acc0 = accounts[0]
acc1 = accounts[1]
acc2 = accounts[2]
acc3 = accounts[3]
truffle(development)> acc0 = accounts[0]
'0x840E940c19E56d8D93e0Ca650D7AE7a252172939'
truffle(development)> acc1 = accounts[1]
'0x3209B9e5cabCF136AcAFC7184749788478bCbA31'
truffle(development)> acc2 = accounts[2]
'0x7f7171974C3cb51D5dF88bAEfab94450345F6118'
truffle(development)> acc3 = accounts[3]
'0x17b4578Bd4d200B9DE1623E4F1Fef5E1E2DA969D'
let instance = await BlindAuction.deployed()
bid阶段:这时候互相不知道各自的出价。(出价的同时已经扣除了相应以太币,使其pending,作为deposit,此时不会转入受益者账户。)可以多次出价,本文只设置出了一次。
instance.bid("0x07f3e45fc119cd1a168f25138fc30d83ffc2224377e7332fc277b82c86a322b3", {from:acc1, value:web3.utils.toWei('10','ether')})
instance.bid("0x829b2f3a3afdf58ee75d639b1f6ecef0e2fcf338cbfc87259738e3e92f9db2e1",{from:acc2, value:web3.utils.toWei('20','ether')})
instance.bid("0x02c27359bed673bba1d9728fcbe2b82e5691c032113a6977091fe5eb75ceb34c",{from:acc3, value:web3.utils.toWei('15','ether')})
reveal:如果正确(哈希值一致),且此刻没有更高出价,视作成功,否则退回deposit,出价失败。
instance.reveal([10],[false],[101],{from:acc1})
instance.reveal([20],[false],[201],{from:acc2})
instance.reveal([30],[false],[301],{from:acc3})
来看此时的账户余额:
truffle(development)> web3.eth.getBalance(acc0)
'125535483720000000000'
truffle(development)> web3.eth.getBalance(acc1)
'83952892740000000000'
truffle(development)> web3.eth.getBalance(acc2)
'59980973360000000000'
truffle(development)> web3.eth.getBalance(acc3)
'84992782900000000000'
到时间后,调用auctionEnd。
instance.auctionEnd.sendTransaction({from:acc0})
出价失败的账户调用withdraw函数收回deposit。(这里的失败指的是出价时是成功的,但是后来又reveal了新的更高的出价)
instance.withdraw({from:acc1})
instance.withdraw({from:acc3})
最后来看各个账户的余额,符合一开始的预期。
truffle(development)> web3.eth.getBalance(acc0)
'145534356500000000000'
truffle(development)> web3.eth.getBalance(acc1)
'93952491920000000000'
truffle(development)> web3.eth.getBalance(acc2)
'59980973360000000000'
truffle(development)> web3.eth.getBalance(acc3)
'84992338400000000000'
truffle(development)> let highest = await instance.highestBid.call()
undefined
truffle(development)> highest.toString()
'20'
truffle(development)> let highestBidder = await instance.highestBidder.call()
undefined
truffle(development)> highestBidder.toString()
'0x7f7171974C3cb51D5dF88bAEfab94450345F6118'
参考文章:https://www.youtube.com/watch?v=cgRB-uoXVLg&list=PLFPZ8ai7J-iTJDENUIY40VsU_5Wmxkr7j&index=10
根据例子学习Solidity — Solidity develop 文档
solidity keccak256初探_白速龙王的回眸的博客-CSDN博客_keccak256
solidity中transfer异常"send" and "transfer" are only available for objects of type address_程序新视界的博客-CSDN博客
Solidity中将address转换为address payable的方法_七月流火666的博客-CSDN博客_address payable
Truffle入门学习1-合约部署与函数调用_厨师长爱炼丹的博客-CSDN博客_truffle 调用合约
End
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)