修訂日期 | 姓名 | 郵箱 |
---|---|---|
2018-10-18 | brucefeng | brucefeng@brucefeng.com |
寫這篇文章的初衷其實很簡單,在MyEtherWallet上申請以太坊ENS的時候,競標的以太幣兩次被吞,並且是在規定時間點進行了價格公告,這篇文章的設計思路其實就是跟ENS的競標流程相似,但願對你們有所幫助,因此,準備寫完以後,再從新去整一次ENS的申請,若是再被吞,我就要舉報了:-),本文主要是本人用於項目整理,便於本身查詢,不作任何商業用途。css
如今迴歸到技術上來,這個項目其實涉及到蠻多的知識點的,是很是不錯的以太坊智能合約以及Dapp學習項目,至少在目前而言,尚未看到特別好的學習項目被分享出來,經過該項目,咱們能夠掌握以下內容:前端
(1)項目展現node
容許商家列出項目,咱們將爲任何人創建免費列出項目的功能,咱們會將這些項目都存儲在區塊鏈和非區塊鏈的數據庫中,方便查詢。webpack
(2) 文件存儲web
將文件添加到IPFS:咱們將商品圖像和商品描述(大文本)上傳至IPFS的功能。mongodb
(3)瀏覽商品數據庫
咱們將添加根據類別,拍賣時間等過濾和瀏覽商品的功能。npm
(4)商品拍賣編程
實現維克裏密封拍賣,招標流程跟ENS相似。json
(5)託管合約
一旦投標結束,商品有贏家,咱們將在買方,賣方和第三方仲裁員之間建立一個託管合同
(6) 2-of-3數字簽名
咱們將經過2-of-3數字,其中3名參與者中的2名必須投票將資金釋放給賣方或者將金額退還給賣方。
如下圖片來源於網絡
(1) Web前端
HTML,CSS,JavaScript(大量使用web3js),用戶將經過這個前端應用程序與區塊鏈,IPFS和NodeJS服務器進行交互
(2) 區塊鏈
這是全部代碼和交易所在的應用程序的核心,商店中全部商品,用戶出價和託管都寫在區塊鏈上。
(3) NodeJS服務器
這是前端經過其與數據庫進行通訊的後端服務器,咱們將公開一些簡單的API來爲前端查詢和從數據庫中檢索商品。
(4) MongoDB
儘管商品存儲在區塊鏈中,可是查詢區塊鏈展現商品和應用各類過濾器(僅顯示特定類別的商品,顯示即將過時的商品等)效率並不高,咱們將使用MongoDB數據庫來存儲商品信息並查詢它以展現商品。
(5)區塊鏈存儲IPFS
當用戶在商店中列出商品時,前端會將商品文件和描述上傳至IPFS,並將上傳文件的散列HASH存儲到區塊鏈中。
(1) 用戶訪問前端
(2) 將商品文件與描述信息傳至IPFS中
(3) IPFS返回對應的Hash值
(4) 網頁前端調用合約將Hash值結合產品ID,拍賣時間,分類,價格等寫入區塊鏈中
(5) 從區塊鏈中讀取數據展現在web前端
(6) NodeJs服務器監聽這些事件,當事件被合約觸發時,服務器從區塊鏈中取出數據緩存至mongodb中。
先經過truffle 和 solidity實現合約代碼,將其部署到truffle develop自帶的測試網絡中,而且在truffle console中能夠自由交互。
經過命令行安裝並與IPFS交互
在後端實現完成後,咱們將構建Web前端以與合約和IPFS進行交互,咱們也會實現招標,揭示前端的拍賣功能。
咱們將安裝MongoDB並設計數據結構來存儲商品
數據庫啓動並容許後,咱們將實現監聽合約時間的NodeJS服務端代碼,並將請求記錄到控制檯,而後咱們將執行代碼將商品插入數據庫中。
咱們將更新到咱們的前端,從數據庫而不是區塊鏈中查找商品(如何保證數據庫中的數據不被篡改?)
Truffle是針對基於以太坊的Solidity語言的一套開發框架。自己基於Javascript,相比於沒有框架編寫Solidity智能合約,Truffle提供了以下功能
maven
或gradle
這樣的項目構建機制,能自動生成相關目錄,默認是基於Web的。Javascript
中直接操做對應的合約函數。原理是使用了基於web3.js
封裝的Ether Pudding
工具包。關於其相關介紹,能夠直接到Truffle官網進行了解。
安裝Truffle很是簡單,官網上面也很是簡單明瞭
$ npm install truffle -g
一樣的,本文只寫相關相關的內容與步驟,此處不作過多擴展,移步官方文檔查看更多的內容。
$ mkdir auctionDapp/ ; cd auctionDapp $ truffle unbox webpack
建立項目目錄`auctionDapp
,並進行初始化工做,返回以下信息則表示truffle項目框架搭建完畢
. ├── LICENSE ├── app //前端設計 ├── box-img-lg.png ├── box-img-sm.png ├── build //智能合約編譯後文件存儲路徑 ├── contracts //智能合約文件存儲路徑 ├── migrations //存放發佈腳本文件 ├── node_modules //相關nodejs庫文件 ├── package-lock.json ├── package.json //安裝包信息配置文件 ├── test //合約測試文件存放路徑 ├── truffle.js // truffle配置文件 └── webpack.config.js // webpack配置文件
將用於測試的智能合約刪除,避免干擾咱們的項目。
$ rm -rf contracts/{ConvertLib.sol,MetaCoin.sol}
提到Box,做爲藍鯨智雲的忠實粉絲與早期佈道者,有必要提一下藍鯨MagicBox,那是一個專門提供給運維開發人員的前端框架集合,這裏的box也是相似的用途,官網是這麼描述的
TRUFFLE BOXES THE EASIEST WAY TO GET STARTED Truffle Boxes are helpful boilerplates that allow you to focus on what makes your dapp unique. In addition to Truffle, Truffle Boxes can contain other helpful modules, Solidity contracts & libraries, front-end views and more; all the way up to complete example dapps.
簡而言之,TRUFFLE BOXES
就是將solidity智能合約,相關庫,前端框架都集成在一塊兒的集合,方便開發人員在最大程度上簡化沒必要要的環境搭建與技術選型工做。
Webpack 是一個前端資源加載/打包工具。它將根據模塊的依賴關係進行靜態分析,而後將這些模塊按照指定的規則生成對應的靜態資源。
從圖中咱們能夠看出,Webpack 能夠將多種靜態資源 js、css等轉換成一個靜態文件,減小了頁面的請求。
本章節定義了一個名爲AuctionStore
的合約,定義了枚舉變量ProductStatus
用於區分商品競拍的階段,定義枚舉變量ProductCondition
用於標識拍賣商品是新品仍是二手商品,爲了便於統計商品數量,咱們定義了uint
類型變量productIndex
經過遞增的方式存儲商品數量,在商品發佈以後會造成兩個字典表。
產品ID | 發佈者錢包地址 |
---|---|
1 | 0x627306090abab3a6e1400e9345bc60c78a8bef57 |
2 | 0xf17f52151ebef6c7334fad080c5704d77216b732 |
3 | 0xf17f52151ebef6c7334fad080c5704d77216b732 |
4 | 0x627306090abab3a6e1400e9345bc60c78a8bef57 |
5 | 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef |
如上表
產品ID(1,4)的發佈者爲0x627306090abab3a6e1400e9345bc60c78a8bef57
產品ID(2,3)的發佈者爲0xf17f52151ebef6c7334fad080c5704d77216b732
產品ID爲5的發佈者爲0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
發佈者錢包地址 | 產品ID | 商品對象 |
---|---|---|
0x627306090abab3a6e1400e9345bc60c78a8bef57 | 1 | 如"Macbook Pro 2016" |
0x627306090abab3a6e1400e9345bc60c78a8bef57 | 4 | 如"IPhone 8 Plus" |
0xf17f52151ebef6c7334fad080c5704d77216b732 | 2 | 如"IPhone X" |
0xf17f52151ebef6c7334fad080c5704d77216b732 | 3 | 如"Macbook Pro 2017" |
0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef | 5 | 如"Surface Pro4" |
代碼中定義了投標人結構體Bid
,主要保存其投標人錢包地址
,競標的產品ID
,競標價(虛價)
,是否揭標
,並將其字典映射做爲屬性放入商品結構體Product
中,關於商品結構體Product
的相關說明參考代碼中註釋便可。
pragma solidity ^0.4.24; //定義合約AuctionStore contract AuctionStore { //定義枚舉ProductStatus enum ProductStatus { Open, //拍賣開始 Sold, //已售出,交易成功 Unsold //爲售出,交易未成功 } enum ProductCondition { New, //拍賣商品是否爲新品 Used //拍賣商品是否已經使用過 } // 用於統計商品數量,做爲ID uint public productIndex; //產品Id與錢包地址的對應關係 mapping(uint => address) productIdInStore; // 經過地址查找到對應的商品集合 mapping(address => mapping(uint => Product)) stores; //增長投標人信息 struct Bid { address bidder; uint productId; uint value; bool revealed; //是否已經揭標 } struct Product { uint id; //產品id string name; //商品名稱 string category ; //商品分類 string imageLink ; //圖片Hash string descLink; // 圖片描述信息的Hash uint auctionStartTime; //開始競標時間 uint auctionEndTime; //競標結束時間 uint startPrice; //拍賣價格 address highestBidder ; //出價最高,贏家的錢包地址 uint highestBid ; //贏家得標的價格 uint secondHighestBid ; //競標價格第二名 uint totalBids ; //共計競標的人數 ProductStatus status; //狀態 ProductCondition condition ; //商品新舊標識 mapping(address => mapping(bytes32 => Bid)) bids;// 存儲全部投標人信息 } constructor ()public{ productIndex = 0; } }
咱們開始實現拍賣商品的發佈操做,須要保證傳入的商品拍賣開始時間不能晚於結束時間,當商品被添加後,統計商品的索引ID自增,根據傳入的商品屬性建立商品product
對象,將該對象存入stores
中,發佈者錢包地址爲msg.sender
(能夠經過from
參數傳入), 產品ID爲當前productIndex
的值,同時將數據存入productIdInStore
中,Index爲當前productIndex
的值,Value爲msg.sender
,經過該方法能夠實現
productIndex
自增1productIdInStore
添加數據stores
添加數據//實現添加商品到區塊鏈 function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _auctionStartTime, uint _auctionEndTime ,uint _startPrice, uint _productCondition) public { //開始時間須要小於結束時間 require(_auctionStartTime < _auctionEndTime,"開始時間不能晚於結束時間"); //商品索引ID自增 productIndex += 1; //product對象稍後直接銷燬,類型爲memory便可 Product memory product = Product(productIndex,_name,_category,_imageLink,_descLink,_auctionStartTime,_auctionEndTime,_startPrice,0,0,0,0,ProductStatus.Open,ProductCondition(_productCondition)); stores[msg.sender][productIndex] = product; productIdInStore[productIndex] = msg.sender; }
在實現對拍賣商品信息進行讀取的時候,咱們只須要經過其productId
從productIdInStore
中獲取發佈者地址,經過發佈者地址bidder
與productId
從stores
中獲取到product
對象,從而獲取該對象的相關屬性信息。
//經過產品ID讀取商品信息 function getProduct(uint _productId) public view returns (uint,string, string,string,string,uint ,uint,uint, ProductStatus, ProductCondition) { Product memory product = stores[productIdInStore[_productId]][_productId]; return (product.id, product.name,product.category,product.imageLink,product.descLink,product.auctionStartTime,product.auctionEndTime,product.startPrice,product.status,product.condition); }
商品發佈好以後,咱們須要在規定的時間段內進行商品投標操做,也就是競標,首先須要知足幾個前提
參考讀取商品信息getProduct
方法,經過競標方法傳入的productId
獲取到product
對象,將Bid
對象存入product
對象中,其中傳入的加密參數bid
是經過加密函數對實際競標價格+揭標密鑰進行加密後獲得的,同時將競標人數遞增1。
//投標,傳入參數爲產品Id以及Hash值(實際競標價與祕鑰詞語的組合Hash),須要添加Payable function bid(uint _productId, bytes32 _bid) payable public returns (bool) { Product storage product = stores[productIdInStore[_productId]][_productId]; require(now >= product.auctionStartTime, "商品競拍時間未到,暫未開始,請等待..."); require(now <= product.auctionEndTime,"商品競拍已經結束"); require(msg.value >= product.startPrice,"設置的虛擬價格不能低於開標價格"); require(product.bids[msg.sender][_bid].bidder == 0); //在提交競標以前,必須保證bid的值爲空 //將投標人信息進行保存 product.bids[msg.sender][_bid] = Bid(msg.sender, _productId, msg.value,false); //商品投標人數遞增 product.totalBids += 1; //返回投標成功 return true; }
本文提到的價格公告跟揭標屬於同一個概念,只是在使用的時候根據語境進行了相應的調整。
在競標結束後,競標人須要進行價格公告,核心在於競標人傳入的實際競標價_amount
與揭標密鑰_secret
的加密Hash值須要與上文的加密Hashbid
要一致,不然會找不到對應的錢包地址,同時要保證該帳戶以前並未進行價格揭標操做。
//公告,揭標方法 function revealBid(uint _productId, string _amount, string _secret) public { //確保當前時間大於投標結束時間 require(now > product.auctionEndTime,"競標還沒有結束,未到公告價格時間"); // 對競標價格與競價密鑰進行加密 bytes32 sealedBid = keccak256(_amount,_secret); //經過產品ID獲取商品信息 Product storage product = stores[productIdInStore[_productId]][_productId]; //獲取投標人信息 Bid memory bidInfo = product.bids[msg.sender][sealedBid]; //判斷是否存在錢包地址,錢包地址0x4333 uint160的錢包類型 require(bidInfo.bidder > 0,"該帳戶未在競標者信息中"); //判斷該帳戶是否已經揭標過 require(bidInfo.revealed == false,"該帳戶已經揭標"); // 定義系統的退款 uint refund; uint amount = stringToUint(_amount); // bidInfo.value是在競標時候定義的虛價,經過msg.value設置。 if (bidInfo.value < amount) { //若是bidInfo.value的值< 實際競標價,則返回所有退款,屬於無效投標 refund = bidInfo.value; }else { //若是屬於有效投標,參照以下分類 if (address(product.highestBidder) == 0) { //第一個參與公告的人,此時該值爲0 //將出標人的地址賦值給最高出標人地址 product.highestBidder = msg.sender; // 將出標人的價格做爲最高價格 product.highestBid = amount; // 將商品的起始拍賣價格做爲第二高價格 product.secondHighestBid = product.startPrice; // 將多餘的錢做爲退款,如bidInfo.value = 20,amount = 12,則退款8 refund = bidInfo.value - amount; }else { //此時參與者不是第一個參與公告的人 // amount = 15 , bidInfo.value = 25,amount > 12 if (amount > product.highestBid) { // 將原來的最高價賦值給第二高價 product.secondHighestBid = product.highestBid; // 將原來最高的出價退給原先的最高價地址 product.highestBidder.transfer(product.highestBid); // 將當前出價者的地址做爲最高價地址 product.highestBidder = msg.sender; // 將當前出價做爲最高價,爲15 product.highestBid = amount; // 此時退款爲 20 - 15 = 5 refund = bidInfo.value - amount; }else if (amount > product.secondHighestBid) { //將當前競標價做爲第二高價格 product.secondHighestBid = amount; //退還全部競標款 refund = amount; }else { //若是出價比第二高價還低的話,直接退還競標款 refund = amount; } } if (refund > 0){ //取回退款 msg.sender.transfer(refund); product.bids[msg.sender][sealedBid].revealed = true; } } }
此處的transfer不是常規的轉帳,能夠理解爲退款
//1. 獲取競標贏家信息 function highestBidderInfo (uint _productId)public view returns (address, uint ,uint) { Product memory product = stores[productIdInStore[_productId]][_productId]; return (product.highestBidder,product.highestBid,product.secondHighestBid); } //2. 獲取參與競標的人數 function totalBids(uint _productId) view public returns (uint) { Product memory product = stores[productIdInStore[_productId]][_productId]; return product.totalBids; } //3. 將字符串string到uint類型 function stringToUint(string s) pure private returns (uint) { bytes memory b = bytes(s); uint result = 0 ; for (uint i = 0; i < b.length; i++ ){ if (b[i] >=48 && b[i] <=57){ result = result * 10 + (uint(b[i]) - 48); } } return result; }
pragma solidity ^0.4.24; //定義合約AuctionStore contract AuctionStore { //定義枚舉ProductStatus enum ProductStatus { Open, //拍賣開始 Sold, //已售出,交易成功 Unsold //爲售出,交易未成功 } enum ProductCondition { New, //拍賣商品是否爲新品 Used //拍賣商品是否已經使用過 } // 用於統計商品數量,做爲ID uint public productIndex; //商品Id與錢包地址的對應關係 mapping(uint => address) productIdInStore; // 經過地址查找到對應的商品集合 mapping(address => mapping(uint => Product)) stores; //增長投標人信息 struct Bid { address bidder; uint productId; uint value; bool revealed; //是否已經揭標 } //定義商品結構體 struct Product { uint id; //商品id string name; //商品名稱 string category ; //商品分類 string imageLink ; //圖片Hash string descLink; // 圖片描述信息的Hash uint auctionStartTime; //開始競標時間 uint auctionEndTime; //競標結束時間 uint startPrice; //拍賣價格 address highestBidder ; //出價最高,贏家的錢包地址 uint highestBid ; //贏家得標的價格 uint secondHighestBid ; //競標價格第二名 uint totalBids ; //共計競標的人數 ProductStatus status; //狀態 ProductCondition condition ; //商品新舊標識 mapping(address => mapping(bytes32 => Bid)) bids;// 存儲全部投標人信息 } constructor ()public{ productIndex = 0; } //添加商品到區塊鏈中 function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _auctionStartTime, uint _auctionEndTime ,uint _startPrice, uint _productCondition) public { //開始時間須要小於結束時間 require(_auctionStartTime < _auctionEndTime,"開始時間不能晚於結束時間"); //商品ID自增 productIndex += 1; //product對象稍後直接銷燬便可 Product memory product = Product(productIndex,_name,_category,_imageLink,_descLink,_auctionStartTime,_auctionEndTime,_startPrice,0,0,0,0,ProductStatus.Open,ProductCondition(_productCondition)); stores[msg.sender][productIndex] = product; productIdInStore[productIndex] = msg.sender; } //經過商品ID讀取商品信息 function getProduct(uint _productId) public view returns (uint,string, string,string,string,uint ,uint,uint, ProductStatus, ProductCondition) { Product memory product = stores[productIdInStore[_productId]][_productId]; return (product.id, product.name,product.category,product.imageLink,product.descLink,product.auctionStartTime,product.auctionEndTime,product.startPrice,product.status,product.condition); } //投標,傳入參數爲商品Id以及Hash值(實際競標價與祕鑰詞語的組合Hash),須要添加Payable function bid(uint _productId, bytes32 _bid) payable public returns (bool) { Product storage product = stores[productIdInStore[_productId]][_productId]; require(now >= product.auctionStartTime, "商品競拍時間未到,暫未開始,請等待..."); require(now <= product.auctionEndTime,"商品競拍已經結束"); require(msg.value >= product.startPrice,"設置的虛擬價格不能低於開標價格"); require(product.bids[msg.sender][_bid].bidder == 0); //在提交競標以前,必須保證bid的值爲空 //將投標人信息進行保存 product.bids[msg.sender][_bid] = Bid(msg.sender, _productId, msg.value,false); //商品投標人數遞增 product.totalBids += 1; //返回投標成功 return true; } //公告,揭標方法 function revealBid(uint _productId, string _amount, string _secret) public { //經過商品ID獲取商品信息 Product storage product = stores[productIdInStore[_productId]][_productId]; //確保當前時間大於投標結束時間 require(now > product.auctionEndTime,"競標還沒有結束,未到公告價格時間"); // 對競標價格與關鍵字密鑰進行加密 bytes32 sealedBid = keccak256(_amount,_secret); //獲取投標人信息 Bid memory bidInfo = product.bids[msg.sender][sealedBid]; //判斷是否存在錢包地址,錢包地址0x4333 uint160的錢包類型 require(bidInfo.bidder > 0,"錢包地址不存在"); //判斷是否已經公告揭標過 require(bidInfo.revealed == false,"已經揭標"); // 定義系統的退款 uint refund; uint amount = stringToUint(_amount); // bidInfo.value 其實就是 mask bid,用於迷惑競爭對手的價格 if (bidInfo.value < amount) { //若是bidInfo.value的值< 實際競標價,則返回所有退款,屬於無效投標 refund = bidInfo.value; }else { //若是屬於有效投標,參照以下分類 if (address(product.highestBidder) == 0) { //第一個參與公告的人,此時該值爲0 //將出標人的地址賦值給最高出標人地址 product.highestBidder = msg.sender; // 將出標人的價格做爲最高價格 product.highestBid = amount; // 將商品的起始拍賣價格做爲第二高價格 product.secondHighestBid = product.startPrice; // 將多餘的錢做爲退款,如bidInfo.value = 20,amount = 12,則退款8 refund = bidInfo.value - amount; }else { //此時參與者不是第一個參與公告的人 // amount = 15 , bidInfo.value = 25,amount > 12 if (amount > product.highestBid) { // 將原來的最高價地址 賦值給 第二高價的地址 product.secondHighestBid = product.highestBid; // 將原來最高的出價退還給原先退給原先的最高價地址 product.highestBidder.transfer(product.highestBid); // 將當前出價者的地址做爲最高價地址 product.highestBidder = msg.sender; // 將當前出價做爲最高價,爲15 product.highestBid = amount; // 此時退款爲 20 - 15 = 5 refund = bidInfo.value - amount; }else if (amount > product.secondHighestBid) { // product.secondHighestBid = amount; //退還全部競標款 refund = amount; }else { //若是出價比第二高價還低的話,直接退還競標款 refund = amount; } } if (refund > 0){ //退款 msg.sender.transfer(refund); product.bids[msg.sender][sealedBid].revealed = true; } } } //幫助方法 //1. 獲取競標贏家信息 function highestBidderInfo (uint _productId)public view returns (address, uint ,uint) { Product memory product = stores[productIdInStore[_productId]][_productId]; return (product.highestBidder,product.highestBid,product.secondHighestBid); } //2. 獲取參與競標的人數 function totalBids(uint _productId) view public returns (uint) { Product memory product = stores[productIdInStore[_productId]][_productId]; return product.totalBids; } //3. 將字符串string到uint類型 function stringToUint(string s) pure private returns (uint) { bytes memory b = bytes(s); uint result = 0 ; for (uint i = 0; i < b.length; i++ ){ if (b[i] >=48 && b[i] <=57){ result = result * 10 + (uint(b[i]) - 48); } } return result; } }
$ truffle develop
此處
Warning
警告信息忽略即。
安裝
ethereumjs-util
,加密方法須要調用該庫
$ npm install ethereumjs-util
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 20, c: [ 1000000 ] } truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 20, c: [ 1000000 ] truffle(develop)> web3.eth.getBalance(web3.eth.accounts[3]) BigNumber { s: 1, e: 20, c: [ 1000000 ]
查詢用於測試的帳戶(競標帳戶)的原始額度,均爲100000.
truffle(develop)> auctionAmount = web3.toWei(1,'ether') '1000000000000000000'
truffle(develop)> auctionStartTime = Math.round(new Date() / 1000); 1539885333
truffle(develop)> AuctionStore.deployed().then(function(i) {i.addProductToStore('Macbook Pro 2018 001','Phones & Computers','imagesLink','descLink',auctionStartTime,auctionStartTime + 300,auctionAmount,0).then(function(f) {console.log(f)})});
競標時間設置爲5分鐘
truffle(develop)> AuctionStore.deployed().then(function(i) {i.productIndex.call().then(function(f) {console.log(f)})})
truffle(develop)> AuctionStore.deployed().then(function(i) {i.getProduct.call(1).then(function(f) {console.log(f)})})
獲取合約的方式還有:
truffle(develop)>var instance
truffle(develop)> instance = AuctionStore.deployed().then((i => {instance = i}))
truffle(develop)> instance.productIndex();
BigNumber { s: 1, e: 0, c: [ 1 ] }
務必在競標結束時間前完成競標操做
[1] 導入加密庫
truffle(develop)> EjsUtil = require('ethereumjs-util')
[2] 進行加密
truffle(develop)> sealedBid1 = '0x' + EjsUtil.keccak256(2*auctionAmount + 'firstsecrt').toString('hex') '0xb0d5a0c4d195f138442910cd2ccd16da585784a24482f7e320f48d850e0fb86d' truffle(develop)> sealedBid2 = '0x' + EjsUtil.keccak256(3*auctionAmount + 'secondsecrt').toString('hex') '0x9566873896902aca059cbe402b2aa82638fe6e57980c97ac25c576cc6496a233' truffle(develop)> sealedBid3 = '0x' + EjsUtil.keccak256(4*auctionAmount + 'threesecrt').toString('hex') '0x79e5fcbcc9065408e06f20d224c7183d82089e0fbe8e344446b5f4527b5d2f4f'
實際amount = 2 auctionAmount , Mask BId: 2.5 *auctionAmount
truffle(develop)> AuctionStore.deployed().then(function(i){i.bid(1,sealedBid1,{value:2.5*auctionAmount,from:web3.eth.accounts[1]}).then(function(f) {console.log(f)})})
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 974888, 13600000000000 ] }
實際amount =3 * auctionAmount , Mask BId: 3.5 *auctionAmount
truffle(develop)> AuctionStore.deployed().then(function(i){i.bid(1,sealedBid2,{value:3.5*auctionAmount,from:web3.eth.accounts[2]}).then(function(f) {console.log(f)})})
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 974888, 13600000000000 ] } truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 19, c: [ 964903, 13600000000000 ] }
實際amount = 4 * auctionAmount , Mask BId: 4.5 *auctionAmount
truffle(develop)> AuctionStore.deployed().then(function(i){i.bid(1,sealedBid3,{value:4.5*auctionAmount,from:web3.eth.accounts[3]}).then(function(f) {console.log(f)})})
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 974888, 13600000000000 ] } //扣除2.5ether以及部分gas truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 19, c: [ 964903, 13600000000000 ] }//扣除3.5ether以及部分gas truffle(develop)> web3.eth.getBalance(web3.eth.accounts[3]) BigNumber { s: 1, e: 19, c: [ 954903, 13600000000000 ] } //扣除4.5ether以及部分gas
時間必須超過競標結束時間才能執行合約,揭標時須要填寫實際競標價
truffle(develop)> AuctionStore.deployed().then(function(i) {i.revealBid(1,(2*auctionAmount).toString(),'firstsecrt',{from: web3.eth.accounts[1]}).then(function(f){console.log(f)})});
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 979711, 68300000000000 ] }//觀察變化 truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 19, c: [ 964903, 13600000000000 ] } truffle(develop)> web3.eth.getBalance(web3.eth.accounts[3]) BigNumber { s: 1, e: 19, c: [ 954903, 13600000000000 ] }
truffle(develop)> AuctionStore.deployed().then(function(i) {i.revealBid(1,(3*auctionAmount).toString(),'secondsecrt',{from: web3.eth.accounts[2]}).then(function(f){console.log(f)})});
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 999711, 68300000000000 ] } //觀察變化 truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 19, c: [ 969815, 53800000000000 ] } //觀察變化 truffle(develop)> web3.eth.getBalance(web3.eth.accounts[3]) BigNumber { s: 1, e: 19, c: [ 954903, 13600000000000 ] }
truffle(develop)> AuctionStore.deployed().then(function(i) {i.revealBid(1,(4* auctionAmount).toString(),'threesecrt',{from: web3.eth.accounts[3]}).then(function(f){console.log(f)})});
truffle(develop)> web3.eth.getBalance(web3.eth.accounts[1]) BigNumber { s: 1, e: 19, c: [ 999711, 68300000000000 ] } truffle(develop)> web3.eth.getBalance(web3.eth.accounts[2]) BigNumber { s: 1, e: 19, c: [ 999815, 53800000000000 ] } truffle(develop)> web3.eth.getBalance(web3.eth.accounts[3]) BigNumber { s: 1, e: 19, c: [ 959815, 60200000000000 ] }
truffle(develop)> AuctionStore.deployed().then(function(i){i.highestBidderInfo.call(1).then(function(f){console.log(f)})});
操做 | 帳戶1 | 帳戶2 | 帳戶3 |
---|---|---|---|
初始餘額 | 10 | 10 | 10 |
開始競標 | - | - | - |
實際競標價格 | 2 | 3 | 4 |
對外虛擬價格 | 2.5 | 3.5 | 4.5 |
帳戶餘額 | 9.74888 | 9.64903 | 9.54903 |
帳戶1開始揭標 | - | - | - |
揭標結果 | 最高價(退款爲2.5-2) | - | - |
揭標餘額 | 9.79711 | 9.64903 | 9.54903 |
帳戶2開始揭標 | - | - | - |
揭標結果 | 出局(退款爲實際競標價2) | 最高價(退款爲2.5-2) | - |
揭標餘額 | 9.99711 | 969815 | - |
帳戶3開始揭標 | - | - | - |
揭標結果 | 出局(不變) | 出局(退款爲實際競標價3) | 最高價(退款爲4.5-4) |
揭標餘額 | 9.99711 | 9.99815 | 9.59815 |
因爲時間問題,本文先介紹拍賣網站的智能合約部分,其餘內容會根據後續時間安排考慮再完善,感謝理解與支持!