Grant RFC: pw-sdk - 让 CKB DApps 运行在任何加密货币钱包中

团队背景

为什么你是合适的人 / 团队?

雷兔 (Lay2) 是一个区块链产品研发团队,成员均有十年以上互联网产品研发经验以及两年以上的区块链研发经历。团队的区块链技术栈涉主要为闪电网络、状态通道等 layer 2 技术,产品设计注重实用性,在支付、DApp 方向设计研发了多款应用以及对应的框架。

项目缘由

为什么这个项目能对 Nervos 生态带来价值?

Nervos 还处于项目早期,无论对开发者还是用户而言,基础设施都还很不完善,这一现状给 Nervos 带来了生态的双边冷启动难题:如果没有优质开发者进行生态建设,就很难吸引到用户;但是没有足够规模用户又很难吸引到优质的开发者。更为严峻的是,一些早期的公链由于其先发优势和财富效应,已经聚集起了足够量级的双边生态,而这带来的网络效应进一步加剧了 Nervos 生态双边冷启动的难度。

幸运的是,CKB 的 Cell 模型和 RISC-V 虚拟机带来了前所未有的灵活性和扩展性,使得 Nervos 完全可以另辟蹊径。无需等待钱包以及 DApp 浏览器等设施走完开发、上线、积累应用和用户的漫长的过程,Nervos 可以通过直接「征用」其他区块链基础设施的方式,迅速接入大量用户。这样一来,Nervos 就能够越过生态冷启动困境,直接进入应用发展阶段。雷兔团队设计研发的 pw-sdk 就为 CKB 提供了这一能力。

与一般 sdk 不同的是,基于 pw-sdk 开发的 DApp (称为 pw-dapp) 可以在比特币、以太坊、EOS、Tron 等公链的钱包环境中直接运行。这里「直接」有两层含义:一方面,钱包运营方不需要进行任何针对性的开发,甚至不需要对 CKB 公链进行支持,即可运行 pw-dapp,如 MetaMask 钱包已经可以直接运行 pw-dapp;另一方面,对用户来说,现有的任何公链地址都是一个合法的 CKB 地址,无需进行 CKB 钱包的创建即可使用 pw-dapp,而且无论是操作体验还是资产安全性都能与其公链原生 DApp 保持一致。

对生态而言,pw-sdk 能够为 Nervos 带来大量成熟的基础设施及其附带的庞大用户基础,让 Nervos 生态建设从「基础设施建设->产品研发->用户积累->市场反馈」的单线程串行模式,升级为「基础设施建设」「产品研发」「市场反馈」的多线程并行模式,而且在很大程度上消除了「用户积累」的环节。可以预见,pw-sdk 将大大提升 Nervos 生态发展的速度,也能够给区块链世界展示出 CKB 作为新一代加密经济基础设施的的独特的能力与巨大的价值潜力。

技术规范与实现

你将如何成功地实施这个项目?

pw-sdk 包含:

  • pw-lib: 密码学原语库

例如识别以太坊签名格式所需的 Keccak256 哈希算法库,ECDSA Secp256r1 (NIST P-256) 签名算法库等。

  • pw-lock: 多链通用 lock script

pw-lock 能够验证比特币、以太坊、EOS 等公链钱包的签名,能够将这些公链的每一个地址(以公钥计算)都映射为一个合法 CKB 地址。

pw-lock 将使用 type id 的方式实现,方案稳定后会申请收录到短地址标准中,从而让其他公链钱包对应的 CKB 地址与默认的 CKB 地址在格式上完全一致。

  • pw-core: 面向前端的 js sdk

使用 Typescript 编写的前端 sdk:pw-core,为开发者提供在所有 pw-lock 支持的公链钱包中直接运行 CKB DApp 的能力,并内置了与上述后端服务器的交互。

时间线 / 路线图

项目所需的时间是否符合常理?

需要约 10 个月来完成这些工作,目前已经完成了部分工作,随着项目推进,我们会不断放出阶段性成果,让社区都能够体验和参与进来。


Checkpoint 1:2020 年 4 月

pw-lib,包括:

  • 持 Keccak-256 算法库

pw-lock,包括:

  • 支持以太坊钱包签名
  • 支持 EIP-712 签名标准
  • 能够使用 Type ID 部署

pw-core,包括:

  • API 接口设计

Checkpoint 2:2020 年 6 月

pw-lib,包括:

  • 支持 Secp256r1 (ECC P-256)

pw-lock,包括:

  • 支持 Web Authn

pw-core,包括:

  • 实现基本的 API 接口,如签名、交易发送、手续费计算等
  • 基础版文档和教程
  • 基于 pw-sdk 的样例 DApp

Checkpoint 3:2020 年 8 月

  • 所有 pw 组件增加对 BTC、EOS 等更多公链的支持

Checkpoint 4:2020 年 11 月

  • 正式版 pw-lib
  • 正式版 pw-lock
  • 正式版 pw-core
  • 正式版 p-wallet
  • 完善的文档和示例

感谢 @stwith 在本文的翻译上提供的帮助,感谢 @xxuejie @ash @keith 提供的技术支持,感谢所有内测组的小伙伴们提供的建议和反馈。

8 Likes

中译

本文中,我将介绍一些在摘要中提及的重要的技术和功能,并将其中一部分扩展延申到未来可能的应用场景中去。

关于 pw-lib

我们都知道,可定制的密码学原语是 CKB 的一大亮点,以至于我们在项目介绍的核心关键词和 AMAs 中多次听到这个特性。但是为什么这很重要呢?我们又可以在 CKB 上部署哪些密码学原语呢?我们应该如何使用它们?让我们向你展示一些关于我们将在 pw-lib 中引入的密码学库的想法吧。

  • Keccak-256 哈希算法

Keccak,有时候也叫做 SHA-3,是以太坊的哈希函数。事实上,Keccak-256 是 SHA-3 的超集,最终确定的 NIST SHA-3 才是标准的“SHA-3”,它不同于以太坊采用的哈希算法。

Keccak-256 提供 256 位的哈希。它在以太坊中用于生成交易哈希,区块哈希,以及地址。在 pw-core 中,Keccak-256 被用来生成要签名的哈希,并放在 witness 中。但是真正不可缺少的是在 pw-lock 中的 Keccak-256,我们需要从公钥中恢复以太坊地址。在 EIP-712 类型的数据签名中也需要 Keccak-256 作为唯一的哈希算法。

  • SHA-256 哈希算法

SHA-256 是 Secure Hask Algorithm-256 bit 的缩写。虽然它看起来要比上面提到的 SHA-3 算法“强大”很多,但实际上它属于 SHA-2 家族,自然是要比 SHA-3 弱的。

SHA-256 最著名的用例就是比特币挖矿。它也被用来生成比特币地址,并被广泛地用于 EOS 的哈希算法。pw-lock 有必要支持比特币钱包和 EOS 钱包。

  • Secp256r1(ECC P-256)非对称加密算法

与哈希算法不同,哈希算法主要用于将任意大小的数据以单向的形式映射到固定位数的字符串,非对称加密算法主要用于加密或数据的数字签名。

ECC 是 Eliptic Curve Cryptography(椭圆曲线加密)的简称,Secp256k1 和 Secp256r1 都属于这个算法家族。自从比特币首次使用 Secp256k1 作为签名算法以来,几乎所有的后继者都采用了这个设计,CKB 亦是如此。而 Secp256r1 算法,即 P-256,我们可以为 CKB 打开更加广阔的应用场景。

  • 更多的期待

随着场景的增长,对 CKB 上的密码库的需求也将会越来越大。比如 Tron 适配的 SHA3-256,以及用于和安全的 Web 应用进行交互的 RSA。通过 RISC-V 虚拟机和 CKB 的 Cell 模型,我们可以轻松地持续地集成这些库,而不会对开发者和用户造成任何中断。

关于 pw-lock

  • 以太坊签名算法的支持(包括 EIP-712)
/* secp256k1_keccak256_sighash_all.c */
keccak_init(&sha3_ctx);
unsigned char eth_prefix[28]= {
  0x19, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x20, 0x53,0x69,
  0x67, 0x6e, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61,0x67, 0x65,
  0x3a, 0x0a, 0x33, 0x32
};
keccak_update(&sha3_ctx, eth_prefix, 28);
keccak_update(&sha3_ctx, message, 32);
keccak_final(&sha3_ctx, message);
	
/* Personal hash */
ret = verify_signature(message, lock_bytes, args_bytes_seg.ptr);
if(ret == CKB_SUCCESS){
  return CKB_SUCCESS;
}

/* Typed Data hash */
ret = calculate_typed_data(message, message);
if(ret != CKB_SUCCESS){
  return ret;
}
return verify_signature(message, lock_bytes, args_bytes_seg.ptr)

这是 pw-lock 代码中的一部分,它展示了在 witness 中验证签名的逻辑。其中,‘keccak_int’ 为哈希计算初始化了文本,‘keccak_update’ 将消息追加到了原始的字符串中,‘keccak_final’ 完成计算。正如我们所看到的,lock 首先尝试从消息中恢复并验证 personal_sign 签名的公钥,如果失败,则将签名视为类型化的数据签名。完整代码:https://github.com/lay2dev/ckb-system-scripts/blob/master/c/secp256k1_keccak256_sighash_all.c

  • 如何支持 EOS 签名

EOS 的账户模型和以太坊不同。首先,EOS 的账户不是由公钥生成的,它包含有 12 个字符的长度,可以包含字母 a-z 和数字 1-5。实际上,密钥对仅作为解锁账户的密码。其次,可以有几个不同的密钥关联到同一个账户,并且具有不同的权限。例如,EOS 账户“zhixiandaren”有两个权限(默认就有两个):

Associated public keys for EOS account 'zhixiandaren': 
- active: EOS6P9tLLAQYJbmPzoTEG9vpS6GMHJyWW4bUeppqtAwcUuJpZNXYo
- owner : EOS6P9tLLAQYJbmPzoTEG9vpS6GMHJyWW4bUeppqtAwcUuJpZNXYo

在上面的情况下,只使用了一个密钥,但通常情况下应该有两个不同的密钥,owner 密钥具有最高权限,不应该频繁使用,比如交易、投票等常见操作应该使用 active 密钥进行处理。

为了支持 EOS 签名,pw-lock 需要比较 recovered 密钥和 active 密钥进行对比,哪一个需要被记录到脚本中的 args 字段中去。需要注意的是,这里针对的是普通的 EOS 账户,其他比如合约权限(@eos.code)等目前还不包括在内。

关于 pw-core

  • 如何将其他区块链地址映射到 CKB 地址上

我们先来讨论一下 CKB 地址到底代表了什么,我们都知道,如果 Alice 想给 Bob 发送加密货币,她只需要知道 Bob 的地址,就像给 Bob 发邮件一样,但是 Bob 如何实际使用接受到的资产,比如登陆到他的电子邮箱账户然后转发电子邮件。这就涉及到 lock script,这个脚本决定了你是否能够解锁这个资产,就像是用这个脚本来检测 Bob 是否具有登陆 “[email protected]” 这个邮箱的正确密码一样。

我们的区块链账户有一对密钥:公钥和私钥。地址通常是由公钥转换而来的(例如,以太坊地址是公钥的最后20个字节)。当我们发送一笔交易时,我们必须用我们的私钥创建一个数字签名,并将其与交易信息一起发送。当区块链验证你是否具有访问权限时,首先从签名中恢复公钥,然后将其转换成一个地址,将该地址将与我们声明拥有的地址进行比较。

对于具有固定解锁逻辑的普通区块链来说,使用签名和地址来确认所有权已经足够了。但是 CKB 非常灵活,每个人都可以用任何逻辑来创建自己的 lock script。因此,除了来自公钥 “地址” 部分之外,还有另一部分用于确定使用哪一个 lock script 来验证 “地址”。加上一些其他的部分,比如前缀和校验和,以及一些变换,我们最终就可以得到一个有效的 CKB 地址,像这样:

args:  /* the 'address' transformed from public key */
b39bbc0b3673c7d36450bc14cfcdad2d559c6c64

code_hash: /* which lock script to use */
9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8

address generated:
ckb1jda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgj53qk

让我们把地址拆开,看清楚:

ckb1 - the human readable part

jda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t2 - the Base32 encoded payload part, payload format: 0x02/0x04 | code_hash | args

4n3kxgj53qks - the checksum

关于 CKB 地址的更多信息,请参考:https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md

最后,我们了解到 CKB 的地址不仅包含公钥信息,还包含要使用的 lock script。现在我们就可以解释为什么以太坊地址可以映射到 CKB 地址:我们创建了一个与以太坊的 lock 具有相同逻辑的 lock script(放在 code_hash 部分),并将以太坊地址放在 args 中。就是这样,听起来很容易,不是吗?是的,但只在 CKB 上(可以如此简单容易地实现)。

  • 如何在其他区块链钱包内签名 CKB 交易

让我们以 MetaMask 为例:

/* personal_sign */
const DefaultSinger = (from, message) =>
  new Promise((resolve, reject) => {
    const params = [message, from]
    const method = 'personal_sign'

    window.web3.currentProvider.sendAsync({ method, params, from },
      function(err, result) {
        err && reject(err)
        result.error && reject(result.error)
        resolve(result.result)
      })
  }
  
/* signTypedData */
const TypedDataSigner = (from, message, { inputCapacity, outputs }) =>
  new Promise((resolve, reject) => {
    const typedData = buildTypedData(inputCapacity, outputs, message)
    const params = [from, typedData]
    const method = 'eth_signTypedData_v4'

    window.web3.currentProvider.sendAsync({ method, params, from },
      function(err, result) {
        err && reject(err)
        resolve(result.result)
      })
  }

以上是在以太坊 web3 环境中调用签名方法的常用方法。签名一笔以太坊交易和一笔 CKB 交易之间唯一的区别就是 “message” 部分。现在我们已经知道 pw-lock 能够通过 Keccak-256 从签名中恢复以太坊地址,所有我们唯一需要改变的是用 Keccak-256 算法去对 CKB 的交易进行哈希,然后我们将它传递给要签名交易的人。当涉及到 EIP-712 签名时,情况会略有不同,因为我们必须将消息以类型化数据的形式组合起来。

2 Likes