程序员视角的钱包建立到交易签名 - Kai | Jeth 第二期

编者按:本文系 imToken 首席架构师 Kai 讲师,在由掘金技术社区主办,以太坊社区基金会、以太坊爱好者与 ConsenSys 协办的《开发者的以太坊入门指南 | Jeth 第二期 - 杭州场》 活动上的分享整理。Jeth 围绕以太坊技术开发主题的系列线下活动。每期 Jeth 会邀请以太坊开发领域的优秀技术团队和工程师在线下分享技术干货。旨在为开发者提供线下技术交流互动机会,帮助开发者成长。git

本场分享视频回放连接(B 站)

我是 imToken 的首席架构师 Kai。开篇我先给你们抛出此次分享的核心要点: 在以太坊上,你的私钥就是你的帐户。程序员

生成随机数

一开始你什么都没有,你须要生成一个随机数,随机数我以为程序员都很熟悉,某个事情随机发生须要找一个结果,要产生随机数。github

私钥生成

随机数会生成私钥,这时你有两个选择:算法

1. 直接随机生成 32 bytes

直接随机生成 32 bytes 的二进制内容,把这个做为本身的私钥。假如你打开任何一个以太坊的钱包,你并入到私钥的界面,你随便输出一个东西,只要位数符合要求,它均可以做为钱包导进去。但这里面有一个安全性的问题,你们想到我这样能够看到别人的钱包,可是本身能够回去算下,32个字节能够生成多少个私钥。安全

2. 肯定性推导

这个作法在钱包里很常见。钱包生成一个随机数,而后再拿这个随机数查字典,获得12个单词左右的助记词,再经过路径拿到你的私钥。网络

GitHub 连接:https://github.com/bitcoinjs/bip39数据结构

助记词的生成过程也颇有趣,你把随机数拆分11个 bits,就有了11个二进制的字符串,社区里面有一个叫作 bip39 的二进制字符串的对应规范,里面有一个字典,你根据拿到的字符串查字典,查到11个字符串对应的11个单词。最后对前面这11个词作一个 Checksum ,你的钱包获得12个助记词,最终能够推出你的私钥,进而推出你的钱包帐户。架构

Hierarchical Deterministic Wallet 分层-肯定性-钱包

这里有两个概念,一个叫作分层,另外一个叫作肯定性并发

什么是分层,一开始随机数 seed 推导出了一把 Master key,这把 Master key 在下面会继续推导 Child keys,再往下推能获得私钥。这就是前面所提到的肯定性推导。我知道一份助记词,这个助记词不管如何,按照固定的路径能够推出一个能够预期的私钥,叫作肯定性;分层你有不少把私钥,不少把私钥里面从一个助记词里面一层一层倒出来,因此咱们在这里叫作分层决定性钱包。运维

BIP44

助记词有了,咱们要推导私钥,私钥的推导路径就是由这样一串字符格式:m/purpose/coin/account/change/address_index, 一层一层推导出来的,咱们叫它 BIP44。不一样的链有不一样的推导路径,好比说,以太坊是60。你有一个助记词以后,你能够根据肯定性的路径推导出不一样的私钥,不一样的私钥就是在不一样的这些区块链上面的一个身份的一个钱包。

存储私钥

随机数变成助记词,助记词变成私钥,私钥如 STR 何保存下来,毕竟你的钱包不可能使用一次,好比说你有一个朋友谈到众筹,1.8元买到以太坊,他转给你2个,这2个以太坊你要存下来,你存下来以后,你的私钥须要落地存储。但存储不是简单地存储便可。网络空间不安全,天天都有人被盗 QQ 号,区块链世界里面当你的帐户资产被盗是不可逆的,这时你须要作安全加密的的存储。

最终你的私钥会变成 V3 KeyStore 这样的长字符串。

  • 这里 KDF 的算法是给一个密钥经过 hash 的方式,计算 N 遍,好比说这里面有一个 N 值是26万次,这里面给出的密码会被这个 KDF 计算26万次以后输出所对应的密钥,所以别人想要破解的话,他须要计算的次数也须要26万次,这至关耗费 CPU 资源若是你这个钱包利用很高的值,这是加密的过程。
  • mac 值就是用做校验的。经过刚刚这个算法,把你的密钥保存以后每次输入密码解开,解开以后怎么样经过验证这个密码是正确的,我把这个密码走前面的这个部分,出来的结果是否对得上,你每次输入密码解开钱包签名里面,这个时候会把你的密钥按照一样的过程走一遍,作一个 hash 对比一下这个 mac 值对应得上,若是对得上,说明加密的这个结果就是你用这个密码加密的。

私钥如何转换到帐户地址

私钥有了,也落地存储了,而你的私钥要转换到帐户地址,这里有另外的一个推导过程。

这里用到了前面提的不对称加密算法,先作一个椭圆曲线给你一把密钥,还有一把公钥,就是他把私钥和公钥,把公钥投入到 hash ,再去拿第一位的一部分做为一个地址,你的钱包就有一个地址。

这是一个比较复杂的数学过程,做为咱们作工程的人来说,大 K 的最终的结果是一把私钥,小 k 是一把公钥;公钥跟私钥中间的关系是固定的,只能单向推导——从私钥推到公钥;这个椭圆曲线是比特币最先实现时所选的椭圆曲线,是相对而言效率最高的椭圆曲线。

转帐

刚刚这些事情作完以后,你就能够去转帐,可是转帐以前,你的帐户须要有一些资产。

在以太坊中转帐的数据结构如上图所示,包括一个帐户交易计数 Nonce,你要转给谁,你要转多少钱,以及在最下面有一个签名。你拿你的私钥去对这个交易作一个签名。这里面有不少种参数,咱们一个一个来看一下。

Gas

首先是Gas,Gas 是以太坊代币的机消耗机制,你发起一个以太坊交易的时候,你必须消耗必定的以太币并算到 Gas 中,Gas 最终会流到矿工手里。Gas 的机制是先扣再退——发起交易时,你最初付出的 Gas 要写得大一点,跑完一条交易之后,最终的结果是消耗的 Gas 比填写的要少,系统会把剩下的退还。

Gas 就是让矿工打包须要付出的费用,你本身能够定义须要多少个 Gas,每一个 Gas 值多少以太币,并告诉矿工说,我愿意为这笔交易付出多少钱、这笔钱转给谁和转多少。这里面 Gas 的计算最终在以太坊的虚拟机中按一条一条的指令扣费,你的这些转帐里面,按照指令条数和存储空间计费,最终把你的手续费扣下来。

重放保护

Nonce 是帐户交易计数,比方说你的第一笔交易 Nonce 是0,而后一、二、3以此类推加上去。

ChainID 是分叉链区分。以太坊和比特币都有本身的测试网络,假如说你在测试网络上面签署一笔交易,别人把它放到主网中运行,这时会发生转帐。ChainID 会在当前跑的区块链里面,记录下你的 ID,防止你测试时引起交易。别人拿到这笔交易到主网上面从新运行时,会有一个负责全部交易的计数器,能够防止交易的二次发生。

一些有趣的事情

  • 不指定转帐人:首先转帐的时候没有指定说是谁转帐?它只有 to,但没有 from。
  • 交易签名时就肯定了 TxHash:交易签名的时候,交易的参数最终会算出来一个 hash,这个 hash 会让你到网络上交易以前,告知你 Gas 的费用。
  • Out of Gas:当你付出的矿工费 Gas 不够的时候,系统就会扣除你的矿工费,并告诉你因为 Gas 不足致使交易失败,可是照样把你这一条交易打包进去。

私钥签名

获得签名以后,就会有椭圆曲线还原出你的公钥,公钥能够算出来你的地址,因此这是刚刚所说的以太坊的交易里面没有 from。由于有椭圆曲线这个非对称加密的存在,它拿到你的公钥后,能够反推出这个地址是否为发出人的地址。这也就意味着,若是你们的 nonce 和交易计数是一致的话,不管这个交易发给谁签名,我只要拿签名后的数据就能够上传并执行这笔交易。

交易广播

交易签名完以后你要广播并上链,上图是以太坊矿工打包的记录。

交易上链的过程

交易上链的过程分为四点,首先发送交易到指定节点,或前面提到的某一个授信节点。对于钱包来讲,它们会运行本身的节点,像以太坊会有官方的客户端 Gas。咱们作钱包须要本身运维提供服务的节点池,用户把这一条交易发到节点上面,节点之间互相通信,把这笔交易的信息扩散到全网,而后矿工节点(即专门作打包交易的节点)有一个打包池。以太坊交易的活跃程度比你当前打包要快,特别是网络拥堵的时候,放到这个池子以后,矿工在池子里面挑选一些对于他们有利的,谁愿意花钱打包,被矿工选中打包到区块里面。

矿工打包策略

矿工打包策略很简单,那就是向钱看。网络搜集全网搜集到各类各样的交易上传到 TxPool 即交易池当中。矿工老是在寻找找到开出更高 Gas,也就是说愿意付出更多手续费的交易,并打包进来。由于最终的前30名的 Gas 费用最终落入矿工,因此他们就会由高到低排序,并先服务 Gas 开价更高的富人。在打包的过程当中有可能出现空块,出空块的意义很大,由于你打包一块出来的同时,还能够拿到以太坊网络自己给你的奖励。

Gas 的技巧

  • 交易比较堵的时候,你所发起的交易 Gas 比较低,仍在交易池中待打包。这时你能够再发一笔交易,用高 GasPrice 替换掉 TxPool 中的交易。
  • 想让矿工更快的打包时,能够在把这些交易替换掉,若要加速交易的确认,也能够给出比较高的 GasPrice。
  • 经过网络状况计算最优 GasPrice,由于每一个时间节点网络上面发生的 GasPrice 不同,咱们按照网络状况自动给你一个最优的,以一个较低的 GasPrice 尽快打包。

矿工现状

一条链上数目庞大的节点,能保证你的矿工足够的分散。全部的打包的矿工都是在同一个大楼里面,我控制这个大楼就是控制这个网络。目前来讲主链上只有矿池,没有孤儿矿工。由于矿池是全数据的节点,矿池找了不少矿工(也就是矿机),把交易派发给矿工,让它们算出来比较合理的块,并尽快地把块打包。如今来讲矿机几乎是不知道区块链,它不会理解这一串长链有什么意义。另外,跟矿机和矿池强大的计算能力相比,你的电脑计算能力很弱,挖矿的效率跟不上。因此目前来看,矿池才是金主,即网络的实际控制人。

讲了这么多,你们应该理解到我文章开头下的结论————你的帐户就是一把私钥,并且你的帐户有了一把私钥以后,拿你的私钥作了签名。这时就会发一笔交易,你能够转帐,你有私钥地址后能够接收别人转过来的资产。接下来咱们往下继续探究帐户的结构,从底层思考以太坊。

更底层去看帐户

状态机模型

你能够把以太坊帐户想象成状态机模型的结构。如图, alice 和 Bob 发生一笔交易并发生签名(Nonce = 1, Balance = 3)而后这笔交易被区块打包。打包以前,这个 alice 有42个,打包完成以后变成39个且 Nonce +1。

Ethereum Account (https://github.com/ethereumjs/ethereumjs-account)

这里 nonce 和 balbance 没必要赘述,当 Stateroot 不为空时,这个帐号是一个合约,即合约的状态就存在该字段中。 CodeHash 是你的合约帐户里面的具体代码,但不彻底是在这两个字段里面,这两个字段引用到底下的时候,只是一个指引你去找到对应东西的索引。

合约帐户

  • 只包含代码
  • 可存储状态

合约帐户跟普通帐户不同,普通用户的状态是空的,不可以存储状态。以太坊用户的钱包状态是每一个人有多少钱。

一些帐户有趣的事情

  • Ether 余额记录在帐户自己
  • Token 余额记录对应在合约帐户上

为何要讲这个呢?你们知道以太坊去年由于不少项目发众筹而变得很火。对于众筹来讲,每一个 Token 即每一个代币都是一个合约,这个合约的数据记录在合约帐户上,而像以太坊的余额是记录在帐户自己。

在这里举标准的以太坊 ERC-20 代币规范做为例子。decimal 是余额中小数点的位数。当用户用这个方法查代币余额时,记录在这个部署的合约中。剩下的提供给咱们更加经常使用的方法,包括受权,而在受权以后你能够作一些颇有趣的事情,好比去中心化交易。去中心化交易依赖于标准,把你的代币受权给合约的接口。须要留意以太坊自己的 Gas 是不可变动的,这与 imToken 不一样。

刚刚说有合约,你要去调用这个合约,调用合约的时候读、写。读合约的时候能够经过 JSON-RPC 的接口;写合约就是发交易,把调用合约的方法写在前面的交易的 data 字段,两种调用都须要进行 solidity-ABI 编码,里面有调取合约的方法,每次发这个交易的时候,你要根据这个编码编出来再调用。

编码的过程大概是这样的:你这里有一个 balance ,发起交易时,首先要把 ID 找出来,把你的参数再编码,加起来拼接在一块儿,最终你作调用的时候读取合约,写一下的 data 参数。

One More Thing

如何安全保存钱包

  1. 离线存储
  2. 硬件钱包
  3. 多签钱包
  • 对于咱们作钱包来讲,咱们接每一条链,咱们弄清楚在这一条链上发生了上面,这个链如何上传交易。对于以太坊自己的节点实现,咱们也有必定的了解;
  • 要搜集用户尚未的发生交易;
  • 矿池要足够大,要容纳下咱们本身的用户的交易。

想了解更多,我推荐阅读 Ethereumjs 源码。

Ethereumjs 在个人分享中,讲到代码相关的内容都附带了 GitHub 连接,源码或者里面的例子都来自于对应的仓库。若想快速了解一下以太坊如何运做,不妨去看看 Ethereumjs 的源码。

GitHub: kaichen(github.com/kaichen)

Twitter: _kaichen

以上就是我今天分享的内容,谢谢你们。

相关文章
相关标签/搜索