建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
149
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。- }) U. H! K# h
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
目标
本文目标如下:
; T9 F, h3 l5 m3 f+ w
建立私有以太坊,设置第一个节点,挖矿3 G3 Q' J9 v5 [$ s
完成一笔转账交易
3 L1 t9 [. K9 w6 F, ~
建立简单的智能合约; o. O* u+ Z3 c6 ^
8 b. Y+ v& O5 Y' b
建立第二个网络节点+ _3 B$ t# w; N! B' h- w
9 X3 g8 _' C6 c
环境介绍
+ Z4 f* P& `2 K! E5 H7 u
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:
: h+ j) V* k! r+ t7 G8 p/ ]
操作系统MacOS10.12.6* M& g& i$ N6 j
B7 C7 x9 y% A. O9 m0 y" c$ F( [5 i
Geth以太坊CLIhttps://github.com/ethereum/g…v1.674 G$ r3 W b: _& h$ u s$ m; K
. l i ]/ @* M$ _ U
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang' b: z3 r- |6 {( S W8 ^
3 e( Q; E" Z+ \( u- E( q
安装
3 R% `, s# q/ n* `& P8 F- H
安装Node.js,这里不阐述了,源代码自己编译吧。
* C3 h9 q1 u: ]- n6 f9 |
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。4 G) Y% Y+ v O- o9 {. c; v3 f& Y1 F
最后安装Solidity,本地要先有brew,才能进行安装:$ y! X8 z' {' Z& R
brewtapethereum/ethereum3 Q! z5 C! v* [. o! f
brewinstallsolidity* ?& U: A! K% E5 q# p
创建区块链
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。% d# j! P& T7 S& |0 Y: ]+ p2 A
文件内容如下( f& P# c2 |7 X: U, j# l* j( H' @, Y
{
"config":{1 e& L$ @& @) G$ A
"chainId":2017,
3 E/ e H7 k* y- K C* L ]- Z
"homesteadBlock":0,
+ L) K" N) b3 { h+ R8 r
"eip155Block":0,. V* M3 I. y9 y5 g! o
"eip158Block":0. E8 C8 v$ u0 B4 \6 J4 O2 o; A
9 E; a6 ^0 v2 S$ t# l( w- ?' j4 n
},9 ` Z- K9 R. z
"difficulty":"100",
+ g3 m( q2 y; J' m" Q
"gasLimit":"2000000",
"alloc":{}
}7 }* p2 f& t% d" s
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。' Y( T6 u% h* z$ C/ s" I) e# c
) L2 Z' v# \2 x3 h
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。
* c! w6 O$ M1 Q$ J
?>geth--datadirnode1accountnew$ b" |2 J/ E7 W- E/ `
: |# D+ X; A) y( L! j' X- t8 V" L
?>geth--datadirnode1accountnew p; d0 Y1 K; A; B7 J2 Q7 W# ]: @
用我们刚刚创建的描述文件,创建创世区块。1 Y& W3 N: n7 M* P+ b
?>geth--datadirnode1--networkid27027initgenesis.json
使用console连接节点1并且记录log& C* U/ h6 H! v7 ~/ y4 v
?>geth--datadirnode1--networkid27027console2>>geth.log1 y/ U u5 A+ L+ p* [! `2 ? g
使用geth完成挖矿和交易7 h6 t# r' r u# _0 D2 ^. r
' \1 H T6 p% _! [* @. y0 E: W
连接成功后,看看有几个账户
; z; ?# G4 d+ n7 M' P- R h3 l
>eth.accounts
% \$ w. D1 H6 Z! T; a# o8 j. S
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]( O7 v" E, i4 o1 ~& n
, G" {) f# \- X; \4 D- }5 F
查询下账户余额
>eth.getBalance(eth.accounts[0])
0. t x; [) K# h* H8 ~1 D3 u% f
" p9 ~* G0 ?2 S3 \7 S q1 i
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。
>miner.start(2);admin.sleepBlocks(1);miner.stop();4 Y- k9 D' ?4 V G( u5 d, k; x
# E) i/ N9 F" | y: q+ P$ c8 a" x2 J
true
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数
admin.sleepBlocks(1)挖到1个区块就停止
miner.stop()挖矿停止
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。8 T! K3 B1 t: [; F, I5 }8 }
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')1 h2 E# {. H- F [
; Z: [! v! v" D1 @! h
5
6 v, H. o) s1 F; f5 ]5 T
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。7 x! v3 Z! ?# Z2 S; s/ ?
6 J' f, S( X* \/ l6 x
>personal.unlockAccount(eth.accounts[0])
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
Passphrase:
{1 z+ {4 ] h' x7 m
true/ h, c' k7 g* _
( A0 j) P" \. j$ S$ u6 Y! @
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")}): S1 v6 _# T+ L) N
6 h$ S5 g0 P6 f3 Q
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"1 k& u8 z1 E4 s& |
/ H! v6 {7 S* C4 P! I2 P
最后出现的是这个交易的hash,查一下有没有待处理的交易
V& y/ ~- m7 o6 V9 t
>txpool.status9 L+ q2 O# m6 D9 m1 b
# m5 G* Z9 k& e
{" o/ }; ~; V5 M, \
pending:1,$ I' p I' K& Y- \6 U
* M. _6 I7 u& u
queued:03 R( }1 \% i3 A) }6 s/ I
* r6 N5 t# I: r6 y3 \5 z& b2 y* {" Q
}7 u: f# u# l e2 C
2 |" H2 q. m4 p' y& Z4 t4 f: ?
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether'); ^4 U9 d2 V1 C% Z& i3 ?. c* L
07 W; G/ Y9 D& I5 j4 Q1 T6 g
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖
>miner.start(2);admin.sleepBlocks(1);miner.stop();
; T: I1 m0 g+ }% ?; n/ d( G
true) o5 B) W# @% n+ ]* e, l
8 o& c. v, H0 @6 s8 q" R+ |# j
查下余额
. @& L" h$ }( e
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')% p" r/ I* O( [( A+ L
8 \2 m# ]: g* p( B* A2 z! e3 C7 p
24 x0 v2 V; E- u$ T) _$ ~* U' k5 p" }
已经到账,再看下刚才交易的详情
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
2 Q8 T2 o2 d# A i
{* c% x8 w5 [3 h# F
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",$ h7 C/ q7 Z" \2 B
blockNumber:2,& b" D9 [' @2 K/ b( d
f+ X J# n$ U$ `; c( F
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
gas:90000,
gasPrice:18000000000,1 u% ~& w& x, @
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",
$ I6 W2 w$ E R7 N
input:"0x",/ O$ O- Y6 l* e. z% l1 V
nonce:0,
8 Q5 \" ~' q; Z) `' c) w% n' B
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",: A2 l9 P; G" u/ x" N! D8 m+ U
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",$ C: r% o9 q( o
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",
transactionIndex:0,
v:"0xfe5",
8 Y0 ?4 y7 p9 ]8 C% K9 r8 O
value:2000000000000000000% W, Y* z9 C, l7 \- S+ v
2 l8 P( j$ Q% h1 E, u- l
}
到这里整个交易就完成了
简单的智能合约/ c. P' N9 D: h; r g
# h p5 r5 t1 k+ i* b
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
pragmasolidity^0.4.13;% x- {$ |+ y$ \3 R& S
contractHello{
9 O6 D- c4 {" g" p
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){
+ b* N* M: O4 J( ?- x
o_sum=_a+_b;
+ @$ ?9 p! e. F5 @9 Y/ R& E6 s
o_author="freewolf";! ^2 l( W+ d; x/ f( a
}- e4 a+ c3 a( R1 W
9 z) E& z- T) Q+ i/ D+ y
}) ]9 O# @ e `6 }& ~1 ?- }+ q
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。
$ h- v. P) s2 {9 d! G
这里是Mac命令行环境,不是geth,?>开头的都是命令行0 W) m: A! o6 ^4 [+ K
( [. \9 `, @% [1 a1 I
?>solc-o.--bin--abihello.sol
?>ls
Hello.abiHello.binhello.sol
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件
; b A/ z$ ~5 n: ~
Hello.abi文件内容修改成
8 ?* z+ f9 |- W# {. d% l 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"}])
Hello.bin文件内容修改成9 R. A$ M$ X9 O% |9 h
personal.unlockAccount(eth.accounts[0])3 \: Z7 _5 c" \" i" [
varhello=HelloContract.new({1 ?& v% W+ E5 |! v; R* e# z
from:eth.accounts[0],
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
gas:500000+ y7 f, [: F- H; `5 A: p" |0 @
. @' ~1 @5 r4 b& m3 Y6 z7 F
})
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码
6 N0 Z. a9 Y5 C( `' A/ U: }
文件夹contract就在运行geth命令的目录
>loadScript("contract/Hello.abi")& K* }5 t+ j9 f7 q' D; z) e& F7 v3 |; `
5 B6 J+ u: {6 i B3 F" h
true
5 f' V9 {2 w5 b- M* X4 N
>loadScript("contract/Hello.bin")8 x7 o* h0 }! Q% y1 S
3 M. T6 i$ u0 @$ a( I. {3 e$ Q
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428
" H. h6 [+ T4 f# f( u
Passphrase:
, W3 i1 y4 F) R$ d5 Z
true
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。
>hello
( H9 z+ d( S* Y7 @# T9 H7 ]
{
+ ?1 o; G5 L$ p# O l
abi:[{/ a- Z& M' P0 G' K7 W
1 @+ K/ G D) z" v
constant:false,3 Q8 a) J# L1 a) ^) o1 j
inputs:[{...},{...}],# R! L4 R, t. f2 f# S) C0 L
. l0 i" u! _: B0 D4 {; W8 }/ m8 m
name:"sum",; v6 O2 r; f4 X# U8 v! Q* i" a
outputs:[{...},{...}]," e9 X6 v2 H( A' v5 T
payable:false,% H3 `4 K8 @; m+ g
type:"function"
4 C6 m2 N' Z4 l& S3 Q2 @1 _
}],
address:undefined,! Z& _0 z9 X" ]
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
}
# a* B/ B4 e3 y3 x3 a; `
>hello.sum. N/ q: B C3 K! b
undefined$ e* j+ p7 p) k9 h7 r
! l4 q0 R) R8 {9 v3 b" C* s& w
>miner.start(1);admin.sleepBlocks(1);miner.stop();
true" ^$ P0 g; m) ?# y3 Z* H2 @
7 }1 F& G O7 a$ J, L, J
>hello.sum2 D- |0 t' L/ {8 L [
function()& G% o. J5 N- ~& C1 z7 l- o
>hello.sum.call(1,2). d# J) Y# P2 n j) d" `& N
[3,"freewolf"]; L) j2 L; d4 [
追加-如何建立其他节点. v. U" w' w9 d
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息
! e: i/ d* T; |" o- }
>admin.nodeInfo
{
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",9 _2 \- K `3 v
: j: N6 V) Q5 b/ ]6 S7 U" J$ N
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
ip:"192.168.1.2",8 M! U5 L J/ U6 K. S ^
listenAddr:"[::]:30303",
+ R" c7 Y. V I/ n3 s5 S5 M+ A
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",
ports:{+ V' {5 x5 E6 ~
- \2 X' [7 M7 m) {, h! g
discovery:30303,
: J( g4 _0 n6 U) I; Z8 O" j
listener:30303
% U1 R$ P0 E$ y6 B1 O/ |
},9 C U% D2 U. o" T: Z# e
protocols:{
eth:{' w) A" b1 j. O" m3 f5 ^8 N
difficulty:655652,2 |- \' i% Q2 @: L6 i+ \( j; P
8 E; R* v% R, }$ U+ G/ b
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",
4 ?8 C5 _- x s5 |4 |% R6 f3 u; M' J
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",( G# f$ d2 _- q, J5 E
: N @+ G t0 E) D: Y9 j
network:27027
}/ c( c3 t; o' v! T: N
}
% T$ z, i% D3 t+ Y V! e" c+ U
}3 p6 `7 a. u* d: x6 T& q& N
" S7 O( ?3 S1 F- m' P9 D
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了4 _6 k; E. @8 N( `: M( j: R
: ]3 d; Y1 W( }: V
?>mkdirnode2
& ^4 ~$ B" a# l2 Y; |# `
?>geth--datadirnode2accountnew
?>geth--datadirnode2--networkid27027initgenesis.json5 R/ Y* G+ V* Z* C) u! V
4 G; K. u$ f' D0 ~
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令3 _8 e6 [6 b" M) m$ ]& j. F
>eth.getBlock('latest')
% U! @ v- r" k% j
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人