閱讀459 返回首頁    go 阿裏雲 go 技術社區[雲棲]


手把手教你搭建智能合約測試環境、開發、編譯、部署以及如何通過JS調用合約方法

如何編寫智能合約(Smart Contract)?(I)

完整視頻教程

學習目標

  1. 了解智能合約
  2. 簡單環境搭建
  3. 能夠利用solidity編寫Hello World合約
  4. 合約部署
  5. 和合約互動

使用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插件來開發。solidity插件除了支持語法高亮之外,也會透過Solium檢查並提示基本的語法錯誤,相當方便。其他編輯器應該也有類似的插件可選擇。



安裝所需工具

首先開發機上必須裝好Node.js,再使用以下命令安裝所需的工具:

$ npm install -g ethereumjs-testrpc truffle
liyuechun:~ yuechunli$ npm install -g ethereumjs-testrpc truffle
/usr/local/bin/testrpc -> /usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js
/usr/local/bin/truffle -> /usr/local/lib/node_modules/truffle/build/cli.bundled.js
+ truffle@3.4.9
+ ethereumjs-testrpc@4.1.3
added 1 package and updated 7 packages in 76.132s
liyuechun:~ yuechunli$ 

啟動Testrpc

安裝好後隨時可以使用testrpc命令來啟動以太坊測試環境。

liyuechun:~ yuechunli$ testrpc
EthereumJS TestRPC v4.1.3 (ganache-core: 1.1.3)

Available Accounts
==================
(0) 0xbbd414b340f2255dab9d923428c97f0b65d9df81
(1) 0xe9869e3cf29b6fca81762c314df229c7c4fea25e
(2) 0xc79e72362a4511b9e499d186654332c4d6f569be
(3) 0x9a6f0651907c149d4173c03927144dbbba1473d4
(4) 0x5b13a5d6788752b26dd4e338aae2e01058ee145e
(5) 0xfc7f56d942ad5260be23ecee92a344aba1b7e7d8
(6) 0xc48dc22c6bacd6ade4421ab54f25bc45c1c51142
(7) 0x3fe2b7d4141dd0a456661f77086d055cbaf3b78f
(8) 0x567979fed26ca85e9d1b4ac919c840e3fc9857e2
(9) 0xb2eafe245f098eef1c2c1f466d9a8dcd58764c62

Private Keys
==================
(0) 947ab78e91133103612ca099d60e6c38cac5bb769f7f097c82d003cf058500bd
(1) 8ffe0ba8dc53e16944a17dddd3378b5fba0379cd84df4e5237b8b46d05b8762f
(2) ffe2e04e43e4106b247407656f5233bcc3e0c49730972d0df9c1d1093375e2ef
(3) a20e453dc44c76aaca6a22efdbb605c2ed9eea64c11317e683461e11bd105ea7
(4) 4748268ff1b828868dc56d07a1b121b427e1bdede5dbb3c14ef1254d9d26b1a5
(5) f9957e68c6d20d38b81604a0509e6c4591478bc754f87d5682564073705fbb46
(6) 34e648b23c0ace6b2b0893651d87f70be8496f97ecf6b7b4607b2acc4e05c9bd
(7) d2477cedec217e3fb19a5981dafbc125ef66ccc9dc7df29301d08a24da843cf5
(8) d319f85ccd80e55b2e707e05f09662632564c297248f8b96f82ea5eeaeef0851
(9) 88c33ac9f1062b82f9e82f86a0ce307e3bd8fcf683b9751232c2f193f5bdc668

HD Wallet
==================
Mnemonic:      hire custom clinic expect fury fantasy try dress source spy viable flag
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

可以看到testrpc啟動後自動建立了10個帳號(Accounts),與每個帳號對應的私鑰(Private Key)。每個帳號中都有100個測試用的以太幣(Ether)。要注意testrpc僅運行在內存中,因此每次重開時都會回到全新的狀態。

一切準備就緒,我們可以開始建立第一份智能合約項目了。

建立項目

開啟另一個終端窗口,輸入以下命令以建立項目:

liyuechun:Desktop yuechunli$ mkdir SmartContractDemo
liyuechun:Desktop yuechunli$ cd SmartContractDemo/
liyuechun:SmartContractDemo yuechunli$ mkdir HelloWorld
liyuechun:SmartContractDemo yuechunli$ cd HelloWorld/
liyuechun:HelloWorld yuechunli$ truffle init

Downloading project...
Project initialized.

  Documentation: https://truffleframework.com/docs

Commands:

  Compile: truffle compile
  Migrate: truffle migrate
  Test:    truffle test

liyuechun:HelloWorld yuechunli$ ls
contracts   migrations  test        truffle.js

目錄結構:

/contracts:存放智能合約原始代碼的地方,可以看到裏麵已經有三個sol文件,我們開發的HelloWorld.sol文件就存放在這裏。

/migrations:這是 Truffle用來部署智能合約的功能,待會兒我們會修改2_deploy_contracts.js來部署 HelloWorld.sol
/test:測試智能合約的代碼放在這裏,支持jssol 測試。
truffle.js: Truffle 的設置文檔。

新建HelloWorld合約

contracts文件夾下新建HelloWorld.sol文件,當然也可以直接在HelloWorld路徑下麵直接執行truffle create contract HelloWorld命令來創建HelloWorld.sol

HelloWorld.sol文件內容如下:

pragma solidity ^0.4.4;

contract HelloWorld {
  function sayHello() returns (string) {
    return ("Hello World");
  }
}

講解

pragma solidity ^0.4.4;

第一行指名目前使用的solidity版本,不同版本的solidity可能會編譯出不同的bytecode^代表兼容solidity0.4.4 ~ 0.4.9的版本。

contract HelloWorld {
    ...
}

contract關鍵字類似於其他語言中較常見的class。因為solidity是專為智能合約(Contact)設計的語言,聲明contract後即內置了開發智能合約所需的功能。也可以把這句理解為class HelloWorld extends Contract

function sayHello() returns (string) {
    return ("Hello World");
}

函數的結構與其他程序類似,但如果有傳入的參數或回傳值,需要指定參數或回傳值的類型(type)。

編譯

現在執行truffle compile命令,我們可以將HelloWorld.sol原始碼編譯成Ethereum bytecode

liyuechun:HelloWorld yuechunli$ ls
contracts   migrations  test        truffle.js
liyuechun:HelloWorld yuechunli$ truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

liyuechun:HelloWorld yuechunli$ ls
build       contracts   migrations  test        truffle.js
liyuechun:HelloWorld yuechunli$ cd build/
liyuechun:build yuechunli$ ls
contracts
liyuechun:build yuechunli$ cd contracts/
liyuechun:contracts yuechunli$ ls
ConvertLib.json HelloWorld.json MetaCoin.json   Migrations.json
liyuechun:contracts yuechunli$ cat HelloWorld.json 
{
  "contract_name": "HelloWorld",
  "abi": [
    {
      "inputs": [],
      "payable": false,
      "type": "constructor"
    }
  ],
  "unlinked_binary": "0x60606040523415600e57600080fd5b5b5b5b603680601e6000396000f30060606040525b600080fd00a165627a7a723058203ee98a767948e9bc08094df4a46ab0361f068b2a559032cf968df5bbf63e91430029",
  "networks": {},
  "schema_version": "0.0.5",
  "updated_at": 1505805826302
}
liyuechun:contracts yuechunli$ 

編譯成功後,會在HelloWorld文件夾下麵的build/contracts文件夾下麵看見HelloWorld.json文件。

部署

truffle框架中提供了方便部署合約的腳本。打開migrations/2_deploy_contracts.js文件(腳本使用Javascript編寫),將內容修改如下:

var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
  deployer.deploy(HelloWorld);
};

使用artifacts.require語句來取得準備部署的合約。使用deployer.deploy語句將合約部署到區塊鏈上。這邊HelloWorldcontract的名稱而不是文件名。因此可以用此語法讀入任一.sol文件中的任一合約。

現在執行truffle migrate命令:

liyuechun:HelloWorld yuechunli$ ls
build       contracts   migrations  test        truffle.js
liyuechun:HelloWorld yuechunli$ truffle migrate
Compiling ./contracts/HelloWorld.sol...
Writing artifacts to ./build/contracts

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x218431f16a5cadc6347449808d981887c90b3872898af7cc9dc9b3280c07c184
  Migrations: 0x64e9673cf962d21642a08635e6654fb7f2ea9bcd
Saving successful migration to network...
  ... 0xd9ec788c106df36b8491c95a0ab02ff1e5ef22c1965c910a2576e8259a00535c
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying HelloWorld...
  ... 0x17774b4914d7bc7ab2505a53c59bda6a1fce30c9839d19d735290ca9140450ea
  HelloWorld: 0x471a22ffe2bddd02e82853059871067e4c07a7f4
Saving successful migration to network...
  ... 0xe5e2e11cf5a63ca4517221c68dadb3cae2ca42cbfed93c09c575b6d5f275fc8b
Saving artifacts...
liyuechun:HelloWorld yuechunli$ 

如此一來合約已經部署到testrpc中。切換到testrpc窗口,可以看到testrpc有反應了。

與合約互動

truffle提供命令行工具,執行truffle console命令後,可用Javascript來和剛剛部署的合約互動。

liyuechun:HelloWorld yuechunli$ ls
build       contracts   migrations  test        truffle.js
liyuechun:HelloWorld yuechunli$ 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],
        abi: [Object],
        network: [Function: network],
        networks: [Function: networks],
        address: [Object],
        links: [Function: links],
        events: [Function: events],
        binary: [Function: binary],
        unlinked_binary: [Object],
        schema_version: [Function: schema_version],
        updated_at: [Function: updated_at] },
     _property_values: {},
     _json: 
      { contract_name: 'HelloWorld',
        default_network: undefined,
        abi: [Array],
        unlinked_binary: '0x6060604052341561000f57600080fd5b5b6101488061001f6000396000f300606060405263ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663ef5fb05b811461003d575b600080fd5b341561004857600080fd5b6100506100c8565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561008d5780820151818401525b602001610074565b50505050905090810190601f1680156100ba5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d061010a565b60408051908101604052600b81527f48656c6c6f20576f726c64000000000000000000000000000000000000000000602082015290505b90565b602060405190810160405260008152905600a165627a7a723058202b9d4dd8e7739264271524ea58db573fa09a0b634d1d5b78502e6dd01d76ba330029',
        networks: [Object],
        schema_version: '0.0.5',
        updated_at: 1505806918535 },
     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: '0xbbd414b340f2255dab9d923428c97f0b65d9df81',
        gas: 4712388,
        gasPrice: 100000000000 },
     currentProvider: 
      HttpProvider {
        host: 'https://localhost:8545',
        timeout: 0,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     network_id: '1505794143155' },
  abi: 
   [ { constant: false,
       inputs: [],
       name: 'sayHello',
       outputs: [Array],
       payable: false,
       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: '0x471a22ffe2bddd02e82853059871067e4c07a7f4',
     abi: [ [Object] ],
     sayHello: 
      { [Function: bound ]
        request: [Function: bound ],
        call: [Function: bound ],
        sendTransaction: [Function: bound ],
        estimateGas: [Function: bound ],
        getData: [Function: bound ],
        '': [Circular] },
     allEvents: [Function: bound ] },
  sayHello: 
   { [Function]
     call: [Function],
     sendTransaction: [Function],
     request: [Function: bound ],
     estimateGas: [Function] },
  sendTransaction: [Function],
  send: [Function],
  allEvents: [Function: bound ],
  address: '0x471a22ffe2bddd02e82853059871067e4c07a7f4',
  transactionHash: null }
truffle(development)> contract.sayHello.call()
'Hello World'
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方法接受輸入一個參數,並回傳傳送的參數。

function echo(string name) constant returns (string) {
    return name;
}

新的echo方法中傳入了一個name參數。我們也為echo方法加入一個constant聲明,表示調用這個方法並不會改變區塊鏈的狀態。如此一來,透過truffle-contract來調用此方法時,會自動選用call來唿叫,也不需要額外提供gas。

由於更新了合約內容,我們需要先重新新編譯一次,將編譯結果部署到testrpc上,再透過truffle console執行看看結果。

liyuechun:HelloWorld yuechunli$ ls
build       contracts   migrations  test        truffle.js
liyuechun:HelloWorld yuechunli$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Writing artifacts to ./build/contracts

liyuechun:HelloWorld yuechunli$ truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0x64cdc42e08a7e3f8070c46d4877ba246d95cbbccbfe1b9abd2450cfc02b48eda
  Migrations: 0x42843f6a470b84e2669f19686a223c1bdefb6f4d
Saving successful migration to network...
  ... 0x57042b767c0f40a4f88ce855e39549010d6d5ae5f880771a45c1f7f36ea0e5b3
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Replacing HelloWorld...
  ... 0x2330e3264aae9d6be3744d8fc71d235fc6dc2934d33ff5159ae209df4cf8f12b
  HelloWorld: 0xbf68789cdd6be1577339e8c739abfa1190c31b6c
Saving successful migration to network...
  ... 0xb4b706d7004654215067ea5954a32f0562b32724d1c646dc43b688b466b10159
Saving artifacts...
liyuechun:HelloWorld yuechunli$ 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],
        abi: [Object],
        network: [Function: network],
        networks: [Function: networks],
        address: [Object],
        links: [Function: links],
        events: [Function: events],
        binary: [Function: binary],
        unlinked_binary: [Object],
        schema_version: [Function: schema_version],
        updated_at: [Function: updated_at] },
     _property_values: {},
     _json: 
      { contract_name: 'HelloWorld',
        default_network: undefined,
        abi: [Array],
        unlinked_binary: '0x6060604052341561000f57600080fd5b5b61022c8061001f6000396000f300606060405263ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663ef5fb05b8114610048578063f15da729146100d3575b600080fd5b341561005357600080fd5b61005b61019c565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100985780820151818401525b60200161007f565b50505050905090810190601f1680156100c55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100de57600080fd5b61005b60046024813581810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506101de95505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100985780820151818401525b60200161007f565b50505050905090810190601f1680156100c55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101a46101ee565b60408051908101604052600b81527f48656c6c6f20576f726c64000000000000000000000000000000000000000000602082015290505b90565b6101e66101ee565b50805b919050565b602060405190810160405260008152905600a165627a7a723058208fec8695bd430eed53d1591d841c6e80b1a32a91caab996bb270d54425ebd7140029',
        networks: [Object],
        schema_version: '0.0.5',
        updated_at: 1505809278810 },
     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: '0xbbd414b340f2255dab9d923428c97f0b65d9df81',
        gas: 4712388,
        gasPrice: 100000000000 },
     currentProvider: 
      HttpProvider {
        host: 'https://localhost:8545',
        timeout: 0,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     network_id: '1505794143155' },
  abi: 
   [ { constant: false,
       inputs: [],
       name: 'sayHello',
       outputs: [Array],
       payable: false,
       type: 'function' },
     { constant: true,
       inputs: [Array],
       name: 'echo',
       outputs: [Array],
       payable: false,
       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: '0xbf68789cdd6be1577339e8c739abfa1190c31b6c',
     abi: [ [Object], [Object] ],
     sayHello: 
      { [Function: bound ]
        request: [Function: bound ],
        call: [Function: bound ],
        sendTransaction: [Function: bound ],
        estimateGas: [Function: bound ],
        getData: [Function: bound ],
        '': [Circular] },
     echo: 
      { [Function: bound ]
        request: [Function: bound ],
        call: [Function: bound ],
        sendTransaction: [Function: bound ],
        estimateGas: [Function: bound ],
        getData: [Function: bound ],
        string: [Circular] },
     allEvents: [Function: bound ] },
  sayHello: 
   { [Function]
     call: [Function],
     sendTransaction: [Function],
     request: [Function: bound ],
     estimateGas: [Function] },
  echo: 
   { [Function]
     call: [Function],
     sendTransaction: [Function],
     request: [Function: bound ],
     estimateGas: [Function] },
  sendTransaction: [Function],
  send: [Function],
  allEvents: [Function: bound ],
  address: '0xbf68789cdd6be1577339e8c739abfa1190c31b6c',
  transactionHash: null }
truffle(development)> contract.echo("春哥微信:liyc1215")
'春哥微信:liyc1215'
truffle(development)> 

echo方法確實將我們輸入的內容回傳了。同時因為聲明了constant,我們不需要直接調用call()方法,truffle會自動選用call來唿叫。

另一點需要注意的,是這次如果還是用truffle migrate命令,我們會得到如下信息:

$ truffle migrate
Using network 'development'.
Network up to date.

Truffle會告訴你現在網絡上的合約都已是最新的,但事實上剛剛程序中新增的方法並沒有更新到內存塊鏈上。要更新內存塊鏈上已部署的程序,需要改寫migrations中的腳本,但現在還不到介紹migration的時候。還好我們開發用的內存塊鏈是怎麼修改都沒關係的testrpc,可以使用truffle migrate --reset命令直接重新在testrpc上部署一次。

總結

這篇文章非常簡單,通過這篇文章,你將掌握如何配置開發環境、如何創建新項目、如何編譯、如何部署合約以及了解整個智能合約開發的流程。

打賞地址

比特幣:1FcbBw62FHBJKTiLGNoguSwkBdVnJQ9NUn
以太坊:0xF055775eBD516e7419ae486C1d50C682d4170645

技術交流

  • 區塊鏈技術交流QQ群:348924182

  • 「區塊鏈部落」官方公眾號

參考資料

最後更新:2017-09-20 10:03:32

  上一篇:go  AI大事件 | Geoffrey Hinton決定拋棄反向傳播,預期策略梯度算法
  下一篇:go  Spring 環境下開發部署RUKU v1.0的過程