Farcaster Protocol 是继 Lens Protocol 之后的又一 SocialFi 赛道龙头产品,Farcaster 是前 Coinbase 高管 Dan Romero 和 Varun Srinivasan 的项目,目前已获得了 A16Z 领投的 3000 万美金。
Farcaster 的目标是为 WEB3 生态提供一个可信的中立协议,使用户与受众有直接的联系,同时可以让开发者自由的建立新的客户端。
Farcaster 的远期目标是成为 WEB3 社交赛道中重要的底层基础设施,这一点和 Lens Protocol 的方向是一致的,但 Farcaster 的架构设计相比 Lens Protocol 精细很多,采取了一个试图在 WEB2 和 WEB3 之间寻找一个最优平衡点的策略。
今天我们来深入了解一下 Farcaster 的协议层设计与生态应用思路,如果想要深入研究可以去查看官方的 github:
社交网络是去年 10 年发展的最迅速的行业,很多的社交平台为开发者提供了 API,使开发者可以 “二次创造”,并发展出新的生态,例如 Twitter 上的各类好玩的插件。但是,最近几年,情况好像不太对了,开发者能做的事情似乎变得越来越少,API 的限制与各种各样的审查使得开发者不再自由,甚至有时在没有任何通知的前提下,就被剥夺了访问的权利。
Farcaster 是一个完全的去中心化协议,方便开发者构建去中心化的社交网络应用。Farcaster 对于去中心化的定义很简单:当两个用户想互相交流的时候,没有任何一种方式可以阻止这件事情发生。也就是说,用户可以完全控制他们的身份,他们的数据以及他们的社交关系。开发人员可以打破任何第三方甚至是网络的限制构建一个完全去中心化的社交应用。
这样的愿景也是 Lens Protocol 想要实现的,我们可以认为 SocialFi 的底层协议的最大价值,是提供一个完全去中心化的社交网络的技术底层的实现方法,使其完全不会被任何的第三方控制。类似于 IPFS 在去中心化存储市场中的价值。
Farcaster 采用的一种链上 + 链下的混合架构,来完成去中心化协议的搭建。Farcaster 的身份是被存储在以太坊链上的,并利用以太坊来保证其安全性,可组合性与一致性。身份通过以太坊地址来控制,并通过以太坊账户来签署链下信息。
用户的数据则通过身份进行加密签名,存储在用户控制的服务器上(Farcaster Hubs),之所以数据不存储在链上是因为在大多数 L1 和 L2 网络上的结算成本过高,速度过慢。
这一架构与 LENS Protocol 的设计不太一样,Farcaster 更多考虑了开发者的实际需求,并更类似于 WEB2 社交媒体的表现形式,降低用户学习的成本,但 Farcaster 依旧是去中心化的,用户的身份,数据和社交关系是基于区块链的。
Farcaster 账户类似于 Twitter 或 Reddit 等假名社交网络上的账户,个人可以同时操作几个账户。每个账户都有一个与之相关的唯一号码,称为 Farcaster ID 或 Fid。Farcaster ID 可以通过调用 Farcaster ID 注册处(FIR)从一个以太坊地址获得。这个地址被称为托管地址,可以代表账户签署链外和链上信息。用户可以选择从 Farcaster 名称注册处(FNR)获得一个 Farcaster 名称或 fname,该处为其颁发一个独特的名称,如 @alice。
可以这样理解,Fid 是链上身份,而 FNR 则是社交可读身份。如果 Fid 是钱包地址的话,那么 FNR 就是 ENS。
签名信息是一种防篡改和自我认证的对象,由 fid 来进行签名。签名信息代表了用户的行为,如发帖、社交反馈(评论 / 转发)或修改账户信息,如用户名。
签名消息具有消息属性,消息属性包含了一些有效载荷(payload)。有效载荷被序列化、散列化,并由一个有效的密钥对签名(例如 costody address)。envelope 包含哈希值、签名和签名密钥对的公钥,任何接收者都可以用它来验证 fid 的签名。
信息必须用 RFC-8785 进行序列化,用 BLAKE2b 进行散列,并用 Ed25519 签名方案进行签名。每条信息还必须包含一个 fid 来查询链上的托管地址,以及一个用于排序的时间戳。
应用程序是人们用来与 Farcaster 网络互动的程序。一个简单的应用程序可能包括一个独立的桌面或移动客户端,直接与 Farcaster Hub 会话。它可以发布新消息,并查看其他 fids 发布的消息。这样的应用程序是自托管的,必须用托管地址或有效的签名密钥才能进行实例化。
更复杂的应用程序可能会添加一个代理后台服务器,对 Hub 的数据进行索引。索引允许服务器实现搜索、算法馈送和垃圾邮件检测等功能,这些功能在 Hub 上很难执行或很昂贵。这样的应用可以通过在客户端存储密钥来实现自托管;通过要求用户提供委托签名密钥来实现委托;或者通过管理所有密钥来实现托管。
Hub 是一个永远在线的服务器,用于验证、存储和复制签名信息。用户选择一个 Hub,并使用 FIR 在链上发布其 URL。他们的 Follower 可以使用这个 URL 来寻找和下载他们的消息。同时,用户也可以自己运行 Hub 或使用第三方托管服务。
Farcaster 的身份系统使用户的身份具有以下特性:
安全且完全去中心化
在社交网络中容易被识别
建立起来很容易(快速且低成本)
可恢复(不违背去中心化的特性)
这些特性在一个身份系统中实现是具有挑战性的,因为它们经常是冲突的。例如,拥有一个去中心化的、值得信赖的名称系统是很难的(例如是否要保证名称系统的唯一性)。
Farcaster 通过两个独立的系统来平衡这些特性。Farcaster ID Registry(FIR)发布新的 ID 号码,称为 fids;Farcaster Name Registry(FNR)发布新的用户名,称为 fnames。Fids 是安全的、分散的标识符,存在于每条信息中,在概念上类似于 uuids。
Fnames 主要是对于 FIR 修饰,在渲染时取代 fid,并可在任何时候改变。将一个身份分离成这两个组件使我们能够实现我们的目标,但代价是给系统增加了一些复杂性。这两个系统还实现了一个恢复机制,在不影响去中心化的情况下保护控制名字的密钥对的丢失。
Farcaster ID 是数字标识符,类似于 uuids。当显示给用户时,它们前面会有一个感叹号(例如:!8098)。
一个 FID 代表一个独特的实体,如一个人或一个组织。每个引用该实体的信息都必须使用它的 fid,而不是它的 fname。fid 的注册费用很低,而且是终身拥有。FID 合约不能以任何方式升级或修改。
FID 从 0 开始,每当有新的注册发生时就会递增 1。一个 fid 以 uint256 的形式存储在链上,保证了近乎无限的供应,因为它可以被递增到~10^77。
用户可以使用 FIR 来配置 URL,用来寻找他们的链外信息的位置。
Farcaster 名称是唯一的,类似于其他网络的用户名。当显示给用户时,他们会在前面显示一个 @符号(例如:@alice)。
Fname,连同个人资料、名称和验证标记,有助于在浏览网络时直观地识别一个实体。与 fids 不同,fname 主要是可读的,与用户创建的基础数据没有关系。fname 的所有权不是永久性的,用户必须每年支付一些费用。fname 续费可以在 fname 到期前 90 天进行。过期的名字将以荷兰式拍卖的方式进行拍卖,投标人必须支付年费和溢价,起价为 1000ETH。溢价每 8 小时减少 10%,直到达到 0 ETH。
Fnames 是由 Farcaster 名称注册处以先到先得的方式发行的 NFT。每个名字必须符合正则表达式 /^[a-z0-9][a-z0-9-]{0,15}$/。它们具有特定的属性,使它们在社会网络中相对于其他命名空间(如 ENS)来说更加有用。它们的铸造和拥有成本较低,由于字符集的限制,不容易受到同音字的攻击,而且也可以恢复。Farcaster 并不强制要求使用 fname,用户可以自由地使用其他的名字空间和他们的 fids。
放弃 fname 的使用并不会有太大的不影响,因为 Farcaster 是围绕 fid 设计的,每条信息和行为都指向 fid 而非 frame。fnames 可以在任何时候改变,而不会失去任何一个之前的依赖信息。
在测试期间,用户名可以免费注册,并受一个简单的政策约束。该政策的目的是防止名字被不活跃的用户占用或被恶意用来冒充他人。这个问题的解决方案不容易自动化,需要人工判断来执行。用户名政策有两个核心原则:
冒名注册 - 如果你注册的用户名属于一个知名的公众人物或实体,你的名字可能会被取消注册。例如,@elonmusk、@vitalikbuterin、@google 或 @whitehouse。
不活跃 - 如果你在 60 天以上没有积极使用一个用户名,你的名字可能会在其他用户的要求下被取消注册,或者由我们决定取消注册。
我们预计经常需要人工干预,因为可能会有合理的冲突。例如,你注册了 @vitalik,而 Vitalik Buterin 在你之后注册并想要这个名字。在这种情况下,我们会问三个问题来指导决策:
该用户在 Farcaster 上是否活跃并参与?(例如,如果他们在过去的几个月里发表了高质量的帖子。)
该用户是否对这个名字有合理的要求?(例如,如果他们的名字也是 Vitalik)
该用户是否在其他网络上拥有类似的、活跃的账户?(例如,如果他们在 twitter 上拥有 vitalik 和 vitalik.ens)。
如果这些问题的答案大多是肯定的,他们将保留对自己名字的要求。在测试网中,核心团队将对这种冲突进行仲裁,我们希望在接近主网时,围绕这个问题正式建立一个管理制度。如果一个名字由于仲裁的结果而被收回,用户将不会被退款。
如果用户失去了持有 ID 和名字的地址的密钥,Farcaster ID 和名字是可以恢复的。这两个合约都实施了一个延时恢复系统,允许恢复地址请求转移到一个新的地址。如果保管地址在三天内没有取消转移,恢复地址可以完成转移。
用户可以将恢复地址设置为自己钱包中的另一个地址,与朋友共享的多重签名,或第三方恢复服务。用户也可以在任何时候改变恢复地址。所有权仍然是去中心化的,因为恢复地址不能进行保管地址不同意的转移。
将资产转移到一个新的托管地址将解除恢复地址的设置。否则,用户可能会在 OpenSea 上购买一个名字,但以前的所有者却用他们的恢复地址隐秘地将其领回。
信息处理是 Hub 接受新消息和确定用户状态的过程。用户为他们的每一个行动向一个 Hub 发送消息。如果一个用户喜欢一个 URL,不喜欢它,又喜欢它,就会产生三条信息。一个收到所有信息的 Hub 将确定用户喜欢的 URL 的当前状态。
Hub 可以丢弃前两条信息以节省空间。Hub 可以使用合并操作来压缩这样的消息,这就避免了客户端的不一致性并节省了空间。消息可能对其合并操作有不同的规则。例如,一个用户对同一个用户的两个喜欢可以压缩成一个,而两个回复则不能。
Hub 可能会遗失用户的一些信息,并最终处于一个错误状态。例如,它可能只收到第一个喜欢和不喜欢的信息,这就把当前状态设定为不喜欢。合并操作应该允许状态向前发展,并在缺失的消息被重新广播时达到一致性。换句话说,合并应该确保最终的强一致性。
Hub 通过为每个消息类型实现 CRDT 集,编码特定的验证和合并规则来实现这一点。这一特性使得 Hub 具有很高的可用性,因为它们可以在任何时候下线,并且总是能够恢复同步。从形式上看,我们的 CRDT 集是匿名的 Δ- 状态的 CRDT2,每条消息都是集上的一个连接且不可减少的更新。
信息集合可以通过时间戳对签名信息进行排序,以最后写入的策略解决合并冲突。然而,他们不能保证完美的排序,因为时间戳容易受到时钟偏移、时钟漂移、恶意用户欺骗的影响,并且可能因为一些原因发生碰撞。应用程序可以使用混合时钟来产生完美排序的时间戳,不发生碰撞,但我们不能强制使用它们。
相反,我们为消息定义了一个排序系统,通过使用时间戳来确定初始排序,并使用哈希值来打破冲突,从而确保总排序。总排序是有保证的,因为两个消息不能有相同的哈希值,除非它们是同一消息。两个消息 a 和 b 可以用这个算法进行比较:
如果 a.timestamp>b.timestamp,则 a 更大。
如果 a.timestamp < b.timestamp,则 b 更大。
如果 a.timestamp == b.timestamp
如果 a.hash>b.hash,则 a 大。
如果 a.hash < b.hash,则 b 更大。
如果 a.hash = b.hash,a == b
时间戳是作为数字比较的,而哈希值是作为字符串比较的。由于字符串的比较在不同的实现中会有差异,我们必须在比较算法中精确。我们认为,两个哈希值 x 和 y 可以通过比较每一对字符来进行比较:
如果所有的字符对都相等,并且 x 和 y 都终止,那么 x == y
如果所有的字符对都相等,并且 x 先终止,那么 y>x
如果遇到一个不同的字符对 xC,yC,那么如果 ASCII (yC)>ASCII (xC),则 y>x
除了消息类型的特定验证外,所有消息必须通过以下验证:
message.timestamp 比系统时间提前不超过 1 小时。
message.fid 必须是 FIR 中的一个已知 fid 号码。
signerPubKey 应该是 message.fid 的有效托管签名者或委托签名
hashFn (serializeFn (message)) 必须与 envelope.hash 匹配,其中 hashFn 是一个 Blake2B 函数,serializeFn 执行 JSON 规范化。
EdDSA_signature_verify (envelope.hash, envelope.signerPubKey, envelope.signature) 应该通过。
Cast 是由用户创建的公共信息,其中包含文本,也可以嵌入媒体、链上活动或其他 Cast。Cast 被存储在一个两阶段的集合 CRDT3 中,解决消息之间的冲突。
一个 Cast 可以通过 CastAdd 消息来添加,该消息被放置在 CRDT 的 add-set 中。每个 Cast 都被其哈希值所索引,除非 Cast 是相同的,否则哈希值保证是唯一的。推而广之,两个添加消息永远不会冲突,除非它们是相同的,在这种情况下,一个可以被安全地丢弃。
Cast 可以通过 CastRemove 消息来移除,该消息包含对目标 CastAdd 的哈希值的引用。当收到该消息时,如果目标存在,则从 add-set 中移除,而移除则被添加到 rem-set 中。添加和删除之间的冲突用 Remove-Wins 规则处理,而删除之间的冲突用 Last-Write-Wins 规则处理,在平局的情况下回到 lexicographical 排序。
一个 Cast Add 可以包含最多 320 个字符的 unicode 文本和两个最多 256 个字符的 URI。客户端负责将 URI 和文本一起解码和渲染。
没有父代的 Cast 是一个顶级的 Cast,客户应该显示在用户的个人资料或时间线上。有父代的 cast 是对另一个 cast、web URL 或链上对象的回复,应该在一个线程中显示。
投票形成一系列的树,每个根是一个投票或 URI,每个子节点是一个回复投票。每个树都可以被渲染成一个线程对话。树被保证为非周期性的,因为在子节点指向它之前,父节点必须被散列和签名。对父节点数据的任何改变都会破坏与其子节点的所有关系。
Cast 信息必须通过以下验证步骤。
文本必须包含 <=320 个有效的 unicode 字符
embed 必须包含 0 到 2 个项目
项目必须是一个最多 256 个字符的 URI
如果存在父代,必须是一个有效的 URI,不等于这个消息的 URI(例如,fid:/cast:)。
Cast Remove 只包含一个对 Cast Add 的哈希值的引用。它允许永久删除 Cast,同时删除原始 Cast 的数据。
该消息必须通过以下验证步骤:
message.data.body.hash 必须不等于 message.envelope.hash。
message.timestamp 必须 <= 系统时钟 + 10 分钟
message.data.fid 必须是 FIR 中的一个已知 fid
当收到一个添加消息 a 时,如果在 rem-set 中存在 r,并且 r.data.body.hash 等于 a.hash,则丢弃 a。否则,将 a 添加到添加集中。当收到一个移除信息 r 时,如果在添加集中有一个 a.hash 等于 r.data.body.hash,则删除它。如果在 rem-set 中存在一个 r',其中 r.data.body.hash 等于 r'.data.body.hash,如果 r'>r,丢弃 r';如果 r'<r,则删除 r 并将 r' 添加到 rem-set 中;否则,将 r 添加到 rem-set 中。
Action 是用户对一个目标进行的公开操作,这个目标可以是另一个用户,也可以是链上活动。目前支持两种类型的操作:喜欢和关注。该协议可以很容易地被扩展以支持新的 Action。用户可以通过切换消息上的活动属性来撤销和重做行动。从概念上讲,每个行动是社交图中的一条边。
Action 是用 LWW-Element-Set CRDT 管理的,它保证了最终的一致性。从概念上讲,有一个单一的集合来存储所有的消息,冲突是通过时间戳和 lexicographical hash 顺序来解决。增加是通过构建一个 active 为真的行动消息 a 来进行的,而删除则是通过将 active 设置为假来进行的。在这两种情况下,将消息合并到集合中的逻辑是:
如果集合中存在一个 Action x,其类型、目标 Uri 和 fid 的值与传入的动作 y 相同。如果 x>y,丢弃 y; 如果 x<y,删除 x 并将 y 加入到集合中;否则,将 y 添加到集合中。
验证是 Farcaster 账户和外部实体之间所有权的双向证明。验证可以用来证明以太坊地址、特定的 NFT、其他社交媒体账户、甚至是域名的所有权。
验证有三个核心概念:
声明,包括对 Farcaster 账户和外部实体的引用。声明可以被哈希化,以便为每个索赔创建一个独特的标识符。
来自外部实体的方向性证明,该实体被授权提出要求,显示其与 Farcaster 账户的连接意图。
来自 Farcaster 账户的方向性证明,接受将索赔与 Farcaster 账户关联的请求。
签名者授权是一个消息,授权一个新的密钥对为 Farcaster 账户生成签名。
当一个 fid 被铸成后,只有保管地址可以代表它签署信息。用户可能不想把这个密钥对加载到每个设备中,因为它增加了账户被破坏的风险。保管地址,也被称为保管签名者,可以授权其他被称为委托签名者的密钥对。与监管签名者不同,委托签名者只允许发布链外信息,不能执行任何链上操作。
托管签名人在 secp256k1 曲线上生成 ECDSA 签名,只能发布签名人授权信息。所有其他类型的消息必须由委托签名者签署,委托签名者在 curve255194 上创建 EdDSA 签名。委托签名者可以用来授权新的设备甚至第三方服务来为一个账户签署消息。如果一个委托签名者被破坏,它可以被自己、其信任链中的祖先或任何托管签名者撤销。当一个签名者被撤销时,Hubs 会丢弃它所有的签名信息,因为没有办法区分用户的信息和攻击者的信息。
用户也可能因为密钥恢复或更换钱包而将一个 id 转移到一个新的托管地址。通常希望保留历史,因此两个托管地址都成为有效的托管签名者。一个 id 的有效签名人集合形成了一系列不同的树。每棵树的根是一个历史保管地址,而叶子是委托签名人。
签名人集合是一个修改过的两阶段集合,具有删除 - 赢和最后写入 - 赢的语义。如果新信息被有效的委托人或监护人签名,则被添加到该集合中。如果是由自己或祖先签署的删除信息则被接受。签名者一旦被移除就不能再被添加,它的所有后代子代和消息都会被丢弃。
如果两个有效的签名者分别授权同一个委托签名者,就会发生集合冲突,这就破坏了树状数据结构。如果发生这种情况,集合将保留具有最高时间戳和 lexicographical hash 的消息,按顺序排列。
Hubs 可以只复制特定账户的数据,这对扩展网络是一个有用的属性。如果 Farcaster 发展到足够大,以至于一台服务器无法支持复制整个网络的 Hub,那么工作负载可以分散到多个 Hub 上。集线器运营商也可以避免为那些有恶意行为或与运营商无关的用户同步数据。
选择性复制只能提供网络的部分视图。如果一个 Hub 正在同步 Alice 的数据,它就会知道她回复并喜欢 Bob 的一个帖子。然而,它不会知道 Bob 的帖子的内容,或者 Bob 喜欢她的回复,然后继续回复的事实。一个旨在提供准确的喜欢计数并提供所有回复信息的应用程序应该尽可能多地复制用户。