- A+
甩锅提醒:本文更新于 2018.3.31,未来这些内容肯定会过时,务必善用搜索引擎去获取知识和技能。本文内容杂多,可能需要对区块链有一些深入了解的同学才能看懂。
0x00 一些 Tips
-
关于私有链搭建。直接用 puppeth。生成创世块 json 配置文件后,可以手动修改其中的参数。如果使用 PoW 共识,账本数据目录下 keystore/ 目录不需要放置挖矿账户的 keystore 文件。如果选择 PoA 共识,则要把记账账户的 keystore 放到这个目录下,因为 PoA 记账需要解锁这个账户。
-
以太坊区块最多可以容纳多少笔交易?
比特币把块大小限制到了 1M 或 nM。和比特币不同,在以太坊里,块中容纳的交易数由共识节点的 gasLimit 设置。当前公网的 gasLimit 可以从 https://ethstats.net 查询到,大约 8000000 左右。理论上,gasLimit 可以设置无限大,这样一个块中可以打包无限笔交易。问题讨论请参考 https://forum.ethereum.org/discussion/1757/maximum-block-size
但是,在创世块配置信息里改 gasLimit 是无效的!需要在启动 geth 的时候用--targetgaslimit
设置。 -
Ethereum Wallet 和 Mist 有什么区别?
https://github.com/ethereum/mist/releases 这里提供了这两个软件的下载,有人会把这两者都称为钱包。区别是 Mist 是一个去中心化应用浏览器。可以用 Mist 浏览器打开任何 Ethereum Dapp 应用。 Ethereum Wallet 是 Mist 浏览器 + 以太坊钱包 Wallet Dapp 应用。
参考:https://ethereum.stackexchange.com/questions/2690/what-is-the-relationship-between-mist-and-ethereum-wallet -
Mist 如何连接私有链?
1
2
3
4// 把 127.0.0.1:8545 替换成你的私有链地址
mist --rpc http://127.0.0.1:8545 --swarmurl="http://swarm-gateways.net"
// 不启动 rpc 的话,也可以如下直接通过 ipc 通信
mist --rpc /path/to/geth.ipc --node-networkid your_network_id --node-datadir /path/to/ethereum/dir/ -
Ubuntu 16.04 x64 启动 mist 时缺少 libXss.so.1
1
2
3
4
5
6
7// 错误描述
mist: error while loading shared libraries: libXss.so.1: cannot open shared object file: No such file or directory
// 在 64 位系统中,需要安装 32 位的依赖库
apt install libxss1:i386
apt install libgconf2-4:i386
apt install libasound2:i386 -
启动 mist 提示不能启动 swarm
错误描述:[ERROR] main - Error starting up node and/or syncing Error: Couldn’t start swarm process.
终端/命令行启动时加上--swarmurl="http://swarm-gateways.net"
参数。如果在 Windows 中使用,右键 Mist 快捷方式属性,在快捷方式
选项卡,目标
里加上上述参数。 -
MyEtherWallet 的坑。MyEtherWallet 是一个离线钱包,开源,你可以下载这个项目本地打开 index.html 文件离线运行。生成的 keystore 与标准 keystore 存在一个字母大小写的差别,它把 keystore 文件中的
Crypto
的首字母大写了,以至 imToken 等钱包无法导入。
修改源码 js\etherwallet-master.js 文件,把Crypto:
改为crypto:
即可。 -
以太坊账户的疑问。不少人会问到这个问题:账户钱包地址都是离线创建,以太坊网络怎么知道你的地址和余额?以太坊是基于账户的设计,和比特币的 UTXO 不一样。比特币网络不保存某个账户的余额,这是由钱包或者其他中心和的服务通过账本进行了遍历和索引。以太坊网络中则保存了账户地址对应的余额。那么这个账户地址的余额是什么时候出现到了链上呢?答案是 当有交易从发送者的账户转移价值到接收者账户时,如果接收账户还不存在,则在区块链中创建此账户。 这时,你离线创建的账号地址才真正出现在链上。
0x01 以太坊钱包开发
通过 geth 客户端创建账户时,不会有助记词,是直接通过密码生成了 keystone 文件。以太坊中的助记词是从比特币钱包拿过来用的。助记词、种子、私钥的关系,可以参考《精通比特币》 http://zhibimo.com/read/wang-miao/mastering-bitcoin/Chapter04.html 中的 确定性(种子)钱包 章节。
-
Android 钱包开发,可以参考 https://github.com/p-acs/ethereum-offline-signer
-
iOS 钱包开发,可以参考 https://github.com/ethers-io/ethers.objc
-
Web 钱包开发,参照 MyEtherWallet https://github.com/kvhnuke/etherwallet
移动端 Android 和 iOS 开发,还可以选择 React Native,使用 web3js,毕竟 web3js 已经把 JSON-RPC 接口封装好了。
0x02 以太坊接口开发
Geth、Parity 对外都提供了标准的 HTTP JSON-RPC 接口。更方便的是,Node 有 web3js 库可以用,Java 和 Android 有 web3j 库可以用。
HTTP JSON-RPC 的说明
JONS-RPC 中提供了 eth_sendTransaction
、 eth_sendRawTransaction
、 personal_sendTransaction
。这三个都是向节点发起交易,可以用来转账、调用合约。那么,它们的的区别是?
eth_sendTransaction
发送未签名的交易,但是在发送这笔交易之前,需要调用 personal_unlockAccount
解锁交易发起方的账户。开放 personal_unlockAccount
容易引起安全问题,例如这样的攻击手段 https://mp.weixin.qq.com/s/Kk2lsoQ1679Gda56Ec-zJg
这种交易构造起来是这样的:
1 |
{ |
eth_sendRawTransaction
发送签名后的交易,没有安全风险。但是需要自己把合约方法名、所有参数按照以太坊规定编码成 ABI 十六进制形式。编码方式参照 https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
好消息是,以太坊提供这样的库来编码函数名和参数编码。go 使用 go-ethereum 的 github.com/ethereum/go-ethereum/accounts/abi 中提供的 Pack() 方法,JS 使用 https://github.com/ethereumjs/ethereumjs-abi 提供的 rawEncode() 方法。
这种交易构造起来是这样的:
1 |
{ |
personal_sendTransaction
发送未签名交易,但附带上账户的密码。
这种交易构造起来是这样的,123456
是账户密码:
1 |
{ |
Mist 是通过这种方式发起交易的。personal_sendTransaction
并不能用在高并发上,因为节点解锁账号需要消耗大量时间。
go 开发说明
因为 go-ethereum 以太坊客户端使用 go 语言编写,因此可以直接借鉴 go-ethereum 中调用智能合约的方法来用。
使用 abigen 工具可以把 Solidity 写的智能合约编译成 go 语言文件,然后就可以使用了。
参照 https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
会用到:
1 |
"github.com/ethereum/go-ethereum/ethclient" |
ethclient 可用于连接以太坊节点,RPC 或 IPC 都能用,用法:
1 |
conn, err := ethclient.Dial("http://localhost:8545/") 或 |
然后在 abigen 生成的 xxx.go
合约中可以看到合约名字 XXX
。利用合约名子、合约地址、keystore 和密码去调用合约,发起交易:
1 |
contract, err := XXX(common.HexToAddress("合约地址"), conn) |
利用这个 contract 和 auth 就可以调用合约了:
1 |
# 如果只是调用合约查询,不发起交易,不用 auth 参数 |
注意!
本质上,go 的使用方法也是调用了 JSON-RPC。调用合约封装了eth_sendRawTransaction
方法。这个方法比eth_sendTransaction
复杂一些,调用之前会自行处理 nonce、gasPrice、gasLimit 等参数。nonce 这个参数使用eth_getTransactionCount
方法获取(参数为账号地址和 “pending”)。这就会导致一个问题,当一个账号刚刚提交了一个交易,该账号立刻又要发起另一笔交易时,上一笔交易还未被 “pending” ,此时eth_getTransactionCount
的结果不变,导致构造新交易的 nonce 和上一笔交易 nonce 相同。会提示 “replacement transaction underpriced” 这种错误。而使用
JSON-RPC 的eth_sendTransaction
或personal_sendTransaction
不会出现这个问题。Mist 即使用的后者。
Node 开发说明
web3js 库封装了 JSON-RPC API。只需要知道合约 ABI 和合约地址,就可以很方便地调用合约。
Java / Android 开发说明
web3j 封装了 JSON-RPC API。合约操作也有捷径,把 Solidity 合约编译成 Java 类去使用。
-
安装 Solidity 编译工具 solc
-
下载 web3j 工具 https://github.com/web3j/web3j
1 |
# 把合约 test.sol 编译为 bin 和 abi 文件, 输出到当前目录下的 output 文件夹中 |