Relation ONE IM 介绍

图 1-1 IM Architecture

Relation ONE IM是一个去中心化的隐私聊天协议。

Relation ONE IM真正实现了去中心化和隐私性。

它主要有四部分组成(见图 1-1)

  • Client: 是一个前端应用程序,帮助用户加密/解密消息、发送消息至Proxy。并且需要调用Relation Protocol的标准接口来进行好友和群组管理。

  • Proxy: 是一个简单的后端程序,仅用于接收Client发来的消息,存储消息至Arweave并且推送至消息接收者。

  • Decentralized Storage(Arweave): 用于永久化存储加密的消息。

  • 合约:一系列符合Relation Protocol的合约(Relation Name Service、Follow、DAO),用于存储用户信息、Follow信息、DAO信息。

1. 消息发送主要流程

流程见图 1-1

  1. 发消息的用户使用钱包连接至Client

  2. 在Client端使用Lit Protocol对消息进行加密

  3. Client将加密消息发生至Proxy

  4. Proxy将消息保存至永久化存储

  5. Proxy通过websocket将消息推送至需要接收消息的Client

  6. 收消息用户在Client端使用Lit Protocol对消息进行解密

2. 身份

每一个以太坊的公私钥对就是一个身份。可以使用MetaMask等钱包来连接Client。

以太坊地址相当于用户的id,聊天的时候在消息体中指定接收人的地址,Proxy会将消息推送至目标地址登录的Client。

私钥用于对消息体进行签名,保证了消息是由该地址发出去的,并且不能被任何人修改。

该身份也用于登录Lit Protocol,用来在Client中加密或解密消息。

该身份需要在Relation Name Service注册一个SBT Token作为隐私聊天的准入条件。

2.1. Relation Name Service

Relation Name Service是由Relation部署的一个符合Relation Protocol的Name Service标准的合约。

Relation Name Service作为隐私聊天的准入条件,使用隐私聊天之前需要在Relation Name Service合约注册,用户将会获得一个SBT Token。

在聊天时,从Relation Name Service合约中获取对方的Name和头像等信息,可以更方便的我们找到和认出好友。

Relation Name Service合约主要提供以下接口:

  • 注册:register(address owner, string calldata name, bool reverseRecord) external returns (uint)

  • 设置头像:setProfileURI(string memory profileURI) external

  • 获取用户名: nameOf(address addr) external view returns (string memory)

  • 获取头像:profileURI(address addr) external view returns (string memory)

图 1-2 Relation Name Service

3. Client

Client是一个前端应用程序,可以是web应用、Chrome插件、桌面应用程序,移动应用程序等,在Relation ONE中是一个Chrome 插件

用户在登录到Client后,Client会通过WebSocket连接至Proxy。当有人给自己发消息时,Proxy主动会推送消息到Client,Client使用Lit对消息解密后就可以看到明文了。

用户在发消息前,Client按照消息结构组装消息体,使用Lit对消息体进行加密后再发送至Proxy。

由于消息是在Client进行加密和解密的,保证了数据的隐私性。

Client还需通过调用合约来管理好友群组

4. Proxy

Relation ONE Proxy是一个后端应用程序,提供了一套REST Api接口(用于接收Client发送的消息、修改状态等)和一个WebSocket端口(用于推送消息至Client)。

Proxy会将Client发过来的消息存储至Arweave,由于所有消息都记录在去中心化存储中,因此Client并不是强依赖于某一个Proxy。即使Proxy宕机,Client在更换Proxy后,消息数据依然不会丢失。

4.1. 接收/发送消息

Client通过REST Api接口将(加密)消息发送至Proxy,Proxy会根据永久化存储策略将消息存储至Arweave,同时通过WebSocket将消息推送至接收者登录的Client上。

图 1-3 Send Message

4.2. 状态保存

未读消息数、屏蔽群消息、置顶消息等状态信息,由Proxy记录在自己的数据库中,用户连接到不同的Proxy时未读消息数不共享。

4.3. 消息永久化存储

Proxy会将消息打包上传至Arweave,每个消息包里包含1条或多条消息。每个包上传至Arweave时,都会打一个tag,指向前一个交易的Hash。

永久化存储消息有三个策略:

  1. 每12小时将消息打包上传到Arweave上。

  2. 如果12小时内没有消息,将不会打包,然后重新计时12小时。

  3. 如果没有到定时任务触发的时候,消息的大小超过了100MB,立即将消息打包上传到Arweave。

4.4. REST接口

Proxy需提供以下接口

接口描述
Http Method
接口地址
Request Body
Response Body

发送消息

POST

/message/send

查询聊天内容

GET

/message/list?limit=${limit}&offset=${offset}

标记为已读

POST

/message/read?messageId=${messageId}

未读消息数

GET

/message/unreadCount

Long

聊天列表

GET

/chats/list?limit=${limit}&offset=${offset}

5. 关系管理

5.1. 好友管理

使用符合Relation Protocol的Follow合约来管理好友。

每个身份都会通过FollowRegister创建一个Follow合约(见图 1-4),当AlicefollowBob的时候,只用调用Bob的Follow合约follow()方法即可。

图 1-4 Follow
  • 好友列表:通过FollowRegister遍历出所有的Follow合约,再遍历出每个用户的follow信息。

  • 关注:调用对方合约的follow()方法。

  • 取关:调用对方合约的unfollow()方法。

5.2. 群组管理

使用符合Relation Protocol的DAO合约来管理群聊,该合约中所有持有Semantic SBT的地址即为群成员。

Client通过调用标准的DAO接口来进行成员管理。

例如:发起人通过DaoRegister创建一个DAO合约。通过isFreeJoin()方法设置群聊是否可以自由加入。通过调用join()方法来加入群聊,通过调用remove(address addr)方法离开群聊。(见图1-5)

图 1-5 DAO
  • 创建群聊:在Client端调用DaoRegister合约的deployDaoContract(address owner,string memory name) external returns (uint256)方法创建一个新的群聊(见图 1-5)。

  • 加入群聊:如果isFreeJoin()设置为True。可以通过join()方法自由加入;如果为False需要管理员通过addMember(address[] memory addr) external来添加成员。

  • 离开群聊:自己调用remove(address addr)方法离开群聊。或者管理员调用remove(address addr)方法移除某个成员。

  • 设置群公告:群公告由群主向群里发送一条特殊的消息,发送时将kind设置为3

  • 群名称:通过name()方法获取群名称。

  • 群头像:通过daoURI()方法获取群头像。

  • 群成员:合约里所有持有SBT Token的地址即为群成员。

6. 聊天

6.1. 点对点聊天

可以与好友进行聊天,也可以给非好友的以太坊地址发送消息。具体步骤:

Client(A)使用Lit对消息的payload进行加密(accessCondition为接收方的以太坊地址),receiver设置为对方的以太坊地址,按照消息结构组装成消息体后发送至Proxy。Proxy会将消息推送至接收方地址登录的Client(B)。

6.2. 群聊

发送群消息时,用户A登录到Client(A)设置Lit对消息的payload进行加密(accessCondition为群成员可见),receiver设置为群聊的合约地址,按照消息结构组装成消息体后发送至Proxy。Proxy会将消息推送至每个群成员所在的Client(C)、Client(D)、Client(E)……

6.3. 加密/解密

Client会集成Lit Protocol SDK,每条消息都在发送者的CLient加密,在接收者的Client解密。

图 1-6 消息加密解密

如图 1-6,Alice要给Bob发送一条加密消息:Hi

  1. Alice的Client会使用Lit SDK在本地对Hi进行加密,得到消息密文encryptedString和秘钥symmetricKey

  1. Alice的Client设置accessControlConditions为Bob的地址可见,并将accessControlConditions和秘钥symmetricKey上传到Lit的节点

  1. Alice的Client组装Message,并发送至Proxy。

  1. Proxy将消息转发至Bob的Client。

  2. Bob接收到Proxy推送的Message后,去Lit节点校验通过后拿到加密秘钥_symmetricKey

  1. Bob在本地对消息解密:

7. 消息定义

7.1. Message结构

  • messageId生成方法: 组装下面格式的json字符串,计算其Keccak256

  • kind

    • 0: p2p message。

    • 1: group message。

    • 2: dao message。

    • 3: announcement。

    • 4: hide message。

  • Session Key:

在客户端生成一个公私钥对作为Session Key,把Session Key的公钥组装成delegationMessage,使用用户的私钥对delegationMessage进行签名,得到delegationSig

用户发消息时,使用Session Key的私钥对消息进行签名,并且带上delegationMessagedelegationSig,通过这个delegationChain,就能验证消息是由用户本人签名的。

  • sig生成方法:

通过Session Key的私钥对messageId进行签名即得到sig

7.2. 消息包

1条或多条Message组成的数组称为一个消息包(Messages),它是以下格式:

7.3. Payload

  • 文本消息

  • 卡片消息

  • NFT

  • 本地图片

  • 表情包

  • 群转账

  • 公告

7.4. Chat结构

8. 消息存储

使用永久存储Arweave来存储加密消息,保证了数据的可靠性和去中心化。

MessageProxy打包成Messages,上传至rweave上。每个Transaction都被打上一个tag,指向前一个Transaction的Hash。可以方便的根据最新Hash找到所有历史消息。

图 1-7 消息存储

9. 合约

Name
Docs
Mumbai
Goerli
zkSync Era Testnet

Relation Name Service

0x04C23dDfE813d8D208bc8120b73F8EF30f423850

0xe4AA1f1E5be0A4E7F80CAC26C2Db1611C3E70c41

-

FollowRegister

0xA0cECa90d7A2033f5162dc73391cdB72272456E7

-

0x734dE4f1b3a1c0619ECB80e3a676a6cFabe72Adc

DaoRegister

0x153C3eF051C7665c9A9c1a718bF12bC8EE6b5115

-

0x7229Dc0117E8484D61137B7304b02b163beC912c

Last updated