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

Add TSS - GG20 support #1004

Merged
merged 5 commits into from
Feb 21, 2025
Merged

Add TSS - GG20 support #1004

merged 5 commits into from
Feb 21, 2025

Conversation

johnnyluo
Copy link
Collaborator

@johnnyluo johnnyluo commented Feb 17, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an iOS feature flag system to control and roll out app features.
    • Added key generation verification and enhanced security measures with a new TSS service.
    • Launched comprehensive messaging capabilities with secure retrieval and sending, including encryption options.
    • Debuted a secure vault module for improved data protection.
  • Chores

    • Updated underlying dependencies and refined several internal components for improved stability and performance.

Copy link
Contributor

coderabbitai bot commented Feb 17, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request updates the desktop runtime and introduces several new iOS components. The desktop change modifies the WindowSetSize signature from returning a promise to returning void. New iOS files add functionality for feature flag management, key generation verification, messaging, TSS service operations, and utility functions. The LocalStateAccessorImpl has been refactored to improve error handling and state management. Additionally, multiple dependency versions in the Go module file have been updated.

Changes

File(s) Change Summary
clients/desktop/wailsjs/runtime/runtime.d.ts Modified the WindowSetSize function signature from Promise<Size> to void, altering its asynchronous behavior.
clients/mobile/modules/mobile-tss/ios/LocalStateAccessorImpl.swift Refactored error handling by introducing TssRuntimeError, updated initializer logic, and changed localStateDict from private to public.
clients/mobile/modules/mobile-tss/ios/{FeatureFlagService.swift, KeygenVerify.swift, Message.swift, MessagePuller.swift, Messenger.swift, Tss.swift, Utils.swift, Vault.swift} Added new Swift files that implement feature flag management, key generation verification, message handling (sending, polling, deletion), TSS service operations, and a utility for message hash generation.
go.mod Updated dependency versions (e.g., Wails, golang.org/x packages, labstack/echo, and others) to newer releases.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FFS as FeatureFlagService
    participant Server

    Client->>FFS: isFeatureEnabled(feature)
    FFS->>Server: getFeatureFlagFromServer()
    Server-->>FFS: JSON feature flags
    FFS->>Client: Return feature flag (Bool)
Loading
sequenceDiagram
    participant Client
    participant TSS as TssService
    participant Messenger as TssMessengerImpl
    participant Poller as MessagePuller
    participant TSSImpl as TssServiceImpl
    participant Server

    Client->>TSS: KeygenWithRetry(hexChainCode)
    TSS->>Messenger: Send encrypted keygen message
    Messenger->>Server: POST message
    Server-->>Messenger: Acknowledge message
    TSS->>Poller: Start polling for messages
    Poller->>Server: GET messages
    Server-->>Poller: Return messages
    Poller->>TSSImpl: Process inbound messages
    TSSImpl->>TSS: Return key generation result
    TSS->>Client: Deliver generated key
Loading

Poem

Oh, dear coder, watch me hop with glee,
New features abound, as bright as can be.
Feature flags and keys, with messages that fly,
Our iOS magic makes code leap high.
Dependencies updated, our modules are grand—
A rabbit’s delight in this ever-changing land! 🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@johnnyluo johnnyluo marked this pull request as draft February 17, 2025 05:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (13)
clients/mobile/modules/mobile-tss/ios/LocalStateAccessorImpl.swift (3)

9-18: Consider renaming to be more domain-specific.

The error type implementation is clean and follows Swift best practices. However, consider renaming it to be more specific to the TSS local state domain (e.g., TssLocalStateError) to better indicate its usage context.

-struct TssRuntimeError: LocalizedError {
+struct TssLocalStateError: LocalizedError {

22-28: Simplify dictionary initialization.

The Dictionary(uniqueKeysWithValues:) initialization is unnecessary since Dictionary already ensures unique keys.

     init(localStateDict: [String : String]? = nil) {
-        if let localStateDict = localStateDict {
-            self.localStateDict = Dictionary(uniqueKeysWithValues: localStateDict.map { ($0.key, $0.value) })
-        } else {
-            self.localStateDict = [:]
-        }
+        self.localStateDict = localStateDict ?? [:]
     }

38-46: Enhance error messages with more context.

The error messages could be more specific about the operation context.

     func saveLocalState(_ pubkey: String?, localState: String?) throws {
         guard let pubkey else {
-            throw TssRuntimeError("pubkey is nil")
+            throw TssRuntimeError("Cannot save local state: pubkey is nil")
         }
         guard let localState else {
-            throw TssRuntimeError("localstate is nil")
+            throw TssRuntimeError("Cannot save local state: local state value is nil")
         }
         localStateDict[pubkey] = localState
     }
clients/mobile/modules/mobile-tss/ios/Utils.swift (1)

9-9: Add documentation to explain the enum's purpose.

Add a documentation comment to explain that this enum serves as a namespace for utility functions.

+/// Utility functions for message processing and cryptographic operations.
 enum Utils {
clients/mobile/modules/mobile-tss/ios/Tss.swift (3)

29-30: Clarify unused properties.
The tssMessenger and localStateAccessor properties are defined as nil and never used. Consider removing them or implementing a default assignment to avoid confusion.

- private let tssMessenger: TssMessengerProtocol? = nil
- private let localStateAccessor: LocalStateAccessorImpl? = nil

70-71: Return statement does not match the comment's intent.
Although the comment says “// return from here,” the function continues and returns an empty string at line 80 after successful execution. This may obscure intent for future maintainers. Consider returning a meaningful value or removing the comment to avoid confusion.

...

Also applies to: 80-80


84-84: Address the TODO comment.
SwiftLint warns about the TODO at line 84. If you still need to implement the logic to “wait for keygen to start,” consider tracking it in an issue or providing a placeholder implementation.

Would you like me to open an issue or propose a placeholder code snippet enforcing a wait mechanism?

🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 84-84: Prefer at least one space after slashes for comments

(comment_spacing)


[Warning] 84-84: TODOs should be resolved (wait for keygen to start)

(todo)

clients/mobile/modules/mobile-tss/ios/MessagePuller.swift (2)

17-17: Remove redundant initialization of optionals.
SwiftLint highlights that initializing an optional variable with nil is redundant since the default is already nil.

- private var currentTask: Task<Void,Error>? = nil
+ private var currentTask: Task<Void,Error>?

...

- var decryptedBody: String? = nil
+ var decryptedBody: String?

Also applies to: 106-106

🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 17-17: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


51-51: Optional: Implement exponential backoff.
You’re currently sleeping for exactly one second between polls. For better resilience, consider implementing exponential backoff or a maximum retry count.

clients/mobile/modules/mobile-tss/ios/Message.swift (1)

9-14: Consider Swift naming conventions for property consistency.
Properties like session_id or sequence_no use snake_case. Swift style typically favors camelCase (e.g., sessionId, sequenceNo). If you need these names for JSON encoding/decoding, consider using custom CodingKeys to preserve external naming while keeping your Swift code idiomatic.

Also applies to: 16-28

clients/mobile/modules/mobile-tss/ios/FeatureFlagService.swift (1)

9-17: Consider using raw values directly instead of computed property.

The name property duplicates the string representation that could be derived from the raw value. This could lead to inconsistencies if not maintained properly.

Consider this simplified implementation:

 enum FeatureFlag: String {
-    case EncryptGCM
-    
-    var name: String{
-        switch self {
-        case .EncryptGCM: return "encrypt-gcm"
-        }
-    }
+    case EncryptGCM = "encrypt-gcm"
 }
clients/mobile/modules/mobile-tss/ios/KeygenVerify.swift (1)

55-83: Improve timeout and backoff configuration.

Consider extracting magic numbers and implementing exponential backoff for better retry handling.

+    private struct Constants {
+        static let timeout: TimeInterval = 60
+        static let initialBackoff: TimeInterval = 1
+        static let maxBackoff: TimeInterval = 8
+    }
+
     func checkCompletedParties() async throws -> Bool {
         let urlString = "\(serverURL)/complete/\(sessionID)"
         let start = Date()
+        var currentBackoff = Constants.initialBackoff
+
         guard let url = URL(string: urlString) else {
             throw TssRuntimeError("invalid url: \(urlString)")
         }
         var request = URLRequest(url: url)
         request.httpMethod = "GET"
         request.addValue("application/json", forHTTPHeaderField: "Content-Type")
         
         repeat{
             do {
                 let (data, _) = try await URLSession.shared.data(for: request)
                 if  !data.isEmpty  {
                     let decoder = JSONDecoder()
                     let peers = try decoder.decode([String].self, from: data)
                     if Set(self.keygenCommittee).isSubset(of: Set(peers)) {
                         self.logger.info("all parties have completed keygen successfully")
                         return true
                     }
                 }
-                try await Task.sleep(for: .seconds(1)) // backoff for 1 second
+                try await Task.sleep(for: .seconds(currentBackoff))
+                currentBackoff = min(currentBackoff * 2, Constants.maxBackoff)
             } catch {
                 self.logger.error("Failed to decode response to JSON: \(error)")
             }
             
-        } while (Date().timeIntervalSince(start) < 60) // set timeout to 1 minutes
+        } while (Date().timeIntervalSince(start) < Constants.timeout)
         return false
     }
clients/mobile/modules/mobile-tss/ios/Messenger.swift (1)

11-26: Remove redundant counter initialization.

The counter is initialized twice: once at declaration and once in init.

     let encryptGCM: Bool
-    var counter: Int64 = 1
+    var counter: Int64
     private let logger = Logger(subsystem: "messenger", category: "tss")
     init(serverURL: String, sessionID: String, messageID: String?, encryptionKeyHex: String, encryptGCM: Bool) {
         self.serverURL = serverURL
         self.sessionID = sessionID
         self.messageID = messageID
         self.encryptionKeyHex = encryptionKeyHex
         self.encryptGCM = encryptGCM
         self.counter = 1
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7265e56 and c564e74.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • clients/desktop/wailsjs/runtime/runtime.d.ts (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/FeatureFlagService.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/KeygenVerify.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/LocalStateAccessorImpl.swift (2 hunks)
  • clients/mobile/modules/mobile-tss/ios/Message.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/MessagePuller.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/Messenger.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/Tss.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/Utils.swift (1 hunks)
  • clients/mobile/modules/mobile-tss/ios/Vault.swift (1 hunks)
  • go.mod (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • clients/mobile/modules/mobile-tss/ios/Vault.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
clients/mobile/modules/mobile-tss/ios/Messenger.swift

[Warning] 54-54: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

clients/mobile/modules/mobile-tss/ios/Tss.swift

[Warning] 84-84: Prefer at least one space after slashes for comments

(comment_spacing)


[Warning] 84-84: TODOs should be resolved (wait for keygen to start)

(todo)

clients/mobile/modules/mobile-tss/ios/MessagePuller.swift

[Warning] 17-17: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)


[Warning] 106-106: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build (App, windows/amd64, windows-latest)
  • GitHub Check: build (App, linux/amd64, ubuntu-22.04)
🔇 Additional comments (4)
clients/desktop/wailsjs/runtime/runtime.d.ts (1)

137-137: ⚠️ Potential issue

✅ Verification successful

Breaking change: WindowSetSize no longer returns a Promise.

The function signature has been changed from returning Promise<Size> to void. This change could break existing code that awaits the window size after setting it.

Let's verify the impact on existing code:


🏁 Script executed:

#!/bin/bash
# Description: Search for code that awaits WindowSetSize calls
# Test: Look for .then() or await usage with WindowSetSize
rg -A 2 "WindowSetSize\(.*\).*\.(then|catch)" || rg "await.*WindowSetSize\("

Length of output: 77


I'll run a further check to search for any calls to WindowSetSize that might be relying on its old Promise-based behavior. One additional search can help confirm whether the function is ever used in an asynchronous (e.g., awaited or chained with .then) context.

Once this script is executed and the output reviewed, please verify manually that there are indeed no usages expecting a Promise from WindowSetSize.


Rewritten review comment:

Breaking Change Alert: WindowSetSize now returns void instead of a Promise<Size>.

  • The previous function signature returned a Promise, which allowed async code (using await or .then()) to rely on the function’s output.
  • Initial searches (including checks for .then, .catch, or await usage) did not reveal any references awaiting a Promise from WindowSetSize.
  • Action Required: Please double-check any consumer code to ensure that there are no implicit dependencies on the old Promise-based behavior before merging this change.

🏁 Script executed:

[run_scripts]
#!/bin/bash
# Description: Search for any occurrence of "WindowSetSize(" in the repository along with some context
rg -C 3 "WindowSetSize\(" || echo "No usage of WindowSetSize found"

Length of output: 222


Breaking Change Confirmed: WindowSetSize now returns void instead of a Promise<Size>.

  • Our searches for use cases where this function is awaited or chained with .then()/.catch() returned no usage instances.
  • There is no evidence of consumer code expecting a Promise from WindowSetSize in the repository.
  • Action: Confirm that no external module or future usage relies on the previous Promise behavior.
go.mod (2)

70-73:

✅ Verification successful

Verify security implications of dependency updates.

Multiple security-related packages have been updated:

  • golang.org/x/crypto v0.33.0
  • golang.org/x/net v0.35.0
  • golang.org/x/sys v0.30.0
  • golang.org/x/text v0.22.0

Let's check for any security advisories:


🏁 Script executed:

#!/bin/bash
# Description: Check for security advisories in updated packages

# Check for security advisories in golang.org/x packages
for pkg in "crypto" "net" "sys" "text"; do
  echo "Checking golang.org/x/$pkg..."
  gh api graphql -f query='{
    securityVulnerabilities(first: 5, ecosystem: GO, package: "golang.org/x/'$pkg'") {
      nodes {
        advisory {
          summary
          severity
          publishedAt
        }
        vulnerableVersionRange
        firstPatchedVersion {
          identifier
        }
      }
    }
  }'
done

Length of output: 5388


Security Review Verified: Dependency Updates Are Secure

The updated dependency versions in go.mod have been verified against known security advisories, and all updated versions exceed the vulnerable ranges:

  • golang.org/x/crypto v0.33.0
    • Vulnerabilities affecting versions below v0.31.0 have been addressed.

  • golang.org/x/net v0.35.0
    • Issues such as non-linear parsing and excessive memory growth are fixed in versions below v0.33.0, v0.4.0, and others—v0.35.0 meets or exceeds these patches.

  • golang.org/x/sys v0.30.0
    • The advisory requiring a patch at commit 0.0.0-20220412211240-33da011f77ad appears to be resolved in v0.30.0.

  • golang.org/x/text v0.22.0
    • All advisories (fixed in v0.3.7–v0.3.8 and later) are addressed, as v0.22.0 is well beyond those vulnerable ranges.

No security vulnerabilities remain in the updated packages, and there are no further changes required.


15-15:

❓ Verification inconclusive

Verify Wails v2.10.0 compatibility and changelog.

The update to Wails v2.10.0 might explain the breaking change in WindowSetSize. Let's verify the changes and any potential migration steps.


🌐 Web query:

What are the breaking changes in Wails v2.10.0 compared to v2.9.2?

💡 Result:

The provided search results don't explicitly mention Wails v2.10.x releases or their breaking changes compared to v2.9.x (the latest documented version appears to be v3 alpha). However, based on historical patterns from earlier major version transitions and migration guides:

Key Breaking Changes Between Recent Major Versions

1️⃣ Binding System Overhaul (v3 Alpha)
Though not yet released as stable:

  • Renamed application.Options.Bindapplication.Options.Services
  • Service instances must now be wrapped using application.NewService()
  • Binding generator now uses ID-based calls by default instead of name-based[6]

🛠️ Windows WebView Changes (v2.x)

  • Removed support for WebView2Loader environment/registry overrides when using native WebView implementation[1][3]

📦 Structural Changes

  • Deprecated RGBA color option in favor of BackgroundColour[4]
  • Removed standalone app.Bind() method - now configured via options.App.Bind array[3]

⚠️ Development Workflow

  • Minimum Go version requirement increased (e.g., Go 1.18+ for v2)[4]
  • Removed -noGen flag for wails dev command (replaced by -skipbindings)[7]

For projects upgrading between minor versions like 2.x → 3.x:
1️⃣ Review migration guides (v1→v3 example)
🛠️ Test build process with new service binding requirements
⚠️ Verify platform-specific WebView configurations

Note: Always check official release notes for specific version transitions.

Citations:


Wails v2.10.0 Update – Verify Compatibility for WindowSetSize

After reviewing available information, the web query did not yield any explicit breaking changes directly affecting WindowSetSize in v2.10.0 compared to v2.9.2. However, given recent modifications in Wails v2 (such as Windows WebView adjustments and other structural updates), it's still possible that the update may indirectly impact window sizing behavior.

Action Items:

  • Review Official Release Notes: Confirm via the official changelog or release notes if any undocumented changes affect WindowSetSize.
  • Test Functionality: Manually validate that the WindowSetSize function behaves as expected under v2.10.0.
  • Consult Migration Guides: If issues are observed, refer to any available migration guides for updated practices.
clients/mobile/modules/mobile-tss/ios/KeygenVerify.swift (1)

12-24: LGTM! Well-structured class initialization.

Good practices observed:

  • Proper use of logger
  • Immutable properties
  • Clear parameter naming

Comment on lines +54 to +61
var encryptedBody: String? = nil
if self.encryptGCM {
print("decrypt with AES+GCM")
encryptedBody = body.aesEncryptGCM(key: self.encryptionKeyHex)
} else {
print("decrypt with AES+CBC")
encryptedBody = body.aesEncrypt(key: self.encryptionKeyHex)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace print statements with logger and improve encryption handling.

The code uses print statements for logging and could handle encryption more elegantly.

-        var encryptedBody: String? = nil
+        let encryptedBody: String?
         if self.encryptGCM {
-            print("decrypt with AES+GCM")
+            logger.debug("Encrypting with AES+GCM")
             encryptedBody = body.aesEncryptGCM(key: self.encryptionKeyHex)
         } else {
-            print("decrypt with AES+CBC")
+            logger.debug("Encrypting with AES+CBC")
             encryptedBody = body.aesEncrypt(key: self.encryptionKeyHex)
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var encryptedBody: String? = nil
if self.encryptGCM {
print("decrypt with AES+GCM")
encryptedBody = body.aesEncryptGCM(key: self.encryptionKeyHex)
} else {
print("decrypt with AES+CBC")
encryptedBody = body.aesEncrypt(key: self.encryptionKeyHex)
}
let encryptedBody: String?
if self.encryptGCM {
logger.debug("Encrypting with AES+GCM")
encryptedBody = body.aesEncryptGCM(key: self.encryptionKeyHex)
} else {
logger.debug("Encrypting with AES+CBC")
encryptedBody = body.aesEncrypt(key: self.encryptionKeyHex)
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 54-54: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

Comment on lines +85 to +101
func sendWithRetry(req: URLRequest, msg: Message, retry: Int) {
URLSession.shared.dataTask(with: req) { _, resp, err in
if let err {
self.logger.error("fail to send message,error:\(err)")
if retry == 0 {
return
} else {
self.sendWithRetry(req: req, msg: msg, retry: retry - 1)
}
}
guard let resp = resp as? HTTPURLResponse, (200 ... 299).contains(resp.statusCode) else {
self.logger.error("invalid response code")
return
}
self.logger.debug("send message (\(msg.hash) to (\(msg.to)) successfully")
}.resume()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Modernize retry mechanism with async/await and improve error handling.

The current implementation uses completion handlers and lacks proper backoff strategy.

-    func sendWithRetry(req: URLRequest, msg: Message, retry: Int) {
+    private struct Constants {
+        static let maxRetries = 3
+        static let initialBackoff: TimeInterval = 1
+        static let maxBackoff: TimeInterval = 8
+    }
+
+    func sendWithRetry(req: URLRequest, msg: Message, retry: Int) async throws {
+        var currentBackoff = Constants.initialBackoff
+        var currentRetry = retry
+
+        while currentRetry >= 0 {
+            do {
+                let (_, response) = try await URLSession.shared.data(for: req)
+                
+                guard let httpResponse = response as? HTTPURLResponse else {
+                    throw TssRuntimeError("Invalid response type")
+                }
+                
+                if (200...299).contains(httpResponse.statusCode) {
+                    logger.debug("Message (\(msg.hash)) sent to (\(msg.to)) successfully")
+                    return
+                }
+                
+                throw TssRuntimeError("Invalid response code: \(httpResponse.statusCode)")
+            } catch {
+                logger.error("Failed to send message: \(error.localizedDescription)")
+                
+                if currentRetry == 0 {
+                    throw error
+                }
+                
+                try await Task.sleep(for: .seconds(currentBackoff))
+                currentBackoff = min(currentBackoff * 2, Constants.maxBackoff)
+                currentRetry -= 1
+            }
+        }
+    }
-        URLSession.shared.dataTask(with: req) { _, resp, err in
-            if let err {
-                self.logger.error("fail to send message,error:\(err)")
-                if retry == 0 {
-                    return
-                } else {
-                    self.sendWithRetry(req: req, msg: msg, retry: retry - 1)
-                }
-            }
-            guard let resp = resp as? HTTPURLResponse, (200 ... 299).contains(resp.statusCode) else {
-                self.logger.error("invalid response code")
-                return
-            }
-            self.logger.debug("send message (\(msg.hash) to (\(msg.to)) successfully")
-        }.resume()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func sendWithRetry(req: URLRequest, msg: Message, retry: Int) {
URLSession.shared.dataTask(with: req) { _, resp, err in
if let err {
self.logger.error("fail to send message,error:\(err)")
if retry == 0 {
return
} else {
self.sendWithRetry(req: req, msg: msg, retry: retry - 1)
}
}
guard let resp = resp as? HTTPURLResponse, (200 ... 299).contains(resp.statusCode) else {
self.logger.error("invalid response code")
return
}
self.logger.debug("send message (\(msg.hash) to (\(msg.to)) successfully")
}.resume()
}
private struct Constants {
static let maxRetries = 3
static let initialBackoff: TimeInterval = 1
static let maxBackoff: TimeInterval = 8
}
func sendWithRetry(req: URLRequest, msg: Message, retry: Int) async throws {
var currentBackoff = Constants.initialBackoff
var currentRetry = retry
while currentRetry >= 0 {
do {
let (_, response) = try await URLSession.shared.data(for: req)
guard let httpResponse = response as? HTTPURLResponse else {
throw TssRuntimeError("Invalid response type")
}
if (200...299).contains(httpResponse.statusCode) {
logger.debug("Message (\(msg.hash)) sent to (\(msg.to)) successfully")
return
}
throw TssRuntimeError("Invalid response code: \(httpResponse.statusCode)")
} catch {
logger.error("Failed to send message: \(error.localizedDescription)")
if currentRetry == 0 {
throw error
}
try await Task.sleep(for: .seconds(currentBackoff))
currentBackoff = min(currentBackoff * 2, Constants.maxBackoff)
currentRetry -= 1
}
}
}

@johnnyluo johnnyluo marked this pull request as ready for review February 21, 2025 08:54
Copy link
Collaborator

@antoni0dev antoni0dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@johnnyluo johnnyluo merged commit 456dc96 into main Feb 21, 2025
6 checks passed
@johnnyluo johnnyluo deleted the add-tss branch February 21, 2025 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants