diff --git a/Desktop/DesktopApp.js b/Desktop/DesktopApp.js index 6581230a1..5da36f492 100644 --- a/Desktop/DesktopApp.js +++ b/Desktop/DesktopApp.js @@ -1,10 +1,13 @@ exports.newDesktopApp = function newDesktopApp() { let thisObject = { - socialGraph: undefined, + userProfiles: undefined, + p2pNetworkClient: undefined, p2pNetwork: undefined, + p2pNetworkPeers: undefined, webSocketsClient: undefined, webSocketsInterface: undefined, + socialGraph: undefined, run: run } @@ -13,37 +16,54 @@ exports.newDesktopApp = function newDesktopApp() { return thisObject async function run() { - /* - This is the Personal Social Graph for the user running this App. - */ - thisObject.socialGraph = DK.projects.socialTrading.modules.socialGraph.newSocialTradingModulesSocialGraph() - await thisObject.socialGraph.initialize() - /* - We can join now the P2P Network as a client app. - */ - thisObject.p2pNetwork = SA.projects.network.modules.p2pNetwork.newNetworkModulesP2PNetwork() - await thisObject.p2pNetwork.initialize('Network Client') - /* - Here we will pick a Network Node from all users profiles available that do have a Network Node running. - */ - let selectedNetworkNode = thisObject.p2pNetwork.p2pNodesToConnect[0] - /* - This is the Web Sockets client that interacts with the Superalgos Network. - */ - thisObject.webSocketsClient = SA.projects.network.modules.webSocketsNetworkClient.newNetworkModulesWebSocketsNetworkClient() - await thisObject.webSocketsClient.initialize('Network Client', selectedNetworkNode) - /* - These are the Network Interfaces by which the Web App interacts with this Desktop Client. - */ - thisObject.webSocketsInterface = DK.projects.socialTrading.modules.webSocketsInterface.newDesktopModulesWebSocketsInterface() - thisObject.webSocketsInterface.initialize() - console.log('Desktop Client Web Sockets Interface ....................................... Listening at port ' + global.env.DESKTOP_WEB_SOCKETS_INTERFACE_PORT) + setupNetwork() + setupServices() + async function setupNetwork() { + /* + We set up ourselves as a Network Client. + */ + thisObject.p2pNetworkClient = SA.projects.network.modules.p2pNetworkClient.newNetworkModulesP2PNetworkClient() + await thisObject.p2pNetworkClient.initialize() + /* + We will read all user profiles plugins and get from there our network identity. + */ + thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles() + await thisObject.userProfiles.initialize(global.env.DESKTOP_APP_SIGNING_ACCOUNT, thisObject.p2pNetworkClient) + /* + We set up the P2P Network. + */ + thisObject.p2pNetwork = SA.projects.network.modules.p2pNetwork.newNetworkModulesP2PNetwork() + await thisObject.p2pNetwork.initialize('Network Client') + /* + Set up the connections to network nodes. + */ + thisObject.p2pNetworkPeers = SA.projects.network.modules.p2pNetworkPeers.newNetworkModulesP2PNetworkPeers() + await thisObject.p2pNetworkPeers.initialize( + 'Network Client', + thisObject.p2pNetworkClient, + thisObject.p2pNetwork, + global.env.DESKTOP_APP_MAX_OUTGOING_PEERS + ) + } - thisObject.httpInterface = DK.projects.socialTrading.modules.httpInterface.newDesktopModulesHttpInterface() - thisObject.httpInterface.initialize() - console.log('Desktop Client Http Interface .............................................. Listening at port ' + global.env.DESKTOP_HTTP_INTERFACE_PORT) + async function setupServices() { + /* + This is the Personal Social Graph for the user running this App. + */ + thisObject.socialGraph = DK.projects.socialTrading.modules.socialGraph.newSocialTradingModulesSocialGraph() + await thisObject.socialGraph.initialize() + /* + These are the Network Interfaces by which the Web App interacts with this Desktop Client. + */ + thisObject.webSocketsInterface = DK.projects.socialTrading.modules.webSocketsInterface.newDesktopModulesWebSocketsInterface() + thisObject.webSocketsInterface.initialize() + console.log('Desktop Client Web Sockets Interface ....................................... Listening at port ' + global.env.DESKTOP_WEB_SOCKETS_INTERFACE_PORT) + thisObject.httpInterface = DK.projects.socialTrading.modules.httpInterface.newDesktopModulesHttpInterface() + thisObject.httpInterface.initialize() + console.log('Desktop Client Http Interface .............................................. Listening at port ' + global.env.DESKTOP_HTTP_INTERFACE_PORT) + } } } diff --git a/Environment.js b/Environment.js index bb00891ce..04b9a78a4 100644 --- a/Environment.js +++ b/Environment.js @@ -11,9 +11,9 @@ exports.newEnvironment = function () { DEMO_MODE: false, BASE_PATH: basePath, WEB_SERVER_URL: 'localhost', - PLATFORM_WEB_SOCKETS_INTERFACE_PORT: 18041, + PLATFORM_WEB_SOCKETS_INTERFACE_PORT: 16042, + DESKTOP_WEB_SOCKETS_INTERFACE_PORT: 17042, NETWORK_WEB_SOCKETS_INTERFACE_PORT: 18042, - DESKTOP_WEB_SOCKETS_INTERFACE_PORT: 18043, DESKTOP_WEB_SOCKETS_INTERFACE_HOST: 'localhost', PLATFORM_HTTP_INTERFACE_PORT: 34248, DESKTOP_HTTP_INTERFACE_PORT: 34249, @@ -28,6 +28,7 @@ exports.newEnvironment = function () { PATH_TO_MY_WORKSPACES: path.join(basePath, './Platform/My-Workspaces'), PATH_TO_FONTS: path.join(basePath, './Platform/WebServer/Fonts'), DESKTOP_APP_SIGNING_ACCOUNT: 'Social-Trading-Desktop-App-1', + DESKTOP_APP_MAX_OUTGOING_PEERS: 2, MOBILE_APP_SIGNING_ACCOUNT: 'Social-Trading-Mobile-App-1', SERVER_APP_SIGNING_ACCOUNT: 'Social-Trading-Server-App-1', PLATFORM_APP_SIGNING_ACCOUNT: 'Algo-Traders-Platform-1', diff --git a/Network/NetwokNode.js b/Network/NetwokNode.js index 91fb26487..8edc1c6f4 100644 --- a/Network/NetwokNode.js +++ b/Network/NetwokNode.js @@ -1,11 +1,13 @@ exports.newNetworkNode = function newNetworkNode() { let thisObject = { + userProfiles: undefined, + p2pNetwork: undefined, + p2pNetworkPeers: undefined, + p2pNetworkNode: undefined, webSocketsInterface: undefined, socialGraphService: undefined, storage: undefined, - p2pNetwork: undefined, - p2pNetworkPeers: undefined, run: run } @@ -14,32 +16,55 @@ exports.newNetworkNode = function newNetworkNode() { return thisObject async function run() { - /* - The Social Graph Service is the first and for now, the only service this Network Node provides. - */ - thisObject.socialGraphService = NT.projects.socialTrading.modules.socialGraph.newNetworkModulesSocialGraph() - await thisObject.socialGraphService.initialize() - /* - The Sotrage deals with persisting the Social Graph. - */ - thisObject.storage = NT.projects.socialTrading.modules.storage.newSocialTradingModulesStorage() - thisObject.storage.initialize() - /* - Let's discover who is at the p2p network. - */ - thisObject.p2pNetwork = SA.projects.network.modules.p2pNetwork.newNetworkModulesP2PNetwork() - await thisObject.p2pNetwork.initialize('Network Peer') - /* - We can join now the P2P Network as a new peer there. - */ - thisObject.p2pNetworkPeers = SA.projects.network.modules.p2pNetworkPeers.newNetworkModulesP2PNetworkPeers() - await thisObject.p2pNetworkPeers.initialize() - /* - Other Network Nodes and Client Apps will communicate with this Network Node via it's Websocket Interface. - */ - thisObject.webSocketsInterface = NT.projects.network.modules.webSocketsInterface.newNetworkModulesWebSocketsInterface() - thisObject.webSocketsInterface.initialize() - console.log('Network Node Web Sockets Interface ....................................... Listening at port ' + JSON.parse(NT.networkNode.p2pNetwork.thisNetworkNode.node.config).webSocketsPort) + await setupNetwork() + await setupServices() + + async function setupNetwork() { + /* + We set up ourselves as a Network Node. + */ + thisObject.p2pNetworkNode = SA.projects.network.modules.p2pNetworkNode.newNetworkModulesP2PNetworkNode() + await thisObject.p2pNetworkNode.initialize() + /* + We will read all user profiles plugins and get from there our network identity. + */ + thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles() + await thisObject.userProfiles.initialize(global.env.P2P_NETWORK_NODE_SIGNING_ACCOUNT, thisObject.p2pNetworkNode) + /* + Let's discover who is at the p2p network. + */ + thisObject.p2pNetwork = SA.projects.network.modules.p2pNetwork.newNetworkModulesP2PNetwork() + await thisObject.p2pNetwork.initialize('Network Peer') + /* + Set up the connections to network nodes. + */ + thisObject.p2pNetworkPeers = SA.projects.network.modules.p2pNetworkPeers.newNetworkModulesP2PNetworkPeers() + await thisObject.p2pNetworkPeers.initialize( + 'Network Peer', + thisObject.p2pNetworkNode, + thisObject.p2pNetwork, + global.env.P2P_NETWORK_NODE_MAX_OUTGOING_PEERS + ) + } + + async function setupServices() { + /* + The Social Graph Service is the first and for now, the only service this Network Node provides. + */ + thisObject.socialGraphService = NT.projects.socialTrading.modules.socialGraph.newNetworkModulesSocialGraph() + await thisObject.socialGraphService.initialize() + /* + The Sotrage deals with persisting the Social Graph. + */ + thisObject.storage = NT.projects.socialTrading.modules.storage.newSocialTradingModulesStorage() + thisObject.storage.initialize() + /* + Other Network Nodes and Client Apps will communicate with this Network Node via it's Websocket Interface. + */ + thisObject.webSocketsInterface = NT.projects.network.modules.webSocketsInterface.newNetworkModulesWebSocketsInterface() + thisObject.webSocketsInterface.initialize() + console.log('Network Node Web Sockets Interface ....................................... Listening at port ' + JSON.parse(NT.networkNode.p2pNetworkNode.node.config).webSocketsPort) + } } } diff --git a/Projects/Network/NT/Modules/WebSocketsInterface.js b/Projects/Network/NT/Modules/WebSocketsInterface.js index 16c2593e6..af01c391f 100644 --- a/Projects/Network/NT/Modules/WebSocketsInterface.js +++ b/Projects/Network/NT/Modules/WebSocketsInterface.js @@ -12,6 +12,9 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock and sending messages to those entities. */ let thisObject = { + socketServer: undefined, + clientInterface: undefined, + peerInterface: undefined, networkClients: undefined, networkPeers: undefined, callersMap: undefined, @@ -19,10 +22,6 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock finalize: finalize } - let socketServer - let clientInterface - let peerInterface - let web3 = new SA.nodeModules.web3() return thisObject @@ -32,19 +31,19 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock thisObject.networkPeers = undefined callersMap = undefined - socketServer = undefined - clientInterface = undefined - peerInterface = undefined + thisObject.socketServer = undefined + thisObject.clientInterface = undefined + thisObject.peerInterface = undefined web3 = undefined } function initialize() { - let port = JSON.parse(NT.networkNode.p2pNetwork.thisNetworkNode.node.config).webSocketsPort + let port = JSON.parse(NT.networkNode.p2pNetworkNode.node.config).webSocketsPort - socketServer = new SA.nodeModules.ws.Server({ port: port }) - clientInterface = NT.projects.socialTrading.modules.clientInterface.newSocialTradingModulesClientInterface() - peerInterface = NT.projects.socialTrading.modules.peerInterface.newSocialTradingModulesPeerInterface() + thisObject.socketServer = new SA.nodeModules.ws.Server({ port: port }) + thisObject.clientInterface = NT.projects.socialTrading.modules.clientInterface.newSocialTradingModulesClientInterface() + thisObject.peerInterface = NT.projects.socialTrading.modules.peerInterface.newSocialTradingModulesPeerInterface() thisObject.networkClients = [] thisObject.networkPeers = [] @@ -55,7 +54,7 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock function setUpWebSocketServer() { try { - socketServer.on('connection', onConnectionOpened) + thisObject.socketServer.on('connection', onConnectionOpened) function onConnectionOpened(socket) /* @@ -65,7 +64,8 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock let caller = { socket: socket, userProfile: undefined, - role: undefined + role: undefined, + node: undefined } caller.socket.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId() @@ -123,19 +123,19 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock let response switch (caller.role) { case 'Network Client': { - response = await clientInterface.messageReceived(messageHeader.payload, caller.userProfile) + response = await thisObject.clientInterface.messageReceived(messageHeader.payload, caller.userProfile) response.messageId = messageHeader.messageId caller.socket.send(JSON.stringify(response)) break } case 'Network Peer': { - response = await peerInterface.messageReceived(messageHeader.payload) + response = await thisObject.peerInterface.messageReceived(messageHeader.payload) response.messageId = messageHeader.messageId caller.socket.send(JSON.stringify(response)) break } } - if (response.result === 'Ok' && messageHeader.payload.requestType === 'Event') { + if (response.result === 'Ok' && JSON.parse(messageHeader.payload).requestType === 'Event') { broadcastToPeers(messageHeader, caller) broadcastToClients(messageHeader, caller) } @@ -251,6 +251,32 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock } } /* + The caller needs to provide it's Node. + */ + if (messageHeader.callerNode === undefined) { + let response = { + result: 'Error', + message: 'node Not Provided.' + } + caller.socket.send(JSON.stringify(response)) + caller.socket.close() + return + } + /* + The caller's Node needs to be parseable. + */ + try { + caller.node = JSON.parse(messageHeader.callerNode) + } catch (err) { + let response = { + result: 'Error', + message: 'node Not Coorrect JSON Format.' + } + caller.socket.send(JSON.stringify(response)) + caller.socket.close() + return + } + /* The caller needs to provide it's User Profile Handle. */ if (messageHeader.callerProfileHandle === undefined) { @@ -262,7 +288,6 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock caller.socket.close() return } - caller.userProfileHandle = messageHeader.callerProfileHandle /* The caller needs to provide a callerTimestamp. @@ -492,14 +517,24 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock } function broadcastToPeers(messageHeader, caller) { + /* + The Boradcast to network peers is not done via + the network peers incomming connections, but + on the outgoing connections only. + */ let callerIdToAVoid if (caller.role === 'Network Peer') { - callerIdToAVoid = caller.socket.id + callerIdToAVoid = caller.node.id } - for (let i = 0; i < thisObject.networkPeers.length; i++) { - let networkPeer = thisObject.networkPeers[i] - if (networkPeer.socket.id === callerIdToAVoid) { continue } - networkPeer.socket.send(messageHeader) + for (let i = 0; i < NT.networkNode.p2pNetworkPeers.peers.length; i++) { + let peer = NT.networkNode.p2pNetworkPeers.peers[i] + if (peer.p2pNetworkNode.node.id === callerIdToAVoid) { continue } + peer.webSocketsClient.sendMessage(messageHeader.payload) + .catch(onError) + + function onError() { + console.log('[ERROR] Web Sockets Interface -> broadcastToPeers -> Sending Message Failed.') + } } } @@ -511,7 +546,12 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock for (let i = 0; i < thisObject.networkClients.length; i++) { let networkClient = thisObject.networkClients[i] if (networkClient.socket.id === callerIdToAVoid) { continue } - networkClient.socket.send(messageHeader) + networkClient.socket.send(messageHeader.payload) + .catch(onError) + + function onError() { + console.log('[ERROR] Web Sockets Interface -> broadcastToPeers -> Sending Message Failed.') + } } } diff --git a/Projects/Network/SA/Modules/P2PNetwork.js b/Projects/Network/SA/Modules/P2PNetwork.js index 131f82e60..9c660c2e4 100644 --- a/Projects/Network/SA/Modules/P2PNetwork.js +++ b/Projects/Network/SA/Modules/P2PNetwork.js @@ -4,9 +4,7 @@ exports.newNetworkModulesP2PNetwork = function newNetworkModulesP2PNetwork() { of this current node to other peers. */ let thisObject = { - joinTheNetworkAsNetworkPeer: joinTheNetworkAsNetworkPeer, p2pNodesToConnect: undefined, - thisNetworkNode: undefined, /* Framework Functions */ initialize: initialize, finalize: finalize @@ -38,18 +36,10 @@ exports.newNetworkModulesP2PNetwork = function newNetworkModulesP2PNetwork() { let p2pNetworkNode = SA.projects.network.globals.memory.arrays.P2P_NETWORK_NODES[i] if (thisP2PNodeId !== p2pNetworkNode.node.id) { thisObject.p2pNodesToConnect.push(p2pNetworkNode) - } else { - thisObject.thisNetworkNode = p2pNetworkNode } } break } } } - - function joinTheNetworkAsNetworkPeer() { - /* - To join the network we will need to thorugh the list of Network Nodes and pick one to connect to. - */ - } } \ No newline at end of file diff --git a/Projects/Network/SA/Modules/P2PNetworkClient.js b/Projects/Network/SA/Modules/P2PNetworkClient.js new file mode 100644 index 000000000..daa0bdb08 --- /dev/null +++ b/Projects/Network/SA/Modules/P2PNetworkClient.js @@ -0,0 +1,27 @@ +exports.newNetworkModulesP2PNetworkClient = function newNetworkModulesP2PNetworkClient() { + /* + This module represents the P2P Network Client running at the moment. + */ + let thisObject = { + + node: undefined, + blockchainAccount: undefined, + userProfile: undefined, + + /* Framework Functions */ + initialize: initialize, + finalize: finalize + } + + return thisObject + + function finalize() { + thisObject.node = undefined + thisObject.blockchainAccount = undefined + thisObject.userProfile = undefined + } + + async function initialize() { + + } +} \ No newline at end of file diff --git a/Projects/Network/SA/Modules/P2PNetworkPeers.js b/Projects/Network/SA/Modules/P2PNetworkPeers.js index e22197804..a11e388d3 100644 --- a/Projects/Network/SA/Modules/P2PNetworkPeers.js +++ b/Projects/Network/SA/Modules/P2PNetworkPeers.js @@ -1,9 +1,10 @@ exports.newNetworkModulesP2PNetworkPeers = function newNetworkModulesP2PNetworkPeers() { /* - This module holds the P2P Nodes we are coonected to and that are connected to us. + This module holds the P2P Nodes we are coonected to. */ let thisObject = { - outgoingConnectedPeers: undefined, + peers: undefined, + sendMessage: sendMessage, /* Framework Functions */ initialize: initialize, @@ -16,63 +17,82 @@ exports.newNetworkModulesP2PNetworkPeers = function newNetworkModulesP2PNetworkP return thisObject function finalize() { - thisObject.outgoingConnectedPeers = undefined + thisObject.peers = undefined clearInterval(intervalIdConnectToPeers) } - async function initialize() { + async function initialize( + callerRole, + p2pNetworkIdentity, + p2pNetwork, + maxOutgoingPeers + ) { - thisObject.outgoingConnectedPeers = [] + thisObject.peers = [] connectToPeers() intervalIdConnectToPeers = setInterval(connectToPeers, RECONNECT_DELAY); - } + async function connectToPeers() { - async function connectToPeers() { + if (thisObject.peers.length >= maxOutgoingPeers) { return } - if (thisObject.outgoingConnectedPeers.count >= global.env.P2P_NETWORK_NODE_MAX_OUTGOING_PEERS) { return } + for (let i = 0; i < p2pNetwork.p2pNodesToConnect.length; i++) { + if (thisObject.peers.length >= maxOutgoingPeers) { break } - for (let i = 0; i < NT.networkNode.p2pNetwork.p2pNodesToConnect.length; i++) { - if (thisObject.outgoingConnectedPeers.count >= global.env.P2P_NETWORK_NODE_MAX_OUTGOING_PEERS) { break } - let peer = {} - peer.p2pNetworkNode = NT.networkNode.p2pNetwork.p2pNodesToConnect[i] - if (isOutgoingConnectedPeer(peer) === true) { continue } - peer.webSocketsClient = SA.projects.network.modules.webSocketsNetworkClient.newNetworkModulesWebSocketsNetworkClient() - await peer.webSocketsClient.initialize('Network Peer', peer.p2pNetworkNode, onConnectionClosed) - .then(addPeer) - .catch(onError) + let peer = { + p2pNetworkNode: undefined, + webSocketsClient: undefined + } - function addPeer() { - thisObject.outgoingConnectedPeers.push(peer) - } + peer.p2pNetworkNode = p2pNetwork.p2pNodesToConnect[i] + if (isConnectedPeer(peer) === true) { continue } + peer.webSocketsClient = SA.projects.network.modules.webSocketsNetworkClient.newNetworkModulesWebSocketsNetworkClient() + await peer.webSocketsClient.initialize(callerRole, p2pNetworkIdentity, peer.p2pNetworkNode, onConnectionClosed) + .then(addPeer) + .catch(onError) - function onError(err) { - if (err !== undefined) { - console.log('[ERROR] P2P Network Peers -> onError -> While connecting to node -> ' + peer.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + peer.p2pNetworkNode.node.name + ' -> ' + err.message) - } else { - console.log('[WARN] P2P Network Peers -> onError -> Peer Not Available at the Moment -> ' + peer.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + peer.p2pNetworkNode.node.name) + function addPeer() { + thisObject.peers.push(peer) + } + + function onError(err) { + if (err !== undefined) { + console.log('[ERROR] P2P Network Peers -> onError -> While connecting to node -> ' + peer.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + peer.p2pNetworkNode.node.name + ' -> ' + err.message) + } else { + console.log('[WARN] P2P Network Peers -> onError -> Peer Not Available at the Moment -> ' + peer.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + peer.p2pNetworkNode.node.name) + } + } + + function onConnectionClosed(webSocketsClientId) { + for (let i = 0; i < thisObject.peers.length; i++) { + let connectedPeer = thisObject.peers[i] + if (connectedPeer.webSocketsClient.id === webSocketsClientId) { + thisObject.peers.splice(i, 1) + return + } + } } } - function onConnectionClosed(webSocketsClientId) { - for (let i = 0; i < thisObject.outgoingConnectedPeers.length; i++) { - let connectedPeer = thisObject.outgoingConnectedPeers[i] - if (connectedPeer.webSocketsClient.id === webSocketsClientId) { - thisObject.outgoingConnectedPeers.splice(i, 1) - return + function isConnectedPeer(peer) { + for (let i = 0; i < thisObject.peers.length; i++) { + let connectedPeer = thisObject.peers[i] + if (connectedPeer.p2pNetworkNode.node.id === peer.p2pNetworkNode.node.id) { + return true } } } } + } - function isOutgoingConnectedPeer(peer) { - for (let i = 0; i < thisObject.outgoingConnectedPeers.length; i++) { - let connectedPeer = thisObject.outgoingConnectedPeers[i] - if (connectedPeer.p2pNetworkNode.node.id === peer.p2pNetworkNode.node.id) { - return true - } - } - } + async function sendMessage(message) { + /* + This function will send the message from a random picked network node + selected from the array of already connected peers. + */ + let peerIndex = Math.max(Math.round(Math.random() * thisObject.peers.length) - 1, 0) + let peer = thisObject.peers[peerIndex] + return await peer.webSocketsClient.sendMessage(message) } } \ No newline at end of file diff --git a/Projects/Network/SA/Modules/UserProfiles.js b/Projects/Network/SA/Modules/UserProfiles.js index 48f8a2f76..09df3e54a 100644 --- a/Projects/Network/SA/Modules/UserProfiles.js +++ b/Projects/Network/SA/Modules/UserProfiles.js @@ -18,7 +18,7 @@ exports.newNetworkModulesUserProfiles = function newNetworkModulesUserProfiles() } - async function initialize() { + async function initialize(signingAccountChildCodeName, p2pNetworkIdentity) { let pluginFileNames = await SA.projects.communityPlugins.utilities.plugins.getPluginFileNames( 'Governance', @@ -65,7 +65,7 @@ exports.newNetworkModulesUserProfiles = function newNetworkModulesUserProfiles() let signatureObject = config.signature let web3 = new SA.nodeModules.web3() let blockchainAccount = web3.eth.accounts.recover(signatureObject) - SA.projects.network.globals.memory.maps.USER_PROFILES_BY_BLOKCHAIN_ACCOUNT.set(blockchainAccount, userProfile) + SA.projects.network.globals.memory.maps.USER_PROFILES_BY_BLOKCHAIN_ACCOUNT.set(blockchainAccount, userProfile) /* If the Signing Account is for a P2P node, we will add the node to the array of available nodes at the p2p network. */ @@ -85,12 +85,22 @@ exports.newNetworkModulesUserProfiles = function newNetworkModulesUserProfiles() } if (config.webSocketsPort === undefined) { continue - } + } let p2pNetworkNode = SA.projects.network.modules.p2pNetworkNode.newNetworkModulesP2PNetworkNode() p2pNetworkNode.initialize(signingAccount.signingAccountChild, userProfile, blockchainAccount) SA.projects.network.globals.memory.arrays.P2P_NETWORK_NODES.push(p2pNetworkNode) } + /* + Now, we will extract the information from the User Profile, specifically of our identity at the p2p network. + */ + if ( + signingAccount.signingAccountChild.id === SA.secrets.map.get(signingAccountChildCodeName).signingAccountChildId + ) { + p2pNetworkIdentity.node = signingAccount.signingAccountChild + p2pNetworkIdentity.blockchainAccount = blockchainAccount + p2pNetworkIdentity.userProfile = userProfile + } } } } diff --git a/Projects/Network/SA/Modules/WebSocketsNetworkClient.js b/Projects/Network/SA/Modules/WebSocketsNetworkClient.js index 38fed2cea..1542e743f 100644 --- a/Projects/Network/SA/Modules/WebSocketsNetworkClient.js +++ b/Projects/Network/SA/Modules/WebSocketsNetworkClient.js @@ -2,16 +2,19 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb let thisObject = { id: undefined, + socket: undefined, isConnected: undefined, host: undefined, port: undefined, + callerRole: undefined, + p2pNetworkNode: undefined, + p2pNetworkIdentity: undefined, + onConnectionClosedCallBack: undefined, sendMessage: sendMessage, initialize: initialize, finalize: finalize } - let socketClient - let web3 let called = {} let onMessageFunctionsMap = new Map() @@ -19,33 +22,44 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb return thisObject function finalize() { - socketClient.close() - socketClient = undefined - networkInterface = undefined - peerInterface = undefined + thisObject.socket.close() + thisObject.socket = undefined + thisObject.id = undefined + thisObject.isConnected = undefined + thisObject.host = undefined + thisObject.port = undefined + thisObject.callerRole = undefined + thisObject.p2pNetworkNode = undefined + thisObject.p2pNetworkIdentity = undefined + thisObject.onConnectionClosedCallBack = undefined web3 = undefined called = undefined onMessageFunctionsMap = undefined } - async function initialize(callerRole, p2pNetworkNode, onConnectionClosedCallBack) { + async function initialize(callerRole, p2pNetworkIdentity, p2pNetworkNode, onConnectionClosedCallBack) { + + thisObject.callerRole = callerRole + thisObject.p2pNetworkIdentity = p2pNetworkIdentity + thisObject.p2pNetworkNode = p2pNetworkNode + thisObject.onConnectionClosedCallBack = onConnectionClosedCallBack web3 = new SA.nodeModules.web3() thisObject.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId() - thisObject.host = JSON.parse(p2pNetworkNode.node.config).host - thisObject.port = JSON.parse(p2pNetworkNode.node.config).webSocketsPort + thisObject.host = JSON.parse(thisObject.p2pNetworkNode.node.config).host + thisObject.port = JSON.parse(thisObject.p2pNetworkNode.node.config).webSocketsPort - socketClient = new SA.nodeModules.ws('ws://' + thisObject.host + ':' + thisObject.port) - await setUpWebsocketClient(callerRole, p2pNetworkNode, onConnectionClosedCallBack) + thisObject.socket = new SA.nodeModules.ws('ws://' + thisObject.host + ':' + thisObject.port) + await setUpWebSocketClient() - console.log('Websockets Client Connected to Network Node via Web Sockets .............. Connected to ' + p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + p2pNetworkNode.node.name + ' -> ' + thisObject.host + ':' + thisObject.port) + console.log('Websockets Client Connected to Network Node via Web Sockets ................ Connected to ' + thisObject.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + thisObject.p2pNetworkNode.node.name + ' -> ' + thisObject.host + ':' + thisObject.port) thisObject.isConnected = true } - async function setUpWebsocketClient(callerRole, p2pNetworkNode, onConnectionClosedCallBack) { + async function setUpWebSocketClient() { return new Promise(connectToNewtwork) @@ -53,9 +67,9 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb try { - socketClient.onopen = () => { onConnectionOpened() } - socketClient.onclose = () => { onConnectionClosed() } - socketClient.onerror = err => { onError(err) } + thisObject.socket.onopen = () => { onConnectionOpened() } + thisObject.socket.onclose = () => { onConnectionClosed() } + thisObject.socket.onerror = err => { onError(err) } function onConnectionOpened() { @@ -79,18 +93,19 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb it's own identity, and later we will sign it's own handle to prove ours. */ - socketClient.onmessage = socketMessage => { stepOneResponse(socketMessage) } + thisObject.socket.onmessage = socketMessage => { stepOneResponse(socketMessage) } callerTimestamp = (new Date()).valueOf() let message = { messageType: 'Handshake', - callerRole: callerRole, + callerRole: thisObject.callerRole, callerProfileHandle: SA.secrets.map.get(global.env.DESKTOP_APP_SIGNING_ACCOUNT).userProfileHandle, callerTimestamp: callerTimestamp, + callerNode: JSON.stringify(thisObject.p2pNetworkIdentity.node), step: 'One' } - socketClient.send(JSON.stringify(message)) + thisObject.socket.send(JSON.stringify(message)) } function stepOneResponse(socketMessage) { @@ -116,7 +131,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb We will check that the blockchain account taken from the signature matches the one we have on record for the user profile of the Network Node we are calling. */ - if (called.blockchainAccount !== p2pNetworkNode.blockchainAccount) { + if (called.blockchainAccount !== thisObject.p2pNetworkNode.blockchainAccount) { console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.') reject() return @@ -138,7 +153,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb We will check that the Network Node that responded has the same User Profile Handle that we have on record, otherwise something is wrong and we should not proceed. */ - if (signedMessage.calledProfileHandle !== p2pNetworkNode.userProfile.userProfileHandle) { + if (signedMessage.calledProfileHandle !== thisObject.p2pNetworkNode.userProfile.userProfileHandle) { console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.') reject() return @@ -175,7 +190,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb Here we will sign a message with the Network Node profile handle and timestamp to prove our own identity. */ - socketClient.onmessage = socketMessage => { stepTwoResponse(socketMessage) } + thisObject.socket.onmessage = socketMessage => { stepTwoResponse(socketMessage) } let signature = web3.eth.accounts.sign(JSON.stringify(signedMessage), SA.secrets.map.get(global.env.DESKTOP_APP_SIGNING_ACCOUNT).privateKey) @@ -184,7 +199,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb signature: JSON.stringify(signature), step: 'Two' } - socketClient.send(JSON.stringify(message)) + thisObject.socket.send(JSON.stringify(message)) } function stepTwoResponse(socketMessage) { @@ -200,7 +215,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb Network Node and from now on, all response messages will be received at this following function. */ - socketClient.onmessage = socketMessage => { onMenssage(socketMessage) } + thisObject.socket.onmessage = socketMessage => { onMenssage(socketMessage) } resolve() } } @@ -208,10 +223,10 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb function onConnectionClosed() { if (thisObject.isConnected === true) { - console.log('Websockets Client Connected to Network Node via Web Sockets .............. Disconnected from ' + p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + p2pNetworkNode.node.name + ' -> ' + thisObject.host + ':' + thisObject.port) + console.log('Websockets Client Disconnected from Network Node via Web Sockets ......... Disconnected from ' + thisObject.p2pNetworkNode.userProfile.userProfileHandle + ' -> ' + thisObject.p2pNetworkNode.node.name + ' -> ' + thisObject.host + ':' + thisObject.port) } - if (onConnectionClosedCallBack !== undefined) { - onConnectionClosedCallBack(thisObject.id) + if (thisObject.onConnectionClosedCallBack !== undefined) { + thisObject.onConnectionClosedCallBack(thisObject.id) } thisObject.isConnected = false } @@ -229,7 +244,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb } } catch (err) { - console.log('[ERROR] Web Sockets Client -> setUpWebsocketClient -> err.stack = ' + err.stack) + console.log('[ERROR] Web Sockets Client -> setUpWebSocketClient -> err.stack = ' + err.stack) } } @@ -241,7 +256,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb function sendSocketMessage(resolve, reject) { - if (socketClient.readyState !== 1) { // 1 means connected and ready. + if (thisObject.socket.readyState !== 1) { // 1 means connected and ready. console.log('[ERROR] Web Sockets Client -> sendMessage -> Cannot send message while connection is closed.') reject('Websockets Connection Not Ready.') return @@ -253,7 +268,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb payload: message } onMessageFunctionsMap.set(socketMessage.messageId, onMenssageFunction) - socketClient.send( + thisObject.socket.send( JSON.stringify(socketMessage) ) @@ -279,8 +294,44 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb /* We get the function that is going to resolve or reject the promise given. */ - onMenssageFunction = onMessageFunctionsMap.get(response.messageId) - onMessageFunctionsMap.delete(response.messageId) - onMenssageFunction(response) + let onMenssageFunction = onMessageFunctionsMap.get(response.messageId) + + if (onMenssageFunction !== undefined) { + /* + The message received is a response to a message sent. + */ + onMessageFunctionsMap.delete(response.messageId) + onMenssageFunction(response) + } else { + /* + The message received is a not response to a message sent. + */ + let messageHeader + try { + messageHeader = JSON.parse(message) + } catch (err) { + console.log('[ERROR] Web Sockets Client -> onMenssage -> message = ' + message) + console.log('[ERROR] Web Sockets Client -> onMenssage -> err.stack = ' + err.stack) + thisObject.socket.close() + return + } + + switch (thisObject.callerRole) { + case 'Network Client': { + /* + This is the use case of a network client receiving a notification of + somethinig that happened at the network that is relevant to itself. + */ + break + } + case 'Network Peer': { + /* + No use cases so far were a Network Peer would send a message through an + incomming websockets connection, without having received a request first. + */ + break + } + } + } } } \ No newline at end of file diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index feebfdbbf..93a9be097 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -1419,6 +1419,12 @@ "propertyName": "p2pNetworkPeers", "functionName": "newNetworkModulesP2PNetworkPeers", "fileName": "P2PNetworkPeers.js" + }, + { + "name": "P2P Network Client", + "propertyName": "p2pNetworkClient", + "functionName": "newNetworkModulesP2PNetworkClient", + "fileName": "P2PNetworkClient.js" } ], "utilities": [ diff --git a/Projects/Social-Trading/DK/modules/SocialGraph.js b/Projects/Social-Trading/DK/modules/SocialGraph.js index faf71cee4..7816d51e5 100644 --- a/Projects/Social-Trading/DK/modules/SocialGraph.js +++ b/Projects/Social-Trading/DK/modules/SocialGraph.js @@ -14,7 +14,6 @@ exports.newSocialTradingModulesSocialGraph = function newSocialTradingModulesSoc no need to ask it to a network node. */ let thisObject = { - userProfiles: undefined, initialize: initialize, finalize: finalize } @@ -26,7 +25,6 @@ exports.newSocialTradingModulesSocialGraph = function newSocialTradingModulesSoc } async function initialize() { - thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles() - await thisObject.userProfiles.initialize() + } } \ No newline at end of file diff --git a/Projects/Social-Trading/DK/modules/WebAppInterface.js b/Projects/Social-Trading/DK/modules/WebAppInterface.js index d4b522dd7..96830e81e 100644 --- a/Projects/Social-Trading/DK/modules/WebAppInterface.js +++ b/Projects/Social-Trading/DK/modules/WebAppInterface.js @@ -58,11 +58,11 @@ exports.newSocialTradingModulesWebAppInterface = function newSocialTradingModule response = { result: 'Ok', message: 'Web App Interface Query Processed.', - data: await DK.desktopApp.webSocketsClient.sendMessage(JSON.stringify(messageHeader)) + data: await DK.desktopApp.p2pNetworkPeers.sendMessage(JSON.stringify(messageHeader)) } } else { - let events = await DK.desktopApp.webSocketsClient.sendMessage(JSON.stringify(messageHeader)) + let events = await DK.desktopApp.p2pNetworkPeers.sendMessage(JSON.stringify(messageHeader)) for (let i = 0; i < events.length; i++) { let event = events[i] if (event.eventType === SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST) { diff --git a/Projects/Social-Trading/NT/Modules/PeerInterface.js b/Projects/Social-Trading/NT/Modules/PeerInterface.js index e25193d5d..29ff7c114 100644 --- a/Projects/Social-Trading/NT/Modules/PeerInterface.js +++ b/Projects/Social-Trading/NT/Modules/PeerInterface.js @@ -8,9 +8,9 @@ exports.newSocialTradingModulesPeerInterface = function newSocialTradingModulesP websockets interface. */ let thisObject = { + messageReceived: messageReceived, initialize: initialize, - finalize: finalize, - run: run + finalize: finalize } return thisObject @@ -22,8 +22,130 @@ exports.newSocialTradingModulesPeerInterface = function newSocialTradingModulesP function initialize() { } + + async function messageReceived(message) { + let messageHeader + try { + messageHeader = JSON.parse(message) + } catch (err) { + let response = { + result: 'Error', + message: 'Peer Interface message Not Correct JSON Format.' + } + return response + } - function run() { + if (messageHeader.requestType === undefined) { + let response = { + result: 'Error', + message: 'Peer Interface requestType Not Provided.' + } + return response + } + if (messageHeader.requestType !== 'Event' && messageHeader.requestType !== 'Query') { + let response = { + result: 'Error', + message: 'Peer Interface requestType Not Supported.' + } + return response + } + + switch (messageHeader.requestType) { + case 'Event': { + return await eventReceived(messageHeader.eventMessage) + } + case 'Query': { + let response = { + result: 'Error', + message: 'Peer Interface requestType Query Not Supported.' + } + return response + } + } + } + + async function eventReceived(eventMessage) { + /* + We expect here a JSON string with some or all of the following properties: + + { + "eventId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "eventType": 10, + "emitterUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "targetUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "emitterBotProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "targetBBotProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "emitterPostHash": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "targetPostHash": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "timestamp": 124234234234, + "botAsset": "BTC", + "botExchange": "Binance", + "botEnabled": true + } + */ + + let eventReceived + try { + eventReceived = JSON.parse(eventMessage) + } catch (err) { + let response = { + result: 'Error', + message: 'Peer Interface eventMessage Not Correct JSON Format.' + } + return response + } + /* + We will not accept events that don't have an eventId. + */ + if (eventReceived.eventId === undefined) { + let response = { + result: 'Error', + message: 'eventId Not Provided.' + } + return response + } + /* + We will not accept events that have already been processed. + */ + + if (NT.projects.network.globals.memory.maps.EVENTS.get(eventReceived.eventId) !== undefined) { + let response = { + result: 'Ok', + message: 'Peer Interface Event Already Exists.' + } + return response + } + /* + Here we will process the event and change the state of the Social Graph. + */ + try { + let event = NT.projects.socialTrading.modules.event.newSocialTradingModulesEvent() + event.initialize(eventReceived) + NT.projects.network.globals.memory.maps.EVENTS.set(eventReceived.eventId, event) + NT.projects.network.globals.memory.arrays.EVENTS.push(event) + + let response = { + result: 'Ok', + message: 'Peer Interface Event Processed.' + } + return response + + } catch (err) { + /* + Any exception that happens while trying to change the state of the Social Graph, + will be returned to the caller without doing anything else here. + */ + if (err.stack !== undefined) { + console.log('[ERROR] Peer Interface -> err.stack = ' + err.stack) + } + let errorMessage = err.message + if (errorMessage === undefined) { errorMessage = err } + let response = { + result: 'Error', + message: 'Peer Interface ' + errorMessage + } + return response + } } } \ No newline at end of file diff --git a/Projects/Social-Trading/NT/Modules/SocialGraph.js b/Projects/Social-Trading/NT/Modules/SocialGraph.js index f31c44a43..d3b611439 100644 --- a/Projects/Social-Trading/NT/Modules/SocialGraph.js +++ b/Projects/Social-Trading/NT/Modules/SocialGraph.js @@ -9,7 +9,7 @@ exports.newNetworkModulesSocialGraph = function newNetworkModulesSocialGraph() { their posts. */ let thisObject = { - userProfiles: undefined, + /* Framework Functions */ initialize: initialize, finalize: finalize @@ -22,7 +22,6 @@ exports.newNetworkModulesSocialGraph = function newNetworkModulesSocialGraph() { } async function initialize() { - thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles() - await thisObject.userProfiles.initialize() + } } \ No newline at end of file