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

[Sign] Dapp extend session exp #1187

Merged
merged 3 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import SwiftUI

struct ConnectionDetailsView: View {
@EnvironmentObject var presenter: ConnectionDetailsPresenter


private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss E, d MMM y"
return formatter
}

var body: some View {
ZStack {
Color.grey100
Expand Down Expand Up @@ -145,6 +151,39 @@ struct ConnectionDetailsView: View {
.padding(.top, 30)
}

VStack(alignment: .leading) {
Text("Expiry")
.font(.system(size: 15, weight: .semibold, design: .rounded))
.foregroundColor(.whiteBackground)
.padding(.horizontal, 8)
.padding(.vertical, 5)
.background(Color.grey70)
.cornerRadius(28, corners: .allCorners)
.padding(.leading, 15)
.padding(.top, 9)

VStack(spacing: 0) {
TagsView(items: [dateFormatter.string(from: presenter.session.expiryDate)]) {
Text($0)
.foregroundColor(.cyanBackround)
.font(.system(size: 13, weight: .semibold, design: .rounded))
.padding(.horizontal, 8)
.padding(.vertical, 3)
.background(Color.cyanBackround.opacity(0.2))
.cornerRadius(10, corners: .allCorners)
}
.padding(10)
}
.background(Color.whiteBackground)
.cornerRadius(20, corners: .allCorners)
.padding(.horizontal, 5)
.padding(.bottom, 5)
}
.background(Color("grey95"))
.cornerRadius(25, corners: .allCorners)
.padding(.horizontal, 20)
.padding(.top, 30)

Button {
presenter.onDelete()
} label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class SessionRequestPresenter: ObservableObject {
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 ?? "Failed to decrypt") ?? "Failed to decrypt"
return (decryptedMessage ?? String(describing: sessionRequest.params.value)) ?? String(describing: sessionRequest.params.value)
}

@Published var showError = false
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 @@ -50,9 +50,9 @@ final class SessionEngine {
func hasSession(for topic: String) -> Bool {
return sessionStore.hasSession(forTopic: topic)
}

func getSessions() -> [Session] {
sessionStore.getAll().map {$0.publicRepresentation()}
sessionStore.getAll().map { $0.publicRepresentation() }
}

func request(_ request: Request) async throws {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

final class SessionExtendRequester {
private let sessionStore: WCSessionStorage
private let networkingInteractor: NetworkInteracting

init(
sessionStore: WCSessionStorage,
networkingInteractor: NetworkInteracting
) {
self.sessionStore = sessionStore
self.networkingInteractor = networkingInteractor
}

func extend(topic: String, by ttl: Int64) async throws {
guard var session = sessionStore.getSession(forTopic: topic) else {
throw WalletConnectError.noSessionMatchingTopic(topic)
}

let protocolMethod = SessionExtendProtocolMethod()
try session.updateExpiry(by: ttl)
let newExpiry = Int64(session.expiryDate.timeIntervalSince1970)
sessionStore.setSession(session)
let request = RPCRequest(method: protocolMethod.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry))
try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,7 @@ final class ControllerSessionStateMachine {
try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
}

func extend(topic: String, by ttl: Int64) async throws {
var session = try getSession(for: topic)
let protocolMethod = SessionExtendProtocolMethod()
try validateController(session)
try session.updateExpiry(by: ttl)
let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 )
sessionStore.setSession(session)
let request = RPCRequest(method: protocolMethod.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry))
try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
}

// MARK: - Handle Response

private func setupSubscriptions() {
networkingInteractor.responseSubscription(on: SessionUpdateProtocolMethod())
.sink { [unowned self] (payload: ResponseSubscriptionPayload<SessionType.UpdateParams, RPCResult>) in
Expand Down
7 changes: 5 additions & 2 deletions Sources/WalletConnectSign/Sign/SignClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public final class SignClient: SignClientProtocol {
private let sessionPingService: SessionPingService
private let nonControllerSessionStateMachine: NonControllerSessionStateMachine
private let controllerSessionStateMachine: ControllerSessionStateMachine
private let sessionExtendRequester: SessionExtendRequester
private let appProposeService: AppProposeService
private let historyService: HistoryService
private let cleanupService: SignCleanupService
Expand Down Expand Up @@ -138,6 +139,7 @@ public final class SignClient: SignClientProtocol {
sessionPingService: SessionPingService,
nonControllerSessionStateMachine: NonControllerSessionStateMachine,
controllerSessionStateMachine: ControllerSessionStateMachine,
sessionExtendRequester: SessionExtendRequester,
appProposeService: AppProposeService,
disconnectService: DisconnectService,
historyService: HistoryService,
Expand All @@ -152,6 +154,7 @@ public final class SignClient: SignClientProtocol {
self.sessionPingService = sessionPingService
self.nonControllerSessionStateMachine = nonControllerSessionStateMachine
self.controllerSessionStateMachine = controllerSessionStateMachine
self.sessionExtendRequester = sessionExtendRequester
self.appProposeService = appProposeService
self.historyService = historyService
self.cleanupService = cleanupService
Expand Down Expand Up @@ -259,13 +262,13 @@ public final class SignClient: SignClientProtocol {
try await controllerSessionStateMachine.update(topic: topic, namespaces: namespaces)
}

/// For wallet to extend a session to 7 days
/// For dapp and wallet to extend a session to 7 days
/// - Parameters:
/// - topic: Topic of the session that is intended to be extended.
public func extend(topic: String) async throws {
let ttl: Int64 = Session.defaultTimeToLive
if sessionEngine.hasSession(for: topic) {
try await controllerSessionStateMachine.extend(topic: topic, by: ttl)
try await sessionExtendRequester.extend(topic: topic, by: ttl)
}
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/WalletConnectSign/Sign/SignClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public struct SignClientFactory {
let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyContextStore: verifyContextStore, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger)
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)
let sessionTopicToProposal = CodableStore<Session.Proposal>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionTopicToProposal.rawValue)
let approveEngine = ApproveEngine(
networkingInteractor: networkingClient,
Expand Down Expand Up @@ -60,6 +61,7 @@ public struct SignClientFactory {
sessionPingService: sessionPingService,
nonControllerSessionStateMachine: nonControllerSessionStateMachine,
controllerSessionStateMachine: controllerSessionStateMachine,
sessionExtendRequester: sessionExtendRequester,
appProposeService: appProposerService,
disconnectService: disconnectService,
historyService: historyService,
Expand Down
2 changes: 1 addition & 1 deletion Sources/WalletConnectSign/Types/Session/WCSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ struct WCSession: SequenceObject, Equatable {
mutating func updateExpiry(to expiry: Int64) throws {
let newExpiryDate = Date(timeIntervalSince1970: TimeInterval(expiry))
let maxExpiryDate = Date(timeIntervalSinceNow: TimeInterval(WCSession.defaultTimeToLive))
guard newExpiryDate >= expiryDate && newExpiryDate <= maxExpiryDate else {
guard newExpiryDate.millisecondsSince1970 >= (expiryDate.millisecondsSince1970 / 1000) && newExpiryDate <= maxExpiryDate else {
throw WalletConnectError.invalidUpdateExpiryValue
}
self.expiryDate = newExpiryDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import WalletConnectKMS

class ControllerSessionStateMachineTests: XCTestCase {
var sut: ControllerSessionStateMachine!
var sessionExtendRequester: SessionExtendRequester!
var networkingInteractor: NetworkingInteractorMock!
var storageMock: WCSessionStorageMock!
var cryptoMock: KeyManagementServiceMock!
Expand All @@ -15,6 +16,7 @@ class ControllerSessionStateMachineTests: XCTestCase {
storageMock = WCSessionStorageMock()
cryptoMock = KeyManagementServiceMock()
sut = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: cryptoMock, sessionStore: storageMock, logger: ConsoleLoggerMock())
sessionExtendRequester = SessionExtendRequester(sessionStore: storageMock, networkingInteractor: networkingInteractor)
}

override func tearDown() {
Expand Down Expand Up @@ -66,40 +68,24 @@ class ControllerSessionStateMachineTests: XCTestCase {
let session = WCSession.stub(isSelfController: true, expiryDate: tomorrow)
storageMock.setSession(session)
let twoDays = 2*Time.day
await XCTAssertNoThrowAsync(try await sut.extend(topic: session.topic, by: Int64(twoDays)))
await XCTAssertNoThrowAsync(try await sessionExtendRequester.extend(topic: session.topic, by: Int64(twoDays)))
let extendedSession = storageMock.getAll().first {$0.topic == session.topic}!
XCTAssertEqual(extendedSession.expiryDate.timeIntervalSinceReferenceDate, TimeTraveler.dateByAdding(days: 2).timeIntervalSinceReferenceDate, accuracy: 1)
}

func testUpdateExpirySessionNotSettled() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow, acknowledged: false)
storageMock.setSession(session)
let twoDays = 2*Time.day
await XCTAssertThrowsErrorAsync(try await sut.extend(topic: session.topic, by: Int64(twoDays)))
}

func testUpdateExpiryOnNonControllerClient() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow)
storageMock.setSession(session)
let twoDays = 2*Time.day
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: Int64(twoDays)))
}

func testUpdateExpiryTtlTooHigh() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: true, expiryDate: tomorrow)
storageMock.setSession(session)
let tenDays = 10*Time.day
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: Int64(tenDays)))
await XCTAssertThrowsErrorAsync( try await sessionExtendRequester.extend(topic: session.topic, by: Int64(tenDays)))
}

func testUpdateExpiryTtlTooLow() async {
let dayAfterTommorow = TimeTraveler.dateByAdding(days: 2)
let session = WCSession.stub(isSelfController: true, expiryDate: dayAfterTommorow)
storageMock.setSession(session)
let oneDay = Int64(1*Time.day)
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: oneDay))
await XCTAssertThrowsErrorAsync( try await sessionExtendRequester.extend(topic: session.topic, by: oneDay))
}
}