建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
85
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。1 D$ }! n. t1 k$ D/ d
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。5 q5 u( l" m% T+ v! w* @% j
目标
4 {/ g# O& n4 |- w: c4 B0 ~9 Z
本文目标如下:# \$ ~$ X g1 ?$ G$ S
建立私有以太坊,设置第一个节点,挖矿
* N( Z2 u, p- v8 L9 v! \
完成一笔转账交易0 s- X% y& `( |, ^- |3 h
8 Q9 U* {9 `; ?/ z( U+ E: j
建立简单的智能合约7 i! c9 Y& G/ C7 X, S. y
建立第二个网络节点
% H9 L5 T+ h. a1 h
环境介绍
8 ?- O7 q9 `- F& a9 c: K
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:/ _/ t- ^1 e( ? G( {1 J
操作系统MacOS10.12.65 ^1 G* l# @" O+ w t
; G6 R' K6 T9 F5 l; M, {
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67% X5 Y: u& U/ N4 E
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang7 p, h; D* t) s4 O+ X$ F7 j `- T% p
- W: P5 _- U8 U% |4 j% T5 K
安装
( T7 R( r8 w) A' ^7 T6 g
安装Node.js,这里不阐述了,源代码自己编译吧。) X( `' ~0 Y& j8 X
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。; B* r, `9 T3 \! G* v
8 s% w/ }& f8 `: X
最后安装Solidity,本地要先有brew,才能进行安装:4 H8 e+ M6 ?1 Z( d6 r( X" F
1 [4 u# L% r1 u$ h. K3 Q5 t
brewtapethereum/ethereum/ j$ M# d/ E! L# a
brewinstallsolidity
创建区块链* k1 m8 N4 J0 D/ \1 h9 m
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。5 P' g# C3 T7 q; x0 @& u
文件内容如下
7 V; x/ D7 {( H5 f% _
{* u6 q" }0 s- V' ]$ B. t% u
, U( f1 H7 g5 T& W* V
"config":{) `2 A. G! h) \* `% k3 \
"chainId":2017,
"homesteadBlock":0,
"eip155Block":0,
"eip158Block":0
},# j% t+ ~. R7 v. q
5 J' {' i" Z ~3 O& E
"difficulty":"100",
"gasLimit":"2000000",
"alloc":{}
}6 c; M0 o- F* m1 m
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。- e% Z; q7 f6 {
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。
3 h8 }: f; ]- p# u7 J5 N* c* M5 v
?>geth--datadirnode1accountnew
?>geth--datadirnode1accountnew2 V! @& E: l) h! B0 ]9 X, v
& ~8 {) K' O2 c7 m$ p/ t& b
用我们刚刚创建的描述文件,创建创世区块。
* y$ i L4 L0 V! Y1 I( G3 z, N; I
?>geth--datadirnode1--networkid27027initgenesis.json
使用console连接节点1并且记录log0 L M. L; z# X2 M" w% J
% Y O9 n( H! U) M/ h# l
?>geth--datadirnode1--networkid27027console2>>geth.log
使用geth完成挖矿和交易
9 E& l$ y3 |8 ?
连接成功后,看看有几个账户; q6 i j! s# b! F# e _3 r
>eth.accounts
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]6 J. U, _4 v6 T g8 t# U" w
1 v1 H+ t# u N- c; E9 n
查询下账户余额
>eth.getBalance(eth.accounts[0])
6 V, o/ L4 t$ A/ L) ^1 X' d$ N
08 f6 _# I4 J' B; A# W/ D L7 L
0 x. I# z6 a6 y5 R6 m
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。; T5 ~) M' a8 {* \2 D
, W1 n' G2 @5 {) j+ a; p
>miner.start(2);admin.sleepBlocks(1);miner.stop();) B6 O4 A9 K8 z2 b1 ?2 I
true& }# N) K! _7 \) Z+ g8 `
/ _$ A$ j4 A+ b7 K( J) O) O% J
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数& l' p! X5 {1 z8 n, W0 R
admin.sleepBlocks(1)挖到1个区块就停止
9 s( q1 b8 x- m/ A$ I0 V
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。; V/ d5 p* p1 l% g1 m! J, F
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')4 P: ^& R! E& H) e2 ~
, P" ]0 A) y/ W- a/ k9 Z9 [0 L/ L
53 r; h `, @5 y' ]
5 o: S9 j. h$ h& k+ s- @, s# ^
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。' N! O! v1 L/ n. l$ _9 R& @, b9 _
>personal.unlockAccount(eth.accounts[0])
( d* r( N; {6 `3 F
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc4285 }- t0 c9 Q h
Passphrase:4 i x# T5 L/ f) V. r. j
- c: C. c9 a3 K4 t1 H% Y& m
true
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")}); | Y: }. z4 q% y
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645": Y9 a' g7 ^: U: J+ s7 A
) N* K7 z; J8 `
最后出现的是这个交易的hash,查一下有没有待处理的交易9 c0 H: b8 F+ s5 y7 m( d" H' |
>txpool.status
{: G& B9 g. D- J) M' L- b
pending:1,/ q5 i9 @; B5 f0 w& }/ B
& T0 i- d7 E; z; Q% { p; I c1 Q/ u
queued:0! l$ j c( H; w) e; {
; t8 A1 e/ {, Z: u2 z b2 k" c
}) }, V+ ?0 y( m8 x2 U
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
J8 o6 u4 y7 O+ M/ n+ R
0
* v/ Q' V) v. ], U. @
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
- |" B) E3 p) z* O! L/ J/ w
>miner.start(2);admin.sleepBlocks(1);miner.stop();
true
: O8 ]/ c4 f' N0 y5 J1 w
查下余额( O2 k" k) E5 a3 P" S) \
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether') g( o# T# n9 y
2
已经到账,再看下刚才交易的详情 l% x6 j( p2 {
" ~& |- Y; T+ G9 z) d7 M+ }' H! |
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
* }3 B1 Z4 k: Z9 N4 G* j4 D
{
) j+ @. p2 g0 L" a1 s9 ]+ D
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",, J7 {) m% ]( ]' P- }7 [ u
blockNumber:2,& J2 Y& x% e7 C- e( d/ V
3 r; ]; e, Z% G' H
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",( }- G% E8 l9 R! y7 o
gas:90000,
( {+ i0 p7 E: O) u: e/ |5 S# v- |6 a
gasPrice:18000000000,7 ]. @ v& l5 z# s, E6 n8 R
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",
6 r% n6 f' z( g+ v7 J
input:"0x",6 y, k$ j5 ]: S- o
nonce:0,
) e* J$ t- c4 `$ j
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
/ `7 H1 O, i1 I& J% p
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",' E, e& Q* A4 R# w- \
- l, v. D! }- A( r# l* ?& ]
transactionIndex:0,
6 L5 p4 b2 |( U) Z2 {& v4 T v
v:"0xfe5", L) r6 {. ]2 r9 {; u* O
7 R% j% p/ v0 M6 a5 L F: d
value:2000000000000000000. F" a5 t, B7 \, a7 K! R! U( c
}$ Z& y! H$ }4 h/ l' A# O& o* P# O
到这里整个交易就完成了% m7 b/ I! \) S( o m8 o
8 \' c5 B1 r0 \( d# A
简单的智能合约7 a7 C& t: n. w: t0 `( p
' m; p7 g* D; t: K
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
pragmasolidity^0.4.13;
contractHello{
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){( \+ g3 B; W/ ? Z
o_sum=_a+_b;
o_author="freewolf";
; H# t3 [0 w8 x. ^
}
& @; u! m) g( T/ ?# z' |) p6 B
}
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。* _7 ~* V1 ^* d2 Z
这里是Mac命令行环境,不是geth,?>开头的都是命令行; A/ n, V5 V; Z) k; K! r
2 g" G; b; @) |% a7 G% E
?>solc-o.--bin--abihello.sol
?>ls" N0 v( n2 R" Y9 J u, r
: b6 X& L4 Q) n7 s
Hello.abiHello.binhello.sol7 a/ o0 i, K& {7 Q% b6 C, f/ K
1 J: t) l" r) u& \& j- ]0 a5 Z
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件
Hello.abi文件内容修改成
& `* q, Q3 s3 a# A# B7 J
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"}])
* U2 e( n8 C6 [* L8 X, S( s. u0 L" E5 v- Q
Hello.bin文件内容修改成/ ?# V8 K f' e; ^0 N) Y
0 }% u, |5 w7 K' P4 b8 E
personal.unlockAccount(eth.accounts[0])
varhello=HelloContract.new({$ B2 a; J" g4 S- D4 v# O9 ^
from:eth.accounts[0],% d& U+ T6 n) ?
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
/ `2 K8 |' v" g& n1 R8 \. \8 M' T
gas:5000008 t7 _& \2 ^: t4 v
6 N" d" o: G# _2 n* S
})& X$ X3 m4 ^: ^0 [
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了# J- s& O$ ]* F
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码6 }: O, i) K6 P5 f8 S. }
$ S2 D/ w3 n$ w
文件夹contract就在运行geth命令的目录
>loadScript("contract/Hello.abi")" f8 B$ Z+ Z7 `9 i8 g
true
; S, M: g, X% z; K& G3 h# A( _ {+ k
>loadScript("contract/Hello.bin")
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428$ j7 T5 \% c% k5 @! J7 T
4 w$ g- D+ i `
Passphrase:
true
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。
>hello
{
! D6 u, H- n8 r- ?
abi:[{3 [. f3 ^, E5 ~0 d
constant:false," Y7 g2 [' u ^. e
/ a9 B+ o* S' T# b
inputs:[{...},{...}],
name:"sum",
$ R7 m3 t% J/ P- j2 B6 n
outputs:[{...},{...}],
' N: _ j: C& [ ?8 X; `+ l
payable:false,. p$ k- x% q( y8 O; V: `( v
type:"function") E# X0 f) a' f: ?0 T3 y
}],* a: K& m w. n: k7 F3 C
( q6 J1 b; h$ ^) r m1 Y
address:undefined, s& _" P8 ]! S& c" w7 }- u1 W
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"6 p( @) f0 O0 l* J& j, {% |
}
; T1 L' T- _9 Y& w+ o
>hello.sum8 f# \4 `6 n7 ]6 M2 H
+ B3 ]7 A: w9 f
undefined
>miner.start(1);admin.sleepBlocks(1);miner.stop();
true% R! N$ ~: D! A" A+ m% `2 h4 M( w9 N
>hello.sum% V$ C9 y( i) Q/ }9 R1 d! @; w$ U
; t6 `2 \# w- H, B6 A9 I/ h6 D* \
function()
>hello.sum.call(1,2)
0 N9 u& ]4 J. e8 o1 f
[3,"freewolf"]
追加-如何建立其他节点
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息
>admin.nodeInfo- i/ y% q2 h8 j5 T
+ F% ^8 M4 h; j: D& I
{
+ x* D1 s* f& S/ i- S
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",) t" z6 u1 Q6 y7 M' q
& Y! @/ y4 ^, e% }
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
1 i! K& e7 R6 L5 j/ c! ]# P
ip:"192.168.1.2",
* T0 o/ U- ^3 F) z
listenAddr:"[::]:30303",0 o: i& b) t) |, q \
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3", F& `; @2 f# A3 f) Y0 a1 y: k
! t. L5 F. M) c _; H4 r: p1 p- N- T# t
ports:{
discovery:30303,; o# B D9 f3 ~1 y
listener:303037 v# Q8 V8 ^: [' x3 }+ x
},
" U. ?, C) z7 S3 u/ S N
protocols:{
3 @' S( j1 F. i
eth:{7 U. _3 d" s7 l9 ^: R$ ^8 L6 m
difficulty:655652,
) F% p- d. n1 g8 n4 v( J
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",; T2 a8 P% w6 X: m5 {/ v( H t
% J! {: X" q; [- A4 q
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153", d9 w$ V Q2 a% |- ~* b3 \
network:27027$ ?7 j4 ]; r# q# @8 t( j
}- M7 m* ?3 ^3 o! S3 }7 z; |; M
) x5 E8 v0 v2 x/ }& ]
}
}
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了5 k0 m* n' J! _% ~
, C! P/ h% U4 q8 Y% Q
?>mkdirnode2
?>geth--datadirnode2accountnew1 h# ^% _! D2 R# X$ ^5 V
9 ]( ^2 {5 D+ N, f+ U
?>geth--datadirnode2--networkid27027initgenesis.json2 ]) G# G8 e0 P/ K" T9 t
# s9 Q- ^- ?' R5 _# o7 m( p
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令
>eth.getBlock('latest'); j# v; K, Y3 h* e5 c
5 O2 @# B5 u }- r$ D# ]
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人