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

[Network] Refactor: #355 Relay client RPC #384

Merged
merged 13 commits into from
Aug 1, 2022

Conversation

sekimondre
Copy link

@sekimondre sekimondre commented Jul 28, 2022

closes #355

Overview:

  • Integration of JSONRPC types into the Relay client target.
  • Introduces relay RPC methods declared conforming to RelayRPC protocol.
  • Small improvements to Either and RPCResponse.
  • ID generation logic for RPC methods decoupled in isolation.
  • Introduces improved and expanded RPCHistory to track all RPC requests sent and received from the client.

Notes:

  • RPCMethod protocol could be extracted to utils to be used in other targets for scalability.
  • RPCHistory is still only replacing the current behavior for Subscription method.

Next steps:

  • Expand RPCHistory usage to track all requests and responses, from local and remote origins, to provide a full history.
  • Implement RPC responding for parsing and other errors on client side.
  • Find a solution to avoid leaking cancellables on error calls.

Comment on lines -120 to -132
dispatcher.send(requestJson) { [weak self] error in
cancellable = requestAcknowledgePublisher
.filter { $0 == request.id }
.sink { (_) in
cancellable?.cancel()
onNetworkAcknowledge(nil)
}
dispatcher.send(message) { [weak self] error in
if let error = error {
self?.logger.debug("Failed to Publish Payload, error: \(error)")
cancellable?.cancel()
onNetworkAcknowledge(error)
}
}
cancellable = requestAcknowledgePublisher
.filter {$0.id == request.id}
.sink { (_) in
cancellable?.cancel()
onNetworkAcknowledge(nil)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

any advantage of changing the order?

Copy link
Author

Choose a reason for hiding this comment

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

Setting up the events before the call prevents any race conditions in case of local socket errors. It's just a good practice

Copy link
Contributor

Choose a reason for hiding this comment

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

Socket error will be handled in the completion block on a different thread so I think it's not the case.
Subscription will happen right after send() method execution as its just adds execution block.
correct me if I am wrong but imo the risk here is close to 0

if send would be a throwing function, error could happen before subscription but if error happens, you do not even care about subscription. anyway it does not apply for above code.

Copy link
Contributor

Choose a reason for hiding this comment

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

anyway, you can leave it like this, won't argue 😄

Copy link

Choose a reason for hiding this comment

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

merhaba birşey sormak istiyorum

@@ -0,0 +1,67 @@
import JSONRPC

public final class RPCHistory {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder how new history can affect existing integrations 🤔
any thoughts?

Copy link
Author

Choose a reason for hiding this comment

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

Currently, the history is being used just as a means of message deduplication, so the worst case scenario risk is the SDK receiving a duplicate Subscription once, which looks like a small trade-off to migrate to a history that can track all requests (the current implementation is generic, so it's bound to a single type of params). What do you think?

Copy link
Author

Choose a reason for hiding this comment

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

Also, we still have the transition to version 1.0.0 in the future as an opportunity to include all breaking changes that we need.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want to avoid any breaking changes that could affect apps on production. But currently I do not think it will be very dangerous. Anyway it's worth to test it before merging.

@sekimondre sekimondre self-assigned this Jul 31, 2022
@sekimondre sekimondre added the enhancement New feature or request label Jul 31, 2022
@sekimondre sekimondre marked this pull request as ready for review July 31, 2022 23:09
Copy link
Contributor

@llbartekll llbartekll left a comment

Choose a reason for hiding this comment

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

Looks good! wish to see a generic networking interactor :)

Comment on lines +11 to +17
func wrapToIridium() -> PrefixDecorator<Self> {
return PrefixDecorator(rpcMethod: self, prefix: "iridium")
}

func wrapToIRN() -> PrefixDecorator<Self> {
return PrefixDecorator(rpcMethod: self, prefix: "irn")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

As I mentioned before I do not like those wrappings.
I'd prefer concrete types like IrnPublish etc.
imho, it adds extra unnecessary complexity, couples RelayRPC to wrapToIridium/wrapToIRN and eventually it will leave some wrapping methods that are not in use.

Copy link
Author

Choose a reason for hiding this comment

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

We already saw the the protocol be renamed twice, it's better to be prepared for any future changes. Ideally, the RelayClient calls should be refactored in a way that the wrapping is called in just one line, so it's easy to change it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need to have a way to configure this in single place (not for every request).

After next protocol renaming is better to replace method than add another wrapToXXX method 😁

guard let subscriptionId = subscriptions[topic] else {
completion(RelyerError.subscriptionIdNotFound)
return nil
completion(RelayerError.subscriptionIdNotFound)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a good time to rename the error. We no longer use Relayer anywhere. Errors would be consistent with other classes.

Copy link
Author

Choose a reason for hiding this comment

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

Renamed

subscriptionResponsePublisherSubject.send((response.id, subscriptionId))
}
case .failure(let rpcError):
logger.error("Received error message from iridium network, code: \(rpcError.code), message: \(rpcError.message)")
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we could use relay network instead of iridium network? as it is suppose to change.

Copy link
Author

Choose a reason for hiding this comment

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

True, I've removed references to the "iridium" naming

@discardableResult func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) -> Int64
func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void))
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 yeah that was really not necessary to return an id.

@sekimondre sekimondre force-pushed the refactor/#355-relay-rpc branch from 4fd874a to 156e32b Compare August 1, 2022 13:16
Package.swift Outdated
@@ -36,7 +36,7 @@ let package = Package(
path: "Sources/Auth"),
.target(
name: "WalletConnectRelay",
dependencies: ["WalletConnectUtils", "WalletConnectKMS"],
Copy link
Contributor

Choose a reason for hiding this comment

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

WalletConnectUtils depends on JSONRPC, do we need to add JSONRPC dependency here also?

Copy link
Author

Choose a reason for hiding this comment

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

Removed it

path: "Tests/TestingUtils"),
.testTarget(
name: "WalletConnectUtilsTests",
dependencies: ["WalletConnectUtils"]),
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

Copy link
Author

Choose a reason for hiding this comment

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

Removed it

let requestJson = try request.json()
try await dispatcher.send(requestJson)
let request = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag))
.wrapToIridium()
Copy link
Contributor

Choose a reason for hiding this comment

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

It's easy to miss this wrap on new request implementation. We need to configure it once IG

Copy link
Author

Choose a reason for hiding this comment

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

In one of my first experiments, the wrap + transform into RPC needed to be called only in a single place before sending the message, but I couldn't fit it in the current implementation (because of the callback problems that I was trying to solve). I agree that it should be called just once.

var cancellable: AnyCancellable?
dispatcher.send(requestJson) { [weak self] error in
cancellable = subscriptionResponsePublisher
Copy link
Contributor

Choose a reason for hiding this comment

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

is it possible to replace completion with withCheckedThrowingContinuation? To make this method async?

Copy link
Author

Choose a reason for hiding this comment

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

The problem is that we need to wait for the acknowledgement with the subscription ID, the same problem that I was talking about in out sync meeting, that I couldn't solve yet

var cancellable: AnyCancellable?
dispatcher.send(requestJson) { [weak self] error in
cancellable = requestAcknowledgePublisher
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to find way how to move requestAcknowledgePublisher subscription out of this method.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, the RelayClient refactor is not over

@sekimondre sekimondre merged commit 21b6598 into develop Aug 1, 2022
@sekimondre sekimondre deleted the refactor/#355-relay-rpc branch August 1, 2022 15:29
}
let id: RPCID
let topic: String
let origin: Origin
Copy link
Contributor

Choose a reason for hiding this comment

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

just noticed it. What is the purpose of origin?

Copy link
Author

Choose a reason for hiding this comment

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

Being able to track whether a request was sent from the client or from the peer. Thinking on long term support, I think is a nice thing to have, might be useful in the future

if let request = tryDecode(RPCRequest.self, from: payload) {
if let params = try? request.params?.get(Subscription.Params.self) {
do {
try rpcHistory.set(request, forTopic: params.data.topic, emmitedBy: .remote)
Copy link
Contributor

Choose a reason for hiding this comment

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

it is the only place where we are setting a history record. Should we record history when requesting as well?

Copy link
Author

Choose a reason for hiding this comment

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

Right now, it's the only place. I'm in for recording all requests, so we can have a complete history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants