【本文目標】
經過本文的學習和時間,你將熟悉以太坊開發框架Truffle的配置和運行,並藉助Truffle完成一個智能合約的部署。javascript
【技術收穫】 經過本文的學習,你將掌握如下內容: 1,瞭解TRUFFLE的功能 2,瞭解TRUFFLE的安裝,配置和啓動 3,藉助TRUFFLE完成METACOIN一個智能合約的運行 4,Testrpc,Geth環境的使用html
【實操課程列表】 第一課 如何在WINDOWS環境下搭建以太坊開發環境java
第二課 如何實現以太坊最簡智能合約「Hello World」的運行node
第六課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例)webpack
第七課 技術小白如何在45分鐘內發行通證(TOKEN)並上線交易git
第八課 如何調試以太坊官網的智能合約衆籌案例github
【說明】未列出的課程爲知識普及的非實操類課程,全部區塊鏈文章參考「區塊鏈入口」專欄。web
Truffle是一個世界級的開發環境,測試框架,以太坊的資源管理通道,致力於讓以太坊上的開發變得簡單,Truffle有如下:npm
在Ubuntu命令上窗口輸入如下命令,完成安裝:
$ npm install -g truffle
若是安裝成功,可輸入truffle version名稱,正常狀況下會有版本顯示:
NodeJS 5.0+ Windows,Linux(推薦Ubuntu),或Mac OS X
有許多的以太坊客戶端能夠選擇。咱們推薦在開發和部署時使用不一樣客戶端。 適用開發的客戶端
當開發基於Truffle的應用時,咱們推薦使用EthereumJS TestRPC。它是一個完整的在內存中的區塊鏈僅僅存在於你開發的設備上。它在執行交易時是實時返回,而不等待默認的出塊時間,這樣你能夠快速驗證你新寫的代碼,當出現錯誤時,也能即時反饋給你。它同時仍是一個支持自動化測試的功能強大的客戶端。Truffle充分利用它的特性,能將測試運行時間提速近90%。
適用正式發佈的客戶端
對此有許多官方和非官方的以太坊客戶端可供選擇。最好使用TestRPC客戶端充分測試後,再使用這些客戶端。這些是完整的客戶端實現,包括挖礦,網絡,塊及交易的處理,Truffle能夠在不須要額外配置的狀況下發布到這些客戶端。
當發佈到私有網絡中
私人網絡中使用了相同的技術,但卻有不一樣的配置。因此你能夠將上面說起的客戶端來運行一個私有的網絡,部署到這樣的網絡也是使用一樣的方式。 【說明】做者使用TestRPC和 Geth (go-ethereum)這2種客戶端,他們的安裝方式參考文章:www.jianshu.com/p/683ea7d62…
咱們假設前面的安裝和環境搭建已所有成功,此時應該能夠直接使用命令truffle了,下面咱們創建一個工做間truffle-workspace,而後在工做間執行:
mkdir MetaCoin
cd MetaCoin
truffle unbox metacoin
複製代碼
原來使用truffle init,但如今它存在於unbox。
執行截圖以下:
Truffle 的盒子Boxs裝有不少很是實用的項目樣板,可讓你忽略一些環境配置問題,從而能夠集中與開發你本身的DApp的業務惟一性。除此以外,Truffle Boxes可以容納其餘有用的組件、Solidity合約或者庫,先後端視圖等等。全部這些都是一個完整的實例Dapp程序。均可如下載下來逐一研究,尋找適合本身公司目前業務模型的組件。
能夠看到,如今官方盒子還很少,總共7個,有三個是關於react的,兩個是truffle本身的項目,能夠下載體驗,剩下兩個是咱們比較關心的,一個是metacoin,很是好的入門示例,另外一個是webpack,顧名思義,它是一套比起metacoin更加完整的模板的存在。既然咱們是初學,下面咱們就從metacoin入手學習。
1) tutorialtoken 1] This box has all you need to get started with our Open Zeppelin (TutorialToken) tutorial. 2] truffleframework.com/boxes/tutor…
2)PET-SHOP 1] This box has all you need to get started with our Pet Shop tutorial. 2] truffleframework.com/boxes/pet-s…
3) METACOIN truffle unbox metacoin
4) ENDLESS-NAMELESS-INC/CHESHIRE(加密貓) 1] An Ethereum testnet running the CryptoKitties smart contracts An HTTP server running a minimal implementation of the CryptoKitties web API: A simple Node.js framework for seeding the development environment with realistic data and bootstraping your dApp. 2] truffleframework.com/boxes/chesh…
進入metacoin目錄,當前目錄已經被初始化成一個新的空的以太坊工程,目錄結構以下:
contracts * ConvertLib.sol * MetaCoin.sol * Migrations.sol * .placeholder migrations * 1_initial_migration.js * 2_deploy_contracts.js test * metacoin.js * TestMetacoin.sol * .placeholder
pragma solidity ^0.4.2;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
function Migrations() public {
owner = msg.sender;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
複製代碼
上面咱們學習了Solidity具體的類型語法,咱們來分析一下這個文件:
modifier的使用方法,就看上面的Migrations合約的例子便可,它能夠自動改變函數的行爲,例如你能夠給他預設一個條件,他會不斷檢查,一旦符合條件便可走預設分支。它能夠影響當前合約以及派生合約。
pragma solidity ^0.4.11;
contract owned {
function owned() public { owner = msg.sender; }
address owner;
// 這裏僅定義了一個modifier可是沒有使用,它將被子類使用,方法體在這裏「_;」,這意味着若是owner調用了這個函數,函數會被執行,其餘人調用會拋出一個異常。
modifier onlyOwner {
require(msg.sender == owner);
_;
}
}
// 經過is關鍵字來繼承一個合約類,mortal是owned的子類,也叫派生類。
contract mortal is owned {
// 當前合約派生了owned,此方法使用了父類的onlyOwner的modifier
// public onlyOwner, 這種寫法挺讓人困惑,下面給出了個人思考,暫理解爲派生類要使用基類的modifier。
function close() public onlyOwner {
selfdestruct(owner);
}
}
contract priced {
// Modifiers能夠接收參數
modifier costs(uint price) {
// 這裏modifier方法體是經過條件判斷,是否知足,知足則執行「_;」分支。
if (msg.value >= price) {
_;
}
}
}
contract Register is priced, owned {
mapping (address => bool) registeredAddresses;
uint price;
// 構造函數給全局變量price賦值。
function Register(uint initialPrice) public { price = initialPrice; }
// payable關鍵字重申,若是不聲明的話,函數關於以太幣交易的操做都會被拒回。
function register() public payable costs(price) {
registeredAddresses[msg.sender] = true;
}
// 此派生類也要使用基類的modifier。
function changePrice(uint _price) public onlyOwner {
price = _price;
}
}
contract Mutex {
bool locked;
modifier noReentrancy() {
require(!locked);
locked = true;
_;
locked = false;
}
function f() public noReentrancy returns (uint) {
require(msg.sender.call());
return 7;
}
}
複製代碼
又延伸出來一個盲點:require關鍵字,它是錯誤判斷,提到assert就懂了,官方文檔的解釋爲:
require(bool condition):
throws if the condition is not met - to be used for errors in inputs or external components.
複製代碼
總結一下modifier:
限制訪問一種針對合約的常見模式。但其實你永遠不可能限制得了任何人或電腦讀取你的交易內容或者你的合同狀態。你可使用加密增大困難,但你的合約就是用來讀取數據的,那麼其餘人也會看到。因此,其實上面的modifier onlyOwner是一個特別好的可讀性極高的限制訪問的手段。
那麼restricted關鍵字如何使用呢?
好吧,我剛剛帶着modifier的知識從新看了上面的Migrations合約的內容發現,restricted並非關鍵字,而是modifier的方法名,在其下的想增長該modifier功能的函數中,都使用了public restricted的方式來聲明。
說到這裏,我又明白了爲何要使用public onlyOwner這種寫法,由於public是函數可見性修飾符,onlyOwner是自定義的限制訪問的modifier方法,他們都是關於函數使用限制方面的,因此會寫在一塊兒,能夠假想一個括號將它倆括起來,他們佔一個位置,就是原來屬於public|private|internal|external的那個位置。
這一點很重要了,咱們研究一下Solidity自身攜帶的特殊變量以及函數:
msg有兩個屬性,一個是msg.sender,另外一個是msg.value,這兩個值能夠被任何external函數調用,包含庫裏面的函數。
注意謹慎使用block.timestamp, now and block.blockhash,由於他們都是有可能被篡改的。
pragma solidity ^0.4.18;
import "./ConvertLib.sol";
// 這是一個簡單的仿幣合約的例子。它並非標準的可兼容其餘幣或token的合約,
// 若是你想建立一個標準兼容的token,請轉到 https://github.com/ConsenSys/Tokens(TODO:一下子咱們再過去轉)
contract MetaCoin {
mapping (address => uint) balances;// 定義了一個映射類型變量balances,key爲address類型,值爲無符整型,應該是用來存儲每一個帳戶的餘額,能夠存多個。
event Transfer(address indexed _from, address indexed _to, uint256 _value);// Solidity語法event,TODO:見下方詳解。
function MetaCoin() public {// 構造函數,tx.origin查查上面,找到它會返回交易發送方的地址,也就是說合約實例建立時會默認爲當前交易發送方的餘額塞10000,單位應該是你的仿幣。
balances[tx.origin] = 10000;
}
function sendCoin(address receiver, uint amount) public returns(bool sufficient) {// 函數聲明部分沒有盲點,方法名,參數列表,函數可見性,返回值類型定義。
if (balances[msg.sender] < amount) return false;// 若是餘額不足,則返回發送幣失敗
balances[msg.sender] -= amount;// 不然從發送方餘額中減去發送值,注意Solidity也有 「-=」,「+=」 的運算符哦
balances[receiver] += amount;// 而後在接收方的餘額中加入發送值數量。
Transfer(msg.sender, receiver, amount);// 使用以上event關鍵字聲明的方法
return true;
}
function getBalanceInEth(address addr) public view returns(uint){// 獲取以太幣餘額
return ConvertLib.convert(getBalance(addr),2);// 調用了其餘合約的方法,TODO:稍後介紹ConvertLib合約時說明。
}
function getBalance(address addr) public view returns(uint) {// 獲取當前帳戶的仿幣餘額
return balances[addr];
}
}
複製代碼
Events allow the convenient usage of the EVM logging facilities, which in turn can be used to 「call」 JavaScript callbacks in the user interface of a dapp, which listen for these events. Events提供了日誌支持,進而可用於在用戶界面上「調用」dapp JavaScript回調,監聽了這些事件。簡單來講,咱們的DApp是基於web服務器上的web3.js與EVM以太坊結點進行交互的,而智能合約是部署在EVM以太坊結點上的。舉一個例子:
contract ExampleContract {
// some state variables ...
function foo(int256 _value) returns (int256) {
// manipulate state ...
return _value;
}
}
複製代碼
合約ExampleContract有個方法foo被部署在EVM的一個結點上運行了,此時用戶若是想在DApp上調用合約內部的這個foo方法,如何操做呢,有兩種辦法:
第一種辦法在方法自己比較耗時的狀況下會阻塞,或者不會獲取到準確的返回值。因此採用第二種辦法:就是經過Solidity的關鍵字event。event在這裏就是一個回調函數的概念,當函數運行結束之後(交易進塊),會經過event返回給web3,也就是DApp用戶界面相應的結果。這是以太坊一種客戶端異步調用方法。關於這個回調,要在DApp使用web3時顯示編寫:
exampleEvent.watch(function(err, result) {
if (err) {
console.log(err)
return;
}
console.log(result.args._value)
// 檢查合約方法是否反返回結果,如有則將結果顯示在用戶界面而且調用exampleEvent.stopWatching()方法中止異步回調監聽。
})
複製代碼
寫Solidity最大的不一樣在於,咱們要隨時計算好咱們的gas消耗,方法的複雜度,變量類型的存儲位置(memory,storage等等)都會決定gas的消耗量。
使用event能夠得到比storage更便宜的gas消耗。
總結一下event,就是若是你的Dapp客戶端web3.js想調用智能合約內部的函數,則使用event做爲橋樑,它能方便執行異步調用同時又節約gas消耗。
pragma solidity ^0.4.4;
library ConvertLib{
function convert(uint amount,uint conversionRate) public pure returns (uint convertedAmount)
{
return amount * conversionRate;
}
}
複製代碼
與MetaCoin智能合約不一樣的是,ConvertLib是由library聲明的一個庫,它只有一個方法,就是返回給定的兩個無符整數值相乘的結果。返回到上面的MetaCoin中該庫的使用位置去分析,便可知道,MetaCoin的仿幣的價格是以太幣的一倍,因此MetaCoin是以以太幣爲標杆,經過智能合約發佈的一個token,仿幣。
這彷佛就能夠很好地解決我在《以太坊RPC機制與API實例》文章中須要發佈三倍以太幣的token的需求了,而咱們徹底沒必要更改以太坊源碼,但那篇文章經過這個需求的路線研究了以太坊的Go源碼也算功不可沒。
var Migrations = artifacts.require("./Migrations.sol");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};
複製代碼
這個js文件是nodejs的寫法,看上去它的做用就是部署了上面的Migrations智能合約文件。
var ConvertLib = artifacts.require("./ConvertLib.sol");
var MetaCoin = artifacts.require("./MetaCoin.sol");
module.exports = function(deployer) {
deployer.deploy(ConvertLib);
deployer.link(ConvertLib, MetaCoin);
deployer.deploy(MetaCoin);
};
複製代碼
這個文件是meatcoin智能合約的部署文件,裏面約定了部署順序,依賴關係。這裏咱們看到了MetaCoin智能合約是要依賴於庫ConvertLib的,因此要先部署ConvertLib,而後link他們,再部署MetaCoin,這部分js的寫法能夠參照官方文檔DEPLOYER API,主要就是介紹了一下deploy、link以及then三個方法的詳細用法,不難這裏再也不贅述。
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
};
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
};
複製代碼
這兩個文件也都是nodejs,他們都是配置文件,可能做用域不一樣,目前它倆是徹底相同的(由於啥也沒有)。咱們去它推薦的網站看一看。給出了一個例子:
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
複製代碼
這個例子展現了該配置文件能夠配置網絡環境,暫先到這,之後趕上了針對該配置文件進行研究。
This is a placeholder file to ensure the parent directory in the git repository. Feel free to remove.
翻譯過來就是:placeholder文件是用來保證在git庫中父級目錄的,能夠刪除。
和下面的文件同樣,他們的功能都是用來作單元測試的,truffle在編譯期間會自動執行這些測試腳本。當前文件爲js版本,模擬用戶在DApp客戶端用戶界面操做的情形。
var MetaCoin = artifacts.require("./MetaCoin.sol"); // 這與1_initial_migration.js文件的頭是同樣的,引入了一個智能合約文件。
contract('MetaCoin', function(accounts) {
it("should put 10000 MetaCoin in the first account", function() {
return MetaCoin.deployed().then(function(instance) {
return instance.getBalance.call(accounts[0]);
}).then(function(balance) {
assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
});
});
it("should call a function that depends on a linked library", function() {
var meta;
var metaCoinBalance;
var metaCoinEthBalance;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(accounts[0]);
}).then(function(outCoinBalance) {
metaCoinBalance = outCoinBalance.toNumber();
return meta.getBalanceInEth.call(accounts[0]);
}).then(function(outCoinBalanceEth) {
metaCoinEthBalance = outCoinBalanceEth.toNumber();
}).then(function() {
assert.equal(metaCoinEthBalance, 2 * metaCoinBalance, "Library function returned unexpected function, linkage may be broken");
});
});
it("should send coin correctly", function() {
var meta;
// Get initial balances of first and second account.
var account_one = accounts[0];
var account_two = accounts[1];
var account_one_starting_balance;
var account_two_starting_balance;
var account_one_ending_balance;
var account_two_ending_balance;
var amount = 10;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_starting_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_starting_balance = balance.toNumber();
return meta.sendCoin(account_two, amount, {from: account_one});
}).then(function() {
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_ending_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_ending_balance = balance.toNumber();
assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "Amount wasn't correctly taken from the sender");
assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "Amount wasn't correctly sent to the receiver");
});
});
});
複製代碼
咱們來分析一波這個truffle metacoin js版本的單元測試:
這是官方文檔,詳細說明如何使用JS來編寫智能合約的單元測試。
好下面來看看Solidity智能合約版本的單元測試。通常來說,這種文件的命名規則是Test加待測智能合約的名字拼串組成。
pragma solidity ^0.4.2;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/MetaCoin.sol";
contract TestMetacoin {
function testInitialBalanceUsingDeployedContract() public {
MetaCoin meta = MetaCoin(DeployedAddresses.MetaCoin());
uint expected = 10000;
Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
function testInitialBalanceWithNewMetaCoin() public {
MetaCoin meta = new MetaCoin();
uint expected = 10000;
Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
}
複製代碼
繼續分析:
這是官方文檔,詳細說明如何使用Solidity來編寫智能合約的單元測試。
鍵入
truffle compile
輸出狀況:
根據編譯輸出的路徑地址./build/contracts,咱們去查看一下
能夠看到原來所在在contracts目錄下的智能合約文件(有合約contract,有庫library)均被編譯成了json文件。
這些json文件就是truffle用來部署合約的編譯文件。 ##2.4 配置以太坊本地環境 truffle.js是truffle的配置文件,啓動好以太坊本地結點之後,咱們須要讓truffle去識別它並使用它,這就須要在truffle.js中配置相關屬性:
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
複製代碼
【說明】若是不啓動TestRPC,直接執行部署合約的話,會有如下錯誤提示:
啓動以前安裝好的EthereumJS RPC客戶端。
testrpc
【說明】必定要啓動一個新的客戶端執行testrpc命令,能夠觀察到默認帳戶和私鑰信息。
移植(migrate),對這裏叫移植,但下面咱們仍使用「部署」這個詞,truffle中部署的命令爲:
truffle migrate
輸出結果截圖以下:
查看testrpc的輸出窗口,能夠看到這筆交易和花費的區塊:
咱們知道在執行編譯時會自動執行這些單元測試,若是有一個測試未經過則會中斷編譯過程。而在開發階段,咱們也能夠本身使用命令來測試。
truffle test
沒有報錯就說明經過了,綠條「5 passing(2s)」,有報錯就會打印在下方。
返回父級目錄,建立一個文件夾HelloWorld,來作爲你的工程根目錄。
mkdir HelloWorld
輸入結果:
在工做目錄HelloWorld目錄下,執行truffle初始化動做:
truffle init
輸出截圖:
│ truffle-config.js
│ truffle.js
│
├─contracts
│ Migrations.sol
│
├─migrations
│ 1_initial_migration.js
│
└─test
複製代碼
目錄結構簡單說明以下:
contract/ - Truffle默認的合約文件存放地址。 migrations/ - 存放發佈腳本文件 test/ - 用來測試應用和合約的測試文件 truffle.js - Truffle的配置文件
在./contract目錄下建立一個本身的合約文件Greeter.sol。
pragma solidity ^0.4.17;
contract Greeter
{
address creator;
string greeting;
function Greeter(string _greeting) public
{
creator = msg.sender;
greeting = _greeting;
}
function greet() public constant returns (string)
{
return greeting;
}
function setGreeting(string _newgreeting) public
{
greeting = _newgreeting;
}
/**********
Standard kill() function to recover funds
**********/
function kill()public
{
if (msg.sender == creator)
suicide(creator); // kills this contract and sends remaining funds back to creator
}
}
複製代碼
在./migrations/目錄下新建一個文件:2_deploy_contracts.js,增長髮布代碼。
var Greeter = artifacts.require("./Greeter.sol");
module.exports = function(deployer) {
deployer.deploy(Greeter,"Hello, World!");//"參數在第二個變量攜帶"
};
複製代碼
進入到工程根目錄./HelloWorld目錄下,進行編譯:
truffle compile
輸出截圖以下:
若是以前沒有啓動RPC客戶端的話,則須要啓動以前安裝好的EthereumJS RPC客戶端。若是已啓動的則忽略此步。
$ testrpc
執行部署命令(truffle migrate)提示出錯。
truffle migrate
錯誤截圖輸出:
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*" // 匹配任何network id
}
}
};
複製代碼
從新執行編譯命令,從新執行部署命令(truffle migrate),則運行正確。對應Greeter的智能合約地址爲「0x7d62724f397a99613b84923a1166d683de2db680」
Truffle提供了一種更加簡單的方式,經過交互式控制檯來與你的那些準備好的合約進行交互。 truffle console 一個基本的交互控制檯,能夠鏈接任何EVM客戶端。若是你已經有了本身的ganache或者geth等EVM的本地環境,那麼就可使用truffle console來交互,因此若是你已經有一個現成的小組共享的開發用EVM,那麼使用這個沒錯。 truffle develop 一個交互控制檯,啓動時會自動生成一個開發用區塊鏈環境(其實我認爲它與ganache就是一個底層實現機制,都是默認生成10個帳戶)。若是你沒有本身的EVM環境的話,直接使用truffle develop很是方便。
truffle console
輸入Greeter智能合約命令,顯示打印出一個json結構,展現了它的各類屬性內容。
###啓動GETH環境 本節假設GETH環境已安裝好了。若是尚未安裝的同窗,可參考文章《第一課 如何在WINDOWS環境下搭建以太坊開發環境》(www.jianshu.com/p/683ea7d62…
而後在IDE內部打開一個terminal,啓動GETH的EVM環境。
geth --datadir testNet3 --dev --rpc console
GETH 中是經過abi來註冊合約對象的。 首先咱們找到./build/contracts/Greeter.json中的abi的value:
"abi": [
{
"inputs": [
{
"name": "_greeting",
"type": "string"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [],
"name": "greet",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_newgreeting",
"type": "string"
}
],
"name": "setGreeting",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "kill",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
],
複製代碼
經過json壓縮成一行獲得 var abi = [{"inputs": [{"name": "_greeting","type": "string"}],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"constant": true,"inputs": [],"name": "greet","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_newgreeting","type": "string"}],"name": "setGreeting","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": false,"inputs": [],"name": "kill","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"}];
啓動一個新的命令窗口,到
cd /usr/work/HelloWorld truffle migrate
成功部署輸出截圖:
var abi = [{"inputs": [{"name": "_greeting","type": "string"}],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"constant": true,"inputs": [],"name": "greet","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_newgreeting","type": "string"}],"name": "setGreeting","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": false,"inputs": [],"name": "kill","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"}];
var HelloWorld = eth.contract(abi).at('0xb52bb3ce336f71a14345c78e5b2f8e63685e3f92')
HelloWorld.greet()
複製代碼
輸出截圖顯示成功:
本文站在巨人的肩膀上,完成了以太坊開發框架Truffle從入門到實戰的演示。對巨人的文章表示感謝: 1,Solidity的Truffle框架實戰(手把手) 2, 【精解】開發一個智能合約 3,官網參考: truffleframework.com/docs/ 4, 官網GITHUB的代碼: github.com/trufflesuit…
知識對接服務: 輝哥和歐陽哥哥在知識星球開通了區塊鏈入門專欄,用於存放簡書區塊鏈入門專欄文章的工程源碼等內容,並創建專項微信羣用於技術交流,歡迎加入。