From fc1a8a6ac6f5ef09bd2b72bfbacba163878d06e0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Jan 2024 19:41:45 +0300 Subject: [PATCH 1/5] Delete on resolve --- .../NetworkingInteractor.swift | 6 +++--- .../WalletConnectUtils/RPCHistory/RPCHistory.swift | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index f311cdc5b..b428a77f5 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -171,9 +171,10 @@ public class NetworkingInteractor: NetworkInteracting { } public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { - try rpcHistory.resolve(response) + try rpcHistory.validate(response) let message = try serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag, prompt: protocolMethod.responseConfig.prompt, ttl: protocolMethod.responseConfig.ttl) + try rpcHistory.resolve(response) } public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { @@ -216,8 +217,7 @@ public class NetworkingInteractor: NetworkInteracting { private func handleResponse(topic: String, response: RPCResponse, publishedAt: Date, derivedTopic: String?) { do { - try rpcHistory.resolve(response) - let record = rpcHistory.get(recordId: response.id!)! + let record = try rpcHistory.resolve(response) responsePublisherSubject.send((topic, record.request, response, publishedAt, derivedTopic)) } catch { logger.debug("Handle json rpc response error: \(error)") diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 4fc00aebe..4cf815884 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -43,17 +43,22 @@ public final class RPCHistory { @discardableResult public func resolve(_ response: RPCResponse) throws -> Record { + let record = try validate(response) + storage.delete(forKey: "\(record.id)") + return record + } + + @discardableResult + public func validate(_ response: RPCResponse) throws -> Record { guard let id = response.id else { throw HistoryError.unidentifiedResponse } - guard var record = get(recordId: id) else { + guard let record = get(recordId: id) else { throw HistoryError.requestMatchingResponseNotFound } guard record.response == nil else { throw HistoryError.responseDuplicateNotAllowed } - record.response = response - storage.set(record, forKey: "\(record.id)") return record } From aaa925b0ce7c7fb4fbfb4c14e579747941f16856 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Jan 2024 20:21:16 +0300 Subject: [PATCH 2/5] removeOutdated --- .../RPCHistory/RPCHistory.swift | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 4cf815884..5f02a10c8 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -1,3 +1,5 @@ +import Foundation + public final class RPCHistory { public struct Record: Codable { @@ -9,7 +11,8 @@ public final class RPCHistory { public let topic: String let origin: Origin public let request: RPCRequest - public var response: RPCResponse? + public let response: RPCResponse? + public var timestamp: Date? } enum HistoryError: Error { @@ -24,10 +27,13 @@ public final class RPCHistory { init(keyValueStore: CodableStore) { self.storage = keyValueStore + + removeOutdated() } public func get(recordId: RPCID) -> Record? { - try? storage.get(key: recordId.string) + // FOR TESTING!!! + try! storage.get(key: recordId.string) } public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws { @@ -37,7 +43,7 @@ public final class RPCHistory { guard get(recordId: id) == nil else { throw HistoryError.requestDuplicateNotAllowed } - let record = Record(id: id, topic: topic, origin: origin, request: request) + let record = Record(id: id, topic: topic, origin: origin, request: request, response: nil, timestamp: Date()) storage.set(record, forKey: "\(record.id)") } @@ -100,3 +106,23 @@ public final class RPCHistory { storage.getAll().filter { $0.response == nil } } } + +private extension RPCHistory { + + func removeOutdated() { + let records = storage.getAll() + + let thirtyDays: TimeInterval = 30*86400 + + for var record in records { + if let timestamp = record.timestamp { + if timestamp.distance(to: Date()) > thirtyDays { + storage.delete(forKey: record.id.string) + } + } else { + record.timestamp = Date() + storage.set(record, forKey: "\(record.id)") + } + } + } +} From a41e93c630691d5b65cc163012c95fc04b3db1f6 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Jan 2024 20:21:49 +0300 Subject: [PATCH 3/5] Force unwrap removed --- Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 5f02a10c8..44dfdad68 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -32,8 +32,7 @@ public final class RPCHistory { } public func get(recordId: RPCID) -> Record? { - // FOR TESTING!!! - try! storage.get(key: recordId.string) + try? storage.get(key: recordId.string) } public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws { From 7d709770c034d19a160f4d828155bae5bc698910 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Jan 2024 11:52:01 +0300 Subject: [PATCH 4/5] RPCHistory tests resplved --- Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift | 2 +- Tests/WalletConnectUtilsTests/RPCHistoryTests.swift | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 44dfdad68..84f93768f 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -106,7 +106,7 @@ public final class RPCHistory { } } -private extension RPCHistory { +extension RPCHistory { func removeOutdated() { let records = storage.getAll() diff --git a/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift index 37b05ccdd..82becd7b8 100644 --- a/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift +++ b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift @@ -35,10 +35,8 @@ final class RPCHistoryTests: XCTestCase { try sut.set(requestB, forTopic: String.randomTopic(), emmitedBy: .local) try sut.resolve(responseA) try sut.resolve(responseB) - let recordA = sut.get(recordId: requestA.id!) - let recordB = sut.get(recordId: requestB.id!) - XCTAssertEqual(recordA?.response, responseA) - XCTAssertEqual(recordB?.response, responseB) + XCTAssertNil(sut.get(recordId: requestA.id!)) + XCTAssertNil(sut.get(recordId: requestB.id!)) } func testDelete() throws { @@ -95,7 +93,7 @@ final class RPCHistoryTests: XCTestCase { } func testResolveDuplicateResponse() throws { - let expectedError = RPCHistory.HistoryError.responseDuplicateNotAllowed + let expectedError = RPCHistory.HistoryError.requestMatchingResponseNotFound let request = RPCRequest.stub() let responseA = RPCResponse(matchingRequest: request, result: true) From 7195db91b827fbfc4548a121f9b3828dfae98e31 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Jan 2024 12:38:49 +0300 Subject: [PATCH 5/5] Remove outdated tests --- .../RPCHistory/RPCHistory.swift | 4 ++-- Sources/WalletConnectUtils/TimeProvider.swift | 12 ++++++++++ .../RPCHistoryTests.swift | 23 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 Sources/WalletConnectUtils/TimeProvider.swift diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 84f93768f..b310586d0 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -35,14 +35,14 @@ public final class RPCHistory { try? storage.get(key: recordId.string) } - public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws { + public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin, time: TimeProvider = DefaultTimeProvider()) throws { guard let id = request.id else { throw HistoryError.unidentifiedRequest } guard get(recordId: id) == nil else { throw HistoryError.requestDuplicateNotAllowed } - let record = Record(id: id, topic: topic, origin: origin, request: request, response: nil, timestamp: Date()) + let record = Record(id: id, topic: topic, origin: origin, request: request, response: nil, timestamp: time.currentDate) storage.set(record, forKey: "\(record.id)") } diff --git a/Sources/WalletConnectUtils/TimeProvider.swift b/Sources/WalletConnectUtils/TimeProvider.swift new file mode 100644 index 000000000..86732b6f0 --- /dev/null +++ b/Sources/WalletConnectUtils/TimeProvider.swift @@ -0,0 +1,12 @@ +import Foundation + +public protocol TimeProvider { + var currentDate: Date { get } +} + +public struct DefaultTimeProvider: TimeProvider { + public init() {} + public var currentDate: Date { + return Date() + } +} diff --git a/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift index 82becd7b8..b4023eaff 100644 --- a/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift +++ b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift @@ -105,4 +105,27 @@ final class RPCHistoryTests: XCTestCase { XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) } } + + func testRemoveOutdated() throws { + let request1 = RPCRequest.stub() + let request2 = RPCRequest.stub() + + let time1 = TestTimeProvider(currentDate: .distantPast) + let time2 = TestTimeProvider(currentDate: Date()) + + try sut.set(request1, forTopic: .randomTopic(), emmitedBy: .local, time: time1) + try sut.set(request2, forTopic: .randomTopic(), emmitedBy: .local, time: time2) + + XCTAssertEqual(sut.get(recordId: request1.id!)?.request, request1) + XCTAssertEqual(sut.get(recordId: request2.id!)?.request, request2) + + sut.removeOutdated() + + XCTAssertEqual(sut.get(recordId: request1.id!)?.request, nil) + XCTAssertEqual(sut.get(recordId: request2.id!)?.request, request2) + } + + struct TestTimeProvider: TimeProvider { + var currentDate: Date + } }