以太坊文檔整理

本文是對(duì)以太坊文檔 Ethereum Frontier GuideEthereum Homestead 的整理。Fontier 和 Homestead 分別是以太坊的第 1 和 第 2 個(gè)版本。

本文使用 go 語言編寫的命令行客戶端 geth

geth 的命令可以分為 CLI 命令和 JavaScript 控制臺(tái)交互式命令,約定如下

  • geth account list:這是 CLI 命令
  • > eth.accounts:這是 javaScript 控制臺(tái)的命令,前面有一個(gè) >。進(jìn)入控制臺(tái)的方式為 geth console

1. 安裝和運(yùn)行節(jié)點(diǎn)

1.1 安裝客戶端

geth 的安裝教程請(qǐng)參見 Building Ethereum

1.2 同步區(qū)塊

「同步」的意思是把網(wǎng)絡(luò)上的區(qū)塊全部下載到本地,以同步到網(wǎng)絡(luò)的最新狀態(tài)。使用客戶端前必須先同步區(qū)塊。

同步命令如下

  • geth:全節(jié)點(diǎn)模式同步模式
  • geth --fast --cache=1024--fast 快速同步模式,只下載狀態(tài)(state downloads)

更優(yōu)雅的同步方式

  • geth --fast console 2>network_sync.log:同步時(shí)把輸出日志重定向到network_sync.log中,并進(jìn)入控制臺(tái)。這樣就可以邊同步邊使用控制臺(tái)命令。
  • tail -f network_sync.log 可以重新瀏覽到日志

數(shù)據(jù)存放目錄

主網(wǎng)絡(luò)區(qū)塊數(shù)據(jù)的默認(rèn)存放目錄是 ~/Library/Ethereum(Mac OS X)

  • 其他系統(tǒng)下,可用該方式找到默認(rèn)路徑:geth -h 后搜索 --datadir,后面跟著的就是默認(rèn)目錄
  • 如果你想將區(qū)塊下載到其他目錄,可以使用命令 geth --fast --datadir "<path>"

不同客戶端是可以共用區(qū)塊數(shù)據(jù)的。用 geth 同步的區(qū)塊數(shù)據(jù),可以在 Ethereum Wallet 或 Mist 客戶端里直接使用。

導(dǎo)入已有的區(qū)塊文件

如果本地已有區(qū)塊文件,可以將其導(dǎo)入

  • geth export filename:導(dǎo)出區(qū)塊文件
  • geth import filename:導(dǎo)入?yún)^(qū)塊文件

啟動(dòng)節(jié)點(diǎn)

  • geth 借助啟動(dòng)節(jié)點(diǎn)(bootstrap nodes)來初始化尋找過程。啟動(dòng)節(jié)點(diǎn)被寫在源碼里,但可用這些方式修改
    • geth --bootnodes "enode://pubkey1@ip1:port1 enode://pubkey2@ip2:port2 enode://pubkey3@ip3:port3"pubkeyipport 依次為啟動(dòng)節(jié)點(diǎn)的公鑰地址、ip 和端口號(hào)。
    • > admin.addPeer("enode://pubkey@ip:port")
  • geth 使用名為 discover protocol 的協(xié)議來尋找其他節(jié)點(diǎn)

1.3 啟動(dòng)客戶端

啟動(dòng)客戶端的方式如下

  • 主網(wǎng)絡(luò)
    • 如果區(qū)塊數(shù)據(jù)在默認(rèn)目錄下:geth
    • 如果區(qū)塊數(shù)據(jù)在其他目錄下:geth --datadir <path>
  • 測(cè)試網(wǎng)絡(luò):geth --datadir <path> --networkid 15。你只會(huì)連接與你的協(xié)議版本和 networkid 都相同的節(jié)點(diǎn)。主網(wǎng)絡(luò)的 networkid 是 1,所以 networkid 只要不是 1 就可以

更常用的是啟動(dòng)客戶端,并進(jìn)入控制臺(tái)模式:geth --datadir <path> console 2>console.log。同時(shí)可以另開窗口,用 tail -f console.log 瀏覽日志。

更復(fù)雜的啟動(dòng)命令

geth --identity "MyNodeName" --rpc --rpcport "8080" --rpccorsdomain "*" --datadir "./ethdev" --port "30303" --nodiscover --rpcapi "db,eth,net,web3" --networkid 1999(參考:Command line parameters

  • identity "MyNodeName":為你的節(jié)點(diǎn)設(shè)置身份標(biāo)識(shí),以更容易在節(jié)點(diǎn)列表中便是
  • --rpc:開啟 RPC 接口
  • --rpcport "8080":RPC 端口
  • --rpccorsdomain "*":設(shè)置能連接到你的節(jié)點(diǎn)的 URL,用來完成 RPC 任務(wù)。* 指任何 URL 都能連接到你。
  • --datadir "./ethdev":區(qū)塊數(shù)據(jù)文件夾
  • --port "30303":用來監(jiān)聽其他節(jié)點(diǎn)的端口
  • --nodiscover:你的節(jié)點(diǎn)不會(huì)被其他人發(fā)現(xiàn),除非他們手動(dòng)添加你
  • --rpcapi "db,eth,net,web3":提供給別人使用的 RPC API,默認(rèn)為 web3 接口
  • networkid 1999:相同 networkdid 才會(huì)連接到一起

監(jiān)控

在控制臺(tái)里,使用這些命令檢查連接狀態(tài)

  • > net.listening:檢查是否連接
  • > net.peerCount:連接到的節(jié)點(diǎn)個(gè)數(shù)
  • > admin.peers:返回連接到的節(jié)點(diǎn)的詳細(xì)信息
  • > admin.nodeInfo:返回本地節(jié)點(diǎn)的詳細(xì)信息

此外,還有一些網(wǎng)站供你查看以太坊主網(wǎng)絡(luò)的狀態(tài)

1.4 測(cè)試網(wǎng)絡(luò)

以太坊公有的測(cè)試網(wǎng)絡(luò)有 Ropsten 和 Rinkeby。除此之外,你可以搭建自己的私有網(wǎng)絡(luò),即只能本地訪問的私網(wǎng)。

下面介紹 3 種測(cè)試網(wǎng)絡(luò)的搭建方式

Ropsten網(wǎng)絡(luò)

  • 同步區(qū)塊:geth --testnet --fast --bootnodes "enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303,enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303",來連接特殊的啟動(dòng)節(jié)點(diǎn)來同步 Ropsten 網(wǎng)絡(luò)的數(shù)區(qū)塊。或者也可以使用 Mist 進(jìn)行同步。
  • 進(jìn)入網(wǎng)絡(luò):geth --testnetwork

參考:ethereum/ropsten

Rinkeby網(wǎng)絡(luò)

參見 Rinkeby: Ethereum Testnet - Connect Yourself,有 archive, full, light, embedded 4種模式

私有網(wǎng)絡(luò)

搭建私有網(wǎng)絡(luò),需要先新建創(chuàng)世塊參數(shù)文件 genesis.json

{
    "config": {
        "chainId": 15,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
    "difficulty": "10000",
    "gasLimit": "2100000",
    "alloc": {
        "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" },
        "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" }
    }
}

接下來使用命令 geth --datadir ./ethdev init <genesis.json> console 初始化測(cè)試網(wǎng)絡(luò),并進(jìn)入控制臺(tái)。

該測(cè)試網(wǎng)絡(luò)只有你一個(gè)人,你需要自己挖坑來記錄交易。

參考

2. 賬戶管理

2.1 創(chuàng)建賬戶

新建賬戶

  • geth account new
  • > personal.newAccount("passphrase")
  • 非交互方式:geth --password <passwordfile> account new,密碼以明文方式寫在文件 passwordfile

導(dǎo)入賬戶

  • geth account import <keyfile>,私鑰以明文方式寫在文件 keyfile 里,每個(gè)一行。
    • <keyfile> 是存著賬戶信息的 json 文件,其中私鑰是被賬號(hào)密碼(password)加密后,存放在里面的
    • 不同平臺(tái)的 keyfile 默認(rèn)存儲(chǔ)位置是不同的
      • Windows: C:\Users\username%appdata%\Roaming\Ethereum\keystore
      • Linux: ~/.ethereum/keystore
      • Mac: ~/Library/Ethereum/keystore
  • 結(jié)合 --password,可以使用在導(dǎo)入賬戶時(shí)設(shè)置密碼:geth --password <passwordfile> account import <keyfile>

修改密碼

  • geth account update <address>
  • geth account update 2:2是賬戶的編號(hào),在 geth account list 中可以看到

2.2 導(dǎo)入錢包

  • geth wallet import <etherwallet.json>

創(chuàng)建多簽名錢包,請(qǐng)參見Creating a Multi-Signature Wallet in Mist

2.3 查看賬戶信息

列出所有賬戶

  • geth account list;對(duì)應(yīng)的控制臺(tái)命令為 > eth.accounts

查看賬戶余額

  • > eth.getBalance(<address>):列出賬戶余額,單位是 Wei
  • > web3.fromWei(eth.getBalance(<address>), "ether"):列出賬戶余額,單位是 eth

下面的代碼可以打印所有的賬戶余額

function checkAllBalances() { 
var i =0; 
eth.accounts.forEach( function(e){
     console.log("  eth.accounts["+i+"]: " +  e + " \tbalance: " + web3.fromWei(eth.getBalance(e), "ether") + " ether"); 
i++; 
})
};

小技巧:可以把代碼存到文件中。進(jìn)入 geth 的控制臺(tái)后,用 > loadScript(<loadfile.js>) 導(dǎo)入文件中的函數(shù)。

2.4 發(fā)送交易

以發(fā)起一個(gè) 0.01 個(gè) ether 的轉(zhuǎn)賬交易為例

> var sender = eth.accounts[0];
> var receiver = eth.accounts[1];
> var amount = web3.toWei(0.01, "ether")

> eth.sendTransaction({from:sender, to:receiver, value: amount, gas: gasAmount})  //`gas` 不是必須的

之后會(huì)讓你輸入密碼

或者也可以先用 personal.unlockAccount(sender, <passphrase>) 解鎖賬戶,再發(fā)送交易。

3. 挖礦

3.1 介紹

挖礦會(huì)有挖礦獎(jiǎng)勵(lì)

以太坊使用 Ethash 的 PoW 算法

3.2 CPU 挖礦

挖礦

  • geth --mine --minerthreads=4--minerthreads 設(shè)置并行挖礦的線程數(shù)量,默認(rèn)為所有的處理器數(shù)量;
  • > miner.star(8),使用 8 個(gè) minerthreads;>miner.stop() 停止
  • 稍微復(fù)雜點(diǎn)的挖礦命令:geth --mine --minerthreads 4 --datadir /usr/local/share/ethereum/30303 --port 30303:使用不同數(shù)據(jù)目錄(ethereum/30303)和不同的端口(30303)挖礦

發(fā)放獎(jiǎng)勵(lì):eth.etherbase(也叫 coinbase)是一個(gè)地址,挖礦獎(jiǎng)勵(lì)會(huì)發(fā)到這個(gè)地址里。改變 etherbase 的方式如下

  • geth --etherbase 1 --mine:改變 etherbase 為編號(hào)1的地址;或 geth --etherbase '0xa4d8e9cae4d04b093aac82e6cd355b6b963fb7ff'
  • 控制臺(tái):> miner.setEtherbase(eth.accounts[2])

其他

  • > eth.getBlock(i).miner:查看塊的挖出者
  • eth.getBlockTransactionCount("pending"):查看未確認(rèn)交易的數(shù)量
  • eth.getBlock("pending", true).transactions:查看所有未確認(rèn)交易

下面的函數(shù)可以統(tǒng)計(jì)不同地址的出塊數(shù)量

function minedBlocks(lastn, addr) {
  addrs = [];
  if (!addr) {
    addr = eth.coinbase
  }
  limit = eth.blockNumber - lastn
  for (i = eth.blockNumber; i >= limit; i--) {
    if (eth.getBlock(i).miner == addr) {
      addrs.push(i)
    }
  }
  return addrs
}
// scans the last 1000 blocks and returns the blocknumbers of blocks mined by your coinbase 
// (more precisely blocks the mining reward for which is sent to your coinbase).   
minedBlocks(1000, eth.coinbase);
//[352708, 352655, 352559]

3.3 其他挖礦方式

4. 接口

4.1 命令行接口

CLI 命令已介紹得差不多了。

可以去 Command Line Options 查閱具體的命令。

4.2 JSON RPC API

JSON-RPC 是一種無狀態(tài)、輕量級(jí)的 RPC 協(xié)議,規(guī)定了通信的數(shù)據(jù)結(jié)構(gòu)和規(guī)則。以太坊客戶端使用 JSON-RPC 和其他客戶端通信。

比如 MetaMask 錢包就是通過 JSON-RPC 和以太坊客戶端進(jìn)行通信的。

對(duì)于不同的以太坊客戶端,默認(rèn) JSON-RPC 地址如下

geth 是 go 客戶端,因此 JSON-RPC 為 http://localhost:8545

常用命令

  • geth --rpc:開啟 HTTP JSON-RPC
  • geth --rpc --rpcaddr <ip> --rpcport <portnumber>:改變 JSON-RPC 的 ip 和端口
  • 控制臺(tái)下:> admin.startRPC(addr, port)

細(xì)節(jié)請(qǐng)參閱 JSON RPC

4.3 使用 Dapp 的 JavaScript API

你可以使用 web3.js 庫所提供的對(duì)象,來搭建 Dapp。

細(xì)節(jié)請(qǐng)參閱 JavaScript API

4.4 JavaScript 控制臺(tái)

一般操作都在控制臺(tái)模式下進(jìn)行。

  • 進(jìn)入控制臺(tái)的方式:geth console
  • 啟動(dòng)測(cè)試網(wǎng)絡(luò),并進(jìn)入控制臺(tái):geth --datadir ./ethdev console

詳細(xì)命令請(qǐng)參閱 JavaScript Console

5. 智能合約

5.1 對(duì)智能合約進(jìn)行基本概念的介紹;后面的小節(jié)依次介紹合約的編寫、編譯和部署。

參考

5.1 介紹

5.1.1 賬戶

以太坊有 2 種賬戶

  • 外部賬戶(Externally owned account,EOA)
    • 有 ether 余額
    • 能發(fā)送交易(轉(zhuǎn)賬或觸發(fā)合約)
    • 被私鑰控制,即人類直接掌管的賬戶
    • 沒有代碼
  • 合約(Contract)
    • 有 ether 余額
    • 內(nèi)部有代碼
    • EOA 或其他交易發(fā)來的消息可以觸發(fā)代碼執(zhí)行
    • 圖靈完備,且有持久性的存儲(chǔ),即它自身有持久性的狀態(tài)。
    • 可以調(diào)用其他合約

當(dāng)合約收到交易時(shí),以太坊虛擬機(jī)(EVM)會(huì)根據(jù)它收到的參數(shù),來執(zhí)行內(nèi)部的代碼。

5.1.2 交易與消息

交易(Transaction):EOA發(fā)送給其他賬戶(EOA或合約)的簽名過的消息

消息(Message):合約發(fā)給其他合約的消息

兩者的不同就在于發(fā)送方的不同

5.1.3 gas

ether 是以太坊中的貨幣,用于支付 EVM 的計(jì)算。

以太坊中貨幣最小的單位是 wei。

Unit Wei Value Wei
wei 1 wei 1
Kwei (babbage) 1e3 wei 1,000
Mwei (lovelace) 1e6 wei 1,000,000
Gwei (shannon) 1e9 wei 1,000,000,000
microether (szabo) 1e12 wei 1,000,000,000,000
milliether (finney) 1e15 wei 1,000,000,000,000,000
ether 1e18 wei 1,000,000,000,000,000,000

其他:Ether 供應(yīng)量

Gas 被認(rèn)為是網(wǎng)絡(luò)資源的不變花費(fèi)。我們希望發(fā)送每筆交易的真實(shí)成本總是保持不變,所以 Gas 不能被發(fā)行,否則價(jià)格會(huì)有波動(dòng)。

反之,我們發(fā)行 ether。當(dāng) ether 價(jià)格上升時(shí),Gas 價(jià)格就對(duì)應(yīng)下降,以保持真實(shí)成本不變

參考:Ether

5.1.4 賬戶交互的例子

合約通常為這 4 種目的服務(wù)

  • 維護(hù)數(shù)據(jù),這些數(shù)據(jù)通常對(duì)其他合約有用,或來表示外部世界。典型例子是貨幣和記錄特定組織中的成員身份
  • 充當(dāng)一種能執(zhí)行復(fù)雜規(guī)則的 EOA,這被叫作 forwarding contact。該合約收到消息后,在滿足特定情況下,轉(zhuǎn)發(fā)結(jié)果給其他 EOA,比如,直到 3 個(gè)私鑰中的 2 個(gè)確定,才能發(fā)送消息。
  • 管理其他合約和多用戶之間的關(guān)系,典型例子是金融合約或擔(dān)保。一方還可以公開合約,然后供其他人來參與。比如合約自動(dòng)發(fā)送獎(jiǎng)勵(lì)給那些解決某些數(shù)學(xué)問題的人
  • 為其他合約提供函數(shù),作為庫的作用。

合約能充當(dāng)不同的角色,因此我們希望合約可以多交互。下面舉一個(gè)合約交互的例子

  • Alice 和 Bob 下了一個(gè)賭注,每人出 50 GavCoin,賭明年某個(gè)時(shí)候 San Francisco 的氣溫是不是會(huì)超過 35°,若超過,則 Bob 贏得 100 GavCoin。
  • Bob 使用一個(gè) Forwarding Contract。Bob 會(huì)先發(fā)送消息給 Forwarding Contract,然后它再轉(zhuǎn)發(fā)給其他地址;接受消息也是一樣。該 Forwarding Contract 需要發(fā)送方不僅提供 ECDSA 簽名,還需要提供 Lamport 簽名,才能轉(zhuǎn)發(fā)消息。

這個(gè)例子有 5 個(gè)合約

  • GavCoin Contract:管理 GavCoin 代幣的合約
  • Bet Contract:賭注合約
  • Weather Contract:查詢天氣的合約
  • Forwarding Contract:Bob 的轉(zhuǎn)發(fā)合約
  • Lamport Contract:提供 Lamport 簽名基本操作的合約

Alice 完成賭注,需要這么做

  1. Alice 發(fā)送消息給 Bet Contract,其消息是「接受賭約,并把自己的 50 個(gè) GavCoin 存到 Bet Contract 的賬戶下」
  2. Bet Contract 發(fā)送消息給 GavCoin Contract,把 Alice 的 50 個(gè) GavCoin 存到自己的賬戶(即合約地址)里

Bob 想完成賭注,需要這么做

  1. Bob 的 EOA 發(fā)送消息給 Forwarding Contract,該消息包含了 Bob 的 EOA 的 ECDSA 和 Lamport 簽名,以及消息「接受賭約,并把自己的 50 個(gè) GavCoin 存到 Bet Contract 的賬戶下」。
  2. Forwarding Contract 發(fā)送消息給 Lamport Contract,要求驗(yàn)證 Lamport 簽名
  3. 若驗(yàn)簽成功,Lamport Contract 返回 1 給 Forwarding Contract。Forwarding Contract 發(fā)送消息給 Bet Contract
  4. Bet Contract 發(fā)送消息給 GavCoin Contract,把 Bob 的 50 個(gè) GavCoin 存到自己的賬戶(即合約地址)里

Bet Contract 將自動(dòng)執(zhí)行賭約

  1. Bet Contract 每隔一定周期發(fā)消息給 Weather Contract,查詢 San Francisco 的當(dāng)前氣溫
  2. 一旦氣溫超過 35°C,Bet Contract 發(fā)消息給 GavCoin Contract,把自己賬戶里的 100 GavCoin 發(fā)到 Bob 的賬戶里

5.2 編寫合約

學(xué)習(xí) solidity 的資源

接下來以合約 greeter.sol 為例(來自 Contract Tutorial

pragma solidity ^0.4.10;

contract mortal {
    /* Define variable owner of the type address*/
    address owner;

    /* this function is executed at initialization and sets the owner of the contract */
    function mortal() { owner = msg.sender; }

    /* Function to recover the funds on the contract */
    function kill() { if (msg.sender == owner) suicide(owner); }
}

contract greeter is mortal {
    /* define variable greeting of the type string */
    string greeting;
    
    /* this runs when the contract is executed */
    function greeter(string _greeting) public {
        greeting = _greeting;
    }

    /* main function */
    function greet() constant returns (string) {
        return greeting;
    }
}

5.3 編譯合約

合約以「以太坊虛擬機(jī)(EVM)字節(jié)碼」的形式存在與區(qū)塊中,因此需要對(duì)源文件進(jìn)行編譯得到字節(jié)碼。

有幾種編譯方式

5.3.1 安裝 solc 編譯器

下面是 Mac OS 平臺(tái)上的 solc 編譯器安裝方式。其他平臺(tái)請(qǐng)參考 Building from Source

  • git clone --recursive https://github.com/ethereum/solidity.git
  • cd solidity
  • 需要先安裝 Xcode,然后 sudo xcodebuild -license accept 同意 license
  • ./scripts/install_deps.sh:安裝外部依賴
  • ./scripts/build.sh:開始編譯

現(xiàn)在敲入命令 solc 就可以調(diào)用編譯器了

  • Installing Solidity - npm / Node.js 里的 npm install -g solc 方式安裝的是另一種編譯器 solcjs
  • 由于 geth 中的 eth.compile 已被移除,因此舊版本的 admin.setSolc() 設(shè)置編譯器的命令已無效

5.3.2 編譯

編譯后,我們會(huì)得到 2 個(gè)東西,用來部署合約:

  • .bin:二進(jìn)制字節(jié)碼
  • .abi(Application Binary Interface):一個(gè) JSON 對(duì)象,用來定義接口,用來告訴別人怎么使用這個(gè)合約,就好比用戶手冊(cè)。

常用編譯命令

  • solc --bin greeter.sol:編譯得到 .bin 二進(jìn)制字節(jié)碼
  • sloc --abi greeter.sol:編譯得到 .abi 接口(其實(shí)這里只做了解析,沒有編譯)
  • solc --optimize --bin greeter.sol--optimize 可優(yōu)化編譯。優(yōu)化生成的字節(jié)碼更小,部署所需的 gas 也會(huì)減少。
  • solc -o outputDirectory --bin --ast --asm greeter.sol:設(shè)置輸出目錄,目錄 outputDirectory 會(huì)生成 greeter.bingreeter.astgreeter.asm 文件
  • solc --combined-json abi,bin,interface greeter.sol:輸出一個(gè) json 對(duì)象,里面包含 bin、abi 和 interface 信息

為了方便部署,我們一般使用這個(gè)命令

echo "var compilerOutput=`solc --optimize --combined-json abi,bin greeter.sol`" > greeter_compiled.js

這將生成一個(gè) greeter_compiled.js 文件,看起來會(huì)像這個(gè)樣子(已經(jīng)過格式化)

var compilerOutput =
{
  "contracts": {
    "greeter.sol:greeter": {
      "abi": "[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"greet\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_greeting\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]",
      "bin": "6060604052341561000f57600080fd5b6040516103173803806103178339810160405280805160008054600160a060020a03191633600160a060020a03161790559190910190506001818051610059929160200190610060565b50506100fb565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a157805160ff19168380011785556100ce565b828001600101855582156100ce579182015b828111156100ce5782518255916020019190600101906100b3565b506100da9291506100de565b5090565b6100f891905b808211156100da57600081556001016100e4565b90565b61020d8061010a6000396000f300606060405263ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b58114610047578063cfae32171461005c57600080fd5b341561005257600080fd5b61005a6100e6565b005b341561006757600080fd5b61006f610127565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100ab578082015183820152602001610093565b50505050905090810190601f1680156100d85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff908116911614156101255760005473ffffffffffffffffffffffffffffffffffffffff16ff5b565b61012f6101cf565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101c55780601f1061019a576101008083540402835291602001916101c5565b820191906000526020600020905b8154815290600101906020018083116101a857829003601f168201915b5050505050905090565b602060405190810160405260008152905600a165627a7a723058202a04be9bc62f62ece115fc346e0b98ea5019ba1d0199402c0883c957096ac9790029"
    },
    "greeter.sol:mortal": {
      "abi": "[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]",
      "bin": "6060604052341561000f57600080fd5b60008054600160a060020a033316600160a060020a031990911617905560b98061003a6000396000f300606060405263ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b58114603b57600080fd5b3415604557600080fd5b604b604d565b005b6000543373ffffffffffffffffffffffffffffffffffffffff90811691161415608b5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b5600a165627a7a723058208d5720dc8ecd1ce214bdca4a93dd356c2894a206b14d349dba56a43e49ac2ae80029"
    }
  },
  "version": "0.4.17-develop.2017.8.28+commit.2b3a49f7.Darwin.appleclang"
}

這個(gè)文件包含了 binabi 信息。

接下來要用該文件部署合約

5.4 部署合約

進(jìn)入 geth 控制臺(tái)里,執(zhí)行下面的命令

> loadScript('greeter_compiled.js')  // 導(dǎo)入編譯后生成的文件
true
> var _greeting = "Hello World!!!"  // 部署該合約需要的初始化參數(shù)
undefined
> var greeterContract = web3.eth.contract(JSON.parse(compilerOutput.contracts["greeter.sol:greeter"].abi));  // 要解析成 json 對(duì)象
undefined
> var gt = greeterContract.new(_greeting, {from: eth.accounts[0], data: "0x" + compilerOutput.contracts["greeter.sol:greeter"].bin, gas: 4700000},
    function (e, contract) {
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
            console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
    }
  );

總結(jié)一下,可以得到部署合約的一般命令(# 開頭的變量是我們需要設(shè)置的)

> var Contract = web3.eth.contract(#abi);  // abi 是一個(gè) json 對(duì)象
undefined
// 部署一個(gè)合約實(shí)例
> var deployed  = Contract.new(#args, {from: #sender, data: "0x" + #bin, gas: 4700000},   // args:合約的構(gòu)造函數(shù)所需的參數(shù)。如果有多個(gè)參數(shù),依次用,隔開 中;sender:合約的部署者地址;bin:二進(jìn)制字節(jié)碼,前面要加'0x';gas:部署需要花費(fèi)的 gas 量
    function (e, contract) {  // 這是個(gè)回調(diào)函數(shù),作用是合約部署成功后,通知你一聲
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
            console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
    }
  );
null [object Object]
undefined

這里只是把你的合約廣播出去,只有被區(qū)塊記錄后,才部署成功。

私有網(wǎng)絡(luò)下,可以自己來挖新區(qū)塊

> miner.start()
null
> null [object Object]
Contract mined! address: 0xdfb2938df1e4c80f309f7f09ceae871175b94a81 transactionHash: 0x49fad3bbb8fe00efa018fa8387d9ee2fef3a53dd5c841f2131b0afb5ba17f349  // 部署返回的合約地址和交易哈希值

更方便(偷懶)的方法:使用在線編譯器里 Remix - Solidity IDE

  • 把代碼復(fù)制進(jìn)去
  • 選擇你需要的合約,點(diǎn)擊下方的 Contract details (bytecode, interface etc.)
  • 找到 Web3 deploy,其里面的內(nèi)容就是我們要在 geth 控制臺(tái)里輸入的 abi 和 bin

由于 1.6 之后的 geth 控制臺(tái)不能使用 solidity 編譯器,而官方教程 Contract Tutorial 還是老版本,沒有更新,于是饒了很多彎。最后參考 #14850How to compile Solidity contracts with Geth v1.6? 才弄懂。

5.5 使用合約

先體驗(yàn)一下合約的使用

> gt.greet()  // gt 是 5.4 里得到的合約實(shí)例
"Hello World!!!"
> gt.address  // 合約地址
"0xdfb2938df1e4c80f309f7f09ceae871175b94a81"
> eth.getCode(gt.address)
"0x60606....."
> gt.kill.sendTransaction({from:eth.accounts[0]})  // 銷毀合約。銷毀后就無法使用了

這里的 .greet().kill() 函數(shù)的用法不同,是因?yàn)?/p>

  • .greet() 不會(huì)改變鏈上的狀態(tài),是一個(gè)調(diào)用
  • .kill() 會(huì)改變鏈上的狀態(tài),是一個(gè)交易

5.5.1 實(shí)例化

使用合約前,需要對(duì)其進(jìn)行實(shí)例化。先前的 gt 就是一個(gè)實(shí)例。

實(shí)例化合約有 2 步

  • 第一步,知道合約的 abi:var myContract = web3.eth.contract(abi);
  • 第二步:知道合約地址,間接獲得 bin:var contractInstance = myContract.at(address);
    • 執(zhí)行部署命令時(shí),也順帶會(huì)實(shí)例化合約:var contractInstance = myContract.new([constructorParam1] [, constructorParam2], {from: myAccount, data: '0x12345...', gas: 1000000});

參考:web3.eth.contract

5.5.2 調(diào)用合約中的方法

根據(jù)是否會(huì)改變網(wǎng)絡(luò)狀態(tài),可將方法分成調(diào)用(call) 和 交易(sendTransaction) 兩種類型。它們的調(diào)用方式分別如下

  • contractInstance.method.call(param1 [, param2, ...] [, transactionObject] [, defaultBlock] [, callback]);call 類型函數(shù),不會(huì)改變網(wǎng)絡(luò)狀態(tài)。
  • contractInstance.method.sendTransaction(param1 [, param2, ...] [, transactionObject] [, callback]);sendTransaction 類型函數(shù),會(huì)改變網(wǎng)絡(luò)狀態(tài)。

若使用 contractInstance.method(param1 [, param2, ...] [, transactionObject] [, defaultBlock] [, callback]);,則 EVM 會(huì)自動(dòng)根據(jù)方法類型,來選擇使用 call()sendTransaction()

參考:Contract Methods

5.5.3 Event

參考:Contract Event

5.X 其他

除了 geth 控制臺(tái),還可以用 JSON-RPC 部署合約。具體參考 Accessing Contracts and Transactions

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,656評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評(píng)論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,855評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,254評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,473評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,014評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,833評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,016評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,273評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評(píng)論 1 288
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,730評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,006評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • 這個(gè)問題已經(jīng)是講了“千世萬道” 有人說,從小吃,所以愛吃 為什么天天吃都沒有吃膩了的時(shí)候? 以下是南昌人對(duì)米粉的愛...
    知曉南昌閱讀 585評(píng)論 0 0
  • 想要抓住她的心[愛心],得先抓住她的胃[愉快],愛她,不但要給她最好吃的,還得是最健康的[奸笑],比如我們保留95...
    zl張麗健康咨詢服務(wù)中心閱讀 167評(píng)論 0 0
  • 難得的休閑時(shí)光在家,聽了朋友的推薦,也做了一回電視迷,追了一下當(dāng)下的熱播電視劇我的前半生。當(dāng)即被吸引,一下追到20...
    一只魚的故事閱讀 230評(píng)論 0 0
  • 傳統(tǒng)的廠商接連推出網(wǎng)絡(luò)攝像頭,緊接著互聯(lián)網(wǎng)公司也通過各種手段推出了自己的產(chǎn)品。目前雖沒有看到它在市場(chǎng)上的廣泛應(yīng)用,...
    殘劍閱讀 560評(píng)論 2 2
  • 我的孩子4歲,男孩,名叫樂樂,已讀幼兒園,正是發(fā)展社會(huì)功能化和學(xué)習(xí)分享的階段。本周發(fā)生了一件他與小伙伴的沖...
    紫文閱讀 290評(píng)論 0 1