建立基于以太坊的私有网络和智能合约
V刘晨曦
发表于 2022-11-17 19:05:51
271
0
0
最近一段时间一直关注区块链的相关的领域和知识,今天本来想帮助小伙伴建立一个基于以太坊的智能合约Demo,发现很多过去的文档都已经过时了,无法正常工作。那就只能自己造个轮子,弄个版本新一些帮助大家入门。
i3 l( A$ i7 d4 M7 J3 y/ k, r
本文以流程tutorial为主,不过多去讲技术原理,原理文章网络大把。
1 q+ O: ], I3 i2 t0 t$ @# D
目标
! {/ _ h; }# q" {' Y
本文目标如下:/ D7 J" ^6 V# N: L
( s3 w* j* ~5 P6 _- R3 w2 @) e* B F
建立私有以太坊,设置第一个节点,挖矿5 L/ X% g j7 m" J
. a9 A3 x# ?/ u1 t0 E. R# B
完成一笔转账交易
/ l3 H' M( T5 w; ^% y; V
建立简单的智能合约- L# c- ~- o4 p# `; R6 R5 T
( h( P, o* v: U% `1 e" h
建立第二个网络节点( r+ q+ |9 Y& i6 o; m' f q" J% g
环境介绍+ n9 k& I( M2 L. T4 y5 B
% s' A1 O' ^3 D7 k! ~; a. a
无论什么开发都离不开相应的环境,我尽可能将所有软件都升级到最新版本,以下是本文内容相关的环境:
操作系统MacOS10.12.6* ^, ^# a' A! h; B
8 p/ M. |- d7 O4 ?' p+ ?! y
Geth以太坊CLIhttps://github.com/ethereum/g…v1.67
Solidity智能合约编译器Version:0.4.15+commit.8b45bddb.Darwin.appleclang
0 ~" ]8 t. p e Z, U |
安装
安装Node.js,这里不阐述了,源代码自己编译吧。
8 _8 z8 _; N4 @# d% x! w
安装Geth,这里直接去官方网站下载最新的可执行程序,复制到/usr/local/bin就OK。
5 t( m- X. O/ C9 k* \9 \
最后安装Solidity,本地要先有brew,才能进行安装:: V+ W. @! c+ @6 ]$ F/ k
2 x* e. o" {# i+ @0 o& B
brewtapethereum/ethereum- x+ Y; z$ l4 W. [% @4 [; T
brewinstallsolidity
创建区块链% g( k, d7 ~/ p! G. L$ e
) c7 Q) D/ G B+ Q# ?" s+ \+ ~2 K+ o- }
创建自己的以太坊私有链很简单,新建一个目录,在目录中先建立自己的创世区块描述genesis.json文件。
文件内容如下
3 I6 X7 A2 d8 ^# _ \4 c
{
"config":{
% k- R. q4 f* K7 ~; D6 [0 a8 V- f
"chainId":2017,
"homesteadBlock":0,
"eip155Block":0,6 U; F; s0 c1 a, d$ o( I1 Y
"eip158Block":0
) d) G1 Q( Y4 `; Z6 _% `9 t
},
"difficulty":"100",
$ Y, T4 }2 E+ ?6 q Y4 Z2 a7 z6 ]
"gasLimit":"2000000",
"alloc":{}
}) I2 \$ A& d9 y. b
% R: e6 z( F3 F1 W. \
为什么自己创建创世区块描述,如果使用默认值,difficulty值非常高,这样挖矿要急死人的。
首先创建两个账户,本文后面需要用到这两个账户,创建账户需要输入两次密码。
; z" ?$ [7 y2 y# T
?>geth--datadirnode1accountnew
Y, y) i' C3 b' F4 d2 n
?>geth--datadirnode1accountnew
1 O: s/ L: f% Z: ?9 t; G) ?
用我们刚刚创建的描述文件,创建创世区块。# E/ I% ^0 v5 p6 L3 C
?>geth--datadirnode1--networkid27027initgenesis.json
" Q' Z4 G0 X& K3 ^' C
使用console连接节点1并且记录log
?>geth--datadirnode1--networkid27027console2>>geth.log3 |% O; X! o* s, A* J# o
% }4 g$ ~/ G) z+ E1 }1 ]! A3 w; _, L$ N
使用geth完成挖矿和交易( _# {. F w+ `3 U0 v
* i) s) a3 `' m* L+ z
连接成功后,看看有几个账户+ n: Y: P/ ^; u3 G# m0 ~0 _
>eth.accounts; W. }6 M8 U" p. Y+ i- A
["0x65070d1d224114fd3c8358e9614fd948daecc428","0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8"]
查询下账户余额
; `! o) p5 I) f
>eth.getBalance(eth.accounts[0])' K; t3 l$ \2 a5 k
, J6 s4 d8 R8 a9 P
0
第一个账户没有余额,accounts[0]默认情况是coinbase账户,也就是挖矿的收益归集账户,现在我们就来挖矿,赚取奖励,由于difficulty值很低,挖矿秒出基本。5 L# _) V9 B, b& X+ W; @7 b8 d
>miner.start(2);admin.sleepBlocks(1);miner.stop();7 I* ^, i$ N. s2 a0 H% [
, P. b( {3 z: {
true
! ?" B5 Q9 K; D0 a" A1 s
miner.start(2)开始挖矿,参数是开启挖矿的计算的线程数
admin.sleepBlocks(1)挖到1个区块就停止7 Z8 a M0 o+ `( A0 Z K& w5 d; o( s
miner.stop()挖矿停止; v* E. P( z, S- i- O
+ p+ [2 ~% m1 Q4 ^9 ]: r
第一次会创建DAG,这里会花费一些时间,关于DAG,详情见底部参考。出现true说明挖矿完毕,挖完查询余额。3 w2 ^$ l J: ]# [ J- P
" B$ K' K' |/ H7 j; l$ F0 V
>web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')& t3 {) v/ N7 i( w
5
, v9 V; I- C/ R7 u
5个以太币在accounts[0],现在转2个给accounts[1],转账时候,单位是wei,但是注意,既然转账,别忘先解锁账户accounts[0],这里要输入账户密码。 j2 h1 c) p0 ^% G5 j5 q
>personal.unlockAccount(eth.accounts[0])
3 U& S' U4 \+ |
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc428+ T7 l2 I& F1 l% n) r! _; B
' R8 ]) o( x/ k
Passphrase:
$ q+ c+ P7 m: p& c/ A$ X
true' S6 F5 I$ b, s2 `7 ~' w0 x! H
1 R* o9 ]: e8 f0 S
>eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,"ether")})5 B: ]; X& R; R, V
"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645"! w2 p+ [1 K3 q `
最后出现的是这个交易的hash,查一下有没有待处理的交易8 y3 w5 f$ C, e: y) ~* h9 v6 ?
% A" L, M- p' G/ c0 Z% }( Y1 `: `
>txpool.status
6 [0 H9 T4 X1 q" q
{' m! f; d% z2 `
% ~! P' l" A! w8 p) y/ \3 j' u
pending:1,
. \/ k7 } A$ r# B
queued:0
}
* }4 V U* n) U' E/ k k. e
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
0- c4 l# Y# e( Q l- }
果然有一笔pending,查询账户accounts[1],并没有发现以太币,这里需要旷工来挖矿,打包这个交易到最新区块。交易才能生效,继续挖% r* m. q8 a+ u+ V2 B6 ]$ z
>miner.start(2);admin.sleepBlocks(1);miner.stop();
0 I( F A% }' @( Y
true
! E1 F) s8 \# ?+ C
查下余额
2 A8 f/ J* V9 P1 \
>web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')8 U* T5 w. I0 X9 P
2! X% g: w3 B$ N, Y
已经到账,再看下刚才交易的详情
! o% U0 f6 k5 t, \' e" l- o
>eth.getTransaction("0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645")
# b/ n9 v+ }0 |% p
{+ C8 w; @. W. G% v/ V( F* N
$ h& E# L8 v2 ?( U. e& ?, a& R
blockHash:"0xd30fbefb48de05a458a909d9486402bfa4d1459619226a3f8b95aaf407669bb7",3 n6 t4 {4 J" c0 A
blockNumber:2,
from:"0x65070d1d224114fd3c8358e9614fd948daecc428",
gas:90000,
gasPrice:18000000000,1 F1 D0 R |( _8 O& E: Y8 n: D! i
hash:"0x3f190d2af25dff4e6b6fac9537f8f6152fdb193ca9afe228fbdc9301bbff5645",# ^6 C4 u5 d" W4 F/ n* ~
% I7 S; r( v. u7 Q# _
input:"0x",
7 V, e) m/ Q Z J1 O* G* Y
nonce:0,% k, W+ @; C1 v$ W; H3 j
% m8 p; C' s8 v' q! f
r:"0xd9b7c4830b9a7ae8ac922179c4e73e6bf2a52178ee0c01250bd940586334d412",
s:"0xa1b0058b63e1c0360eae6073791b1d63d4a737c71c5932b4b203e853a8185cd",
to:"0xf11167054eb5fb91dd7b46726380f0f0cb09a6d8",1 @2 ?$ n: w* e# ~1 K
transactionIndex:0,
* V1 W- n/ X. o- Y1 T* r4 \
v:"0xfe5",
value:20000000000000000001 |$ ]" W. G- ~* ^4 ^
}% w3 `, u4 q3 }9 t6 T5 o P
到这里整个交易就完成了
简单的智能合约" l3 k& U7 j0 K3 b
" C! ^) t0 G+ o5 R K
下面我们来创建一个极简单的智能合约,geth1.6变化蛮大的,以前编译智能合约的方法都有一些问题,没什么简单的办法,browser-solidity是个不错的在线编译选择,我们还是选择在本地进行操作,前面已经通过brew安装了solidity,创建一个contract文件夹,在文件夹中创建一个hello.sol智能合约文件
pragmasolidity^0.4.13;; \% e- `3 w5 g6 i7 T
contractHello{8 t( X( {1 x! D; z8 ]
functionsum(uint_a,uint_b)returns(uinto_sum,stringo_author){
$ U' P, R. O+ |# k& \8 ^9 }/ N- L6 A" O
o_sum=_a+_b;
o_author="freewolf";
}+ j4 c' [" u7 {% J
4 T: h7 y% [9 f1 d
}* T& S s( W: k6 _$ Z
1 L# j: e+ X0 H$ z
然后我们来编译,完成后,会多出两个文件,abi文件就是智能合约相关的接口,bin文件就是智能合约编译代码。
这里是Mac命令行环境,不是geth,?>开头的都是命令行
?>solc-o.--bin--abihello.sol
8 i! S3 a) [9 s
?>ls* ]% O9 \$ i8 z* ?* {5 B
Hello.abiHello.binhello.sol
( Z- r9 V7 v: t& k
在geth中加载这些文件很复杂,这里我们修改下刚生成的文件
- c; Z. S7 ]( g0 A1 A C
Hello.abi文件内容修改成2 l$ @; c _7 I0 k$ K+ g
5 W4 R2 } M( s
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"}])4 S. q F0 w9 j | V8 C
Hello.bin文件内容修改成
personal.unlockAccount(eth.accounts[0])9 z& k9 Q1 K# ^& m: x
varhello=HelloContract.new({0 j! v' F0 Z8 V P1 P
& ~$ P1 S+ I! n U2 ]
from:eth.accounts[0],( V% f$ [2 H; Y& g, w
+ G7 |9 q" x9 _4 [) D! O
data:"0x6060604052341561000f57600080fd5b5b61017a8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063cad0899b1461003e575b600080fd5b341561004957600080fd5b61006860048080359060200190919080359060200190919050506100eb565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100af5780820151818401525b602081019050610093565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006100f561013a565b82840191506040805190810160405280600881526020017f66726565776f6c6600000000000000000000000000000000000000000000000081525090505b9250929050565b6020604051908101604052806000815250905600a165627a7a72305820063cb95e17166637bd4ab62eae6b0e6c4e1fcd85a9c2e3be29aa75a272280b830029",
" C, H8 B0 q$ Q5 _; Q3 t9 w
gas:500000$ D1 {8 D6 I& r! Q7 I6 B% j& T. o
})% _. ]3 u/ b6 p9 x& N& R( |; F
注意别忘了data必须0x开头,New合约就得解锁自己的账户,这里解锁也写在这里了
回到geth,加载刚修改的文件,加载bin文件需要输入账户密码& M3 {9 w7 c8 G" F% ?
文件夹contract就在运行geth命令的目录' `4 w$ D G9 T* w2 _
>loadScript("contract/Hello.abi")
true' X7 N# G" M$ j. m. p: D( ?+ l
>loadScript("contract/Hello.bin") F$ A7 g- s4 W# {
Unlockaccount0x65070d1d224114fd3c8358e9614fd948daecc4287 H' c2 \) A4 N2 z. b
0 ~( W( v# z+ w; R$ a; f8 P; L
Passphrase:
true6 u2 b3 Z0 p4 s- m& s
* _- \( l2 S$ l# y9 c5 K. e
现在智能合约已经部署到区块链上了,但是要挖矿才能生效,挖完就可以尽情玩耍了。
>hello
{
abi:[{$ p. N$ v# E( v# l
constant:false,* |! O+ ?8 Q) @# j; q( s7 a2 }1 M
inputs:[{...},{...}],5 J, \: R$ \& S0 d- T
. f: d' F3 E; g9 L3 Q. c3 i
name:"sum",! i$ X6 U, U) H) j0 z- u1 m* L
outputs:[{...},{...}],3 e4 @% z) b" M0 i {+ ]1 [
payable:false," R: k/ `% w# _* u! Z; Q
type:"function"
}],! G+ w* P# | E% g
' t) [5 G7 K/ ^- J. q3 m
address:undefined,9 Z) g; V$ f2 O# }7 J* p
transactionHash:"0x783f5cae1f9b40f25da1260267d5e6f801d1746541b5f28f84684883723807b8"
6 ?0 ~" Y ]( S
}/ \" E. e* x7 A' \% X o* i4 x- h3 z
>hello.sum
undefined
>miner.start(1);admin.sleepBlocks(1);miner.stop();
4 [+ G+ I5 O- m/ c
true9 B2 O' w8 \" A$ ^
>hello.sum3 h( _! r4 G" S1 {( M
function()
( [% g9 u0 g% F' s& p2 r+ n
>hello.sum.call(1,2)
/ o3 n% b0 y$ b* {5 {1 n7 c
[3,"freewolf"]1 ~7 t/ ]$ M" k1 w
0 V- M/ y: u2 C9 o' c2 H
追加-如何建立其他节点
7 s. v" ]8 `: f% r2 ^2 H1 S1 R% B5 ~
追加一段,如何创建其他的P2P节点,首先在原先节点执行下面代码,查看当前节点信息' p8 L; x( u9 _- W5 P. S
>admin.nodeInfo
; q5 w3 `" j; X4 ^6 z/ { O
{
enode:"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303",
4 D5 ^. j2 y F+ {/ F# d3 i7 i
id:"bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3",
/ y- w/ c+ X/ E9 A4 D
ip:"192.168.1.2",) f7 r5 {: n0 w6 p
3 K+ f, Y X, ~$ K
listenAddr:"[::]:30303",8 t9 }8 u8 B7 D* C; v/ ?: \
% R9 f* c0 s0 @
name:"Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3",9 n0 S0 b: }, K. I* R: h- u! Z
ports:{
5 R$ A0 x$ ~2 g& @$ V h
discovery:30303,& u* w, n: n: d; F/ A% ]
listener:30303
},! R7 n6 `$ M8 a; d0 C E
; W' K3 G; r" U7 e; v) e ?
protocols:{
eth:{; ?2 L9 c) A9 h6 }
difficulty:655652,
genesis:"0x7b0286b147e6b5b8710b8acff38053fdf1991a980da8ca73b4b359c28c7144fc",) F) V6 Q ?. P6 |2 l7 s, X
- { ~: [. k+ d2 A8 ~0 R; U
head:"0xd545cee3b9247b67c5d43728eddcbcfe9315dcf18cbc12187a7a178220829153",
network:27027
; d5 u- @2 v z3 j
}/ J) C) f+ N8 f# b8 x/ U
+ d [" f! K9 J1 i; m5 O0 F9 [$ c
}
}) Q) p& \, A8 V/ {9 Q
拿到node相关信息,就可以创建新节点了,这里就不过多解释了,前面基本都介绍过了6 l: C% ?& {8 p! \
% K! L- [5 U" @
?>mkdirnode2
& x0 K( q0 w, }9 ?$ D
?>geth--datadirnode2accountnew
6 W8 R% L8 ?' T* S3 M. W8 J) w, N
?>geth--datadirnode2--networkid27027initgenesis.json
" F- X' v0 z* z9 h! G Y% u8 \3 l
?>geth--datadirnode2--networkid27027--port30304--bootnodes"enode://bbd3d0f2afad68c3e4b2e79a6daddc6e8498b9266cc3e953bbb121bae40fe44b5e0377c0768b03e47ac04cead52235a12931bfb96528a8225be5808fc8c174b3@192.168.1.2:30303"console4 c/ G+ W8 L. R {3 k$ \4 [2 s
3 D9 q" J5 ?. X S
需要注意的也就是最后一行,写入你自己的node信息,其他的也没什么了,进入geth后,数据同步后,可以使用下面命令
6 I$ f0 A @0 s4 G Q1 C
>eth.getBlock('latest')9 Z5 t* |) r$ K0 m
( r& m1 K* ~2 Y' W* l# Z
>admin.peers
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人