HD 钱包

HD 钱包是一个允许用户以一种确定性方式从普通种子中获取密钥的功能。基本上,你可以从一个随机种子中产生一个初始密钥。然后你就可以从 SK₀ 得到孩子 SK₀-₀, SK₀-₁。从这些孩子中,你可以得到 SK₀-₀-₀, SK₀-₀-₁, SK₀-₁-₀ 等(派生任意深度的树)。

我们会区分两种类型的键:

  • Hardened
  • Non-hardened

这里唯一的区别是 hardened 密钥允许从父密钥派生出子密钥。因此,为了派生一个 hardened 键,你必须拥有私钥。Non-hardened密钥允许从父公钥派生子公钥(不具有可用密钥)。

每个孩子被分配一个4字节的索引 i:

  • i ≤ 2³¹ - 1 non-hardened 键。
  • i > 2³¹ - 1 hardened 键。

属性:

  1. 树结构保存在根地址中。用户需要复制公钥将其传递给任何想要恢复树的人。

地址格式

我们使用 PublicKey 地址(已经存在于系统中),并添加属性字段。在由 0 (HD 钱包属性) ,我们存储树数据。

树存储为派生路径的列表。每个派生路径被指定为派生索引的列表。每个派生索引都是4字节的无符号整数。

所得到的对象被序列化并使用对称方案(ChaChaPoly1305算法进行加密),密码被计算为根公钥的SHA-512散列。只要我们实际上没有再根密钥上存储任何资金(不通过共识规则,而是通过用户界面),那么将不允许攻击者将链上的所有地址映射到根。

设计的关键点:根密钥不用于实际存储金钱。

用例

财务审计

应该提供一个根公钥的审计散列,让审计人员找到层次结构中的所有密钥。

付款服务器

它只适用于 non-hardened 键。

为了使服务器能够获得后续地址来收取付款,需要上传:

  • 根公钥
  • 有效载荷:
    • i 级别的 PK
    • 跟公钥的哈希
    • PK 的树路径

钱包

要使钱包在某个子树上运行,需要提供:

  • 根密钥
  • 有效载荷:
    • SK 级别的密钥 i
    • 根公钥的哈希
    • SK 的树路径

要求

A(K) 表示保存密钥对信息的地址 Kchild(K, i) 表示第 i个子密钥对 Ktree(K) 表示从 K(有证书余额)派生,以 utxo 保存的密钥对地址树。

a -> b 表示 ba 派生。a -x b 表示 b 不是从 a 派生的(在任何情况下):

priv(K) -> pub(K)
pub(K) -> A(K)
pub(K) -x priv(K)
A(K) -x pub(K)
A(K) -x A(child(K, i))

对于 hardened 键:

(priv(K), utxo) -> tree(K)
pub(K) -x pub(child(K, i))
priv(K) -> priv(child(K, i))

对于 non-hardened 键:

(pub(K), utxo) -> tree(K)
pub(K) -> pub(child(K, i))
priv(K) -> priv(child(K, i))

派生加密接口

符号:

  • kp 表示具有索引 p 的私钥。只是一个 Ed25519 私钥。
  • kp 表示带有索引 p 的公钥。只是一个 Ed25519 公钥。
  • cp 表示带有索引的链。

在比特币中,它们使用512位的散列,但 kp 只有 256 位,因为这个原因,我们需要遵循512位的密钥,所以我们不减少哈希空间。

  • 扩展的私钥是表示为 (ki, ci) 的一对。
  • 扩展的私钥是表示为 (Ki, ci) 的一对。

从应用角度来看,HD 钱包(BIP-32)引入了以下密码原语:

  • CKDpriv :: ((kpar, cpar), i) → (ki, ci)
    从父扩展私钥计算自扩展私钥

  • CKDpub :: ((Kpar, cpar), i) → (Ki, ci)
    从父扩展私钥中调用一个子扩展私钥

代达罗斯 HD 钱包

本节介绍 HD 钱包功能的使用方式。它分为两部分:

  1. 扩展钱包后端 API 以在本地支持 HD 钱包结构(就像在比特币中完成的那样)
  2. 利用新的地指数型来扩展区块链处理以保持多个客户端实例的 HD 结构同步。

本地存储

旧的存储

旧的钱包存储存的是地址列表。每个地址都与一个名称相关联,并且是从单独的密钥(由助记符备份并用消费密码加密)派生而来。

新的存储

钱包的存储扩展到存储钱包列表。每个钱包对应一个根密钥(由助记符备份并用消费密码加密)。

每个钱包都包含一个账户

每个账户都包含多个地址(即地址是 HD 树中第二层的关键字)。

这映射到一个 HD 树:

  • 钱包组对应第 0 级()密钥。
  • 钱包对应1级密钥(根的孩子)。
  • 地址对应于第2级(根的孙子)的密钥。

钱只保存在地址上。

当从一个或多个地址花费金钱时,如果有的话,将产生新的余额。

可用性

用户能够:

  • 导入/导出任意数量的钱包
  • 生成任意数量的账户
  • 分配钱包账户名称,
  • 生成任意数量的地址,
  • 改变钱包消费密码。

从区块链中读取 HD 钱包数据

有两种导入/导出钱包的方法:

  • 通过助记符
  • 通过导出文件。

助记符在前端生成,并允许确定性地生成密钥。名称不会被恢复。

导出文件能够恢复整个钱包结构。

导入

在两种情况下我们都有一个根密钥。在导入的时候会执行下面的步骤:

  • 根密钥在本地存储中被检查为不存在。
  • 遍历 utxo 查找与此根密钥对应的所有有余额的地址,并将它们与服务(钱包)一起添加到存储中。
  • 在文件导入的情况下,从步骤2得到的结构标有名称。此外,导入的文件中列出的钱包/地址,目前没有被使用。

新的事务处理

当有新的交易可用时(出现在区块或内存池中),输入会被分析。如果输入对应于具有 HD 钱包属性的公共密钥地址,则检查该地址是否对应于我们的钱包之一。如果是这样,地址将被导入到结构中(为了在用于界面显示余额)。