建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
270
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
. a: j# O: C5 D9 X/ h5 |
目标) N K+ P4 V0 n8 l# P
本文目标如下:
\5 m; d5 J/ x5 X; d' K
建立私有以太坊,设置第一个节点,挖矿
完成一笔转账交易! J( c8 `9 B) a, r, L2 v
X0 G- P# o- Y* |0 \
建立简单的智能合约
建立第二个网络节点5 m" }6 P: y( T+ }
环境介绍
( I1 |! _: I: I+ d
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:+ R7 h! @8 y* j( ]; E& I
+ @7 e6 [/ K, j) x, \& w$ C& e
操作系统MacOS10.12.6
7 f. i7 C+ y* M% ^7 A7 L
Geth以太坊CLIhttps://github.com/ethereum/g…v1.676 x" S4 |6 l3 b
e5 ]+ n! q5 G( h: p) \- J2 Y
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang
3 N. l0 t7 r, i, R4 y- J8 C3 g! J
安装
" D5 u, ], @" f) q7 u
安装Node.js,这里不阐述了,源代码自己编译吧。
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。* p- I9 f- r4 j4 y, k, f7 o
最后安装Solidity,本地要先有brew,才能进行安装:
) [; f" ]# L3 p: E
brewtapethereum/ethereum
- Q4 J6 P9 v% H
brewinstallsolidity
创建区块链
( i' z9 d |' M5 y6 R9 f$ j
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。
# [& k2 f* ^% q4 U; r& y6 c% \
文件内容如下
! C( L7 |" m7 f% T. Z$ m( V
{
t3 R7 d+ g, g) j
"config":{
"chainId":2017,, U1 G# @: N3 t
. P$ w* L, v; F4 |5 k! R- j( S( N
"homesteadBlock":0,
' M; M/ ]: k$ j$ p2 t* N9 J
"eip155Block":0, l- X0 ?5 u; x* G) L& j, w
"eip158Block":0; C+ p9 H# B6 w$ r0 y1 A- w. X
},& ^( U/ ^+ }. N! P; x) q
& ]9 |7 Y% U( \- v h4 ?
"difficulty":"100",
"gasLimit":"2000000",
8 B) p* i% I, A3 d. J, F$ Y5 l b' F
"alloc":{}4 c) t4 @& e, Y( c! E7 |' u9 p
: [* Q8 Y# e/ I+ n! w# Z
}+ X' p9 c' o$ b7 @" ]: G4 P
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。* n9 U* K* a0 Y C7 ~$ _
; Y1 r2 U% Z) z
?>geth--datadirnode1accountnew
2 p, K0 K) q) O; x
?>geth--datadirnode1accountnew
用我们刚刚创建的描述文件,创建创世区块。
?>geth--datadirnode1--networkid27027initgenesis.json* \5 [9 N( @! d# s
使用console连接节点1并且记录log
9 h9 f8 Z" B+ _
?>geth--datadirnode1--networkid27027console2>>geth.log1 p( y1 `- Q$ [0 n0 U- D
使用geth完成挖矿和交易
连接成功后,看看有几个账户1 s5 {# g7 s' J, N! ~! J
>eth.accounts3 w7 x( @+ _& ]1 g$ ~- k# q R" z
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]
2 t! p- T- G0 N8 q% \
查询下账户余额
9 j( V. k8 u0 e; i
>eth.getBalance(eth.accounts[0])
0
9 [: t+ G4 ^8 }
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。- E# q2 X9 h0 v4 p5 W
>miner.start(2);admin.sleepBlocks(1);miner.stop();
true4 H/ ]& f, }) U$ C2 L1 @3 e
9 F- g( }0 Q. t: W9 R1 h3 D
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数0 F3 I. H$ p9 k7 X
admin.sleepBlocks(1)挖到1个区块就停止; T. N' {. @5 ]9 V" t. w* M0 d4 i7 I0 e( G
4 }# U, B- [0 i& S. O' a1 J" I
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。
$ X) U- l' A ~( y
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
5) X) M9 L; ~2 |7 T: \
; r+ ~; _9 U' b: M$ t0 C
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。' w% w' y* F A/ m
1 P W! |5 @, d. L* B
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
+ y# Y3 C) a+ [+ T* v! d
Passphrase:2 Q$ f- d6 \8 R6 k6 g5 j( p$ m
3 a; L# E& b1 `4 h! L. I1 p/ J
true
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})
7 s' W0 a& w9 @( }3 y
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"! ^. w* N7 }% }. _; m: P5 [4 h1 M
最后出现的是这个交易的hash,查一下有没有待处理的交易$ M( O' m% H4 L! G) s' }1 }
8 S$ q, _5 u' R& {' R- ^
>txpool.status8 r, y! t: m: ~3 H0 }' \$ r! C
- y) D% |8 a8 M$ s9 B) F
{ i2 X% S( E9 W7 {9 b
pending:1,
queued:0" b" D; R/ y8 {. D
$ \/ h3 g9 x. j: E
}
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')! u* R4 f2 D1 v+ [
2 v: O: p4 C! z( x
02 v+ t/ T- F% U6 K7 a5 U- {( Q! e
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖* E+ r( S- J" [" d/ t
>miner.start(2);admin.sleepBlocks(1);miner.stop();
true& W" g4 j Y: m
查下余额: A& c J# k1 x& K
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')7 V+ V }3 z4 o' Q& Y5 x& A
6 c. h. j' t& l/ z0 ^5 I# w
2: g- r0 \$ c1 w9 J
已经到账,再看下刚才交易的详情
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
{8 ]0 u0 M3 ~% _4 y3 l3 v2 r
8 V: I4 c r9 l
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",
( }+ f$ ~+ ]: k0 U6 t5 s
blockNumber:2,
& n5 I: Y8 h: B, `2 {4 ?5 x! C( _
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
- X- \/ N- \) w! h
gas:90000,
$ A" i9 p1 ~& f' e& p
gasPrice:18000000000,: G7 W& c+ Y" K% c7 F0 O6 `
) n" o' C7 K+ {* v
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",4 u$ H5 t8 j) w/ c
input:"0x",9 X* B: ?+ M" s5 }7 d: d
& ?6 U. A6 D' t' i" n% {* `
nonce:0," ]6 g( H2 \% t& k) r
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",% z( O; |% u( P, c, \* c$ D i
8 j! l& l" d+ m2 }, j" p
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
transactionIndex:0,5 J; Y0 v+ w) q$ |/ y' I+ r' P2 x
v:"0xfe5",
value:2000000000000000000
}
到这里整个交易就完成了
简单的智能合约, O0 s- v: B& k( e6 f. _% f
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件: B8 i6 I# Z4 H( u
pragmasolidity^0.4.13;
2 W+ K2 Z5 l2 U% M' F8 ~
contractHello{0 X3 j* b* f* H+ L
. y+ U. X# H8 I3 t1 _% z+ ^
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){
: P X: h# t- I) J4 p& _
o_sum=_a+_b; n _1 X2 O2 Z+ s) {' j
9 [' m: l% Z' `0 M l2 D! F
o_author="freewolf";
}
}
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。
这里是Mac命令行环境,不是geth,?>开头的都是命令行/ X% y9 ?8 p5 q: d- s2 z2 _
; T/ }) v* P4 k
?>solc-o.--bin--abihello.sol% z* K# [2 G# a( S$ M- Y2 M; T! f# C
?>ls
Hello.abiHello.binhello.sol, r; U5 }$ B6 {
% V/ z/ u4 ^2 n: P
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件( F9 J) X% _& a
R. h3 {# r0 s: r- E
Hello.abi文件内容修改成2 d3 A4 D0 k- N+ \9 d- G* E
/ [" I, c+ v4 z7 ^& i
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"}])) O) H& L* A2 f- f1 i' o
3 h* g8 \3 l0 d" A9 p, k
Hello.bin文件内容修改成# a [' q' y; C) u( o2 B- E
J) ?4 l+ E* ]6 |1 O2 j
personal.unlockAccount(eth.accounts[0]) o# w* Q2 \1 Y* Z! I
varhello=HelloContract.new({ u, [; P' D, |' w9 [. D) b1 ^
from:eth.accounts[0],
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",$ O+ O( k# N, b& U2 Y8 g+ s+ K7 A" w5 ^
% ` G1 z2 [1 P
gas:500000
})
4 W) M( z* s, X9 s* `5 _9 Q7 I
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了1 T% U" G+ \; q# Q- ]9 B5 @3 B! _
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
文件夹contract就在运行geth命令的目录) S. t$ X7 A! B6 ~9 l
7 W8 q1 D( @/ y# z: {7 `; G8 ~
>loadScript("contract/Hello.abi")
3 B1 m( x: L$ e4 M5 {
true9 y- B2 y' `0 l
>loadScript("contract/Hello.bin"); W6 u |) Y7 l$ ]
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:/ l% w6 z* G6 W% J* e% n5 p' ~$ l
true. ^" l( ]& b* I+ W
5 H8 w8 `+ g3 s, o/ T4 ]
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。- j% ^" b4 g: j+ w3 ~
. A, }$ s7 q, n- v# h
>hello
9 B1 g5 A' C9 Q# I2 L
{$ U& y1 S3 r& G9 B1 B' o% X
% x( e# @% @+ `
abi:[{9 E; E* W# c/ j! q; p* }
/ G: D' o4 D: Y" i5 T5 ]) y
constant:false,
inputs:[{...},{...}],
name:"sum",, J$ Z! @9 k# o5 |! @9 v
outputs:[{...},{...}],
payable:false,
type:"function"
" g; j- A' O, [! L
}],
address:undefined,
. a: F- p- ?0 X* y7 A
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
}- K! K. Z: G& g/ f, K6 T
>hello.sum
undefined
- F" @0 b- N9 j% p. v7 ?( c8 r; L
>miner.start(1);admin.sleepBlocks(1);miner.stop();
# [8 h5 i0 o; ?; L
true: A. P k/ k& r1 m" ~
>hello.sum |: y! W) v9 F2 u. Y
9 ^& L0 r! b- ?; v: {$ O
function()5 @$ f( P. w& A
$ _4 A7 q& E( K d0 p& u
>hello.sum.call(1,2)
[3,"freewolf"]4 c1 n- f$ l, L4 X. w4 Q7 r7 Q
追加-如何建立其他节点
; o* K, ~6 B, L( b
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息
+ w; k/ j: B: A; \4 d. t9 p
>admin.nodeInfo9 c. V2 }3 |! t- O" y( p
{
- ]% l2 B% D3 c- g, O1 W
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"," \ |6 {( k# f6 a3 F* g
' T5 h7 [" {) e4 D9 m1 n
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",# l% Q! o1 i. E/ u' ^& L
8 u/ W( Q- o* _0 }* B
ip:"192.168.1.2",& _% H' |: O) \3 W9 n% Q
; Q! c I2 _ h3 W5 f0 z7 x5 v! Y
listenAddr:"[::]:30303",
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
4 s$ u$ W) p! g- h
ports:{5 A1 z Y2 [2 m# ~: t% _( I) m: N* j
discovery:30303,
listener:30303
: h( X7 A6 F8 X5 Q5 B5 }
},' x3 b, \) u% d- {# V/ T
+ u$ D; I; ]' B2 X6 K
protocols:{6 T! o: M/ H5 F0 w' j+ g" c8 c
* G. z9 B0 B1 F. {2 ^
eth:{
difficulty:655652,
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",, B( k7 s/ c# A( k
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",0 e3 t: Q# U6 Q3 A8 u% _6 C
network:27027
}
( J, I8 {- e, h+ B
}& u1 q# m+ D. g! I- i
}
; R9 o7 {, X5 A7 ^
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了8 g1 p. [, x3 \ B' q, t
?>mkdirnode2
0 E8 U& W$ a6 o1 d1 W: H: g
?>geth--datadirnode2accountnew# Q9 f! B- c2 W2 @( J- ~& n. `
3 e" i% q8 U* i7 Y1 q' B
?>geth--datadirnode2--networkid27027initgenesis.json
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console' g# y9 G0 v' o
2 i1 ]+ z) ]# ?. G
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令, n' A# @5 \( p" `6 K7 s
7 i( z! i5 O @ U3 w6 V0 M% P: X
>eth.getBlock('latest')
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人