區塊鏈:從零構建加密貨幣部落幣(BLC)

這裏寫圖片描述

truffle framework初體驗 - 如何編寫智能合約

使用solidity語言撰寫智能合約

Ethereum上的智能合約需要使用solidity語言來撰寫。雖然還有其他能用來撰寫智能合約的語言如Serpent(類Python)lll(類Fortran),但目前看到所有公開的智能合約都是使用solidity撰寫。

宣傳上說,solidity是一種類似Javascript的語言,而且圍繞着solidity的各種開發工具鏈,都是使用屬於Javascript生態系的npm來提供的。但我覺得solidity還是比較像JavaC#。因爲和Javascript不同,solidityJavaC#同屬於強類型(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: 測試智能合約的代碼放在這裏,支持jssol 測試。 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語句將合約部署到區塊鏈上。這邊HelloWorldcontract的名稱而不是文件名。因此可以用此語法讀入任一.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重新部署一次。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章