建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
117
0
0
k0 }4 g1 k9 q4 k
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
- x4 K! d* v, n1 e }) u1 W
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。4 L" z# t5 F+ a& f" T
目标
& S9 ^7 Z9 v) `/ w; a9 x; W
本文目标如下:
建立私有以太坊,设置第一个节点,挖矿
完成一笔转账交易
建立简单的智能合约
建立第二个网络节点' o2 j: j' k" }" M4 p9 U! v/ H, p# Z
# h' O; E1 y) r$ q
环境介绍
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:* F- `7 X4 b2 l( q
3 \' Z7 g @# b" ^- j7 M) @8 b* i
操作系统MacOS10.12.6
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67
# R- d9 a/ y' q* D4 Y2 Z6 s' i
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang8 k D p, T" l9 p" L, m$ Y: {
安装! M8 }: K8 B, s+ z/ Q* T0 G0 w
安装Node.js,这里不阐述了,源代码自己编译吧。
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。6 @; ]0 Y9 s% Q% Z: V$ U
1 t2 b* W4 n0 t$ V+ h
最后安装Solidity,本地要先有brew,才能进行安装:
3 _- L, Y6 X1 \& B: B
brewtapethereum/ethereum+ ?$ Z7 i) z- Q: o, C- y
brewinstallsolidity
创建区块链
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。% ]& t& i5 M3 Y# E# O% T
+ W0 z, p) L+ C7 j
文件内容如下
6 m# t! g; \- a) Q+ c4 I; j; B
{
"config":{
/ d% s, h+ l6 ~
"chainId":2017,7 V" p' R, _0 D2 o. T+ m1 C& f- ?
"homesteadBlock":0,
. S7 d# ~" E4 W1 r7 @7 L, s! H8 u# Z
"eip155Block":0,$ o. i% M+ a) D: Q. v; X" @
" O/ I! y7 Y) `( w# x
"eip158Block":0/ D1 T8 r2 r$ t' o
* l v+ E) K8 Z5 H$ j; I
},
"difficulty":"100",
"gasLimit":"2000000",
"alloc":{}
}4 m& d5 ]; p: T% S! x& \
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。, L) L) D9 ~6 w
?>geth--datadirnode1accountnew
/ D. k8 F( U3 M7 \' _: O3 l0 Q
?>geth--datadirnode1accountnew
* B+ E; c( I1 n. v8 H& H; H
用我们刚刚创建的描述文件,创建创世区块。
?>geth--datadirnode1--networkid27027initgenesis.json; ?4 m9 U4 V* ]' A) F& h' E u0 }4 X
1 ^" {" b: T! }
使用console连接节点1并且记录log& ?" V2 k* g+ @' o5 B
, U' x" ] l4 G7 h7 r6 g5 Z7 \
?>geth--datadirnode1--networkid27027console2>>geth.log! R0 a4 L* Z4 t
! i( l/ @6 b1 C
使用geth完成挖矿和交易
连接成功后,看看有几个账户
# Y* {) B9 @; Y3 c' @5 o; |% i$ D1 s
>eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]' l! R& J. x$ m E/ }6 _1 x O
查询下账户余额
9 x1 p0 C0 h0 D) g% b
>eth.getBalance(eth.accounts[0])
) x/ E, T, q& E! a( x+ G' z
0
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。: z9 [7 u. @' ?+ Z1 t% g( P; i9 U' W8 n
>miner.start(2);admin.sleepBlocks(1);miner.stop();- k0 @4 N+ k- H7 [
( Q4 Z. T C. |& F$ `
true2 \) g! N& O* ] u8 \( J6 Q
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数& t- P( E% W% B0 f% u" _- C! R# Z3 M
! K8 {% j* x: c" U4 F4 [/ N2 }& K
admin.sleepBlocks(1)挖到1个区块就停止4 {1 Y% E0 e$ t3 z. V
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
* e- y( a s9 b. M. q$ t( }
5& D) q; S5 }$ j/ u, `
0 B- p) s+ y4 `2 m9 o" J. v$ }; L
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。7 A3 V( k; p# }% y
9 r" J* e8 Z3 H# P- H' O: D
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428# @: r4 r4 K8 D
& Q- k: X. J3 [9 J
Passphrase:8 o' L" }) s1 H/ J- v! G' F
true
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})
9 [2 p* `7 w% g+ l
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"
最后出现的是这个交易的hash,查一下有没有待处理的交易, Y$ V3 u$ I5 m g& ^5 y7 ` v+ E3 c1 x
, n3 v, T* V8 J' t! g8 ?3 F) k
>txpool.status
{
9 s7 ~4 Y8 f: ]2 d L
pending:1,5 L6 _7 A/ X2 c9 i
6 e2 \1 o' o0 n8 o' i9 N L
queued:05 z* K& O- H" `, ^8 M% H0 @
+ @/ _5 i5 N" ]* z" N% p
}
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
( i( |: w c4 Y) `" e6 o
0
9 C. s7 L: }2 G i- p% t }
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
3 f7 s: k3 r& G! n3 r6 r
>miner.start(2);admin.sleepBlocks(1);miner.stop();
true
( Z5 _* C$ s5 L0 ?7 |
查下余额
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')7 t5 j7 ~8 ]8 M* f
8 x5 {# F1 t/ I8 ^1 g1 f4 o
2
1 l+ H5 L/ g' {- Z) @
已经到账,再看下刚才交易的详情
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")! }! U7 d7 E5 D' a. L
{
* D. j' j& ~0 Q* k5 D
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",% l; K) T/ U. Z. {$ L6 R( L
6 {4 x7 s4 i2 [7 i+ ~9 K) v0 z
blockNumber:2,) a" @" m# s G- T J- u3 r1 e
$ {0 v. e3 c% N" |
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
gas:90000,4 v! @* L, r; K! d5 G# G4 E7 Z
. H: v' ~3 s& D
gasPrice:18000000000,
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",4 ^! B, |0 \4 @# q
8 v3 |! Z& ]1 y# f$ R2 V* m, f
input:"0x",
/ e; h1 I+ J- d) }+ Y
nonce:0,$ a8 g1 _% h/ i8 ~8 Q- G& ]4 L
. f& ]) ?" H4 R' }3 m
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",+ }9 ?( t q4 D9 r) c
6 `. o" k" \8 \# c4 ?' V
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",0 `, W( K' @' j- Y) P( a4 v; p% Y
# u' w% K4 d8 I$ R- }' P
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
0 j' J: D% F; h- n2 E
transactionIndex:0,
v:"0xfe5",6 I; j& R. l. }5 l& U6 Z3 {
value:2000000000000000000
5 H; `# C5 A: z! X( d
}* k% G6 Y8 K+ `+ f8 O% v7 E
6 }, B: _) @- `% f& n+ s
到这里整个交易就完成了1 G4 Z. x' V- M# `: y/ F0 Z- ~1 G
; E e# p- X- M8 c
简单的智能合约# k# W6 }8 P/ |& b+ D" Y* ?
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
7 P n' X5 v# I- @& t* n1 r
pragmasolidity^0.4.13;
contractHello{& T ], y; R% l2 k
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){0 U3 V K' ]( {6 {
o_sum=_a+_b;
o_author="freewolf";
}
' w1 ?- V5 U2 i! i7 q
}
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。1 b6 W& h# O) z5 d6 _" W' K
这里是Mac命令行环境,不是geth,?>开头的都是命令行& C: Q% Z8 X. u- I8 _' T
?>solc-o.--bin--abihello.sol
9 f5 C4 `- T0 x' W: b' u# C, i! u- y
?>ls
. g& J5 B' j0 |! w% f) I4 }: |
Hello.abiHello.binhello.sol2 w5 b3 P7 q- P; R: J" h
+ I! R. n1 G% Y
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件
Hello.abi文件内容修改成0 x7 U6 U+ L2 _6 \" D! t& |
& `& _; J4 l3 B$ `4 r8 d
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"}]), x+ @& P g- [) E4 H4 h
+ e2 J% h, b, z K8 L) k, X U* Z
Hello.bin文件内容修改成1 t+ B/ g( k, Q* A& o
personal.unlockAccount(eth.accounts[0])# A* t7 W; L- u$ H# d* w
varhello=HelloContract.new({
from:eth.accounts[0],
! W3 ~6 c+ @2 M( W2 [9 T
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",+ S8 f! G% ]$ f5 k" F
% L2 t1 x) _! H2 t2 w
gas:500000
})# t8 N/ _* T8 H
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
文件夹contract就在运行geth命令的目录9 h: t" n# P: f& {5 w, H
5 u' i) N Q3 n: [& C7 d8 h, r
>loadScript("contract/Hello.abi")4 K8 a& T4 _6 L! V$ S2 L% e% v
true9 ~- C& Y% ]" p( Z
- p2 I% ?- q: j6 M; l: B U
>loadScript("contract/Hello.bin")( m& f8 b! D/ g& C; b. ]4 x
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc4285 X Y6 |% A- o% b8 E, t
Passphrase:- I& A! o9 C0 c! ^+ ?7 \+ U# |, S
0 z( G D8 d/ Q8 ]5 o( C
true
; c( K; s- L! I( Y
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。
>hello' Y+ P, j i5 t4 U; n6 k
0 I- N) u# c @! H0 q
{
/ M4 h9 A# q! ^% | ]
abi:[{
" B! D* e) c( b& S* G4 }
constant:false,9 m& e& b% |$ i( N! ^) B
inputs:[{...},{...}],5 t6 {; C3 j# q4 S% }1 j
name:"sum",* I6 N7 N/ X( a9 n% O
: s6 B; w+ X& D% j5 D: M
outputs:[{...},{...}],
) o1 Q9 l" t1 N' x5 Q
payable:false,# y! R# i. K O7 t, j4 e6 _4 C1 a
; j: _7 o) C/ X6 u4 f
type:"function") o( O$ m* A0 _
& v0 C4 A, x: S% [8 X1 I4 B. g: |8 F
}],* Y' t& X1 j( K/ m' p+ I) E( E
3 z6 i+ }& d' C1 o. l
address:undefined,
. g# r( c/ t$ R* T9 p! A1 s2 a
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"% W5 E- w9 v2 a8 \: k: c5 t) s
}
>hello.sum
undefined
0 c0 ]( a, X( k% T3 z4 l/ E0 O6 e
>miner.start(1);admin.sleepBlocks(1);miner.stop();
; X! _* q# ^, e* B1 B
true7 y0 E4 N; F4 B6 p- ^
/ |- ^! Z7 `) r* A2 j' h. i0 X
>hello.sum6 g" t2 u( E8 m+ e. P
" ^% \$ m% s) W% G
function()# S) I0 l( M6 Z# d* }" b
4 E% O* H3 j5 W# c3 ~' e# l7 F
>hello.sum.call(1,2)0 w2 Q; _7 B' {1 K
[3,"freewolf"]4 ~% y9 u, n! q% X5 s, g% T
追加-如何建立其他节点4 Z2 O4 E6 h% J
; o% f' j% ^7 l
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息7 S: A' \( h9 ?+ U/ n4 j; b1 H
, L' Q0 B- C4 l+ ]6 }2 J+ N
>admin.nodeInfo: \# O: w8 k9 n
: f! M4 e7 @- H0 d" `/ o2 c# I: C2 B- H
{2 k5 F6 x4 ] j# v/ A! R
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",4 I$ \6 ^% {4 x% n$ x% Q7 }
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
! X" N1 ?6 n2 w! z
ip:"192.168.1.2",4 l+ A4 D" u H, i' u
) J# H9 }& l! }2 a! O* p
listenAddr:"[::]:30303",+ J3 K, j4 F( k4 s: K
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
ports:{2 r) M' g5 U/ W T; q' m
5 x* W2 W5 f+ x% |( C1 w+ Q+ |8 u
discovery:30303,5 P4 o* w) x7 u$ E
. ^2 ^5 a4 Q. [* d, [
listener:303039 b0 I0 k7 x: _: S
6 v+ u: V r1 }" D v
},
9 F$ ~ c6 h& P0 F( T* `8 v$ `
protocols:{3 A/ h( \, l: _4 Z, X
eth:{
( h0 A* a" ]9 n5 ]$ Z
difficulty:655652,5 w% d4 V' T9 H! y
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",% V3 z% F" j% q- i% |& v
0 B/ T' f& g! K K$ h% D% ~
network:270273 V, x9 h' ]; k! h7 J) M7 ^
! n$ V; R u4 j# C$ N) Y7 E( Q+ Z- h
}& x8 r Q* U/ X. A! `- J
}
: B- }3 [' y+ B2 k
}
|" Q9 D6 n( l) x' B2 o
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了% G! }* E, E. z! s3 N
/ w2 S# o4 R: N& F6 ^4 L) P
?>mkdirnode2
$ Y$ w, ]9 H4 e7 V6 T2 t8 T
?>geth--datadirnode2accountnew& u+ j3 F9 ?: K$ L: w7 x: G% c
?>geth--datadirnode2--networkid27027initgenesis.json
0 K7 w. H8 D* p2 Q+ c d
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console
5 V& H3 o& ^; j8 I' m0 v
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令% e0 n" Q3 V/ T' a8 J
7 x1 K" C7 y# m# a3 G b1 V
>eth.getBlock('latest')
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人