建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
269
0
0
5 [( o/ W: t1 y& \6 r
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
. A& v" }' S$ x6 i5 j
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。$ g& Z! j8 A- m9 H
) V; l. }1 R6 C! A+ T4 l4 g0 b" z
目标! x8 \1 G" f) g$ {1 |- e U; `" u$ Z
本文目标如下:
建立私有以太坊,设置第一个节点,挖矿
7 k A @9 h) i. X' s
完成一笔转账交易
建立简单的智能合约
; ^: t- g2 t+ J, r B* W# ?
建立第二个网络节点
- [; p! n7 s/ F! D
环境介绍
$ k" }! U0 U6 \ k9 d/ @- Z/ q
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:
操作系统MacOS10.12.6
% F4 `& R1 c: k1 k$ g
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67
9 C3 T+ c" D, _9 ^) e+ K) l
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang" I) U. U. o7 [: B ?8 z
! ~+ h1 x$ H4 n
安装
安装Node.js,这里不阐述了,源代码自己编译吧。
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。
最后安装Solidity,本地要先有brew,才能进行安装:
' x' q6 Z2 @: \# q" ]+ H# P! Z
brewtapethereum/ethereum
brewinstallsolidity
) F3 p+ h: {8 t3 F( R2 a" C9 i
创建区块链
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。
, Z6 U, P" t6 e+ ^/ @
文件内容如下0 u! _& F: k( S$ v8 F
2 N7 u4 O0 V$ z! L% O
{2 r+ B( z+ G' d
; V. u5 S$ l9 e, x3 x {
"config":{
"chainId":2017,
"homesteadBlock":0,* q' ?# l& L6 p' U8 E/ s1 g
"eip155Block":0,
% Z$ D0 y& D. m
"eip158Block":0
5 i; o) x& o3 B6 o5 I, q
},9 s; Z5 d: U0 u8 g8 C
"difficulty":"100",- U1 `7 k) g9 y/ _
4 \ ?* j9 r! A- S* E m
"gasLimit":"2000000",+ x' f+ o- ~' ], O$ R* w, q
"alloc":{}& |1 N9 W* K3 h* [: |
$ U4 l6 ~$ w z3 G0 \
}0 n) d s( q) B* |- ^% s
4 F5 f4 I. k" n4 c- b( ?: Q
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
2 o" L3 Q: P" y( o, G# j
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。4 F) P! Z3 o$ _! J8 ?/ t! X: ?
?>geth--datadirnode1accountnew3 X3 K1 ?9 ^9 `9 L9 _
?>geth--datadirnode1accountnew
. d- ^/ Z' \/ i# v( V& S: ^; n, @
用我们刚刚创建的描述文件,创建创世区块。
: C) e1 _4 N+ g& b( U
?>geth--datadirnode1--networkid27027initgenesis.json7 x+ T3 D3 M M. f6 J
. O. ]& l$ H% i H3 W
使用console连接节点1并且记录log' k, @: w9 D/ d
0 [9 x3 K' `; N8 m' g& F2 q2 D
?>geth--datadirnode1--networkid27027console2>>geth.log
使用geth完成挖矿和交易
连接成功后,看看有几个账户
>eth.accounts
( D6 T# `1 [ o. W. C
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]
: V; o4 e7 ^% b
查询下账户余额8 G# g3 W. P' G W4 f" L, ]
$ F" [ t; O5 }0 G) v
>eth.getBalance(eth.accounts[0])0 h7 r" V- L, ?8 i! T) T) ?) d. b
0
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。/ q/ U+ K& J4 B( S. O) ?* g
>miner.start(2);admin.sleepBlocks(1);miner.stop();$ S9 ?) e Y& g" B I& Y/ G; k: s
9 f# k( k. S6 F
true
# o) N" _% o5 X' e& Q$ m, a1 M
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数
3 a) X9 t% N9 {8 a5 h
admin.sleepBlocks(1)挖到1个区块就停止7 E$ V/ d# ~; ?% O+ D
0 ~, s& \- A. F6 O! Z: d8 U, y
miner.stop()挖矿停止1 p! j% @' O5 J. y0 t* |- G
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。% {7 }* a1 A! |; c4 i
; o$ ]1 K" H5 x+ N; }. H$ a2 Z+ e
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')% v6 h5 w. J+ I: j) e, m
: A. A! {; b) o
51 L, O& ?, y+ l
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。: X; D& S' z; ?4 U H* S
5 M# ^6 G N$ k2 O* y3 v1 e
>personal.unlockAccount(eth.accounts[0])- a- z3 h2 r2 n+ k- `
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
/ N7 E! D, g; W8 ^# P7 M- l/ W
Passphrase:
true
4 U7 P) B$ a( m" @, j
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"
& s, I3 E% m" [* q8 I8 R5 `0 J7 i
最后出现的是这个交易的hash,查一下有没有待处理的交易
6 n7 L4 }5 z3 v# `4 }
>txpool.status
. W4 ], j% t4 e8 {; Q) c
{
pending:1,
. }7 {4 H) D0 Q" I3 P, l$ |% a
queued:0
S1 V$ ?; r9 o8 D( `
}5 ]6 Z" J: P) A
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')7 Q) k4 i2 M$ M
a; I( Z' N2 G6 r3 U2 g( h! g4 K
04 W8 x a# b& h! _
* i7 K* E9 Z8 |- i. @- k
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
. v7 V' t$ m8 P+ C. J# T9 V9 z
>miner.start(2);admin.sleepBlocks(1);miner.stop();
6 v/ k$ E% S1 |+ o% |
true6 H2 e5 _, r7 k) }% b- A4 F) `
查下余额
: Z( W$ S5 C Q! i1 p
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether'), ?# a9 x8 y2 M, Y
2* [9 k5 j; g' s) S, F& r! R- h
: h& `$ t* _, ]: P# ?
已经到账,再看下刚才交易的详情8 S7 W6 X' B' _7 e6 m6 H
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")- I' e( E: d' w5 Q" U4 X
/ R' p& c3 w8 r* h( K
{
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",
blockNumber:2,$ K: P% i: a' z9 N2 Y7 p" V
4 b" _5 k+ h! Y$ B+ Y" H* b0 q
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
( f! t5 F% x/ }% V5 j
gas:90000,
" x7 y; ]; z, ~8 H
gasPrice:18000000000,* `; ^6 s9 }, h- }+ w
) b+ i9 s1 K0 h/ @ q) Z
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",: _& i5 B2 ~2 W( w5 z0 t5 t
input:"0x",1 |2 n4 X1 K6 j) F' M/ ^" H
nonce:0,% A; U+ ^% v& U1 ]. Y) z# Z# {- B
5 A. |. g! C3 A
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",1 d0 v# h9 g3 w) R! @: k# B
; R5 B! P- e% p& @. U0 @
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",, E# ]! _# m2 p3 F5 L0 B
; _1 b# W2 o3 u8 c( h! m9 Q
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",7 i8 P2 n1 M, f l
4 Q& [ G% p1 r; X) E; K, i9 U" Q! R
transactionIndex:0,6 u6 m/ k" K6 N7 R% ?( \7 c
! P8 `0 U! m7 Y" e% h$ `
v:"0xfe5",
value:2000000000000000000$ b, g& d. l2 W% n+ j
}8 q, w; R/ {+ G3 p9 R& D
R8 a e) S, A% k4 X) K2 M
到这里整个交易就完成了9 m9 e$ O2 [9 t; f
6 X& S6 k' g6 r1 d4 S6 I. Y
简单的智能合约
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件/ u7 o$ N, c! g% k& X! P
pragmasolidity^0.4.13; \4 [) K; S! r9 Q* G
3 A& Y3 {1 L, z$ X) @9 W
contractHello{
1 ]8 \9 g& q3 q; k6 x* ?3 u: t
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){
+ a' O4 q0 B" Q0 D/ t; ~# H
o_sum=_a+_b;
- q3 D& u" H- V6 m4 l g$ c
o_author="freewolf";
}$ ^8 o' H7 ]- V1 V% a
}/ i: p( t k2 M6 V
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。! H" w( @8 I' [9 q' U
3 N# s) w. U/ c5 L7 B
这里是Mac命令行环境,不是geth,?>开头的都是命令行) v7 O: n. G5 i3 x' W
. j# {: o" J5 X1 _7 f
?>solc-o.--bin--abihello.sol# I% r/ q0 \; U0 S. h
8 o% Z9 {: d# I+ T
?>ls
Hello.abiHello.binhello.sol
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件
Hello.abi文件内容修改成) Q9 X T( S" r
: U3 K3 N) T5 z" d- c
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"}]), y2 `$ x7 Q u4 k7 R8 r
6 E& L- U( l ^. V4 L
Hello.bin文件内容修改成 ]- V L3 v# D L
9 B D0 K5 ]0 R, t% E3 y
personal.unlockAccount(eth.accounts[0])9 ^7 R( M5 W/ Z6 M1 X
' ~( S! b1 N" f% t0 u/ o% i
varhello=HelloContract.new({1 r3 m5 M, w c( ^: R
! r; _* `; D% R+ E8 N
from:eth.accounts[0],
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",- i' H. h" G, p8 s( l( M5 P
gas:500000
})
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了, d8 L4 G" ^5 E6 G: X% F( o
O1 \' ~: w) P8 J$ z/ r$ B3 y2 z
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
- p0 l! p$ f0 x
文件夹contract就在运行geth命令的目录
>loadScript("contract/Hello.abi")
true! Y- i& E9 ~1 |; p4 v6 }
, m' o Z* x2 d( t" K- \" X
>loadScript("contract/Hello.bin")
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:
true
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。8 |8 i# N! R6 M7 O3 U! C5 b' J8 q
>hello1 k% L, N% f; ?9 A0 t; r0 M/ A$ \
{
. y8 _# Q, c# V: y3 |4 ?; ^( H
abi:[{% C; H! _* Q, [
constant:false,
: }; [) q, C- I: X! a4 Z
inputs:[{...},{...}],
name:"sum",
8 E' U3 C K9 D K2 X5 @7 ]
outputs:[{...},{...}],3 o5 K$ W* L! ?0 f' P% [
payable:false,. n# g' v5 ^, z% g( Q! U( Z
. l% d; N7 b2 H# X" O
type:"function"
}],
: p4 ^) w8 K+ r
address:undefined,. Z9 W& Z# b' M3 h8 W2 k
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"! A0 o/ x9 {5 f. D
7 t! `: {$ \; g: w9 i+ F8 R
}
>hello.sum
undefined
' k. h1 k# O7 q/ }6 i, Z2 q. n
>miner.start(1);admin.sleepBlocks(1);miner.stop();
true
; o; [) Z$ n2 \9 ~6 E8 \
>hello.sum
function()
$ S& J7 Y8 p& A6 M) B( i7 P+ e
>hello.sum.call(1,2)
[3,"freewolf"] ]$ ] {5 ^2 d& m0 t# H# \* W
' k$ \' G9 S: x/ j
追加-如何建立其他节点4 [0 @6 f+ F- z8 l2 N/ n) p/ n
) H' w7 V. f2 M" r. Z
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息
3 ?$ W2 u8 a& ]. @, [
>admin.nodeInfo2 }" `4 ~6 ]6 X9 ?- H( v" m
. f6 q6 D w7 v5 }; f
{
5 y) F9 y3 Z) s) g. a0 l- ^. o# u
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",! \$ O+ v$ e, _3 K7 b. z
- K9 q6 v0 }- X* X9 V# Y; Y
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",; I; X, P5 M/ V& }% @5 _
7 ]8 C J4 m1 t5 ^" x9 q& U! [& J
ip:"192.168.1.2",
listenAddr:"[::]:30303",) g7 M. D* ]5 H' X, B) d1 ]7 h
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",. K, {# G% Q6 Z1 K# `/ t8 n
8 t0 b( G5 [ C) Z* L
ports:{
discovery:30303,: A) Z. i$ @! t9 N2 {
' |( e2 E# p: K1 P, @* p7 L: V( w/ _
listener:303038 U" _5 p' m1 ~- Y+ p: B
: h7 N8 ` U1 |4 A0 f% f
},
protocols:{" n3 K7 |4 z; A7 n5 C( Q
3 Y. a, P9 X2 n( k
eth:{2 G! m. `+ n7 q& b
2 r1 _0 r9 |) B) ^) _
difficulty:655652,
! k, H" ?% L1 x, r
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",! Q+ `2 L+ V9 u# V
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153"," V; A. p. L5 T) {& g( E X! J
% a, }" v- Q: ^( S" J! \
network:27027) K& r& t9 c" a
; T2 @# o7 e) I0 g+ ~$ d; v
}
} t5 ?; V6 Q9 ~ B
1 }4 J1 o- H5 p+ p
}
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了0 q1 q8 I& i) z: s: \5 A6 C. c
?>mkdirnode2! D$ ~9 ]" C) ~6 |
! b2 b/ M* H X+ m7 r; \5 x$ I/ R
?>geth--datadirnode2accountnew. N& V) t% T+ l+ [5 o
; f4 z; ] I, |8 P/ z
?>geth--datadirnode2--networkid27027initgenesis.json6 ~9 Z9 O. A8 G
) r& d; g+ u# F N v) e! g
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console9 Z9 U1 _; E6 _! m; p, |; Z
; p1 }* E: u9 v4 g% P7 Q8 x
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令0 {$ y9 X/ I( J# r+ ]1 T
0 P1 f9 o9 G F/ ]5 B
>eth.getBlock('latest')
/ F, f, }% Y% Q8 q o4 a8 r
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人