truffle framework初體驗 - 如何編寫智能合約
使用solidity語言撰寫智能合約
Ethereum
上的智能合約需要使用solidity
語言來撰寫。雖然還有其他能用來撰寫智能合約的語言如Serpent(類Python)
、lll(類Fortran)
,但目前看到所有公開的智能合約都是使用solidity
撰寫。
宣傳上說,solidity
是一種類似Javascript
的語言,而且圍繞着solidity
的各種開發工具鏈
,都是使用屬於Javascript生態系的npm來提供
的。但我覺得solidity
還是比較像Java
或C#
。因爲和Javascript
不同,solidity
與Java
或C#
同屬於強類型(Strong Type,在定義變數時需要指定類型)語言
、在定義函式(function)
時同樣需指定回傳的類型(type)
、同樣也需要先編譯才能執行。這些特性都是Javascript所不具備的。
開發前的準備
使用當前最活躍的智能合約開發框架truffle
爲基礎來開發。ENS(Ethereum Name Service)
也是採用truffle框架
。其他選擇還有embark
等。
就像一般網站或App開發一樣,在提供公開服務之前,開發者會在自己用於寫程序的電腦(又稱作本機)或透過測試網絡來測試程序執行的效果,測試完成後,纔會部署到公開的網絡上提供服務。開發區塊鏈智能合約(程序)的過程也是如此。特別是公開鏈上所有寫入或讀取計算結果的操作都需要真金白銀(虛擬代幣),而且根據網絡狀況,每個公開鏈上的操作都需要要一小段反應時間(15秒~數分鐘),這些等待頗浪費寶貴的開發時間⏳。因此在開發的過程中,我們將使用testrpc
工具在電腦上模擬智能合約所需的以太坊內存塊鏈測試環境。
testrpc
中包含了Javascript
版本的Ethereum
虛擬機(Ethereum Virtual Machine),因此可以完整地執行智能合約。
使用Atom
搭配solidity
插件來開發。
安裝linter-solium、linter-solidity、autocomplete-solidity
插件
安裝所需工具
首先開發機上必須裝好Node.js,再使用以下命令安裝所需的工具:
npm install -g ethereumjs-testrpc truffle
啓動Testrpc
安裝好後隨時可以使用testrpc
命令來啓動以太坊測試環境。
macdeiMac:~ mac$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x04cde7790a0f0516cf12d10ab59337e6c4eb1b36
(1) 0x0ca91acc617b5f7b0c05ee5e60de9ec719968a7b
(2) 0x6a57647fdc0fa5ec7b8cac9d1a72d562c0736081
(3) 0x19f5133c05607fc0827076f0e1b9d08f67d48e68
(4) 0x13ed5b70cdc9d6ba777c700711d499d73c7b6fde
(5) 0x60c1bab11e8423fc2641ee3d18d268f1ab4d1179
(6) 0x2e35a79acbd4f31b87dcf27ff8ada2e0d19614cd
(7) 0x5770edb98d556c22424d05fa6cf08a854f990c1e
(8) 0xb17ac0eaa0d98e77184bf89eb01d94148f4d182f
(9) 0x32fa7b15c0889bc2402e7ec46f28e3fef79b84d6
Private Keys
==================
(0) f201d11edc21e61446de02a70cbf04ebbb72b400332db1d9aa1470589a96f724
(1) 001582693f965c0cd71d02614836e39343a2a4dff4a10294422a2e4b6769de8a
(2) 676eaca6c724178dfce26e5f3839b94377e694d042b3369d190183582c37c076
(3) afea92aa7b2f6a48ff5304dff0cb3a17f96b3dc7c41adf6a851b4985a39918cf
(4) b35b3a6121c78db0277abdcc08fb158e845339dacff959b5f0b9b0afe5edfe07
(5) f057d0aefe5709917d54e7dabc349eceb01f6b6c799bba239752dbe5def82fa0
(6) d28765577a1c951ef7f572ecc24c0c2931ad9045ef3f2205e6f4b582b3df8641
(7) 64c97dde41b22d14650a989f4c5c5e08bcdee52046129a8892875c78f12e9c81
(8) 31121a9faf1672f920ea8d252a168ffb020ff85132c5091e25fd2fbaa721d589
(9) b4951287e2844b10eb28b5e73f46b2e24ca2e49868a93910c6fdfefd40be3c8c
HD Wallet
==================
Mnemonic: hand aim again inmate unable rocket guide cactus trash stairs road lizard
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
可以看到testrpc
啓動後自動建立了10個帳號
(Accounts),與每個帳號對應的私鑰(Private Key
)。每個帳號中都有100個測試用的以太幣(Ether)
。要注意testrpc
僅運行在內存中,因此每次重開時都會回到全新的狀態
。
一切準備就緒,我們可以開始建立第一份智能合約項目了。
建立項目
開啓另一個終端窗口,輸入以下命令以建立項目:
macdeiMac:learn mac$ cd SmartContractDemo/
macdeiMac:SmartContractDemo mac$ ls
macdeiMac:SmartContractDemo mac$ mkdir HelloWorld
macdeiMac:SmartContractDemo mac$ cd HelloWorld/
macdeiMac:HelloWorld mac$ ls
macdeiMac:HelloWorld mac$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
macdeiMac:HelloWorld mac$ ls
contracts test truffle.js
migrations truffle-config.js
macdeiMac:HelloWorld mac$
目錄結構:
/contracts:
存放智能合約原始代碼的地方,可以看到裏面已經有三個sol
文件,我們開發的HelloWorld.sol
文件就存放在這裏。
/migrations:
這是 Truffle
用來部署智能合約的功能,待會兒我們會修改2_deploy_contracts.js
來部署 HelloWorld.sol
。
/test:
測試智能合約的代碼放在這裏,支持js
與 sol
測試。 truffle.js: Truffle
的設置文檔。
新建HelloWorld合約
在contracts
文件夾下新建HelloWorld.sol
文件,當然也可以直接在HelloWorld
路徑下面直接執行truffle create contract HelloWorld
命令來創建HelloWorld.sol
。
編譯
現在執行truffle compile
命令,我們可以將HelloWorld.sol
原始碼編譯成Ethereum bytecode
。
ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:HelloWorld mac$ cd build
macdeiMac:build mac$ ls
contracts
macdeiMac:build mac$ cd contracts
macdeiMac:contracts mac$ ls
HelloWorld.json Migrations.json
macdeiMac:contracts mac$ cat HelloWorld.json
{
"contractName": "HelloWorld",
"abi": [
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
}
],
"bytecode": "0x6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a723058203a07e3fe53400ae73f8a3ac97dbeac2183d363480bb7c2b37c455ccf244e46770029",
"deployedBytecode": "0x6080604052600080fd00a165627a7a723058203a07e3fe53400ae73f8a3ac97dbeac2183d363480bb7c2b37c455ccf244e46770029",
"sourceMap": "26:52:0:-;;;50:26;8:9:-1;5:2;;;30:1;27;20:12;5:2;50:26:0;26:52;;;;;;",
"deployedSourceMap": "26:52:0:-;;;;;",
"source": "pragma solidity ^0.4.22;\n\ncontract HelloWorld {\n constructor() public{\n\n }\n}\n",
"sourcePath": "/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol",
"ast": {
"absolutePath": "/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol",
"exportedSymbols": {
"HelloWorld": [
6
]
},
"id": 7,
"nodeType": "SourceUnit",
"nodes": [
{
"id": 1,
"literals": [
"solidity",
"^",
"0.4",
".22"
],
"nodeType": "PragmaDirective",
"src": "0:24:0"
},
{
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"documentation": null,
"fullyImplemented": true,
"id": 6,
"linearizedBaseContracts": [
6
],
"name": "HelloWorld",
"nodeType": "ContractDefinition",
"nodes": [
{
"body": {
"id": 4,
"nodeType": "Block",
"src": "70:6:0",
"statements": []
},
"documentation": null,
"id": 5,
"implemented": true,
"isConstructor": true,
"isDeclaredConst": false,
"modifiers": [],
"name": "",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 2,
"nodeType": "ParameterList",
"parameters": [],
"src": "61:2:0"
},
"payable": false,
"returnParameters": {
"id": 3,
"nodeType": "ParameterList",
"parameters": [],
"src": "70:0:0"
},
"scope": 6,
"src": "50:26:0",
"stateMutability": "nonpayable",
"superFunction": null,
"visibility": "public"
}
],
"scope": 7,
"src": "26:52:0"
}
],
"src": "0:79:0"
},
"legacyAST": {
"absolutePath": "/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol",
"exportedSymbols": {
"HelloWorld": [
6
]
},
"id": 7,
"nodeType": "SourceUnit",
"nodes": [
{
"id": 1,
"literals": [
"solidity",
"^",
"0.4",
".22"
],
"nodeType": "PragmaDirective",
"src": "0:24:0"
},
{
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"documentation": null,
"fullyImplemented": true,
"id": 6,
"linearizedBaseContracts": [
6
],
"name": "HelloWorld",
"nodeType": "ContractDefinition",
"nodes": [
{
"body": {
"id": 4,
"nodeType": "Block",
"src": "70:6:0",
"statements": []
},
"documentation": null,
"id": 5,
"implemented": true,
"isConstructor": true,
"isDeclaredConst": false,
"modifiers": [],
"name": "",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 2,
"nodeType": "ParameterList",
"parameters": [],
"src": "61:2:0"
},
"payable": false,
"returnParameters": {
"id": 3,
"nodeType": "ParameterList",
"parameters": [],
"src": "70:0:0"
},
"scope": 6,
"src": "50:26:0",
"stateMutability": "nonpayable",
"superFunction": null,
"visibility": "public"
}
],
"scope": 7,
"src": "26:52:0"
}
],
"src": "0:79:0"
},
"compiler": {
"name": "solc",
"version": "0.4.24+commit.e67f0147.Emscripten.clang"
},
"networks": {},
"schemaVersion": "2.0.1",
"updatedAt": "2018-08-29T07:00:56.861Z"
}macdeiMac:contracts mac$
編譯成功後,會在HelloWorld
文件夾下面的build/contracts
文件夾下面看見HelloWorld.json
文件。
部署
truffle
框架中提供了方便部署合約的腳本。打開migrations/2_deploy_contracts.js
文件(腳本使用Javascript
編寫),將內容修改如下:
var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};
修改truffle.js文件
module.exports = {
networks:{
development:{
host:"localhost",
port:8545,
network_id:"*"//匹配任何netword id
}
}
};
用artifacts.require
語句來取得準備部署的合約。使用deployer.deploy
語句將合約部署到區塊鏈上。這邊HelloWorld
是contract
的名稱而不是文件名。因此可以用此語法讀入任一.sol
文件中的任一合約。
現在執行truffle migrate
命令:
macdeiMac:HelloWorld mac$ truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xb3b755e9eb0876125e12f0a11d0fc3be78ff575d96bf34718ef31d4fdd8964cd
Migrations: 0xdaed6c72fcc917add4b4c75b4dbf5aab621c3e16
Saving successful migration to network...
... 0x4df93d62e0cc9c4f7cf81855a4a1b3be0904e542e8113e2c4342f9caf9d76659
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying HelloWorld...
... 0x8221bc0d4417083a04e3173ceed53e19f8e9b8767686be41f3f52edac64c84ed
HelloWorld: 0xa26f21f301fb1630bf607cd3a168c3019c00b0ff
Saving successful migration to network...
... 0x2cac8e09936f8dcc65794a622e47ee8b366ea0509517abed4d64efdf21972179
Saving artifacts...
macdeiMac:HelloWorld mac$
如此一來合約已經部署到testrpc
中。切換到testrpc
窗口,可以看到testrpc
有反應了。
合約互動
truffle
提供命令行工具,執行truffle console
命令後,可用Javascript
來和剛剛部署的合約互動。
macdeiMac:HelloWorld mac$ ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:HelloWorld mac$ truffle console
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
transactionHash: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
legacyAST: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Array],
bytecode: '0x6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a723058203a07e3fe53400ae73f8a3ac97dbeac2183d363480bb7c2b37c455ccf244e46770029',
deployedBytecode: '0x6080604052600080fd00a165627a7a723058203a07e3fe53400ae73f8a3ac97dbeac2183d363480bb7c2b37c455ccf244e46770029',
sourceMap: '26:52:0:-;;;50:26;8:9:-1;5:2;;;30:1;27;20:12;5:2;50:26:0;26:52;;;;;;',
deployedSourceMap: '26:52:0:-;;;;;',
source: 'pragma solidity ^0.4.22;\n\ncontract HelloWorld {\n constructor() public{\n\n }\n}\n',
sourcePath: '/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
legacyAST: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '2.0.1',
updatedAt: '2018-08-29T08:53:01.048Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [Object],
currentProvider: [Object],
eth: [Object],
db: [Object],
shh: [Object],
net: [Object],
personal: [Object],
bzz: [Object],
settings: [Object],
version: [Object],
providers: [Object],
_extend: [Object] },
class_defaults:
{ from: '0x04cde7790a0f0516cf12d10ab59337e6c4eb1b36',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://localhost:8545',
timeout: 0,
user: undefined,
password: undefined,
headers: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1535509790991' },
abi:
[ { inputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [Object],
getBalance: [Object],
getStorageAt: [Object],
getCode: [Object],
getBlock: [Object],
getUncle: [Object],
getCompilers: [Object],
getBlockTransactionCount: [Object],
getBlockUncleCount: [Object],
getTransaction: [Object],
getTransactionFromBlock: [Object],
getTransactionReceipt: [Object],
getTransactionCount: [Object],
call: [Object],
estimateGas: [Object],
sendRawTransaction: [Object],
signTransaction: [Object],
sendTransaction: [Object],
sign: [Object],
compile: [Object],
submitWork: [Object],
getWork: [Object],
coinbase: [Getter],
getCoinbase: [Object],
mining: [Getter],
getMining: [Object],
hashrate: [Getter],
getHashrate: [Object],
syncing: [Getter],
getSyncing: [Object],
gasPrice: [Getter],
getGasPrice: [Object],
accounts: [Getter],
getAccounts: [Object],
blockNumber: [Getter],
getBlockNumber: [Object],
protocolVersion: [Getter],
getProtocolVersion: [Object],
iban: [Object],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0xa26f21f301fb1630bf607cd3a168c3019c00b0ff',
abi: [ [Object] ],
allEvents: [Function: bound ] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0xa26f21f301fb1630bf607cd3a168c3019c00b0ff',
transactionHash: null }
truffle(development)>
講解
HelloWorld.deployed().then(instance => contract = instance)
truffle console
中預載了truffle-contract
函數庫,以方便操作部署到區塊鏈上的合約。
這邊使用HelloWorld.deployed().then
語句來取得HelloWorld
合約的Instance
(實例),並存到contract
變量中,以方便後續的調用。
上面用的是Javascript ES6+
的語法,這句也可以寫成:
HelloWorld.deployed().then(instance => {
contract = instance
});
還可以用ES5的寫法:
HelloWorld.deployed().then(function(instance) {
hello = instance;
});
truffle(development)> contract.sayHello.call()
'Hello World'
這裏直接呼叫contract.sayHello()
也會得到一樣的結果。truffle-contract
提供使用call()
來讀取只讀(read only)
的數據,這樣就不需提供gas
。因此如果遇到的操作需要向區塊鏈寫入數據,我們就不能用call語句了。
如此一來,我們已寫好並部署完成了第一個智能合約,也驗證了合約確實可以運作。
加入新方法
我們在HelloWorld.sol
中再加入一個echo
方法,echo
方法接受輸入一個參數,並回傳傳送的參數。
pragma solidity ^0.4.22;
contract HelloWorld {
constructor() public{
}
function echo(string name) public pure returns (string) {
return name;
}
function sayHello() public pure returns (string) {
return ("Hello World");
}
}
新的echo
方法中傳入了一個name
參數。我們也爲echo
方法加入一個constant
聲明,表示調用這個方法並不會改變區塊鏈的狀態。如此一來,透過truffle-contract
來調用此方法時,會自動選用call
來呼叫,也不需要額外提供gas
。
由於更新了合約內容,我們需要先重新新編譯一次,將編譯結果部署到testrpc
上,再透過truffle console
執行看看結果。
macdeiMac:HelloWorld mac$ ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:HelloWorld mac$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Writing artifacts to ./build/contracts
macdeiMac:HelloWorld mac$ ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:HelloWorld mac$ cd build
macdeiMac:build mac$ ls
contracts
macdeiMac:build mac$ cd contracts
macdeiMac:contracts mac$ ls
HelloWorld.json Migrations.json
macdeiMac:contracts mac$ cat HelloWorld.json
echo
方法確實將我們輸入的內容回傳了。同時因爲聲明瞭constant,我們不需要直接調用call()方法,truffle會自動選用call來呼叫。
另一點需要注意的,是這次如果還是用truffle migrate命令,我們會得到如下信息:
macdeiMac:HelloWorld mac$ ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:HelloWorld mac$ truffle compile
macdeiMac:HelloWorld mac$ truffle migrate --reset
Using network 'development'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x559837b6531e0f2daf5890d6a5ac38c9a1cb1c1c17c0c9c55ce89c68e523447b
Migrations: 0xd4d94b950b29ef3a9d3b97b873dc78ffd7d65dd1
Saving successful migration to network...
... 0xc62e9b40d42ccc3e87b3014337af9466fa2847ffbc930567ab4d877586621c42
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing HelloWorld...
... 0x897dc5127e0b4dc3b20afa451542114faf01e059db4b9984bdd2f0e9b6208c4b
HelloWorld: 0xde18ec26755086b579a0f64f94b2109e0fa91691
Saving successful migration to network...
... 0xa67de22853046112d1a660432cc052601263d527b8c521e6029c67b9aa8c3148
Saving artifacts...
macdeiMac:HelloWorld mac$ truffle console
truffle(development)> let contract
undefined
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
transactionHash: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
legacyAST: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Array],
bytecode: '0x608060405234801561001057600080fd5b50610236806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100e1575b600080fd5b34801561005d57600080fd5b506100666101c3565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610200565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561018857808201518184015260208101905061016d565b50505050905090810190601f1680156101b55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60606040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b60608190509190505600a165627a7a7230582029c5df56a080542733aa0e20d5c7723690ea729ee1914eefdef8db79708b06fe0029',
deployedBytecode: '0x60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100e1575b600080fd5b34801561005d57600080fd5b506100666101c3565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610200565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561018857808201518184015260208101905061016d565b50505050905090810190601f1680156101b55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60606040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b60608190509190505600a165627a7a7230582029c5df56a080542733aa0e20d5c7723690ea729ee1914eefdef8db79708b06fe0029',
sourceMap: '26:220:0:-;;;50:26;8:9:-1;5:2;;;30:1;27;20:12;5:2;50:26:0;26:220;;;;;;',
deployedSourceMap: '26:220:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;162:82;;8:9:-1;5:2;;;30:1;27;20:12;5:2;162:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;162:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;80:78;;8:9:-1;5:2;;;30:1;27;20:12;5:2;80:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;80:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;162:82;203:6;217:22;;;;;;;;;;;;;;;;;;;;162:82;:::o;80:78::-;128:6;149:4;142:11;;80:78;;;:::o',
source: 'pragma solidity ^0.4.22;\n\ncontract HelloWorld {\n constructor() public{\n\n }\n\n function echo(string name) public pure returns (string) {\n return name;\n }\n\n function sayHello() public pure returns (string) {\n return ("Hello World");\n }\n}\n',
sourcePath: '/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
legacyAST: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '2.0.1',
updatedAt: '2018-08-29T09:29:16.049Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [Object],
currentProvider: [Object],
eth: [Object],
db: [Object],
shh: [Object],
net: [Object],
personal: [Object],
bzz: [Object],
settings: [Object],
version: [Object],
providers: [Object],
_extend: [Object] },
class_defaults:
{ from: '0x04cde7790a0f0516cf12d10ab59337e6c4eb1b36',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://localhost:8545',
timeout: 0,
user: undefined,
password: undefined,
headers: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1535509790991' },
abi:
[ { inputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor' },
{ constant: true,
inputs: [Array],
name: 'echo',
outputs: [Array],
payable: false,
stateMutability: 'pure',
type: 'function' },
{ constant: true,
inputs: [],
name: 'sayHello',
outputs: [Array],
payable: false,
stateMutability: 'pure',
type: 'function' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [Object],
getBalance: [Object],
getStorageAt: [Object],
getCode: [Object],
getBlock: [Object],
getUncle: [Object],
getCompilers: [Object],
getBlockTransactionCount: [Object],
getBlockUncleCount: [Object],
getTransaction: [Object],
getTransactionFromBlock: [Object],
getTransactionReceipt: [Object],
getTransactionCount: [Object],
call: [Object],
estimateGas: [Object],
sendRawTransaction: [Object],
signTransaction: [Object],
sendTransaction: [Object],
sign: [Object],
compile: [Object],
submitWork: [Object],
getWork: [Object],
coinbase: [Getter],
getCoinbase: [Object],
mining: [Getter],
getMining: [Object],
hashrate: [Getter],
getHashrate: [Object],
syncing: [Getter],
getSyncing: [Object],
gasPrice: [Getter],
getGasPrice: [Object],
accounts: [Getter],
getAccounts: [Object],
blockNumber: [Getter],
getBlockNumber: [Object],
protocolVersion: [Getter],
getProtocolVersion: [Object],
iban: [Object],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0xde18ec26755086b579a0f64f94b2109e0fa91691',
abi: [ [Object], [Object], [Object] ],
echo:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
string: [Circular] },
sayHello:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'': [Circular] },
allEvents: [Function: bound ] },
echo:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sayHello:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0xde18ec26755086b579a0f64f94b2109e0fa91691',
transactionHash: null }
truffle(development)> contract.sayHello.call()
'Hello World'
truffle(development)> contract.echo("哈哈")
'哈哈'
truffle(development)>
echo
方法確實將我們輸入的內容回傳了。同時因爲聲明瞭constant
,我們不需要直接調用call()
方法,truffle
會自動選用call
來呼叫。
另一點需要注意的,是這次如果還是用truffle migrate
命令,我們會得到如下信息
macdeiMac:HelloWorld mac$ truffle migrate
Using network 'development'.
Network up to date.
macdeiMac:HelloWorld mac$
Truffle
會告訴你現在網絡上的合約都已是最新的,但事實上剛剛程序中新增的方法並沒有更新到內存塊鏈上
。要更新內存塊鏈上已部署的程序,需要改寫migrations
中的腳本,但現在還不到介紹migration
的時候。還好我們開發用的內存塊鏈是怎麼修改都沒關係的testrpc
,可以使用truffle migrate --reset
命令直接在testrpc
上重新部署
一次。