Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.18.8 #1357

Merged
merged 16 commits into from
May 17, 2024
Merged

1.18.8 #1357

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading