建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
112
0
0
$ w# l6 F2 M: }# X
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。) m% o6 [: g# Z/ r4 K! N. r; y4 S$ Z
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
目标
本文目标如下:4 T( M9 e+ I- V& Z3 B
建立私有以太坊,设置第一个节点,挖矿: z/ d( O2 y3 E" P6 p( U6 N1 o1 K
完成一笔转账交易. J+ I3 c1 J; W t1 d
建立简单的智能合约
- x$ c8 I* ?( P
建立第二个网络节点. J: _, P8 }% l* T5 g: M8 S- O8 J
& O& | u. Y- A$ M- T
环境介绍2 t! A B3 g5 X, v/ P
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:0 H5 N* z" B/ @' A8 |7 ]( a" [
操作系统MacOS10.12.6
- ]: w7 ?' d: l9 O) o
Geth以太坊CLIhttps://github.com/ethereum/g…v1.676 N* {9 O; q7 x. Y+ c
1 H9 a" e, r" ^- E8 `6 E( Q
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang
安装
; x' Z# D2 O8 S0 ^( |
安装Node.js,这里不阐述了,源代码自己编译吧。/ c: i9 w4 _4 a$ k, s
* x ^% E( F+ z# N, \1 N9 D9 b+ p
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。
R7 z# H" p! k+ I. T0 m/ e1 T
最后安装Solidity,本地要先有brew,才能进行安装:
brewtapethereum/ethereum
brewinstallsolidity4 y. h% i/ D: O3 w
创建区块链
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。- ~5 Y" l8 A! R6 t6 R: Z- a
文件内容如下3 y4 ^' b/ f5 i' `+ D" R# H. t" N9 s
{
. R, A0 Z* G/ z0 Z/ K- E6 O
"config":{
7 [. K( Z, n6 k7 i
"chainId":2017,
"homesteadBlock":0,
% ~& T) R- d6 S# E2 F/ x/ @+ e
"eip155Block":0,
"eip158Block":0
},; q; S) M, K" |
"difficulty":"100",6 M7 s7 H% s' V! |
c6 I H5 ^' d, L
"gasLimit":"2000000",4 o& G' p2 _9 H% Z2 H
/ H: `4 K% b% x6 v, _
"alloc":{}
_8 A8 x4 j; z1 v) @, }7 S
}3 B* j d$ @1 ^* C l" d
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。8 H: n! P% l0 D
6 W0 ]6 X% h+ A% t. X! [' `
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。
: h' H7 b2 ?2 T+ r6 H2 i
?>geth--datadirnode1accountnew2 R+ H$ V, U2 [* k$ S
6 P0 ?7 E+ s1 t6 t$ {5 b
?>geth--datadirnode1accountnew7 s. ^8 k9 D$ h# ?! F0 m
用我们刚刚创建的描述文件,创建创世区块。
; q+ g6 D% q r
?>geth--datadirnode1--networkid27027initgenesis.json! s# @- U8 v8 v+ @. D U+ {# G _5 Y
- q+ e8 N$ p) Q1 U+ E. l8 g
使用console连接节点1并且记录log
?>geth--datadirnode1--networkid27027console2>>geth.log
' ?! F4 J# M: |$ h6 @. t9 @
使用geth完成挖矿和交易6 B9 c5 I8 T! m. v P% A# }. u
连接成功后,看看有几个账户
>eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]
$ d' x) p& D H1 z# B% i$ v
查询下账户余额
>eth.getBalance(eth.accounts[0])4 C9 V; O9 B; P5 R' S- V% g8 F0 p
0/ y5 e4 w2 s9 u; W- W
, z6 s" m7 Y3 ~5 z8 C0 Q& H
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。- q; h" K/ g4 d
>miner.start(2);admin.sleepBlocks(1);miner.stop();
! E* m* R7 O! k2 C8 j* u
true
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数( j: m" o* h. Y3 [& P
* ^' z" C9 z6 d( Q% Q' b0 v1 \
admin.sleepBlocks(1)挖到1个区块就停止
1 k4 V" K2 F# u ~
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')! f2 z0 x" f& r+ T
. K, ~8 ^. o& V
5
. s' E1 e! i9 h2 u2 i+ I
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。6 |- T6 |" y$ @4 c0 x0 o
" G: _ V" ]2 w8 J
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:
r! _1 n# ]0 M" f$ h4 g7 C/ f, r5 }
true
- W0 z) _ `' t$ V; L% y1 _
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})/ P. X8 g4 K- P
( R5 J, y: C' f, d( Q/ A# n' @
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"
& ~% @) j) ^! u: {5 ^$ i/ U
最后出现的是这个交易的hash,查一下有没有待处理的交易6 J5 @4 q6 ^* m7 `. J
8 }% [, Z; @8 @
>txpool.status% x( c, P) J1 E6 f" b9 Q0 N
{: M: ^6 p4 Y, U
4 g1 c& U- i4 B' T& g: }7 t
pending:1,- o( W9 H$ |/ y, Y: z0 n
( w( B4 @2 K1 J5 q0 }3 G' a
queued:0
' |, [( d! C; ]& M
}7 [/ W' D7 t& F$ I$ v$ \) P6 ?
% s$ `5 j) N. i$ t4 }, |
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
0
/ v! A( V4 |: `" R+ p7 j* b& W
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖0 X5 Z5 \8 G; ^) N5 S( V! S
- C, I+ k- D0 S* D' J& A2 C" g
>miner.start(2);admin.sleepBlocks(1);miner.stop();. U( [5 U( J2 m: H* O1 E4 n; H
% `5 m/ z; O( ]8 `. `# r
true- V: R! ?$ ?/ X" ]; B. e
查下余额
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
2% K4 p* C: K* D! o k6 V) ~
已经到账,再看下刚才交易的详情9 k2 }; l1 |; U" k. F
% T8 l3 ]* B8 Z; l5 O+ ~* k
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")& z" H% S3 D/ Y7 a
- L0 h: {9 ]6 _6 q
{( @& U) t( ~! Y& [. j* A8 {
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",# R0 A: a- I& @0 d* q. e
blockNumber:2,
+ G0 s- J+ W3 I/ ~
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",& h4 s+ e! D& l! _9 `
gas:90000,6 y6 J) H. q. R( g. H* K1 c, W
gasPrice:18000000000,- [) J+ J7 l/ _, A
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",, u5 E3 z/ f; r. f! T
/ }9 z- v. D- O/ b
input:"0x",/ l% Y7 m4 m4 O& \. R$ D' I% k+ E
nonce:0,( F& n ]. [6 X1 e
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",& }( J. J3 B% Z0 m# V5 a
U. ~4 G6 { H. P/ m
transactionIndex:0,
2 J* R& H o1 X% \& H
v:"0xfe5",$ p% v! V' V- s' y" p7 A- c
, V4 E+ h0 d$ R! ^( S d+ n
value:2000000000000000000
3 ?, V3 z: Z8 E N2 j+ B0 V1 i, j
}
2 f' `! E& Z( s; k
到这里整个交易就完成了
简单的智能合约
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
$ ^. ]7 C2 h% s# T
pragmasolidity^0.4.13;
contractHello{2 D `8 n; Y, E, x7 Y% W' n
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){; F2 ^0 I1 I1 Y
o_sum=_a+_b;9 t* z" G; k, n9 q5 s
o_author="freewolf";
2 g+ b' C6 i, c$ {* f
}
}4 g! H' J% q- e1 C, I+ J# n% @9 Q( u
$ S4 V' D1 D) Y; m: {% |& T& d
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。
( E: u. v; G% `% z7 ?: O7 B' w
这里是Mac命令行环境,不是geth,?>开头的都是命令行7 U- m- y+ Y; p) t9 d2 d a5 f
?>solc-o.--bin--abihello.sol
?>ls3 V6 m$ C* _: j3 x
& a" y- L, }: s! V2 V% [) ?2 s% @8 k
Hello.abiHello.binhello.sol! L! r/ @! c# z+ `( U; E* ]& Z
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件4 l6 L, I2 a, s, i$ H+ l+ m
. |! h& ]4 @* V. g, a
Hello.abi文件内容修改成
3 ?" [- U4 _( I5 k( t0 r3 N
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"}])& h( j* L- l! z7 n7 ?) p- q
Hello.bin文件内容修改成% s6 [# I% Q X) p
personal.unlockAccount(eth.accounts[0]); }, y E7 c" L) z0 E
varhello=HelloContract.new({
from:eth.accounts[0],9 L7 b1 Y+ J: }. _, P; ^4 L3 C: [
1 \( j' o, J0 p' a/ I
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
/ R+ B f& E5 A' H, o, J
gas:500000
})
( X1 Y0 m0 K7 }" ^7 q8 n
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了: y1 {7 Q, U* ~- L- }; @- g7 k/ r
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码" E6 B5 i8 d/ v. e9 p# ]2 ^* \
文件夹contract就在运行geth命令的目录
>loadScript("contract/Hello.abi")
4 T) |# G1 G+ V; u8 M) }: n
true
5 ]/ _4 i; p7 H1 i7 K2 T# W: n. s! z
>loadScript("contract/Hello.bin")
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428% l8 f' ~& c1 d
Passphrase:$ k% A7 J$ }- u
% {! G5 F( w% i9 {& ~, w
true% y. j; D0 w, `' p
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。
# {! X6 ?5 f' t8 o* \
>hello- p) `# W. W* T/ \: Z
{
abi:[{
constant:false,9 s* q# r: L" ^ `" i( L! q
inputs:[{...},{...}],
name:"sum",/ f: j* \+ L: K: S
/ t c. n: P% y0 q% v) L6 k! t8 r
outputs:[{...},{...}],
payable:false,; X: T& F( i O; I1 k
4 |- L; |1 a& U3 x" e
type:"function"( T. m% o( _2 B: N% p
}],' ]) J7 q. ^; |1 @. _
address:undefined,
L6 ?( N$ O+ z( S8 r
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
" i1 q3 Q2 P2 f3 I
}+ y2 j6 z/ \! b+ V
% n' y) C* [: d# Y' q, t# m
>hello.sum
7 v4 y F7 D1 ~1 V/ F3 J/ t! ?7 x
undefined) V+ L/ v, a4 { Z
! r. k' O$ s+ S4 s4 i
>miner.start(1);admin.sleepBlocks(1);miner.stop();5 M9 ~/ U6 ]: t
true
>hello.sum4 ?& C4 p3 ^* I) r4 F- \* w
function()
>hello.sum.call(1,2)6 `" T4 Y: h6 j, X6 s$ q
8 C: E" L0 T" @& r8 D4 a
[3,"freewolf"]
. h# _$ g' }* x( @* l1 e; S8 S: d" H
追加-如何建立其他节点( C* {5 r v) u {3 g3 t! }
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息7 M# X5 o/ {; m- `$ X2 S
0 ]- {$ q: ^( y y. I
>admin.nodeInfo
{
- }. y* A2 x1 L6 Z' q, k
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",9 z0 |4 R( j# w# i& X
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",5 w- g' l) _/ B) B! |5 |0 G
ip:"192.168.1.2",% Q2 r. @7 J1 F
listenAddr:"[::]:30303",9 U, S7 n# M( w8 F$ ~
( E" d2 a* T) u$ P8 J. V
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
5 ~5 j7 g: @% H
ports:{
discovery:30303,
listener:30303) _# }: ? Y( g
},
protocols:{
3 i( x# x; F' r8 k8 S3 y9 R; a6 Q
eth:{
* o: J% m1 T/ `# w
difficulty:655652,( L! K. j. u8 n, e' z5 I. K/ [
( K6 L! }5 J8 a1 i2 m: [
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",
& `" M( {- W# U4 x0 L R8 z C3 W+ S
network:27027
}6 C! G% }% s0 `) }* {3 G
: d( Y) y2 O* w
}
}
* E( b5 T( W, q8 v5 j
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了( y5 X5 t& t! K6 L/ h4 g& ?3 F
' T# ]5 s& j6 H9 h% _; h2 S
?>mkdirnode2: ` a' i) c& i( c) v
?>geth--datadirnode2accountnew; ^+ W8 x) I' w2 I* S, a* ?
?>geth--datadirnode2--networkid27027initgenesis.json* R% L6 i) k0 O
, i ?9 F0 v4 E- Z6 Q$ x4 r
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console0 I/ Q7 r# e8 y, k
$ y2 G4 Q- ~3 I" ?, b( g o
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令' [# |1 B+ V0 F1 l
>eth.getBlock('latest')
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人