建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
115
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。7 J! H& i x: u1 b
目标5 Z' Q! S! q1 @) [# q: O
! L" q; C4 j4 u8 C5 Y$ u1 R( Z
本文目标如下:
建立私有以太坊,设置第一个节点,挖矿( S; A% d3 v" x: K) S% c7 g
完成一笔转账交易. k0 a4 B" V i; D! E( X3 k O# Y
# N( L) N0 m8 x* k8 |6 k
建立简单的智能合约: a% u" ~# \( E" X/ y' v
5 y, y6 n. B) H* b+ b' a
建立第二个网络节点; h4 I1 o& J [. x2 W7 ^; L
环境介绍
% J0 j( S9 g* o4 Q! ?# O
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境: v! |; n9 X4 I, z
2 F7 e- D6 s6 @4 M+ a1 @ \
操作系统MacOS10.12.6
: N5 A! M3 u; D! n
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang* }2 j% O) K% f3 d
安装8 h- t7 k3 T2 e3 i
7 t* F' C0 Y& p2 w( w
安装Node.js,这里不阐述了,源代码自己编译吧。
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。& ?/ @/ U; M& H4 n8 d% U& n+ X7 m
& E* M: E: C {6 @9 ^% l! H' i
最后安装Solidity,本地要先有brew,才能进行安装:
brewtapethereum/ethereum* {0 h) J( G' D5 @# I( q
brewinstallsolidity
Y* |& w( G& E$ y6 W
创建区块链
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。0 t/ t% N; V' Q
文件内容如下: G; V9 a4 o( S3 S
2 ^ B) u2 E* F
{
"config":{
1 H6 h; F) q9 s
"chainId":2017,
7 Q X9 @- N7 N5 S, b y
"homesteadBlock":0,
"eip155Block":0,
: P T W" P6 ~ r% M
"eip158Block":0
% i7 L' k4 e! b3 ]3 c; g
},' n+ _: p4 f6 p
"difficulty":"100",2 y9 K2 M. \1 P7 p2 `: `
"gasLimit":"2000000",
4 O, w* b! b- x1 X: O# d, m) q
"alloc":{}4 a0 n0 [) g# e1 _( B, ?+ g% e
: l4 }: `% _7 v* b, }
}
. d& G! A8 f3 F+ ]2 }' h
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。. [% r* m# m& B2 }8 p1 g
" |0 G: i* v- Q/ X+ H9 n% l
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。8 ]: M1 X v" y+ j1 X6 _$ q, U
6 @0 d' h7 r$ R/ i9 G
?>geth--datadirnode1accountnew; l$ n/ h8 n% H* w
?>geth--datadirnode1accountnew" `3 f& a* @' y/ f! \" h
用我们刚刚创建的描述文件,创建创世区块。3 t, |7 @/ ?8 t3 h! a; w+ f, U i
4 _4 J0 a e7 T( H" p
?>geth--datadirnode1--networkid27027initgenesis.json
使用console连接节点1并且记录log9 m" d0 _) y1 g
?>geth--datadirnode1--networkid27027console2>>geth.log
使用geth完成挖矿和交易* y& [1 i$ ]+ u$ U7 {
连接成功后,看看有几个账户- V) J. D! E; y* w/ [8 E# v4 i
: [2 d: n- s& B9 [$ ~6 b
>eth.accounts3 T2 U7 @0 R! {; Z4 C: C" F2 u0 @
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"] r8 B# Q# q/ H& [/ t- w6 c$ w
查询下账户余额+ h) t' e0 O8 P! r& R; b, A
>eth.getBalance(eth.accounts[0])& e; y5 @& ?, o7 r. a
0
7 J" J0 k8 c: \
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。
>miner.start(2);admin.sleepBlocks(1);miner.stop();. i- m% |$ }% O5 [# ]0 k8 ^
" `% o- M4 [: u2 G! _3 u+ b
true
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数3 u, I3 W9 l3 q3 N
3 G1 R. {3 y; J! h: w
admin.sleepBlocks(1)挖到1个区块就停止: X, x' g6 H+ G% I2 o1 l* h, H
# K5 W; u6 ]$ u/ ?& \4 V
miner.stop()挖矿停止 D7 y8 a5 P1 P) o7 z# b
0 V, M8 E3 b U8 F* H
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。7 Q; C5 s( \) U
/ P* B! }1 w+ R, e% t8 b
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
) ?* [# ~* B2 e
5
* [4 s* a* ]7 j4 i
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。6 u: ^/ X" k# V4 s, s i
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428' P7 \6 |6 N5 l" t5 t
Passphrase:
true5 m: D3 N8 Q& [- n* A
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})* M. q, \& {. n$ a5 U! d7 _, {. ?
6 ?3 j% ]6 N8 I8 V2 g3 r
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"" m# }" e/ k6 p1 h1 T
最后出现的是这个交易的hash,查一下有没有待处理的交易
>txpool.status
{
pending:1,9 |0 E( n# U1 o W' |2 ] i$ d' @
queued:0+ G+ g$ i5 o% G2 \' P
}
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')$ q, O* X. \/ p' Z2 ^
! S7 }9 g" h/ U% h) G7 r
0
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖1 \8 }7 e' ~0 x( z- k5 b( j _
>miner.start(2);admin.sleepBlocks(1);miner.stop();
' a& w! g5 h" J1 \
true& m, R' g. y1 ]4 j3 l
查下余额7 L$ S. j" k- a( F- x/ U2 C: V# d8 }
1 j# E. V5 _ M+ a) f' U* k4 g# a
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether') }/ K) R9 o% _# f8 z
8 z# L5 E. J0 m$ v
2
已经到账,再看下刚才交易的详情$ m+ f- v/ [8 `
* {$ ]. ]4 |5 w
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")# u4 b! u( x3 C& ?. N& T
{: k! g `/ O7 O( t J8 { v5 X2 F8 d
6 ~# a+ P, e4 x# W
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",& W/ k$ O! p% C3 t
3 f3 q: S7 `2 _
blockNumber:2,
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
1 r! J- n6 X* L1 Y. a
gas:90000,* U5 @! `% V! d: S' H$ S+ [
0 k4 P0 G" q" e. n0 W' b) Z
gasPrice:18000000000,
: T& [9 t$ `4 U" c; a
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",/ H; X7 b/ B2 J+ c
input:"0x",' g* O: G$ C5 J; K+ m
nonce:0,+ X) h- l3 y& J' w' d- F' t
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
, Y" y4 z% S, K, L s
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
. R+ }# N' h& Y! W4 Z
transactionIndex:0,
3 X; E! P! ?1 a H5 r9 p$ J, j: i
v:"0xfe5",
value:2000000000000000000# `! t) f$ `/ I, V4 g! W0 H7 \! i
6 [& O7 B# M6 Y4 ^; m: Q
}
到这里整个交易就完成了" r7 v' d' z3 K- d) T
简单的智能合约1 U }: w7 Y8 A% ~8 m, g; A D
+ r3 V* I: J. I+ S7 A
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件. {9 ?7 i& z0 q7 L* z
pragmasolidity^0.4.13;
1 m, Z8 w ~# d8 t5 n F: @
contractHello{; P8 e5 R" q# Y9 ?% ]* ]
) e: u& J# l3 w
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){
( Q; o; ]: y1 A7 F0 f
o_sum=_a+_b;
o_author="freewolf";' f0 y- p; d' Z7 t
}
}
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。- D1 [% c2 F' S9 p
6 W5 f" Z) J3 {% q/ u# Z
这里是Mac命令行环境,不是geth,?>开头的都是命令行
?>solc-o.--bin--abihello.sol) M4 Q u. u7 l9 m
?>ls
Hello.abiHello.binhello.sol" b/ r6 H/ \! S; W7 g, p
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件. E6 Y$ F$ B+ T! Z" R9 ^ E
Hello.abi文件内容修改成; y1 X1 h; H- \
* d6 t5 R+ @+ l. p! f
varHelloContract=eth.contract([{"constant":false,"inputs":[{"name":"_a","type":"uint256"},{"name":"_b","type":"uint256"}],"name":"sum","outputs":[{"name":"o_sum","type":"uint256"},{"name":"o_author","type":"string"}],"payable":false,"type":"function"}]), z; o9 j1 M) a% |7 X5 _- O: H
Hello.bin文件内容修改成
personal.unlockAccount(eth.accounts[0])
varhello=HelloContract.new({
from:eth.accounts[0],, M1 m1 A [9 F$ \' B7 Q& K
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
gas:500000! t6 y0 t0 k& D H! E& t: d
' r7 T6 b# y: ]; o8 [
})
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了
, N7 x; p# M3 u- ^/ l- Q$ {0 |
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
# j$ _1 `; \# F
文件夹contract就在运行geth命令的目录
9 t. T7 d+ n4 n7 y* T) m
>loadScript("contract/Hello.abi") d. C; @7 I0 z+ ^' z! W0 _- e
true
8 q$ T) C8 h o4 c+ }3 V/ a# ?
>loadScript("contract/Hello.bin")
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:0 D% [4 w) R! g0 u( n5 ^- j
true" a0 o, t# G0 v
4 O5 z9 `0 T) ^6 B
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。/ o* B+ Q, q8 a1 r# V9 ~
9 [ E, `" V' i& n P3 {8 z1 ]6 b* [" z
>hello
{5 Q% b& ]$ w' o
/ `' @, O4 P- j. P
abi:[{" C5 ?) m% D) B! U+ @6 U: ]0 p! k
constant:false,+ J6 Y9 S. k! A. B# _7 j- z
# V" }0 ~' O$ W
inputs:[{...},{...}],
name:"sum",
5 G1 R% _: b- g5 _
outputs:[{...},{...}],, _1 a6 p. X! Z- U) m) q
. `0 b( | i0 Q8 M
payable:false,. Z( w, K8 T: R1 R
type:"function"( a* w7 _" M# |1 _/ |! O
}],
0 f' D- U' G. f% x" l
address:undefined,- Y5 X Z0 A2 t/ w. D! L( I/ E$ M
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"6 |# l( K+ c. \8 I' y+ z
% B3 U4 |+ t( Z1 k* k
}9 j5 m+ }7 U/ p- l( V
>hello.sum
/ o& ~" X* Q# y r7 d+ @- Y$ l
undefined
>miner.start(1);admin.sleepBlocks(1);miner.stop();3 d0 P* c/ ~" y' T; Z& v
true6 h! \& P k- B7 j
>hello.sum
function()5 v: P! w8 {: W' W+ {% M- Z0 P. t
+ k' M* }* C' j6 W& b, }6 \' I
>hello.sum.call(1,2): l4 H" a; o; A; N1 n. e" j- f/ `5 c
. K5 ^6 ^( h: c+ a5 \$ Y5 v, u
[3,"freewolf"]4 Y2 W7 R* Z, `9 B0 T
8 a9 l" ?9 x. V0 V* A6 T
追加-如何建立其他节点
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息
>admin.nodeInfo
{, G9 E, U; ?2 L
( L1 L& u' [& F
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",+ |# [, s5 C& p( X
% F- e6 Q0 j, t6 P5 z; G
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
ip:"192.168.1.2",% ]# n5 H+ x% q& ]! _ ]: ~0 h
C3 ? a9 s) K
listenAddr:"[::]:30303",3 \2 }/ t- {0 |* [( |) ]) {
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
ports:{: n' ~0 @% t; s% T" R% E
2 |( m8 d! Q7 r c8 J
discovery:30303,
% W0 m& G# J s( v! N5 L: ~- \
listener:30303' W4 ]) p0 G- d! {8 N# i' r
1 a) \" g9 r }, x' M9 l
},
+ N+ f# |8 R1 K$ G
protocols:{2 |3 E. K% C; n X; M) i& z5 c( F
eth:{# ^/ y- m5 C6 K q& d6 A) H$ B& f, Y
difficulty:655652,' X8 b) s' k$ m. L6 e8 z0 J* {
- A7 P3 m& d9 F; R) Y
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",. t2 `( j: D1 _ r0 H+ w9 D
+ _. k6 F) v7 ~: `) }
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",
3 H( v) A$ F$ O7 b' R
network:270273 r, n; N6 g' V, n! i* H3 v& `; D
}
}
}
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了
?>mkdirnode2
?>geth--datadirnode2accountnew* R, _6 N' {8 D+ W4 O* f- j
, Y- o1 ?7 z+ @/ X1 G) k
?>geth--datadirnode2--networkid27027initgenesis.json6 m& c7 Z* ~! b+ ^$ C5 ]! L
" K- n3 R( d |5 n4 P. ?
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console6 R3 @7 ]/ F* A9 w9 X1 |+ ]
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令
5 J, J0 W }0 @* v. V+ o
>eth.getBlock('latest')
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人