建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
261
0
0
1 e: {4 V: ]1 [: K( D9 K
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
! G) \9 L* J3 v+ |* v, r
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
目标
本文目标如下:" p' K) r* O) D( n/ ?
建立私有以太坊,设置第一个节点,挖矿
完成一笔转账交易6 a- u- l& ^( m5 u4 V
. Z1 U& `% R5 I( v5 l
建立简单的智能合约
建立第二个网络节点) A& Y$ I6 M# b4 j: }* R9 d
环境介绍
+ t2 p) Z/ ~- r. C) [3 O
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:
. E |( Q# ^2 h
操作系统MacOS10.12.6
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67" m" }6 |$ {( {2 F1 N! T$ d
, {1 }" M8 _2 N* g
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang
安装
! H6 n- `" }# ^9 }- C* ` k `
安装Node.js,这里不阐述了,源代码自己编译吧。
' m5 x3 M0 B# A/ |
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。* S" H8 Y& }" |. T
8 i0 y- ]# z5 |" Q: D
最后安装Solidity,本地要先有brew,才能进行安装:% P- Q2 g! P: V/ d1 h
! H8 H1 h B" W/ ]0 ?
brewtapethereum/ethereum% h) a) L* E# n) G% v' p
brewinstallsolidity
创建区块链4 s; M* \1 D# H% K- Q
' u4 @7 L# L$ y) c: |
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。
- i$ I; h* F* O- n
文件内容如下7 V) h2 n) z: v; n# e5 G0 [
/ y2 V" z3 `/ z9 S6 u* k9 }! n
{, O |7 K; C3 I$ b: X5 \2 F4 V# t
"config":{
9 l4 n0 C4 t2 G {+ `* ?. W' ^
"chainId":2017,
"homesteadBlock":0,
"eip155Block":0,5 o w! s; J _ o% q% J: Z
3 a/ X5 Q1 z; X) t4 ]; m- U& R
"eip158Block":0/ g3 w( t) |% Z+ r* \8 T0 `
},
: o; }: ]/ S5 H1 C6 J" _
"difficulty":"100",7 d8 ?7 G5 D6 D8 `4 V, W
"gasLimit":"2000000",
- Z+ G% s: C. E) x/ c% s
"alloc":{}7 {- H8 w l5 o C5 v5 o3 ~
" y% C: l, {0 T2 o A+ d
}, E2 v j) D2 |. E
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
. _- G4 [* r0 r
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。# I; D) z1 w1 S% \
; z6 ~' j- p& H( m$ X
?>geth--datadirnode1accountnew6 q& G" f. O: ? z
7 W9 J% q# \8 \
?>geth--datadirnode1accountnew# c8 T. f3 d5 w Q) _' D1 t$ N
用我们刚刚创建的描述文件,创建创世区块。
?>geth--datadirnode1--networkid27027initgenesis.json( E, t$ y% i+ i9 T$ i! z* {5 ]
8 a5 q" L. q4 o- h( V5 S+ _* }* j
使用console连接节点1并且记录log4 M: B. ~7 U9 x) G9 D" T% M$ N& [$ Z
7 N9 q- {% {9 \! `( H
?>geth--datadirnode1--networkid27027console2>>geth.log) M( I; q; x/ R: r/ b
+ B# S6 c" h/ _3 J( [& T8 o) C
使用geth完成挖矿和交易
; A. C X( T. V% k" Y' A" n
连接成功后,看看有几个账户$ a# m7 v" U0 {' G& U
>eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"], |4 [9 U3 x1 ~0 [
查询下账户余额4 ~) D) s! x, ^" Q% d/ ~2 i, f
4 k" {8 M' i) D
>eth.getBalance(eth.accounts[0])
* s* u7 v' C* c
0: I. ?8 r ?( z( k6 X. o- z8 s$ o
9 C2 p g$ l! B: Y9 I
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。
>miner.start(2);admin.sleepBlocks(1);miner.stop();- m) K# S9 g. q( t0 g6 f
# f) X9 S5 V5 Y7 a6 W$ ?: V; D* l
true$ P4 I# P p- e7 m/ C, \
4 m- b7 p% D; L! [+ f
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数' F/ w8 d m' C R8 l
admin.sleepBlocks(1)挖到1个区块就停止
, s; O3 S# w3 B$ s _
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。1 i# Z$ L) x0 G6 i
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')" ?" @4 s% H: c4 N4 ?
2 v- L. l, |- Y' y I" |
50 i( w- E w R+ Y% x
: f3 w7 t h$ S2 x$ C2 M
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。
>personal.unlockAccount(eth.accounts[0])# \! p/ ^7 Y; m# |3 S7 a
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428' \3 p0 P/ Q# _, t
Passphrase:
true n6 T6 L) I5 C9 e% a4 ^/ w. @" l$ g
/ ?! v& I1 F/ ?
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})0 U- ~: |7 o; }
% U1 V( _# w2 h8 ]
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"6 X. _ n$ O% d3 s; M4 K+ p
最后出现的是这个交易的hash,查一下有没有待处理的交易+ t/ @, p7 a, s2 c9 n% a& y
>txpool.status" f3 c; ?) H0 q$ Y) Q
2 n7 s4 `0 E: B; T+ z3 V5 v: x/ z
{
pending:1,' {* n- M |$ f* C- k0 `) h
; v; ?7 y: n, l7 f! d& s
queued:0
}
' s8 ^4 @2 Y; H
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether'): i! {$ u, J) i8 O' E, `3 Z
9 [- k! p. n+ N1 v- T, X# z
00 ?& G: g5 T: F
: A" Z+ z( ?4 ^4 u
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
>miner.start(2);admin.sleepBlocks(1);miner.stop();
% V. [& ~" G1 Q9 l; u1 w
true
2 j9 c" b. Z$ G4 I2 H3 n- s
查下余额
3 V; l( B5 D! X
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')* l; e: |5 n# ^5 h# @4 w6 y
- E& @, G( ?9 v* |0 P: p& _( L4 Y
2
: s0 K6 ~, S6 b6 m: a
已经到账,再看下刚才交易的详情
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
/ g3 |& b0 |4 d. r/ j$ X! [: A, N: J
{2 d% s8 Y2 d; @9 r
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",
; B$ B/ V @- _' n, r
blockNumber:2,6 k% K4 i! _; ^
' { R' ^* M, x5 Q3 c0 h' T
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
H: S6 o$ n' d/ e) O
gas:90000,
gasPrice:18000000000,- g, m3 P( ]4 O6 L( r/ b
2 S+ E- a) z/ [ g( Q/ R4 o
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",9 Q6 V1 m) n6 T, {: }
input:"0x",
nonce:0,
8 }1 B6 D$ L0 h% [+ D( @, u! l
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",; ^0 H* s# C. f b+ T
transactionIndex:0,* `- r& v8 j9 P( ? k" ?
v:"0xfe5",) W+ P' v9 ]3 G: A2 P; e% V0 q. z
, f6 M6 A$ N7 q- v# L) n" t
value:2000000000000000000
0 P0 E4 `1 T5 }
}/ J7 V- }; C' H2 `/ G% \+ s
到这里整个交易就完成了
简单的智能合约& r! e5 ]6 @: F: y
4 V4 U) u2 A; L+ k+ C5 U3 a% S
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
: U% g* w2 L2 }6 b( C; I
pragmasolidity^0.4.13;
% Y, I! ?8 g( ~! l2 F/ ~; _
contractHello{
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){( G6 }0 t$ a; S e/ j$ |% P9 w
5 S. E# \' J# ^1 m9 X& b
o_sum=_a+_b;
. w# F! M& A, y: R5 O
o_author="freewolf";! o3 Z* b u1 F9 B$ C$ B
$ P: g6 J8 ~! g& Z0 O% @1 ]) H
}2 E& E( c) ]6 j) t: H& b
}
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。2 B8 d: g- @" d" y4 n& J
0 P$ z$ n. |" N# `* m* v# p9 ^
这里是Mac命令行环境,不是geth,?>开头的都是命令行
?>solc-o.--bin--abihello.sol1 q/ e. B) |) N0 u- j
?>ls
Hello.abiHello.binhello.sol- ]8 v' m" g: ?. F* b
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件4 ]" X. i. a3 |' N
4 I" @8 h" y$ D: r7 T6 w( A# |
Hello.abi文件内容修改成9 B2 N& |- \ F9 }
$ [2 m5 v0 r/ n7 y% U+ x0 I' P( X
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"}])
! j' j, v1 x# y& l1 z4 ?' j* D
Hello.bin文件内容修改成6 X; ~7 E; `1 I+ ]+ @
' C9 C l) m% s$ h2 x$ K, W! K
personal.unlockAccount(eth.accounts[0])% p1 D9 m+ }6 n# m4 L( K0 g
2 c" @2 A9 ?" A6 L2 m7 W" u1 T
varhello=HelloContract.new({" F( z. V1 z: H) p1 J% o9 Y
from:eth.accounts[0],( F+ a, T7 S8 J, J4 B ^2 w
# }; d; _' a; i% Y
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
gas:5000009 t. X1 D& _5 a, [1 a3 w5 y( t" ^
: C! U' V* _; z1 B1 i" ~- w4 _3 g
})2 [+ i* _4 p, V5 \$ i
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
# p& J3 r; B' Y3 `7 F x% D1 n9 y! X8 K
文件夹contract就在运行geth命令的目录7 S% s# X3 v6 Z( B
>loadScript("contract/Hello.abi")/ y! F0 I* w9 W. ^0 L# i' w
true8 t( O$ e8 z+ b; ]
7 f; ], R! i8 E* [. }
>loadScript("contract/Hello.bin")9 E( |8 u! D* y4 C
$ c6 _% G& q% f; G
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
7 T1 O( Z% _1 v l) l" G
Passphrase:
C+ W3 o" t M9 J5 c
true! M6 ~6 r% Y6 K5 ~: M: ~: ^
+ Q) O& b U2 v* v* t6 U
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。/ U' ^% ]0 s5 X& I9 I2 Q. D
w1 }" R: h2 N8 P, X
>hello
{
abi:[{6 K3 g6 a( j+ q. D4 Y& n
1 a4 `* g. W( Q
constant:false,
inputs:[{...},{...}],
' ], ^3 d+ A2 a( q, e; O) i
name:"sum",: w( f4 I' h, q4 M
# G* Y) S2 X) {# [% V5 n" D6 b. T
outputs:[{...},{...}],
payable:false,5 X0 K9 P& D$ Q# R# I. N: Q( F
type:"function"+ m; i; A1 X r$ H4 W. b
}],
address:undefined,
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
% s$ k6 ^! v: W( v# }" N( e2 s5 x
}3 L% \/ E" K8 N1 u+ n2 \+ H2 |
# {+ T* N; Q4 z6 F& \
>hello.sum) _% o8 Z# J/ `
7 A0 Y- e! E# L1 b
undefined4 a( s! l5 Y3 p
>miner.start(1);admin.sleepBlocks(1);miner.stop();% h& K; N2 e; N# F
) M. |- T, h. _. T0 r5 Z
true# O# a! e( }; h2 v/ I) `& Q' {& V
>hello.sum: `( {: Q: [, D- L
function()
, N4 W+ {2 W: x2 \
>hello.sum.call(1,2)
* U, d/ K" i ^. Q, B
[3,"freewolf"]3 X9 ^2 t l7 m6 f4 U( Z
* h5 V' |# D1 ^. P8 k2 W! _
追加-如何建立其他节点/ l$ J0 b1 A( [; N' w% [
# u o( V: i8 N$ f- C, `' y
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息1 k' t, t+ g1 ^# J
8 O# Z4 w# ^4 l/ n; y1 U
>admin.nodeInfo7 L1 N) ]; `( y9 i
{6 |, h3 I) p9 r. j9 G
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",; O/ E; {7 ^+ Z/ U2 n% N0 v3 u
1 m8 _4 }, ?# Z, |. k. h& ]
ip:"192.168.1.2",
8 m0 G: c0 f% @
listenAddr:"[::]:30303",$ P4 C7 a" {+ U
0 S9 H" i* l& m8 A" H' L: l2 ~3 a
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",% X6 q" f T* Q- c f& D$ @/ k+ ]. h
ports:{
x' @) X. M$ M8 l, j
discovery:30303,0 d% |5 V/ w5 T0 F: b$ i, I
5 ^ }5 @% g4 @1 }7 V' k% s
listener:30303
},
' z3 t- Y' P- ?! y8 {
protocols:{+ a7 m" t3 f2 G! x/ R. q$ G
3 p7 t6 I8 w/ O$ b. c7 c
eth:{/ t. c2 N0 J8 H; C1 c5 l
0 U9 {$ i) n* ^" S$ B5 ^! U
difficulty:655652,
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",5 V c* z8 D' @$ V0 P+ v
* j" J z7 ?5 p) v2 D; F$ S
network:270272 }/ U/ o, {) |" y; G1 x* d' J
}
4 `( Q0 Y, d: @0 N2 Y- V
}2 w, d4 \/ }! m# E
}
2 M3 j2 S$ p/ M: f' A# U/ J
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了
% Y+ l, K2 q9 l
?>mkdirnode2: [4 {5 v' x( z: v) O8 u6 o5 y5 T
( G4 ~& B) k2 T0 h& ~- T6 Q
?>geth--datadirnode2accountnew
?>geth--datadirnode2--networkid27027initgenesis.json& m ?# | k3 K1 A4 ~7 ~3 u( |% w
2 t1 B, f& g7 P) L" @/ Z
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console
6 A! g3 H- w0 v- ?& U) @5 @6 q/ q
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令3 b: H- ]. U3 c$ M1 v$ u$ f
2 J2 U, l- \# D. \& p% f
>eth.getBlock('latest')4 X2 ?1 L7 U0 p0 @
. J, Z# I2 I5 V; }% o
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人