宠物商店(pet-shop) 学习笔记
寵物商店(pet-shop) 學習筆記
truffle 可以算是一個超級強大的 Ethereum 開發工具集, 集各種的功能集一身, 今天, 照著官方的文檔, 和 手把手的教程, 完成了其中提供的一個demo.
truffle的目錄結構
目錄樹
demo ├── build │ └── contracts │ ├── Migrations.json │ └── Adoption.json ├── contracts │ ├── Migrations.sol │ └── Adoption.sol ├── migrations │ └── 1_initial_migration.js ├── truffle-config.js ├── truffle.js ├── test │ └──1_initial_migration.js └── src└── ...- contract 此目錄就是我們的編寫的智能合約所存在的目錄, 使用solidity語言編寫
- migrations 此目錄下是用于遷移部署合約的JS的腳本
- test 測試合約時所用的測試腳本
- src 一個前端的實現, 主要是調用 wed3 的庫, 與節點服務器進行RPC
代碼分析
合約代碼
pragma solidity ^0.4.17;contract Adoption {address[16] public adopters; // 保存領養者的地址/**func: 領養寵物 para: 領養寵物ID */function adopt(uint petId) public returns (uint) { require(petId >= 0 && petId <= 15); // 確保id在數組長度內adopters[petId] = msg.sender; // 保存調用這地址 return petId; //返回當前寵物ID}// 返回領養者function getAdopters() public view returns (address[16]) {return adopters;}}以上是實現寵物領養的合約代碼.
- 指定編譯器版本
定義 Adoption 的合約結構
contract {...}- 定義一個存放地址的定長數組
- 定義合約函數 adopt
- Public類型, 可以被外部訪問, uint參數 為調用時傳入的要領養的寵物的ID, 返回值就是當前領養的寵物id
- require() 用于檢查變量值是否滿足當前條件, 不滿足條件則立即拋出異常, 并且對所有的已做修改進行回滾(revert)
- 使用數組的對應的 ID index 來保存領養者的地址
- 定義合約函數 getAdopters()
- 直接返回 存儲領養者的定長數組
測試代碼
truffle 作為一個集成的環境, 也是很好的提供了合約測試的功能
pragma solidity ^0.4.17;import "truffle/Assert.sol"; // 引入的斷言文件 import "truffle/DeployedAddresses.sol"; // 用來獲取被測試合約的地址 // 上面兩個文件是Truffle框架提供, 本身并沒有import "../contracts/Adoption.sol"; // 被測試合約, 這樣才能調用它//這個合約是用來測試合約的, 每個用例都會被執行, 通過斷言判斷是否有問題contract TestAdoption {Adoption adoption = Adoption(DeployedAddresses.Adoption());// 領養測試用例function testUserCanAdoptPet() public {uint returnedId = adoption.adopt(8);// 這里傳入是8 返回也應該是8uint expected = 8;Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");}// 寵物所有者測試用例function testGetAdopterAddressByPetId() public {// 期望領養者的地址就是本合約地址,因為交易是由測試合約發起交易,address expected = this;address adopter = adoption.adopters(8);// 當前地址和返回地址的判斷, adopters明明是Array啊, 還能這樣?Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");}// 測試所有領養者function testGetAdopterAddressByPetIdInArray() public {// 領養者的地址就是本合約地址address expected = this;address[16] memory adopters = adoption.getAdopters(); // 內存分配,不是storageAssert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");} }// 關于 this的使用, this代表當前合約, 也是ADDRESS代碼分析寫在了注釋里面 , 下面是執行結果
> truffle testUsing network 'development'.Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... Compiling truffle/Assert.sol... Compiling truffle/DeployedAddresses.sol...TestAdoption? testUserCanAdoptPet (113ms)? testGetAdopterAddressByPetId (110ms)? testGetAdopterAddressByPetIdInArray (196ms)3 passing (1s)可見, 通過測試命令, Truffle 對每個函數進行自動的 測試, 對運行結果進行assert(斷言)分析, 如果合約代碼存在問題, 測試過程會把錯誤顯示出. 這里是全部測試通過的情況.
前端應用代碼
奈何 沒接觸過JS 所以看起來有些吃力, 也算是借這個學習一下了!
代碼主體存在于 src/js/app.js , 從功能上講就是對 我們的合約進行調用, 把我們領養的寵物這個信息記錄在區塊里.
InitWeb3
initWeb3: function () {// 是否當前瀏覽器提供web3(如 metaMask)?if (typeof web3 !== 'undefined') {App.web3Provider = web3.currentProvider; //如果是就直接使用當前的} else {// 如果沒有插件提供的web3, 就向本地的節點要一個App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');}web3 = new Web3(App.web3Provider);return App.initContract(); // 調用后續},這個是web3這個JS包的初始化代碼,代碼中優先使用Mist 或 MetaMask提供的web3實例,如果沒有則從本地環境創建一個。
InitContract
initContract: function () {$.getJSON('Adoption.json', function (data) {// 用Adoption.json數據創建一個可交互的TruffleContract合約實例。var AdoptionArtifact = data;App.contracts.Adoption = TruffleContract(AdoptionArtifact);// Set the provider for our contractApp.contracts.Adoption.setProvider(App.web3Provider);// Use our contract to retrieve and mark the adopted petsreturn App.markAdopted(); //進行回調,所以先執行這個});return App.bindEvents();},這里實現了合約的初始化, 這里加載了Adoption.json,保存了Adoption的ABI(接口說明)信息及部署后的網絡(地址)信息,它在編譯合約的時候生成ABI,在部署的時候追加網絡信息.
畫外音: 這里也是展現了NodeJs的一切皆回調的 異步屬性 , 中間部分的代碼就是我們執行GetJson的 回調代碼.(不同于同步(順序)編程)
MarkAdopted
markAdopted: function(adopters, account) { var adoptionInstance; App.contracts.Adoption.deployed().then(function(instance) {adoptionInstance = instance;// 這里部署了合約, 并且保存該合約的實例// 調用合約的getAdopters(), 用call讀取信息不用消耗gas, 返回領養者的arrayreturn adoptionInstance.getAdopters.call(); }).then(function(adopters) {// 得到領養者列表之后, 這里進行遍歷, 如果發現有存在合理的領養者(if), 改變前端的按鈕樣式吧(應該)for (i = 0; i < adopters.length; i++) {if (adopters[i] !== '0x0000000000000000000000000000000000000000') {$('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);}} }).catch(function(err) {console.log(err.message);//這里捕獲異常, 并且log出來 }); },畫外音 : then 也是一種回調用法, 在前一函數執行完之后, 才會進行 then 內的函數, 這樣就確保了數據的完整獲得
這里實現了 合約的部署, 和對已經領養的dog進行?標記, 遍歷數組得到, 這個判斷方式值得學習.
綁定事件
bindEvents: function() {$(document).on('click', '.btn-adopt', App.handleAdopt);},這個地方就是通俗易懂了,把按鈕點擊的事件, 和它的服務函數(handle), 綁定起來.
畫外音: JavaScript 是一種事件驅動的語言, 所以, 這里和QT很相似, 也是事件驅動, 用戶的點擊, 產生事件, 之后調用事件的處理函數,區別于 消息驅動(如MFC)
服務函數
handleAdopt: function(event) {event.preventDefault();var petId = parseInt($(event.target).data('id'));var adoptionInstance;// 獲取用戶賬號.調用Web3web3.eth.getAccounts(function(error, accounts) {// 出常的回調if (error) {console.log(error);}var account = accounts[0];// 創建合約實例 App.contracts.Adoption.deployed().then(function(instance) {adoptionInstance = instance;// 發送交易領養寵物return adoptionInstance.adopt(petId, {from: account}); 執行領養函數}).then(function(result) {return App.markAdopted(); // 標記所領養寵物}).catch(function(err) {console.log(err.message);});});}這里就是, 鼠標點擊事件的服務函數. 當鼠標點擊Button的時候事件觸發, 服務函數被回調.
>
>
>
event.preventDefault()
該方法將通知 Web 瀏覽器不要執行與事件關聯的默認動作(如果存在這樣的動作)。避免服務函數被中斷
parseInt()
該方法將通知 Web 瀏覽器不要執行與事件關聯的默認動作(如果存在這樣的動作)
寫在后面
這次是個簡單的demo的實現和分析,也得上是第一個Dapp的實現
這個Demo 的實現參照博文
一步步教你開發、部署第一個去中心化應用(Dapp) - 寵物商店
也感謝博主的OFS的開源精神!(保留版權聲明)
以后的博文將轉戰自己的博客 鏈接在此~ 也希望大家多多交流~
這里也是自己推的公眾號, 也可以關注一波~
總結
以上是生活随笔為你收集整理的宠物商店(pet-shop) 学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过例子学Solidity[注释翻译]
- 下一篇: 圈钱跑路 发行自己的ERC20 Toke