Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 11 分钟

去中心化社交网络:协议、架构与实用构建路径

一份技术指南,讲解去中心化社交网络在联邦式与基于区块链的设计中如何运作,并提供可运行的步骤来验证联邦、索引链上社交数据,以及衡量你所部署的内容。

TL;DR

去中心化社交网络指的是没有单一运营方拥有身份层、内容图谱或用户准入权的社交平台。存在两大主流技术家族:联邦式协议(如 ActivityPub),由独立运行的服务器交换签名消息;以及基于区块链的设计,身份、帖子或社交图谱锚定在链上并通过 RPC 读取。本文解释每种设计背后的思维模型,给出可运行的流程来验证联邦和索引链上社交事件,并展示如何在不编造数字的情况下衡量延迟与可用性。最后讨论决定哪种架构适合特定社区的权衡,以及承诺前需要测试的内容。

去中心化社交网络究竟改变了什么

去中心化社交网络移除了中心化平台依赖的单一瓶颈:一个托管账户、排序信息流并可单方面移除用户的运营方。在实践中,去中心化是一个光谱。联邦式网络(如 fediverse)将内容保留在独立运营、使用共享协议的服务器上,因此一个服务器上的用户可以关注并回复另一服务器上的用户。基于区块链的社交网络将身份、帖子或社交图谱推上账本,因此任何能读取链的客户端都可以重建网络状态。

重要的工程后果是客户端不再是产品边界。在联邦式设计中,服务器是审核和可用性的单位;在基于链的设计中,RPC 端点和索引器是读取可用性的单位。两种设计都将难题从“构建信息流”转移到“就协议达成一致、验证签名并保持读取一致性”。

  • 联邦式:独立服务器、共享协议、本地审核、协议内可移植身份。
  • 基于区块链:账本锚定身份和内容、客户端重建、依赖 RPC 的读取。
  • 混合式:社交操作使用联邦传输,身份或支付使用链。

构建前的假设与前提条件

本指南假设你可以运行小型服务器或容器,拥有 Node.js 18+ 或 Python 3.10+,并且拥有用于读取任何链的 RPC_URL。它不假设特定提供商;端点选择是单独的决定,详见下方链接的 RPC Assistant 材料。如果你正在为基于链的社交应用评估基础设施,请先从多链 RPC 端点指南和RPC 端点指南开始,然后再进行基准测试。

对于联邦式工作,你需要一个你控制的域名以及提供 HTTPS 的能力,因为 ActivityPub actor 由 HTTPS URL 标识,签名绑定到这些 URL。对于基于链的工作,如果你计划回填历史帖子,你需要一个支持归档的端点,因为社交图谱会增长,对修剪节点进行全历史查询会失败。

思维模型:联邦式与账本锚定

联邦式社交网络是服务器之间的消息传递。每个账户是一个具有收件箱和发件箱的“actor”。当你发布时,你的服务器将签名的 Activity 投递到关注者服务器的收件箱。这些服务器根据你的 actor 公钥验证 HTTP 签名,然后在本地存储并渲染帖子。没有全局数据库;网络状态是每个服务器选择接受和保留的内容的并集。

账本锚定的社交网络是从日志重建状态。帖子是交易或链上引用的签名记录;社交图谱通过读取事件并按顺序应用来推导。链提供排序和防篡改,但不提供信息流。你仍然需要一个索引器将原始事件转换为时间线、关注者数量和回复树。该索引器是你大部分运营风险所在。

  • 联邦式:投递是基于推送且按服务器的;失败是部分和局部的。
  • 账本:投递是一次写入;读取依赖索引器新鲜度和 RPC 可用性。
  • 两者:身份是公钥或密钥对,验证是安全边界。

用可运行的请求验证联邦 actor

在信任任何联邦行为之前,确认 actor 文档可访问且格式正确。ActivityPub actor 以 JSON 形式提供,需带 Accept 头。将占位主机替换为你运营或正在评估的服务器。

正确的响应返回内容类型 application/activity+json(或兼容的 profile)、与请求 URL 匹配的 id、带有 owner 的 publicKey,以及同源的 inbox/outbox URL。如果 id 与你请求的 URL 不匹配,下游签名验证将失败,这是“我的服务器无法关注任何人”报告最常见的原因。

curl -sS -H 'Accept: application/activity+json' \
  https://YOUR_DOMAIN/users/alice | python3 -m json.tool

检查收件箱投递与签名处理

联邦失败通常表现为静默未投递而非错误。诊断方法是检查服务器的出站投递队列和接收服务器的日志中的签名验证结果。最小检查是确认你的 actor 公钥可获取,并且签名中的 keyId 解析到同一个 actor。

下面的脚本获取一个 actor 并打印对签名验证重要的字段。对你的服务器和你试图联邦的对等服务器分别运行;密钥所有权不匹配或 actor URL 重定向解释了大多数失败。

import json, urllib.request

def actor(url):
    req = urllib.request.Request(url, headers={"Accept": "application/activity+json"})
    with urllib.request.urlopen(req, timeout=10) as r:
        data = json.load(r)
    print("id:", data.get("id"))
    print("inbox:", data.get("inbox"))
    print("outbox:", data.get("outbox"))
    key = data.get("publicKey", {})
    print("keyId:", key.get("id"))
    print("owner:", key.get("owner"))

actor("https://YOUR_DOMAIN/users/alice")

通过 RPC 索引链上社交事件

对于基于链的社交网络,读取路径是一个索引器,它扫描日志以获取社交合约的事件并将其写入可查询的存储。下面的流程是一个诊断骨架:它拉取最新区块,然后在有界范围内获取合约地址的日志。使用占位 RPC_URL 和你控制的真实合约地址。

首次运行时保持区块范围较小。公共端点通常限制 getLogs 范围,深度回填需要归档端点。如果你需要一致的历史读取,专用节点消除了共享速率限制的波动,使索引器时序不可预测;参见专用节点选项和RPC 定价了解运营权衡。

const RPC_URL = process.env.RPC_URL;
const ADDRESS = "0xYOUR_SOCIAL_CONTRACT";

async function rpc(method, params) {
  const res = await fetch(RPC_URL, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
  });
  const body = await res.json();
  if (body.error) throw new Error(JSON.stringify(body.error));
  return body.result;
}

(async () => {
  const latest = parseInt(await rpc("eth_blockNumber", []), 16);
  const from = "0x" + (latest - 500).toString(16);
  const to = "0x" + latest.toString(16);
  const logs = await rpc("eth_getLogs", [{ address: ADDRESS, fromBlock: from, toBlock: to }]);
  console.log("blocks", from, to, "events", logs.length);
})();

预期验证结果及其含义

健康的联邦检查返回一个 actor 文档,其 id 等于请求的 URL,收件箱在同源,publicKey owner 与 actor id 匹配。健康的索引器检查返回无错误的日志数组,并且区块范围在运行之间推进。如果日志数组为空,在假设索引器损坏之前,确认合约地址和事件主题。

将每次运行记录在结果表中,而不是信任单次观察。下表是模板;用你自己的测量值填写,不要发布你未观察到的数字。

  • | 检查项 | 方法 | 观察值 | 通过标准 |
  • | Actor 文档 | 带 Accept 头的 curl | (记录) | id 匹配 URL,密钥所有者匹配 |
  • | 收件箱投递 | 服务器投递日志 | (记录) | 签名 Activity 被接受,无 4xx |
  • | 日志扫描 | eth_getLogs 有界范围 | (记录) | 无错误,事件已解析 |
  • | 读取延迟 | 计时 RPC 调用 | (记录) | 在你的 SLO 内 |

在不猜测的情况下衡量延迟与可用性

去中心化社交应用是读取密集型的:时间线、个人资料和回复树的获取频率远高于帖子写入。将读取路径与写入路径分开衡量。对于 RPC 支持的读取,以稳定间隔对固定批次的调用(例如 eth_blockNumber 和已知范围内的 eth_getLogs)计时,并记录 p50 和 p95。对于联邦式读取,对多个对等服务器上的 actor 和 outbox 获取计时,因为跨服务器延迟占主导。

对至少两个端点或两个对等服务器运行相同的测量,以便区分慢提供商和慢网络。如果你正在为基于链的社交应用比较提供商,Web3 项目最佳 RPC 提供商和区块链节点托管指南涵盖了运营标准;实际的端点选择属于 RPC Assistant,而不是你未运行的基准测试。

import time, statistics, urllib.request, json

RPC_URL = "RPC_URL"

def call(method, params):
    body = json.dumps({"jsonrpc":"2.0","id":1,"method":method,"params":params}).encode()
    req = urllib.request.Request(RPC_URL, data=body, headers={"content-type":"application/json"})
    start = time.perf_counter()
    with urllib.request.urlopen(req, timeout=10) as r:
        json.load(r)
    return (time.perf_counter() - start) * 1000

samples = [call("eth_blockNumber", []) for _ in range(20)]
print("p50_ms", round(statistics.median(samples), 1))
print("p95_ms", round(sorted(samples)[int(len(samples)*0.95)-1], 1))

排查看似去中心化缺陷的故障

大多数“去中心化坏了”的报告是普通的分布式系统故障。在联邦侧,主要原因是 actor URL 重定向、时钟偏差破坏签名有效性,以及阻止未知用户代理的服务器。在链侧,主要原因是 getLogs 范围限制、在索引器需要历史时从修剪节点读取,以及使最近索引区块失效的重组。

显式处理重组:跟踪确认深度并在每次运行时重新扫描最后 N 个区块。显式处理联邦失败:记录每次投递尝试的 HTTP 状态和签名验证结果,并使用退避重试。这两个问题都不能通过增加更多服务器来解决;都通过使故障可观察来解决。

  • Actor id 不匹配:修复规范 URL 和任何反向代理重定向。
  • 签名被拒绝:检查系统时钟和密钥轮换。
  • getLogs 为空:对照已知事件验证地址、主题和区块范围。
  • 时间线陈旧:检查索引器延迟和确认深度。

每种架构的局限与权衡

联邦式网络继承每个服务器的审核和可用性。这对社区治理是一个特性,对正常运行时间是一个负担:如果你的服务器宕机,你的用户身份就会变暗,即使协议是开放的。可移植性原则上存在,但在服务器之间迁移关注者和内容在操作上仍然粗糙。

账本锚定的网络继承链及其 RPC 层的可用性和成本特征。写入消耗 gas 或费用,读取依赖你必须运营或信任的索引器,内容永久性是一把双刃剑:删除成为社会惯例而非保证。两种架构都没有严格更好;选择取决于你的社区更看重本地审核控制还是可验证的全局状态。

下一步:从验证到可工作的部署

首先针对测试网搭建一个联邦 actor 和一个索引器,然后运行上述验证流程,直到两者持续通过。只有在那之后才添加第二个服务器或第二个 RPC 端点,这样你可以将故障归因于特定层。如果你正在为社交应用选择链,在承诺索引器设计之前,请查看支持的网络和API 服务概览。

具体到端点选择,使用 RPC Assistant 而不是从列表中猜测:它专注于简短、直接的提供商和网络推荐,而本指南涵盖架构和衡量方法。将你的测量表与部署一起保留,以便未来的变更与观察到的基线进行比较,而不是假设。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始