拥有安全的CEX:偿付能力证明及其他

2022 11月 19查看所有帖子


特别感谢Balaji Srinivasan以及Coinbase,Kraken和Binance员工的讨论。

每当一个主要的中心化交易所爆炸时,都会出现一个常见的问题是,我们是否可以使用加密技术来解决问题。交易所可以创建加密证据,证明他们在链上持有的资金足以支付他们对用户的责任,而不是仅仅依靠政府许可证、审计师和检查公司治理和运营交易所的个人背景等“法定”方法。

更雄心勃勃的是,交易所可以建立一个系统,未经存款人同意,它根本不能提取存款人的资金。潜在地,我们可以探索“不要作恶”的有抱负的好人CEX和“不可能是邪恶的”之间的整个范围,但目前效率低下且隐私泄露的链上DEX。这篇文章将进入尝试将交易所推向无信任一两步的历史,这些技术的局限性,以及一些依赖ZK-SNARKs和其他先进技术的更新和更强大的想法。

余额表和默克尔树:老式偿付能力证明

交易所试图以加密方式证明他们没有欺骗用户的最早尝试可以追溯到很久以前。2011 年,当时最大的比特币交易所 MtGox 通过发送一笔交易来证明他们有资金,该交易将 424242 BTC 转移到预先宣布的地址。2013年,开始讨论如何解决问题的另一面:证明客户存款的总规模。如果你证明客户的存款等于X(“负债证明”),并证明X币私钥的所有权(“资产证明”),那么你就有了偿付能力的证明:你已经证明了交易所有资金偿还所有存款人。

证明存款的最简单方法是简单地发布一个货币对列表。每个用户都可以检查他们的余额是否包含在列表中,任何人都可以检查完整的列表,以查看(i)每个余额都是非负数,以及(ii)总金额是索赔金额。当然,这会破坏隐私,因此我们可以稍微更改一下方案:发布一个配对列表,并私下向每个用户发送他们的值。但即使这样也会泄露余额,并且泄露余额变化的模式。保护隐私的愿望将我们带到了下一个发明默克尔树技术(username, balance)(hash(username, salt), balance)

 

 

绿色:查理的节点。蓝色:节点查理将作为他证明的一部分收到。黄色:根节点,公开展示给大家。

 

美库尔树技术包括将客户的余额表放入美库尔总和树中。在默克尔和树中,每个节点都是一对。底层叶节点表示单个客户的余额和加盐用户名哈希。在每个较高层节点中,余额是下面两个余额的总和,哈希是下面两个节点的哈希值。默克尔和证明,就像默克尔证明一样,是树的一个“分支”,由从叶子到根的路径上的姐妹节点组成。(balance, hash)

交易所将向每个用户发送其余额的默克尔总和证明。然后,用户可以保证他们的余额正确包含在总额中。可以在此处找到一个简单的示例代码实现

# The function for computing a parent node given two child nodes
def combine_tree_nodes(L, R):
    L_hash, L_balance = L
    R_hash, R_balance = R
    assert L_balance >= 0 and R_balance >= 0
    new_node_hash = hash(
        L_hash + L_balance.to_bytes(32, 'big') +
        R_hash + R_balance.to_bytes(32, 'big')
    )
    return (new_node_hash, L_balance + R_balance)

# Builds a full Merkle tree. Stored in flattened form where
# node i is the parent of nodes 2i and 2i+1
def build_merkle_sum_tree(user_table: "List[(username, salt, balance)]"):
    tree_size = get_next_power_of_2(len(user_table))
    tree = (
        [None] * tree_size +
        [userdata_to_leaf(*user) for user in user_table] +
        [EMPTY_LEAF for _ in range(tree_size - len(user_table))]
    )
    for i in range(tree_size - 1, 0, -1):
        tree[i] = combine_tree_nodes(tree[i*2], tree[i*2+1])
    return tree

# Root of a tree is stored at index 1 in the flattened form
def get_root(tree):
    return tree[1]

# Gets a proof for a node at a particular index
def get_proof(tree, index):
    branch_length = log2(len(tree)) - 1
    # ^ = bitwise xor, x ^ 1 = sister node of x
    index_in_tree = index + len(tree) // 2
    return [tree[(index_in_tree // 2**i) ^ 1] for i in range(branch_length)]

# Verifies a proof (duh)
def verify_proof(username, salt, balance, index, user_table_size, root, proof):
    leaf = userdata_to_leaf(username, salt, balance)
    branch_length = log2(get_next_power_of_2(user_table_size)) - 1
    for i in range(branch_length):
        if index & (2**i):
            leaf = combine_tree_nodes(proof[i], leaf)
        else:
            leaf = combine_tree_nodes(leaf, proof[i])
    return leaf == root

这种设计中的隐私泄漏比完全公开的列表要低得多,并且可以通过每次发布根时洗牌分支来进一步减少隐私泄漏,但仍然存在一些隐私泄漏:查理了解到有人的余额为 164 ETH,一些两个用户的余额加起来为 70 ETH, 等。控制许多帐户的攻击者仍可能了解有关交易所用户的大量信息。

该计划的一个重要微妙之处是余额的可能性:如果一个拥有 1390 ETH 客户余额但只有 890 ETH 储备的交易所试图通过在树中的某个虚假账户下添加 -500 ETH 余额来弥补差额怎么办?事实证明,这种可能性并没有破坏这个方案,尽管这就是为什么我们特别需要一个默克尔和树而不是一个常规的默克尔树的原因。假设亨利是交易所控制的假账户,交易所在那里放了-500 ETH:

 

 

Greta的证明验证将失败:交易所必须给她亨利的-500 ETH节点,她会因为无效而拒绝。夏娃和弗雷德的证明验证也会失败,因为亨利上面的中间节点总 ETH 为 -230,所以也是无效的!为了逃脱盗窃,交易所不得不希望整个树的右半部分没有人检查他们的余额证明。

如果交易所能够识别出价值 500 ETH 的用户,他们相信这些用户要么不会费心检查证据,要么在他们抱怨他们从未收到过证明时不会被相信,他们就可以逃脱盗窃。但是,交易所也可以将这些用户从树中排除并产生相同的效果。因此,默克尔树技术基本上与责任证明方案一样好,如果仅实现负债证明是目标。但它的隐私属性仍然不理想。你可以通过以更聪明的方式使用默克尔树走得更远,比如让每个聪或 wei 都是一片单独的叶子,但最终有了更现代的技术,就有更好的方法可以做到这一点。

使用 ZK-SNARK 提高隐私性和稳健性

ZK-SNARKs是一项强大的技术。ZK-SNARKs之于密码学,就像变压器之于人工智能:一种通用技术,它是如此强大,以至于它将完全解决几十年前开发的一大堆特定于应用程序的技术。因此,当然,我们可以使用ZK-SNARKs来大大简化和改善责任证明协议中的隐私。

我们可以做的最简单的事情是将所有用户的存款放入Merkle树(或者更简单的KZG承诺),并使用ZK-SNARK来证明树中的所有余额都是非负的,并加起来达到一些索赔价值。如果我们为隐私添加一层哈希,那么提供给每个用户的 Merkle 分支(或 KZG 证明)将不会透露任何其他用户的余额。

 

 

使用 KZG 承诺是避免隐私泄露的一种方法,因为不需要提供“姐妹节点”作为证明,并且可以使用简单的 ZK-SNARK 来证明余额的总和以及每个余额都是非负的。

 

我们可以用专用 ZK-SNARK 证明上述 KZG 中余额的总和和非负性。这是一个简单的示例方法。我们引入一个辅助多项式I(x),它“建立每个余额的位”(为了示例,我们假设余额低于215),其中每 16 个位置跟踪带有偏移量的运行总计,以便仅当实际总计与声明的总计匹配时,它的总和才为零。如果z是 128 阶的统一根,我们可以证明方程:

I(z16x)=0

I(z16 x+14)=P(ω2x+1)

I(z i)−2∗I(zi−1)∈{0,1} i f i mod 16∉{0,15}

I(z 16∗x+15)=I(z 16∗x−1)+I(z 16∗x+14)−t h e d e c l a r e d t o t al u s e r count

有效设置的第一个值I(x)将。。。0 0 0 00 0 0 00 0 1 25 10 20 -1650 0 0 00 0 0 00 1 3 612 25 50 -300

请参阅此处此处 在我关于 ZK-SNARKs 的帖子中,进一步解释如何将这样的方程转换为多项式检查,然后转换为 ZK-SNARK。这不是一个最佳协议,但它确实表明,如今这些类型的加密证明并不是那么诡异!

只需几个额外的方程,像这样的约束系统就可以适应更复杂的设置。例如,在杠杆交易系统中,个人用户有负余额是可以接受的,但前提是他们有足够的其他资产来支付具有一定抵押保证金的资金。SNARK可以用来证明这个更复杂的约束,让用户放心,交易所不会通过秘密地免除其他用户的规则来冒他们的资金风险。

从长远来看,这种ZK负债证明可能不仅可以用于客户在交易所的存款,还可以用于更广泛的贷款。任何借款的人都会将记录放入包含该贷款的多项式或树中,该结构的根将在链上发布。这将使任何寻求贷款的人ZK向贷方证明他们尚未获得太多其他贷款。最终,法律创新甚至可以使以这种方式承诺的贷款比没有承诺的贷款具有更高的优先级。这将我们引向与“去中心化社会:寻找 Web3 的灵魂”论文中讨论的一个想法完全相同的方向:通过某种形式的“灵魂绑定代币”在链上建立负面声誉或负担的一般概念。

资产证明

最简单的资产证明版本是我们上面看到的协议:为了证明您持有 X 币,您只需在某个预先约定的时间或在数据字段包含“这些资金属于币安”字样的交易中移动 X 币。为了避免支付交易费用,您可以签署链下消息;比特币以太坊都有链下签名消息的标准。

这种简单的资产证明技术有两个实际问题:

  • 处理冷库
  • 附带两用

出于安全原因,大多数交易所将绝大多数客户资金保存在“冷存储”中:在离线计算机上,交易需要手动签名并转移到互联网上。字面上的气隙很常见:我曾经用于个人资金的冷存储设置涉及一台永久离线的计算机,生成一个包含签名交易的二维码,我会从手机上扫描。由于风险很高,交易所使用的安全协议更加疯狂,并且通常涉及在多个设备之间使用多方计算,以进一步减少针对单个设备泄露密钥的黑客攻击的机会。鉴于这种设置,即使是一条额外的消息来证明对地址的控制也是一项昂贵的操作!

交易所可以采用以下几种途径:

  • 保留一些公开的长期使用地址。交易所将生成几个地址,发布每个地址的证明一次以证明所有权,然后重复使用这些地址。这是迄今为止最简单的选择,尽管它确实在如何保护安全和隐私方面增加了一些限制。
  • 有很多地址,随机证明几个。交易所将有许多地址,甚至可能只使用每个地址一次,并在单笔交易后将其停用。在这种情况下,交易所可能有一个协议,不时随机选择几个地址,并且必须“打开”以证明所有权。一些交易所已经与审计师做了这样的事情,但原则上这种技术可以变成一个完全自动化的程序。
  • 更复杂的 ZKP 选项。例如,交易所可以将其所有地址设置为1-of-2多重签名,其中一个密钥每个地址不同,另一个是某些“大”紧急备份密钥的盲版本,以某种复杂但非常高安全性的方式存储,例如。16 个多重签名中的 12 个。为了保护隐私并避免泄露其整个地址集,交易所甚至可以在区块链上运行零知识证明,证明链上具有这种格式的所有地址的总余额。

另一个主要问题是防范附带性双重用途。在彼此之间来回穿梭抵押品以证明准备金是交易所可以轻松做到的事情,并且允许它们假装有偿付能力,而实际上并非如此。理想情况下,偿付能力的证明将实时完成,并在每次区块后更新证明。如果这是不切实际的,下一个最好的办法是在不同的交易所之间按照固定的时间表进行协调,例如。每周二 1400 UTC 的探证储备。

最后一个问题是:你能用法定货币做资产证明吗?交易所不仅持有加密货币,还持有银行系统内的法定货币。在这里,答案是:是的,但这样的程序将不可避免地依赖于“法定”信托模型:银行本身可以证明余额,审计师可以证明资产负债表等。鉴于法定货币无法加密验证,这是在该框架内可以做的最好的事情,但它仍然值得做。

另一种方法是将一个运行交易所并处理 USDC 等资产支持的稳定币的实体与另一个处理在加密和传统银行系统之间移动的现金进出流程的实体(USDC 本身)完全分开。因为USDC的“负债”只是链上的ERC20代币,所以负债证明是“免费”的,只需要资产证明。

等离子体和验证:我们可以使CEXs成为非托管的吗?

假设我们想走得更远:我们不想只是证明交易所有资金来偿还用户。相反,我们希望防止交易所完全窃取用户的资金

这方面的第一个重大尝试是Plasma,这是一种在2017年和2018年在以太坊研究界流行的扩展解决方案。等离子体的工作原理是将余额分成一组单独的“硬币”,其中每个硬币都被分配一个索引,并位于等离子体块的默克尔树中的特定位置。进行硬币的有效转移需要将交易放入树的正确位置,其根在链上发布。

 

 

一个版本的 Plasma 的过度简化图。硬币保存在智能合约中,该合约在提款时执行 Plasma 协议的规则。

 

OmiseGo试图基于该协议进行去中心化交换,但从那时起,他们转向了其他想法 - 就此而言,Plasma Group本身也是如此,现在是乐观的EVM汇总项目Optimism

不值得一看 2018 年设想的 Plasma 的技术局限性(例如。证明硬币碎片整理)作为关于整个概念的某种道德故事。自 2018 年 Plasma 话语达到顶峰以来,ZK-SNARKs 对于与扩展相关的用例变得更加可行,正如我们上面所说,ZK-SNARKs 改变了一切。

Plasma思想的更现代版本是Starkware所说的validium:基本上与ZK汇总相同,除了数据保存在链下。这种结构可以用于许多用例,可以想象集中式服务器需要运行一些代码并证明它正确执行代码的任何事情。在验证中,运营商无法窃取资金,尽管根据实施的细节,如果操作员消失,一定数量的用户资金可能会卡住

这一切都非常好:CEX vs DEX 远非二进制,事实证明有各种各样的选择,包括各种形式的混合中心化,您可以在其中获得一些好处,例如效率,但仍然有很多加密护栏阻止中心化运营商参与大多数形式的滥用。

 

 

但值得讨论的是这个设计空间右半部分的根本问题:处理用户错误。到目前为止,最重要的错误类型是:如果用户忘记密码,丢失设备,被黑客入侵或以其他方式无法访问其帐户怎么办?

交易所可以解决这个问题:首先电子邮件恢复,即使失败了,通过KYC进行更复杂的恢复形式。但是为了能够解决这些问题,交易所需要真正控制硬币。为了能够出于充分的理由收回用户帐户的资金,交易所需要拥有也可用于出于不良原因窃取用户帐户资金的权力。这是一个不可避免的权衡。

理想的长期解决方案是依靠自我监护,在未来,用户可以轻松访问多重签名和社交恢复钱包等技术来帮助处理紧急情况。但在短期内,有两种明确的替代方案具有明显不同的成本和收益:

 

选择 交易所风险 用户端风险
监护交换(例如今天的币库) 如果交易所方面出现问题,用户资金可能会丢失 交换可以帮助恢复帐户
非监护交换(例如今日联借) 即使交易所恶意行为,用户也可以提款 如果用户搞砸了,用户资金可能会丢失

 

另一个重要问题是跨链支持:交易所需要支持许多不同的链,而像Plasma和validiums这样的系统需要用不同的语言编写代码来支持不同的平台,并且根本无法在其他平台(特别是比特币)上实现。从长远来看,这有望通过技术升级和标准化来解决;然而,在短期内,这是另一个支持托管交易所目前仍然是托管的论点。

结论:更好的交流的未来

在短期内,有两类明确的交易所:托管交易所和非托管交易所。今天,后一类只是像Uniswap这样的DEX,在未来,我们还可能看到加密“受限”的CEX,其中用户资金被保存在类似validium智能合约的东西中。我们还可能看到半托管交易所,我们信任他们与法定货币而不是加密货币。

这两种类型的交易所将继续存在,提高托管交易所安全性的最简单向后兼容方法是添加储备证明。这包括资产证明和负债证明的组合。为两者制定良好的协议存在技术挑战,但我们可以而且应该尽可能地在两者方面取得进展,并尽可能开源软件和流程,以便所有交易所都能受益。

从长远来看,我希望我们越来越接近所有非托管的交易所,至少在加密方面。钱包恢复将存在,并且可能需要为处理小额金额的新用户以及出于法律原因需要此类安排的机构提供高度集中的恢复选项,但这可以在钱包层而不是在交易所本身内完成。在法定货币方面,传统银行系统和加密生态系统之间的流动可以通过USDC等资产支持的稳定币原生的现金进出流程来完成。但是,我们还需要一段时间才能完全到达那里。