区块链钱包的技术演进和创新路径:HD 钱包、Web3Auth 和 WalletConnect 的原理和优势
2023-12-01 17:40
WarderPANgo
2023-12-01 17:40
订阅此专栏
收藏此文章

一、区块链钱包行业现状

1.1 市场分析

DappRadar 发布了 2022 年 8 月的报告显示,DApps 活动逐月持续下跌,唯一活跃钱包(UAW,指与给定智能合约交互的唯一地址)已降至 167 万个的最低点,2022 年 8 月,UAW 与上个月相比下跌了 2.53%,与 2021 年 8 月相比也下跌了 14.73%

钱包同质化严重,但头部效应也很明显,在安卓设备上最受欢迎的 5 个加密货币钱包(Trust、Metamask、Crypto.com、imToken 和 BitPay)累计安装量超过 2000 万,远远超过排名靠后的钱包。

用户需求多样化,从原来单纯的存储和交易代币,发展到质押投票、流动性挖矿、使用 DeFi,收藏和展示 NFT,游玩 Gamefi,参与 Web3 建设。

1.2 商业模式分析

   基础功能 + 增值服务的商业模式

基础功能:收款、转账、查询,Dapp 浏览器

增值服务:

交易及兑换类服务 · 行情资讯服务

理财服务 · POS 挖矿服务

资产聚合类服务 · 社交聊天

Dapp Store

1.3 钱包分类

1.4 主流钱包对比

二、区块链钱包关键技术

2.1 HD(Hierarchical Deterministic)分层确定性钱包

HD 钱包由来:在 HD Wallet 出现之前,比特币钱包是通过随机数生成互不相关的多个私钥,这种类型的钱包被称作非确定钱包。为了解决非确定性钱包的私钥管理问题,社区成员提出了一种使用“种子”和 哈希函数单向生成一组私钥的方法,并在这种思想的基础上,增加了密钥派生(BIP32)、助记词(BIP39)、派生路径规范(BIP43/44)等功能,形成了现在统一规范的 HD wallet。

2.1.1 BIP32

钱包可以看做是一个私钥的容器,按照上面的方法,我们可以生成一堆私钥(一个人也有很多账号的需求,可以更好保护隐私),而每个私钥都需要备份就特别麻烦的,为了解决这种麻烦,就有了 BIP32 提议: 根据一个随机数种子通过分层确定性推导的方式得到 n 个私钥,这样保存的时候,只需要保存一个种子就可以,私钥可以推导出来,大致流程如下:

根种子输入到 HMAC-SHA512 算法中就可以得到一个主私钥 (m) 和 一个主链编码

上一步生成的密钥(由私钥或公钥)及主链编码再加上一个索引号,将作为 HMAC-SHA512 算法的输入继续衍生出下一层的私钥及链编码

推导过程是确定(相同的输入,总是有相同的输出)也是单向的,子密钥不能推导出同层级的兄弟密钥,也不能推出父密钥。如果没有子链码也不能推导出孙密钥。

   

2.1.2 BIP39

BIP39 使用助记词的方式生成种子,这样用户只需要记住 12(或 24)个单词,通过 PBKDF2 与 HMAC-SHA512 函数创建出随机种子作为 BIP32 的种子。

助记词生成:先生成一个 128 位随机数,再加上对随机数做的校验 4 位,得到 132 位的一个数,然后按每 11 位做切分,这样就有了 12 个二进制数,然后用每个数去查 BIP39 定义的单词表,这样就得到 12 个助记词。

助记词推导出种子:使用 PBKDF2 函数把助记词明文和盐值作为输入参数,然后重复进行运算最终产生生成一个更长的(512 位)密钥种子,再构建一个确定性钱包并派生出它的密钥。

2.1.3 BIP44

BIP44 为秘钥路径约定了一个规范的含义 ( 也扩展了对多币种的支持 ),指定了包含 5 个预定义树状层级的结构:

m / purpose' / coin' / account' / change / address_index

m 是固定的, purpose 也是固定的,值为 44

coin type:这个代表的是币种,0 代表比特币,1 代表比特币测试链,60 代表以太坊,完整的币种列表地址:https://github.com/satoshilabs/slips/blob/master/slip-0044.md

account:代表这个币的账户索引,从 0 开始

change:常量 0 用于外部 ( 收款地址 ),常量 1 用于内部(也称为找零地址)。外部用于在钱包外可见的地址(例如,用于接收付款)。内部链用于在钱包外部不可见的地址,用于返回交易变更。 ( 所以一般使用 0)

address_index:这就是地址索引,从 0 开始,代表生成第几个地址,官方建议,每个 account 下的 address_index 不要超过 20

助记词生成以太坊地址示例:

var bip39 = require('bip39’)var hdkey = require('ethereumjs-wallet/hdkey’) var util = require('ethereumjs-util’) var seed = bip39.mnemonicToSeed(mnemonic, "pwd"); var hdWallet = hdkey.fromMasterSeed(seed); var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0"); console.log("私钥:"+util.bufferToHex(key1._hdkey._privateKey)); var address1 = util.pubToAddress(key1._hdkey._publicKey, true); console.log("地址:"+util.bufferToHex(address1));

2.2 Web3Auth 原理简介 - Torus 钱包

Web3Auth 是用于 Web3 钱包和应用程序的身份验证基础架构,支持 Web 和移动原生平台上主流社交登录(谷歌、Twitter、GitHub、微信等),用户不需要保存记录冗长的助记词,减少了用户由于助记词丢失造成资产损失的情况。同时 Web3Auth 架构保证用户完整加密私钥不会记录在密钥基础架构系统的任何地方。

目前已在 Binance Extension Wallet、Ubisoft、Kukai、Skyweaver 等钱包和应用程序上保护了每月超过 100 万用户和 800 万密钥。


用户私钥被拆分三个 share,与现有的 2FA 系统类似,用户需要证明至少拥有 3 份秘密中的 2 份,以便检索并组装成用户的私钥。三个 share 如下:

Social Login Share:加密存储在 Web3Auth Auth Network 中,与用户社交账号关联检索。

Trusted Device Share:存储在用户可信的设备上,可以是浏览器本地存储或者 Dapp 应用上下文中。

Recovery Share:由用户保留的额外共享,可以保存在单独设备上。

优势弊端非托管式的密钥存储方式,用户始终可以控制其加密密钥对的所有权和访问权限, Web3Auth Auth Network 只能访问其中一个共享,无法组装成用户私钥。

共享冗余设计,当用户丢失设备时,仍然可以通过 Recovery Share 和 Social Login Share 恢复其私钥,还可以刷新吊销丢失的共享,这比丢失助记词就可以完全访问私钥的情况,更加的安全有效。同样如果 Web3Auth Auth Network 拒绝返回 Social Login Share 时,用户仍可以使用 Trusted Device Share 和 Recovery Share 重建奇私钥。

安全可扩展,用户可以通过将 2/3 阈值增加到更高的阈值来提高其密钥的安全性,例如,用户可以将阈值从 2/3 增加到 3/4,并添加另一个身份验证因素(如硬件设备)

虽然 Web3Auth 的用户准入门槛很低,但后续登录新设备的就会复杂很多。Web3Auth 为了减轻自己的中心化程度,要求用户提供邮箱、密码、老设备等多个验证方式后才能登录,这就让用户更换设备登录的操作变得很麻烦。

因为 Web3Auth 没有链上结构,所以用社交账号或邮箱登陆的环节和区块链没有太大关系,虽说采用了比中心化托管稍微好些的多节点托管,但本质上还是属于托管服务。

2.3 WalletConnect 原理简介

WalletConnect 是一个开源项目,是基于⼆维码建立连接的基础通讯设施,主要解决使用移动端钱包登录 PC 端 Dapp 问题。Dapp 登录期间虽然获取到了钱包地址,但私钥始终安全地存储在我们的移动设备上,Dapp 上进行的每笔交易都需要我们通过移动端钱包软件进行额外的手动确认。WalletConnect 只是⼀套加密的通讯协议,具体传输信息由 Dapp 和 Wallet 决定。

    

详细交互流程如下:

① Dapp 端与 Bridge 服务器建立 Socket 连接;

② Dapp 端⽣成 Dapp ClientID 和 Dapp Topic,并订阅 Dapp ClientID;

③ Dapp 端向 Bridge 服务器发送 Topic 为 Dapp Topic 的信息,并携带 Dapp ClientID 信息;

④ Dapp 端展示⼆维码包含 Dapp Topic 信息,Bridge 服务器的地址和密码;

⑤ 钱包端扫描 Dapp 端的⼆维码,解析出⼆维码信息获得 Dapp Topic, Bridge 服务器地址和密码;

⑥ 钱包端与 Bridge 服务器建立 socket 连接,然后产生 Wallet PeerID,并订阅 Dapp Topic 和 Wallet PeerID;

⑦ 接下来钱包端将接收到中继服务器转发过来的 Dapp 的登陆请求;

⑧ 钱包端处理是否同意 Dapp 的登陆,发送 Topic 为 Dapp ClientID 的消息,并把处理结果和 Wallet PeerID 的信息传递回去;

⑨ 此时连接建立成功,如果 Dapp 想给钱包端发送消息,则发送 Topic 为 Wallet PeerID 的信息即可;

⑩ 如果钱包端想给 Dapp 发送消息,则发送 Topic 为 Dapp ClientID 的信息即可。


三、以太坊钱包开发

3.1 以太坊钱包开发常用开源库

3.1.1 开源库 1 keythereum

示例 1 生成 keystore 并存储

var keythereum = require("keythereum");var params = { keyBytes: 32, ivBytes: 16 };var dk = keythereum.create(params);keythereum.create(params, function (dk) {    var password = "wheethereum";    var kdf = "pbkdf2";    var options = {        kdf: "pbkdf2",        cipher: "aes-128-ctr",        kdfparams: {            c: 262144,            dklen: 32,            prf: "hmac-sha256"        }    };    keythereum.dump(password, dk.privateKey, dk.salt, dk.iv, options, function (keyObject) {        keythereum.exportToFile(keyObject);    });});

示例 2 导入 keystore

var datadir = "/home/jack/.ethereum-test";var keyObject = keythereum.importFromFile(address, datadir);console.log(keyObject)keythereum.importFromFile(address, datadir, function (keyObject) {   console.log(keyObject)});

示例 3 从 keystore 中恢复私钥

var privateKey = keythereum.recover(password, keyObject);console.log(privateKey)keythereum.recover(password, keyObject, function (privateKey) {  console.log(privateKey)});

3.1.2 开源库 2 ethereumjs-tx

示例 4 交易签名

const transaction = require('ethereumjs-tx’);var rawTx = {    nonce:'0x' + transactionNonce,    gasPrice: '0x5208',    gas:'0x4a817c800', //+ oxgasFeeToWei,    to: '0x' + toAddress,    value:'0x' + oxNumBalance,    data: '',    chainId: 1};var tx = new transaction(rawTx);tx.sign(privateKey);var serializedTx = tx.serialize();if serializedTx == null  {    console.log("Serialized transaction fail")} else {    console.log("The sender address is " + tx.getSenderAddress().toString('hex'));    if (tx.verifySignature()) {        console.log('Signature Checks out!')    } else {        console.log("Signature checks fail")    }}

3.1.3 开源库 3 web3 库

示例 5 web3.js 初始化 Provider

var Web3 = require("web3");if (typeof web3 !== 'undefined'){    web3 = new Web3(web3.currentProvider);} else {    web3 = new Web3(new Web3.providers.HttpProvider("http://10.23.1.209:8545"));}

示例 6 获取账户余额

web3.eth.getBalance("0x68db18a9cd87403c39e84467b332195b43fc33b5", function(err, result){    if (err == null)    {        console.log('~balance Ether:' +web3.fromWei(result, "ether"));    } else {        console.log('~balance Ether:' + web3.fromWei(result, "ether"));    }});

示例 7 发起交易

web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) {    if (!err)    {        console.log(“hash:” + hash);     } else {        console.log(err);    }});

3.1.4 开源库 4 blockchain-wallet-sdk

示例 8 创建助记词并生成以太坊地址

 var mnemonicS = require("../sdk/mnemonic/generateWord");  var address = require("../sdk/address/generateAddress");  var mnemonic= mnemonicS.createHelpWord(12, 'english');  var seed = mnemonicS.mnemonicToSeed(mnemonic);  var addressParmas = {      "seed":seed,      "coinType":"ETH",      "number":"0",      "bipNumber":"60",      "receiveOrChange":“0",      "coinMark":"ETH"  }  var addr = address.blockchainAddress(addressParmas);  console.log(addr);

示例 9 单笔转账签名

const testEthSign = require('../sdk/sign/ethereumSign');var privateKey = "a2506976294fc506f6969e8f914ae9371804b104163f07e8d0e96794d5b43189";var nonce = 78;var toAddress = "0xe558be4e90b2ac96ae5cad47dc39cd08316f2e57";var gasPrice = 9000000000;var gasLimit = 120000;var sendToBalance = 10; var signValue = testEthSign.ethereumSign(privateKey, nonce, toAddress, sendToBalance, gasPrice, gasLimit);console.log(signValue);

3.2 开源钱包 TrustWallet - Android 源码分析

Github 地址:https://github.com/trustwallet/trust-wallet-android-source

依赖的 Ethereum 相关开源包

主要目录结构:

四、区块链钱包未来发展

当前区块链钱包行业存在的困境:同质化严重,在任何一款钱包都能满足用户基本需求的时候,就都失去了用户粘性;区块链的“去中心化”特性,使得用户的钱包账号以及相关数据是完全互通的,无信息壁垒;以下是针对金融属性和互联网属性两个方面简单分析了区块链钱包未来发展方向。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

WarderPANgo
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开