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

Synchronization of proxies for a network after adding/removing proxies in staking #959

Merged
merged 6 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions novawallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@
77A0B2F92A3CA40E00CBF653 /* StakingMoreOptionsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A0B2F82A3CA40E00CBF653 /* StakingMoreOptionsSection.swift */; };
77A4F4012B035027006294BC /* AssetOperationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4F4002B035027006294BC /* AssetOperationState.swift */; };
77A4F4032B036615006294BC /* Optional+Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4F4022B036615006294BC /* Optional+Result.swift */; };
77A502F12B63E3830062FA51 /* ProxyAccountSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A502F02B63E3830062FA51 /* ProxyAccountSubscription.swift */; };
77A6F5AB2A2E0B31004AFD1A /* ReceiveAssetOperationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A6F5AA2A2E0B31004AFD1A /* ReceiveAssetOperationPresenter.swift */; };
77A6F5AE2A2E0C7C004AFD1A /* ReceiveAssetOperationWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A6F5AD2A2E0C7C004AFD1A /* ReceiveAssetOperationWireframe.swift */; };
77A6F5B92A2E2AAD004AFD1A /* BuyAssetOperationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A6F5B72A2E2AAD004AFD1A /* BuyAssetOperationPresenter.swift */; };
Expand Down Expand Up @@ -1047,6 +1048,7 @@
77F9FB0D2A9D9C5600820625 /* NominationPoolBondMoreBaseProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F9FB0C2A9D9C5600820625 /* NominationPoolBondMoreBaseProtocols.swift */; };
77FDFC462B0DEB34005A3569 /* OpenDAppUrlParsingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FDFC452B0DEB34005A3569 /* OpenDAppUrlParsingService.swift */; };
77FDFC482B0E1CBD005A3569 /* ImportWalletUrlParsingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FDFC472B0E1CBD005A3569 /* ImportWalletUrlParsingService.swift */; };
77FE76402B67682A00ADA73F /* ProxyAccountUpdatingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FE763F2B67682A00ADA73F /* ProxyAccountUpdatingService.swift */; };
77FF02692B21ECB900B655BC /* ProxyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FF02682B21ECB900B655BC /* ProxyTableViewCell.swift */; };
77FFB2B12B0D392A00C7C879 /* OpenStakingUrlParsingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FFB2B02B0D392A00C7C879 /* OpenStakingUrlParsingService.swift */; };
77FFB2B32B0D394700C7C879 /* OpenGovernanceUrlParsingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FFB2B22B0D394700C7C879 /* OpenGovernanceUrlParsingService.swift */; };
Expand Down Expand Up @@ -5192,6 +5194,7 @@
77A0B2F82A3CA40E00CBF653 /* StakingMoreOptionsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingMoreOptionsSection.swift; sourceTree = "<group>"; };
77A4F4002B035027006294BC /* AssetOperationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetOperationState.swift; sourceTree = "<group>"; };
77A4F4022B036615006294BC /* Optional+Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Result.swift"; sourceTree = "<group>"; };
77A502F02B63E3830062FA51 /* ProxyAccountSubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyAccountSubscription.swift; sourceTree = "<group>"; };
77A6F5AA2A2E0B31004AFD1A /* ReceiveAssetOperationPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiveAssetOperationPresenter.swift; sourceTree = "<group>"; };
77A6F5AD2A2E0C7C004AFD1A /* ReceiveAssetOperationWireframe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveAssetOperationWireframe.swift; sourceTree = "<group>"; };
77A6F5B72A2E2AAD004AFD1A /* BuyAssetOperationPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuyAssetOperationPresenter.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5314,6 +5317,7 @@
77F9FB0C2A9D9C5600820625 /* NominationPoolBondMoreBaseProtocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NominationPoolBondMoreBaseProtocols.swift; sourceTree = "<group>"; };
77FDFC452B0DEB34005A3569 /* OpenDAppUrlParsingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenDAppUrlParsingService.swift; sourceTree = "<group>"; };
77FDFC472B0E1CBD005A3569 /* ImportWalletUrlParsingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletUrlParsingService.swift; sourceTree = "<group>"; };
77FE763F2B67682A00ADA73F /* ProxyAccountUpdatingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyAccountUpdatingService.swift; sourceTree = "<group>"; };
77FF02682B21ECB900B655BC /* ProxyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyTableViewCell.swift; sourceTree = "<group>"; };
77FFB2B02B0D392A00C7C879 /* OpenStakingUrlParsingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenStakingUrlParsingService.swift; sourceTree = "<group>"; };
77FFB2B22B0D394700C7C879 /* OpenGovernanceUrlParsingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGovernanceUrlParsingService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -16177,6 +16181,8 @@
0CC2E5612A6E5C43004092E7 /* NominationPoolsAccountUpdatingService.swift */,
0CE1504F2A6FAC1200B61CC1 /* NominationPoolsPoolSubscriptionService.swift */,
0CAC02FC2B4BFB9400DDEC3A /* WalletRemoteQueryWrapperFactory.swift */,
77FE763F2B67682A00ADA73F /* ProxyAccountUpdatingService.swift */,
77A502F02B63E3830062FA51 /* ProxyAccountSubscription.swift */,
);
path = Substrate;
sourceTree = "<group>";
Expand Down Expand Up @@ -23630,6 +23636,7 @@
848F8B2B2864824200204BC4 /* AssetListChainControlView.swift in Sources */,
4F03B1138E7160AEA00B7793 /* ParaStkCollatorInfoViewFactory.swift in Sources */,
0119531FAE0D22EA9464F84D /* ParaStkYourCollatorsProtocols.swift in Sources */,
77FE76402B67682A00ADA73F /* ProxyAccountUpdatingService.swift in Sources */,
542588DA751A44C993BC1F27 /* ParaStkYourCollatorsWireframe.swift in Sources */,
D6D9D16440AB588F581AF5BA /* ParaStkYourCollatorsPresenter.swift in Sources */,
FA62AACACA15CB04275DE957 /* ParaStkYourCollatorsInteractor.swift in Sources */,
Expand Down Expand Up @@ -24209,6 +24216,7 @@
77F9FB0B2A9D97A100820625 /* NominationPoolBondMoreSetupWireframe.swift in Sources */,
F0C3DCA3CD4F850C16406716 /* GovernanceDelegateSearchViewFactory.swift in Sources */,
0C13DFD52AFA4F1500E5F355 /* SwapIssueCheckParams.swift in Sources */,
77A502F12B63E3830062FA51 /* ProxyAccountSubscription.swift in Sources */,
C98A02D4DEAC6E4CACB9E47E /* StakingRebagConfirmProtocols.swift in Sources */,
B09F155D14D146377FB2952A /* StakingRebagConfirmWireframe.swift in Sources */,
840AE2E329C9A715008FF665 /* OptionStringCodable+Empty.swift in Sources */,
Expand Down
46 changes: 33 additions & 13 deletions novawallet/Common/Services/Proxy/ChainProxySyncService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import SubstrateSdk
import RobinHood
import BigInt

final class ChainProxySyncService: ObservableSyncService, AnyCancellableCleaning {
protocol ChainProxySyncServiceProtocol: ObservableSyncServiceProtocol {
func sync(at blockHash: Data?)
}

final class ChainProxySyncService: ObservableSyncService, ChainProxySyncServiceProtocol, AnyCancellableCleaning {
let walletUpdateMediator: WalletUpdateMediating
let metaAccountsRepository: AnyDataProviderRepository<ManagedMetaAccountModel>
let chainRegistry: ChainRegistryProtocol
Expand Down Expand Up @@ -47,8 +51,33 @@ final class ChainProxySyncService: ObservableSyncService, AnyCancellableCleaning
}

override func performSyncUp() {
let chainId = chainModel.chainId
performSync(at: nil)
}

func sync(at blockHash: Data?) {
mutex.lock()

defer {
mutex.unlock()
}

guard isActive else {
return
}

if isSyncing {
stopSyncUp()

isSyncing = false
}

isSyncing = true

performSync(at: blockHash)
}

func performSync(at blockHash: Data?) {
let chainId = chainModel.chainId
guard let connection = chainRegistry.getConnection(for: chainId) else {
completeImmediate(ChainRegistryError.connectionUnavailable)
return
Expand All @@ -59,22 +88,13 @@ final class ChainProxySyncService: ObservableSyncService, AnyCancellableCleaning
return
}

performSyncUp(
connection: connection,
runtimeProvider: runtimeProvider
)
}

private func performSyncUp(
connection: JSONRPCEngine,
runtimeProvider: RuntimeCodingServiceProtocol
) {
pendingCall.cancel()

let proxyListWrapper = proxyOperationFactory.fetchProxyList(
requestFactory: requestFactory,
connection: connection,
runtimeProvider: runtimeProvider
runtimeProvider: runtimeProvider,
at: blockHash
)

let walletsWrapper = createWalletsWrapper(for: chainWalletFilter, chain: chainModel)
Expand Down
11 changes: 8 additions & 3 deletions novawallet/Common/Services/Proxy/ProxyOperationFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ protocol ProxyOperationFactoryProtocol {
func fetchProxyList(
requestFactory: StorageRequestFactoryProtocol,
connection: JSONRPCEngine,
runtimeProvider: RuntimeCodingServiceProtocol
runtimeProvider: RuntimeCodingServiceProtocol,
at blockHash: Data?
) -> CompoundOperationWrapper<[ProxiedAccountId: [ProxyAccount]]>
}

final class ProxyOperationFactory: ProxyOperationFactoryProtocol {
func fetchProxyList(
requestFactory: StorageRequestFactoryProtocol,
connection: JSONRPCEngine,
runtimeProvider: RuntimeCodingServiceProtocol
runtimeProvider: RuntimeCodingServiceProtocol,
at blockHash: Data?
) -> CompoundOperationWrapper<[ProxiedAccountId: [ProxyAccount]]> {
let request = UnkeyedRemoteStorageRequest(storagePath: Proxy.proxyList)
let codingFactoryOperation = runtimeProvider.fetchCoderFactoryOperation()

let options = StorageQueryListOptions(ignoresFailedItems: true)
let options = StorageQueryListOptions(
atBlock: blockHash,
ignoresFailedItems: true
)
let fetchWrapper: CompoundOperationWrapper<[AccountIdKey: ProxyDefinition]> =
requestFactory.queryByPrefix(
engine: connection,
Expand Down
13 changes: 12 additions & 1 deletion novawallet/Common/Services/Proxy/ProxySyncService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ protocol ProxySyncServiceProtocol: ApplicationServiceProtocol {
func unsubscribeSyncState(_ target: AnyObject)
func updateWalletsStatuses()
func syncUp()
func syncUp(
chainId: ChainModel.Id,
blockHash: Data?
)
}

typealias ProxySyncChainFilter = (ChainModel) -> Bool
Expand All @@ -32,7 +36,7 @@ final class ProxySyncService {

private(set) var isActive: Bool = false

private(set) var updaters: [ChainModel.Id: ObservableSyncServiceProtocol & ApplicationServiceProtocol] = [:]
private(set) var updaters: [ChainModel.Id: ChainProxySyncServiceProtocol & ApplicationServiceProtocol] = [:]
private let mutex = NSLock()

private var stateObserver = Observable<ProxySyncServiceState>(state: [:])
Expand Down Expand Up @@ -272,4 +276,11 @@ extension ProxySyncService: ProxySyncServiceProtocol {
func syncUp() {
updaters.values.forEach { $0.syncUp() }
}

func syncUp(
chainId: ChainModel.Id,
blockHash: Data?
) {
updaters[chainId]?.sync(at: blockHash)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Foundation
import RobinHood
import IrohaCrypto
import SubstrateSdk

final class ProxyAccountSubscription: WebSocketSubscribing {
let accountId: AccountId
let chainId: ChainModel.Id
let chainRegistry: ChainRegistryProtocol
let proxySyncService: ProxySyncServiceProtocol
let storageFacade: StorageFacadeProtocol
let logger: LoggerProtocol?

private let mutex = NSLock()
private let operationQueue: OperationQueue
private let workingQueue: DispatchQueue
private var subscription: CallbackBatchStorageSubscription<BatchSubscriptionHandler>?
private var storageSubscriptionHandler: StorageChildSubscribing?

private lazy var repository: AnyDataProviderRepository<ChainStorageItem> = {
let coreDataRepository: CoreDataRepository<ChainStorageItem, CDChainStorageItem> =
storageFacade.createRepository()
return AnyDataProviderRepository(coreDataRepository)
}()

init(
accountId: AccountId,
chainId: ChainModel.Id,
chainRegistry: ChainRegistryProtocol,
proxySyncService: ProxySyncServiceProtocol,
storageFacade: StorageFacadeProtocol,
operationQueue: OperationQueue,
workingQueue: DispatchQueue,
logger: LoggerProtocol? = nil
) {
self.accountId = accountId
self.chainId = chainId
self.chainRegistry = chainRegistry
self.proxySyncService = proxySyncService
self.operationQueue = operationQueue
self.workingQueue = workingQueue
self.logger = logger
self.storageFacade = storageFacade

do {
try subscribeRemote(for: accountId)
} catch {
logger?.error(error.localizedDescription)
}
}

deinit {
unsubscribeRemote()
}

private func unsubscribeRemote() {
subscription?.unsubscribe()
subscription = nil
}

private func subscribeRemote(for accountId: AccountId) throws {
guard let connection = chainRegistry.getConnection(for: chainId) else {
throw ChainRegistryError.connectionUnavailable
}
guard let runtimeService = chainRegistry.getRuntimeProvider(for: chainId) else {
throw ChainRegistryError.runtimeMetadaUnavailable
}

let localKey = try LocalStorageKeyFactory().createFromStoragePath(
Proxy.proxyList,
accountId: accountId,
chainId: chainId
)

let request = BatchStorageSubscriptionRequest(
innerRequest: MapSubscriptionRequest(
storagePath: Proxy.proxyList,
localKey: localKey
) {
BytesCodable(wrappedValue: accountId)
},
mappingKey: nil
)

subscription = CallbackBatchStorageSubscription(
requests: [request],
connection: connection,
runtimeService: runtimeService,
repository: repository,
operationQueue: operationQueue,
callbackQueue: workingQueue
) { [weak self] result in
self?.mutex.lock()

self?.handleSubscription(result)

self?.mutex.unlock()
}

subscription?.subscribe()
}

private func handleSubscription(_ result: Result<BatchSubscriptionHandler, Error>) {
switch result {
case let .success(handler):
if let blockHash = handler.blockHash {
proxySyncService.syncUp(chainId: chainId, blockHash: blockHash)
}
case let .failure(error):
logger?.error(error.localizedDescription)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation

protocol ProxyAccountUpdatingServiceProtocol {
func setupSubscription(
for accountId: AccountId,
chainId: ChainModel.Id
) throws

func clearSubscription()
}

class ProxyAccountUpdatingService: ProxyAccountUpdatingServiceProtocol {
private var proxySubscription: ProxyAccountSubscription?

let chainRegistry: ChainRegistryProtocol
let proxySyncService: ProxySyncServiceProtocol
let operationQueue: OperationQueue
let logger: LoggerProtocol?
let storageFacade: StorageFacadeProtocol

init(
chainRegistry: ChainRegistryProtocol,
proxySyncService: ProxySyncServiceProtocol,
storageFacade: StorageFacadeProtocol,
operationQueue: OperationQueue,
logger: LoggerProtocol? = nil
) {
self.chainRegistry = chainRegistry
self.proxySyncService = proxySyncService
self.storageFacade = storageFacade
self.operationQueue = operationQueue
self.logger = logger
}

func setupSubscription(
for accountId: AccountId,
chainId: ChainModel.Id
) throws {
proxySubscription = ProxyAccountSubscription(
accountId: accountId,
chainId: chainId,
chainRegistry: chainRegistry,
proxySyncService: proxySyncService,
storageFacade: storageFacade,
operationQueue: operationQueue,
workingQueue: .init(label: "com.novawallet.proxy.updating", qos: .userInitiated)
)
}

func clearSubscription() {
proxySubscription = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ final class StakingAccountSubscription: WebSocketSubscribing {
let childSubscriptionFactory: ChildSubscriptionFactoryProtocol
let operationQueue: OperationQueue
let logger: LoggerProtocol?
let chainHasProxy: Bool
private let mutex = NSLock()

private var subscription: Subscription?
Expand All @@ -48,7 +47,6 @@ final class StakingAccountSubscription: WebSocketSubscribing {
accountId: AccountId,
chainId: ChainModel.Id,
chainFormat: ChainFormat,
chainHasProxy: Bool,
chainRegistry: ChainRegistryProtocol,
provider: StreamableProvider<StashItem>,
childSubscriptionFactory: ChildSubscriptionFactoryProtocol,
Expand All @@ -58,7 +56,6 @@ final class StakingAccountSubscription: WebSocketSubscribing {
self.accountId = accountId
self.chainId = chainId
self.chainFormat = chainFormat
self.chainHasProxy = chainHasProxy
self.chainRegistry = chainRegistry
self.provider = provider
self.childSubscriptionFactory = childSubscriptionFactory
Expand Down Expand Up @@ -132,10 +129,6 @@ final class StakingAccountSubscription: WebSocketSubscribing {
)
}

if chainHasProxy {
requests.append(.init(storagePath: Proxy.proxyList, accountId: stashId))
}

return requests
}

Expand Down
Loading
Loading