智能合約學(xué)習(xí):密封拍賣(truffle + ganache-cli)

本文主要介紹了如何使用truffle + Atom進(jìn)行以太坊密封拍賣智能合約的編寫,以及如何使用ganache-cli進(jìn)行智能合約的交互測(cè)試。


1 Trueffle框架編寫代碼

相關(guān)細(xì)節(jié)可以查看另一篇文章以太坊公開(kāi)拍賣智能合約(truffle + ganache-cli)。本文主要介紹合約實(shí)現(xiàn),以及一些新的點(diǎn)。

1.1 建立項(xiàng)目

PS H:\TestContract> mkdir BlindAuction


    目錄: H:\TestContract


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2018/7/14     14:34                BlindAuction


PS H:\TestContract> cd BlindAuction
PS H:\TestContract\BlindAuction> truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
PS H:\TestContract\BlindAuction> cd contracts
PS H:\TestContract\BlindAuction\contracts> truffle create contract BlindAuction
  • \contracts:存放智能合約源代碼的地方,可以看到里面已經(jīng)有一個(gè)sol文件,我們開(kāi)發(fā)的BlindAuction.sol文件就存放在這個(gè)文件夾。
  • \migrations:這是Truffle用來(lái)部署智能合約的功能,待會(huì)兒我們會(huì)新建一個(gè)類似1_initial_migration.js的文件來(lái)部署BlindAuction.sol。
  • \test:測(cè)試智能合約的代碼放在這里,支持jssol測(cè)試。
  • truffle-config.jstruffle.jsTruffle的配置文件,需要配置要連接的以太坊網(wǎng)絡(luò)。

1.2 創(chuàng)建合約

需求:
請(qǐng)實(shí)現(xiàn)一個(gè)拍賣協(xié)議,在該協(xié)議中,每個(gè)用戶可以提交自己的出價(jià)。但是用戶之間不能看到之間的出價(jià),最后出價(jià)最高的人獲得拍賣。

思路:
如何才能讓大家互相看不到出價(jià)呢?我們可以讓每個(gè)人把自己的出價(jià)加密一下,然后在一段時(shí)間內(nèi)大家都給出加密后的出價(jià)。再出價(jià)結(jié)束后,給出一段時(shí)間讓大家揭示自己的出價(jià),并且從中選擇最高的出價(jià)。

但是,我們依然可以從你傳遞的代幣的數(shù)量判斷你的出價(jià)。因此我們一個(gè)方案是大家只是支付定金,最后要補(bǔ)上全額。但是這個(gè)問(wèn)題是,大家可以根據(jù)已經(jīng)展示的用戶的出價(jià)來(lái)判斷自己是否展示自己的出價(jià)。

那么我們需要設(shè)計(jì)更加復(fù)雜的出價(jià)方案。一個(gè)方法是每個(gè)人都需要把自己的出價(jià)高于一個(gè)真實(shí)的出價(jià)值。同時(shí)允許用戶虛假的出價(jià),來(lái)混淆視聽(tīng)。但是這個(gè)都需要用戶在揭秘的時(shí)候,得到屬于自己的出價(jià)。

流程:

  • 進(jìn)入出價(jià)時(shí)刻
    • 每個(gè)人可以提交自己的出價(jià),且只能進(jìn)行一次報(bào)價(jià)
    • 用戶只需要傳遞的是出價(jià)的一個(gè)hash
  • 進(jìn)入展示時(shí)刻
    • 每個(gè)人給出自己之前提交的hash對(duì)應(yīng)的真實(shí)出價(jià)列表
    • 針對(duì)有效的出價(jià),我們進(jìn)行核算,選出勝者,退換押金
pragma solidity ^0.4.22;


contract BlindAuction {
  struct Bid{
      bytes32 blindBid;
      uint deposit;
  }

  address public beneficiary; // 受益人
//  uint public currentTime; // 當(dāng)前時(shí)間
  uint public biddingEnd; // 出價(jià)結(jié)束時(shí)間
  uint public revealEnd;  // 揭示價(jià)格結(jié)束時(shí)間
  bool public ended;  // 拍賣是否結(jié)束
  //uint public testValue; // 僅僅是為了測(cè)試用

  mapping(address => Bid) public bids;  // 地址到競(jìng)標(biāo)之間的映射

  address public highestBidder; // 最高出價(jià)者
  uint public highestBid;  // 最高出價(jià)

  // 允許撤回沒(méi)有成功的出價(jià)
  mapping(address => uint) pendingReturns;

  event AuctionEnded(address winner, uint highestBid);  // 拍賣結(jié)束的事件


  /// 修飾符主要用于驗(yàn)證輸入的正確
  /// onlyBefore和onlyAfter用于驗(yàn)證是否大于或小于一個(gè)時(shí)間點(diǎn)
  /// 其中‘_’是原始程序開(kāi)始執(zhí)行的地方
  modifier onlyBefore(uint _time) { require(now < _time); _;  }
  modifier onlyAfter(uint _time) {  require(now > _time); _;  }

  // 構(gòu)建函數(shù):保存受益人、競(jìng)標(biāo)結(jié)束時(shí)間、公示價(jià)格結(jié)束時(shí)間
  constructor(address _beneficiary, uint _biddingTime, uint _revealTime) public {
      beneficiary = _beneficiary;
      biddingEnd = now + _biddingTime;
      revealEnd = biddingEnd + _revealTime;
  }

  function getCurrentTime() public returns (uint){
  //    currentTime = now;
      return now;
  }
  // 由于truffle console中的web3.sha3()的返回值與solidity不同,在這里用一個(gè)測(cè)試函數(shù)
  // 為了得到hash值
  function Encryption(uint _value, uint nonce) public returns (bytes32){
      //require (_values != 0 && _fake != 0 && _secret != 0);
        return sha3(msg.sender, _value, nonce);
  }

  /// 給出一個(gè)秘密出價(jià)_blindBid = sha3(value, fake, secret)
  /// 給出的保證金只在出價(jià)正確時(shí)給予返回
  /// 有效的出價(jià)要求出價(jià)的ether至少到達(dá)value
  /// 如果是我的話,我會(huì)限制一個(gè)人的出價(jià)都是有效的才進(jìn)行進(jìn)一步的操作,從而增加造假的難度
  function bid(bytes32 _blindBid) public payable onlyBefore(biddingEnd) {
      require(bids[msg.sender].blindBid == bytes32(0) );
      bids[msg.sender] = Bid({blindBid: _blindBid, deposit:msg.value});
  }


  /// 公示你的出價(jià),對(duì)于正確參與的出價(jià),只要沒(méi)有最終獲勝,都會(huì)被歸還
  function reveal(uint _value, uint nonce) public onlyAfter(biddingEnd) onlyBefore(revealEnd) returns (uint){
   
      require (_value != 0 && nonce != 0);

      uint refund;

      var bid = bids[msg.sender];
      uint value = _value * 1 ether;
      
      // bid不正確,不會(huì)退回押金
      require(bid.blindBid == sha3(msg.sender, _value, nonce));
      
      refund += bid.deposit;
      if (bid.deposit >= value) {
          // 處理bid超過(guò)value的情況
          if(placeBid(msg.sender, value)){
                refund -= value;
          }
      }
      // 防止再次claimed押金
      bid.blindBid = bytes32(0);

      msg.sender.transfer(refund);  // 如果之前沒(méi)有置0,會(huì)有fallback風(fēng)險(xiǎn)
      return refund;
  }

  // 這是個(gè)內(nèi)部函數(shù),只能被協(xié)約本身調(diào)用
  function placeBid(address bidder, uint value) internal returns(bool success){
      if(value <= highestBid){
          return false;
      }
      if (highestBidder != 0){
          // 返回押金給之前出價(jià)最高的人
          pendingReturns[highestBidder] += highestBid;
      }
      highestBid = value;
      highestBidder = bidder;
      return true;
  }

  // 撤回過(guò)多的出價(jià)
  function withdraw() public {
      uint amount = pendingReturns[msg.sender];
      if (amount > 0) {
          // 一定要先置0,規(guī)避風(fēng)險(xiǎn)
          msg.sender.transfer(amount);
      }
  }

  // 拍賣結(jié)束,把代幣發(fā)給受益人
  function auctionEnd() public onlyAfter(revealEnd){
      require(!ended);
      ended = true;
      emit AuctionEnded(highestBidder, highestBid);
      beneficiary.transfer(highestBid);
  }

  function getBalance() public returns (uint) {
      return this.balance;
  }

  function () public{
        revert();
  }
}

關(guān)于modifier修飾符:
繼承這個(gè)modifier修飾的function加上一個(gè)特定的約束,在執(zhí)行函數(shù)時(shí),會(huì)先檢查modifier中的內(nèi)容。
特殊_表示使用修改符的函數(shù)體的替換位置。

1.3 編譯合約

同樣可以參考之前的文章,有詳細(xì)說(shuō)明。
在項(xiàng)目根目錄BlindAuction的powershell中執(zhí)行truffle compile命令:

PS H:\TestContract\BlindAuction> truffle compile
Compiling .\contracts\BlindAuction.sol...
Compiling .\contracts\Migrations.sol...

Compilation warnings encountered:

.....

Writing artifacts to .\build\contracts

2 Ganache-cli 部署測(cè)試智能合約

2.1 啟動(dòng)ganache-cli

打開(kāi)powershell終端,可以看到ganache-cli啟動(dòng)后自動(dòng)建立了10個(gè)賬號(hào)(Accounts),與每個(gè)賬號(hào)對(duì)應(yīng)的私鑰(Private Key)。每個(gè)賬號(hào)中都有100個(gè)測(cè)試用的以太幣(Ether)。
Note. ganache-cli僅運(yùn)行在內(nèi)存中,因此每次重開(kāi)時(shí)都會(huì)回到全新的狀態(tài)。

補(bǔ)充ganache-cli制定創(chuàng)建賬號(hào)數(shù):
ganache-cli,有時(shí)10個(gè)賬號(hào)可能不夠測(cè)試用。
我們可以使用ganache-cli -a 20來(lái)指定創(chuàng)建20個(gè)測(cè)試賬號(hào)。
命令ganache-cli -aganache-cli -accounts

啟動(dòng)選項(xiàng)

  • -a 或 –accounts: 指定啟動(dòng)時(shí)要?jiǎng)?chuàng)建的測(cè)試賬戶數(shù)量。
  • -e 或 –defaultBalanceEther: 分配給每個(gè)測(cè)試賬戶的ether數(shù)量,默認(rèn)值為100。
  • -b 或r –blockTime: 指定自動(dòng)挖礦的blockTime,以秒為單位。默認(rèn)值為0,表示不進(jìn)行自動(dòng)挖礦。
  • -d 或 –deterministic: 基于預(yù)定的助記詞(mnemonic)生成固定的測(cè)試賬戶地址。
  • -n 或 –secure: 默認(rèn)鎖定所有測(cè)試賬戶,有利于進(jìn)行第三方交易簽名。
  • -m 或 –mnemonic: 用于生成測(cè)試賬戶地址的助記詞。
  • -p 或 –port: 設(shè)置監(jiān)聽(tīng)端口,默認(rèn)值為8545。
  • -h 或 –hostname: 設(shè)置監(jiān)聽(tīng)主機(jī),默認(rèn)值同NodeJS的server.listen()。
  • -s 或 –seed: 設(shè)置生成助記詞的種子。.
  • -g 或 –gasPrice: 設(shè)定Gas價(jià)格,默認(rèn)值為20000000000。
  • -l 或 –gasLimit: 設(shè)定Gas上限,默認(rèn)值為90000。
  • -f 或 –fork: 從一個(gè)運(yùn)行中的以太坊節(jié)點(diǎn)客戶端軟件的指定區(qū)塊分叉。輸入值應(yīng)當(dāng)是該節(jié)點(diǎn)旳HTTP地址和端口,例如http://localhost:8545。 可選使用@標(biāo)記來(lái)指定具體區(qū)塊,例如:http://localhost:8545@1599200
  • -i 或 –networkId:指定網(wǎng)絡(luò)id。默認(rèn)值為當(dāng)前時(shí)間,或使用所分叉鏈的網(wǎng)絡(luò)id。
  • –db: 設(shè)置保存鏈數(shù)據(jù)的目錄。如果該路徑中已經(jīng)有鏈數(shù)據(jù),ganache-cli將用它初始化鏈而不是重新創(chuàng)建。
  • –debug:輸出VM操作碼,用于調(diào)試。
  • –mem:輸出ganache-cli內(nèi)存使用統(tǒng)計(jì)信息,這將替代標(biāo)準(zhǔn)的輸出信息。
  • –noVMErrorsOnRPCResponse:不把失敗的交易作為RCP錯(cuò)誤發(fā)送。開(kāi)啟這個(gè)標(biāo)志使錯(cuò)誤報(bào)告方式兼容其他的節(jié)點(diǎn)客戶端,例如geth和Parity。

特殊選項(xiàng)

  • –account: 指定賬戶私鑰和賬戶余額來(lái)創(chuàng)建初始測(cè)試賬戶。可多次設(shè)置:
    $ganache-cli --account="<privatekey>,balance" [--account="<privatekey>,balance"]
    注意私鑰長(zhǎng)度為64字符,必須使用0x前綴的16進(jìn)制字符串。賬戶余額可以是整數(shù),也可以是0x前綴的17進(jìn)制字符串,單位為wei。
    使用–account選項(xiàng)時(shí),不會(huì)自動(dòng)創(chuàng)建HD錢包。

  • -u 或 –unlock: 解鎖指定賬戶,或解鎖指定序號(hào)的賬戶??梢栽O(shè)置多次。當(dāng)與–secure選項(xiàng)同時(shí)使用時(shí),這個(gè)選項(xiàng)將改變指定賬戶的鎖定狀態(tài):
    $ ganache-cli --secure --unlock "0x1234..." --unlock "0xabcd..."
    也可以指定一個(gè)數(shù)字,按序號(hào)解鎖賬號(hào):
    $ ganache-cli --secure -u 0 -u 1

2.2 部署合約

(1)migrations目錄下創(chuàng)建一個(gè)名字叫做2_deploy_contracts.js的文件。文件中的內(nèi)容為:

var BlindAuction = artifacts.require('./BlindAuction.sol');

module.exports = function(deployer) {
    deployer.deploy(BlindAuction, '0xa988cf1fae1275fdacf1d5bdf591c2eb57e3e8e1', 500, 200);
}

(2)修改truffle.js文件,連接本地ganache-cli環(huán)境。參數(shù)在最開(kāi)初始化ganache-cli環(huán)境的窗口可以看到。

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks: {
    development:{
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // match any network id
    }
  }
};

(3)現(xiàn)在執(zhí)行truffle migrate命令,我們可以將BlindAuction.sol原始碼編譯成Ethereum bytecode。

PS H:\TestContract\BlindAuction> truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x8c623ea9503338d10d5d250d292290fe09ae1bf8f44d819af6056b1db8d2c036
  Migrations: 0xf908b0ae60ab2d974d68a19c54c4a8e8400d0e6a
Saving successful migration to network...
  ... 0x3982ca94a4259f9e6530ec8e9068653dce008e6dc75b371bc6dd87042bdc46b9
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying BlindAuction...
  ... 0xf78c790e3566212d13ddef7c0cbb62fe7a562ab2451cedf154506c02a7a4add4
  BlindAuction: 0x043d70d33c5953d12df26e17e55a4a5a9182aebd
Saving successful migration to network...
  ... 0x2b9c56edfdef31a1c7a3effcaeec9ec5fc9825bddcf5d937012d3d1314c2323f
Saving artifacts...

2.3 與合約交互

truffle提供命令行工具,執(zhí)行truffle console命令后,可用Javascript來(lái)和剛剛部署的合約互動(dòng)。

PS H:\TestContract\SimpleAuction> truffle console
truffle(development)>

使用web3.eth.accounts會(huì)輸出ganache-cli網(wǎng)絡(luò)上的所有賬戶。

truffle(development)> web3.eth.accounts
[ '0x9392600526453a1f140581cc6b2eceed4e73d9d6',
  '0x4d6bc04cf9caa4409a1193e13f20d38fc6fe65a7',
  '0x6f560775ed5180a362e133c212374b9449ce4f1a',
  '0x0f060c7a5e74926be69fa5aaf204cc81c007df69',
  '0x75ef9c9200cf06497df4f279f3f076b973b115ce',
  '0x06a2f829f5b8563faf7a67cdd0616ed7e49ae621',
  '0x2af8343940d42d327c11e211ed2b0b58b346d1aa',
  '0xea6cccbcb61f96a18ab15ed5966335178dd2ca30',
  '0xf57ecb22bfc8429f8e1acd1aae208634250af23d',
  '0xa988cf1fae1275fdacf1d5bdf591c2eb57e3e8e1' ]

我們需要準(zhǔn)備一些測(cè)試賬戶。
它會(huì)把第一個(gè)帳戶的地址分配給變量account0,第二個(gè)帳戶分配給變量account1。Web3是一個(gè)JavaScript API,它將RPC調(diào)用包裝起來(lái)以方便我們與區(qū)塊鏈進(jìn)行交互。
我在這里將第9個(gè)賬戶作為部署合約初始化的拍賣發(fā)起人。

2.3.1 參與拍賣的賬戶

PS H:\TestContract\BlindAuction> truffle console
truffle(development)> address = web3.eth.accounts[9]
'0xa988cf1fae1275fdacf1d5bdf591c2eb57e3e8e1'
truffle(development)> acc1 = web3.eth.accounts[1]
'0x4d6bc04cf9caa4409a1193e13f20d38fc6fe65a7'
truffle(development)> acc2 = web3.eth.accounts[2]
'0x6f560775ed5180a362e133c212374b9449ce4f1a'
truffle(development)> acc3 = web3.eth.accounts[3]
'0x0f060c7a5e74926be69fa5aaf204cc81c007df69'

我們可以看一下拍賣發(fā)起人以及第一個(gè)賬戶的余額:

truffle(development)> web3.eth.getBalance(address)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }

2.3.2 啟動(dòng)拍賣

現(xiàn)在我們需要先啟動(dòng)一個(gè)拍賣,才能進(jìn)行接下來(lái)的操作。
當(dāng)拍賣一旦啟動(dòng),計(jì)時(shí)就開(kāi)始了。

truffle(development)> let contract
undefined
truffle(development)> BlindAuction.deployed().then(instance => contract = instance)

Note: 非常非常重要
大家如果看代碼,可以發(fā)現(xiàn),我在合約中寫了一個(gè)生成hash的函數(shù),實(shí)際使用中會(huì)被別人調(diào)用來(lái)破解你的實(shí)際價(jià)格。然后目前存在一個(gè)問(wèn)題是,truffle consoleweb3.sha3()sodility中的sha3() / keccak256()hash結(jié)果不同。我只能通過(guò)調(diào)用solidity函數(shù)加密得到hash值,再用這個(gè)hash值做后續(xù)測(cè)試。并且目前我在網(wǎng)上沒(méi)有找到很好的解決辦法。如果有好的辦法的,請(qǐng)一定告訴我,感謝??!

truffle console
web3.sha3(string, options):只能放一個(gè)變量,如web3.sha3("hello world", {encoding: 'hex')。

solidity
sha3(arg1, arg2, ...):可以放多個(gè)變量,如web3.sha3(2, 3, 4)。

但是我需要多個(gè)參數(shù)的hash,console這個(gè)無(wú)法實(shí)現(xiàn)。
還有一個(gè)最重要的問(wèn)題是:在console中定義一個(gè)報(bào)價(jià)value = 2,web3.sha3(2)是會(huì)報(bào)錯(cuò)的。我們用web3.sha3('2'),結(jié)果會(huì)和solidity中的sha3(2)完全不同,這種寫法在solidity中正確。實(shí)際上在solidity中傳遞進(jìn)去的參數(shù)2,是uint格式,它真正的寫法是0x0000000000...002256位的16進(jìn)制數(shù)。

這時(shí)候我們就想到了使用web3.sha3(web3.toHex(2))或者web3.sha3('2', {encoding: 'hex'}),然而發(fā)現(xiàn)結(jié)果仍然不對(duì)。

那肯定是web3.toHex(2)solidity中的2不一樣,果然打印web3.toHex(2),結(jié)果是0x2

在網(wǎng)上看到有人說(shuō)使用leftpad填充成256位,然而嘗試了一下,沒(méi)有這個(gè)函數(shù)。如果有網(wǎng)友可以幫我解決在truffle console中將類似0x2的數(shù)填充成0x00000000000...002這樣的數(shù)的問(wèn)題,那我將不勝感激。

所以由于上述問(wèn)題的存在,我們首先需要先生成參與拍賣賬戶的hash,并記錄下來(lái),以便參與bid過(guò)程。

truffle(development)> contract.Encryption.call(30,1000,{from:acc1})
'0x6403671c6162860975ffd948c931906f19a21bb99274689fd4c91dcc48000f5a'
truffle(development)> contract.Encryption.call(65,1323,{from:acc2})
'0x5a8fd4c2bb0a84b354464a85e3b81b9fa74d1fe6dc8071ba24b3a91b9faa8c51'
truffle(development)> contract.Encryption.call(40,576,{from:acc3})
'0x7182e09ffb55fc0d219ea44373a9d2e586cd82c0bc2fc3a674b3f8ad4230bb5e'
truffle(development)> bid1 = '0x6403671c6162860975ffd948c931906f19a21bb99274689fd4c91dcc48000f5a'
'0x6403671c6162860975ffd948c931906f19a21bb99274689fd4c91dcc48000f5a'
truffle(development)> bid2 = '0x5a8fd4c2bb0a84b354464a85e3b81b9fa74d1fe6dc8071ba24b3a91b9faa8c51'
'0x5a8fd4c2bb0a84b354464a85e3b81b9fa74d1fe6dc8071ba24b3a91b9faa8c51'
truffle(development)> bid3 = '0x7182e09ffb55fc0d219ea44373a9d2e586cd82c0bc2fc3a674b3f8ad4230bb5e'
'0x7182e09ffb55fc0d219ea44373a9d2e586cd82c0bc2fc3a674b3f8ad4230bb5e'

2.3.3 開(kāi)始報(bào)價(jià)

此時(shí)我們用acc1調(diào)用bid(),發(fā)送hash值,并且附帶50 ether作為押金。

truffle(development)> contract.bid(bid1,{from:acc1,value:web3.toWei(50,"ether")})
{ tx: '0xcc5d68719659eff2a1393375b6d629e512397426e1a323b51c14e38339f10f2f',
  receipt:
   { transactionHash: '0xcc5d68719659eff2a1393375b6d629e512397426e1a323b51c14e38339f10f2f',
     transactionIndex: 0,
     blockHash: '0xf17e17497165eb47cc692e4e4a8afe14a77acce153c439f9d2529a291f6d7f9d',
     blockNumber: 5,
     gasUsed: 64655,
     cumulativeGasUsed: 64655,
     contractAddress: null,
     logs: [],
     status: '0x1',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
  logs: [] }

并且查看此時(shí)acc1余額。

truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 19, c: [ 499912, 93500000000000 ] }

這里給出參與拍賣三個(gè)賬戶的參數(shù),以及bid結(jié)束后各賬戶的余額:

Account Bid Nonce value(deposit) Balance
acc1 30 1000 50 499912, 93500000000000
acc2 65 1323 70 299912, 93500000000000
acc3 40 576 80 199912, 93500000000000
address 拍賣發(fā)起人 1000000

2.3.4 打開(kāi)價(jià)格

等待報(bào)價(jià)結(jié)束,展示報(bào)價(jià)階段:

truffle(development)> contract.reveal(30,1000,{from:acc1})
{ tx: '0xf2e2ceb794991d66fc545a444c6d996ff7de4de25e5a7294bf649eeb8c9266da',
  receipt:
   { transactionHash: '0xf2e2ceb794991d66fc545a444c6d996ff7de4de25e5a7294bf649eeb8c9266da',
     transactionIndex: 0,
     blockHash: '0x7eb68508ddbdedd3b10a76781555c20cb73595f72fc400d7b1af36de245db2ee',
     blockNumber: 8,
     gasUsed: 62209,
     cumulativeGasUsed: 62209,
     contractAddress: null,
     logs: [],
     status: '0x1',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
  logs: [] }

同樣地:

truffle(development)> contract.reveal(65,1323,{from:acc2})
...
truffle(development)> contract.reveal(40,576,{from:acc3})

打開(kāi)報(bào)價(jià):

報(bào)價(jià)高于當(dāng)前highestBid時(shí),會(huì)進(jìn)行替換最高價(jià),并且退回deposit-bid的部分,同時(shí)將上一個(gè)報(bào)價(jià)最高的人的價(jià)格存起來(lái),最后可以取回。

報(bào)價(jià)低于當(dāng)前highestBid時(shí),會(huì)直接退回全部deposit,競(jìng)價(jià)失敗。

報(bào)價(jià)全部打開(kāi),沒(méi)有按時(shí)打開(kāi)的,或者給出了與開(kāi)始報(bào)價(jià)不同的錯(cuò)誤報(bào)價(jià),將會(huì)失去自己的押金。

再看此時(shí)各賬戶的余額(此時(shí)還沒(méi)有進(jìn)行withdraw):

Account Value
acc1 699873, 13600000000000
acc2 349882, 31300000000000
acc3 999913, 62800000000000

分析一下:

acc1第一個(gè)打開(kāi),成為highestBid,因?yàn)?code>30 > 0。返還差價(jià)50-30 = 20 ether,賬戶目前少了30 ether。

acc2第二個(gè)打開(kāi),成為highestBid,因?yàn)?code>65 > 30。返還差價(jià)70 - 65 = 5 ether,賬戶目前少了65 ether。同時(shí)將上一個(gè)最高價(jià)者(也就是acc1)的價(jià)格30加入pendingReturns,以供拍賣結(jié)束后,acc1取回自己的錢。此時(shí)pendingReturns[acc1] = 30。

acc3第三個(gè)打開(kāi),40 < 65,直接退回所有押金80 ether,賬戶目前不少錢。

當(dāng)然會(huì)發(fā)現(xiàn),每個(gè)賬戶都會(huì)少量的失去以太幣,這是因?yàn)榻灰仔枰?code>gas。

2.3.5 拍賣結(jié)束

最終,拍賣發(fā)起者結(jié)束拍賣:

truffle(development)> contract.auctionEnd({from:address})
{ tx: '0xb6c6aaf61b1a5be2996c26c35e0e32c23328e11308b10e33746d966f2910197e',
  receipt:
   { transactionHash: '0xb6c6aaf61b1a5be2996c26c35e0e32c23328e11308b10e33746d966f2910197e',
     transactionIndex: 0,
........
       event: 'AuctionEnded',
       args: [Object] } ] }
truffle(development)> web3.eth.getBalance(address)
BigNumber { s: 1, e: 20, c: [ 1649948, 3300000000000 ] }

可以看到發(fā)起者addressbalance = 1649948, 3300000000000,增加了約65 ether。

其他拍賣者,可以取回自己的錢:

truffle(development)> contract.withdraw({from:acc1})
{ tx: '0x69eb8c1fc115ef55c7f5174fa67724b589b526269dc29bd268cf1a5d8d373e94',
  receipt:
   { transactionHash: '0x69eb8c1fc115ef55c7f5174fa67724b589b526269dc29bd268cf1a5d8d373e94',
.........
  logs: [] }
truffle(development)> contract.withdraw({from:acc2})
truffle(development)> contract.withdraw({from:acc3})

看看當(dāng)前各賬戶余額:

Account Balance
address 1649948, 3300000000000
acc1 999843, 79000000000000
acc2 349860, 47000000000000
acc3 999891, 78500000000000

至此,整個(gè)拍賣過(guò)程完全結(jié)束。

2.5 賬戶變化過(guò)程總結(jié)

同時(shí)給出拍賣參數(shù)和賬戶余額變化,方便比較。

參與hash的三個(gè)參數(shù),地址&真實(shí)報(bào)價(jià)&隨機(jī)數(shù)。

Account Bid Nonce value(deposit)
acc1 30 1000 50
acc2 65 1323 70
acc3 40 576 80

每個(gè)參與拍賣的賬戶余額變化:

賬戶 1.初始化 2.Bid 3.Reveal 4.End & Withdraw
address 1000000 1000000 1000000 1649948, 3300000000000
acc1 1000000 499912, 93500000000000 699873, 13600000000000 9999843, 79000000000000
acc2 1000000 299912, 93500000000000 349882, 31300000000000 349860, 4700000000000
acc3 1000000 199912, 93500000000000 999913, 62800000000000 999891, 78500000000000

如果測(cè)試過(guò)程出現(xiàn)什么難以理解的事情,請(qǐng)把build文件夾完全刪除,重新編譯。
因?yàn)榫幾g器多次編譯,內(nèi)部出的錯(cuò)誤,讓我折騰了一天,都懷疑自己了。╮(╯▽╰)╭

本文作者:Joyce
文章來(lái)源:http://www.itdecent.cn/p/8d7bbf966dda
版權(quán)聲明:轉(zhuǎn)載請(qǐng)注明出處!

2018年7月16日

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容