diff --git a/framework/core/core.go b/framework/core/core.go index c38ffa4..c221ffb 100644 --- a/framework/core/core.go +++ b/framework/core/core.go @@ -162,27 +162,36 @@ func NewService(config *BridgeConfig) (*Service, error) { } // setup mDNS - if config.MdnsLockerDriver != nil { - mdnslogger := logger.Named("mdns") - - s.mdnsLocker.Lock() - dh := mdns.DiscoveryHandler(ctx, mdnslogger, s.ipfsCoreAPI) - mdnsService := mdns.NewMdnsService(mdnslogger, s.ipfsCoreAPI, mdns.MDNSServiceName, dh) - - go func() { - mdnsNetworkManagerConfig := mdns.NetworkManagerConfig{ - Logger: mdnslogger, - NetManager: s.netmanager, - Service: mdnsService, - } - mdns.NetworkManagerHandler(ctx, mdnsNetworkManagerConfig) - }() + { + if config.MdnsLockerDriver != nil { + s.mdnsLocker.Lock() - close = closeFunc(close, func() error { - mdnsService.Close() - s.mdnsLocker.Unlock() - return nil - }) + close = closeFunc(close, func() error { + s.mdnsLocker.Unlock() + return nil + }) + } + + if config.ConnectivityDriver != nil { + mdnslogger := logger.Named("mdns") + + dh := mdns.DiscoveryHandler(ctx, mdnslogger, s.ipfsCoreAPI) + mdnsService := mdns.NewMdnsService(mdnslogger, s.ipfsCoreAPI, mdns.MDNSServiceName, dh) + + go func() { + mdnsNetworkManagerConfig := mdns.NetworkManagerConfig{ + Logger: mdnslogger, + NetManager: s.netmanager, + Service: mdnsService, + } + mdns.NetworkManagerHandler(ctx, mdnsNetworkManagerConfig) + }() + + close = closeFunc(close, func() error { + mdnsService.Close() + return nil + }) + } } s.service, err = weshnet.NewService(weshnet.Opts{ diff --git a/gen-clients.js b/gen-clients.js index 624729b..d3718f8 100644 --- a/gen-clients.js +++ b/gen-clients.js @@ -19,7 +19,7 @@ const services = ["weshnet.protocol.v1.ProtocolService"]; // Prepare Handlebars templates const serviceClientTemplate = Handlebars.compile(` -import api from './api/index.d' +import { protocol } from "./api/index.d"; import { Unary, ResponseStream, RequestStream } from './types' export type ServiceClientType = {{#each services}} {{this}} {{/each}}never; @@ -28,7 +28,7 @@ export type ServiceClientType = {{#each services}} {{this}} {{/each}}never; const ClientTemplate = Handlebars.compile(` export interface {{name}}Client { {{#each methods}} - {{this.name}}: {{this.type}}, + {{this.name}}: {{this.type}}<{{this.svcName}}.{{this.request}}, {{this.svcName}}.{{this.reply}}>, {{/each}} } `); @@ -37,7 +37,7 @@ export interface {{name}}Client { let serviceData = { services: services.map((svcType) => { const svc = pb.lookup(svcType); - return `S extends typeof api.${svc.parent.parent.name}.${svc.name} ? ${svc.name}Client :`; + return `S extends typeof ${svc.parent.parent.name}.${svc.name} ? ${svc.name}Client :`; }), }; diff --git a/ios/src/Sources/ConnectivityDriver.swift b/ios/src/Sources/ConnectivityDriver.swift new file mode 100644 index 0000000..1a76048 --- /dev/null +++ b/ios/src/Sources/ConnectivityDriver.swift @@ -0,0 +1,132 @@ +// +// ConnectivityDriver.swift +// Berty +// +// Created by u on 01/02/2023. +// + +import SystemConfiguration +import CoreBluetooth +import CoreTelephony +import Foundation +import WeshnetCore +import Network + +class ConnectivityDriver: NSObject, WeshnetCoreIConnectivityDriverProtocol, CBCentralManagerDelegate { + let queue = DispatchQueue.global(qos: .background) + let pathMonitor = NWPathMonitor() + var centralManager: CBCentralManager! + + var state: WeshnetCoreConnectivityInfo + var handlers: [WeshnetCoreIConnectivityHandlerProtocol] = [] + + override init() { + self.state = WeshnetCoreConnectivityInfo()! + + super.init() + + self.centralManager = CBCentralManager(delegate: self, queue: nil) + + self.pathMonitor.pathUpdateHandler = { [weak self] path in + self!.updateNetworkState(path) + + for handler in self!.handlers { + handler.handleConnectivityUpdate(self!.state) + } + } + self.pathMonitor.start(queue: self.queue) + } + + func updateNetworkState(_ info: NWPath) { + self.state.setState(info.status == .satisfied ? WeshnetCoreConnectivityStateOn : WeshnetCoreConnectivityStateOff) + self.state.setMetering(WeshnetCoreConnectivityStateUnknown) + self.state.setNetType(WeshnetCoreConnectivityNetUnknown) + self.state.setCellularType(WeshnetCoreConnectivityCellularUnknown) + + if info.status != .satisfied { + return + } + + if #available(iOS 13.0, *) { + self.state.setMetering(info.isConstrained ? WeshnetCoreConnectivityStateOn : WeshnetCoreConnectivityStateOff) + } + + if let interface = self.pathMonitor.currentPath.availableInterfaces.first { + switch interface.type { + case .wifi: + self.state.setNetType(WeshnetCoreConnectivityNetWifi) + case .cellular: + self.state.setNetType(WeshnetCoreConnectivityNetCellular) + self.state.setCellularType(ConnectivityDriver.getCellularType()) + case .wiredEthernet: + self.state.setNetType(WeshnetCoreConnectivityNetEthernet) + default: + self.state.setNetType(WeshnetCoreConnectivityNetUnknown) + } + } + } + + static func getCellularType() -> Int { + let networkInfo = CTTelephonyNetworkInfo() + + guard let carrierType = networkInfo.serviceCurrentRadioAccessTechnology?.first?.value else { + return WeshnetCoreConnectivityCellularNone + } + + switch carrierType { + case CTRadioAccessTechnologyGPRS, + CTRadioAccessTechnologyEdge, + CTRadioAccessTechnologyCDMA1x: + return WeshnetCoreConnectivityCellular2G + case CTRadioAccessTechnologyWCDMA, + CTRadioAccessTechnologyHSDPA, + CTRadioAccessTechnologyHSUPA, + CTRadioAccessTechnologyCDMAEVDORev0, + CTRadioAccessTechnologyCDMAEVDORevA, + CTRadioAccessTechnologyCDMAEVDORevB, + CTRadioAccessTechnologyeHRPD: + return WeshnetCoreConnectivityCellular3G + case CTRadioAccessTechnologyLTE: + return WeshnetCoreConnectivityCellular4G + default: + if #available(iOS 14.1, *) { + if carrierType == CTRadioAccessTechnologyNRNSA + || carrierType == CTRadioAccessTechnologyNR { + return WeshnetCoreConnectivityCellular5G + } + } + + return WeshnetCoreConnectivityCellularUnknown + } + } + + func centralManagerDidUpdateState(_ central: CBCentralManager) { + switch central.state { + case .poweredOn: + self.state.setBluetooth(WeshnetCoreConnectivityStateOn) + case .poweredOff, + .unsupported, + .unauthorized, + .resetting: + self.state.setBluetooth(WeshnetCoreConnectivityStateOff) + case .unknown: + self.state.setBluetooth(WeshnetCoreConnectivityStateUnknown) + @unknown default: + self.state.setBluetooth(WeshnetCoreConnectivityStateUnknown) + } + + for handler in self.handlers { + handler.handleConnectivityUpdate(self.state) + } + } + + public func getCurrentState() -> WeshnetCoreConnectivityInfo? { + return self.state + } + + public func register(_ handler: WeshnetCoreIConnectivityHandlerProtocol?) { + if (handler != nil) { + self.handlers.append(handler!) + } + } +} diff --git a/ios/src/PromiseBlock.swift b/ios/src/Sources/PromiseBlock.swift similarity index 100% rename from ios/src/PromiseBlock.swift rename to ios/src/Sources/PromiseBlock.swift diff --git a/ios/src/WeshnetExpoModule.swift b/ios/src/WeshnetExpoModule.swift index 271ed09..d27cd24 100644 --- a/ios/src/WeshnetExpoModule.swift +++ b/ios/src/WeshnetExpoModule.swift @@ -4,6 +4,8 @@ import WeshnetCore public class WeshnetExpoModule: Module { var service: WeshnetCoreService? var appRootDir: String? + var connectivityDriver: ConnectivityDriver? + public func definition() -> ModuleDefinition { Name("WeshnetExpo") @@ -11,6 +13,7 @@ public class WeshnetExpoModule: Module { OnCreate { do { self.appRootDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).path + self.connectivityDriver = ConnectivityDriver() } catch let error as NSError { NSLog("Error creating app root directory: \(error.localizedDescription)") } @@ -50,6 +53,8 @@ public class WeshnetExpoModule: Module { } config.rootDir = self.appRootDir! + config.connectivityDriver = self.connectivityDriver + guard let service = WeshnetCoreNewService(config, &err) else { throw WeshnetError(.coreError(err!)) } diff --git a/src/api/index.ts b/src/api/index.ts index bf2a552..c369023 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,13 +1,33 @@ -import { default as protocolpb } from './protocoltypes.pb' -import { default as rpcmanagerpb } from './rpcmanager.pb' - -export const protocol: typeof protocolpb.weshnet.protocol.v1 = ( - protocolpb as any -).lookup('weshnet.protocol.v1') -export const rpcmanager: typeof rpcmanagerpb.rpcmanager = ( - rpcmanagerpb as any -).lookup('rpcmanager') - -// const protocol = pbp -// const rpcmanager = rpcmanager -export default { protocol, rpcmanager } +// import { weshnet as protocolpb } from "./protocoltypes.pb"; +// import { rpcmanager as rpcmanagerpb } from "./rpcmanager.pb"; +// +// export const protocol: typeof protocolpb.protocol.v1 = ( +// protocolpb as any +// ).lookup("weshnet.protocol.v1"); +// export const rpcmanager: typeof rpcmanagerpb = (rpcmanagerpb as any).lookup( +// "rpcmanager", +// ); +// +// export default { protocol, rpcmanager }; +// export { weshnet } from "./protocoltypes.pb"; +// export { rpcmanager } from "./rpcmanager.pb"; +// + +import * as pbjs from "protobufjs"; +// import type { rpcmanager as rpcmanagerpb } from "./index.d"; +// import type { protocol as protocolpb } from "./index.d"; + +var protocol; +var rpcmanager; + +pbjs.load("./protocoltypes.pb.js", (err, root) => { + if (!root) { + console.error(err); + return; + } + + protocol = root.lookup("weshnet.protocol.v1"); + rpcmanager = root.lookup("rpcmanager"); +}); + +export { protocol, rpcmanager }; diff --git a/src/error.ts b/src/error.ts index cefa86f..ca58cf3 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,38 +1,38 @@ -import api from './api' -import types from './api/index.d' +import type { rpcmanager as rpcmanagerpb } from "./api/index.d"; +import { rpcmanager } from "./api"; class GRPCError extends Error { - public EOF: boolean - public OK: boolean + public EOF: boolean; + public OK: boolean; // public Code: beerrcode.ErrCode | beweshnet_errcode.ErrCode - public GrpcCode: types.rpcmanager.GRPCErrCode + public GrpcCode: rpcmanagerpb.GRPCErrCode; - public error: types.rpcmanager.Error + public error: rpcmanagerpb.Error; - constructor(e: types.rpcmanager.IError | null | undefined) { + constructor(e: rpcmanagerpb.IError | null | undefined) { if (!e) { // this should not happen, but should not break the app either. // instead simply create a empty error and warn about this console.warn( `GRPCError: (${e}) grpc error provided, empty error returned`, - ) - e = api.rpcmanager.Error.create({}) + ); + e = rpcmanager.Error.create({}); } - const error = api.rpcmanager.Error.create(e) - super(error.message) + const error = rpcmanager.Error.create(e); + super(error.message); - this.error = error + this.error = error; // this.Code = error.errorCode - this.GrpcCode = error.grpcErrorCode + this.GrpcCode = error.grpcErrorCode; - this.OK = error.grpcErrorCode === api.rpcmanager.GRPCErrCode.OK + this.OK = error.grpcErrorCode === rpcmanager.GRPCErrCode.OK; // error.errorCode === beerrcode.ErrCode.Undefined this.EOF = - error.grpcErrorCode === api.rpcmanager.GRPCErrCode.CANCELED || - (error.grpcErrorCode === api.rpcmanager.GRPCErrCode.UNKNOWN && - error.message === 'EOF') + error.grpcErrorCode === rpcmanager.GRPCErrCode.CANCELED || + (error.grpcErrorCode === rpcmanager.GRPCErrCode.UNKNOWN && + error.message === "EOF"); } // public details(): beerrcode.ErrDetails { @@ -47,8 +47,8 @@ class GRPCError extends Error { // return this.Code // } - public grpcErrorCode(): types.rpcmanager.GRPCErrCode { - return this.GrpcCode + public grpcErrorCode(): rpcmanagerpb.GRPCErrCode { + return this.GrpcCode; } public toJSON(): any { @@ -58,12 +58,12 @@ class GRPCError extends Error { return { message: this.message, - grpcErrorCode: api.rpcmanager.GRPCErrCode[this.GrpcCode], + grpcErrorCode: rpcmanager.GRPCErrCode[this.GrpcCode], // errorCode: beerrcode.ErrCode[this.Code], // details: details, EOF: this.EOF, OK: this.OK, - } + }; } // public hasErrCode(error: beerrcode.ErrCode): boolean { @@ -72,16 +72,16 @@ class GRPCError extends Error { } const newGRPCError = (code: number, message: string): GRPCError => { - const error = api.rpcmanager.Error.fromObject({ + const error = rpcmanager.Error.fromObject({ message: message, grpcErrorCode: code, - }) - return new GRPCError(error) -} + }); + return new GRPCError(error); +}; const EOF = new GRPCError({ - grpcErrorCode: api.rpcmanager.GRPCErrCode.CANCELED, - message: 'EOF', -}) + grpcErrorCode: rpcmanager.GRPCErrCode.CANCELED, + message: "EOF", +}); -export { GRPCError, EOF, newGRPCError } +export { GRPCError, EOF, newGRPCError }; diff --git a/src/index.ts b/src/index.ts index 2bc7608..43c8ee5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,17 +2,15 @@ // Import the native module. On web, it will be resolved to WeshnetExpo.web.ts // and on native platforms to WeshnetExpo.ts -import api from './api' -import { rpcBridgeImpl } from './rpc' -import { createServiceClient } from './service' -import { ProtocolServiceClient } from './weshnet.types.gen' -import WeshnetExpoModule from './WeshnetExpoModule' +import { protocol } from "./api/index.d"; +import { rpcBridgeImpl } from "./rpc"; +import { createServiceClient } from "./service"; +import { ProtocolServiceClient } from "./weshnet.types.gen"; +import WeshnetExpoModule from "./WeshnetExpoModule"; export async function init(): Promise { return WeshnetExpoModule.init() .then(() => WeshnetExpoModule.init()) - .then(() => - createServiceClient(api.protocol.ProtocolService, rpcBridgeImpl), - ) - .catch((err: any) => console.error('init error', err)) + .then(() => createServiceClient(protocol.ProtocolService, rpcBridgeImpl)) + .catch((err: any) => console.error("init error", err)); } diff --git a/src/rpc/rpc.bridge.ts b/src/rpc/rpc.bridge.ts index 019a80e..e46d8d1 100644 --- a/src/rpc/rpc.bridge.ts +++ b/src/rpc/rpc.bridge.ts @@ -1,17 +1,16 @@ -import * as pbjs from 'protobufjs' - -import rpcNative from './rpc.native' -import { getServiceName } from './utils' -import { rpcmanager } from '../api/index' -import types from '../api/index.d' -import { GRPCError, EOF } from '../error' -import { createServiceClient } from '../service' +import * as pbjs from "protobufjs"; + +import rpcNative from "./rpc.native"; +import { getServiceName } from "./utils"; +import { rpcmanager } from "../api/index.d"; +import { GRPCError, EOF } from "../error"; +import { createServiceClient } from "../service"; // import { ServiceClientType } from '../welsh-clients.gen' const ErrStreamClientAlreadyStarted = new GRPCError({ // grpcErrorCode: beapi.bridge.GRPCErrCode.CANCELED, - message: 'client stream not started or has been closed', -}) + message: "client stream not started or has been closed", +}); const makeStreamClient = ( streamid: string, @@ -23,131 +22,142 @@ const makeStreamClient = ( started: false, _publish(...args: unknown[]) { - this.events.forEach(listener => listener.apply(this, args)) + this.events.forEach((listener) => listener.apply(this, args)); }, onMessage(listener: (...a: unknown[]) => void) { - this.events.push(listener) + this.events.push(listener); }, async emit(payload: Uint8Array) { const response = await bridgeClient.clientStreamSend({ streamId: streamid, payload, - }) + }); // check for error if (response.error) { - const grpcerr = new GRPCError(response.error) + const grpcerr = new GRPCError(response.error); if (!grpcerr.OK) { - throw grpcerr + throw grpcerr; } } }, async start() { if (this.started) { - throw ErrStreamClientAlreadyStarted + throw ErrStreamClientAlreadyStarted; } - this.started = true - + this.started = true; - let response: types.rpcmanager.ClientStreamRecv.Reply + let response: rpcmanager.ClientStreamRecv.Reply; for (;;) { - response = await bridgeClient.clientStreamRecv({ streamId: streamid }) - const grpcerr = new GRPCError(response.error) + response = await bridgeClient.clientStreamRecv({ streamId: streamid }); + const grpcerr = new GRPCError(response.error); if (!grpcerr.OK) { - this._publish(null, grpcerr) - return + this._publish(null, grpcerr); + return; } - this._publish(response.payload, null) + this._publish(response.payload, null); } }, async stop() { if (!this.started) { - throw ErrStreamClientAlreadyStarted + throw ErrStreamClientAlreadyStarted; } - const response = await bridgeClient.clientStreamClose({ streamId: streamid }) - const grpcerr = new GRPCError(response.error) + const response = await bridgeClient.clientStreamClose({ + streamId: streamid, + }); + const grpcerr = new GRPCError(response.error); if (!grpcerr.OK) { - this._publish(null, grpcerr) - return + this._publish(null, grpcerr); + return; } - return + return; }, async stopAndRecv() { if (this.started) { - throw ErrStreamClientAlreadyStarted + throw ErrStreamClientAlreadyStarted; } - const response = await bridgeClient.clientStreamCloseAndRecv({ streamId: streamid }) - const grpcerr = new GRPCError(response.error) + const response = await bridgeClient.clientStreamCloseAndRecv({ + streamId: streamid, + }); + const grpcerr = new GRPCError(response.error); if (!grpcerr.OK) { - this._publish(null, grpcerr) - return + this._publish(null, grpcerr); + return; } - const payload = method.resolvedResponseType?.decode(response.payload) - return payload + const payload = method.resolvedResponseType?.decode(response.payload); + return payload; }, - } + }; return { __proto__: eventEmitter, events: [], started: false, - } -} + }; +}; const unary = - (bridgeClient: any) => - async (method: M, request: Uint8Array, _metadata?: never) => { - const methodDesc = { - name: `/${getServiceName(method)}/${method.name}`, - } - - const response = await bridgeClient.clientInvokeUnary({ - methodDesc: methodDesc, - payload: request, - // metadata: {}, // @TODO: pass metdate object - }) - const grpcerr = new GRPCError(response.error) - if (!grpcerr.OK) { - throw grpcerr - } - - return response.payload - } + (bridgeClient: any) => + async ( + method: M, + request: Uint8Array, + _metadata?: never, + ) => { + const methodDesc = { + name: `/${getServiceName(method)}/${method.name}`, + }; + + const response = await bridgeClient.clientInvokeUnary({ + methodDesc: methodDesc, + payload: request, + // metadata: {}, // @TODO: pass metdate object + }); + const grpcerr = new GRPCError(response.error); + if (!grpcerr.OK) { + throw grpcerr; + } + + return response.payload; + }; const stream = - (bridgeClient: any) => - async (method: M, request: Uint8Array, _metadata?: never) => { - const methodDesc = { - name: `/${getServiceName(method)}/${method.name}`, - - isClientStream: !!method.requestStream, - isServerStream: !!method.responseStream, - } - - const response = await bridgeClient.createClientStream({ - methodDesc: methodDesc, - payload: request, - // metadata: {}, - }) - - const grpcerr = new GRPCError(response.error) - if (!grpcerr.OK) { - throw grpcerr.EOF ? EOF : grpcerr - } - - return makeStreamClient(response.streamId, method, bridgeClient) - } + (bridgeClient: any) => + async ( + method: M, + request: Uint8Array, + _metadata?: never, + ) => { + const methodDesc = { + name: `/${getServiceName(method)}/${method.name}`, + + isClientStream: !!method.requestStream, + isServerStream: !!method.responseStream, + }; + + const response = await bridgeClient.createClientStream({ + methodDesc: methodDesc, + payload: request, + // metadata: {}, + }); + + const grpcerr = new GRPCError(response.error); + if (!grpcerr.OK) { + throw grpcerr.EOF ? EOF : grpcerr; + } + + return makeStreamClient(response.streamId, method, bridgeClient); + }; const client = (bridgeClient: any) => ({ unaryCall: unary(bridgeClient), streamCall: stream(bridgeClient), -}) +}); -const bridgeClient = createServiceClient(rpcmanager.RPCManager, rpcNative) -export default client(bridgeClient) +const bridgeClient = createServiceClient(rpcmanager.RPCManager, rpcNative); +export default client(bridgeClient); diff --git a/src/weshnet.types.gen.ts b/src/weshnet.types.gen.ts index dfbc695..6c12908 100644 --- a/src/weshnet.types.gen.ts +++ b/src/weshnet.types.gen.ts @@ -1,50 +1,50 @@ -import api from './api/index.d' +import { protocol } from "./api/index.d"; import { Unary, ResponseStream, RequestStream } from './types' -export type ServiceClientType = S extends typeof api.protocol.ProtocolService ? ProtocolServiceClient : never; +export type ServiceClientType = S extends typeof protocol.ProtocolService ? ProtocolServiceClient : never; export interface ProtocolServiceClient { - serviceExportData: ResponseStream, - serviceGetConfiguration: Unary, - contactRequestReference: Unary, - contactRequestDisable: Unary, - contactRequestEnable: Unary, - contactRequestResetReference: Unary, - contactRequestSend: Unary, - contactRequestAccept: Unary, - contactRequestDiscard: Unary, - shareContact: Unary, - decodeContact: Unary, - contactBlock: Unary, - contactUnblock: Unary, - contactAliasKeySend: Unary, - multiMemberGroupCreate: Unary, - multiMemberGroupJoin: Unary, - multiMemberGroupLeave: Unary, - multiMemberGroupAliasResolverDisclose: Unary, - multiMemberGroupAdminRoleGrant: Unary, - multiMemberGroupInvitationCreate: Unary, - appMetadataSend: Unary, - appMessageSend: Unary, - groupMetadataList: ResponseStream, - groupMessageList: ResponseStream, - groupInfo: Unary, - activateGroup: Unary, - deactivateGroup: Unary, - groupDeviceStatus: ResponseStream, - debugListGroups: ResponseStream, - debugInspectGroupStore: ResponseStream, - debugGroup: Unary, - systemInfo: Unary, - credentialVerificationServiceInitFlow: Unary, - credentialVerificationServiceCompleteFlow: Unary, - verifiedCredentialsList: ResponseStream, - replicationServiceRegisterGroup: Unary, - peerList: Unary, - outOfStoreReceive: Unary, - outOfStoreSeal: Unary, - refreshContactRequest: Unary, + serviceExportData: ResponseStream, + serviceGetConfiguration: Unary, + contactRequestReference: Unary, + contactRequestDisable: Unary, + contactRequestEnable: Unary, + contactRequestResetReference: Unary, + contactRequestSend: Unary, + contactRequestAccept: Unary, + contactRequestDiscard: Unary, + shareContact: Unary, + decodeContact: Unary, + contactBlock: Unary, + contactUnblock: Unary, + contactAliasKeySend: Unary, + multiMemberGroupCreate: Unary, + multiMemberGroupJoin: Unary, + multiMemberGroupLeave: Unary, + multiMemberGroupAliasResolverDisclose: Unary, + multiMemberGroupAdminRoleGrant: Unary, + multiMemberGroupInvitationCreate: Unary, + appMetadataSend: Unary, + appMessageSend: Unary, + groupMetadataList: ResponseStream, + groupMessageList: ResponseStream, + groupInfo: Unary, + activateGroup: Unary, + deactivateGroup: Unary, + groupDeviceStatus: ResponseStream, + debugListGroups: ResponseStream, + debugInspectGroupStore: ResponseStream, + debugGroup: Unary, + systemInfo: Unary, + credentialVerificationServiceInitFlow: Unary, + credentialVerificationServiceCompleteFlow: Unary, + verifiedCredentialsList: ResponseStream, + replicationServiceRegisterGroup: Unary, + peerList: Unary, + outOfStoreReceive: Unary, + outOfStoreSeal: Unary, + refreshContactRequest: Unary, }