建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
107
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
5 W. J1 b# ]' L. t) r+ A! d5 R
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
9 w* N7 e' \" E+ T, c G6 f e
目标
1 F9 T! x" F/ D: i
本文目标如下:
# x* \+ [* Y: u+ H Z
建立私有以太坊,设置第一个节点,挖矿
完成一笔转账交易* Q+ `0 s% w% ]
2 \) V; w n! V( ~* i
建立简单的智能合约
建立第二个网络节点& b5 D. D% X) R- C( V3 u4 Z% \' p
3 T2 B) P9 N0 Z! e$ g' O8 L
环境介绍
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:
操作系统MacOS10.12.6
Geth以太坊CLIhttps://github.com/ethereum/g…v1.673 ~' H) \2 S" F$ B- k
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang
安装: G; F/ M% B0 L0 C# I$ H9 h
0 ], t8 ?0 L5 |. Z
安装Node.js,这里不阐述了,源代码自己编译吧。
+ {7 k' s0 k# T
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。+ g- E1 G6 {7 Z
8 o, A4 F% u% n* V/ g+ X
最后安装Solidity,本地要先有brew,才能进行安装:
n+ Z8 ~+ P0 ^9 u+ D5 V
brewtapethereum/ethereum& j8 [4 y, w; \! {( `
+ M/ [4 t+ w) s( \- B
brewinstallsolidity% P5 x0 H: \8 d6 m" ]8 ~% E
! \% w7 f2 d3 }% v" W6 ?" W3 a
创建区块链8 Y" ]5 [! v6 {$ o) h4 m5 @: o
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。5 l) L7 w4 P. V1 s; K; p
% x6 I+ A4 \( `+ O
文件内容如下
: \. {7 x& q9 h7 D: C
{4 k" C6 u" D! j# n
"config":{
"chainId":2017,2 i- L; G, ~( N1 |) h! D1 O6 i$ i
"homesteadBlock":0,. \; _2 \4 X9 i' }4 M; |
: r4 h, m& u- U
"eip155Block":0,
/ w. W/ y' L3 C ?
"eip158Block":0$ h/ g2 s( d: J- g
- n' R+ C S* Z" E$ c* C+ K8 l7 b
},
' @4 Q( n$ F" s
"difficulty":"100",
" O, I" i* o; G; c5 u, R) \$ j
"gasLimit":"2000000",
"alloc":{}
& G3 H/ e5 F7 @, t; z
}" ^$ e2 Y0 b4 H, L$ a% \
+ }$ {: j l( [! }" `# F; _* ?
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。) l: r; o7 i3 k% r7 p+ o" P
4 H+ F. G" ^, V
?>geth--datadirnode1accountnew
! S( [ p4 \" v% s
?>geth--datadirnode1accountnew/ k; ~6 A9 V( e6 C7 \( r! l) G3 d
用我们刚刚创建的描述文件,创建创世区块。
?>geth--datadirnode1--networkid27027initgenesis.json1 W" N* N; X7 C5 f3 T
使用console连接节点1并且记录log: G, L# x Z9 J# v# W* I
?>geth--datadirnode1--networkid27027console2>>geth.log
U/ P1 U8 i$ k- v M: j) y( h7 O
使用geth完成挖矿和交易# Y+ n' m5 F/ e- E- S; E
' u9 Z( h3 | |9 a. v# Z
连接成功后,看看有几个账户3 I M* {, z2 L2 X- g/ H s
0 N0 _$ J- e! C0 h5 ^6 s
>eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]! }/ u" Z* a, e& `7 }% H$ H
6 C+ `$ F" w* G, a5 A4 I
查询下账户余额; |* Q1 G8 k6 y) e7 ?6 }: K
>eth.getBalance(eth.accounts[0])- l0 t5 {% S' z# h1 [9 j I
0
3 W- I) ?# |. J3 y \
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。 G4 z& g* `! N. E6 v
% `0 h* Y$ G2 H1 q
>miner.start(2);admin.sleepBlocks(1);miner.stop();; l: M9 e6 a. G( u
2 j7 S" @$ M5 y. B* A' |
true7 w! x. r' H: \8 _5 D T0 Z
: l" N$ s3 L {2 C; Q
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数
; m Q: _- B% K9 D+ Y
admin.sleepBlocks(1)挖到1个区块就停止
" R( z# Y- V/ @3 s) B
miner.stop()挖矿停止, x# R$ S# d; O$ a( h
/ q6 M- B( X5 b$ t9 V
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。
9 R. R" J+ b5 I- g0 v
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')) ~0 S# J# d/ e# O" X
5
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。% S3 G5 {" s+ K8 o5 ~; ^! G; {5 v
~" A' X5 G9 @$ J) X
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
; T0 D; h# c" Y2 ?- Z
Passphrase:
5 |7 `" N- p- X, V7 ~
true
( t4 V+ o) Y7 {% O1 E
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"- y0 ~( k& j e- g0 z7 M
/ u0 ?. @; U0 G. N$ `
最后出现的是这个交易的hash,查一下有没有待处理的交易# `5 j0 [$ Z9 ?6 Q9 R/ L" \
>txpool.status" R( K, J2 S5 d
I7 v+ X5 ]5 F0 F% O8 R$ b8 |/ k5 p
{3 h% [, b T: _# `0 k9 g5 H6 s
% i( A2 n& g" W
pending:1,
2 d l" G/ d2 h5 U0 s2 ^
queued:0 T' g, W0 D. k1 F
$ ?: k ^/ |% y) K( E0 j( r) y
}' O! a# @0 n3 U
' O1 A! n5 b/ n- c7 L
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')" X) \% L% u1 ?( B h( [
0
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
7 w" y% \. Y; k7 k
>miner.start(2);admin.sleepBlocks(1);miner.stop();5 [. r0 m0 w4 u& |3 W
true, s+ h0 V( u: O+ n
. U7 C: J$ h, h" P
查下余额0 s; K) q9 D+ q) K7 X
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
9 m( L. k0 k' ~
2; {/ @' U7 [2 a$ J# B7 T6 D3 o
/ F- B! i6 x# ?! {# z f" u D- e
已经到账,再看下刚才交易的详情
4 d4 F3 x4 S1 `6 A4 R* V
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
" ^1 N' S/ c7 w5 i. o9 @
{* C9 i% z0 R& X# M: o2 m
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",
5 l, @4 }/ Y% k+ m& |' ]
blockNumber:2,+ ~: f8 D9 c7 R, @" \
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",5 Z4 G ?; {9 i) o
gas:90000," l0 B1 U4 g9 q. w/ A ^( Q: K
, w- E9 r7 d' y/ E
gasPrice:18000000000,0 w3 {" [; }" X
, P- D, U+ e& M/ K+ m* n
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",
input:"0x",% e% ]5 F/ q4 h4 V9 V$ }8 }# r
& ~1 p+ t8 _0 x$ }" ]& R% V) o) X
nonce:0,
7 B- v: ], \2 k" k+ Q
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
transactionIndex:0,0 |6 ]) G: q5 X/ Z- x# E
v:"0xfe5",
3 @1 {' q w6 R' V9 l: z9 ]- ]- j
value:2000000000000000000: y+ v" b" |- j H* B* x
, y) }+ y( v6 D B8 r6 H; R
}) ~# G- e: G, _8 S b3 n8 {
到这里整个交易就完成了1 a9 i/ A; T7 k$ j% d
; e C, I3 t. D7 |1 i* {; k4 j3 e
简单的智能合约/ l' Y. Q; h2 b' _
7 [" z1 _1 t6 i& x: _6 X
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件5 I' i7 O/ [1 C% D, Y, U- _, C
' ^& z) S8 y7 K b6 s; Z
pragmasolidity^0.4.13;
# S, X: {7 |! h2 s
contractHello{) l c9 d; |+ B2 K8 z* {: T
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){1 z7 x4 \9 T D6 z7 D8 T& s
o_sum=_a+_b;
o_author="freewolf";- N0 S# o3 R/ G& u9 m
4 ~# ^; }$ Y' V3 p4 w+ O
}
}
% o. @8 ?* Z6 o! ]4 ~0 c
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。" C+ `6 m" B3 o; i7 c
. `0 Z; d9 p. P9 s1 g0 L& O
这里是Mac命令行环境,不是geth,?>开头的都是命令行; T7 E( j" a/ w% N
?>solc-o.--bin--abihello.sol/ f0 D; V; O. R- Q0 G) B1 j
+ }! ?" @ Z! ^6 q' l! u
?>ls
; f' {; k) R! t5 [% C
Hello.abiHello.binhello.sol
: E" O" g8 W4 K$ a
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件8 y& z, P! l/ q! U* u: P8 i {5 u
6 l# B* Y& I# `2 L) A/ n
Hello.abi文件内容修改成
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"}]) w3 ~# k3 |0 y# b. Q8 Q# m
Hello.bin文件内容修改成) \7 O* W( C8 S) t9 u- [
personal.unlockAccount(eth.accounts[0])
varhello=HelloContract.new({
from:eth.accounts[0],: D- \' ~4 k7 {% f: Y* x
% @( w% O! V/ o' n
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029"," c+ ]% g9 v" E8 f! s
gas:500000
2 {" P$ |" H/ o2 d
})
5 S( [, X5 X/ G# E0 w* b
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了' I4 r; n8 S) c6 N2 c: p% [- y) K: T
- m! |) T6 A. G& }& g$ ] C
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码$ n2 f/ @ p% o6 n6 R/ O8 ?1 C
文件夹contract就在运行geth命令的目录2 G' i8 Q% `. }; V2 B
+ |! |0 T+ ?1 H
>loadScript("contract/Hello.abi")
true: I2 L& H2 D- i) s3 S
>loadScript("contract/Hello.bin"); V; R6 @1 G$ x: h+ b' s1 H/ W
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428( U1 v" H# Y' H, P+ m m
Passphrase:6 x6 d& c. y2 \- |* D8 _0 T
4 v0 d4 H3 S: n, B
true
) h! p! S4 r( ^
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。: x6 o( z% [% i1 K
: O& R1 |+ V: N: L5 H* z! H2 O
>hello; z* { j0 f. T: @
4 z2 }4 O) h9 j
{
+ p* l7 M5 r1 P. J
abi:[{5 x! _2 O, r1 P8 R9 D" e
constant:false," X" G6 g) j! A
inputs:[{...},{...}],* V4 o! Q1 }% i& x' ^/ ^) P
name:"sum",( J0 H# Q+ Z0 Y5 n
outputs:[{...},{...}],0 r. `" @+ e0 H* q- v0 W
* w5 u- }; J! j8 K" i
payable:false,3 t5 ~ z+ O& p. w9 ?. G8 ^) @& \
type:"function"
}],
address:undefined," M' R( T6 L: l1 }* O
V# f0 B) R( C7 z! _. N
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
, a/ p0 w U# i: T, T0 t& y" c8 V
}0 Z! V( w3 x: {
$ f6 J4 F. o8 m- p J
>hello.sum
$ b, ~; j( m0 @+ K2 H" \
undefined
>miner.start(1);admin.sleepBlocks(1);miner.stop();6 S3 G$ j* x! X. F7 N( e( @8 O
true
>hello.sum
; g6 {* _0 w: ~
function()* }& r/ ~9 d2 W4 \9 k' C2 ]
>hello.sum.call(1,2)
[3,"freewolf"]
0 d$ R' J& N* _$ A7 r4 H
追加-如何建立其他节点9 y+ f. b# k- Z2 Z
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息# H1 {1 N' H3 L9 T# d
* J: q- p0 y6 r
>admin.nodeInfo
- t$ \/ i" `$ U, k8 q
{2 ]$ }( ?" f* s" R: P$ j, i+ X
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",4 P: F! ?, f7 X6 j: B. v0 m/ e8 y
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
0 Z u p, V' o% Z# B$ Y
ip:"192.168.1.2",' \6 m# k+ \+ D* c* Z
( k# \6 I s* A1 v/ M
listenAddr:"[::]:30303",
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
ports:{. g; P' [& \ p4 S0 _" }8 `
discovery:30303,* n( o8 T# f1 P8 h; v& u
+ x" w+ F% j) C) N6 }' ]
listener:30303
},
& I6 S" h5 S0 {- W8 S/ i3 E, [
protocols:{( n+ M4 L+ |& b! z) V6 K( T# F% T
5 {+ D' A2 C: ^8 R/ o1 f5 v8 }
eth:{ h! ~0 w0 m0 L' l
6 P1 P, Q% p# e% w: o
difficulty:655652,. E1 Q5 O1 K' l" S
& ~! ^1 t$ R2 Q# ]
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
! J: S5 r9 K* r: c ^
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",
7 A0 I+ v5 I* Z
network:270273 y2 k- t8 `+ z' `! V
8 }3 D; d: Q+ D& K. _' U
}
& r; \, [8 C$ ^* t
}7 B$ j+ T8 _: y2 `
$ U1 z( Q& M& }, u: t( H" z6 B) d
}: ^7 Q2 z5 [- Y% o$ ^
4 q; T& R9 k" `9 L5 Z F! n7 t
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了
. Q& i8 e2 L; Z
?>mkdirnode29 h2 m$ v5 R( R: ?0 Q
7 K$ X; d G( h# Q& S, G. c$ U2 e( ?
?>geth--datadirnode2accountnew, G* }+ S8 \: G$ q
?>geth--datadirnode2--networkid27027initgenesis.json! W$ ^2 o2 R, G' S
. ~9 G& T% y- V4 U+ p4 d. k
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console! y0 u1 b ]% R0 m* p& Z6 }
' t# d- ?8 I& j+ p6 F
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令" O; K0 K+ x# t
>eth.getBlock('latest')
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人