通过保护性资金池追踪不良资金
我想解释一个看似疯狂的想法,即通过一个受保护的资金池来追踪不良资金,同时尽可能地保护隐私。
剧情梗概
邪恶博士窃取了1000亿美元,并将其投入到一个隐私协议中。
不知何故,盗窃和存款事件竟然瞒过了一段时间,存款的延迟期限也已过去。钱已经进入系统。私下转账随即展开。没人能分辨真假钞票。时局艰难。
最终,有人发现了盗窃行为并拉响了警报。
用户如何识别余额中的受污染票据?我们如何最大限度地保护用户隐私?我们如何识别受污染的取款(以及是否应该识别)?
首要目标:使票据持有人能够确定其票据中有多少来自“不良”存款,而不会向任何人泄露任何其他信息。
超高水平
用户有一张便条。便条中包含用户无法解密的加密信息,内容是关于构成这张便条的存款信息。
notes[i][j] = {owner,amount,randomness,deposits: [{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},...],}一切都很舒适私密。
如果过一段时间后,某笔存款被列入黑名单,而这张票据又是该笔“不良”存款的后续存款,那么用户就会突然得知这一信息:
notes[i][j] = {owner,amount,randomness,deposits: [{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},{The bad deposit_id,Some fraction to deduce how much of that 'bad' depositis contained within this note,},{ < unintelligible ciphertext > , < unintelligible ciphertext > ,},...],}然后,用户可以执行一个名称颇为引人注目的“净化电路”,以创建一个不包含此错误 deposit_id 的新票据。
注意事项
目前还没有人读过这篇文章。
我并不是真的提倡这种方法,但这确实是一个有趣的设计,它试图解决一个我以前从未见过有人解决的问题。
我可以想到一些强烈的批评意见,这些批评意见贯穿了整部纪录片(并在结尾处进行了总结)。
它或许可以作为后世的参考资料。
希望您会觉得这本书有趣。 
引言
如果你想对这个主题有一个非常精彩的介绍,请阅读Privacy Pools 论文的第一部分和第二部分。
我的总结如下:
有些人希望在区块链上进行交易时注重隐私,因此各个团队正在探索如何实现私密区块链交易。一种常见的模式如下:
用户可以将代币存入所谓的“屏蔽代币池”。一段时间后,他们可以将代币提取到另一个地址。此类协议旨在打破存款地址和取款地址之间的可观测联系。
有些协议还支持对存入资金池的代币进行私密转账,这样一来,旁观者就无法推断出是谁向谁发送了什么。我对这些充值-转账-提现协议最感兴趣。
所有这些资金池都存在一个问题,那就是不法分子也可以利用它们,事实上,已经发生过不法分子将非法所得资金存入此类资金池的案例。一旦资金存入,追踪非法存款就变得更加困难,区分合法提款和非法提款也变得更加困难。
因此,各种项目都试图解决这个问题。
方法包括:
- 通过引入存款延迟机制,在不良存款进入资金池之前将其识别出来。
- 限制在一定时期内可以存入资金池的金额。
- 只接受信誉良好的演员的存款。
- 允许用户提供与不良行为者无关的证明,或仅与良好行为者相关的证明。
- 将每笔交易的详细信息加密后提供给第三方(例如监管机构、审计师或会计师),或者与第三方共享查看密钥。
- 放宽部分隐私保障,以便对资金池中的所有资金进行一定程度的追踪。
这些方法都有各自的优缺点,我们将在下文中讨论。
我一直在琢磨的想法如下:
当向用户发送一条消息时,该消息将包含一个重新随机加密的列表,其中包含所有对该消息做出任何贡献的上游 deposit_ids 的值,以及每个存款对该消息贡献了多少价值的加密值。
在理想情况下,用户的笔记不会是任何“不良”存款的后代,在这种情况下,他们的隐私应该与 Zcash 相当:用户将无法知道哪些存款促成了他们的笔记。
然而,如果一笔存款被公开认定为“不良存款”,则该设计允许所有下游票据持有人了解有多少不良存款流入了他们的票据。至关重要的是,除了“我的票据中有 X 笔款项被认为源自这笔不良存款”之外,不会向票据持有人泄露任何其他信息。资金池的观察者不应获知任何信息。
理想情况下,不良存款会在进入系统之前就被识别并标记为“不良”,例如通过一定的延迟期。这样,它就不会有机会“侵蚀”诚实用户的资金。但如果延迟时间不够长呢?或者如果尽职调查有所遗漏呢?我认为,探讨如何在不泄露用户交易记录的情况下,识别那些侥幸通过存款延迟机制的不良资金会很有意思。
补充说明:Railgun 最近确实遇到了这个问题: https://x.com/dethective/status/1994397800847589736?s =20
我们在此过程中会遇到一些可扩展性问题,并探讨如何缓解这些问题。
背景
这里快速回顾一下基于 utxo 的隐私协议,以便统一术语。
如果您愿意,可以跳到下一节“现有技术”。
存款
从概念上讲,存款看起来大致如下:
用户向系统中存入一些公共代币,系统会创建一个包含存入数量和所有者(地址或公钥)信息的凭证。该凭证代表用户对这些底层代币的所有权,这些代币由系统持有在一个资金池中,直到最终所有者选择提取或转移部分余额。
如果您想了解更多细节,这里有一张更详细的图表,展示了存款流程。不同的实现方式在很多细节上有所不同,但核心在于此。
转账
从概念上讲,转账过程大致如下:
发送方花费(或“作废”)两张票据,为转账接收方创建一张票据,并为自己创建一张找零票据。两张票据被销毁,两个作废票据被发出,同时创建并发出两张新票据。
我敢不解释什么是无效化项吗?我想我可以!
啊,实际上,我需要就无效化符说以下几点,因为这将有助于我在下一节中对一种方法进行批判:
在私密支付协议中,使用无效化符来断开创建票据的交易与之后花费该票据的交易之间的关联。如果没有无效化符提供的这种“交易不可关联性”,整个交易图对观察者都是可见的,用户可以获取不应泄露的信息。
本文档的其余部分将忽略电路图中的无效化符,因为它们只会造成混乱。如果您看到某个音符被输入到电路中,请假定它已被无效化。
另外,为了减少图表中的混乱,我将忽略图表中的变更说明:
已简化:不显示无效符;不显示变更说明。
提款
从概念上讲,提款过程大致如下:
用户烧毁一张包含一定金额的纸币,系统(负责保管所有公开存入的资金,用户在系统内进行私密交易)会将该金额转移到用户指定的地址。
现有技术
按照论文的惯例,在深入探讨新思想的细节之前,我们必须对所有现有思想进行无情的批判,以证明这份文件的存在是合理的。
但我会温柔对待你,因为:
- 现有的想法其实相当不错,而且已经转化为具有实际性能指标(PMF)的实际产品。这太棒了。
- 你永远无法预知将来是否会和某人共事;
- 我的想法可能存在缺陷;
- 即使这个想法没有缺陷,我也可以想到一些尖锐的批评意见,我会在讨论过程中一一提出,而这些批评意见你可能已经在考虑了。
留在我身边。
所有产品都寻求某种形式的合规私有代币。有些是简单的充值提现方案,有些是充值转账提现方案。它们本质上都遵循“背景”部分概述的方法,但在应对恶意行为者方面采取了不同的策略。
佩伊
当你口头说出这个产品的名字时,你必须说“Pay with two y”,否则别人会一脸茫然。或者你必须说“Payyyyyyy”,并且要非常强调发音。
Payy 是一种存款-转账-取款协议。
Payy采取了一种非常务实和简单的方法来应对不良行为者:他们从协议中移除了无效化器:
回顾屏蔽协议的基本原理,无效化器提供了“交易不可链接性”。如果没有无效化器,转账交易的输入备注将公开可见。也就是说,每笔交易的输出备注都可能被其他交易的输入备注所取代。这意味着在 Payy 上,整个交易图都是可观测的。
如果系统中出现一笔不良存款,您可以精确追踪这笔资金的流向,并查看系统中哪些提款源于这笔不良存款。票据的接收者可以立即识别出该票据源于一笔不良存款,尽管他们无法得知该不良存款中有多少资金最终流入了他们的票据。
所以,Payy牺牲了一些隐私来追踪不法分子。我们来谈谈这种隐私牺牲的程度。
我能想到的,Payy 的设计会带来以下几项隐私泄露问题:
存款后的第一笔转账可以被公开识别为来自存款人本人。也就是说,汇款人身份会被泄露。
提款前的最后一笔转账是公开可识别的,收款人信息会被泄露。
如果 A 存款,则 A → B(即“A 转账给 B”),然后 B 取款,那么外界就能看到 A 与 B 进行了交易。如果该子图中没有其他交易,外界甚至可以得知 A 转账给 B 的确切金额。
如果 E → B → C → E,那么 E 就会得知 B 和 C 有过交易。E 还会得知 B认识C。这泄露的信息量相当大。这种程度的信息泄露,恐怕连婚外情都会曝光。
如果E是一个大型实体,例如交易所,他们或许能够从这种泄露的信息中推断出大量的交易图谱。他们可以拼凑出参与者之间的关系,并有可能推断出参与者之间的支付金额。
这些是我思考五分钟后发现的一些蛛丝马迹。一些技术高超的团队或许能推断出更多。
电磁炮
Railgun是一个存款-转账-取款协议。非常棒。
所有存款进入系统都有一小时的延迟,以便拒绝“不良”存款。
一旦资金进入系统,用户之间就可以进行私密转账。
问题在于——这也是促使我深入研究并撰写这篇文章的主要原因——一个小时的时间并不长,理论上来说,不法分子有可能“钻空子”,将“赃款”存入资金池。一旦资金进入资金池,就很难再抓到他们了。
写完这些之后补充:事情真的发生了!!!
https://x.com/dethective/status/1994397800847589736?s=20
如果我能在事情发生之前完成这份纪录片就好了。那样我就会被认为很有先见之明了。
总之,我着手解决通过资金池追踪“不良”资金的问题,该资金池支持私密转账,即使我们在资金进入资金池时并不知道这些资金是否“不良”,同时尽可能地保护所有用户的隐私。在介绍完其他几个项目之后,我会更详细地说明具体要求。
EY区块链
早在 2019 年,我所在的那个小型研究团队就探索了这个问题:如何设计一个隐私池,既能追踪不良存款,又能确保良好的隐私。
我记得有一段时间,我们走上了一条显而易见——现在也众所周知——的死路:给每张新存入的票据都分配一个 deposit_id。这样一来,每次转账,输出票据就会包含所有输入票据的 deposit_id 的并集。这会导致列表呈指数级增长,每次转账后列表大小都会翻倍。这种方法不可持续。我想我们当时并没有公开讨论过这个问题。
实际上,我在2019年做过一些公开演讲,解决了通过一个受保护的NFT池追踪被盗NFT这个相对容易的问题。NFT交易的非同质性意味着不会出现列表呈指数级增长的问题。
那支小团队做了很多超前于时代的研究,但我们没有发表那些不成熟的想法,现在回想起来真是可惜。
但是……请参阅本文档后面的内容,我们将尝试通过偶尔启动(重置)该列表来解决该问题。
任何追踪存款ID的方法都普遍受到批评,因为这样一来,纸币就失去了可替代性:存款ID变成了纸币上的标记。人们能够从他们发送和接收的纸币中看出规律。
一个简单的泄露路径是 Eve->Bob->Eve,或者 Eve->Bob->Charlie->Eve,甚至是 Eve->…->Eve。Eve 能够发现她发送和接收的笔记中共同的 deposit_id,并可能从中推断出信息。例如,在 E->B->C->E 的情况下,Eve 可以推断出 Bob 认识 Charlie,并且 Bob 给 Charlie 汇过款。
这份文档试图通过每次转账时重新随机生成 deposit_ids 列表来解决 deposit_ids 作为标记的问题。这样一来,Eve 就无法识别她发送的笔记和她之后收到的笔记之间的任何共同点。
隐私泳池
Privacy Pools 产品是一种存款取款协议。
我的第一个批评是“它不支持转账”,但 Privacy Pools 的论文确实讨论了一种可以实现私密转账的方法。
在与开发者大会上交流后更新:隐私池 v2 版本似乎将支持私密转账,但这与隐私池论文中解释的方法有所不同。以下是我对 v2 版本功能的理解:
每次存款,生成的票据都会包含一个 deposit_id。转账可以在系统内进行,但所有输入的票据必须具有相同的 deposit_id ,并且所有输出的票据都会被赋予相同的 deposit_id。
这无疑是避免“列表呈指数级增长”问题的巧妙方法:一条笔记始终包含 1 个 deposit_id。
这种方法可能存在以下问题。如果用户需要发送 100 ETH,他们需要两张 deposit_id 相同的票据,且两张票据的金额总和必须大于等于 100。但如果他们没有两张 deposit_id 相同的票据怎么办?他们需要发送多笔交易,每张票据对应一个不同的 deposit_id。
我怀疑,随着存款活动的增加,用户收到包含相同 deposit_id 的票据的情况会变得非常罕见。如果真是如此,用户将两张票据合并的情况也会变得非常罕见(因为它们必须具有相同的 deposit_id)。而且我怀疑,这可能会导致系统趋于“臃肿”,票据中的金额会变得非常小且不实用,因为“拆分”操作的次数将远远超过“合并”操作的次数。
或许用户可以互动:潜在的收款人可以向发件人请求包含特定 deposit_id 的便条;有点像纸牌游戏“钓鱼”:“有 deposit_id = 1234 的吗?”
本文提出的方案旨在实现 deposit_ids 的混合使用。
对 v2 版本方案的另一个批评是——正如前一小节所述——deposit_ids 作为标记会破坏资金的可互换性。本文档旨在通过在每次转账时重新随机化 deposit_ids 来解决这个问题。
我对隐私资金池的第二个(也是较弱的)批评是,资金存入池子前存在一段不确定的延迟。也许这没什么问题,但因为我想探究的是,如果不良资金进入资金池会发生什么,我不希望答案是“只需等待更长时间进行尽职调查”。我认为尽职调查过程可能会出错,因此我们希望能够追溯性地将不良资金标记为不良资金,即使它们是被意外放入的。
隐私池论文中描述的方法如下:
一种批评意见认为,这种模式存在过多的信息泄露;而这些信息通常是受保护资金池所希望保密的。在存款最终被列入白名单之前,用户之间会共享大量私人数据。
空白方格
你可能不相信,但我早在九十月份就开始构思这篇文章了。只是后来忙于其他事情,比如我的日常工作、愉快的家庭旅行以及开发者大会。
然后,在11月下旬的某个星期六,我作为伴侣参加婚礼时,在一个Signal群组里收到了这个链接:
它建议对 deposit_ids 进行加密——这个想法也出现在这份文档中。英雄所见略同!
总之,我没有抄袭;它们是对同一事物的独立诠释。我有带时间戳的收据!
这份文档对这个观点进行了更深入的阐述,希望您会觉得有趣。它还反驳了对这个观点的一些批评,我们接下来会谈到这些批评。
简而言之,其思路是让某个实体为系统中的每一笔存款生成一个新的密钥对。然后,每个 deposit_id 都可以用其对应的公钥进行加密。之后,任何相应的提款都会被强制发送相同的加密 deposit_id(该加密后的 deposit_id 会进行加盐处理,使提款信号看起来与存款无关)。在理想情况下,观察者永远不会知道提款与存款有关。但如果之后某笔存款被判定为“不良”,持有解密密钥的人就可以泄露私钥,这将使所有观察者都能解密存款和提款交易的加密 deposit_id,并最终将提款与存款关联起来。
这种方法存在的问题:
解密密钥的持有者是系统的一个故障点。如果密钥泄露(无论是有意还是无意),那么任何人都能关联所有用户的存款和取款记录。有人建议使用TEE(目标执行环境)或通过MPC(多方计算)节点集合来秘密共享解密密钥。当然,秘密共享解密密钥(以及其他秘密)是FHE(全同态加密)和CoSnark(CoSnark加密)中常见的做法,因此有些人可能认为这种做法是可以接受的。
维塔利克显然不喜欢“剥夺隐私”这种说法:
我在这篇文章中提出的方法仍然需要有争议的 MPC 集体来生成密钥,但我试图大大减少最坏情况下所有解密密钥泄露造成的损失。
采用 Blanksquare 方法:
- 最糟糕的情况下,会泄露什么内容?
- 每一笔提款都与每一笔存款相关联。
- 泄露给了谁?
- 全世界每个人(每个区块链观察者)。
本文档中介绍的方法:
- 最糟糕的情况下,会泄露什么内容?
- 为特定用户的笔记做出贡献的 deposit_ids。
- 每个 deposit_id 对该票据“做出贡献”的金额。
- 泄露给了谁?
- 仅限纸条持有人本人。
泄漏点位于局部地区。
还不错,对吧?
至少我们还没有剥夺隐私到让维塔利克伤心的地步(我希望如此)。
最坏的情况下,本文档中的协议会简化为票据所有者可以看到所有参与其票据的 deposit_ids。这并非理想方案(参见前面章节,其中抱怨 deposit_ids 会成为票据上不可互换的“标记”),但我觉得这是一个值得向大家介绍的进步。
在理想情况下,隐私其实相当不错。
这个想法
最后我们可以讨论这个想法了。
要求
它需要哪些功能?
- 支持私密转账的保护资金池。
- 在正常情况下,存款 ID(在票据中)是随机化的,以防止高级票据持有者将其用作“标记”来发现活动模式。
- 如果意外地允许不良存款进入资金池,之后可以将其标记为“不良”,资金池的所有用户都可以识别:他们的票据是否包含任何“不良”价值,以及他们的票据价值中有多少被认为是“不良”的。
- 即使在最坏的情况下(即该协议的解密密钥泄露),区块链观察者也无法得知哪些票据包含“坏”值。
- 避免对历史提款进行追溯性隐私泄露:即使在系统解密密钥泄露的最坏情况下,该系统也可以设计成不会泄露已发生的提款信息。
- 在进行转账或取款时,用户可以证明其输入的票据并非源自“不良”存款。
- 如果用户的笔记源自不良存款,用户可以通过将笔记拆分为“好”笔记和“坏”笔记来“净化”笔记。
- 我不保证约束数量。

高级
接下来是低层部分。
其基础是一个简单的存款-转账-取款协议。
某个黑名单维护者维护着一份不良 deposit_ids 黑名单。
存款进入系统可能需要一段时间,但我们感兴趣的是能够捕捉到那些错过这段延迟期的存款的机制。
在一个庞大的预处理步骤中,生成了一个很长的 deposit_id 密钥对列表:每个密钥对对应协议中未来的每一次存款。
[(x_1, Y_1), (x_2, Y_2), ..., (x_{1000000}, Y_{1000000})] [ ( x 1 , Y 1 ) , ( x 2 , Y 2 ) , . 。 。 , ( x 1000000 , Y 1000000 ) ]
其中x_i是私钥, Y_i是公钥。
清单内容不足时,可以定期补充。
这份名单可能由单个参与者生成,但你大概不太喜欢这种说法,所以我们假设这份名单是由一个大型MPC(多方控制)组织生成的。我们称他们为密钥持有者。密钥持有者可能与黑名单维护者是同一人,也可能是不同的实体。我们稍后会再讨论这个令人担忧的问题。
当进行新的存款i i时,它将与公钥Y_i Y i公开关联。
预先生成的列表使用户能够以非交互方式向协议中存入密钥,而无需等待密钥持有者生成和发布新的公钥。
我们将使用此公钥Y_i Y i对信息进行加密。具体来说,此存款“下游”的每一张票据都将包含:
- deposit_id i i的加密,加密为Y_i Y i ;
- 对用于支付此票据的存款比例进行加密,加密为Y_i Y i 。
我们将在后面的章节中慢慢讲解技术细节。
由于该系统支持转账,一张票据可能源自多笔存款。这没关系:票据将包含所有上游存款的列表。细心的读者会注意到,这样的列表会呈指数级增长,而且是不可持续的——每次转账都会使列表翻倍。我们稍后会再讨论这个问题。
正常情况下,下游票据的持有者无法解密其票据中包含的加密存款信息,因为没有人(希望甚至连密钥持有者集体也不知道)知道解密密钥x_i ,因此他们无法得知哪些存款流入了他们的票据。该系统的目标是尽可能地实现接近 Zcash 的隐私性,即观察者无法了解资金池内部的运作情况,交易图不可见,价值可互换,票据持有者也无法追踪其票据的来源。
如果存款i i之后被判定为“不良”,则黑名单维护者会在链上标记存款i i 。一旦i i被列入黑名单,密钥持有者(们)就需要披露对应的链上私钥x_i x i 。如果密钥持有者是一个 MPC 集体,他们需要共同协作才能了解x_i x i 的实际内容,例如通过某种阈值机制。
此次公布i i和x_i x i将引发一系列活动:整个系统中所有纸币持有者都可以尝试解密其纸币中包含的所有加密 deposit_ids。如果他们的纸币是deposit i i的后代,那么他们将成功解密其纸币的某个加密 deposit_id 字段,从而得到i i 。
假设他们的票据确实包含来自存款i 的“不良”资金。糟糕!为了了解具体金额,还可以使用已揭示的x_i x i来解密票据中相应的“加密部分”。这将揭示票据中包含的原始存款的百分比。
一种名为“净化电路”的全新功能,将使用户能够从票据中“剔除”不良资金。
需要注意的是,票据持有人将无法解密票据中任何其他加密的 deposit_id 字段。票据持有人唯一能获取到的信息是:
“我的票据是存款i i的‘下游’;协议认为我的票据包含来自该不良存款的 X 金额”。
或者更简洁地说:
“我的票据中有 X 金额被认为是源自不良存款i i ”。
票据持有人不会了解到票据的任何其他来源信息,包括票据价值在到达他们手中之前经过了哪些人。
外部观察者不会知道哪些音符被污染了,也不会了解有关 tx 图的有意义的信息。
此时此刻,你脑海中可能已经涌现出许多尖锐的批评意见。我们不妨列举一些:
- 如果密钥持有者串通起来,他们就能知道x_i x i的值。
- 黑名单维护者可能会心怀恶意,通过将人们添加到黑名单中来审查他们。
- 一旦i i被列入黑名单,密钥持有者可能不会履行其披露x_i x i的职责。
- 对于诚实的用户来说,收到一张无法查看哪些存款构成了这张票据的通知,之后却发现(在被列入黑名单后)这张票据实际上被一些“不良”价值污染了,这似乎很不公平。
- 该协议如何确定每张钞票中存款的比例?
- 如果你追踪存款清单,那么每次“转账”交易都会使存款金额翻倍。这不可持续。
我们稍后会详细讨论这些问题。
步步
设置
在一个庞大的预处理步骤中,生成了一个很长的 deposit_id 密钥对列表:每个密钥对对应协议中未来的每一次存款。
[(x_1, Y_1), (x_2, Y_2), ..., (x_{1000000}, Y_{1000000})] [ ( x 1 , Y 1 ) , ( x 2 , Y 2 ) , . 。 。 , ( x 1000000 , Y 1000000 ) ]
其中x_i是私钥, Y_i是公钥。
清单内容不足时,可以定期补充。
稍微正式一点(但符号仍然比较随意):
G \in \mathbb{G} G ∈ G是生成点。
x_i \in \mathbb{F} x i ∈ F是 deposit_id i i的密钥,在群\mathbb{G} G的标量域中。
Y_i := x_i \cdot G Y i : = x i ⋅ G deposit_id i i对应的公钥。
订金
用户存入一定金额作为存款i i的一部分。该存款与公钥Y_i Y i相关联。
系统会创建一个新笔记,其中包含以下数据:
notes[i][1] = {owner,amount,randomness,deposits: [{encrypted_deposit_id = "i" ,encrypted_fraction = "1" ,},{},...{}],}在哪里:
我们有时会使用符号"x"来表示“值x的加密”。
encrypted_deposit_id = "i" = \text{enc}_{Y_i}(i) enc Y i ( i ) ,是 deposit_id i i的加密,使用公钥Y_i Y i进行加密。
该加密方案必须支持密文的重新随机化。Elgamal 是一个不错的选择(参见附录)。
encrypted_fraction = "1" = \text{enc}_{Y_i}(1) enc Y i ( 1 ) ,表示流入此票据的存款部分的加密,最初是1 1的加密。
这种加密方案必须支持密文与已知标量进行标量乘法运算。稍后我们会解释原因。Paillier 加密是一个不错的选择,但在 Snark 电路中效率很低(参见附录)。
图表时间到!
enc_dep_id_i_j是存款i经过jth跳转(转账)后的“加密 deposit_id”。
enc_frac_i_j是存款i的价值中对本票据做出贡献的“加密分数”(从存款经过jth转账后)。
用户实际上看不到j;这只是为了帮助图表阅读者。
Paillier 加密并非一成不变;显然,对于密文与已知标量的乘法运算,还有效率更高的替代方案。
请注意,我使用引号是为了便于阅读和偷懒:"x"表示“x的加密”。
转移
例子
私人转账会销毁两条备注,并为收款人创建一条备注,为付款人创建一条找零备注。(为简洁起见,我们忽略找零备注。)
假设我们最初存入 5 个 ETH 的存款人想要向某人发送 1.5 个 ETH。
假设他们除了5 ETH纸币外,还有一张10 ETH纸币。
他们可以作废这两张价值共计 15 ETH 的票据,并创建一张价值 1.5 ETH 的新票据。
5 ETH 的存款中有多少最终进入了新的 1.5 ETH 纸币中?10 ETH 的存款中又有多少最终进入了新的 1.5 ETH 纸币中?
考虑到货币的流动性,实际上很难说一笔存款对后续票据的贡献有多大。这就像试图弄清楚一勺汤里有多少番茄一样。
但是,我们可以采取一种数学上自然的方法,那就是将贡献视为与输入的音符成比例。
在我们的例子中,我们可以认为5 * \frac{1.5}{15} = 0.5 5 ∗ 1.5 15 = 0.5来自 5 ETH 存款, 10 * \frac{1.5}{15} = 1 10 ∗ 1.5 15 = 1来自 10 ETH 的存款。换句话说,我们可以应用\frac{15}{150} 15 150的一部分。 对两个输入存款。
理论上,用户可以对捐款进行不同的分配:例如,将某个 deposit_id 的所有金额分配到自己的找零中,而完全不分配到预期收款人的找零中。但由于我们的协议会隐藏每张找零中包含哪些 deposit_id(如果 deposit_id 是“有效的”),因此这样做实际上毫无意义;用户甚至不知道自己分配了哪些 deposit_id!而且,如果检测到某个 deposit_id 是“无效的”,还有一个独立的去污电路可以将其分离出来。
此图说明了如何将分数应用于构成特定票据的 deposit_ids:
请注意,我使用引号是为了便于阅读和偷懒:
"x"表示“x的加密”。
我们可以考虑后续的转账,这次转账的输入是这张面值 1.5 ETH 的纸币。假设我们要转账 20 ETH,而另一张输入的纸币价值 100 ETH。
接下来,适用于所有为 20 ETH 输出票据做出贡献的存款的下一个分数是: \frac{200}{1015} 200 1015 。
图表中的符号总是很繁琐。
你可以看到我在那张价值 100 ETH 的纸条里偷懒用了些符号。例如,
enc_frac_3_j = "prod(f_3_j)"表示“对用于生成这张纸条的存款 3 的那部分进行加密”。我们姑且认为,从存款 3 到生成这张纸条,一共进行了j转账。
如果你查看 20 ETH 的票据,它应该会开始显示我们正在跟踪的存款数据模式。
正在追踪的 5 ETH 存款数据如下:
deposits: [(enc_dep_id = "1" enc_frac_1_3 = "1 * (15/150) * (200/1015)" ,),现在,这张 20 ETH 纸币的持有者实际上无法解密这些密文enc_dep_id_1_3和enc_frac_1_3 ,因为他们不知道解密密钥x_1 x 1 。
但如果黑名单维护者突然将存款1 1列入黑名单,并且密钥持有者履行了其职责,导出并发布了x_1 x 1 ,那么这张 20 ETH 纸币的持有者将能够解密密文enc_dep_id_1_3和enc_frac_1_3 。(注意:他们仍然无法解密存储在纸币中的其他密文,因为每个密文都使用不同的公钥加密。)
笔记持有者会发现:“哦,我可以用新发布的x_1 x 1解密enc_dep_id_1_3 ,得到1 1 ,这意味着我的笔记被存款1 1中的某些值‘污染’了。我现在解密enc_frac_1_3 ,得到分数1 * (15/150) * (200/1015) 。我知道存款 1 的金额是多少(因为存款金额是公开的):是 5 ETH。所以我计算5 * (1 * (15/150) * (200/1015)) = 0.098522 ETH 。因此,协议认为我的笔记中有0.098522 ETH来自‘不良’存款1 1。 ”
先别管你脑子里那些批评的声音:这很酷,对吧?网络中数百张票据里可以存在大量乱码、难以理解的加密数据,而不会泄露交易图,然后突然间,一笔存款被列入黑名单,却能揭示出一小部分有用的信息,而且只有下游票据的持有者才能看到。
帕利耶
我们可以使用 Paillier 加密对分数进行加密,这样我们就可以在每次未来的转账交易中将生成的密文重复乘以一个新的分数,而无需知道生成的分数是多少。
也就是说,如果我给你enc(m) e n c ( m ) ,你可以计算出已知标量k k 的enc(km) e n c ( k m ) ,而无需知道m m是什么。
请参阅附录,其中对 Paillier 加密进行了详尽的解释。即使不考虑本文档的其他内容,它本身也非常出色。显然,使用全同态加密方案在 snark 中实现这种“标量乘法”特性还有更高效的方法。感兴趣的读者可以探索这些方法。 
回顾我们之前的例子,我们从以下几点开始:
enc( 1 ) enc ( 1 )
下一个传输电路将此密文与一个已知的标量15/150进行标量乘法运算(表示为四舍五入到小数点后 6 位的整数,即15/150 * 10^6 = 100,000 ) 。如果使用Paillier算法,则运算结果为enc(1)^{100000} = enc(1 * 100000) ,即enc ( 1 ) 100000 = enc ( 1 * 100000 ) 。纸条的接收者不会知道哪些 deposit_id 流入了纸条,也不会知道每个 deposit_id 分别贡献了多少比例的金额:他们只会看到新计算出的密文。
下一个传输电路将此密文与已知标量200/1015进行标量乘法运算。200 / 1015 (表示为四舍五入到小数点后 6 位的整数,如200/1015 * 10^6 = 197,044 ) 。如果使用Paillier算法,则运算为enc(1 * 100000)^{197044} = enc(1 * 100000 * 197044 ) enc ( 1 * 100000 ) 197044 = enc ( 1 * 100000 * 197044 ) 。
解密后,真实分数可推导为1 * 100000 * 197044 / (10^6)^2 = 0.0197044 1 ∗ 100000 ∗ 197044 / ( 10 6 ) 2 = 0.0197044
如果解密密钥被泄露,用户将能够解密这个分数并将其应用于原始存款金额(在我们的示例中为 5 ETH),从而发现他们的票据被认为包含0.0197044 * 5 = 0.098522 0.0197044 ∗ 5 = 0.098522 ETH。
关于示例的一些评论
在我们继续之前,先提几点。
分数 → 小数
在图表中,我们追踪的是加密分数,但实际上我们并不想这样做,原因有二:
对分数进行加密,并迭代地乘以新的分数,需要分别对分子和分母进行加密(至少在我熟悉的加密方案中是这样),这浪费了约束条件。
分数的分子和分母是会漏分的:
分子可以分解,从而推断出自存款以来每次“转账”交易可能转移的金额。分母可以分解,从而推断出自存款以来每位中间票据持有人所持有的票据规模。
相反,我们可以将每个分数转换为小数,然后(至关重要且必要地)将其四舍五入到一定的小数位数。四舍五入会减少通过因式分解可以推断出的值(尤其是在用户选择转账金额时,因为由此产生的分数确实需要四舍五入)。
为什么要对 deposit_ids 进行加密?
假设 Eve 向 Bob 发送消息,Bob 又向 Eve 发送消息(E → B → E)。如果 deposit_ids 没有加密,那么 Eve 就会注意到她发送的消息和她收到的消息之间存在相同的 deposit_ids。
更糟糕的是,假设Eve给Bob汇款,Bob再汇给Charlie,Charlie再汇给Eve(E → B → C → E)。如果deposit_ids没有加密,Eve就会注意到她发给Bob的那张纸条和Charlie发给她的那张纸条的deposit_ids是相同的。Eve可能会推断出“Bob认识Charlie”和“Bob给Charlie汇过款”之类的信息,甚至可能推断出他们彼此汇款的金额。这种信息泄露可以通过重新加密deposit_ids来避免。
但是,如果加密的 deposit_ids 在转账之间没有改变,那么这些密文实际上就成了存款的替代标识符。
这就是为什么我们每次转账都会重新随机化加密后的 deposit_ids。这样,每张票据中的 deposit_ids 列表都是一个不同的、看似随机的数字列表!这也是为什么 Elgamal 是一种不错的加密方案选择。请参阅 Appx 文件,了解 Elgamal 和重新随机化的工作原理。
为什么要对分数(或小数)进行加密?
这与 deposit_ids 加密的原因类似。
在 tx 图中多次出现的实体可能会发现其笔记中包含的分数模式,并将 tx 图拼凑起来,或者做出类似于上述部分的推断。
糟糕的是,在这个例子中,用户损失了 0.098522 ETH。
在这个例子中,用户收到一张纸币后,其中 0.098522 ETH 的部分被判定为“无效”。该用户诚实守信,在收到纸币时并不知道它“无效”。
理想情况下,黑名单的建立应该在系统进行任何转账之前完成,例如在存款延迟期间。在这种情况下,所有用户在任何转账发生之前就已经知道恶意存款的密钥x_i 。如果恶意用户试图将资金转给诚实用户,该用户能够立即识别出来。
此外,还有一个有趣的值得探讨的问题,那就是在现实世界的类似情况下应该发生什么。













