Skip to content

Commit

Permalink
Merge pull request #1357 from WalletConnect/develop
Browse files Browse the repository at this point in the history
1.18.8
  • Loading branch information
jackpooleywc committed May 17, 2024
2 parents 1fcdbdc + 5338c26 commit d819f76
Show file tree
Hide file tree
Showing 16 changed files with 337 additions and 48 deletions.
28 changes: 19 additions & 9 deletions Example/Shared/Signer/ETHSigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Commons
import Web3

struct ETHSigner {

private let importAccount: ImportAccount

init(importAccount: ImportAccount) {
Expand All @@ -21,8 +20,21 @@ struct ETHSigner {
func personalSign(_ params: AnyCodable) -> AnyCodable {
let params = try! params.get([String].self)
let messageToSign = params[0]
let dataToHash = dataToHash(messageToSign)
let (v, r, s) = try! privateKey.sign(message: .init(hex: dataToHash.toHexString()))

// Determine if the message is hex-encoded or plain text
let dataToSign: Bytes
if messageToSign.hasPrefix("0x") {
// Hex-encoded message, remove "0x" and convert
let messageData = Data(hex: String(messageToSign.dropFirst(2)))
dataToSign = dataToHash(messageData)
} else {
// Plain text message, convert directly to data
let messageData = Data(messageToSign.utf8)
dataToSign = dataToHash(messageData)
}

// Sign the data
let (v, r, s) = try! privateKey.sign(message: .init(Data(dataToSign)))
let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16)
return AnyCodable(result)
}
Expand All @@ -45,12 +57,10 @@ struct ETHSigner {
return AnyCodable(result)
}

private func dataToHash(_ message: String) -> Bytes {
private func dataToHash(_ data: Data) -> Bytes {
let prefix = "\u{19}Ethereum Signed Message:\n"
let messageData = Data(hex: message)
let prefixData = (prefix + String(messageData.count)).data(using: .utf8)!
let prefixedMessageData = prefixData + messageData
let dataToHash: Bytes = .init(hex: prefixedMessageData.toHexString())
return dataToHash
let prefixData = (prefix + String(data.count)).data(using: .utf8)!
let prefixedMessageData = prefixData + data
return .init(hex: prefixedMessageData.toHexString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ final class SessionRequestPresenter: ObservableObject {
let validationStatus: VerifyContext.ValidationStatus?

var message: String {
let message = try? sessionRequest.params.get([String].self)
let decryptedMessage = message.map { String(data: Data(hex: $0.first ?? ""), encoding: .utf8) }
return (decryptedMessage ?? String(describing: sessionRequest.params.value)) ?? String(describing: sessionRequest.params.value)
guard let messages = try? sessionRequest.params.get([String].self),
let firstMessage = messages.first else {
return String(describing: sessionRequest.params.value)
}

// Attempt to decode the message if it's hex-encoded
let decodedMessage = String(data: Data(hex: firstMessage), encoding: .utf8)

// Return the decoded message if available, else return the original message
return decodedMessage?.isEmpty == false ? decodedMessage! : firstMessage
}


@Published var showError = false
@Published var errorMessage = "Error"
Expand Down
5 changes: 4 additions & 1 deletion Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ public class NotifyClient {
}

public func unregister(account: Account) async throws {
try await identityClient.unregister(account: account)
if identityClient.isIdentityRegistered(account: account) {
try await identityClient.unregister(account: account)
}
notifyWatcherAgreementKeysProvider.removeAgreement(account: account)
try notifyStorage.clearDatabase(account: account)
try await pushClient.unregister()
notifyAccountProvider.logout()
subscriptionWatcher.stop()
}
Expand Down
10 changes: 9 additions & 1 deletion Sources/WalletConnectPush/PushClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@ import Combine

public class PushClient: PushClientProtocol {
private let registerService: PushRegisterService
private let unregisterService: UnregisterService
private let logger: ConsoleLogging

public var logsPublisher: AnyPublisher<Log, Never> {
return logger.logsPublisher
}

init(registerService: PushRegisterService, logger: ConsoleLogging) {
init(registerService: PushRegisterService,
logger: ConsoleLogging,
unregisterService: UnregisterService) {
self.registerService = registerService
self.unregisterService = unregisterService
self.logger = logger
}

public func register(deviceToken: Data, enableEncrypted: Bool = false) async throws {
try await registerService.register(deviceToken: deviceToken, alwaysRaw: enableEncrypted)
}

public func unregister() async throws {
try await unregisterService.unregister()
}

#if DEBUG
public func register(deviceToken: String) async throws {
try await registerService.register(deviceToken: deviceToken, alwaysRaw: true)
Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectPush/PushClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public struct PushClientFactory {
let pushAuthenticator = PushAuthenticator(clientIdStorage: clientIdStorage, pushHost: pushHost)

let registerService = PushRegisterService(httpClient: httpClient, projectId: projectId, clientIdStorage: clientIdStorage, pushAuthenticator: pushAuthenticator, logger: logger, environment: environment)
let unregisterService = UnregisterService(httpClient: httpClient, projectId: projectId, clientIdStorage: clientIdStorage, pushAuthenticator: pushAuthenticator, logger: logger, pushHost: pushHost, environment: environment)

return PushClient(registerService: registerService, logger: logger)
return PushClient(registerService: registerService, logger: logger, unregisterService: unregisterService)
}
}
55 changes: 55 additions & 0 deletions Sources/WalletConnectPush/UnregisterService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Foundation

actor UnregisterService {
private let httpClient: HTTPClient
private let projectId: String
private let logger: ConsoleLogging
private let environment: APNSEnvironment
private let pushAuthenticator: PushAuthenticating
private let clientIdStorage: ClientIdStoring
private let pushHost: String

init(httpClient: HTTPClient,
projectId: String,
clientIdStorage: ClientIdStoring,
pushAuthenticator: PushAuthenticating,
logger: ConsoleLogging,
pushHost: String,
environment: APNSEnvironment) {
self.httpClient = httpClient
self.clientIdStorage = clientIdStorage
self.pushAuthenticator = pushAuthenticator
self.projectId = projectId
self.logger = logger
self.pushHost = pushHost
self.environment = environment
}

func unregister() async throws {
let pushAuthToken = try pushAuthenticator.createAuthToken()
let clientId = try clientIdStorage.getClientId()

guard let url = URL(string: "https://\(pushHost)/\(projectId)/clients/\(clientId)") else {
logger.error("Invalid URL")
return
}

var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.addValue("\(pushAuthToken)", forHTTPHeaderField: "Authorization")

do {
let (_, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
logger.error("Failed to unregister from Push Server")
throw NSError(domain: "UnregisterService", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to unregister"])
}

logger.debug("Successfully unregistered from Push Server")
} catch {
logger.error("Push Server unregistration error: \(error.localizedDescription)")
throw error
}
}
}
2 changes: 1 addition & 1 deletion Sources/WalletConnectRelay/PackageConfig.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version": "1.18.7"}
{"version": "1.18.8"}
19 changes: 15 additions & 4 deletions Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@ final class SessionEngine {

private let sessionStore: WCSessionStorage
private let networkingInteractor: NetworkInteracting
private let historyService: HistoryService
private let historyService: HistoryServiceProtocol
private let verifyContextStore: CodableStore<VerifyContext>
private let verifyClient: VerifyClientProtocol
private let kms: KeyManagementServiceProtocol
private var publishers = [AnyCancellable]()
private let logger: ConsoleLogging
private let sessionRequestsProvider: SessionRequestsProvider
private let invalidRequestsSanitiser: InvalidRequestsSanitiser

init(
networkingInteractor: NetworkInteracting,
historyService: HistoryService,
historyService: HistoryServiceProtocol,
verifyContextStore: CodableStore<VerifyContext>,
verifyClient: VerifyClientProtocol,
kms: KeyManagementServiceProtocol,
sessionStore: WCSessionStorage,
logger: ConsoleLogging,
sessionRequestsProvider: SessionRequestsProvider
sessionRequestsProvider: SessionRequestsProvider,
invalidRequestsSanitiser: InvalidRequestsSanitiser
) {
self.networkingInteractor = networkingInteractor
self.historyService = historyService
Expand All @@ -45,15 +47,23 @@ final class SessionEngine {
self.sessionStore = sessionStore
self.logger = logger
self.sessionRequestsProvider = sessionRequestsProvider
self.invalidRequestsSanitiser = invalidRequestsSanitiser

setupConnectionSubscriptions()
setupRequestSubscriptions()
setupResponseSubscriptions()
setupUpdateSubscriptions()
setupExpirationSubscriptions()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
sessionRequestsProvider.emitRequestIfPending()
self?.sessionRequestsProvider.emitRequestIfPending()
}

removeInvalidSessionRequests()
}

private func removeInvalidSessionRequests() {
let sessionTopics = Set(sessionStore.getAll().map(\.topic))
invalidRequestsSanitiser.removeInvalidSessionRequests(validSessionTopics: sessionTopics)
}

func hasSession(for topic: String) -> Bool {
Expand Down Expand Up @@ -192,6 +202,7 @@ private extension SessionEngine {

func setupExpirationSubscriptions() {
sessionStore.onSessionExpiration = { [weak self] session in
self?.historyService.removePendingRequest(topic: session.topic)
self?.kms.deletePrivateKey(for: session.selfParticipant.publicKey)
self?.kms.deleteAgreementSecret(for: session.topic)
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/WalletConnectSign/RejectionReason.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public enum RejectionReason {
case unsupportedChains
case unsupportedMethods
case unsupportedAccounts
case upsupportedEvents
case unsupportedEvents
}

internal extension RejectionReason {
Expand All @@ -18,7 +18,7 @@ internal extension RejectionReason {
return SignReasonCode.unsupportedChains
case .unsupportedMethods:
return SignReasonCode.userRejectedMethods
case .upsupportedEvents:
case .unsupportedEvents:
return SignReasonCode.userRejectedEvents
case .unsupportedAccounts:
return SignReasonCode.unsupportedAccounts
Expand All @@ -36,7 +36,7 @@ public extension RejectionReason {
case .requiredMethodsNotSatisfied:
self = .unsupportedMethods
case .requiredEventsNotSatisfied:
self = .upsupportedEvents
self = .unsupportedEvents
case .emptySessionNamespacesForbidden:
self = .unsupportedAccounts
}
Expand Down
49 changes: 46 additions & 3 deletions Sources/WalletConnectSign/Services/HistoryService.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import Foundation

final class HistoryService {
protocol HistoryServiceProtocol {

func getSessionRequest(id: RPCID) -> (request: Request, context: VerifyContext?)?

func removePendingRequest(topic: String)

func getPendingRequests() -> [(request: Request, context: VerifyContext?)]

func getPendingRequestsSortedByTimestamp() -> [(request: Request, context: VerifyContext?)]
}

final class HistoryService: HistoryServiceProtocol {

private let history: RPCHistory
private let verifyContextStore: CodableStore<VerifyContext>
Expand All @@ -12,14 +23,20 @@ final class HistoryService {
self.history = history
self.verifyContextStore = verifyContextStore
}

public func getSessionRequest(id: RPCID) -> (request: Request, context: VerifyContext?)? {
func getSessionRequest(id: RPCID) -> (request: Request, context: VerifyContext?)? {
guard let record = history.get(recordId: id) else { return nil }
guard let (request, recordId, _) = mapRequestRecord(record) else {
return nil
}
return (request, try? verifyContextStore.get(key: recordId.string))
}

func removePendingRequest(topic: String) {
DispatchQueue.global(qos: .background).async { [unowned self] in
history.deleteAll(forTopic: topic)
}
}

func getPendingRequests() -> [(request: Request, context: VerifyContext?)] {
getPendingRequestsSortedByTimestamp()
Expand Down Expand Up @@ -80,3 +97,29 @@ private extension HistoryService {
return (mappedRequest, record.id, record.timestamp)
}
}

#if DEBUG
final class MockHistoryService: HistoryServiceProtocol {

var removePendingRequestCalled: (String) -> Void = { _ in }

var pendingRequests: [(request: Request, context: VerifyContext?)] = []

func removePendingRequest(topic: String) {
pendingRequests.removeAll(where: { $0.request.topic == topic })
removePendingRequestCalled(topic)
}

func getSessionRequest(id: JSONRPC.RPCID) -> (request: Request, context: VerifyContext?)? {
fatalError("Unimplemented")
}

func getPendingRequests() -> [(request: Request, context: VerifyContext?)] {
pendingRequests
}

func getPendingRequestsSortedByTimestamp() -> [(request: Request, context: VerifyContext?)] {
fatalError("Unimplemented")
}
}
#endif
20 changes: 20 additions & 0 deletions Sources/WalletConnectSign/Services/InvalidRequestsSanitiser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

import Foundation

final class InvalidRequestsSanitiser {
private let historyService: HistoryServiceProtocol
private let history: RPCHistoryProtocol

init(historyService: HistoryServiceProtocol, history: RPCHistoryProtocol) {
self.historyService = historyService
self.history = history
}

func removeInvalidSessionRequests(validSessionTopics: Set<String>) {
let pendingRequests = historyService.getPendingRequests()
let invalidTopics = Set(pendingRequests.map { $0.request.topic }).subtracting(validSessionTopics)
if !invalidTopics.isEmpty {
history.deleteAll(forTopics: Array(invalidTopics))
}
}
}
4 changes: 2 additions & 2 deletions Sources/WalletConnectSign/Sign/SessionRequestsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Combine
import Foundation

class SessionRequestsProvider {
private let historyService: HistoryService
private let historyService: HistoryServiceProtocol
private var sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: VerifyContext?), Never>()
private var lastEmitTime: Date?
private let debounceInterval: TimeInterval = 1
Expand All @@ -11,7 +11,7 @@ class SessionRequestsProvider {
sessionRequestPublisherSubject.eraseToAnyPublisher()
}

init(historyService: HistoryService) {
init(historyService: HistoryServiceProtocol) {
self.historyService = historyService
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectSign/Sign/SignClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public struct SignClientFactory {
let historyService = HistoryService(history: rpcHistory, verifyContextStore: verifyContextStore)
let verifyClient = VerifyClientFactory.create()
let sessionRequestsProvider = SessionRequestsProvider(historyService: historyService)
let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyContextStore: verifyContextStore, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger, sessionRequestsProvider: sessionRequestsProvider)
let invalidRequestsSanitiser = InvalidRequestsSanitiser(historyService: historyService, history: rpcHistory)
let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyContextStore: verifyContextStore, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger, sessionRequestsProvider: sessionRequestsProvider, invalidRequestsSanitiser: invalidRequestsSanitiser)
let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let sessionExtendRequester = SessionExtendRequester(sessionStore: sessionStore, networkingInteractor: networkingClient)
Expand Down
Loading

0 comments on commit d819f76

Please sign in to comment.