环境: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": {}
}
-
config.chainId
:用来声明以太坊网络编号,选择一个大于10的数字即可(实际操作最好大于1000,不然很容易报错)。 -
config.homesteadBlock
: Homestead 硬分叉区块高度,不需要关注 -
config.eip155Block
: EIP 155 硬分叉高度,不需要关注 -
config.eip158Block
: EIP 158 硬分叉高度,不需要关注 -
coinbase
:矿工账号,第一个区块挖出后将给这个矿工账号发送奖励的以太币 -
difficulty
:用来声明挖矿难度,越小的值难度越低,也就能更快速地出块。 -
extraData
: 附加信息随便填 -
gasLimit
: gas 的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大 -
nonce
: 一个 64 位随机数 -
mixhash
: 与 nonce 配合用于挖矿,由上一个区块的一部分生成的 hash -
parentHash
: 上一个区块的 hash 值 -
alloc
: 预设账号以及账号的以太币数量,私有链挖矿比较容易可以不配置
2.2 初始化创世区块
geth --datadir "D:\chain" init genesis.json
2.2 初始化私有链节点
直接进入D:\chain
文件夹,同时按住Shift
和鼠标右键
,点击在此处打开Power shell窗口
。
利用geth
的init
命令初始化一个以太坊(开发者)网络节点。
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
-
personal.newAccount("aby")
中的aby
是自己设置的账户密码,需要记住,后面解锁账户需要。
可以看到当前账户数组有两个账户,新账户在第二个(索引为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合约一文,由于各种软件版本更新的原因,实际操作过程出现很多问题,因此将注意点和解决办法做了总结。
网友评论