Skip to content

Commit

Permalink
Merge pull request #322 from WalletConnect/kotlin-sync
Browse files Browse the repository at this point in the history
[Chat] sync chat sdk with kotlin implementation and add reject method
  • Loading branch information
llbartekll authored Jul 12, 2022
2 parents df4711a + 08b49ee commit 566fed3
Show file tree
Hide file tree
Showing 14 changed files with 111 additions and 25 deletions.
6 changes: 3 additions & 3 deletions Example/IntegrationTests/Chat/ChatTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class ChatTests: XCTestCase {
}.store(in: &publishers)
wait(for: [inviteExpectation], timeout: 4)
}
//

// func testAcceptAndCreateNewThread() async {
// await waitClientsConnected()
// let newThreadInviterExpectation = expectation(description: "new thread on inviting client expectation")
Expand All @@ -70,7 +70,7 @@ final class ChatTests: XCTestCase {
// try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount)
//
// invitee.invitePublisher.sink { [unowned self] invite in
// Task {try! await invitee.accept(inviteId: invite.pubKey)}
// Task {try! await invitee.accept(inviteId: invite.id)}
// }.store(in: &publishers)
//
// invitee.newThreadPublisher.sink { _ in
Expand All @@ -95,7 +95,7 @@ final class ChatTests: XCTestCase {
// try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount)
//
// invitee.invitePublisher.sink { [unowned self] invite in
// Task {try! await invitee.accept(inviteId: invite.pubKey)}
// Task {try! await invitee.accept(inviteId: invite.id)}
// }.store(in: &publishers)
//
// invitee.newThreadPublisher.sink { [unowned self] thread in
Expand Down
4 changes: 2 additions & 2 deletions Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ final class ChatService {
}

func accept(invite: Invite) async throws {
try await client.accept(inviteId: invite.pubKey)
try await client.accept(inviteId: invite.id)
}

func reject(invite: Invite) async throws {
try await client.reject(inviteId: invite.pubKey)
try await client.reject(inviteId: invite.id)
}

func invite(peerPubkey publicKey: String, peerAccount: Account, message: String, selfAccount: Account) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private extension InviteListPresenter {

func loadInvites() async {
let invites = await interactor.getInvites()
self.invites = invites.sorted(by: { $0.pubKey < $1.pubKey })
self.invites = invites.sorted(by: { $0.publicKey < $1.publicKey })
.map { InviteViewModel(invite: $0) }
}
}
8 changes: 5 additions & 3 deletions Sources/Chat/ChatClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ChatClient {
private let messagingService: MessagingService
private let invitationHandlingService: InvitationHandlingService
private let inviteService: InviteService
private let leaveService: LeaveService
private let kms: KeyManagementService
private let threadStore: Database<Thread>
private let messagesStore: Database<Message>
Expand All @@ -36,7 +37,7 @@ public class ChatClient {
public init(registry: Registry,
relayClient: RelayClient,
kms: KeyManagementService,
logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off),
logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug),
keyValueStorage: KeyValueStorage) {
let topicToInvitationPubKeyStore = CodableStore<String>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.topicToInvitationPubKey.rawValue)
self.registry = registry
Expand All @@ -63,6 +64,7 @@ public class ChatClient {
kms: kms,
threadStore: threadStore,
logger: logger)
self.leaveService = LeaveService()
self.messagesStore = Database<Message>()
self.messagingService = MessagingService(
networkingInteractor: networkingInteractor,
Expand Down Expand Up @@ -102,7 +104,7 @@ public class ChatClient {
}

public func reject(inviteId: String) async throws {

try await invitationHandlingService.reject(inviteId: inviteId)
}

/// Sends a chat message to an active chat thread
Expand All @@ -120,7 +122,7 @@ public class ChatClient {
}

public func leave(topic: String) async throws {
fatalError("not implemented")
try await leaveService.leave(topic: topic)
}

public func getInvites(account: Account) -> [Invite] {
Expand Down
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions Sources/Chat/ProtocolServices/Common/LeaveService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

import Foundation

class LeaveService {
func leave(topic: String) async throws {
fatalError("not implemented")
}
}
6 changes: 4 additions & 2 deletions Sources/Chat/ProtocolServices/Common/MessagingService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class MessagingService {
// TODO - manage author account
let thread = await threadStore.first {$0.topic == topic}
guard let authorAccount = thread?.selfAccount else { throw Errors.threadDoNotExist}
let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: JsonRpcID.generate())
let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp)
let request = JSONRPCRequest<ChatRequestParams>(params: .message(message))
try await networkingInteractor.request(request, topic: topic, envelopeType: .type0)
Task(priority: .background) {
Expand All @@ -53,7 +54,8 @@ class MessagingService {
private func setUpRequestHandling() {
networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in
switch subscriptionPayload.request.params {
case .message(let message):
case .message(var message):
message.topic = subscriptionPayload.topic
handleMessage(message)
default:
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class InvitationHandlingService {

let selfThreadPubKey = try kms.createX25519KeyPair()

let inviteResponse = InviteResponse(pubKey: selfThreadPubKey.hexRepresentation)
let inviteResponse = InviteResponse(publicKey: selfThreadPubKey.hexRepresentation)

let response = JsonRpcResult.response(JSONRPCResponse<AnyCodable>(id: payload.request.id, result: AnyCodable(inviteResponse)))

Expand All @@ -52,24 +52,43 @@ class InvitationHandlingService {

try await networkingInteractor.respond(topic: responseTopic, response: response)

let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: invite.pubKey)
let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: invite.publicKey)

let threadTopic = threadAgreementKeys.derivedTopic()

try kms.setSymmetricKey(threadAgreementKeys.sharedKey, for: threadTopic)

try await networkingInteractor.subscribe(topic: threadTopic)

logger.debug("Accepting an invite")
logger.debug("Accepting an invite on topic: \(threadTopic)")

// TODO - derive account
let selfAccount = Account("eip155:56:0xe5EeF1368781911d265fDB6946613dA61915a501")!
let thread = Thread(topic: threadTopic, selfAccount: selfAccount, peerAccount: invite.account)
await threadsStore.add(thread)

invitePayloadStore.delete(forKey: inviteId)

onNewThread?(thread)
}

func reject(inviteId: String) async throws {

guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound }

guard case .invite(let invite) = payload.request.params else {return}

let responseTopic = try getInviteResponseTopic(payload, invite)

//TODO - error not in specs yet
let error = JSONRPCErrorResponse.Error(code: 0, message: "user rejected")
let response = JsonRpcResult.error(JSONRPCErrorResponse(id: payload.request.id, error: error))

try await networkingInteractor.respond(topic: responseTopic, response: response)

invitePayloadStore.delete(forKey: inviteId)
}

private func setUpRequestHandling() {
networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in
switch subscriptionPayload.request.params {
Expand All @@ -87,7 +106,7 @@ class InvitationHandlingService {

private func handleInvite(_ invite: Invite, _ payload: RequestSubscriptionPayload) throws {
logger.debug("did receive an invite")
invitePayloadStore.set(payload, forKey: invite.pubKey)
invitePayloadStore.set(payload, forKey: invite.publicKey)
onInvite?(invite)
}

Expand All @@ -101,7 +120,7 @@ class InvitationHandlingService {

let selfPubKey = try AgreementPublicKey(hex: selfPubKeyHex)

let agreementKeysI = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: invite.pubKey)
let agreementKeysI = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: invite.publicKey)

// agreement keys already stored by serializer
let responseTopic = agreementKeysI.derivedTopic()
Expand Down
10 changes: 7 additions & 3 deletions Sources/Chat/ProtocolServices/Inviter/InviteService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ class InviteService {
}

var peerAccount: Account!

func invite(peerPubKey: String, peerAccount: Account, openingMessage: String, account: Account) async throws {
// TODO ad storage
self.peerAccount = peerAccount
let selfPubKeyY = try kms.createX25519KeyPair()
let invite = Invite(message: openingMessage, account: account, pubKey: selfPubKeyY.hexRepresentation)
let invite = Invite(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation)
let symKeyI = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: peerPubKey)
let inviteTopic = try AgreementPublicKey(hex: peerPubKey).rawRepresentation.sha256().toHexString()

// overrides on invite toipic
try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic)

let request = JSONRPCRequest<ChatRequestParams>(params: .invite(invite))
Expand All @@ -42,7 +45,6 @@ class InviteService {
try kms.setSymmetricKey(symKeyI.sharedKey, for: responseTopic)

try await networkingInteractor.subscribe(topic: responseTopic)

try await networkingInteractor.request(request, topic: inviteTopic, envelopeType: .type1(pubKey: selfPubKeyY.rawRepresentation))

logger.debug("invite sent on topic: \(inviteTopic)")
Expand All @@ -67,7 +69,9 @@ class InviteService {
let inviteResponse = try jsonrpc.result.get(InviteResponse.self)
logger.debug("Invite has been accepted")
guard case .invite(let inviteParams) = response.requestParams else { return }
Task { try await createThread(selfPubKeyHex: inviteParams.pubKey, peerPubKey: inviteResponse.pubKey, account: inviteParams.account, peerAccount: peerAccount)}
Task(priority: .background) {
try await createThread(selfPubKeyHex: inviteParams.publicKey, peerPubKey: inviteResponse.publicKey, account: inviteParams.account, peerAccount: peerAccount)
}
} catch {
logger.debug("Handling invite response has failed")
}
Expand Down
31 changes: 29 additions & 2 deletions Sources/Chat/Types/ChatRequestParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,45 @@ import Foundation
import WalletConnectUtils

enum ChatRequestParams: Codable, Equatable {
enum Errors: Error {
case decoding
}
case invite(Invite)
case message(Message)

private enum CodingKeys: String, CodingKey {
case invite
case message
}

func encode(to encoder: Encoder) throws {
switch self {
case .invite(let invite):
try invite.encode(to: encoder)
case .message(let message):
try message.encode(to: encoder)
}
}

init(from decoder: Decoder) throws {
if let invite = try? Invite(from: decoder) {
self = .invite(invite)
} else if let massage = try? Message(from: decoder) {
self = .message(massage)
} else {
throw Errors.decoding
}
}
}

extension JSONRPCRequest {
init(id: Int64 = JsonRpcID.generate(), params: T) where T == ChatRequestParams {
var method: String!
switch params {
case .invite:
method = "invite"
method = "wc_chatInvite"
case .message:
method = "message"
method = "wc_chatMessage"
}
self.init(id: id, method: method, params: params)
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/Chat/Types/InviteParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import WalletConnectUtils
import Foundation

struct InviteResponse: Codable {
let pubKey: String
let publicKey: String
}

public struct Invite: Codable, Equatable {
public var id: String {
return publicKey
}
public let message: String
public let account: Account
public let pubKey: String
public let publicKey: String
}
23 changes: 22 additions & 1 deletion Sources/Chat/Types/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@ import Foundation
import WalletConnectUtils

public struct Message: Codable, Equatable {
public var topic: String
internal init(topic: String? = nil, message: String, authorAccount: Account, timestamp: Int64) {
self.topic = topic
self.message = message
self.authorAccount = authorAccount
self.timestamp = timestamp
}

public var topic: String?
public let message: String
public let authorAccount: Account
public let timestamp: Int64

enum CodingKeys: String, CodingKey {
case topic
case message
case authorAccount
case timestamp
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(message, forKey: .message)
try container.encode(authorAccount, forKey: .authorAccount)
try container.encode(timestamp, forKey: .timestamp)
}
}
2 changes: 1 addition & 1 deletion Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public struct JSONRPCRequest<T: Codable&Equatable>: Codable, Equatable {
public let method: String
public let params: T

enum CodingKeys: CodingKey {
public enum CodingKeys: CodingKey {
case id
case jsonrpc
case method
Expand Down

0 comments on commit 566fed3

Please sign in to comment.