顾名思义,钱包是用来保存钱的。但在数字货币的世界中,钱包里面并没有“钱”。钱包账户里有多少“钱”都是记录在区块链上的,钱包里只是存储了账户对应的私钥,账户是从私钥相应的公钥衍生出来的。只要有了私钥,你就可以在数字货币世界里证明你的身份,发送区块链上属于你的资产。因此,钱包实际上是管理和存储私钥的工具。
Dagx钱包结构与Bitcoin类似,Bitcoin对管理和存储私钥以及通过私钥生成地址制定了一系列标准(BIP, Bitcoin Improvement Proposals),主要包括:8 G7 P" p+ j4 ?1 q
· BIP32:定义了HD钱包(Hierarchical Deterministic Wallet),允许从单一种子产生一树状结构储存多组私钥和公钥,可以方便的备份、转移以及分层权限控制等。HD钱包包含以树状结构衍生的密钥,使得父密钥可以衍生一系列子密钥,每个子密钥又可以衍生出一系列孙密钥,以此类推,无限衍生。下图展示了HD钱包的树状结构:
4 g8 r: P9 U/ r: D1 u% }
BIP39:将种子用方便记忆和书写的单词表示,一般由12个单词组成,称为助记词或助记码。由一系列单词生成种子是个标准化的方法,这样易于在钱包中转移、导出和导入。比如shrimp make call path pink draw gym song select brother social base,其相应的私钥为
xprv9s21ZrQH143K4HQ8sBPNNkXHNuVYfivR96gtpJzMWCJVF5hdYjzT5nVQu6GfHAQLPstQtFp9GTWADUcKgzaV5EyL5JxT2rxkyg51ReGTVg8。
BIP43:为HD钱包路径引入purpose字段,路径记作m / purpose’ / *,比如m / 0’ / *表示BIP32的默认账户。
BIP44:定义HD钱包路径字段,赋予树状结构中的各层特殊的意义,从而支持多币种、多账户,路径定义为:& a% e" i4 ]9 {+ ^" R
m / purpose' / coin_type' / account' / change / address_index8 t- w0 b$ S, F) C& P" G% }8 x
其中,purpose’固定是44’,代表使用BIP44;coin_type’用来表示不同币种,例如Bitcoin就是0’,Bitcoin Testnet是1’,Ethereum是60’;account’表示账户编码;change用来表示该地址是否为找零地址;address_index为地址编号。( Z) m F4 _7 ]- ]& C7 @
BIP45:定义多签名HD钱包路径字段,从而支持多签名地址,路径定义为:4 k6 o+ }/ e; m3 o d4 L4 w
m / purpose' / cosigner_index / change / address_index$ @) f# a% q& @: M! j3 |
其中,purpose固定是45’,代表使用BIP45;cosigner_index表示多签名用户编号;其余字段与BIP44相同。
Dagx钱包采用的是符合BIP44标准的HD钱包,其采用的路径结构为:3 d. _2 j0 r E* \5 ?% f
m / 44’ / 0’ / account’ / change / address_index! V! F) q1 E0 e, O; o% z! B
此外,除了使用account来标识账户外,还使用了该账户公钥的哈希值对账户进行唯一表示,表示方法为: l5 A* I9 E5 g! Q
var wallet = crypto.createHash(“sha256”).update(xPubKey, “utf8”).digest(“base64”);
Dagx中还有一类特殊的地址,即设备地址,其采用的路径结构为:
m / 1’ / *% o7 L: h- f. C! q; U
Dagx钱包使用了bitcore-lib和bitcore-mnemonic两个库进行钱包管理,简单的使用方法如下:$ K$ A. J. R/ ^3 C4 d' n
var Mnemonic = require('bitcore-mnemonic');
var Bitcore = require('bitcore-lib');; A! B+ B. C; i' J; b
// 生成助记词
 
// 'fee decorate step culture autumn game social very lemon drum embrace much'
 
var mnemonic = new Mnemonic(); . ? T6 |% H ?8 B2 \7 o
 ' O7 A- Q+ k. h2 N, D3 j1 c9 U
// 生成HD钱包私钥
 
// , [% Y9 ]; m6 ]
 
var xPrivKey = mnemonic.toHDPrivateKey(); ; d$ b2 z6 Q! p, f6 n5 ~
 
// 生成HD钱包公钥,account=0 O B4 ?' Y7 s4 G' I% F$ T# u; s2 z
 
//
 5 ]! J u; a6 s/ \
var xPubKey = Bitcore.HDPublicKey(xPrivKey.derive("m/44'/0'/0'"));
 , g4 S$ O, |! a6 f
// 生成设备私钥,用于产生设备地址
 7 O4 t% {# s- c; J4 h" c3 ?/ i9 ^
//
 
var devicePrivKey = xPrivKey.derive("m/1'");5 u% M; L: G% h9 \% ]- z
地址生成! f, s# r, A" q( M
Dagx中包括三类地址:2 q- [; }4 U) Z# U5 ~4 X& H5 t: B
· 普通地址
· 共享地址
· 设备地址$ N% R. `; }# D1 Y+ @
其中,普通地址和共享地址都是通过地址定义脚本生成的,二者的区别类似于Bitcoin中的P2PKH地址和P2SH地址,具体细节可参考《Dagx原理解析(三)地址、脚本及合约》;设备地址是采用设备公钥生成的,地址格式和校验规则与普通地址相同,只是前缀添加了0。
给定需要生成地址的对象,Dagx生成地址时采用的是ripemd160哈希,并给地址加上校验,使用的是Dagxcore/object_hash.js中的函数getChash160()函数。6 A4 v+ R7 K; X% O+ F w
具体来说,设备地址生成方法:$ h2 y% Y; k7 U% y" z0 x3 T2 H3 y
var ecdsa = require('secp256k1'); // 椭圆曲线非对称加密
 - m, ^& [5 {6 T" _+ b1 }# {0 N9 N
// 获取设备公钥
 
var pubkey = ecdsa.publicKeyCreate(devicePrivKey.privateKey.bn.toBuffer({size:32}), true).toString('base64');
 ) P- S1 [6 ^: w. w
// 生成设备地址
 6 H. R/ v2 R6 C" @
var objectHash = require('Dagxcore/object_hash.js');
 0 q0 P8 I3 q' d/ } j2 P, U
var device_address = '0' + objectHash.getChash160(pubkey);
普通地址生成方法:
// change=0 address_index=0,生成公钥
 7 D7 V, y6 J. E$ W8 K4 i* ]
var pubkey = xPubKey.derive('m/0/0').publicKey.toBuffer().toString('base64');
 ) t9 Y0 F* B2 w0 |
// 设定地址定义脚本,为单签名
 
var arrDefinition = ["sig", {"pubkey": pubkey}];- r1 N; }( V* ?& C
 7 T6 \9 U7 i! y2 a1 G7 o
// 生成地址% V7 ~( Y* K l+ r
 
var address = objectHash.getChash160(arrDefinition);
共享地址的生成方法与普通地址基本一致,唯一的差别在于地址定义脚本要更加复杂一些。
成为第一个吐槽的人