Merge pull request #2537 from Luis-Fernando-Molina/develop

Contribution: Superalgos P2P Network: Desktop connecting to multiple Network Peers
This commit is contained in:
Luis Fernando Molina authored and GitHub committed 2021-11-07 11:06:45 -04:00
commit b3462c8697
14 files changed
+487 -178

No files matched your search

+49 -29
View File
@@ -1,10 +1,13 @@
exports.newDesktopApp = function newDesktopApp() { exports.newDesktopApp = function newDesktopApp() {
let thisObject = { let thisObject = {
socialGraph: undefined, userProfiles: undefined,
p2pNetworkClient: undefined,
p2pNetwork: undefined, p2pNetwork: undefined,
p2pNetworkPeers: undefined,
webSocketsClient: undefined, webSocketsClient: undefined,
webSocketsInterface: undefined, webSocketsInterface: undefined,
socialGraph: undefined,
run: run run: run
} }
@@ -13,37 +16,54 @@ exports.newDesktopApp = function newDesktopApp() {
return thisObject return thisObject
async function run() { 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)
/* setupNetwork()
These are the Network Interfaces by which the Web App interacts with this Desktop Client. setupServices()
*/
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)
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() async function setupServices() {
thisObject.httpInterface.initialize() /*
console.log('Desktop Client Http Interface .............................................. Listening at port ' + global.env.DESKTOP_HTTP_INTERFACE_PORT) 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)
}
} }
} }
+3 -2
View File
@@ -11,9 +11,9 @@ exports.newEnvironment = function () {
DEMO_MODE: false, DEMO_MODE: false,
BASE_PATH: basePath, BASE_PATH: basePath,
WEB_SERVER_URL: 'localhost', 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, NETWORK_WEB_SOCKETS_INTERFACE_PORT: 18042,
DESKTOP_WEB_SOCKETS_INTERFACE_PORT: 18043,
DESKTOP_WEB_SOCKETS_INTERFACE_HOST: 'localhost', DESKTOP_WEB_SOCKETS_INTERFACE_HOST: 'localhost',
PLATFORM_HTTP_INTERFACE_PORT: 34248, PLATFORM_HTTP_INTERFACE_PORT: 34248,
DESKTOP_HTTP_INTERFACE_PORT: 34249, DESKTOP_HTTP_INTERFACE_PORT: 34249,
@@ -28,6 +28,7 @@ exports.newEnvironment = function () {
PATH_TO_MY_WORKSPACES: path.join(basePath, './Platform/My-Workspaces'), PATH_TO_MY_WORKSPACES: path.join(basePath, './Platform/My-Workspaces'),
PATH_TO_FONTS: path.join(basePath, './Platform/WebServer/Fonts'), PATH_TO_FONTS: path.join(basePath, './Platform/WebServer/Fonts'),
DESKTOP_APP_SIGNING_ACCOUNT: 'Social-Trading-Desktop-App-1', DESKTOP_APP_SIGNING_ACCOUNT: 'Social-Trading-Desktop-App-1',
DESKTOP_APP_MAX_OUTGOING_PEERS: 2,
MOBILE_APP_SIGNING_ACCOUNT: 'Social-Trading-Mobile-App-1', MOBILE_APP_SIGNING_ACCOUNT: 'Social-Trading-Mobile-App-1',
SERVER_APP_SIGNING_ACCOUNT: 'Social-Trading-Server-App-1', SERVER_APP_SIGNING_ACCOUNT: 'Social-Trading-Server-App-1',
PLATFORM_APP_SIGNING_ACCOUNT: 'Algo-Traders-Platform-1', PLATFORM_APP_SIGNING_ACCOUNT: 'Algo-Traders-Platform-1',
+53 -28
View File
@@ -1,11 +1,13 @@
exports.newNetworkNode = function newNetworkNode() { exports.newNetworkNode = function newNetworkNode() {
let thisObject = { let thisObject = {
userProfiles: undefined,
p2pNetwork: undefined,
p2pNetworkPeers: undefined,
p2pNetworkNode: undefined,
webSocketsInterface: undefined, webSocketsInterface: undefined,
socialGraphService: undefined, socialGraphService: undefined,
storage: undefined, storage: undefined,
p2pNetwork: undefined,
p2pNetworkPeers: undefined,
run: run run: run
} }
@@ -14,32 +16,55 @@ exports.newNetworkNode = function newNetworkNode() {
return thisObject return thisObject
async function run() { 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)
}
} }
} }
@@ -12,6 +12,9 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
and sending messages to those entities. and sending messages to those entities.
*/ */
let thisObject = { let thisObject = {
socketServer: undefined,
clientInterface: undefined,
peerInterface: undefined,
networkClients: undefined, networkClients: undefined,
networkPeers: undefined, networkPeers: undefined,
callersMap: undefined, callersMap: undefined,
@@ -19,10 +22,6 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
finalize: finalize finalize: finalize
} }
let socketServer
let clientInterface
let peerInterface
let web3 = new SA.nodeModules.web3() let web3 = new SA.nodeModules.web3()
return thisObject return thisObject
@@ -32,19 +31,19 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
thisObject.networkPeers = undefined thisObject.networkPeers = undefined
callersMap = undefined callersMap = undefined
socketServer = undefined thisObject.socketServer = undefined
clientInterface = undefined thisObject.clientInterface = undefined
peerInterface = undefined thisObject.peerInterface = undefined
web3 = undefined web3 = undefined
} }
function initialize() { 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 }) thisObject.socketServer = new SA.nodeModules.ws.Server({ port: port })
clientInterface = NT.projects.socialTrading.modules.clientInterface.newSocialTradingModulesClientInterface() thisObject.clientInterface = NT.projects.socialTrading.modules.clientInterface.newSocialTradingModulesClientInterface()
peerInterface = NT.projects.socialTrading.modules.peerInterface.newSocialTradingModulesPeerInterface() thisObject.peerInterface = NT.projects.socialTrading.modules.peerInterface.newSocialTradingModulesPeerInterface()
thisObject.networkClients = [] thisObject.networkClients = []
thisObject.networkPeers = [] thisObject.networkPeers = []
@@ -55,7 +54,7 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
function setUpWebSocketServer() { function setUpWebSocketServer() {
try { try {
socketServer.on('connection', onConnectionOpened) thisObject.socketServer.on('connection', onConnectionOpened)
function onConnectionOpened(socket) function onConnectionOpened(socket)
/* /*
@@ -65,7 +64,8 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
let caller = { let caller = {
socket: socket, socket: socket,
userProfile: undefined, userProfile: undefined,
role: undefined role: undefined,
node: undefined
} }
caller.socket.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId() caller.socket.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId()
@@ -123,19 +123,19 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
let response let response
switch (caller.role) { switch (caller.role) {
case 'Network Client': { case 'Network Client': {
response = await clientInterface.messageReceived(messageHeader.payload, caller.userProfile) response = await thisObject.clientInterface.messageReceived(messageHeader.payload, caller.userProfile)
response.messageId = messageHeader.messageId response.messageId = messageHeader.messageId
caller.socket.send(JSON.stringify(response)) caller.socket.send(JSON.stringify(response))
break break
} }
case 'Network Peer': { case 'Network Peer': {
response = await peerInterface.messageReceived(messageHeader.payload) response = await thisObject.peerInterface.messageReceived(messageHeader.payload)
response.messageId = messageHeader.messageId response.messageId = messageHeader.messageId
caller.socket.send(JSON.stringify(response)) caller.socket.send(JSON.stringify(response))
break break
} }
} }
if (response.result === 'Ok' && messageHeader.payload.requestType === 'Event') { if (response.result === 'Ok' && JSON.parse(messageHeader.payload).requestType === 'Event') {
broadcastToPeers(messageHeader, caller) broadcastToPeers(messageHeader, caller)
broadcastToClients(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. The caller needs to provide it's User Profile Handle.
*/ */
if (messageHeader.callerProfileHandle === undefined) { if (messageHeader.callerProfileHandle === undefined) {
@@ -262,7 +288,6 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
caller.socket.close() caller.socket.close()
return return
} }
caller.userProfileHandle = messageHeader.callerProfileHandle caller.userProfileHandle = messageHeader.callerProfileHandle
/* /*
The caller needs to provide a callerTimestamp. The caller needs to provide a callerTimestamp.
@@ -492,14 +517,24 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock
} }
function broadcastToPeers(messageHeader, caller) { 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 let callerIdToAVoid
if (caller.role === 'Network Peer') { if (caller.role === 'Network Peer') {
callerIdToAVoid = caller.socket.id callerIdToAVoid = caller.node.id
} }
for (let i = 0; i < thisObject.networkPeers.length; i++) { for (let i = 0; i < NT.networkNode.p2pNetworkPeers.peers.length; i++) {
let networkPeer = thisObject.networkPeers[i] let peer = NT.networkNode.p2pNetworkPeers.peers[i]
if (networkPeer.socket.id === callerIdToAVoid) { continue } if (peer.p2pNetworkNode.node.id === callerIdToAVoid) { continue }
networkPeer.socket.send(messageHeader) 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++) { for (let i = 0; i < thisObject.networkClients.length; i++) {
let networkClient = thisObject.networkClients[i] let networkClient = thisObject.networkClients[i]
if (networkClient.socket.id === callerIdToAVoid) { continue } 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.')
}
} }
} }
-10
View File
@@ -4,9 +4,7 @@ exports.newNetworkModulesP2PNetwork = function newNetworkModulesP2PNetwork() {
of this current node to other peers. of this current node to other peers.
*/ */
let thisObject = { let thisObject = {
joinTheNetworkAsNetworkPeer: joinTheNetworkAsNetworkPeer,
p2pNodesToConnect: undefined, p2pNodesToConnect: undefined,
thisNetworkNode: undefined,
/* Framework Functions */ /* Framework Functions */
initialize: initialize, initialize: initialize,
finalize: finalize finalize: finalize
@@ -38,18 +36,10 @@ exports.newNetworkModulesP2PNetwork = function newNetworkModulesP2PNetwork() {
let p2pNetworkNode = SA.projects.network.globals.memory.arrays.P2P_NETWORK_NODES[i] let p2pNetworkNode = SA.projects.network.globals.memory.arrays.P2P_NETWORK_NODES[i]
if (thisP2PNodeId !== p2pNetworkNode.node.id) { if (thisP2PNodeId !== p2pNetworkNode.node.id) {
thisObject.p2pNodesToConnect.push(p2pNetworkNode) thisObject.p2pNodesToConnect.push(p2pNetworkNode)
} else {
thisObject.thisNetworkNode = p2pNetworkNode
} }
} }
break break
} }
} }
} }
function joinTheNetworkAsNetworkPeer() {
/*
To join the network we will need to thorugh the list of Network Nodes and pick one to connect to.
*/
}
} }
@@ -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() {
}
}
+59 -39
View File
@@ -1,9 +1,10 @@
exports.newNetworkModulesP2PNetworkPeers = function newNetworkModulesP2PNetworkPeers() { 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 = { let thisObject = {
outgoingConnectedPeers: undefined, peers: undefined,
sendMessage: sendMessage,
/* Framework Functions */ /* Framework Functions */
initialize: initialize, initialize: initialize,
@@ -16,63 +17,82 @@ exports.newNetworkModulesP2PNetworkPeers = function newNetworkModulesP2PNetworkP
return thisObject return thisObject
function finalize() { function finalize() {
thisObject.outgoingConnectedPeers = undefined thisObject.peers = undefined
clearInterval(intervalIdConnectToPeers) clearInterval(intervalIdConnectToPeers)
} }
async function initialize() { async function initialize(
callerRole,
p2pNetworkIdentity,
p2pNetwork,
maxOutgoingPeers
) {
thisObject.outgoingConnectedPeers = [] thisObject.peers = []
connectToPeers() connectToPeers()
intervalIdConnectToPeers = setInterval(connectToPeers, RECONNECT_DELAY); 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++) { let peer = {
if (thisObject.outgoingConnectedPeers.count >= global.env.P2P_NETWORK_NODE_MAX_OUTGOING_PEERS) { break } p2pNetworkNode: undefined,
let peer = {} webSocketsClient: undefined
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)
function addPeer() { peer.p2pNetworkNode = p2pNetwork.p2pNodesToConnect[i]
thisObject.outgoingConnectedPeers.push(peer) 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) { function addPeer() {
if (err !== undefined) { thisObject.peers.push(peer)
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 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) { function isConnectedPeer(peer) {
for (let i = 0; i < thisObject.outgoingConnectedPeers.length; i++) { for (let i = 0; i < thisObject.peers.length; i++) {
let connectedPeer = thisObject.outgoingConnectedPeers[i] let connectedPeer = thisObject.peers[i]
if (connectedPeer.webSocketsClient.id === webSocketsClientId) { if (connectedPeer.p2pNetworkNode.node.id === peer.p2pNetworkNode.node.id) {
thisObject.outgoingConnectedPeers.splice(i, 1) return true
return
} }
} }
} }
} }
}
function isOutgoingConnectedPeer(peer) { async function sendMessage(message) {
for (let i = 0; i < thisObject.outgoingConnectedPeers.length; i++) { /*
let connectedPeer = thisObject.outgoingConnectedPeers[i] This function will send the message from a random picked network node
if (connectedPeer.p2pNetworkNode.node.id === peer.p2pNetworkNode.node.id) { selected from the array of already connected peers.
return true */
} let peerIndex = Math.max(Math.round(Math.random() * thisObject.peers.length) - 1, 0)
} let peer = thisObject.peers[peerIndex]
} return await peer.webSocketsClient.sendMessage(message)
} }
} }
+13 -3
View File
@@ -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( let pluginFileNames = await SA.projects.communityPlugins.utilities.plugins.getPluginFileNames(
'Governance', 'Governance',
@@ -65,7 +65,7 @@ exports.newNetworkModulesUserProfiles = function newNetworkModulesUserProfiles()
let signatureObject = config.signature let signatureObject = config.signature
let web3 = new SA.nodeModules.web3() let web3 = new SA.nodeModules.web3()
let blockchainAccount = web3.eth.accounts.recover(signatureObject) 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. 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) { if (config.webSocketsPort === undefined) {
continue continue
} }
let p2pNetworkNode = SA.projects.network.modules.p2pNetworkNode.newNetworkModulesP2PNetworkNode() let p2pNetworkNode = SA.projects.network.modules.p2pNetworkNode.newNetworkModulesP2PNetworkNode()
p2pNetworkNode.initialize(signingAccount.signingAccountChild, userProfile, blockchainAccount) p2pNetworkNode.initialize(signingAccount.signingAccountChild, userProfile, blockchainAccount)
SA.projects.network.globals.memory.arrays.P2P_NETWORK_NODES.push(p2pNetworkNode) 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
}
} }
} }
} }
@@ -2,16 +2,19 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
let thisObject = { let thisObject = {
id: undefined, id: undefined,
socket: undefined,
isConnected: undefined, isConnected: undefined,
host: undefined, host: undefined,
port: undefined, port: undefined,
callerRole: undefined,
p2pNetworkNode: undefined,
p2pNetworkIdentity: undefined,
onConnectionClosedCallBack: undefined,
sendMessage: sendMessage, sendMessage: sendMessage,
initialize: initialize, initialize: initialize,
finalize: finalize finalize: finalize
} }
let socketClient
let web3 let web3
let called = {} let called = {}
let onMessageFunctionsMap = new Map() let onMessageFunctionsMap = new Map()
@@ -19,33 +22,44 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
return thisObject return thisObject
function finalize() { function finalize() {
socketClient.close() thisObject.socket.close()
socketClient = undefined thisObject.socket = undefined
networkInterface = undefined thisObject.id = undefined
peerInterface = undefined thisObject.isConnected = undefined
thisObject.host = undefined
thisObject.port = undefined
thisObject.callerRole = undefined
thisObject.p2pNetworkNode = undefined
thisObject.p2pNetworkIdentity = undefined
thisObject.onConnectionClosedCallBack = undefined
web3 = undefined web3 = undefined
called = undefined called = undefined
onMessageFunctionsMap = 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() web3 = new SA.nodeModules.web3()
thisObject.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId() thisObject.id = SA.projects.foundations.utilities.miscellaneousFunctions.genereteUniqueId()
thisObject.host = JSON.parse(p2pNetworkNode.node.config).host thisObject.host = JSON.parse(thisObject.p2pNetworkNode.node.config).host
thisObject.port = JSON.parse(p2pNetworkNode.node.config).webSocketsPort thisObject.port = JSON.parse(thisObject.p2pNetworkNode.node.config).webSocketsPort
socketClient = new SA.nodeModules.ws('ws://' + thisObject.host + ':' + thisObject.port) thisObject.socket = new SA.nodeModules.ws('ws://' + thisObject.host + ':' + thisObject.port)
await setUpWebsocketClient(callerRole, p2pNetworkNode, onConnectionClosedCallBack) 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 thisObject.isConnected = true
} }
async function setUpWebsocketClient(callerRole, p2pNetworkNode, onConnectionClosedCallBack) { async function setUpWebSocketClient() {
return new Promise(connectToNewtwork) return new Promise(connectToNewtwork)
@@ -53,9 +67,9 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
try { try {
socketClient.onopen = () => { onConnectionOpened() } thisObject.socket.onopen = () => { onConnectionOpened() }
socketClient.onclose = () => { onConnectionClosed() } thisObject.socket.onclose = () => { onConnectionClosed() }
socketClient.onerror = err => { onError(err) } thisObject.socket.onerror = err => { onError(err) }
function onConnectionOpened() { function onConnectionOpened() {
@@ -79,18 +93,19 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
it's own identity, and later we will sign it's own handle it's own identity, and later we will sign it's own handle
to prove ours. to prove ours.
*/ */
socketClient.onmessage = socketMessage => { stepOneResponse(socketMessage) } thisObject.socket.onmessage = socketMessage => { stepOneResponse(socketMessage) }
callerTimestamp = (new Date()).valueOf() callerTimestamp = (new Date()).valueOf()
let message = { let message = {
messageType: 'Handshake', messageType: 'Handshake',
callerRole: callerRole, callerRole: thisObject.callerRole,
callerProfileHandle: SA.secrets.map.get(global.env.DESKTOP_APP_SIGNING_ACCOUNT).userProfileHandle, callerProfileHandle: SA.secrets.map.get(global.env.DESKTOP_APP_SIGNING_ACCOUNT).userProfileHandle,
callerTimestamp: callerTimestamp, callerTimestamp: callerTimestamp,
callerNode: JSON.stringify(thisObject.p2pNetworkIdentity.node),
step: 'One' step: 'One'
} }
socketClient.send(JSON.stringify(message)) thisObject.socket.send(JSON.stringify(message))
} }
function stepOneResponse(socketMessage) { function stepOneResponse(socketMessage) {
@@ -116,7 +131,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
We will check that the blockchain account taken from the signature matches 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. 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.') console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.')
reject() reject()
return return
@@ -138,7 +153,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
We will check that the Network Node that responded has the same User Profile Handle 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. 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.') console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.')
reject() reject()
return return
@@ -175,7 +190,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
Here we will sign a message with the Network Node profile Here we will sign a message with the Network Node profile
handle and timestamp to prove our own identity. 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) 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), signature: JSON.stringify(signature),
step: 'Two' step: 'Two'
} }
socketClient.send(JSON.stringify(message)) thisObject.socket.send(JSON.stringify(message))
} }
function stepTwoResponse(socketMessage) { function stepTwoResponse(socketMessage) {
@@ -200,7 +215,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
Network Node and from now on, all response messages will be received Network Node and from now on, all response messages will be received
at this following function. at this following function.
*/ */
socketClient.onmessage = socketMessage => { onMenssage(socketMessage) } thisObject.socket.onmessage = socketMessage => { onMenssage(socketMessage) }
resolve() resolve()
} }
} }
@@ -208,10 +223,10 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
function onConnectionClosed() { function onConnectionClosed() {
if (thisObject.isConnected === true) { 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) { if (thisObject.onConnectionClosedCallBack !== undefined) {
onConnectionClosedCallBack(thisObject.id) thisObject.onConnectionClosedCallBack(thisObject.id)
} }
thisObject.isConnected = false thisObject.isConnected = false
} }
@@ -229,7 +244,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
} }
} catch (err) { } 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) { 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.') console.log('[ERROR] Web Sockets Client -> sendMessage -> Cannot send message while connection is closed.')
reject('Websockets Connection Not Ready.') reject('Websockets Connection Not Ready.')
return return
@@ -253,7 +268,7 @@ exports.newNetworkModulesWebSocketsNetworkClient = function newNetworkModulesWeb
payload: message payload: message
} }
onMessageFunctionsMap.set(socketMessage.messageId, onMenssageFunction) onMessageFunctionsMap.set(socketMessage.messageId, onMenssageFunction)
socketClient.send( thisObject.socket.send(
JSON.stringify(socketMessage) 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. We get the function that is going to resolve or reject the promise given.
*/ */
onMenssageFunction = onMessageFunctionsMap.get(response.messageId) let onMenssageFunction = onMessageFunctionsMap.get(response.messageId)
onMessageFunctionsMap.delete(response.messageId)
onMenssageFunction(response) 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
}
}
}
} }
} }
+6
View File
@@ -1419,6 +1419,12 @@
"propertyName": "p2pNetworkPeers", "propertyName": "p2pNetworkPeers",
"functionName": "newNetworkModulesP2PNetworkPeers", "functionName": "newNetworkModulesP2PNetworkPeers",
"fileName": "P2PNetworkPeers.js" "fileName": "P2PNetworkPeers.js"
},
{
"name": "P2P Network Client",
"propertyName": "p2pNetworkClient",
"functionName": "newNetworkModulesP2PNetworkClient",
"fileName": "P2PNetworkClient.js"
} }
], ],
"utilities": [ "utilities": [
@@ -14,7 +14,6 @@ exports.newSocialTradingModulesSocialGraph = function newSocialTradingModulesSoc
no need to ask it to a network node. no need to ask it to a network node.
*/ */
let thisObject = { let thisObject = {
userProfiles: undefined,
initialize: initialize, initialize: initialize,
finalize: finalize finalize: finalize
} }
@@ -26,7 +25,6 @@ exports.newSocialTradingModulesSocialGraph = function newSocialTradingModulesSoc
} }
async function initialize() { async function initialize() {
thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles()
await thisObject.userProfiles.initialize()
} }
} }
@@ -58,11 +58,11 @@ exports.newSocialTradingModulesWebAppInterface = function newSocialTradingModule
response = { response = {
result: 'Ok', result: 'Ok',
message: 'Web App Interface Query Processed.', 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 { } 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++) { for (let i = 0; i < events.length; i++) {
let event = events[i] let event = events[i]
if (event.eventType === SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST) { if (event.eventType === SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST) {
@@ -8,9 +8,9 @@ exports.newSocialTradingModulesPeerInterface = function newSocialTradingModulesP
websockets interface. websockets interface.
*/ */
let thisObject = { let thisObject = {
messageReceived: messageReceived,
initialize: initialize, initialize: initialize,
finalize: finalize, finalize: finalize
run: run
} }
return thisObject return thisObject
@@ -22,8 +22,130 @@ exports.newSocialTradingModulesPeerInterface = function newSocialTradingModulesP
function initialize() { 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
}
} }
} }
@@ -9,7 +9,7 @@ exports.newNetworkModulesSocialGraph = function newNetworkModulesSocialGraph() {
their posts. their posts.
*/ */
let thisObject = { let thisObject = {
userProfiles: undefined,
/* Framework Functions */ /* Framework Functions */
initialize: initialize, initialize: initialize,
finalize: finalize finalize: finalize
@@ -22,7 +22,6 @@ exports.newNetworkModulesSocialGraph = function newNetworkModulesSocialGraph() {
} }
async function initialize() { async function initialize() {
thisObject.userProfiles = SA.projects.network.modules.userProfiles.newNetworkModulesUserProfiles()
await thisObject.userProfiles.initialize()
} }
} }