需求背景
考虑中心化交易所,会为每一个用户提供一个唯一地址,用户可以把自己的资产发送到该地址,而该地址由中心化交易所控制,进行去中心化收款、中心化记账。
一个平庸的解决方案是,交易所真的为每一个用户创建一个 secp256k1 地址,并让用户转账 CoTA token。带来的问题是,交易所需要为每一个地址创建一个永久占用 145 ckb 的 CoTA Cell。
解决方案一:代收地址注册(不建议该方案)
方案描述
- 中心化交易所(下称中心方)在 registry-cell 注册 secp256k1 代收地址,其特点为
- 必须是 secp256k1 地址
- 必须在 k-v 中指明 v 为另一个地址的 lockhash(默认的注册行为是指明 v = 0xFFFF…)
- 无需提供 cota cell 作为 tx output
- 必须提供 secp256k1 地址对应的数字签名作为交易签名(默认的注册行为不需要交易签名)
- 当用户充值时,会 withdraw CoTA token 到该 secp256k1 地址
- 交易所归集 token 时,提供如下 proof
- 历史 registry-cell 在 block head 的证明
- 该 secp256k1 代收地址 在 registry-cell 的 smt 中的证明
- 另一个地址的 lockhash 与当前 claim 的目标地址的匹配证明
- 常规的 claim 证明
方案缺点
- 交易所需要频繁操作 registry cell
- 证明复杂,需要多层证明
- 一次只能 claim 一个 token
解决方案二:子地址
方案思路
如果我们要求用户在转账的时候附带一个充值附言,比如 uid,那么用户可以都充值到同一个地址,这样交易所根据 uid 进行入账即可。但缺点有两个1)用户如果忘记填写了,会带来很大的客服压力;2)需要协调应用方增加附言界面,这里难度太大。
那么如果我们把 uid 作为地址的一部分,比如 secp256k1 地址的 args 末尾,这样每个用户收到的地址是不一样的,不存在上面出现的两个问题,同时在 sk 持有人解锁 lock 的时候,script 依然按照前 20byte args 验证即可。
方案描述
名词解释
- secp256k1-sub lock:支持 args 超过 20 字节的 secp256k1 lock
- 主地址:secp256k1-sub lock 地址,记其 args 为 master-arg
- 子地址:args 超过 20 字节的 secp256k1-sub lock 地址
- ext-arg:子地址中,args 超过 20 字节的部分
子地址
- 交易所生成主地址
- 交易所为每个用户生成子地址,子地址args 为 master-arg 加扩展字节(ext-arg)
- ext-arg 的长度、含义由交易所自行指定,默认为 UIN32 uid
- lock hash ⇒ secp256k1 script(修改版本,因原版限制 20 字节arg)
- args ⇒ master-arg | uint32-uid
secp256k1-sub lock 合约描述
考虑到该地址可能不光 CoTA 会作为虚拟地址使用,也可能有真实的 cell 以它为地址保存,例如交易所提供该子地址给用户充值 ckb,那么该合约需要完成符合预期的功能
- 交易 witness 与原 secp256k1 要求一致
- 不限制 20 bytes args
Registry 合约修改
- secp256k1-sub lock 地址只允许主地址注册,不允许子地址注册,即如果注册地址的 lock hash 是 secp256k1-sub,则 args 只能是 20byte
CoTA 合约修改
- withdraw 到交易所子地址(无修改)
- 无特殊要求,和提现到其他地址无区别,提现记录忠实写入完整子地址(带 ext-arg)
- claim 转账到子地址的资产
- 要求 claim 地址必须是对应的 secp256k1-sub lock 主地址,跳过子地址检查(不修改的话,需要匹配子地址,修改的话只需要匹配主地址)
方案优点
- 改动小,兼容性强