From ed89487ef51deb2881b975fe6d472422f2fc21d2 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Tue, 23 May 2023 14:04:04 +1000 Subject: [PATCH] wip: making progress converting handlers * Related #512 * Related #495 [ci skip] --- src/agent-old/service/echo.ts | 17 --- src/agent-old/service/nodesChainDataGet.ts | 57 -------- src/agent-old/service/nodesClaimsGet.ts | 22 --- .../service/nodesClosestLocalNodesGet.ts | 72 ---------- src/agent-old/service/nodesCrossSignClaim.ts | 58 -------- .../service/nodesHolePunchMessageSend.ts | 127 ------------------ src/agent/handlers/clientManifest.ts | 19 +++ src/agent/handlers/echo.ts | 19 +++ src/agent/handlers/index.ts | 2 + src/agent/handlers/nodesChainDataGet.ts | 45 +++++++ src/agent/handlers/nodesClaimsGet.ts | 23 ++++ .../handlers/nodesClosestLocalNodesGet.ts | 57 ++++++++ src/agent/handlers/nodesCrossSignClaim.ts | 40 ++++++ .../handlers/nodesHolePunchMessageSend.ts | 112 +++++++++++++++ src/agent/handlers/serverManifest.ts | 37 +++++ src/agent/handlers/types.ts | 35 +++++ src/agent/index.ts | 2 + src/agent/types.ts | 24 ++++ 18 files changed, 415 insertions(+), 353 deletions(-) delete mode 100644 src/agent-old/service/echo.ts delete mode 100644 src/agent-old/service/nodesChainDataGet.ts delete mode 100644 src/agent-old/service/nodesClaimsGet.ts delete mode 100644 src/agent-old/service/nodesClosestLocalNodesGet.ts delete mode 100644 src/agent-old/service/nodesCrossSignClaim.ts delete mode 100644 src/agent-old/service/nodesHolePunchMessageSend.ts create mode 100644 src/agent/handlers/clientManifest.ts create mode 100644 src/agent/handlers/echo.ts create mode 100644 src/agent/handlers/index.ts create mode 100644 src/agent/handlers/nodesChainDataGet.ts create mode 100644 src/agent/handlers/nodesClaimsGet.ts create mode 100644 src/agent/handlers/nodesClosestLocalNodesGet.ts create mode 100644 src/agent/handlers/nodesCrossSignClaim.ts create mode 100644 src/agent/handlers/nodesHolePunchMessageSend.ts create mode 100644 src/agent/handlers/serverManifest.ts create mode 100644 src/agent/handlers/types.ts create mode 100644 src/agent/index.ts create mode 100644 src/agent/types.ts diff --git a/src/agent-old/service/echo.ts b/src/agent-old/service/echo.ts deleted file mode 100644 index b99923bbbe..0000000000 --- a/src/agent-old/service/echo.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { ConnectionInfoGet } from 'agent/types'; -import * as utilsPB from '../../proto/js/polykey/v1/utils/utils_pb'; - -function echo({ connectionInfoGet }: { connectionInfoGet: ConnectionInfoGet }) { - return async ( - call: grpc.ServerUnaryCall, - callback: grpc.sendUnaryData, - ): Promise => { - connectionInfoGet(call); - const response = new utilsPB.EchoMessage(); - response.setChallenge(call.request.getChallenge()); - callback(null, response); - }; -} - -export default echo; diff --git a/src/agent-old/service/nodesChainDataGet.ts b/src/agent-old/service/nodesChainDataGet.ts deleted file mode 100644 index e20074d40f..0000000000 --- a/src/agent-old/service/nodesChainDataGet.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { DB } from '@matrixai/db'; -import type Sigchain from '../../sigchain/Sigchain'; -import type Logger from '@matrixai/logger'; -import * as grpcUtils from '../../grpc/utils'; -import * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; -import * as agentUtils from '../utils'; -import * as claimsUtils from '../../claims/utils'; -import { encodeClaimId } from '../../ids'; - -/** - * Retrieves the ChainDataEncoded of this node. - */ -function nodesChainDataGet({ - sigchain, - db, - logger, -}: { - sigchain: Sigchain; - db: DB; - logger: Logger; -}) { - return async ( - call: grpc.ServerWritableStream, - ): Promise => { - const genClaims = grpcUtils.generatorWritable(call, false); - try { - // Const seekClaimId = decodeClaimId(call.request.getClaimId()); - await db.withTransactionF(async (tran) => { - for await (const [claimId, signedClaim] of sigchain.getSignedClaims( - { /* seek: seekClaimId,*/ order: 'asc' }, - tran, - )) { - const encodedClaim = claimsUtils.generateSignedClaim(signedClaim); - const response = new nodesPB.AgentClaim(); - response.setClaimId(encodeClaimId(claimId)); - response.setPayload(encodedClaim.payload); - const signatureMessages = encodedClaim.signatures.map((item) => { - return new nodesPB.Signature() - .setSignature(item.signature) - .setProtected(item.protected); - }); - response.setSignaturesList(signatureMessages); - await genClaims.next(response); - } - }); - await genClaims.next(null); - } catch (e) { - await genClaims.throw(e); - !agentUtils.isAgentClientError(e) && - logger.error(`${nodesChainDataGet.name}:${e}`); - return; - } - }; -} - -export default nodesChainDataGet; diff --git a/src/agent-old/service/nodesClaimsGet.ts b/src/agent-old/service/nodesClaimsGet.ts deleted file mode 100644 index 920555388f..0000000000 --- a/src/agent-old/service/nodesClaimsGet.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; - -/** - * Retrieves all claims (of a specific type) of this node (within its sigchain). - * TODO: Currently not required. Will need to refactor once we filter on what - * claims we desire from the sigchain (e.g. in discoverGestalt). - */ -function nodesClaimsGet(_) { - return async ( - call: grpc.ServerUnaryCall, - callback: grpc.sendUnaryData, - ): Promise => { - const response = new nodesPB.Claims(); - // Response.setClaimsList( - // await sigchain.getClaims(call.request.getClaimtype() as ClaimType) - // ); - callback(null, response); - }; -} - -export default nodesClaimsGet; diff --git a/src/agent-old/service/nodesClosestLocalNodesGet.ts b/src/agent-old/service/nodesClosestLocalNodesGet.ts deleted file mode 100644 index d8ad1a7293..0000000000 --- a/src/agent-old/service/nodesClosestLocalNodesGet.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { NodeGraph } from '../../nodes'; -import type { DB } from '@matrixai/db'; -import type { NodeId } from '../../ids/types'; -import type Logger from '@matrixai/logger'; -import * as grpcUtils from '../../grpc/utils'; -import * as nodesUtils from '../../nodes/utils'; -import { validateSync } from '../../validation'; -import * as validationUtils from '../../validation/utils'; -import { matchSync } from '../../utils'; -import * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; -import * as agentUtils from '../utils'; - -/** - * Retrieves the local nodes (i.e. from the current node) that are closest - * to some provided node ID. - */ -function nodesClosestLocalNodesGet({ - nodeGraph, - db, - logger, -}: { - nodeGraph: NodeGraph; - db: DB; - logger: Logger; -}) { - return async ( - call: grpc.ServerUnaryCall, - callback: grpc.sendUnaryData, - ): Promise => { - try { - const response = new nodesPB.NodeTable(); - const { - nodeId, - }: { - nodeId: NodeId; - } = validateSync( - (keyPath, value) => { - return matchSync(keyPath)( - [['nodeId'], () => validationUtils.parseNodeId(value)], - () => value, - ); - }, - { - nodeId: call.request.getNodeId(), - }, - ); - // Get all local nodes that are closest to the target node from the request - const closestNodes = await db.withTransactionF((tran) => - nodeGraph.getClosestNodes(nodeId, undefined, tran), - ); - for (const [nodeId, nodeData] of closestNodes) { - const addressMessage = new nodesPB.Address(); - addressMessage.setHost(nodeData.address.host); - addressMessage.setPort(nodeData.address.port); - // Add the node to the response's map (mapping of node ID -> node address) - response - .getNodeTableMap() - .set(nodesUtils.encodeNodeId(nodeId), addressMessage); - } - callback(null, response); - return; - } catch (e) { - callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && - logger.error(`${nodesClosestLocalNodesGet.name}:${e}`); - return; - } - }; -} - -export default nodesClosestLocalNodesGet; diff --git a/src/agent-old/service/nodesCrossSignClaim.ts b/src/agent-old/service/nodesCrossSignClaim.ts deleted file mode 100644 index 48341005f2..0000000000 --- a/src/agent-old/service/nodesCrossSignClaim.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type NodeManager from '../../nodes/NodeManager'; -import type KeyRing from '../../keys/KeyRing'; -import type * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; -import type Logger from '@matrixai/logger'; -import type { ConnectionInfoGet } from '../types'; -import type ACL from '../../acl/ACL'; -import * as grpcUtils from '../../grpc/utils'; -import * as claimsErrors from '../../claims/errors'; -import * as agentUtils from '../utils'; -import * as nodesErrors from '../../nodes/errors'; - -function nodesCrossSignClaim({ - keyRing, - nodeManager, - acl, - connectionInfoGet, - logger, -}: { - keyRing: KeyRing; - nodeManager: NodeManager; - acl: ACL; - connectionInfoGet: ConnectionInfoGet; - logger: Logger; -}) { - return async ( - call: grpc.ServerDuplexStream, - ) => { - const requestingNodeId = connectionInfoGet(call)!.remoteNodeId; - const nodeId = keyRing.getNodeId(); - const genClaims = grpcUtils.generatorDuplex( - call, - { nodeId, command: nodesCrossSignClaim.name }, - true, - ); - try { - // Check the ACL for permissions - const permissions = await acl.getNodePerm(requestingNodeId); - if (permissions?.gestalt.claim !== null) { - throw new nodesErrors.ErrorNodePermissionDenied(); - } - // Handle claiming the node - await nodeManager.handleClaimNode(requestingNodeId, genClaims); - } catch (e) { - await genClaims.throw(e); - !agentUtils.isAgentClientError(e, [ - claimsErrors.ErrorEmptyStream, - claimsErrors.ErrorUndefinedSinglySignedClaim, - claimsErrors.ErrorUndefinedSignature, - claimsErrors.ErrorNodesClaimType, - claimsErrors.ErrorUndefinedDoublySignedClaim, - ]) && logger.error(`${nodesCrossSignClaim.name}:${e}`); - return; - } - }; -} - -export default nodesCrossSignClaim; diff --git a/src/agent-old/service/nodesHolePunchMessageSend.ts b/src/agent-old/service/nodesHolePunchMessageSend.ts deleted file mode 100644 index bbdded6c8a..0000000000 --- a/src/agent-old/service/nodesHolePunchMessageSend.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { DB } from '@matrixai/db'; -import type NodeManager from '../../nodes/NodeManager'; -import type NodeConnectionManager from '../../nodes/NodeConnectionManager'; -import type KeyRing from '../../keys/KeyRing'; -import type { NodeId } from '../../ids/types'; -import type Logger from '@matrixai/logger'; -import type { ConnectionInfoGet } from 'agent/types'; -import type * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; -import * as networkUtils from '../../network/utils'; -import * as grpcUtils from '../../grpc/utils'; -import { validateSync } from '../../validation'; -import * as validationUtils from '../../validation/utils'; -import * as nodesUtils from '../../nodes/utils'; -import { matchSync } from '../../utils'; -import * as utilsPB from '../../proto/js/polykey/v1/utils/utils_pb'; -import * as agentUtils from '../utils'; - -function nodesHolePunchMessageSend({ - keyRing, - nodeManager, - nodeConnectionManager, - db, - connectionInfoGet, - logger, -}: { - keyRing: KeyRing; - nodeManager: NodeManager; - nodeConnectionManager: NodeConnectionManager; - db: DB; - connectionInfoGet: ConnectionInfoGet; - logger: Logger; -}) { - return async ( - call: grpc.ServerUnaryCall, - callback: grpc.sendUnaryData, - ): Promise => { - try { - const response = new utilsPB.EmptyMessage(); - const { - targetId, - sourceId, - }: { - targetId: NodeId; - sourceId: NodeId; - } = validateSync( - (keyPath, value) => { - return matchSync(keyPath)( - [ - ['targetId'], - ['sourceId'], - () => validationUtils.parseNodeId(value), - ], - () => value, - ); - }, - { - targetId: call.request.getTargetId(), - sourceId: call.request.getSrcId(), - }, - ); - const connectionInfo = connectionInfoGet(call); - const srcNodeId = nodesUtils.encodeNodeId(connectionInfo!.remoteNodeId); - // Firstly, check if this node is the desired node - // If so, then we want to make this node start sending hole punching packets - // back to the source node. - await db.withTransactionF(async (tran) => { - if (keyRing.getNodeId().equals(targetId)) { - if (call.request.getProxyAddress() !== '') { - const [host, port] = networkUtils.parseAddress( - call.request.getProxyAddress(), - ); - logger.debug( - `Received signaling message to target ${call.request.getSrcId()}@${host}:${port}`, - ); - // Ignore failure - try { - await nodeConnectionManager.holePunchReverse(host, port); - } catch { - // Do nothing - } - } else { - logger.error( - 'Received signaling message, target information was missing, skipping reverse hole punch', - ); - } - } else if (await nodeManager.knowsNode(sourceId, tran)) { - // Otherwise, find if node in table - // If so, ask the nodeManager to relay to the node - const targetNodeId = call.request.getTargetId(); - const proxyAddress = networkUtils.buildAddress( - connectionInfo!.remoteHost, - connectionInfo!.remotePort, - ); - // Checking if the source and destination are the same - if (sourceId?.equals(targetId)) { - // Logging and silently dropping operation - logger.warn('Signaling relay message requested signal to itself'); - callback(null, response); - return; - } - call.request.setProxyAddress(proxyAddress); - logger.debug( - `Relaying signaling message from ${srcNodeId}@${ - connectionInfo!.remoteHost - }:${ - connectionInfo!.remotePort - } to ${targetNodeId} with information ${proxyAddress}`, - ); - await nodeConnectionManager.relaySignalingMessage(call.request, { - host: connectionInfo!.remoteHost, - port: connectionInfo!.remotePort, - }); - } - }); - callback(null, response); - return; - } catch (e) { - callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && - logger.error(`${nodesHolePunchMessageSend.name}:${e}`); - return; - } - }; -} - -export default nodesHolePunchMessageSend; diff --git a/src/agent/handlers/clientManifest.ts b/src/agent/handlers/clientManifest.ts new file mode 100644 index 0000000000..fcd3cf3016 --- /dev/null +++ b/src/agent/handlers/clientManifest.ts @@ -0,0 +1,19 @@ +import {ClientRPCRequestParams, ClientRPCResponseResult} from "@/client/types"; +import { EchoMessage } from "./types"; +import { UnaryCaller } from '../../rpc/callers'; + + +const echo = new UnaryCaller< + ClientRPCRequestParams, + ClientRPCResponseResult + >(); + +// No type used here, it will override type inference +const clientManifest = { + echo, +} + +export { + clientManifest, + echo, +} diff --git a/src/agent/handlers/echo.ts b/src/agent/handlers/echo.ts new file mode 100644 index 0000000000..8d5937258b --- /dev/null +++ b/src/agent/handlers/echo.ts @@ -0,0 +1,19 @@ +import { UnaryHandler } from "../../rpc/handlers"; +import type { EchoMessage } from './types'; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; + +class EchoHandler extends UnaryHandler< + {}, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async handle( + input: AgentRPCRequestParams, + ): Promise> { + return { + message: input.message, + } + } +} + +export { EchoHandler }; diff --git a/src/agent/handlers/index.ts b/src/agent/handlers/index.ts new file mode 100644 index 0000000000..65357aabeb --- /dev/null +++ b/src/agent/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './clientManifest'; +export * from './serverManifest'; diff --git a/src/agent/handlers/nodesChainDataGet.ts b/src/agent/handlers/nodesChainDataGet.ts new file mode 100644 index 0000000000..4d14c6cc0e --- /dev/null +++ b/src/agent/handlers/nodesChainDataGet.ts @@ -0,0 +1,45 @@ +import {ServerHandler} from "../../rpc/handlers"; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; +import type Sigchain from "../../sigchain/Sigchain"; +import type { DB } from "@matrixai/db"; +import * as claimsUtils from "@/claims/utils"; +import {ClaimIdMessage, AgentClaimMessage} from "./types"; + + + + +class NodesChainDataGetHandler extends ServerHandler< + { + sigchain: Sigchain, + db: DB, + }, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async *handle( + _input: ClaimIdMessage, + _cancel, + _meta, + ctx + ): AsyncGenerator> { + const { + sigchain, + db, + } = this.container; + yield* db.withTransactionG(async function *(tran): AsyncGenerator> { + for await (const [claimId, signedClaim] of sigchain.getSignedClaims( + { /* seek: seekClaimId,*/ order: 'asc' }, + tran, + )) { + const encodedClaim = claimsUtils.generateSignedClaim(signedClaim); + const response: AgentClaimMessage = { + claimIdEncoded: claimsUtils.encodeClaimId(claimId), + signedTokenEncoded: encodedClaim, + } + yield response; + } + }); + } +} + +export { NodesChainDataGetHandler }; diff --git a/src/agent/handlers/nodesClaimsGet.ts b/src/agent/handlers/nodesClaimsGet.ts new file mode 100644 index 0000000000..954c33b766 --- /dev/null +++ b/src/agent/handlers/nodesClaimsGet.ts @@ -0,0 +1,23 @@ +import { UnaryHandler } from "../../rpc/handlers"; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; + + +/** + * Retrieves all claims (of a specific type) of this node (within its sigchain). + * TODO: Currently not required. Will need to refactor once we filter on what + * claims we desire from the sigchain (e.g. in discoverGestalt). + */ + +class NodesClaimsGetHandler extends UnaryHandler< + {}, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async handle( + input: AgentRPCRequestParams, + ): Promise { + return {} + } +} + +export { NodesClaimsGetHandler }; diff --git a/src/agent/handlers/nodesClosestLocalNodesGet.ts b/src/agent/handlers/nodesClosestLocalNodesGet.ts new file mode 100644 index 0000000000..fe81d53f49 --- /dev/null +++ b/src/agent/handlers/nodesClosestLocalNodesGet.ts @@ -0,0 +1,57 @@ +import {ServerHandler} from "../../rpc/handlers"; +import type {NodeAddressMessage, NodeIdMessage} from './types'; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; +import {NodeId} from "@/ids"; +import {validateSync} from "@/validation"; +import {matchSync} from "@/utils"; +import * as validationUtils from "@/validation/utils"; +import * as nodesUtils from "@/nodes/utils"; +import type {NodeGraph} from "@/nodes"; +import type {DB} from "@matrixai/db"; + +class NodesClosestLocalNodesGetHandler extends ServerHandler< + { + nodeGraph: NodeGraph; + db: DB; + }, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async *handle( + input: AgentRPCRequestParams, + ): AsyncGenerator> { + const { + nodeGraph, + db, + } = this.container; + + const { + nodeId, + }: { + nodeId: NodeId; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['nodeId'], () => validationUtils.parseNodeId(value)], + () => value, + ); + }, + { + nodeId: input.nodeIdEncoded, + }, + ); + // Get all local nodes that are closest to the target node from the request + return yield* db.withTransactionG(async function *(tran): AsyncGenerator> { + const closestNodes = await nodeGraph.getClosestNodes(nodeId, undefined, tran); + for (const [nodeId, nodeData] of closestNodes) { + yield { + nodeIdEncoded: nodesUtils.encodeNodeId(nodeId), + host: nodeData.address.host, + port: nodeData.address.port, + }; + } + }); + } +} + +export { NodesClosestLocalNodesGetHandler }; diff --git a/src/agent/handlers/nodesCrossSignClaim.ts b/src/agent/handlers/nodesCrossSignClaim.ts new file mode 100644 index 0000000000..84ebf765cf --- /dev/null +++ b/src/agent/handlers/nodesCrossSignClaim.ts @@ -0,0 +1,40 @@ +import {DuplexHandler, UnaryHandler} from "../../rpc/handlers"; +import type { EchoMessage } from './types'; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; +import * as nodesUtils from "../../nodes/utils"; +import * as nodesErrors from "../../nodes/errors"; +import KeyRing from "../../keys/KeyRing"; +import {NodeId, NodeIdEncoded} from "../../ids"; +import ACL from "../../acl/ACL"; +import NodeManager from "../../nodes/NodeManager"; + +// TODO: come back to this! +class NodesCrossSignClaimHandler extends DuplexHandler< + { + keyRing: KeyRing; + acl: ACL; + nodeManager: NodeManager; + }, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async *handle( + input: AsyncIterable>, + _, + meta, + ): AsyncGenerator> { + const { keyRing, acl } = this.container; + // TODO: get remote info from metadata. dependent on js-quic meta types + const requestingNodeId: NodeId | undefined = nodesUtils.decodeNodeId(meta?.remoteNodeId); + if(requestingNodeId == null) throw Error('TMP invalid nodeId'); + // Check the ACL for permissions + const permissions = await acl.getNodePerm(requestingNodeId); + if (permissions?.gestalt.claim !== null) { + throw new nodesErrors.ErrorNodePermissionDenied(); + } + // Handle claiming the node + await nodeManager.handleClaimNode(requestingNodeId, genClaims); + } +} + +export { NodesCrossSignClaimHandler }; diff --git a/src/agent/handlers/nodesHolePunchMessageSend.ts b/src/agent/handlers/nodesHolePunchMessageSend.ts new file mode 100644 index 0000000000..947252203a --- /dev/null +++ b/src/agent/handlers/nodesHolePunchMessageSend.ts @@ -0,0 +1,112 @@ +import { UnaryHandler } from "../../rpc/handlers"; +import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; +import {NodeId} from "@/ids"; +import {validateSync} from "@/validation"; +import {matchSync} from "@/utils"; +import * as validationUtils from "@/validation/utils"; +import * as nodesUtils from "@/nodes/utils"; +import {HolePunchRelayMessage} from "./types"; +import type {DB} from "@matrixai/db"; +import type NodeConnectionManager from "../../nodes/NodeConnectionManager"; +import type KeyRing from "../../keys/KeyRing"; +import type Logger from "@matrixai/logger"; +import {Host, Port} from "../../network/types"; +import NodeManager from "../../nodes/NodeManager"; + +class NodesHolePunchMessageSendHandler extends UnaryHandler< + { + db: DB; + nodeConnectionManager: NodeConnectionManager; + keyRing: KeyRing; + nodeManager: NodeManager; + logger: Logger; + }, + AgentRPCRequestParams, + AgentRPCResponseResult + > { + public async handle( + input: AgentRPCRequestParams, + _, + meta, + ): Promise { + const {db, nodeConnectionManager, keyRing, nodeManager, logger} = this.container + const { + targetId, + sourceId, + }: { + targetId: NodeId; + sourceId: NodeId; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [ + ['targetId'], + ['sourceId'], + () => validationUtils.parseNodeId(value), + ], + () => value, + ); + }, + { + targetId: input.dstIdEncoded, + sourceId: input.srcIdEncoded, + }, + ); + const connectionInfo = meta; + const srcNodeId = nodesUtils.encodeNodeId(connectionInfo!.remoteNodeId); + // Firstly, check if this node is the desired node + // If so, then we want to make this node start sending hole punching packets + // back to the source node. + await db.withTransactionF(async (tran) => { + if (keyRing.getNodeId().equals(targetId)) { + if (input.address != null) { + const host = input.address.host as Host; + const port = input.address.port as Port; + logger.debug( + `Received signaling message to target ${input.srcIdEncoded}@${host}:${port}`, + ); + // Ignore failure + try { + await nodeConnectionManager.holePunchReverse(host, port); + } catch { + // Do nothing + } + } else { + logger.error( + 'Received signaling message, target information was missing, skipping reverse hole punch', + ); + } + } else if (await nodeManager.knowsNode(sourceId, tran)) { + // Otherwise, find if node in table + // If so, ask the nodeManager to relay to the node + const targetNodeId = input.dstIdEncoded; + const proxyAddress = { + host: connectionInfo!.remoteHost, + port: connectionInfo!.remotePort, + } + // Checking if the source and destination are the same + if (sourceId?.equals(targetId)) { + // Logging and silently dropping operation + logger.warn('Signaling relay message requested signal to itself'); + return {}; + } + logger.debug( + `Relaying signaling message from ${srcNodeId}@${ + proxyAddress.host + }:${ + proxyAddress.port + } to ${targetNodeId} with information ${proxyAddress}`, + ); + // TODO: fix + call.request.setProxyAddress(proxyAddress); + await nodeConnectionManager.relaySignalingMessage(call.request, { + host: connectionInfo!.remoteHost, + port: connectionInfo!.remotePort, + }); + } + }); + return {} + } +} + +export { NodesHolePunchMessageSendHandler }; diff --git a/src/agent/handlers/serverManifest.ts b/src/agent/handlers/serverManifest.ts new file mode 100644 index 0000000000..b208675d29 --- /dev/null +++ b/src/agent/handlers/serverManifest.ts @@ -0,0 +1,37 @@ +import {EchoHandler} from "./echo"; +import {NodesChainDataGetHandler} from "./nodesChainDataGet"; +import type {DB} from "@matrixai/db"; +import type Sigchain from "../../sigchain/Sigchain"; +import {NodesClosestLocalNodesGetHandler} from "@/agent/handlers/nodesClosestLocalNodesGet"; +import type NodeGraph from "../../nodes/NodeGraph"; +import {NodesCrossSignClaimHandler} from "./nodesCrossSignClaim"; +import type ACL from "../../acl/ACL"; +import type NodeManager from "../../nodes/NodeManager"; +import type KeyRing from "../../keys/KeyRing"; +import {NodesHolePunchMessageSendHandler} from "@/agent/handlers/nodesHolePunchMessageSend"; +import type NodeConnectionManager from "../../nodes/NodeConnectionManager"; +import type Logger from "@matrixai/logger"; +// import {NodesClaimsGetHandler} from "./nodesClaimsGet"; + + +const serverManifest = (container: { + db: DB, + sigchain: Sigchain, + nodeGraph: NodeGraph, + acl: ACL, + nodeManager: NodeManager, + nodeConnectionManager: NodeConnectionManager, + keyRing: KeyRing, + logger: Logger, +}) => { + return { + echo: new EchoHandler(container), + nodesChainDataGet: new NodesChainDataGetHandler(container), + // nodesClaimsGet: new NodesClaimsGetHandler(container), + nodesClosestLocalNodesGet: new NodesClosestLocalNodesGetHandler(container), + nodesCrossSignClaim: new NodesCrossSignClaimHandler(container), + nodesHolePunchMessageSend: new NodesHolePunchMessageSendHandler(container), + } +} + +export { serverManifest }; diff --git a/src/agent/handlers/types.ts b/src/agent/handlers/types.ts new file mode 100644 index 0000000000..b6f3591a94 --- /dev/null +++ b/src/agent/handlers/types.ts @@ -0,0 +1,35 @@ +import {SignedTokenEncoded} from "@/tokens/types"; +import {NodeIdEncoded} from "@/ids"; + + +export type EchoMessage = { + message: string, +}; + +export type ClaimIdMessage = { + claimIdEncoded: string; +} + +export type AgentClaimMessage = + ClaimIdMessage & { + signedTokenEncoded: SignedTokenEncoded; +} + +export type NodeIdMessage = { + nodeIdEncoded: NodeIdEncoded; +}; + +export type AddressMessage = { + host: string; + port: number; +}; + +export type NodeAddressMessage = + NodeIdMessage & + AddressMessage; + +export type HolePunchRelayMessage = { + srcIdEncoded: string, + dstIdEncoded: string, + address: AddressMessage, +}; diff --git a/src/agent/index.ts b/src/agent/index.ts new file mode 100644 index 0000000000..4c80b200fe --- /dev/null +++ b/src/agent/index.ts @@ -0,0 +1,2 @@ +export * from './handlers'; +export * as types from './types'; diff --git a/src/agent/types.ts b/src/agent/types.ts new file mode 100644 index 0000000000..13f0b1ad39 --- /dev/null +++ b/src/agent/types.ts @@ -0,0 +1,24 @@ +import type { JSONValue } from '../types'; + +// eslint-disable-next-line +type NoData = {}; + +type AgentRPCRequestParams = NoData> = { + metadata?: { + [Key: string]: JSONValue; + } & Partial<{ + authorization: string; + timeout: number; + }>; +} & Omit; + +type AgentRPCResponseResult = NoData> = { + metadata?: { + [Key: string]: JSONValue; + } & Partial<{ + authorization: string; + timeout: number; + }>; +} & Omit; + +export type { AgentRPCRequestParams, AgentRPCResponseResult, NoData };