以太坊文档整理

96
趁风卷
0.1 2017.09.03 14:45* 字数 4688

本文是对以太坊文档 Ethereum Frontier GuideEthereum Homestead 的整理。Fontier 和 Homestead 分别是以太坊的第 1 和 第 2 个版本。

本文使用 go 语言编写的命令行客户端 geth

geth 的命令可以分为 CLI 命令和 JavaScript 控制台交互式命令,约定如下

  • geth account list:这是 CLI 命令
  • > eth.accounts:这是 javaScript 控制台的命令,前面有一个 >。进入控制台的方式为 geth console

1. 安装和运行节点

1.1 安装客户端

geth 的安装教程请参见 Building Ethereum

1.2 同步区块

「同步」的意思是把网络上的区块全部下载到本地,以同步到网络的最新状态。使用客户端前必须先同步区块。

同步命令如下

  • geth:全节点模式同步模式
  • geth --fast --cache=1024--fast 快速同步模式,只下载状态(state downloads)

更优雅的同步方式

  • geth --fast console 2>network_sync.log:同步时把输出日志重定向到network_sync.log中,并进入控制台。这样就可以边同步边使用控制台命令。
  • tail -f network_sync.log 可以重新浏览到日志

数据存放目录

主网络区块数据的默认存放目录是 ~/Library/Ethereum(Mac OS X)

  • 其他系统下,可用该方式找到默认路径:geth -h 后搜索 --datadir,后面跟着的就是默认目录
  • 如果你想将区块下载到其他目录,可以使用命令 geth --fast --datadir "<path>"

不同客户端是可以共用区块数据的。用 geth 同步的区块数据,可以在 Ethereum Wallet 或 Mist 客户端里直接使用。

导入已有的区块文件

如果本地已有区块文件,可以将其导入

  • geth export filename:导出区块文件
  • geth import filename:导入区块文件

启动节点

  • geth 借助启动节点(bootstrap nodes)来初始化寻找过程。启动节点被写在源码里,但可用这些方式修改
    • geth --bootnodes "enode://pubkey1@ip1:port1 enode://pubkey2@ip2:port2 enode://pubkey3@ip3:port3"pubkeyipport 依次为启动节点的公钥地址、ip 和端口号。
    • > admin.addPeer("enode://pubkey@ip:port")
  • geth 使用名为 discover protocol 的协议来寻找其他节点

1.3 启动客户端

启动客户端的方式如下

  • 主网络
    • 如果区块数据在默认目录下:geth
    • 如果区块数据在其他目录下:geth --datadir <path>
  • 测试网络:geth --datadir <path> --networkid 15。你只会连接与你的协议版本和 networkid 都相同的节点。主网络的 networkid 是 1,所以 networkid 只要不是 1 就可以

更常用的是启动客户端,并进入控制台模式:geth --datadir <path> console 2>console.log。同时可以另开窗口,用 tail -f console.log 浏览日志。

更复杂的启动命令

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

  • identity "MyNodeName":为你的节点设置身份标识,以更容易在节点列表中便是
  • --rpc:开启 RPC 接口
  • --rpcport "8080":RPC 端口
  • --rpccorsdomain "*":设置能连接到你的节点的 URL,用来完成 RPC 任务。* 指任何 URL 都能连接到你。
  • --datadir "./ethdev":区块数据文件夹
  • --port "30303":用来监听其他节点的端口
  • --nodiscover:你的节点不会被其他人发现,除非他们手动添加你
  • --rpcapi "db,eth,net,web3":提供给别人使用的 RPC API,默认为 web3 接口
  • networkid 1999:相同 networkdid 才会连接到一起

监控

在控制台里,使用这些命令检查连接状态

  • > net.listening:检查是否连接
  • > net.peerCount:连接到的节点个数
  • > admin.peers:返回连接到的节点的详细信息
  • > admin.nodeInfo:返回本地节点的详细信息

此外,还有一些网站供你查看以太坊主网络的状态

1.4 测试网络

以太坊公有的测试网络有 Ropsten 和 Rinkeby。除此之外,你可以搭建自己的私有网络,即只能本地访问的私网。

下面介绍 3 种测试网络的搭建方式

Ropsten网络

  • 同步区块:geth --testnet --fast --bootnodes "enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303,enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303",来连接特殊的启动节点来同步 Ropsten 网络的数区块。或者也可以使用 Mist 进行同步。
  • 进入网络:geth --testnetwork

参考:ethereum/ropsten

Rinkeby网络

参见 Rinkeby: Ethereum Testnet - Connect Yourself,有 archive, full, light, embedded 4种模式

私有网络

搭建私有网络,需要先新建创世块参数文件 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 初始化测试网络,并进入控制台。

该测试网络只有你一个人,你需要自己挖坑来记录交易。

参考

2. 账户管理

2.1 创建账户

新建账户

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

导入账户

  • geth account import <keyfile>,私钥以明文方式写在文件 keyfile 里,每个一行。
    • <keyfile> 是存着账户信息的 json 文件,其中私钥是被账号密码(password)加密后,存放在里面的
    • 不同平台的 keyfile 默认存储位置是不同的
      • Windows: C:\Users\username%appdata%\Roaming\Ethereum\keystore
      • Linux: ~/.ethereum/keystore
      • Mac: ~/Library/Ethereum/keystore
  • 结合 --password,可以使用在导入账户时设置密码:geth --password <passwordfile> account import <keyfile>

修改密码

  • geth account update <address>
  • geth account update 2:2是账户的编号,在 geth account list 中可以看到

2.2 导入钱包

  • geth wallet import <etherwallet.json>

创建多签名钱包,请参见Creating a Multi-Signature Wallet in Mist

2.3 查看账户信息

列出所有账户

  • geth account list;对应的控制台命令为 > 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++; 
})
};

小技巧:可以把代码存到文件中。进入 geth 的控制台后,用 > loadScript(<loadfile.js>) 导入文件中的函数。

2.4 发送交易

以发起一个 0.01 个 ether 的转账交易为例

> 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` 不是必须的

之后会让你输入密码

或者也可以先用 personal.unlockAccount(sender, <passphrase>) 解锁账户,再发送交易。

3. 挖矿

3.1 介绍

挖矿会有挖矿奖励

以太坊使用 Ethash 的 PoW 算法

3.2 CPU 挖矿

挖矿

  • geth --mine --minerthreads=4--minerthreads 设置并行挖矿的线程数量,默认为所有的处理器数量;
  • > miner.star(8),使用 8 个 minerthreads;>miner.stop() 停止
  • 稍微复杂点的挖矿命令:geth --mine --minerthreads 4 --datadir /usr/local/share/ethereum/30303 --port 30303:使用不同数据目录(ethereum/30303)和不同的端口(30303)挖矿

发放奖励:eth.etherbase(也叫 coinbase)是一个地址,挖矿奖励会发到这个地址里。改变 etherbase 的方式如下

  • geth --etherbase 1 --mine:改变 etherbase 为编号1的地址;或 geth --etherbase '0xa4d8e9cae4d04b093aac82e6cd355b6b963fb7ff'
  • 控制台:> miner.setEtherbase(eth.accounts[2])

其他

  • > eth.getBlock(i).miner:查看块的挖出者
  • eth.getBlockTransactionCount("pending"):查看未确认交易的数量
  • eth.getBlock("pending", true).transactions:查看所有未确认交易

下面的函数可以统计不同地址的出块数量

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 是一种无状态、轻量级的 RPC 协议,规定了通信的数据结构和规则。以太坊客户端使用 JSON-RPC 和其他客户端通信。

比如 MetaMask 钱包就是通过 JSON-RPC 和以太坊客户端进行通信的。

对于不同的以太坊客户端,默认 JSON-RPC 地址如下

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

常用命令

  • geth --rpc:开启 HTTP JSON-RPC
  • geth --rpc --rpcaddr <ip> --rpcport <portnumber>:改变 JSON-RPC 的 ip 和端口
  • 控制台下:> admin.startRPC(addr, port)

细节请参阅 JSON RPC

4.3 使用 Ðapp 的 JavaScript API

你可以使用 web3.js 库所提供的对象,来搭建 Ðapp。

细节请参阅 JavaScript API

4.4 JavaScript 控制台

一般操作都在控制台模式下进行。

  • 进入控制台的方式:geth console
  • 启动测试网络,并进入控制台:geth --datadir ./ethdev console

详细命令请参阅 JavaScript Console

5. 智能合约

5.1 对智能合约进行基本概念的介绍;后面的小节依次介绍合约的编写、编译和部署。

参考

5.1 介绍

5.1.1 账户

以太坊有 2 种账户

  • 外部账户(Externally owned account,EOA)
    • 有 ether 余额
    • 能发送交易(转账或触发合约)
    • 被私钥控制,即人类直接掌管的账户
    • 没有代码
  • 合约(Contract)
    • 有 ether 余额
    • 内部有代码
    • EOA 或其他交易发来的消息可以触发代码执行
    • 图灵完备,且有持久性的存储,即它自身有持久性的状态。
    • 可以调用其他合约

当合约收到交易时,以太坊虚拟机(EVM)会根据它收到的参数,来执行内部的代码。

5.1.2 交易与消息

交易(Transaction):EOA发送给其他账户(EOA或合约)的签名过的消息

消息(Message):合约发给其他合约的消息

两者的不同就在于发送方的不同

5.1.3 gas

ether 是以太坊中的货币,用于支付 EVM 的计算。

以太坊中货币最小的单位是 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 供应量

Gas 被认为是网络资源的不变花费。我们希望发送每笔交易的真实成本总是保持不变,所以 Gas 不能被发行,否则价格会有波动。

反之,我们发行 ether。当 ether 价格上升时,Gas 价格就对应下降,以保持真实成本不变

参考:Ether

5.1.4 账户交互的例子

合约通常为这 4 种目的服务

  • 维护数据,这些数据通常对其他合约有用,或来表示外部世界。典型例子是货币和记录特定组织中的成员身份
  • 充当一种能执行复杂规则的 EOA,这被叫作 forwarding contact。该合约收到消息后,在满足特定情况下,转发结果给其他 EOA,比如,直到 3 个私钥中的 2 个确定,才能发送消息。
  • 管理其他合约和多用户之间的关系,典型例子是金融合约或担保。一方还可以公开合约,然后供其他人来参与。比如合约自动发送奖励给那些解决某些数学问题的人
  • 为其他合约提供函数,作为库的作用。

合约能充当不同的角色,因此我们希望合约可以多交互。下面举一个合约交互的例子

  • Alice 和 Bob 下了一个赌注,每人出 50 GavCoin,赌明年某个时候 San Francisco 的气温是不是会超过 35°,若超过,则 Bob 赢得 100 GavCoin。
  • Bob 使用一个 Forwarding Contract。Bob 会先发送消息给 Forwarding Contract,然后它再转发给其他地址;接受消息也是一样。该 Forwarding Contract 需要发送方不仅提供 ECDSA 签名,还需要提供 Lamport 签名,才能转发消息。

这个例子有 5 个合约

  • GavCoin Contract:管理 GavCoin 代币的合约
  • Bet Contract:赌注合约
  • Weather Contract:查询天气的合约
  • Forwarding Contract:Bob 的转发合约
  • Lamport Contract:提供 Lamport 签名基本操作的合约

Alice 完成赌注,需要这么做

  1. Alice 发送消息给 Bet Contract,其消息是「接受赌约,并把自己的 50 个 GavCoin 存到 Bet Contract 的账户下」
  2. Bet Contract 发送消息给 GavCoin Contract,把 Alice 的 50 个 GavCoin 存到自己的账户(即合约地址)里

Bob 想完成赌注,需要这么做

  1. Bob 的 EOA 发送消息给 Forwarding Contract,该消息包含了 Bob 的 EOA 的 ECDSA 和 Lamport 签名,以及消息「接受赌约,并把自己的 50 个 GavCoin 存到 Bet Contract 的账户下」。
  2. Forwarding Contract 发送消息给 Lamport Contract,要求验证 Lamport 签名
  3. 若验签成功,Lamport Contract 返回 1 给 Forwarding Contract。Forwarding Contract 发送消息给 Bet Contract
  4. Bet Contract 发送消息给 GavCoin Contract,把 Bob 的 50 个 GavCoin 存到自己的账户(即合约地址)里

Bet Contract 将自动执行赌约

  1. Bet Contract 每隔一定周期发消息给 Weather Contract,查询 San Francisco 的当前气温
  2. 一旦气温超过 35°C,Bet Contract 发消息给 GavCoin Contract,把自己账户里的 100 GavCoin 发到 Bob 的账户里

5.2 编写合约

学习 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 编译合约

合约以「以太坊虚拟机(EVM)字节码」的形式存在与区块中,因此需要对源文件进行编译得到字节码。

有几种编译方式

5.3.1 安装 solc 编译器

下面是 Mac OS 平台上的 solc 编译器安装方式。其他平台请参考 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:开始编译

现在敲入命令 solc 就可以调用编译器了

  • Installing Solidity - npm / Node.js 里的 npm install -g solc 方式安装的是另一种编译器 solcjs
  • 由于 geth 中的 eth.compile 已被移除,因此旧版本的 admin.setSolc() 设置编译器的命令已无效

5.3.2 编译

编译后,我们会得到 2 个东西,用来部署合约:

  • .bin:二进制字节码
  • .abi(Application Binary Interface):一个 JSON 对象,用来定义接口,用来告诉别人怎么使用这个合约,就好比用户手册。

常用编译命令

  • solc --bin greeter.sol:编译得到 .bin 二进制字节码
  • sloc --abi greeter.sol:编译得到 .abi 接口(其实这里只做了解析,没有编译)
  • solc --optimize --bin greeter.sol--optimize 可优化编译。优化生成的字节码更小,部署所需的 gas 也会减少。
  • solc -o outputDirectory --bin --ast --asm greeter.sol:设置输出目录,目录 outputDirectory 会生成 greeter.bingreeter.astgreeter.asm 文件
  • solc --combined-json abi,bin,interface greeter.sol:输出一个 json 对象,里面包含 bin、abi 和 interface 信息

为了方便部署,我们一般使用这个命令

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

这将生成一个 greeter_compiled.js 文件,看起来会像这个样子(已经过格式化)

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"
}

这个文件包含了 binabi 信息。

接下来要用该文件部署合约

5.4 部署合约

进入 geth 控制台里,执行下面的命令

> loadScript('greeter_compiled.js')  // 导入编译后生成的文件
true
> var _greeting = "Hello World!!!"  // 部署该合约需要的初始化参数
undefined
> var greeterContract = web3.eth.contract(JSON.parse(compilerOutput.contracts["greeter.sol:greeter"].abi));  // 要解析成 json 对象
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);
        }
    }
  );

总结一下,可以得到部署合约的一般命令(# 开头的变量是我们需要设置的)

> var Contract = web3.eth.contract(#abi);  // abi 是一个 json 对象
undefined
// 部署一个合约实例
> var deployed  = Contract.new(#args, {from: #sender, data: "0x" + #bin, gas: 4700000},   // args:合约的构造函数所需的参数。如果有多个参数,依次用,隔开 中;sender:合约的部署者地址;bin:二进制字节码,前面要加'0x';gas:部署需要花费的 gas 量
    function (e, contract) {  // 这是个回调函数,作用是合约部署成功后,通知你一声
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
            console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
    }
  );
null [object Object]
undefined

这里只是把你的合约广播出去,只有被区块记录后,才部署成功。

私有网络下,可以自己来挖新区块

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

更方便(偷懒)的方法:使用在线编译器里 Remix - Solidity IDE

  • 把代码复制进去
  • 选择你需要的合约,点击下方的 Contract details (bytecode, interface etc.)
  • 找到 Web3 deploy,其里面的内容就是我们要在 geth 控制台里输入的 abi 和 bin

由于 1.6 之后的 geth 控制台不能使用 solidity 编译器,而官方教程 Contract Tutorial 还是老版本,没有更新,于是饶了很多弯。最后参考 #14850How to compile Solidity contracts with Geth v1.6? 才弄懂。

5.5 使用合约

先体验一下合约的使用

> gt.greet()  // gt 是 5.4 里得到的合约实例
"Hello World!!!"
> gt.address  // 合约地址
"0xdfb2938df1e4c80f309f7f09ceae871175b94a81"
> eth.getCode(gt.address)
"0x60606....."
> gt.kill.sendTransaction({from:eth.accounts[0]})  // 销毁合约。销毁后就无法使用了

这里的 .greet().kill() 函数的用法不同,是因为

  • .greet() 不会改变链上的状态,是一个调用
  • .kill() 会改变链上的状态,是一个交易

5.5.1 实例化

使用合约前,需要对其进行实例化。先前的 gt 就是一个实例。

实例化合约有 2 步

  • 第一步,知道合约的 abi:var myContract = web3.eth.contract(abi);
  • 第二步:知道合约地址,间接获得 bin:var contractInstance = myContract.at(address);
    • 执行部署命令时,也顺带会实例化合约:var contractInstance = myContract.new([constructorParam1] [, constructorParam2], {from: myAccount, data: '0x12345...', gas: 1000000});

参考:web3.eth.contract

5.5.2 调用合约中的方法

根据是否会改变网络状态,可将方法分成调用(call) 和 交易(sendTransaction) 两种类型。它们的调用方式分别如下

  • contractInstance.method.call(param1 [, param2, ...] [, transactionObject] [, defaultBlock] [, callback]);call 类型函数,不会改变网络状态。
  • contractInstance.method.sendTransaction(param1 [, param2, ...] [, transactionObject] [, callback]);sendTransaction 类型函数,会改变网络状态。

若使用 contractInstance.method(param1 [, param2, ...] [, transactionObject] [, defaultBlock] [, callback]);,则 EVM 会自动根据方法类型,来选择使用 call()sendTransaction()

参考:Contract Methods

5.5.3 Event

参考:Contract Event

5.X 其他

除了 geth 控制台,还可以用 JSON-RPC 部署合约。具体参考 Accessing Contracts and Transactions

以太坊
Web note ad 1