Windows搭建私有链,创建部署Hello world智能合约

2018-07-11  本文已影响0人  卡累利阿派

环境:Windows 10 64 位
注意:本文涉及的很多网站可能都要科学上网,所以附上了百度网盘下载链接。


Phase 1 : 私有链搭建

1 环境搭建

1.1 安装Node.js

可以直接在Node.js官方下载,下载后直接安装即可。安装完毕,打开控制台窗口,可以查看是否安装成功。

打开控制台的方法有两种:
①在win10环境下,直接在系统右下角的搜索栏输入cmd回车;
②同时按住win+R键,输入cmd回车打开。

C:\Users\aby>node -v
v8.11.3

百度网盘
链接:https://pan.baidu.com/s/1RcSREiv3Vguyw5CaxawtGQ
密码:5vzm

1.2 安装Geth

Geth是一个以太坊客户端,可以直接在Go Ethereum官方下载,下载后直接安装即可。安装完毕,打开控制台窗口,可以查看是否安装成功。

C:\Users\aby>geth -v
Geth
Version: 1.8.3-stable
Git Commit: 329ac18ef617d0238f71637bffe78f028b0f13f7
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.10
Operating System: windows
GOPATH=
GOROOT=C:\go

百度网盘
链接:https://pan.baidu.com/s/11rmMCsNXZXiQZaOl3eIUdw
密码:3mvd

1.3 安装Solidity

新手使用Remix -Solidity IDE来进行开发。
Remix 是一个基于浏览器的Solidity,就可以不用安装Solidity,本文的Hello World教程也将基于Remix Solidity IDE来进行。
如果想安装,可以参考Solidity安装指南,也可以查看网上的另一篇文章:搭建智能合约开发环境Remix IDE及使用

1.4 安装Ethereum Wallet

可以官网下载Ethereum Wallet

百度网盘
方法一:直接解压缩
链接:https://pan.baidu.com/s/1sublDw6v-v8sdTsNdpsatw
密码:a4t1
方法二:setup安装软件
链接:https://pan.baidu.com/s/1qg3Rl5hiT3s3VzB0_OcxJg
密码:tx8a

2 启动环境,搭建私有链

2.1 配置创世区块

搭建私有链前需要创建一个 json 配置文件,用于初始化创世区块。这里自己将创建一个名为 genesis.json的文件。
在这里给出修正过的方法,后面也会介绍我在网上搜索各种资源时碰到的错误,比如发送交易会提示Insufficient funds for gas * price + value错误,是因为创始区块genesis.json没有配置好,chainId字段配置太小,当时学习网上的写的15,0也不可以,后来改成了1000。extraData字段同样经常报错,将它置为空就可以了。

{
  "config": {
        "chainId": 1000,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x400",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00",
  "alloc": {}
}

2.2 初始化创世区块

geth --datadir "D:\chain" init genesis.json

2.2 初始化私有链节点

直接进入D:\chain文件夹,同时按住Shift鼠标右键,点击在此处打开Power shell窗口
利用gethinit命令初始化一个以太坊(开发者)网络节点。

geth --datadir testNet --dev --dev.period 1 console 2>> test.log

原文使用geth --datadir testNet --dev console 2>> test.log
注意:发布合约必须要进行挖矿才行,只有发送交易才会自动挖矿。

  • --dev 启用开发者网络(模式),开发者网络会使用POA共识,默认预分配一个开发者账户并且会自动开启挖矿。这是原来的版本,由于geth版本更新之后,--dev模式下新增了一个参数项:
--dev               Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
--dev.period value  Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)

我们先看一下上面的两个参数,--dev是我们常用的参数,之前版本中我们只用使用--dev然后执行miner.start()就可以挖矿,但是在后面的版本中,当我们会发现只有发送交易了才会挖一个块。
引起此问题的原因就是新增了--dev.period value配置项。此配置默认值为0,也就是说只有有pending中的交易才会挖矿。
明白了这个参数的含义之后,解决问题就很简答了,之前的–dev参数依旧使用,然后再在后面添加--dev.period 1,注意,参数值为1,不是默认的0。
再重新启动节点,然后执行挖矿,先不管返回是否是null,执行之后,无论查看日志或执行eth.blockNumber都会发现块在不停的增高。

  • --datadir后面的参数是区块数据及秘钥存放目录。
    第一次输入命令后,它会放在当前"D:\chain"目录下新建一个testNet目录来存放数据。
  • console 进入控制台。
  • 2>> test.log 表示把控制台日志输出到test.log文件,文件名字可以随意。

为了更好的理解,建议新开一个命令行终端,实时显示日志:

tail -f test.log

2.3 准备账户

部署智能合约需要一个外部账户,我们先来看看分配的开发者账户,在控制台使用以下(仍然在D:\chain文件夹下操作)命令查看当前区块:(如果不像上面所述的加上--dev.period 1,区块数不会变化,只有转账等交易一次,才会挖一个区块)

> eth.blockNumber
45
> eth.accounts
["0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655"]

也可以使用personal.listAccounts查看账户
再查看当前账户里的余额,使用命令如下:

> acc0 = eth.accounts[0]
"0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655"
> eth.getBalance(acc0)
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77

也可以直接用地址:

> eth.getBalance(eth.accounts[0])
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77

eth.accounts[0]表示账户列表第一个账户
回车后,可以看到大量的余额,如:1.15792089237316195423570985008687907853269… e+77

开发者账户因余额太多,如果用这个账户来部署合约时会无法看到余额变化,为了更好的体验完整的过程,这里选择创建一个新的账户。

创建账户

使用如下命令:

> personal.newAccount("aby")
"0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"
> eth.accounts
["0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655", "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"]
> acc1 = eth.accounts[1]
"0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"
> eth.blockNumber
199

可以看到当前账户数组有两个账户,新账户在第二个(索引为1)位置。
我们查看账户1的余额,回车后返回值为0:

> eth.getBalance(eth.accounts[1])   // 可以写成eth.getBalance(acc1)
0

给新帐户转账

账户必须要有ether才能进行交易和部署智能合约,因此从默认账户转10以太币给新账户,使用以下命令(请使用你自己eth.accounts对应输出的账户):

> eth.sendTransaction({from: acc0 , to: acc1, value: web3.toWei(10,"ether")})
"0xc291fea6261056145a416d968a32024490cd6df6520ebe202fe4737f900ad050"
> eth.getBalance(acc1)
10000000000000000000

也可以写成

> eth.sendTransaction({from: "0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655" , to: "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6", value: web3.toWei(10,"ether")})
"0xc291fea6261056145a416d968a32024490cd6df6520ebe202fe4737f900ad050"
> eth.getBalance(acc1)
10000000000000000000

可以看到账户1的余额变成了10ether。

Phase 2 : 创建部署智能合约

3 编写智能合约

现在我们来开始编写第一个智能合约代码,solidity代码如下:

pragma solidity ^0.4.0;
contract hello {
    string greeting;
    
    function hello(string _greeting) public {
        greeting = _greeting;
    }

    function say()constant public returns (string) {
        return greeting;
    }
}

简单解释下,我们定义了一个名为hello的合约,在合约初始化时保存了一个字符串(我们会传入hello world),每次调用say返回字符串。

把这段代码写(拷贝)到Browser-Solidity,记住勾选Auto compile如果没有错误,点击右边的Details获取部署代码。

在弹出的对话框中找到WEB3DEPLOY部分,点拷贝,粘贴到编辑器后。
此处要修改两个地方:
(1)修改初始化字符串/* var of type string here */Hello, Joyce
(2)修改from: web3.eth.accounts[0]from: web3.eth.accounts[1] 。因为我们使用账户1来部署智能合约。

也就是将

var _greeting = /* var of type string here */ ;
var helloContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var hello = helloContract.new(
   _greeting,
   {
     from: web3.eth.accounts[0], 
     data: '0x608060405234801561001057600080fd5b506040516102a83803806102a8833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b6101a4806101046000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b34801561005257600080fd5b5061005b6100d6565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009b578082015181840152602081019050610080565b50505050905090810190601f1680156100c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016e5780601f106101435761010080835404028352916020019161016e565b820191906000526020600020905b81548152906001019060200180831161015157829003601f168201915b50505050509050905600a165627a7a72305820a7ef9308b4f91e2cbcb6bc84b3196736625ae7e0b242863d77c56dd80fc1e6480029', 
     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 _greeting = "Hello, Joyce" ;
var helloContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var hello = helloContract.new(
   _greeting,
   {
     from: web3.eth.accounts[1], 
     data: '0x608060405234801561001057600080fd5b506040516102a83803806102a8833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b6101a4806101046000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b34801561005257600080fd5b5061005b6100d6565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009b578082015181840152602081019050610080565b50505050905090810190601f1680156100c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016e5780601f106101435761010080835404028352916020019161016e565b820191906000526020600020905b81548152906001019060200180831161015157829003601f168201915b50505050509050905600a165627a7a72305820a7ef9308b4f91e2cbcb6bc84b3196736625ae7e0b242863d77c56dd80fc1e6480029', 
     gas: '4700000'
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
    }
 })

4 部署智能合约

将修改过的代码拷贝回geth控制台里,回车后,看到输出:

Error: authentication needed: password or unlock undefined

这是因为我们没有解锁账户,而且以太坊客户端每隔一段时间都会锁定账户,要经常解锁。使用以下命令:

> personal.unlockAccount(acc1,"aby")
true

重新将代码复制进去,回车,可以看到:

null [object Object]
undefined
> null [object Object]
Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435

如果前面没有加上--dev.period 1那么这里一直不会看到Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435这句话。因为一个合约的部署需要经过12个区块的确认,这里不会进行挖矿,就无法部署成功。我当时查了资料,就自己发了12次转账交易,才部署成功。

此时我们可以查看部署成功的结果,调用hello:

> hello
{
  abi: [{
      constant: true,
      inputs: [],
      name: "say",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }],
  address: "0xca3e2f09f4ad8e175b4ac673ac692263523458ea",
  transactionHash: "0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435",
  allEvents: function(),
  say: function()
}

调用hello.say()

> hello.say()
"Hello, Joyce"

命令行中的变化,在以太坊钱包可视化界面中会随着变化。

注意:如果没有部署成功,有的时候可以看到Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435,但是调用hello后会显示不同的结果如下,也无法调用hello.say()

> hello
{
  abi: [{
      constant: true,
      inputs: [],
      name: "say",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }],
  address: undefined,
  transactionHash: "0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435"
}
> hello.say()
TypeError:''say' is not a function
        at <anonymous>:1:1

最后,让我们看一下现在区块数:

> eth.blockNumber
9747

即使不进行操作,后台也在一直挖矿。

本文作者:Joyce
版权声明:转载请注明出处!

2018年7月5日


声明:本文参考智能合约开发环境搭建及Hello World合约一文,由于各种软件版本更新的原因,实际操作过程出现很多问题,因此将注意点和解决办法做了总结。

上一篇下一篇

猜你喜欢

热点阅读