Skip to content

Commit

Permalink
Cleanup Service + SequenceStore refactor (#241)
Browse files Browse the repository at this point in the history
* Cleanup service

* Clean sessionToPairingTopic

* SequenceStore refactor

* Rename KeyValueStore -> CodableStore
  • Loading branch information
flypaper0 committed May 31, 2022
1 parent a32b649 commit 6bec5ef
Show file tree
Hide file tree
Showing 23 changed files with 151 additions and 61 deletions.
5 changes: 4 additions & 1 deletion Sources/WalletConnectKMS/Crypto/KeyManagementService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public protocol KeyManagementServiceProtocol {
func deletePrivateKey(for publicKey: String)
func deleteAgreementSecret(for topic: String)
func deleteSymmetricKey(for topic: String)
func deleteAll() throws
func performKeyAgreement(selfPublicKey: AgreementPublicKey, peerPublicKey hexRepresentation: String) throws -> AgreementKeys
}

Expand Down Expand Up @@ -122,7 +123,9 @@ public class KeyManagementService: KeyManagementServiceProtocol {
return try KeyManagementService.generateAgreementKey(from: privateKey, peerPublicKey: hexRepresentation)
}


public func deleteAll() throws {
try keychain.deleteAll()
}

static func generateAgreementKey(from privateKey: AgreementPrivateKey, peerPublicKey hexRepresentation: String) throws -> AgreementKeys {
let peerPublicKey = try AgreementPublicKey(rawRepresentation: Data(hex: hexRepresentation))
Expand Down
1 change: 1 addition & 0 deletions Sources/WalletConnectKMS/Keychain/KeychainStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ protocol KeychainStorageProtocol {
func add<T: GenericPasswordConvertible>(_ item: T, forKey key: String) throws
func read<T: GenericPasswordConvertible>(key: String) throws -> T
func delete(key: String) throws
func deleteAll() throws
}

final class KeychainStorage: KeychainStorageProtocol {
Expand Down
2 changes: 1 addition & 1 deletion Sources/WalletConnectRelay/RelayClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final class RelayClient {
self.logger = logger
self.dispatcher = dispatcher

self.jsonRpcSubscriptionsHistory = JsonRpcHistory<RelayJSONRPC.SubscriptionParams>(logger: logger, keyValueStore: KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: Self.historyIdentifier))
self.jsonRpcSubscriptionsHistory = JsonRpcHistory<RelayJSONRPC.SubscriptionParams>(logger: logger, keyValueStore: CodableStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: Self.historyIdentifier))
setUpBindings()
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/WalletConnectSign/Engine/Common/PairingEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ final class PairingEngine {
var onProposeResponse: ((String, SessionProposal)->())?
var onSessionRejected: ((Session.Proposal, SessionType.Reason)->())?

private let proposalPayloadsStore: KeyValueStore<WCRequestSubscriptionPayload>
private let proposalPayloadsStore: CodableStore<WCRequestSubscriptionPayload>
private let networkingInteractor: NetworkInteracting
private let kms: KeyManagementServiceProtocol
private let pairingStore: WCPairingStorage
private let sessionToPairingTopic: KeyValueStore<String>
private let sessionToPairingTopic: CodableStore<String>
private var metadata: AppMetadata
private var publishers = [AnyCancellable]()
private let logger: ConsoleLogging
Expand All @@ -22,11 +22,11 @@ final class PairingEngine {
init(networkingInteractor: NetworkInteracting,
kms: KeyManagementServiceProtocol,
pairingStore: WCPairingStorage,
sessionToPairingTopic: KeyValueStore<String>,
sessionToPairingTopic: CodableStore<String>,
metadata: AppMetadata,
logger: ConsoleLogging,
topicGenerator: @escaping () -> String = String.generateTopic,
proposalPayloadsStore: KeyValueStore<WCRequestSubscriptionPayload> = KeyValueStore<WCRequestSubscriptionPayload>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue)) {
proposalPayloadsStore: CodableStore<WCRequestSubscriptionPayload> = CodableStore<WCRequestSubscriptionPayload>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue)) {
self.networkingInteractor = networkingInteractor
self.kms = kms
self.metadata = metadata
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class SessionEngine {

private let sessionStore: WCSessionStorage
private let pairingStore: WCPairingStorage
private let sessionToPairingTopic: KeyValueStore<String>
private let sessionToPairingTopic: CodableStore<String>
private let networkingInteractor: NetworkInteracting
private let kms: KeyManagementServiceProtocol
private var metadata: AppMetadata
Expand All @@ -28,7 +28,7 @@ final class SessionEngine {
kms: KeyManagementServiceProtocol,
pairingStore: WCPairingStorage,
sessionStore: WCSessionStorage,
sessionToPairingTopic: KeyValueStore<String>,
sessionToPairingTopic: CodableStore<String>,
metadata: AppMetadata,
logger: ConsoleLogging,
topicGenerator: @escaping () -> String = String.generateTopic) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ protocol JsonRpcHistoryRecording {
}
//TODO -remove and use jsonrpc history only from utils
class JsonRpcHistory: JsonRpcHistoryRecording {
let storage: KeyValueStore<JsonRpcRecord>
let storage: CodableStore<JsonRpcRecord>
let logger: ConsoleLogging

init(logger: ConsoleLogging, keyValueStore: KeyValueStore<JsonRpcRecord>) {
init(logger: ConsoleLogging, keyValueStore: CodableStore<JsonRpcRecord>) {
self.logger = logger
self.storage = keyValueStore
}
Expand Down
25 changes: 25 additions & 0 deletions Sources/WalletConnectSign/Services/CelanupService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Foundation
import WalletConnectKMS
import WalletConnectUtils

final class CleanupService {

private let pairingStore: WCPairingStorage
private let sessionStore: WCSessionStorage
private let kms: KeyManagementServiceProtocol
private let sessionToPairingTopic: CodableStore<String>

init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore<String>) {
self.pairingStore = pairingStore
self.sessionStore = sessionStore
self.sessionToPairingTopic = sessionToPairingTopic
self.kms = kms
}

func cleanup() throws {
pairingStore.deleteAll()
sessionStore.deleteAll()
sessionToPairingTopic.deleteAll()
try kms.deleteAll()
}
}
29 changes: 20 additions & 9 deletions Sources/WalletConnectSign/Sign/SignClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public final class SignClient {
private let networkingInteractor: NetworkInteracting
private let kms: KeyManagementService
private let history: JsonRpcHistory
private let cleanupService: CleanupService

// MARK: - Initializers

Expand All @@ -52,20 +53,20 @@ public final class SignClient {
init(metadata: AppMetadata, projectId: String, relayHost: String, logger: ConsoleLogging, kms: KeyManagementService, keyValueStorage: KeyValueStorage) {
self.metadata = metadata
self.logger = logger
// try? keychain.deleteAll() // Use for cleanup while lifecycles are not handled yet, but FIXME whenever
self.kms = kms
let relayClient = RelayClient(relayHost: relayHost, projectId: projectId, keyValueStorage: keyValueStorage, logger: logger)
let serializer = Serializer(kms: kms)
self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue))
self.history = JsonRpcHistory(logger: logger, keyValueStore: CodableStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue))
self.networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: history)
let pairingStore = PairingStorage(storage: SequenceStore<WCPairing>(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))
let sessionStore = SessionStorage(storage: SequenceStore<WCSession>(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))
let sessionToPairingTopic = KeyValueStore<String>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue)
let pairingStore = PairingStorage(storage: SequenceStore<WCPairing>(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue)))
let sessionStore = SessionStorage(storage: SequenceStore<WCSession>(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue)))
let sessionToPairingTopic = CodableStore<String>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue)
self.pairingEngine = PairingEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, logger: logger)
self.sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, sessionStore: sessionStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, logger: logger)
self.nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger)
self.controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger)
self.pairEngine = PairEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore)
self.cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic)
setUpConnectionObserving(relayClient: relayClient)
setUpEnginesCallbacks()
}
Expand All @@ -87,16 +88,17 @@ public final class SignClient {
self.logger = logger
self.kms = kms
let serializer = Serializer(kms: kms)
self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue))
self.history = JsonRpcHistory(logger: logger, keyValueStore: CodableStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue))
self.networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: history)
let pairingStore = PairingStorage(storage: SequenceStore<WCPairing>(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))
let sessionStore = SessionStorage(storage: SequenceStore<WCSession>(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))
let sessionToPairingTopic = KeyValueStore<String>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue)
let pairingStore = PairingStorage(storage: SequenceStore<WCPairing>(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue)))
let sessionStore = SessionStorage(storage: SequenceStore<WCSession>(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue)))
let sessionToPairingTopic = CodableStore<String>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue)
self.pairingEngine = PairingEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, logger: logger)
self.sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, sessionStore: sessionStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, logger: logger)
self.nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger)
self.controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger)
self.pairEngine = PairEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore)
self.cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic)
setUpConnectionObserving(relayClient: relayClient)
setUpEnginesCallbacks()
}
Expand Down Expand Up @@ -327,4 +329,13 @@ public final class SignClient {
sessionEngine.setSubscription(topic: sessionTopic)
}
}

#if DEBUG
/// Delete all stored data sach as: pairings, sessions, keys
///
/// - Note: Doesn't unsubscribe from topics
public func cleanup() throws {
try cleanupService.cleanup()
}
#endif
}
5 changes: 5 additions & 0 deletions Sources/WalletConnectSign/Storage/PairingStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ protocol WCPairingStorage: AnyObject {
func getPairing(forTopic topic: String) -> WCPairing?
func getAll() -> [WCPairing]
func delete(topic: String)
func deleteAll()
}

final class PairingStorage: WCPairingStorage {
Expand Down Expand Up @@ -39,4 +40,8 @@ final class PairingStorage: WCPairingStorage {
func delete(topic: String) {
storage.delete(topic: topic)
}

func deleteAll() {
storage.deleteAll()
}
}
38 changes: 14 additions & 24 deletions Sources/WalletConnectSign/Storage/SequenceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,47 @@ final class SequenceStore<T> where T: ExpirableSequence {

var onSequenceExpiration: ((_ sequence: T) -> Void)?

private let storage: KeyValueStorage
private let store: CodableStore<T>
private let dateInitializer: () -> Date
private let identifier: String

init(storage: KeyValueStorage, identifier: String, dateInitializer: @escaping () -> Date = Date.init) {
self.storage = storage
init(store: CodableStore<T>, dateInitializer: @escaping () -> Date = Date.init) {
self.store = store
self.dateInitializer = dateInitializer
self.identifier = identifier
}

func hasSequence(forTopic topic: String) -> Bool {
(try? getSequence(forTopic: topic)) != nil
}

// This force-unwrap is safe because Expirable Sequances are JSON Encodable
func setSequence(_ sequence: T) {
let encoded = try! JSONEncoder().encode(sequence)
storage.set(encoded, forKey: getKey(for: sequence.topic))
store.set(sequence, forKey: sequence.topic)
}

func getSequence(forTopic topic: String) throws -> T? {
guard let data = storage.object(forKey: getKey(for: topic)) as? Data else { return nil }
let sequence = try JSONDecoder().decode(T.self, from: data)
return verifyExpiry(on: sequence)
guard let value = try store.get(key: topic) else { return nil }
return verifyExpiry(on: value)
}

func getAll() -> [T] {
return storage.dictionaryRepresentation().compactMap {
guard $0.key.hasPrefix(identifier) else {return nil}
if let data = $0.value as? Data, let sequence = try? JSONDecoder().decode(T.self, from: data) {
return verifyExpiry(on: sequence)
}
return nil
}
let values = store.getAll()
return values.compactMap { verifyExpiry(on: $0) }
}

func delete(topic: String) {
storage.removeObject(forKey: getKey(for: topic))
store.delete(forKey: topic)
}

func deleteAll() {
store.deleteAll()
}

private func verifyExpiry(on sequence: T) -> T? {
let now = dateInitializer()
if now >= sequence.expiryDate {
storage.removeObject(forKey: getKey(for: sequence.topic))
store.delete(forKey: sequence.topic)
onSequenceExpiration?(sequence)
return nil
}
return sequence
}

private func getKey(for topic: String) -> String {
return "\(identifier).\(topic)"
}
}
5 changes: 5 additions & 0 deletions Sources/WalletConnectSign/Storage/SessionStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ protocol WCSessionStorage: AnyObject {
func getSession(forTopic topic: String) -> WCSession?
func getAll() -> [WCSession]
func delete(topic: String)
func deleteAll()
}

final class SessionStorage: WCSessionStorage {
Expand Down Expand Up @@ -39,4 +40,8 @@ final class SessionStorage: WCSessionStorage {
func delete(topic: String) {
storage.delete(topic: topic)
}

func deleteAll() {
storage.deleteAll()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import Foundation

public final class KeyValueStore<T> where T: Codable {
public final class CodableStore<T> where T: Codable {
private let defaults: KeyValueStorage
private let prefix: String

Expand All @@ -10,8 +10,9 @@ public final class KeyValueStore<T> where T: Codable {
self.prefix = identifier
}

public func set(_ item: T, forKey key: String) throws {
let encoded = try JSONEncoder().encode(item)
public func set(_ item: T, forKey key: String) {
// This force-unwrap is safe because T are JSON Encodable
let encoded = try! JSONEncoder().encode(item)
defaults.set(encoded, forKey: getContextPrefixedKey(for: key))
}

Expand All @@ -22,8 +23,7 @@ public final class KeyValueStore<T> where T: Codable {
}

public func getAll() -> [T] {
return defaults.dictionaryRepresentation().compactMap {
guard $0.key.hasPrefix(prefix) else {return nil}
return dictionaryForIdentifier().compactMap {
if let data = $0.value as? Data,
let item = try? JSONDecoder().decode(T.self, from: data) {
return item
Expand All @@ -36,7 +36,17 @@ public final class KeyValueStore<T> where T: Codable {
defaults.removeObject(forKey: getContextPrefixedKey(for: key))
}

public func deleteAll() {
dictionaryForIdentifier()
.forEach { defaults.removeObject(forKey: $0.key) }
}

private func getContextPrefixedKey(for key: String) -> String {
return "\(prefix).\(key)"
}

private func dictionaryForIdentifier() -> [String : Any] {
return defaults.dictionaryRepresentation()
.filter { $0.key.hasPrefix("\(prefix).") }
}
}
4 changes: 2 additions & 2 deletions Sources/WalletConnectUtils/JsonRpcHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ public class JsonRpcHistory<T> where T: Codable&Equatable {
case jsonRpcDuplicateDetected
case noJsonRpcRequestMatchingResponse
}
private let storage: KeyValueStore<JsonRpcRecord>
private let storage: CodableStore<JsonRpcRecord>
private let logger: ConsoleLogging

public init(logger: ConsoleLogging, keyValueStore: KeyValueStore<JsonRpcRecord>) {
public init(logger: ConsoleLogging, keyValueStore: CodableStore<JsonRpcRecord>) {
self.logger = logger
self.storage = keyValueStore
}
Expand Down
6 changes: 6 additions & 0 deletions Tests/TestingUtils/Mocks/KeyManagementServiceMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ final class KeyManagementServiceMock: KeyManagementServiceProtocol {
func deleteAgreementSecret(for topic: String) {
agreementKeys[topic] = nil
}

func deleteAll() throws {
privateKeys = [:]
symmetricKeys = [:]
agreementKeys = [:]
}
}

extension KeyManagementServiceMock {
Expand Down
4 changes: 4 additions & 0 deletions Tests/TestingUtils/Mocks/KeychainStorageMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ final class KeychainStorageMock: KeychainStorageProtocol {
didCallDelete = true
storage[key] = nil
}

func deleteAll() throws {
storage = [:]
}
}
2 changes: 1 addition & 1 deletion Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ final class JsonRpcHistoryTests: XCTestCase {
var sut: WalletConnectSign.JsonRpcHistory!

override func setUp() {
sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStore: KeyValueStore<WalletConnectSign.JsonRpcRecord>(defaults: RuntimeKeyValueStorage(), identifier: ""))
sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStore: CodableStore<WalletConnectSign.JsonRpcRecord>(defaults: RuntimeKeyValueStorage(), identifier: ""))
}

override func tearDown() {
Expand Down
Loading

0 comments on commit 6bec5ef

Please sign in to comment.