How to run Etherenum on CentOS7

2018-02-05 10:24:40
以太坊客户端

Etherenum主流的客户端实现有以下几种,分别是C++, Go, Python,分别对应cpp-ethereum, go-ethereum, pyethapp.
C++实现称为Eth,Go语言的实现被称为Geth,Python的实现被称为Pyethapp.客户端是指一种接入Ethereum网络的节点并且与其发生交互和更新blockchain状态

其中最常用的有 Go 语言实现的 go-ethereum 客户端 Geth,支持接入以太坊网络并成为一个完整节点,也可作为一个 HTTP-RPC 服务器对外提供 JSON-RPC 接口

Geth安装和配置

Go语言的安装和配置略,Geth可以编译安装,也可以直接下载二进制安装包,参考这里

1
2
3
git clone https://github.com/ethereum/go-ethereum
cd go-ethereum
make geth

安装完成后,可以使用 geth version 命令查看是否安装成功。记得把生成的 geth 加入到系统的环境变量中

安装 Solidity 编译器

Solidity 编译器也有多种方法安装,参照这里
官方推荐使用基于浏览器的 IDE 环境:Remix

私有链搭建
  • 配置初始状态
    运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    [root@localhost]#cat genesis.json 
    {
    "nonce": "0x0000000000000042",
    "timestamp": "0x00",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "extraData": "0x00",
    "gasLimit": "0x8000000",
    "difficulty": "0x4000",
    "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "coinbase": "0x3333333333333333333333333333333333333333",

    "config": {
    "chainId": 1984,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
    },

    "alloc":{}
    }

    #初始化创世区块
    [root@localhost]#geth --datadir "/root/.ethereum" init genesis.json
    I0131 18:57:50.858609 ethdb/database.go:83] Alloted 16MB cache and 16 file handles to /root/.ethereum/chaindata
    I0131 18:57:50.865414 cmd/geth/main.go:299] successfully wrote genesis block and/or chain rule set: f2ebfaadd3ae79075cc9485eb5ab634c9927504736c60dc88d571fb85d9f6493

    chainID 指定了独立的区块链网络 ID。网络 ID 在连接到其他节点的时候会用到,以太坊公网的网络 ID 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 ID。不同 ID 网络的节点无法相互连接。配置文件还对当前挖矿难度 difficulty、区块 Gas 消耗限制 gasLimit 等参数进行了设置
    打开此节点的命令控制台console,并新建账户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #启动私有链节点
    geth --datadir "/root/.ethereum" --port 30002 --nodiscover console
    #新建用户
    >personal.newAccount("passward")
    #开始挖矿
    >miner.start(1)
    true
    #停止挖矿
    >miner.stop()
    true
    #查看账户
    > eth.accounts
    #查看账户余额
    >eth.getBalance(eth.accounts[0])

    发送交易

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    >  eth.getBalance(eth.accounts[0])
    320000000000000300000
    > eth.getBalance(eth.accounts[1])
    0
    > personal.unlockAccount(eth.accounts[0])
    Unlock account 0x30a42e0da52f20154ce2b966a53a81099f048e73
    Passphrase:
    true
    > amount = web3.toWei(5,'ether')
    "5000000000000000000"
    > eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
    I0205 11:51:11.752411 eth/api.go:1185] Tx(0x318cf8d6f86f0f294eb927af3019cede420990beac1cf72046d03d454ff48b88) to: 0xae370f3b2af53f6ba282a76bbe6956d443bc8d79
    "0x318cf8d6f86f0f294eb927af3019cede420990beac1cf72046d03d454ff48b88"

    #此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易,可以使用 eth.getBlock("pending", true).transactions 查看当前待确认交易

    > miner.start(1);admin.sleepBlocks(1);miner.stop();

    #新区块挖出后,挖矿结束,查看账户 1 的余额,已经收到了账户 0 的以太币:
    > web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
    5

    查看交易和区块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #查看当前区块总数:
    > eth.blockNumber
    66
    #通过交易 Hash 查看交易(Hash 值包含在上面交易返回值中)
    > eth.getTransaction("0x318cf8d6f86f0f294eb927af3019cede420990beac1cf72046d03d454ff48b88")
    {
    blockHash: "0x45c8732599e45791c22a4c1d2278387eff05e6f062f8f1871912bd4fbee4787f",
    blockNumber: 65,
    from: "0x30a42e0da52f20154ce2b966a53a81099f048e73",
    gas: 90000,
    gasPrice: 20000000000,
    hash: "0x318cf8d6f86f0f294eb927af3019cede420990beac1cf72046d03d454ff48b88",
    input: "0x",
    nonce: 0,
    to: "0xae370f3b2af53f6ba282a76bbe6956d443bc8d79",
    transactionIndex: 0,
    value: 5000000000000000000
    }

    #通过区块号查看区块:
    > eth.getBlock(66)
    {
    difficulty: 131072,
    extraData: "0xd783010412844765746887676f312e372e31856c696e7578",
    gasLimit: 125836029,
    gasUsed: 0,
    hash: "0x7398a4794f1a6c24fa193b3a45e96c9b8925047a4c4ffb5112ffee182e8e05ba",
    logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    miner: "0x30a42e0da52f20154ce2b966a53a81099f048e73",
    nonce: "0x2d4f36efb89667d8",
    number: 66,
    parentHash: "0x45c8732599e45791c22a4c1d2278387eff05e6f062f8f1871912bd4fbee4787f",
    receiptRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
    size: 536,
    stateRoot: "0x50cd8603d98fe157cabc1d596e7a7926231e8dee792cabd199f263dc0c5691a5",
    timestamp: 1517802759,
    totalDifficulty: 8792593,
    transactions: [],
    transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    uncles: []
    }

    连接到其他节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #通过 admin.addPeer() 方法连接到其他节点,两个节点要要指定相同的 chainID
    查看其中一个 enode 信息:
    > admin.nodeInfo.enode
    "enode://34753757021f60aac9ad5402344ae353b9c431c3b60c8e671a753639dd5aef35bb180b8d056a5f3b1ac46fd5cda0163f88eff8e1856a1b3982cc576df81e295a@[::]:30002?discport=0"

    然后在另外节点的 JavaScript console 中执行 admin.addPeer(),就可以连接:
    admin.addPeer("enode://34753757021f60aac9ad5402344ae353b9c431c3b60c8e671a753639dd5aef35bb180b8d056a5f3b1ac46fd5cda0163f88eff8e1856a1b3982cc576df81e295a@[::]:30002?discport=0")

    #addPeer() 的参数就是节点二的 enode 信息,注意要把 enode 中的 [::] 替换成节点二的 IP 地址。
    连接成功后,节点就会开始同步另一个节点的区块,同步完成后,
    任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易

    除了上面的方法,也可以在启动节点的时候指定 –bootnodes 选项连接到其他节点

智能合约操作
  • 创建和编译智能合约
    新建一个 Solidity 智能合约文件,命名为 test.sol,该合约包含一个方法 multiply(),将输入的两个数相乘后输出:

    1
    2
    3
    4
    5
    6
    7
    8
    pragma solidity ^0.4.0;
    contract Test
    {
    function multiply(uint a, uint b) returns (uint)
    {
    return a * b;
    }
    }

    编译智能合约,获得编译后的 EVM 二进制码:

    1
    solc --bin test.sol

    再用 solc 获取智能合约的 JSON ABI(Application Binary Interface),其中指定了合约接口,包括可调用的合约方法、变量、事件等

    1
    solc --abi test.sol

    回到 Geth 的控制台,用变量 code 和 abi 记录上面两个值,注意在 code 前加上 0x 前缀:

    1
    2
    > code = "0x6060604052341561000f57600080fd5b5b60b48061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063165c4a1614603d575b600080fd5b3415604757600080fd5b60646004808035906020019091908035906020019091905050607a565b6040518082815260200191505060405180910390f35b600081830290505b929150505600a165627a7a72305820b494a4b3879b3810accf64d4cc3e1be55f2f4a86f49590b8a9b8d7009090a5d30029"
    > abi = [{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"multiply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]
  • 部署智能合约
    这里使用账户 0 来部署合约,首先解锁账户:

    1
    2
    3
    4
    >  personal.unlockAccount(eth.accounts[0])
    Unlock account 0x30a42e0da52f20154ce2b966a53a81099f048e73
    Passphrase:
    true
  • 发送部署合约的交易

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    > myContract = eth.contract(abi)
    ...
    > contract = myContract.new({from:eth.accounts[0],data:code,gas:1000000})

    INFO [09-12|08:05:19] Submitted contract creation fullhash=0x0a7dfa9cac7ef836a72ed1d5bbfa65c0220347cde4efb067a0b03b15fb70bce1 contract=0x7cbe4019e993f9922b8233502d94890099ee59e6
    {
    abi: [{
    constant: false,
    inputs: [{...}, {...}],
    name: "multiply",
    outputs: [{...}],
    payable: false,
    stateMutability: "nonpayable",
    type: "function"
    }],
    address: undefined,
    transactionHash: "0x0a7dfa9cac7ef836a72ed1d5bbfa65c0220347cde4efb067a0b03b15fb70bce1"
    }

    此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易。可以查看当前待确认的交易,使用 miner.start() 命令开始挖矿,一段时间后交易会被确认,随新区块进入区块链

  • 调用智能合约

    1
    > contract.multiply.sendTransaction(2, 4, {from:eth.accounts[0]})

    如果只是本地运行该方法查看返回结果,可以采用如下方式:

    1
    2
    > contract.multiply.call(2,4)
    8
安装ethereum钱包

钱包下载地址,参考这里
钱包在启动时默认在本机查找的IPC路径,钱包启动后能自动识别到你的私有链
OS的默认查找路径如下,具体可查看这里

1
2
3
4
windows: .\pipe\geth.ipc
linux: /.ethereum/geth.ipc
freebsd: /.ethereum/geth.ipc
sunos: /.ethereum/geth.ipc

ref
Private network
ethereum/cpp-ethereum
Go Ethereum
Installing binaries
以太坊私有链搭建指南
利用puppeth搭建POA共识的以太坊私链网络


您的鼓励是我写作最大的动力

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。