第四题 搭建以太坊私有链环境

主要参考文档

https://www.jianshu.com/p/e978535b1002
默认操作目录为 /opt/module

准备步骤

  1. 虚拟机安装Ubuntu(省略. 建议分配2G以上内存, 以下以Ubuntu 14.04.6 LTS 为例)
    更换更新源(省略, 可选, 加速安装过程)
    设置静态IP, 安装sshd
sudo apt-get update
sudo apt-get install openssh-server
  1. 安装nodejs, npm
    参考nodejs官网 https://nodejs.org/en/download/
    配置环境变量
ubuntu@ubuntu:~$ node -v
v8.16.0
ubuntu@ubuntu:~$ npm -v
6.4.1
  1. 安装go-ethererum
    sudo apt-get install software-properties-common
    sudo add-apt-repository -y ppa:ethereum/ethereum
    sudo apt-get update
    sudo apt-get install ethereum
  2. 创建私有链genesis.json
    创建Ethererum目录
mdkir Ethererum
cd Ethererum
vim genesis.json
{
    "nonce":"0x0000000000000042",
    "mixhash":"0x0000000000000000000000000000000000000000000000000000000000000000",
    "difficulty": "0x4000",
    "alloc": {},
    "coinbase":"0x0000000000000000000000000000000000000000",
    "timestamp": "0x00",
    "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
    "extraData": "0x",
     "config":{  
      "chainId":15,  
      "homesteadBlock":0,  
      "eip155Block":0,  
      "eip158Block":0  
    },  
    "gasLimit":"0xffffffff"
}
  1. 初始化
geth --datadir /opt/module/Ethererum/ init genesis.json
geth --datadir /opt/module/Ethererum/ --identity "YXEtherum" --rpc  --rpcport "8545" --rpccorsdomain "*" --port 30303 --rpcapi db,eth,net,web3,miner,personal --networkid 10 --nodiscover console
  1. 挖矿
INFO [01-27|11:48:34] IPC endpoint opened: ~/Ethererum/yx_data/geth.ipc
INFO [01-27|11:48:34] HTTP endpoint opened: http://127.0.0.1:8545
Welcome to the Geth JavaScript console!

instance: Geth/YXEtherum/v1.7.3-stable/darwin-amd64/go1.9.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> INFO [01-27|11:48:37] Mapped network port                      proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-IP1"

> eth.accounts // 查看当前账户
[]
> personal.newAccount() // 创建新账户
Passphrase:  // 输入密码
Repeat passphrase: // 确认密码
"0x77a4ee7daca8faecec59c52786dcac639dd6ab91"
> eth.accounts 
["0x77a4ee7daca8faecec59c52786dcac639dd6ab91"] // 账户已创建
> eth.getBalance(eth.accounts[0]) // 查看余额
0
> miner.start() // 开始挖矿
INFO [01-27|11:53:37] Updated mining threads                   threads=0
INFO [01-27|11:53:37] Transaction pool price threshold updated price=18000000000
INFO [01-27|11:53:39] Generating DAG in progress               epoch=0 percentage=0 elapsed=1.691s
INFO [01-27|11:53:41] Generating DAG in progress               epoch=0 percentage=1 elapsed=3.465s
INFO [01-27|11:53:43] Generating DAG in progress               epoch=0 percentage=2 elapsed=5.187s
...
...
INFO [01-27|12:00:55]  mined potential block                  number=3 hash=856ca2…60597c
INFO [01-27|12:00:55] Commit new mining work                   number=4 txs=0 uncles=0 elapsed=209.521µs
> miner.stop() // 挖出矿的时候,就可以停止了
true
> eth.getBalance(eth.accounts[0]) 
15000000000000000000 
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether");
15 // 账号已经有15个以太了,有以太我们就可以做交易了。
> exit 退出

部署合约

  1. 搭建环境, 安装ganache-cli
alias cnpm="npm --registry=http://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.cnpmjs.org \
--userconfig=$HOME/.cnpmrc" # 使用淘宝镜像加快安装速度
cnpm install -g ganache-cli 

2.运行ganache-cli

>ganache-cli
Ganache CLI v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x54a2d27c4b30139b538bf887982aae64cfa3c7a3
(1) 0x2d221a7ca36bcbb30ccd574104812539721ceed8
...

Private Keys
==================
(0) 930186bf51b9d503e82cc6e9a1f8adaea58b249278fcf4ca42d7635b3195057f
(1) e769615ea9103ec9f13a7bfe2f0e56b3337e0123298c78404d18c721abde3025
...

HD Wallet
==================
Mnemonic:      bar quit tobacco ring process twice kangaroo eye verify badge lazy phrase
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545 // 注意这里的地址和端口号,默认8545,和 geth 一样,如果 geth 开启着,要关闭 geth,或者启用不同的端口号。
  1. 安装truffle(以下面为准, 不要参考开头的文档, 会出现版本问题)
    tree工具也是要单独安装, 没有也不影响
>cnpm install -g [email protected] 
//如果出现错误, 可以直接clone他给出的github地址
>mkdir solidity-experiments
>cd solidity-experiments/
>truffle init
>tree
.
├── contracts
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
├── truffle-config.js

合约编译, 合并过程会以truffle-config.js为准, 修改该文件指定处:


image.png

image.png
  1. 编译发布
>truffle compile # 编译合约
>truffle migrate # 确保 ganache-cli 已开启
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x2a658b01d6...7ff
  Migrations: 0x38f1873ed43e7c6ce8a4eb78116c2bd186f6d3b0
Saving successful migration to network... # migration 发布成功
  ... 0x6451ee5b....be4

发布成功,然后我们就可以开始编写 solidity 合约,这里以 greeter demo 为例,运行

truffle create contract Greeter

  1. 编写合约
pragma solidity ^0.4.24; // 指定 solidity 版本

contract Greeter         // The contract definition. A constructor of the same name will be automatically called on contract creation. 
{
    address creator;     // At first, an empty "address"-type variable of the name "creator". Will be set in the constructor.
    string greeting;     // At first, an empty "string"-type variable of the name "greeting". Will be set in constructor and can be changed.

     // 构造函数
    function Greeter(string _greeting) public  
    {
        creator = msg.sender;
        greeting = _greeting;
    }

     // 只读函数
    function greet() constant public returns (string)          
    {
        return greeting;
    }
    
    function getBlockNumber() constant public returns (uint) // this doesn't have anything to do with the act of greeting
    {                                                   // just demonstrating return of some global variable
        return block.number;
    }
    
    // 非只读函数
    function setGreeting(string _newgreeting) public
    {
        greeting = _newgreeting;
    }
    
    // 自毁函数
    function kill() public
    { 
        if (msg.sender == creator)  // only allow this action if the account sending the signal is the creator
            selfdestruct(creator);       // kills this contract and sends remaining funds back to creator
    }

}

保存之后,编辑文件 migrations/2_deploy_contracts.js,如果没有则新建一个,文件内容如下:

var Greeter = artifacts.require("./Greeter.sol");

module.exports = function(deployer) {
  deployer.deploy(Greeter,"Hello World");
};

然后重新编译、部署:

>truffle compile # 编译
Compiling ./contracts/Greeter.sol...
Compiling ./contracts/greeter.sol...
Writing artifacts to ./build/contracts // 编译后的 json 文件存放在此目录下。

>truffle migrate --reset # 发布
Compiling ./contracts/Greeter.sol...
Writing artifacts to ./build/contracts

Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0x9eca7628a5a47....c4e
  Migrations: 0xfae368dec758a241f610333cdbd2944009fe23be
Saving successful migration to network...
  ... 0x74406ca1....ce5
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Replacing Greeter...
  ... 0x081c7a46529....3dd
  Greeter: 0xb0d9df51125b0b7d58ec06f1af9e957dff7314ac # Greeter 合约地址
Saving successful migration to network...
  ... 0x2e2396da9....764
Saving artifacts...

Greeter 合约部署完成,Greeter 的地址为 0xb0d9df51125b0b7d58ec06f1af9e957dff7314ac,可通过这个地址和合约进行交互,运行 truffle console

>truffle console
truffle(development)> var greeter = Greeter.at(Greeter.address)
undefined
truffle(development)> greeter.address
'0x52e4b9ecd911843e5c2550df6458374480e5483d'
truffle(development)> greeter.greet()
'Hello World!'
truffle(development)> greeter.setGreeting('Hello World!!!')
{ tx: '0xa6e0c1f376c005647d1e522740085979d710fceb3bb73883e8452c79274826d7',
  receipt: 
   { transactionHash: '0xa6e0c1f376c005647d1e522740085979d710fceb3bb73883e8452c79274826d7',
     transactionIndex: 0,
     blockHash: '0x9c0ccff9d47c894ef79fb13a507d37bbdd5f49d8ef7858c8b6dc20f34c7ed166',
     blockNumber: 9,
     from: '0x091127ffb8ecb383b80caca87f17dbfba7fed05c',
     to: '0x52e4b9ecd911843e5c2550df6458374480e5483d',
     gasUsed: 33688,
     cumulativeGasUsed: 33688,
     contractAddress: null,
     logs: [],
     status: '0x1',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     v: '0x1b',
     r: '0xa4c72524e7d107e6a04cd6f55c4988f1d1a191cc1d05c0f72c43eb035fa0efb0',
     s: '0x25151b7fbfc30636ee1c96f45d7b288efdecb8fa3ccf318d2c3f377b7919836d' },
  logs: [] }
truffle(development)> greeter.greet()
'Hello World!!!'
  1. 发布合约到私有链
    退出 ganache-cli,运行
geth --datadir /opt/module/Ethererum/ --identity "YXEtherum" --rpc --rpcport "8545" --rpccorsdomain "*" --port 30303 --rpcapi "db,eth,net,web3" --networkid 10 --nodiscover --ethash.dagdir /opt/module/Ethererum/Ethash console
// 解锁账号
> personal.unlockAccount(eth.accounts[0], "yourpassword", 24*3600)
true
// 必须开启挖矿,否则合约无法提交、保存到区块链中。因为我们创建的私链中并没有其他节点处理交易。
> miner.start() 

然后在另一个窗口中运行 truffle migrate --reset,看到以下类似信息说明合约已成功部署。(要等到挖到矿以后才能部署, 耐心等待 :-D )

Running migration: 2_deploy_contracts.js
  Deploying Greeter...
  ... 0xcafdaaf58....f70
  Greeter: 0x53410cc....69e # 合约地址
Saving successful migration to network...
  ... 0x63026be34589....eea

如何在私有链中查看合约? 拷贝 truffle compile 编译后的 build/contracts/Greeter.json 文件中的 abi 串,

可以去https://www.json.cn/# 将json串压缩一下

image.png

然后在 geth console 窗口中,执行以下命令

> var abi = JSON.parse('[{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newgreeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]')
undefined
> var greeter = eth.contract(abi).at('0xb0d9df51125b0b7d58ec06f1af9e957dff7314ac')//这里的0xb0d9df51125b0b7d58ec06f1af9e957dff7314ac是合约发布的地址
undefined
> greeter.greet()
"Hello World!"

运行截图:


image.png

其他参考文档

  • go-ethererum Installation Instructions for Ubuntu
    https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Ubuntu
  • 在线合约IDE: remix
    http://remix.ethereum.org/#optimize=true&version=soljson-v0.4.24+commit.e67f0147.js
  • 以太坊智能合约 Hello World 示例程序
    https://blog.csdn.net/CSDN_AF/article/details/77963841
  • 以太坊 Truffle 框架如何修改 solidity 版本
    https://yq.aliyun.com/articles/703235
  • Truffle Smart Contract Error: Invalid number of parameter
    https://stackoverflow.com/questions/54304214/truffle-smart-contract-error-invalid-number-of-parameter
  • 以太坊智能合约 Solidity 的 0.5版本介绍
    https://blog.csdn.net/u012310362/article/details/83058198
  • Full Stack Hello World Voting Ethereum Dapp Tutorial — Part 1
    https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2

你可能感兴趣的:(第四题 搭建以太坊私有链环境)