Truffle + Ganache实现以太坊盲拍智能合约的部署以及交互

        利用密码学技术(哈希函数)可以实现盲拍。

        在规定时间内竞拍者给出他们的出价,但出价是不可见的,直至拍卖结束并且竞拍的价格被揭示(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


版权声明:本文为qq_40880250原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。