ComingChat 앱에 암호화 통합하기

저희 소셜 플랫폼에서는 암호화된 채팅과 Signal 암호화 프로토콜을 활용하여 디지털 자산을 안전하게 전송할 수 있는 수단을 제공합니다.

ComingChat 앱에 암호화 통합하기

Sui 네트워크에 구축된 탈중앙화 소셜 플랫폼인 컴잉챗은 다양한 기능 중 암호화된 채팅을 포함하며, 사용자에게 안전한 커뮤니케이션 수단을 제공합니다. 이 기능은 Signal, WhatsApp, Skype와 같은 앱에서 널리 사용되는 오픈 소스 소프트웨어인 Signal의 암호화 프로토콜을 활용합니다.

컴잉챗은 Sui 에서 종합적인 라이프스타일 경험을 제공합니다. ChatGPT로 강화된 생산성, 소셜 상호작용, 옴니체인 지갑을 결합하여 사용자에게 다양한 기능과 애플리케이션을 제공합니다.

신뢰할 수 있고 입증된 특성 외에도, Sui 네트워크의 계정 시스템과 완벽하게 호환되는 Signal 프로토콜을 ComingChat의 암호화된 통신을 위해 선택했습니다.

채팅 모드

ComingChat의 채팅 모듈은 사용자들이 서로 소통할 수 있는 세 가지 모드를 제공하여 다양한 개인정보 보호 및 보안 요구사항을 충족합니다. 이러한 모드는 다음과 같습니다:

  • 비공개 채팅: 이 모드는 두 사용자 간의 일대일 커뮤니케이션을 제공합니다. Signal 프로토콜은 이러한 대화를 암호화하여 기밀을 유지합니다.
  • 암호화된 그룹 채팅: 이 모드에서는 그룹이 높은 수준의 보안과 프라이버시를 유지하면서 커뮤니케이션을 할 수 있습니다. 비공개 채팅과 마찬가지로, Signal 프로토콜은 암호화된 그룹 채팅에서 메시지를 암호화합니다.
  • 암호화되지 않은 그룹 채팅: 이 모드에서는 메시지가 암호화되지는 않지만 최대 1,000명까지 그룹을 지원하므로 설정 및 관리가 쉽지만 메시지 콘텐츠에 대한 보호 수준은 낮습니다.

Signal 프로토콜은 트위터의 채팅 인프라 내에서 구현되며, 이 인프라는 트위터의 분산형 모멘트 (Dmens) 프로토콜을 사용합니다. 이 프로토콜은 게시하기, 좋아요 누르기, 답글 달기와 같은 일반적인 채팅 기능을 지원합니다. 또한 사용자가 서로 토큰이나 NFT를 보낼 수 있는 레드 엔벨로프 기능도 통합되어 있습니다.

암호화 구현

컴잉챗의 암호화 채팅 기능은 시그널 프로토콜의 이중 래칫 알고리즘을 기반으로 하며, 엔드투엔드 암호화를 제공하고 사용자 간의 안전한 커뮤니케이션을 보장합니다.

암호화된 채팅 기능을 구축하려면 다음 단계가 필요했습니다:

  1. 이중 래칫 알고리즘을 구현하여 메시지에 대한 종단 간 암호화를 활성화하여 의도한 수신자만 암호를 해독하고 읽을 수 있도록 합니다.
  2. 데이터 무결성과 보안을 보장하기 위해 Sui 네트워크에 암호화된 메시지를 저장합니다.
  3. 사용자가 보안 통신을 위해 공개 키를 교환하고 X3DH( 확장 타원 곡선 디파이-헬만 ) 키 계약 프로토콜을 사용하여 보안 세션을 설정할 수 있습니다.
ComingChat의 채팅 아키텍처 다이어그램
ComingChat의 암호화된 채팅 아키텍처는 사용자가 비공개 채팅을 선택하면 다른 사용자에게 전송된 메시지를 암호화한 다음 다른 사용자가 메시지를 읽을 수 있도록 암호를 해독할 수 있습니다.

더블 래칫 알고리즘

이중 래칫 알고리즘은 두 당사자가 공유된 비밀 키를 기반으로 암호화된 메시지를 교환하는 데 사용됩니다. 일반적으로 양 당사자는 X3DH와 같은 키 합의 프로토콜을 사용하여 공유 비밀 키에 동의합니다. 이 합의에 따라 양 당사자는 이중 래칫을 사용하여 암호화된 메시지를 주고받게 됩니다.

양 당사자는 모든 이중 래칫 메시지에 대해 새로운 키를 도출하여 이전 키는 이후 키에서 계산할 수 없도록 합니다. 또한 양 당사자는 메시지에 첨부된 Diffie-Hellman 공개값을 전송합니다. Diffie-Hellman 계산 결과는 파생된 키에 혼합되어 이전 키에서 이후 키를 계산할 수 없도록 합니다. 이러한 속성은 당사자의 키가 손상되는 경우 이전 또는 이후의 암호화된 메시지를 어느 정도 보호합니다.

개인정보 보호 강화 빨간색 봉투

안전한 온라인 거래를 위한 빨간 봉투라는 용어는 중국 명절이나 특별한 날에 실제 빨간 봉투에 돈을 담아 다른 사람에게 주는 관행에서 유래되었습니다. 저희는 암호화 채팅 기능의 보안 메시지 패킷을 위해 이 용어를 ComingChat에서 사용합니다.

Sui의 Move 언어 변형은 코어 Move 와 비교하여 레드 엔벨로프 컨트랙트를 개발하는 방식에서 몇 가지 독특한 차이를 허용합니다. 특히 Move 은 채팅 메시지 순서를 정하는 데 도움이 되는 트랜잭션 상태 동기화 반환과 객체 ID가 필요한 입력 함수 매개변수를 프로그래밍하는 데 변형이 필요합니다.

  • Sui 빨간색 패킷에는 증분이 없습니다. redPacketId하지만 redPacket ObjectIdSui의 데이터 모델에는 모든 객체에 ID가 있어야 하기 때문입니다.
  • Sui 레드 패킷은 오픈/클로즈 트랜잭션이 발행된 후 상태를 비동기적으로 가져올 필요가 없으며, 트랜잭션 반환 데이터의 이벤트에 따라 레드 봉투 상태를 업데이트할 수 있습니다.
  • 서버 측 노드는 사용자가 생성한 레드 패킷의 생성 상태를 비동기적으로 가져와야 하는데, 이는 해당 사용자의 노드가 서버 측 노드의 노드와 다를 수 있기 때문입니다.

아래의 핵심 계약 코드는 Sui 에서 Move 의 고유한 기능 및 요구 사항을 어떻게 고려했는지 보여줍니다. The 구성 객체에는 발신자, 수신자 및 관리자의 주소가 포함되어 있으며 거래 수수료도 정의되어 있습니다. 트랜잭션 수수료 레드패킷 정보 객체에는 코인 잔액, 전송 중인 토큰, 수신자 주소가 포함됩니다. 토큰의 레드패킷 이벤트 객체는 토큰 잔액을 추적합니다.

// Copyright 2022-2023 ComingChat Authors. Licensed under Apache-2.0 License.
module rp::red_packet {
	…
	struct Config has key {
	id: UID, 
	admin: address, 
	beneficiary: address, 
	owner: address, 
	count: u64, 
	fees: Bag
}

struct RedPacketInfo<phantom CoinType> has key,store {
	id: UID, 
	remain_coin: Balance<CoinType>, 
	remain count: u64, 
	beneficiary: address
}

// Event emitted when created/opened/closed a red packet.
struct RedPacketEvent has copy, drop {
	id: ID, 
	event_type: u8, 
	remain_count: u64, 
	remain balance: u64

// One-Time-Witness for the module.
struct RED_PACKET has drop {}

fun init (
	otw: RED_PACKET, 
	ct: &mut TxContext
) {
		…
}

public entry fun create<CoinType> (
	config: &mut Config,
	coins: vector<Coin<CoinType>>, 
	count: u64, 
	total_balance: u64, 
	ctx: &mut TxContext
) {
	// 1. check args
	…

}

public entry fun open<CoinType> (
	info: &mut RedPacketInfo<CoinType>, 
	lucky_accounts: vector<address>, 
	balances: vector<u64>, 
	ct: &mut TXContext
) {
…
}

public entry fun close<CoinType> (
	info: RedPacketInfo CoinType>, 
	ctx: &mut TxContext
) {
…
}

public entry fun withdraw<CoinType> (
	config: Smut Config, 
	ctx: &mut TxContext
) {
…
}

Sui 레드 패킷 열기/닫기 트랜잭션을 전송하면 브라우저에서 트랜잭션 상태를 비동기적으로 가져올 필요 없이 트랜잭션 결과를 응답에서 직접 가져와 데이터베이스 및 캐시 상태를 업데이트합니다.

사용자가 빨간 봉투를 생성하면 시스템은 비동기적으로 트랜잭션 생성 상태를 쿼리하여 이벤트에 따라 금액, 수량, 빨간 봉투 ID를 포함한 빨간 봉투 데이터를 가져옵니다.

컴잉챗 백엔드 아키텍처 다이어그램
저희 앱 아키텍처에서, 컴잉챗은 빨간봉투 만들기 이벤트를 Sui 네트워크에 스마트 컨트랙트로 전송합니다. 수신자의 행동에 따라 해당 계약의 상태를 처리합니다.

빨간색 패킷 상태 변경

컴잉챗이 Sui 트랜잭션을 전송하면 트랜잭션 결과를 바로 가져와서 열기/닫기 상태를 업데이트하기 위한 비동기 작업이 필요하지 않습니다:

  • 오픈 조건을 트리거한 후 관리자는 오픈 트랜잭션을 호출하고 오픈 트랜잭션 상태에 따라 레코드를 직접 성공으로 설정합니다.
  • 청산 조건을 트리거한 후 관리자는 청산 트랜잭션을 호출하고 청산 트랜잭션 상태에 따라 청산 또는 청산 실패로 직접 설정합니다.

개시/청산 거래가 실패하면 거래 재시도를 방지하기 위해 실패를 기록해야 하며, 이 경우 불필요한 가스 수수료가 발생합니다.

재봉투 거래의 흐름을 보여주는 다이어그램
트랜잭션 상태를 모니터링하여 성공적인 트랜잭션을 마감하거나 실패한 상태를 인정하고 자동 재시도를 중지하여 불필요한 가스 수수료를 방지합니다.

디멘스 프로토콜

저희는 사용자 식별, 콘텐츠 공유, 가치 공유와 같은 기능을 제공하기 위해 Sui 에 탈중앙화 모멘트 (Dmens) 프로토콜을 SDK로 구축했습니다. 이 프로토콜은 Sui 를 사용하여 사용자 데이터와 콘텐츠를 관리하고 SUI 토큰으로 가스 요금을 지불합니다. 사용자는 프로필을 생성하고, 콘텐츠를 게시하고, 다른 사용자를 팔로우하고, 그들과 상호 작용할 수 있습니다. 또한 이 프로토콜을 통해 사용자는 자신이 만든 콘텐츠를 고유하고 대체 불가능한 토큰(NFT)으로 변환하고 다양한 시나리오에 따라 여러 유형의 NFT를 발행할 수 있습니다.

이러한 시나리오에는 다음이 포함됩니다:

  1. 주요 오피니언 리더(KOL)는 팬들에게 가치 있는 대체 불가능한 토큰을 발행하여 팬 참여, 충성도, 수익을 높입니다.
  2. 프로젝트는 사용자 참여와 충성도를 높이고 생태계 발전을 촉진하기 위해 운영 활동을 위해 지분 증명 NFT를 발행합니다.
  3. 콘텐츠 크리에이터는 유료 NFT 모델을 통해 콘텐츠를 수익화하여 더 나은 콘텐츠 수익화와 더 많은 수익을 달성할 수 있습니다.
  4. 아티스트는 디지털 아트워크를 NFT로 변환하여 수집가나 투자자에게 판매합니다.

디멘스 아키텍처

저희는 공개 및 비공개 채팅 기능을 지원하도록 ComingChat에서 Dmens를 설계했습니다. 간단히 설명하자면, 사용자가 새 메시지나 답장 등 메시지를 작성하면 ComingChat의 채팅 기능( Sui)이 시작됩니다. GraphQL을 사용하여 사용자 프로필을 위해 오프체인 저장소를 쿼리하고, 메시지가 올바르게 정렬되도록 하기 위해 Dmens 인덱서 모듈을 사용합니다.

sui 와 DMENS 인덱서 간의 흐름 다이어그램
Dmens 아키텍처는 Sui, GraphQL 및 Dmens 인덱서를 사용하여 프로필을 만들거나 새 메시지를 게시하는 등의 사용자 작업을 처리합니다. 여기서 GraphQL은 저장된 프로필에 대한 데이터베이스 쿼리 역할을 합니다.

아래 스마트 컨트랙트 코드에서, 저희는 채팅 객체를 사용하여 메시지를 게시하고, 메시지와 같은 다른 메시지를 다시 게시하고, 기타 일반적인 채팅 기능을 사용할 수 있습니다.

//chat.move
module chat::chat {
	/// Sui Chat NFT (i.e., a post, retweet, like, chat message etc).
	struct Chat has key, store {
		id: UID,
		// The ID of the chat app.
		app_id: address,
		// Post's text.
		text: String,
		// Set if referencing an another object (i.e., due to a Like, Retweet, Reply etc).
		// We allow referencing any object type, not only Chat NFTs.
		ref id: Option<address>,
		// app-specific metadata. We do not enforce a metadata format and delegate this to app layer.
		metadata: vector<u8>,
	}

	/// Simple Chat.text getter.
	public fun text (chat: &Chat): String {
		chat.text
	}

	/// Mint (post) a Chat object.
	fun post internal (
		app_id: address, 
		text: vector<u8>, 
		ref_id: Option<address>, 
		metadata: vector<u8>, 
		ctx: &mut TxContext,
	) {
		…
	}


	/// Mint (post) a Chat object without referencing another object.
	public entry fun post (
		app_identifier: address, 
		text: vector<u8>, 
		metadata: vector<u8>, 
		ctx: &mut IxContext,
	) {
		post_internal(app_identifier, text, option::none (), metadata, ctx);
	}

	/// Mint (post) a Chat object and reference another object (i.e., to simulate retweet, reply, like, attach).
	/// TODO: Using address as app_identifier & 'ref_identifier type, because we cannot pass 'ID' to entry functions. Using vector<u8>' for text instead of String' for the same reason.
	public entry fun post_with_ref 
		app_identifier: address, 
		text: vector<u8>, 
		ref_identifier: address, 
		metadata: vector<u8>, 
		ctx: &mut TxContext,
	) {
		post_internal(app_identifier, text, some (ref_identifier), metadata, ctx);
	}

	/// Burn a Chat object.
	public entry fun burn (chat: Chat) {
		let Chat { id, app_id: _, text: _ , ref_id: _, metadata: _ } = chat;
		object::delete (id);
	}
}

위 코드 스니펫의 Chat 구조체는 채팅 메시지를 나타냅니다. 여기에는 다음과 같은 ID 필드가 있습니다. ref_id를 사용하여 채팅 메시지를 리트윗하거나 답글을 달거나 다른 메시지에 마음에 들어요를 표시할 수 있으며, 이는 코드에서 객체로 표시됩니다. 실제 채팅 메시지는 구조체의 텍스트 문자열입니다.

The 내부 게시물 함수는 새 채팅 메시지를 만듭니다. 이 함수는 모듈 내부에서 호출되어야 하므로 "내부"로 표시되어 있습니다. 이 함수에 의해 생성된 객체에는 ID 필드와 실제 메시지에 대한 텍스트 문자열이 있습니다. 이 객체의 ref_id 를 사용하면 기존 채팅에 대한 답글이나 좋아요로 다른 개체를 참조할 수 있습니다.

마찬가지로 공개 항목 게시 함수를 호출하는 내부 게시물 을 클릭하여 새 채팅을 만들 수 있습니다. 그러나 ref_id 이 기능은 사람들이 새 채팅을 시작하기 위한 것이므로 사용하지 않도록 설정합니다.

디멘스 인덱서 구조 설계

ComingChat의 암호화된 채팅 모듈은 오픈 소스 스트리밍 데이터베이스인 Redis를 오프체인 스토리지로 사용합니다. 메시지 큐를 처리하여 채팅 메시지가 순서대로 표시되도록 합니다.

Redis 스트림의 경우, 고객을 초기화하는 것으로 시작합니다.

함수 (r *BaseRedisCustomer) InitCustomer ( ) 오류 {
	...
}

Redis는 데이터를 RAM에 저장하므로 큐 데이터를 적절하고 정기적으로 잘라내야 합니다. 아래 코드 스니펫에서는 큐를 트리밍하는 함수를 정의합니다.

func (r *BaseRedisCustomer) TrimQueueList (ct context.Context) {
	r.wg.Add (1)
	defer funct( ) { 
		r.wg.Done ( )
	} ( )
	for {
		select {
		case <-ctx.Done ( ) :
			return
	}
	…
}

리스너 코드는 컨트랙트 주소로 트랜잭션을 필터링합니다. 아래 코드의 함수는 기존 오프체인 저장소를 Web3 앱과 통합하는 방법을 보여주는 좋은 예시입니다.

func (1 *ListenLastIxByCycle) cycleFetchTransactionNum(ct context. Context, tx chan<-TxDigest) {
	var (
		cursor *types.TransactionDigest
	)
	…
}

아래 코드 스니펫은 각각의 새 트랜잭션 다이제스트를 다음과 같은 큐로 푸시합니다. 트랜잭션 분석.

rpip.Evalsha (
	r.Context (), 
	r.script["pushNewT×DigestToStream" ],
	[ ]string{topic, fmt.Sprintf(PrefixChainLastDigest, chain, packageId) },
	"데이터", 
	preDigest, 
	digest,

	)

Lua 서버 측 스크립팅을 사용하여 여러 Redis 명령을 결합하여 트랜잭션 다이제스트의 연속성을 보장합니다.

local lastDigest = redis.call( 'get', KEYS[2])
local result = false
if (lastDigest ~= false) and (lastDigest == ARG[2])) 또는 ((lastDigest == false) and (ARGVI 21 == ')) then
	redis.call('xadd', KEYS[1], '*', ARGV[1], ARGV[3]) 
	redis.call ('set', KEYS[2], ARGV[3])
end
반환 참

인덱서는 사용자가 제출한 각 메시지를 수신할 때 다음과 같은 프로세스를 거칩니다:

크론 작업

  1. 테이블 queue_message에서 모든 소비자의 쿼리 실패 메시지를 확인합니다.
  2. 주제에 따라 재사용합니다. 작업이 재사용 임계값을 초과하는 경우 작업을 중지하고 수동으로 액세스해야 합니다.

소비자

  • 트랜잭션 분석
    1. 이 트랜잭션의 영향을 받은 객체를 쿼리하고 코인 객체를 제외합니다
    2. 영향을 받은 개체를 대기열로 푸시합니다.
  • 개체 업데이트
    1. 객체 세부 정보를 가져와 object_list 테이블에 생성 또는 업데이트
    2. 프로필 개체 필터링
    3. ChatGPT를 호출하는 트윗 필터링
  • 프로필 분석
    1. 프로필 개체 디코딩
  • GPT 답장
    1. Dmens 트윗 콘텐츠를 가져와 정규식을 사용하여 gpt 봇 주소와 일치시킵니다.

결론

암호화된 채팅은 Signal, WhatsApp, WeChat과 같은 앱에서 큰 인기를 얻고 있습니다. ComingChat에 이 기능을 포함시키는 것은 Sui 에서 소셜 플랫폼에 대한 기존 아이디어와 잘 맞았습니다. 암호화는 사용자의 프라이버시를 보호하여 악의적인 사용자가 대화를 엿듣지 못하도록 보장합니다. 또한 암호화된 채팅은 Sui 의 기능과도 일치하여 사용자들이 온라인 생활을 할 수 있는 독립적이고 안전한 플랫폼을 제공합니다.

저희의 기술 구현은 신뢰할 수 있는 시그널 프로토콜의 더블 래칫 알고리즘을 활용하여 기존 기술을 웹3 플랫폼에 어떻게 적용할 수 있는지 보여주었습니다. 위에 소개한 Dmens, 레드 패킷, 챗봇과 같은 고급 기능을 포함시킴으로써 컴잉챗에서 풍부한 사용자 경험을 제공할 수 있게 되었습니다.