Go-ethereum客户端通常被称为Geth,它是个命令行界面,执行在Go上实现的完整以太坊节点。Geth得益于Go语言的多平台特性,支持在多个平台上使用(比如Windows、Linux、Mac)。Geth是以太坊协议的具体落地实现,通过Geth,你可以实现以太坊的各种功能,如账户的新建编辑删除,开启挖矿,ether币的转移,智能合约的部署和执行等等。所以,我们选择 geth工具来进行开发。
Mac 中安装geth如下:
brew tap ethereum/ethereum brew install ethereum
检查是否安装成功:
geth --help
如果输出一些帮助命令,则安装成功。
以太坊支持自定义创世区块,要运行私有链,我们就需要定义自己的创世区块,创世区块信息写在一个json格式的配置文件中。
json文件内容如下:
{ "config": { "chainId": 10, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x20000", "extraData" : "", "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000000042", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00" }
准备好创世区块json配置文件后,需要初始化区块链,将上面的创世区块信息写入到区块链中。首先要新建一个目录data0用来存放区块链数据(其实,这个目录data0就相当于一个根节点。当我们基于genesis.json生成根节点后,其他人就可以来连接此根节点,从而能进行交易)。data0目录结构如图所示:
接下来进入privatechain目录中,执行初始化命令:
cd privatechain geth --datadir data0 init genesis.json
上面的命令的主体是 geth init,表示初始化区块链,命令可以带有选项和参数,其中–datadir选项后面跟一个目录名,这里为 data0,表示指定数据存放目录为 data0, genesis.json是init命令的参数。
运行上面的命令,会读取genesis.json文件,根据其中的内容,将创世区块写入到区块链中。如果看到log信息中含有Successfully wrote genesis state
字样,说明初始化成功。
初始化成功后的目录如下:
其中geth/chaindata中存放的是区块数据,keystore中存放的是账户数据。
初始化完成后,就有了一条自己的私有链,之后就可以启动自己的私有链节点并做一些操作,在终端中输入以下命令即可启动节点:
geth --datadir data0 --networkid 1008 console
上面命令的主体是geth console,表示启动节点并进入交互式控制台,–datadir选项指定使用data0作为数据目录,–networkid选项后面跟一个数字,这里是1008,表示指定这个私有链的网络id为1008。网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id。
运行上面的命令后,就启动了区块链节点并进入了Javascript Console:
这是一个交互式的Javascript执行环境,在这里面可以执行Javascript代码,其中>是命令提示符。在这个环境里也内置了一些用来操作以太坊的Javascript对象,可以直接使用这些对象。这些对象主要包括:
eth:包含一些跟操作区块链相关的方法 net:包含以下查看p2p网络状态的方法 admin:包含一些与管理节点相关的方法 miner:包含启动&停止挖矿的一些方法 personal:主要包含一些管理账户的方法 txpool:包含一些查看交易内存池的方法
进入以太坊Javascript Console后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。接下来介绍几个常用功能,下面的操作中,前面带>的表示在Javascript Console中执行的命令。
前面只是搭建了私有链,并没有自己的账户,可以在js console中输入eth.accounts来验证:
> eth.accounts []
此时没有账户,接下来使用personal对象来创建一个账户:
> personal.newAccount() > Passphrase: > Repeat passphrase: "0x67346d5619ae5747eb69629c8b1c8cbae71111c3"
Passphrase其实就是密码的意思,输入两次密码后,就创建了一个账户。再次执行命令:
> personal.newAccount() > Passphrase: > Repeat passphrase: "0xa48dba91281ee727c2424c85400f51f7a22716de"
这时候再去看账户,就有两个了。
> eth.accounts ["0x67346d5619ae5747eb69629c8b1c8cbae71111c3", "0xa48dba91281ee727c2424c85400f51f7a22716de"]
账户默认会保存在数据目录的keystore文件夹中。查看目录结构,发现data0/keystore中多了两个文件,这两个文件就对应刚才创建的两个账户,这是json格式的文本文件,可以打开查看,里面存的是私钥经过密码加密后的信息。
> eth.getBalance(eth.accounts[0]) 0 > eth.getBalance(eth.accounts[1]) 0
> miner.start(10)
其中start的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的DAG文件,这个过程有点慢,等进度达到100%后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。
如果想停止挖矿,并且进度已经达到100%之后,可以在js console中输入
> miner.stop()
挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:
> eth.coinbase "0x67346d5619ae5747eb69629c8b1c8cbae71111c3"
getBalance()返回值的单位是wei,wei是以太币的最小单位,1个以太币=10的18次方个wei。要查看有多少个以太币,可以用web3.fromWei()将返回值换算成以太币:
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
这里报错了,原因是账户每隔一段时间就会被锁住,要发送交易,必须先解锁账户,由于我们要从账户0发送交易,所以要解锁账户0:
> personal.unlockAccount(eth.accounts[0])
输入创建账户时设置的密码,就可以成功解锁账户。然后再发送交易。
我们去查看账户1中的余额,发现还没转过去,此时交易已经提交到区块链,但还未被处理,这可以通过查看txpool来验证:
> txpool.status { pending: 1, queued: 0 }
其中有一条pending的交易,pending表示已提交但还未被处理的交易。
要使交易被处理,必须要挖矿。这里我们启动挖矿,然后等待挖到一个区块之后就停止挖矿:
> miner.start(1);admin.sleepBlocks(1);miner.stop();
当miner.stop()返回true后,txpool中pending的交易数量应该为0了,说明交易已经被处理了,而账户1应该收到币了:
> web3.fromWei(eth.getBalance(eth.accounts[1]),'ether') 10
eth对象封装了查看交易和区块信息的方法。
查看当前区块总数:
> eth.blockNumber
在私有网络中建立多个节点组成的集群,并互相发现,产生交易。
如果是为了在本地网络运行多个以太坊节点的实例,必须确保一下几点:
1. 每个实例都有独立的数据目录(--datadir)
2. 每个实例运行都有独立的端口.(eth和rpc两者都是)(--port 和 --rpcprot)
3. 在集群的情况下, 实例之间都必须要知道彼此.
4. 唯一的ipc通信端点,或者禁用ipc.
在这里,我们选择在不同的电脑上分别建立节点组成集群,互相发现,产生交易。
在第一台电脑上启动节点
geth --datadir data0 --networkid 1008 console
在第二台电脑上初始化并启动节点,注意网络ID要相同,确保节点都在一个私有网络中。
geth --datadir ./data/01 init ./genesis.json 初始化 注意和第一个节点使用相同的创始快json文件
geth --datadir data0 --networkid 1008 --bootnodes "enode://b86d51f0737672b80c9000caa706955b2a94286f8593774aab40c86cbb4fc871ce6853172871eef24bced4e6fa4576fb9d8fadfd0893b3f0873eddb9eb2072f2@192.168.6.150:30303" console
bootnodes后边的参数是第一台电脑上节点的enode url 可以在第一台电脑上通过
>admin.nodeInfo.enode
来获取。
@192.168.6.150:30303 是第一台电脑的IP地址:192.168.150 以及ports:30303
本节点的ports可以通过
admin.nodeInfo
来获取。
至此两个节点应该就链接成功了。
> eth.sendTransaction({from: "0x5fba50fce50baf0b8a7314200ba46336958ac97e", to: "0x0a8c35653d8b229c16f0c9ce6f63cffb877cfdcf", value: web3.toWei(1, "ether")})
from中填当前节点的账号地址,to中填要发送给的节点的账号地址。
执行以上命令发送一个以太币。注意发送交易操作前要先解锁账号:
> personal.unlockAccount(eth.accounts[0], "passwoed")
然后执行挖矿太能让交易生效。
> miner.start()
Greeter是一个智能数字化实体,它存在于区块链中,并能够根据其输入与任何与之交互的人进行对话。它可能不是一个说话者,但它是一个很好的倾听者。这是它的代码:
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) selfdestruct(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;
}
}
你会注意到这个代码中有两个不同的合约:“Mortal”和“Greeter”。这是因为Solidity(我们使用的高级合同语言)具有继承性,这意味着一个合约可以继承另一个合约的特征。这对简化编码非常有用,因为合约的通用特征不需要每次重写,并且所有合约都可以用更小,更易读的块编写。所以通过只声明Greeter是Mortal的,你就继承了来自“Mortal”合约的所有特征,并使Greeter代码简单易读。
继承特征的“Mortal”仅仅意味着Greeter合同可以被其所有者杀死,以清理区块链并在不再需要合同时收回锁定在其中的资金。以太坊中的契约默认为不朽,并且没有所有者,这意味着一旦部署,作者就没有特殊的特权了。部署前请考虑这一点。
在部署合同前,需要准备两样东西:
1.编译后的代码。
2.应用程序二进制接口(ABI),它是一个定义如何与合约进行交互的JavaScript对象
我们可以通过使用Solidity编译器来获得这两者。(在线编译器remix的地址:点击打开链接)
将源代码(位于本页顶部)复制到Remix,它应该自动编译您的代码。 您可以放心地忽略右侧任何黄色警告框。
要访问已编译的代码,请确保右窗格中的下拉菜单已选中Greeter。 然后直接点击下拉菜单右边的details按钮。 在弹出窗口中,向下滚动并复制WEB3DEPLOY文本框中的所有代码。
在计算机上创建一个临时文本文件并粘贴该代码。 确保将第一行更改为如下所示:
var _greeting = "Hello World!"
现在,可以将结果文本粘贴到geth窗口中,或者使用loadScript(“yourFilename.js”)导入文件。
然后执行挖矿。
等待一会终端中会看到类似这样的消息:
您可能必须先使用您在开始时选择的密码来“解锁”发送交易的帐户,因为您需要支付部署合同的GAS费用。
personal.unlockAccount(web3.eth.accounts [0],“yourPassword”)
您可以使用以下命令来验证已部署的代码(将被编译):
eth.getCode(greeter.address)
如果它返回“0x”以外的任何内容,那么恭喜! 你的小家伙活着! 如果再次创建合同(通过执行另一个eth.sendTransaction),它将发布到新地址。
为了召唤你的机器人,只需在终端上输入以下命令:
greeter.greet();
由于这个调用没引起在区块链上的任何变化,因此它会立即返回并且无需任何燃气费用。 你应该看到它返回你的问候语:
'Hello World!'
greeter.address;
得到类似下图的一个合约地址:
为了得到ABI,您需要直接从Remix复制ABI,类似于复制WEB3DEPLOY编译代码的方式。 在右侧窗格中,单击详细信息按钮并向下滚动到ABI文本框。 点击复制按钮复制整个ABI,然后将其粘贴到临时文本文档中。
然后,您可以实例化一个可用于在连接到网络的任何计算机上调用合同的JavaScript对象。 在以下行中,替换ABI(一个数组)和Address(一个字符串)以在JavaScript中创建合同对象:
var greeter = eth.contract(ABI).at(Address);
这样别的节点就也可以通过
greeter.greet();
调用你的机器人了。结果显示:
'Hello World!'