讨论 区块链 互联网的状态根(State Root of the Internet)

互联网的状态根(State Root of the Internet)

Joe 发表于    阅读:45    回复:0

引言

数据如何获得其意义,决定了互联网的结构。数据本质上是无限可构造的——你可以随意编造一些数据。例如,“123aiab” 就是一个例子;另一个例子是“Barry 在 Twitter 上有 10 亿粉丝”。但真正让数据具有意义的是它的来源。如果 Twitter 本身就我在 Twitter 上的粉丝数量发表声明,那这条数据就远比别人说的更有意义。

“数据渴望自由,但也渴望昂贵。”

TLS:原罪
TLS 是一种加密协议,最初在 eBay 早期开发,主要用于在加密连接上传输信用卡号。此后,它成为浏览器默认的加密方式。通过 HTTPS 连接获取的任何内容都使用了 TLS。

一个关键的设计决策是:TLS 没有私钥。它使用一个共享密钥(shared key),这意味着用户可以假装某些数据来自服务器——他们对数据进行签名后,看起来就跟服务器签的一模一样。

这将所有交互定义为“用户与服务器”之间的关系。任何超出这一关系的通信都被视为无意义。

如何使数据可归因(attributable)
一种方法是使用 TLS 公证(TLS notary):通过多方计算(MPC)来共同计算共享密钥,使得该密钥由服务器、用户和公证方(notary)三方共同持有。这样,如果某段数据不是由公证方签署的,那么公证方就能判断出这段数据是否真的来自服务器,因为用户无法在没有公证方协助的情况下完成签名。

这种方法的缺点是:在 MPC 中运行 TLS 非常昂贵(消耗大量带宽、时间和计算复杂度)。一个更简单的方案是:让公证方独自持有完整的共享密钥,而用户完全不持有。在这种情况下,只要公证方自己没有签署某段数据,它就能判断该数据是否由服务器签署。

但这个简化方案的重大缺陷是:所有请求的数据都会泄露给公证方。例如,在我们关心的用例中,各种 Web API 的 API 密钥会被公证方知晓。不过这似乎可以接受,因为这些 API 密钥可以被限流,并在使用后轻松作废。优点在于无需复杂的 MPC 协议——所有操作都可以在加密之外完成,因此不仅可以公证文本,还能公证图片、视频等内容。

数据的可移植性
现在,我可以访问公开的 API,并让服务器对返回的数据进行公证。这样一来,只需一个人从《EVE Online》服务器获取数据并完成公证,其他人就可以通过 P2P 方式共享这些已公证的副本。这极大地降低了 API 的服务成本,也使 API 更具可扩展性和可信度。

这也改变了互联网的拓扑结构:允许构建更复杂的应用,并消除了频繁调用 API 的成本——用户可以直接下载经过身份验证的 P2P 数据。

结论
这是一种让 Web2 数据具备可移植性的简单方法。

虽然它并非传统意义上的“状态根”(即通过默克尔树对数据进行哈希),但在精神上是一致的:它代表一组带有正确性保证的数据。在当前的设计中,这种保证依赖于一个假设——公证方是诚实的。

这项工作的最终目标是让用户或服务器直接对数据签名,从而完成公证。目前尚未实现这一点,因此我们先迈出这一步,为后续组件打下基础。

另一种方案是使用 IO(Input/Output Oracle)作为公证方,这样就无需信任假设。

在附录中,我们展示了如何将此方法扩展到 TB 级别的数据公证。公证能力的限制仅在于用户能下载多少数据。这引出了一个新问题:我们该如何组织、存储和分发如此庞大的数据?(此处省略若干关于 P2P、激励机制等内容,留待另一篇文章讨论。)

附录

简单架构
似乎很容易对
requests.py 进行 fork(代码复刻):

  1. 将其中的 HTTPS 部分替换为一个模块,该模块会请求公证方生成共享密钥。

  2. 实现一种解密机制,用于处理服务器返回的数据。

        if did_i_sign_it(x):
            print("I won't notarize this cos i signed it, So it didn't come from server")
        else:
            out = notarize(data)
            return(out)


如此一来,公证数据就变得非常简单——只需在 Python 脚本中导入一个不同的 requests 库即可。

注:实际上 requests.py 依赖于 urllib3 来处理 HTTPS,因此需要 fork 并修改的是 urllib3

示例
假设我们想对 Twitter API 的某些数据进行公证:

import requests

# Your credentials
BEARER_TOKEN = "YOUR_BEARER_TOKEN_HERE"
USERNAME = "TwitterDev"

# 1. Get the User ID
user_url = f"https://api.twitter.com/2/users/by/username/{USERNAME}"
user_headers = {"Authorization": f"Bearer {BEARER_TOKEN}"}
user_resp = requests.get(user_url, headers=user_headers)
user_id = user_resp.json()['data']['id']
print(f"User ID for {USERNAME} is {user_id}")

# 2. Get the User's Tweets
tweets_url = f"https://api.twitter.com/2/users/{user_id}/tweets"
tweets_params = {"max_results": 10}
tweets_resp = requests.get(tweets_url, headers=user_headers, params=tweets_params)

# 3. Print the Tweets
tweets = tweets_resp.json()
for tweet in tweets['data']:
    print(f"- {tweet['text']}")

只需将 requests 替换为 requests_notary,脚本就能像以前一样运行。我们可能还需要允许用户下载已公证的签名。也许可以将签名直接嵌入返回的 JSON 中。

乐观解密 + 公证(Optimistic Decryption + Notarizing)
这里有一个微妙之处:前文提到的“签名”,实际上指的是加密/解密过程。共享密钥并不能实现传统意义上的数字签名。但我们可以这样理解:如果你能用共享密钥对某段数据加密,而接收方用同一密钥解密后得到了原始数据,这就证明加密者和解密者都拥有该共享密钥。如果其中任何一方没有密钥,解密结果将是随机噪声。

我们正是利用这一特性来“模拟”签名。但问题在于,对于大数据(如 YouTube 视频),公证方必须先下载、解密、再重新签名并上传——这对大文件来说完全不可行。

我们可以反过来设计,避免公证方下载响应内容,代价是在遭受攻击时可能对随机数据进行签名。我们的依据是:只有知道共享密钥的人,才能将加密数据解密为非随机内容。

具体流程如下:

  1. 用户请求公证方对一个加密数据块(encrypted blob)进行公证。

  2. 公证方检查该数据块是否由自己加密。如果不是,就对其进行公证。

  3. 然后,公证方向用户公布共享密钥,用户即可尝试解密。

  • 如果解密结果是非随机的,说明数据确实来自服务器(因为只有服务器能用共享密钥加密)。

  • 否则,用户只会得到随机噪声。

换句话说,我们要求用户先“承诺”一段数据,只有在承诺之后,才允许其解密。

因此,公证方无需下载响应内容即可完成公证。它可以“乐观地”进行公证,并在事后公布共享密钥。只要它愿意在某些场景下(比如公证视频或 TB 级数据时)承担“可能公证了无意义数据”的风险,这种方案就是可行的。




作者:barryWhiteHat

免责声明:本文为c2e Labs的第三方内容,仅供信息分享与传播之目的,不代表我们的立场或观点且不构成任何投资及应用建议。版权归原作者或来源方所有,如内容或素材有所争议请和我们取得联系。

我来评论