Developer Hub
Relation ProtocolRelation ONE APIRelation Graph API
中文
中文
  • 概述
    • Relation ONE
  • 开发指引
    • 名词解释
    • 服务地址
    • 鉴权与限流
    • JS-SDK使用说明
    • 快速开始
  • Relation ONE IM 介绍
  • API
    • 介绍
    • 签名认证
    • 登录认证
    • 用户信息
      • 绑定地址
      • 解绑地址
      • 用户信息
      • 查询地址
      • 查询Web2账户
    • 好友关系
      • 关注
      • 取关
      • 粉丝列表
      • 关注列表
      • 根据Web3地址查询粉丝列表
      • 根据Web3地址查询关注列表
    • 用户推荐
    • 群组管理
      • 创建
      • 加入
      • 移除
      • 离开
      • 解散
      • 转移管理员
      • 详情
      • 成员列表
      • 是否为管理员
      • 查询群公告
      • 设置群公告
    • 聊天管理
      • 置顶
      • 免打扰
      • 列表
      • 隐藏
    • 消息管理
      • 发消息
      • 读消息
      • 删除消息
      • 消息列表
      • 未读消息数
  • JS-SDK
    • Relation-Auth
      • 快速开始
      • 方法
    • IM-JS-SDK
      • 快速开始
      • 静态方法
      • 事件
      • 方法
      • 消息解析
    • Plugin-JS-SDK
      • 快速开始
  • 附录
    • 接口错误码
    • 合约列表
Powered by GitBook
On this page
  • 1. 消息发送主要流程
  • 2. 身份
  • 2.1. Relation Name Service
  • 3. Client
  • 4. Proxy
  • 4.1. 接收/发送消息
  • 4.2. 状态保存
  • 4.3. 消息永久化存储
  • 4.4. REST接口
  • 5. 关系管理
  • 5.1. 好友管理
  • 5.2. 群组管理
  • 6. 聊天
  • 6.1. 点对点聊天
  • 6.2. 群聊
  • 6.3. 加密/解密
  • 7. 消息定义
  • 7.1. Message结构
  • 7.2. 消息包
  • 7.3. Payload
  • 7.4. Chat结构
  • 8. 消息存储
  • 9. 合约

Relation ONE IM 介绍

Previous快速开始Next介绍

Last updated 2 years ago

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将加密消息发生至Proxy

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

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

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

2. 身份

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

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

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

2.1. Relation 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)

3. Client

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

由于消息是在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上。

4.2. 状态保存

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

4.3. 消息永久化存储

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

  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. 好友管理

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

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

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

5.2. 群组管理

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

  • 创建群聊:在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)方法移除某个成员。

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

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

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

6. 聊天

6.1. 点对点聊天

6.2. 群聊

6.3. 加密/解密

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

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

const messageToEncrypt = "Hi";
// 1. Encryption
const { encryptedString, symmetricKey } = await LitJsSdk.encryptString(messageToEncrypt);
  1. Alice的Client设置accessControlConditions为Bob的地址可见,并将accessControlConditions和秘钥symmetricKey上传到Lit的节点

const accessControlConditions = [
    {
      "contractAddress": "",
      "standardContractType": "",
      "chain": "ethereum",
      "method": "",
      "parameters": [
        ":userAddress",
      ],
      "returnValueTest": {
        "comparator": "=",
        //Bob's wallet address
        "value": "0x50e2dac5e78B5905CB09495547452cEE64426db2"
      }
    }
];
// 2. Saving the Encrypted Content to the Lit Nodes
const encryptedSymmetricKey = await litNodeClient.saveEncryptionKey({
  accessControlConditions,
  symmetricKey,
  authSig,
  chain,
});
  1. Alice的Client组装Message,并发送至Proxy。

const message = {
  ...
  "sender": "Alice's wallet address",
  "receiver": "Bob's wallet address",
  "payload": "${encryptedString}",
  "encryptedSymmetricKey": "${encryptedSymmetricKey}",
  ...
}
  1. Proxy将消息转发至Bob的Client。

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

// 5. Decrypt it
// <String> toDecrypt
const toDecrypt = LitJsSdk.uint8arrayToString(encryptedSymmetricKey, 'base16');

// <Uint8Array(32)> _symmetricKey 
const _symmetricKey = await litNodeClient.getEncryptionKey({
    accessControlConditions,
    toDecrypt,
    chain,
    authSig
})
  1. Bob在本地对消息解密:

// <String> decryptedString
let decryptedString;

try{
    // 得到原文:Hi
    decryptedString = await LitJsSdk.decryptString(
        encryptedString,
        _symmetricKey
    );
}catch(e){
    console.log(e);
}

7. 消息定义

7.1. Message结构

{
  "id": "${messageId}",
  "encryptedBy": "Lit",
  "sender": "${senderWalletAddress}",
  "receiver": "${receiverWalletAddress/receiverDaoContractAddress}",
  "timestamp": ${the message create timestamp in millisecond},
  "kind": ${kind},
  "tags": [
    ["quoteId", "${messageId of the quoted message}"],
    ["mentionedUsers", "${The wallet address of the mentioned user}"]
  ],
  "type": "${TEXT/CARD/NFT/IMAGE/EMOJI/BATCH_TRANSFER/ANNOUNCEMENT}",
  "payload": "${message encrypted by Lit}",
  "encryptedSymmetricKey": "${a hex string that LIT will use to decrypt payload as long as you satisfy the conditions}",
  "delegationMessage": "授权 ${地址} 代理签名,创建时间:${}, 过期时间:${}",
  "delegationSig": "${私钥对${delegationMessage}的签名}",
  "sig": "${授权私钥对${messageId}的签名}"
}
  • messageId生成方法: 组装下面格式的json字符串,计算其Keccak256

[
  <encryptedBy>,
  <sender>,
  <receiver>,
  <timestamp>,
  <kind>,
  <tags>,
  <payload>,
  <delegationMessage>,
  <delegationSig>
]
  • 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的私钥对消息进行签名,并且带上delegationMessage和delegationSig,通过这个delegationChain,就能验证消息是由用户本人签名的。

  • sig生成方法:

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

7.2. 消息包

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

Messages = [Message,Message,...] 

7.3. Payload

  • 文本消息

{
  //  ...
  "type": "TEXT",
  "payload": "Hi"
  //  ...
}
  • 卡片消息

{
  //  ...
  "type": "CARD",
  "payload": "type:href;action:shareFavorite;p1(Text):https%3A%2F%2Fspace.id%2Fvoyage,p2(Text):,p3(Text):SPACE ID,p4(Text):https%3A%2F%2Fspace.id%2Ffavicon.ico"
  //type:${href};action:${shareFavorite};p1(Text):${URL},p2(Text):,p3(Text):${title},p4(Text):${icon}
  //  ...
}
  • NFT

{
  //  ...
  "type": "NFT",
  "payload": "type:image;action:nftImage;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fnft%2Fpolygon%2F0x4b5c095380e8e5f016a70a28af570c3bca93b811%2F25908.jpeg,p2(Text):polygon"
  //type:image;action:nftImage;p1(Text):${imageURL},p2(Text):${chainName}
  //  ...
}
  • 本地图片

{
  //  ...
  "type": "IMAGE",
  "payload": "type:image;action:localImage;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fim%2Fimage%2Fnymb5-kqaaa-aaaaj-4thiq-cai%2F2f3537d1a2474f2fb92cb5f7de2ed70a.png,p2(Text):im/image/nymb5-kqaaa-aaaaj-4thiq-cai/2f3537d1a2474f2fb92cb5f7de2ed70a.png"
  //type:image;action:localImage;p1(Text):${imageURL},p2(Text):${s3ObjectKey}
  //  ...
}
  • 表情包

{
  //  ...
  "type": "EMOJI",
  "payload": "type:image;action:customEmoji;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fim%2Femoji%2Flov6e-qqaaa-aaaaj-xykxq-cai%2Fac1dc2bf6bad48aeb1a502c0a28ee942.jpg,p2(Text):im/emoji/lov6e-qqaaa-aaaaj-xykxq-cai/ac1dc2bf6bad48aeb1a502c0a28ee942.jpg"
  //type:image;action:customEmoji;p1(Text):${imageURL},p2(Text):${s3ObjectKey}
  //  ...
}
  • 群转账

{
  //  ...
  "type": "BATCH_TRANSFER",
  "payload": "0xd84ed2b4deacbad8a568747fea45f3fc7f2d204595101f99f43006712f3ff290::USDT::20.0000::0x969f85053b44d7eCE8108094420Aba45149b7A3a::qzoo5-ryaaa-aaaaj-xs6ma-cai"
  //payload: 0xbaf915e778b044dbf62974d17bd2ab8875bccce8bd02c40dfc241d2893755851::USDT::5.0000::aukee-3yaaa-aaaah-qc3ya-cai::4xtgo-viaaa-aaaaj-aavyq-cai,ms5ob-iqaaa-aaaaj-aa2za-cai,kppoo-ozrgz-rtkn3-cgqyq-cai,meohx-szymu-ytezr-ugiza-cai,oazqd-ndeg4-ytqyj-zga2q-cai
  //payload: 0x71de31f73f34fc9af20f7ce3b35ec4e56bf25af476f99cb376779761c2fe91fa::USDT::1.0000::aukee-3yaaa-aaaah-qc3ya-cai::7zshb-ryaaa-aaaaj-aag3a-cai
  //  ...
} 
  • 公告

{
  //  ...
  "type": "TEXT",
  "payload": "It's an announcement"
  //  ...
}

7.4. Chat结构

{
  "id": "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
  "name": "test-group",
  "avatar": "",
  "channelType": "GROUP",
  "unreadMessageCount": 0,
  "type": "TEXT",
  "payload": "the message payload",
  "lastPostAt": "1665219499000"
}

8. 消息存储

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

Messages = [Message, Message, Message]

9. 合约

Name
Docs
Mumbai
Goerli
zkSync Era Testnet

Relation Name Service

0x04C23dDfE813d8D208bc8120b73F8EF30f423850

0xe4AA1f1E5be0A4E7F80CAC26C2Db1611C3E70c41

-

FollowRegister

0xA0cECa90d7A2033f5162dc73391cdB72272456E7

-

0x734dE4f1b3a1c0619ECB80e3a676a6cFabe72Adc

DaoRegister

0x153C3eF051C7665c9A9c1a718bF12bC8EE6b5115

-

0x7229Dc0117E8484D61137B7304b02b163beC912c

在Client端使用对消息进行加密

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

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

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

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

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

Client还需通过调用合约来管理和。

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

[]

[]

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

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

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

Client通过调用来进行成员管理。

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

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

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

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

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

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

Lit Protocol
Lit Protocol
Name Service
Chrome 插件
Follow合约
DAO合约
标准的DAO接口
Relation Name Service
消息结构
好友
群组
消息包
身份
消息
好友
accessCondition
消息结构
accessCondition
消息结构
Lit Protocol SDK
Client
Message
Proxy
Messages
name-service docs
follow docs
DAO docs
Message
Message
Chat
Relation Protocol
Relation Name Service
Relationship
Lit Protocol
Arweave
图 1-1 IM Architecture
图 1-2 Relation Name Service
图 1-3 Send Message
图 1-4 Follow
图 1-5 DAO
图 1-6 消息加密解密
图 1-7 消息存储