以太坊 (四)十分鐘幫你徹底走上智能合約開(kāi)發(fā)之路

學(xué)習(xí)目標(biāo)

  1. 了解智能合約
  2. 簡(jiǎn)單環(huán)境搭建
  3. 能夠利用solidity編寫(xiě)Hello World合約
  4. 合約部署
  5. 和合約互動(dòng)

智能合約是什么

在區(qū)塊鏈上運(yùn)行的程序精肃,通常稱(chēng)為智能合約(Smart Contract)。所以通常會(huì)把寫(xiě)區(qū)塊鏈程序改稱(chēng)寫(xiě)智能合約撇吞。雖然比特幣(Bitcoin)上也能寫(xiě)智能合約分尸,但是比特幣所支持的語(yǔ)法僅與交易有關(guān),能做的事情比較有限。因此目前提到寫(xiě)智能合約肠套,通常指的是支持執(zhí)行圖靈完備程序的以太坊(Ethereum)區(qū)塊鏈 。

智能合約的應(yīng)用場(chǎng)景

目前最常見(jiàn)的智能合約是各種加密貨幣合約猖任,開(kāi)發(fā)者可以很容易地透過(guò)部署一個(gè)智能合約你稚,來(lái)提供運(yùn)行于以太坊上的新加密代幣。如果這份智能合約相容于 ERC20 標(biāo)準(zhǔn) 1朱躺,開(kāi)發(fā)者不需要重新開(kāi)發(fā)從挖礦到交易的整個(gè)代幣生態(tài)系刁赖,你的新加密代幣就可以直接使用支持支持以太坊的電子錢(qián)包來(lái)收送,大大降低了建立新加密代幣的門(mén)檻长搀。

智能合約也可以用來(lái)運(yùn)作各種公開(kāi)公正的自動(dòng)服務(wù)機(jī)構(gòu)(DAO宇弛,權(quán)力下放自治組織)。透過(guò)分散在全球各節(jié)點(diǎn)上運(yùn)作的智能合約盈滴,所有運(yùn)作與決策都是公開(kāi)透明的涯肩,降低了交易的不確定性(不確定性)。

如何編寫(xiě)智能合約巢钓?

Ethereum上的智能合約需要使用solidity語(yǔ)言來(lái)編寫(xiě)病苗。官方宣傳上說(shuō)solidity是一種類(lèi)似的JavaScript的語(yǔ)言,而且圍繞著JavaScript的各種開(kāi)發(fā)工具鏈都是使用屬于使用 Javascript 生態(tài)系的 NPM 來(lái)提供的症汹。

將智能合約部署到區(qū)塊鏈的流程

寫(xiě)好solidity代碼(.sol)后硫朦,需要先將程序代碼編譯(編譯)成 EVM(Ethereum Virtual Machine)能讀懂的二進(jìn)制度Contract ByteCode,才能部署到 Ethereum 的區(qū)塊鏈上執(zhí)行背镇。部署到區(qū)塊鏈上的合約會(huì)有一個(gè)和錢(qián)包地址(地址)一樣格式的合約地址(Contract Address)咬展。

Snip20170918_10.png

部署后智能合約可自動(dòng)執(zhí)行。后續(xù)呼叫智能合約的時(shí)候瞒斩,使用者可以使用部署合約的錢(qián)包地址(所有者帳戶(hù))破婆,或依據(jù)編寫(xiě)的智能合約條件,讓其他錢(qián)包地址也能呼叫這個(gè)智能合約胸囱。 呼叫智能合約祷舀,其實(shí)就是向這個(gè)合約地址發(fā)起交易,只是交易的不只是代幣,而可以是智能合約提供的呼叫方法裳扯。

Snip20170918_11.png

開(kāi)發(fā)所需工具

  • 以太坊內(nèi)存塊鏈測(cè)試環(huán)境ganache-cli
  • 智能合約開(kāi)發(fā)框架truffle
  • 編輯器Atom搭配solidity插件

開(kāi)發(fā)工具安裝

首先開(kāi)發(fā)機(jī)上必須裝好 Node.js抛丽,再使用以下命令安裝ganache-clitruffle

$  npm install -g ganache-cli truffle

注意:如果安裝過(guò)程中遇到問(wèn)題,可以升級(jí)下node的版本試試饰豺。

Atom下載安裝亿鲜,安裝solidity相關(guān)插件

tom.png

啟動(dòng) ganache-cli

安裝好后隨時(shí)可以使用ganache-cli命令來(lái)啟動(dòng)以太坊測(cè)試環(huán)境。

wangsanjundeMacBook-Pro:~ wangsanjun$ ganache-cli
Ganache CLI v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0xde12463cc19e098ba773adfd236fedb0b21faeb6
(1) 0xdfd59924386b19ad1cfea2088c3279363d2fc936
(2) 0xa19c5064e75fee1dad8bdeb9f4e7a373ce3fa1f7
(3) 0x53d5e0b93659bd7f7f7263365c031e318b2261c0
(4) 0x06b6da8c19b3a0f770f57fe5f6ff469d95ebe098
(5) 0x78121378f88cc8295255f65822e0da64829d1464
(6) 0xf2e3eb6669a941d7ad0984d0fa1e8b88ea6eab3d
(7) 0x39400e42b45691b8ad06507e80f644976d512db7
(8) 0x52c644f97806cf3371150561dd1a44e768378d11
(9) 0xf30705a7669c67d96f80288018e7993918ca62d6

Private Keys
==================
(0) 71a96e34a8bd71f2380bc0b46f62109f0c460afc469ab02c7a89374dba107223
(1) 8ec9ab82818b47eea746d0be5a75c159a21107422ee2624a6b8427bdf0e837d0
(2) c50a555a76aa502805da94b6facac4b3784965f10e57218f23704f4d8526a7ed
(3) 81088a0a92e8898c455b607080b0b82e78033b5728494df98ed2a06d1e7ab323
(4) 21b347c449c0e7571fd6f82ef6feea881a56e9d6c172c0134316036fabe6ccdc
(5) 0498c47475049e0c8799593b063ff2e0ee5f07e9fb6d4db05a9d57adc4b9bc23
(6) 4528f26f7fdc64c54d12c32dad9c1836155ef8fadce316bc351ffd47fbcdb6fa
(7) 371eb0e4f309117d53d10c007550acb19c1a387f9e034a52f3e741b8d955fe3c
(8) 4d65400d1f12bf2bb26189e8beadec98bb34e85e34db33ee969449e4993c2b2c
(9) 160a3031008a3583f9cacfe3aebf34a5bc0cbc4c0556cda97baa32c90ea92859

HD Wallet
==================
Mnemonic:      creek found enroll warm flame door used essay indoor cry carpet journey
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

可以看到ganache-cli啟動(dòng)后自動(dòng)建立了10個(gè)帳號(hào)(Accounts)冤吨,與每個(gè)帳號(hào)對(duì)應(yīng)的私鑰(Private Key)蒿柳。每個(gè)帳號(hào)中都有100個(gè)測(cè)試用的以太幣(Ether)。要注意ganache-cli僅運(yùn)行在內(nèi)存中锅很,因此每次重開(kāi)時(shí)都會(huì)回到全新的狀態(tài)其馏。

建立項(xiàng)目

開(kāi)啟另一個(gè)終端窗口,輸入以下命令以建立項(xiàng)目:

wangsanjundeMacBook-Pro:Desktop wangsanjun$ mkdir SmartContractDemo
wangsanjundeMacBook-Pro:Desktop wangsanjun$ cd SmartContractDemo/
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ ls
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ mkdir HelloWorld
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ cd HelloWorld/
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ ls
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

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

目錄結(jié)構(gòu):

truffle項(xiàng)目目錄結(jié)構(gòu).png
  • /contracts:存放智能合約原始代碼的地方爆安,可以看到里面已經(jīng)有一個(gè)sol文件,我們開(kāi)發(fā)的HelloWorld.sol文件就存放在這個(gè)文件夾仔引。

    wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd contracts/
    wangsanjundeMacBook-Pro:contracts wangsanjun$ ls
    Migrations.sol
    
  • /migrations:這是 Truffle用來(lái)部署智能合約的功能扔仓,待會(huì)兒我們會(huì)新建一個(gè)類(lèi)似1_initial_migration.js的文件來(lái)部署 HelloWorld.sol

    wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd migrations/
    wangsanjundeMacBook-Pro:migrations wangsanjun$ ls
    1_initial_migration.js
    
  • /test:測(cè)試智能合約的代碼放在這里咖耘,支持jssol 測(cè)試翘簇。

  • truffle.js/truffle-config.js: Truffle 的配置文件,需要配置要連接的以太坊網(wǎng)絡(luò)

image

新建 HelloWorld 合約

contracts文件夾下新建HelloWorld.sol文件

HelloWorld.sol文件內(nèi)容如下:

pragma solidity ^0.4.17;
contract HelloWorld {
  function sayHello() returns (string) {
    return "HelloWorld";
  }
}

講解

pragma solidity ^0.4.17;

第一行指的是目前使用的solidity版本,不同版本的solidity可能會(huì)編譯出不同的bytecode儿倒。^代表兼容solidity``0.4.17 ~ 0.4.9的版本版保。

contract HelloWorld {
    ...
}

contract關(guān)鍵字類(lèi)似于其他語(yǔ)言中較常見(jiàn)的class。因?yàn)?code>solidity是專(zhuān)為智能合約(Contact)設(shè)計(jì)的語(yǔ)言夫否,聲明contract后即內(nèi)置了開(kāi)發(fā)智能合約所需的功能彻犁。也可以把這句理解為class HelloWorld extends Contract

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

函數(shù)的結(jié)構(gòu)與其他程序類(lèi)似凰慈,但如果有傳入的參數(shù)或回傳值汞幢,需要指定參數(shù)或回傳值的類(lèi)型(type)。

編譯

現(xiàn)在執(zhí)行truffle compile命令微谓,我們可以將HelloWorld.sol原始碼編譯成Ethereum bytecode森篷。

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...

Compilation warnings encountered:

/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: No visibility specified. Defaulting to "public".
  function sayHello() returns (string) {
  ^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: Function state mutability can be restricted to pure
  function sayHello() returns (string) {
  ^
Spanning multiple lines.

Writing artifacts to ./build/contracts

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd build
wangsanjundeMacBook-Pro:build wangsanjun$ ls
contracts
wangsanjundeMacBook-Pro:build wangsanjun$ cd contracts/
wangsanjundeMacBook-Pro:contracts wangsanjun$ ls
HelloWorld.json Migrations.json
編譯成功.png

Warning暫時(shí)忽略,編譯成功后豺型,會(huì)在HelloWorld文件夾下面的build/contracts文件夾下面看見(jiàn)HelloWorld.json文件仲智。

部署

truffle框架中提供了方便部署合約的腳本。新建migrations/2_deploy_contracts.js文件(腳本使用Javascript編寫(xiě))姻氨,將內(nèi)容修改如下:

var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
  deployer.deploy(HelloWorld);
};
部署文件.png

使用artifacts.require語(yǔ)句來(lái)取得準(zhǔn)備部署的合約钓辆。使用deployer.deploy語(yǔ)句將合約部署到區(qū)塊鏈上。這邊HelloWorldcontract的名稱(chēng)而不是文件名。因此可以用此語(yǔ)法讀入任一.sol文件中的任一合約岩馍。

現(xiàn)在執(zhí)行truffle migrate命令:

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate
Error: No network specified. Cannot determine current network.
    at Object.detect (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-core/lib/environment.js:31:1)
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-core/lib/commands/migrate.js:91:1
    at finished (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-workflow-compile/index.js:53:1)
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/index.js:303:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:157:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:3874:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:473:1
    at replenish (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:993:1)
    at iterateeCallback (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:983:1)
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:958:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:3871:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:153:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1126:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:473:1
    at iteratorCallback (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1050:1)
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:958:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1123:1
    at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:132:1
    at FSReqWrap.oncomplete (fs.js:114:15)

報(bào)錯(cuò)了碉咆,Error: No network specified. Cannot determine current network.哦,原來(lái)找不到我們的私有鏈網(wǎng)絡(luò)蛀恩。也是啊疫铜,怎么著才能連接到我們本地的ganache-cli測(cè)試環(huán)境呢。原來(lái)需要配置truffle.js文件

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // Match any network id
    }
  }
};

重新執(zhí)行truffle migrate命令:

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xb821e6898642eac825b02a571f8ce4a3e0d0443e49274bbc4c70971a5ddea5c4
  Migrations: 0xb414c2a65d595dcae17c5c0c51076b95c8babd73
Saving successful migration to network...
  ... 0xd67eafbab4a191dbd18d70b7065f4857374d4770cdd4c44e6fb4f0a13dc27ce3
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying HelloWorld...
  ... 0x5f278a700ea1d9f5f9565841a621d9f4878184c43deb6819f7217435528c7bde
  HelloWorld: 0x046692c66a173925b95e378349433222e7a139bb
Saving successful migration to network...
  ... 0x02f4e867c23a4e21a850935f4642d5a2c5c2b195ce35ac0ba2e0cbda8c0f9a86
Saving artifacts...

如此一來(lái)合約已經(jīng)部署到ganache-cli中双谆。切換到ganache-cli窗口壳咕,可以看到ganache-cli有反應(yīng)了。

ganache.png

與合約互動(dòng)

truffle提供命令行工具顽馋,執(zhí)行truffle console命令后谓厘,可用Javascript來(lái)和剛剛部署的合約互動(dòng)。

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: [Object],
        bytecode: '0x6060604052341561000f57600080fd5b6101578061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b527bfcbe8419a648eb4e7568b2e6c491f30059cc3f7bed7a7695d10400d57150029',
        deployedBytecode: '0x606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b527bfcbe8419a648eb4e7568b2e6c491f30059cc3f7bed7a7695d10400d57150029',
        sourceMap: '25:93:0:-;;;;;;;;;;;;;;;;;',
        deployedSourceMap: '25:93:0:-;;;;;;;;;;;;;;;;;;;;;;;;49:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67:0;78:6;;:::i;:::-;92:19;;;;;;;;;;;;;;;;;;;;49:67;:::o;25:93::-;;;;;;;;;;;;;;;:::o',
        source: 'pragma solidity ^0.4.17;\ncontract HelloWorld {\n  function sayHello() returns (string) {\n    return "HelloWorld";\n  }\n}\n',
        sourcePath: '/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
        ast: [Object],
        legacyAST: [Object],
        compiler: [Object],
        networks: [Object],
        schemaVersion: '2.0.0',
        updatedAt: '2018-03-09T00:54:30.721Z' },
     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: '0x4068364a85fa3bac7e83c45581397fe9c5111b39',
        gas: 6721975,
        gasPrice: 100000000000 },
     currentProvider: 
      HttpProvider {
        host: 'http://127.0.0.1:8545',
        timeout: 0,
        user: undefined,
        password: undefined,
        headers: undefined,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     network_id: '1520386125068' },
  abi: 
   [ { constant: false,
       inputs: [],
       name: 'sayHello',
       outputs: [Object],
       payable: false,
       stateMutability: 'nonpayable',
       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: '0x046692c66a173925b95e378349433222e7a139bb',
     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: '0x046692c66a173925b95e378349433222e7a139bb',
  transactionHash: null }
truffle(development)> contract.sayHello.call()
'Hello World'

講解

HelloWorld.deployed().then(instance => contract = instance)

truffle console中預(yù)載了truffle-contract函數(shù)庫(kù)寸谜,以方便操作部署到區(qū)塊鏈上的合約竟稳。

這邊使用HelloWorld.deployed().then語(yǔ)句來(lái)取得HelloWorld合約的Instance(實(shí)例),并存到contract變量中熊痴,以方便后續(xù)的調(diào)用他爸。

上面用的是Javascript ES6+的語(yǔ)法,這句也可以寫(xiě)成:

HelloWorld.deployed().then(instance => {
    contract = instance
});

還可以用 ES5 的寫(xiě)法:

HelloWorld.deployed().then(function(instance) {
  hello = instance;
});
truffle(development)> contract.sayHello.call()
'Hello World'

如此一來(lái)果善,我們已寫(xiě)好并部署完成了第一個(gè)智能合約诊笤,也驗(yàn)證了合約確實(shí)可以運(yùn)作。

加入新方法

我們?cè)?code>HelloWorld.sol中再加入一個(gè)echo方法巾陕,echo方法接受輸入一個(gè)參數(shù)讨跟,并回傳傳送的參數(shù)。

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

新的echo方法中傳入了一個(gè)name參數(shù)鄙煤。我們也為echo方法加入一個(gè)constant聲明晾匠,表示調(diào)用這個(gè)方法并不會(huì)改變區(qū)塊鏈的狀態(tài)。如此一來(lái)馆类,透過(guò)truffle-contract來(lái)調(diào)用此方法時(shí)混聊,會(huì)自動(dòng)選用call來(lái)呼叫,也不需要額外提供 gas乾巧。

由于更新了合約內(nèi)容句喜,我們需要先重新新編譯一次,將編譯結(jié)果部署到ganache-cli上沟于,再透過(guò)truffle console執(zhí)行看看結(jié)果咳胃。

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle compile
Compiling ./contracts/HelloWorld.sol...

Compilation warnings encountered:

/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: No visibility specified. Defaulting to "public".
  function sayHello() returns (string) {
  ^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:7:3: Warning: No visibility specified. Defaulting to "public".
  function echo(string name) constant returns (string) {
  ^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: Function state mutability can be restricted to pure
  function sayHello() returns (string) {
  ^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:7:3: Warning: Function state mutability can be restricted to pure
  function echo(string name) constant returns (string) {
  ^
Spanning multiple lines.

Writing artifacts to ./build/contracts

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0x9f104e49cbee7b4d058109400529078162b180f402e23b9ff237157a1599a019
  Migrations: 0x95fa54d8abaf328c0d0c2c6713c369feb0eda7fe
Saving successful migration to network...
  ... 0x069eb545ba8689dfd9ea76d4160b47c37f7c6bb7a8bd91f4f7d7ee3d45e2c85c
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Replacing HelloWorld...
  ... 0x8a2d20fb9009bd631e602ede2605bf1b7e750ec81569bdd202e2379e276f05ad
  HelloWorld: 0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540
Saving successful migration to network...
  ... 0x70a4b9f5234b2bfc67f70ccd1722b1256a9b1587079dd7aca54c957f40c00443
Saving artifacts...

wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle console
truffle(development)> let contract;
undefined
truffle(development)> HelloWorld.deployed().then(intance => contract = instance) 
ReferenceError: instance is not defined
    at HelloWorld.deployed.then.intance (evalmachine.<anonymous>:1:50)
    at process._tickDomainCallback (internal/process/next_tick.js:135:7)
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: [Object],
        bytecode: '0x6060604052341561000f57600080fd5b6102488061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a7230582009219be2f4196357c15d51af4de237288899cefcc0f0d8f9b1e81f2537de00e20029',
        deployedBytecode: '0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a7230582009219be2f4196357c15d51af4de237288899cefcc0f0d8f9b1e81f2537de00e20029',
        sourceMap: '25:172:0:-;;;;;;;;;;;;;;;;;',
        deployedSourceMap: '25:172:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;120:75:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67:0;78:6;;:::i;:::-;92:19;;;;;;;;;;;;;;;;;;;;49:67;:::o;120:75::-;165:6;;:::i;:::-;186:4;179:11;;120:75;;;:::o;25:172::-;;;;;;;;;;;;;;;:::o',
        source: 'pragma solidity ^0.4.17;\ncontract HelloWorld {\n  function sayHello() returns (string) {\n    return "HelloWorld";\n  }\n\n  function echo(string name) constant returns (string) {\n    return name;\n  }\n}\n',
        sourcePath: '/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
        ast: [Object],
        legacyAST: [Object],
        compiler: [Object],
        networks: [Object],
        schemaVersion: '2.0.0',
        updatedAt: '2018-03-09T01:43:29.205Z' },
     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: '0x4068364a85fa3bac7e83c45581397fe9c5111b39',
        gas: 6721975,
        gasPrice: 100000000000 },
     currentProvider: 
      HttpProvider {
        host: 'http://127.0.0.1:8545',
        timeout: 0,
        user: undefined,
        password: undefined,
        headers: undefined,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     network_id: '1520386125068' },
  abi: 
   [ { constant: false,
       inputs: [],
       name: 'sayHello',
       outputs: [Object],
       payable: false,
       stateMutability: 'nonpayable',
       type: 'function' },
     { constant: true,
       inputs: [Object],
       name: 'echo',
       outputs: [Object],
       payable: false,
       stateMutability: 'view',
       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: '0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540',
     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: '0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540',
  transactionHash: null }
truffle(development)> contract.echo.call("HelloWorld")
'HelloWorld'
truffle(development)> contract.echo("HelloWorld")
'HelloWorld'

echo方法確實(shí)將我們輸入的內(nèi)容回傳了。同時(shí)因?yàn)槁暶髁?constant旷太,我們不需要直接調(diào)用call()方法展懈,truffle會(huì)自動(dòng)選用call來(lái)呼叫销睁。

另一點(diǎn)需要注意的,是這次如果還是用truffle migrate命令存崖,我們會(huì)得到如下信息:

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

Truffle會(huì)告訴你現(xiàn)在網(wǎng)絡(luò)上的合約都已是最新的冻记,但事實(shí)上剛剛程序中新增的方法并沒(méi)有更新到內(nèi)存塊鏈上。要更新內(nèi)存塊鏈上已部署的程序来惧,需要改寫(xiě)migrations中的腳本冗栗,但現(xiàn)在還不到介紹migration的時(shí)候。還好我們開(kāi)發(fā)用的內(nèi)存塊鏈?zhǔn)窃趺葱薷亩紱](méi)關(guān)系的ganache-cli供搀,可以使用truffle migrate --reset命令直接重新在ganache-cli上部署一次隅居。

總結(jié)

這篇文章非常簡(jiǎn)單,通過(guò)這篇文章葛虐,你將掌握如何配置開(kāi)發(fā)環(huán)境胎源、如何創(chuàng)建新項(xiàng)目、如何編譯屿脐、如何部署合約以及了解整個(gè)智能合約開(kāi)發(fā)的流程涕蚤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市的诵,隨后出現(xiàn)的幾起案子赞季,更是在濱河造成了極大的恐慌,老刑警劉巖奢驯,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異次绘,居然都是意外死亡瘪阁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)邮偎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)管跺,“玉大人,你說(shuō)我怎么就攤上這事禾进』砼埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵泻云,是天一觀(guān)的道長(zhǎng)艇拍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)宠纯,這世上最難降的妖魔是什么卸夕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮婆瓜,結(jié)果婚禮上快集,老公的妹妹穿的比我還像新娘贡羔。我一直安慰自己,他們只是感情好个初,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布乖寒。 她就那樣靜靜地躺著,像睡著了一般院溺。 火紅的嫁衣襯著肌膚如雪楣嘁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天覆获,我揣著相機(jī)與錄音马澈,去河邊找鬼。 笑死弄息,一個(gè)胖子當(dāng)著我的面吹牛痊班,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摹量,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涤伐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缨称?” 一聲冷哼從身側(cè)響起凝果,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睦尽,沒(méi)想到半個(gè)月后器净,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡当凡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年山害,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沿量。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浪慌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出朴则,到底是詐尸還是另有隱情权纤,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布乌妒,位于F島的核電站汹想,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芥被。R本人自食惡果不足惜欧宜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拴魄。 院中可真熱鬧冗茸,春花似錦席镀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至挂绰,卻和暖如春屎篱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背葵蒂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工交播, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人践付。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓秦士,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親永高。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隧土,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359