Intro
- Solidity
Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了 C++,Python 和 Javascript 语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。
- Remix
基于浏览器的 IDE,集成了编译器和 Solidity 运行时环境,不需要服务端组件。
- MetaMask
MetaMask是通过浏览器访问分布式应用(dapps)的一个Chrome插件。可访问以太坊主网,任一以太坊测试网(Ropsten, Kovan, and Rinkeby),或者本地区块链。
- Geth
Geth是Go Ethereum的缩写,是以太坊协议的三大原始实现之一(另外还有C++和Python),也是最受欢迎的客户端实现。它可以作为客户端运行在几乎任一操作系统上。安装了客户端的节点可以与其他节点建立p2p通信信道,签署和广播交易,挖掘,部署和与智能合约交互。
启动Geth时,客户端会自动在端口8545启动RPC服务器。然后使用web3j等库连接到localhost:8545或使用curl或wget手动调用它来访问此端口的RPC服务器及其方法。
- Web3.js
web.js是通过HTTP/IPC/WebSocket等协议用于和本地或远程以太坊节点进行交互的javascript库。
简单的智能合约
- Solidity第一行使用关键词
pragma
指定使用哪个版本编译器处理源代码。 event
事件类型可以让监听该事件的listener收到通知,方便事务追踪。
下面这个示例定义了一个事件,放在需要监听的函数最后执行:
|
|
该事件可这样被监听:
|
|
事件的参数可以通过上述回调函数的第二个参数的属性args
访问,本例为result.args
。
3. 全局变量msg
,tx
,block
保存一些区块链的属性,其中msg.sender
永远指向访问当前函数的来源地址(在构造函数中,msg.sender
指向智能合约的创建者)
区块链基础
· 事务/交易(transaction)
区块链是全球共享的事务性数据库,这意味着每个人都可加入网络来阅读数据库中的记录。如果你想改变数据库中的某些东西,你必须创建一个被所有其他人所接受的事务。事务一词意味着你想做的(假设您想要同时更改两个值),要么一点没做,要么全部完成。此外,当你的事务被应用到数据库时,其他事务不能修改数据库。
举个例子,设想一张表,列出电子货币中所有账户的余额。如果请求从一个账户转移到另一个账户,数据库的事务特性确保了如果从一个账户扣除金额,它总被添加到另一个账户。如果由于某些原因,无法添加金额到目标账户时,源账户也不会发生任何变化。
此外,交易总是由发送人(创建者)签名。
这样,就可非常简单地为数据库的特定修改增加访问保护机制。在电子货币的例子中,一个简单的检查可以确保只有持有账户密钥的人才能从中转账。
以太坊虚拟机
· 账号(account)
以太坊中有两类账户(它们共用同一个地址空间): 外部账户(external accounts) 由公钥-私钥对(也就是人)控制; 合约账户(contract accounts) 由和账户一起存储的代码控制.
外部账户的地址是由公钥决定的,而合约账户的地址是在创建该合约时确定的(这个地址通过合约创建者的地址和从该地址发出过的交易数量计算得到的,也就是所谓的“nonce”)
无论帐户是否存储代码,这两类账户对 EVM 来说是一样的。
每个账户都有一个键值对形式的持久化存储。其中 key 和 value 的长度都是256位,我们称之为 存储(storage) 。
此外,每个账户有一个以太币余额( balance )(最小单位是$Wei$, $1 ether=10^{18}Wei$),余额会因为发送包含以太币的交易而改变。
· 交易(transaction)
交易可以看作是从一个帐户发送到另一个帐户的消息(这里的账户,可能是相同的或特殊的零帐户,请参阅下文)。它能包含一个二进制数据(合约负载)和以太币。
如果目标账户含有代码,此代码会被执行,并以 payload 作为入参。
如果目标账户是零账户(账户地址为 0 ),此交易将创建一个 新合约 。 如前文所述,合约的地址不是零地址,而是通过合约创建者的地址和从该地址发出过的交易数量计算得到的(所谓的“nonce”)。 这个用来创建合约的交易的 payload 会被转换为 EVM 字节码并执行。执行的输出将作为合约代码被永久存储。这意味着,为创建一个合约,你不需要发送实际的合约代码,而是发送能够产生合约代码的代码。
· Gas
为了避免网络滥用及回避由于图灵完备而带来的一些不可避免的问题(the halting problem),在以太坊中所有的程序执行都收费。Gas是基本的工作量成本单位,用于计量在以太坊区块链上执行操作所需的计算、存储资源和带宽,其目的是限制执行交易所需的工作量。各种操作的费用以gas为单位计算。任意的程序片段(包括合约创建、消息调用、分配资源以及访问账户storage、在虚拟机上执行操作等)都有一个普遍认同的gas成本。Gas有两个作用:
- 以太坊(不稳定的)价格和矿工工作报酬之间的缓冲
- 对拒绝服务(DoS)攻击的防御.
每一个交易都要指定一个 gas 上限:gasLimit。发送者通过在交易中指定gas price来购买gas,系统预先从发送者的账户余额中扣除gasLimit * gasPrice的交易费,即采用预付费机制。Gas price是指当你将交易发送到以太坊网络时,愿意支付的每单位gas的价格。如果账户余额不足,交易会被视为无效交易。之所以将其命名为 gasLimit,是因为剩余的 gas会在交易完成后被返还(与购买时同样价格)到发送者账户。每个矿工自己选择他们想要接受和拒绝的gas价格。交易者们则需要在降低 gas 价格和使交易能尽快被矿工打包间进行权衡。
geth安装
下载
|
|
解压缩
|
|
配置环境变量
- 文件夹改名
- 配置环境变量,检验方式
which geth
是否输出geth路径 - 编辑配置文件,保证下次仍可以找到该环境变量,检验方式如上
|
|
NOTE:
区块链开发最好用类unix系统,如ubuntu、Mac,否则可能不太稳定。如果是windows电脑可以选择用虚拟机或者双系统。
geth启动
1.在geth启动目录下创建文件genesis.json
,内容如下:
|
|
步骤01:利用创世块文件初始化
|
|
此时在data文件夹下生成了一些文件
步骤02:启动geth节点
|
|
note:
官方教程推荐使用clef工具进行账户管理,具体步骤可参见
使用Web3.js与智能合约交互
以下示例web3.js
版本为1.2
现在假设你有一条运行中的区块链,且在区块链上部署了你的智能合约。
步骤1 创建web3实例
|
|
所有与区块链的交互都返回一个Promsie,可以用await
等待交互返回的结果,如下所示:
|
|
步骤2 获取智能合约实例
获取实例需要传入两个参数,合约地址以及合约ABI
external
和public
标注的函数。
|
|
前面已经对Web3.js进行了简单的介绍,不再赘述。下面有几点需要了解:
- Web3.js的版本更新非常快,且版本间很可能不能兼容,所以本文中的例子很可能很快就无效了。
- 在以太坊的众多API中,有两个API最为重要
eth_sendTransaction
和eth_call
。
什么时候用transactions
,什么时候用call
呢?
在Solidity中有两种函数
view
函数,不会修改状态变量的函数non-view
函数,要修改状态变量的函数
两种函数分别对应call
和transactions
,因此对应的代码如下:
- call
// js
const result=await myContract.methods.foo(1).call({from: '0x123ABC...'})
// solidity
function foo(uint a) view external returns(bool){
// ...
return true;
}
- transaction
// js
const result=await myContract.methods.bar(1).send({from: '0x123ABC...'})
// solidity
function bar(uint a) external {
// ...
// no return
}
参考教程
[1] 柏链公开课:第五期 Geth的安装
[2] 讲师github:Geth安装与启动
[3] Remix IDE官方文档
[4] Solidity官方文档
[4] Solidity官方文档(该文档对区块链基础知识的描述写得很好)
[5] 链门户:Geth介绍及如何运行以太坊节点
[5] web3js官方文档
[6] web3js官方文档
[7] CSDN:学习以太坊Gas机制