本文主要介紹了如何使用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è)試智能合約的代碼放在這里,支持js與sol測(cè)試。 -
truffle-config.js和truffle.js:Truffle的配置文件,需要配置要連接的以太坊網(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 -a或ganache-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 console中web3.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ā)起者address的balance = 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日