6 d6 i: j1 S$ q Y9 _, P- x
以太坊的应用被称为去中心化应用(DApp),DApp的开发主要包括两大部分:
! [: G2 o8 P# l0 E3 q
智能合约的开发5 o9 i- S0 l$ b, d4 |9 t( @
9 P4 F- A3 P5 ~, n ]7 s+ p! g
用户界面的开发, u# T5 U+ ~. o
在本文中,我们将介绍智能合约的开发语言solidity。
7 f. n0 d6 K9 y
让我们先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。
$ u6 ?5 L$ s! K# W+ D7 M& j7 N
- contract SimpleStorage {
- uint storedData;% R' K/ ]' Z& F* A7 V6 j2 d
- function set(uint x) {
- storedData = x;* P; {3 g3 i" |
- }
- function get() constant returns (uint retVal) {
- return storedData;
- }3 [& K3 T7 U! |& Q# F% s
- }
/ f" [2 p( ?: K0 v, A q5 q2 k
在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。
uint storedData; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。, @+ C A/ I: j8 |" S
5 i3 q/ Z+ G1 G& Q; S7 s
跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。
. `* g: H- q4 E" \' Z2 ^0 [
这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。9 f* g4 L0 `/ U0 `+ U
( O i) _8 T1 U3 ~7 {% I1 _
编写代币合约# e; h7 V, I3 h ]
9 v# ?6 g& o ]0 n2 t
接下来的合约将实现一个形式最简单的加密货币。任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。
contract Coin {
//关键字“public”使变量能从合约外部访问。/ A8 x$ t' ~! I+ [9 `. \
address public minter;8 e6 [( M9 x/ T3 @) _
mapping (address => uint) public balances;
//事件让轻客户端能高效的对变化做出反应。+ ~2 u* R7 q* r, d4 M0 p
event Sent(address from, address to, uint amount);
//这个构造函数的代码仅仅只在合约创建的时候被运行。
- function Coin() {
- minter = msg.sender;
- }
9 z. g' u: D+ k) _
- function mint(address receiver, uint amount) {9 @( }# A% v) G( v, v
- if (msg.sender != minter) return;/ Z; Q6 }0 H. P5 @! ~5 X8 S( S7 a
- balances[receiver] += amount;0 I1 V- _0 s0 W5 ~! g2 B$ [! d+ n2 M( g
- }
7 t& n3 p8 R1 e4 K8 m
function send(address receiver, uint amount) {& j* N: g @9 F K# Y
if (balances[msg.sender]; L- n5 a8 `/ G
9 u x: Y/ g+ w' q
这个合约引入了一些新的概念,让我们来逐个介绍。* m- U" Q7 A* Q" u
& {1 h3 F, i% s# Q/ r. }) e8 P
address public minter;`) {2 K/ J" ~# A4 M7 k% t. v
这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下:6 ^# M8 p) Z$ C, ^! ?- b K+ O# u
function minter() returns (address) { return minter; }
' Z# l# j$ q& s# k1 j
当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。
; b9 |0 u$ V' |* n. U. P
下一行代码创建了一个public的状态变量,但是其类型更加复杂:( |: s/ o" {4 V# [$ c7 E3 C, i2 D
mapping (address => uint) public balances;1 v O* z. O5 I7 ~ D
8 t% M; i$ m: ?0 s0 C) k3 `
该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下:9 m# m" @, d" m) z: ]" Y( D" l- R
- function balances(address _account) returns (uint balance) {
- return balances[_account];' ` h9 B8 G& Q7 R
- }
我们可以很方便的通过这个函数查询某个特定账号的余额。
event Sent(address from, address to, uint value);* I" Z* J9 [9 J7 }* m
0 i1 s5 D2 C$ a" D$ z k1 ~
这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码:, m. d O$ g }' v+ J( Z* |
( K2 x' z) Q( y" l; J/ k1 P
- Coin.Sent().watch({}, '', function(error, result) {
- if (!error) {/ D/ U$ A; Q7 Y9 R
- console.log("Coin transfer: " + result.args.amount +
- " coins were sent from " + result.args.from +8 S" S. n6 X* p
- " to " + result.args.to + ".");
- console.log("Balances now:\n" +
- "Sender: " + Coin.balances.call(result.args.from) +
- "Receiver: " + Coin.balances.call(result.args.to));
- }- z, c5 j) x. R$ }1 }7 c
- }
* W4 [: V+ J" P
注意在客户端中是如何调用自动生成的 balances 函数的。0 H8 C1 J, F% |. W! f
6 b. H& o q( B) N2 M' Q! x* Z( O2 M
这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。- u+ c0 O* B) Y' y& m* O% Q
最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。3 ^1 {: X6 q& f, |
5 W ~! X& r/ S; _) ~. x, o
分享两个教程和一些免费资料给读者:9 m6 m# Y9 O" S a
一个适合区块链新手的以太坊DApp开发教程:
- g" O# f0 `# R; b
http://xc.hubwiz.com/course/5a952991adb3847553d205d1- k8 p* h! d, ~" b
一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台:
) ~: u O- r7 _6 @2 f$ N9 N: a2 z# z
http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd69 r" p# c- x/ z" c
收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接:
; H j! R! }# d+ k- g
" I/ ?" Y! ]5 { v& {: {! T/ ]
web3.js API官方文档中文版:https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g1 K% k v* X, O7 K9 v
3 [. x0 m w4 U: F& P
以太坊官方文档中文版 :https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA
以太坊白皮书中文版 :https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow1 n" u3 | Y, \/ I& q4 e3 p
Solidity的官方文档中文版 :https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw
" R& ?1 y. X2 ^/ |
Truffle的官方文档中文版 :https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ
' t" A/ P: T5 j' y0 N
C#区块链编程指南 :https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA1 r' o* S; R# F: l3 x8 X+ p3 q: Z
区块链技术指南 :https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg& t3 j" N5 K: K& x5 K
精通比特币中文版 :https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg9 x! N# i3 o& }
, S# e5 c: X4 x$ R
Node.js区块链开发 :https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg
geth使用指南文档中文版 :https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag
4 C- w' W6 C2 ^: j6 I0 F
以太坊DApp开发环境搭建-Ubuntu : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA, Y; _9 q6 h. ]
7 L$ F2 H5 v6 a
以太坊DApp开发环境搭建-windows :https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA
以太坊DApp开发私链搭建-Ubuntu : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g, _/ G/ W# j7 c8 ~4 X
以太坊DApp开发私链搭建-windows :https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA: ~. H0 X. Y! z& Q
7 g# F, Q& k- r9 {! c
# W P' v+ t2 Z w+ S- L



