diff --git a/Environment.js b/Environment.js index a599d095e..78846c598 100644 --- a/Environment.js +++ b/Environment.js @@ -17,6 +17,7 @@ exports.newEnvironment = function () { DESKTOP_WEB_SOCKETS_INTERFACE_HOST: 'localhost', PLATFORM_HTTP_INTERFACE_PORT: 34248, DESKTOP_HTTP_INTERFACE_PORT: 33248, + NETWORK_HTTP_INTERFACE_PORT: 31248, PATH_TO_DATA_STORAGE: path.join(basePath, './Platform/My-Data-Storage'), PATH_TO_PROJECTS: path.join(basePath, './Projects'), PATH_TO_LOG_FILES: path.join(basePath, './Platform/My-Log-Files'), diff --git a/Network/NetwokNode.js b/Network/NetwokNode.js index 4bd5f705c..64dcaaa88 100644 --- a/Network/NetwokNode.js +++ b/Network/NetwokNode.js @@ -6,6 +6,7 @@ exports.newNetworkNode = function newNetworkNode() { p2pNetworkPeers: undefined, p2pNetworkNode: undefined, webSocketsInterface: undefined, + httpInterface: undefined, socialGraphService: undefined, storage: undefined, run: run @@ -65,6 +66,12 @@ exports.newNetworkNode = function newNetworkNode() { 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) + /* + Other Network Nodes and Client Apps will communicate with this Network Node via it's HTTP Interface. + */ + thisObject.httpInterface = NT.projects.network.modules.httpInterface.newNetworkModulesHttpInterface() + thisObject.httpInterface.initialize() + console.log('Network Node Http Interface .................................................. Listening at port ' + JSON.parse(NT.networkNode.p2pNetworkNode.node.config).webPort) } } } diff --git a/NetworkRoot.js b/NetworkRoot.js index a09e3cd62..eab6a7d4e 100644 --- a/NetworkRoot.js +++ b/NetworkRoot.js @@ -47,6 +47,7 @@ exports.newNetworkRoot = function newNetworkRoot() { fs: require('fs'), path: require('path'), util: require('util'), + http: require('http'), nodeFetch: require('node-fetch'), web3: require('web3'), ws: require('ws'), diff --git a/Platform/Client/web3Server.js b/Platform/Client/web3Server.js index 0e4624bf5..c156b7e14 100644 --- a/Platform/Client/web3Server.js +++ b/Platform/Client/web3Server.js @@ -90,7 +90,7 @@ exports.newWeb3Server = function newWeb3Server() { let balance - let ABI = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"listAddress","type":"address"},{"name":"isBlackListed","type":"bool"}],"name":"blackListAddress","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint256"},{"name":"_supply","type":"uint256"},{"name":"tokenOwner","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"blackListed","type":"address"},{"indexed":false,"name":"value","type":"bool"}],"name":"Blacklist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] + let ABI = [{ "constant": true, "inputs": [], "name": "name", "outputs": [{ "name": "", "type": "string" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "totalSupply", "outputs": [{ "name": "", "type": "uint256" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }], "name": "transferFrom", "outputs": [{ "name": "", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "decimals", "outputs": [{ "name": "", "type": "uint256" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "unpause", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [{ "name": "account", "type": "address" }, { "name": "amount", "type": "uint256" }], "name": "mint", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [{ "name": "_value", "type": "uint256" }], "name": "burn", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "paused", "outputs": [{ "name": "", "type": "bool" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "_spender", "type": "address" }, { "name": "_subtractedValue", "type": "uint256" }], "name": "decreaseApproval", "outputs": [{ "name": "success", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [{ "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "balance", "type": "uint256" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "listAddress", "type": "address" }, { "name": "isBlackListed", "type": "bool" }], "name": "blackListAddress", "outputs": [{ "name": "success", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [], "name": "pause", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [{ "name": "", "type": "address" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "symbol", "outputs": [{ "name": "", "type": "string" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }], "name": "transfer", "outputs": [{ "name": "", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [{ "name": "_spender", "type": "address" }, { "name": "_addedValue", "type": "uint256" }], "name": "increaseApproval", "outputs": [{ "name": "success", "type": "bool" }], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [{ "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" }], "name": "allowance", "outputs": [{ "name": "", "type": "uint256" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "newOwner", "type": "address" }], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "name": "_name", "type": "string" }, { "name": "_symbol", "type": "string" }, { "name": "_decimals", "type": "uint256" }, { "name": "_supply", "type": "uint256" }, { "name": "tokenOwner", "type": "address" }], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" }], "name": "Mint", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "burner", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" }], "name": "Burn", "type": "event" }, { "anonymous": false, "inputs": [], "name": "Pause", "type": "event" }, { "anonymous": false, "inputs": [], "name": "Unpause", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "previousOwner", "type": "address" }, { "indexed": true, "name": "newOwner", "type": "address" }], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "blackListed", "type": "address" }, { "indexed": false, "name": "value", "type": "bool" }], "name": "Blacklist", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" }], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" }], "name": "Transfer", "type": "event" }] const contractInst = new web3.eth.Contract(ABI, contractAddress) @@ -101,8 +101,11 @@ exports.newWeb3Server = function newWeb3Server() { await getBalance() - return balance - + return { + walletAddress: walletAddress, + balance: balance, + result: 'Ok' + } } async function getWalletBalances(host, port, interface, walletDefinition) { @@ -326,7 +329,7 @@ exports.newWeb3Server = function newWeb3Server() { async function sendTokens(number, userProfile, fromAddress, toAddress, tokenAmount) { try { - + tokenAmount = Math.trunc(tokenAmount / 1000000000000000000) console.log('') @@ -384,12 +387,12 @@ exports.newWeb3Server = function newWeb3Server() { console.log('Transaction:', rawTransaction) let result - + result = await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex')) .catch(err => { console.log('[ERROR] sendSignedTransaction -> err =' + JSON.stringify(err)) }) - + console.log('Result:', result) return result } catch (err) { diff --git a/Platform/WebServer/Images/sa-v10-logo-horiz-dark.png b/Platform/WebServer/Images/sa-v10-logo-horiz-dark.png new file mode 100644 index 000000000..2eab06ccc Binary files /dev/null and b/Platform/WebServer/Images/sa-v10-logo-horiz-dark.png differ diff --git a/Projects/Foundations/UI/Spaces/Top-Space/TopSpace.js b/Projects/Foundations/UI/Spaces/Top-Space/TopSpace.js index 310dfd365..9dfbeadba 100644 --- a/Projects/Foundations/UI/Spaces/Top-Space/TopSpace.js +++ b/Projects/Foundations/UI/Spaces/Top-Space/TopSpace.js @@ -24,7 +24,7 @@ function newFoundationsTopSpace() { logoImage = new Image() loadImage('superalgos-header-background', backgroundImage) - loadImage('sa-beta12-logo-horiz-dark', logoImage) + loadImage('sa-v10-logo-horiz-dark', logoImage) function loadImage(name, image) { const PATH = 'Images/' diff --git a/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json b/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json index 14668a46b..a60755b4f 100644 --- a/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json +++ b/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json @@ -17236,7 +17236,7 @@ "signingAccountChild": { "type": "P2P Network Node", "name": "P2P Network Node # 1", - "config": "{\n \"codeName\": \"P2P-Network-Node-1\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18042\"\n}", + "config": "{\n \"codeName\": \"P2P-Network-Node-1\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18042\",\n \"webPort\": \"31248\"\n}", "project": "Governance", "id": "bb0f0417-9dfb-47a3-941f-dcfa82fbaea1", "savedPayload": { @@ -17292,7 +17292,7 @@ "signingAccountChild": { "type": "P2P Network Node", "name": "P2P Network Node # 2", - "config": "{\n \"codeName\": \"P2P-Network-Node-2\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18043\"\n}", + "config": "{\n \"codeName\": \"P2P-Network-Node-2\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18043\",\n \"webPort\": \"31248\"\n}", "project": "Governance", "id": "76fa8a24-9ab4-4eaf-82f2-458ba557b204", "savedPayload": { @@ -17348,7 +17348,7 @@ "signingAccountChild": { "type": "P2P Network Node", "name": "P2P Network Node # 3", - "config": "{\n \"codeName\": \"P2P-Network-Node-3\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18044\"\n}", + "config": "{\n \"codeName\": \"P2P-Network-Node-3\",\n \"host\": \"localhost\",\n \"webSocketsPort\": \"18044\",\n \"webPort\": \"31248\"\n}", "project": "Governance", "id": "297e57c2-3c1a-4170-b710-d2705804205b", "savedPayload": { diff --git a/Projects/Governance/Schemas/App-Schema/p2p-network-node.json b/Projects/Governance/Schemas/App-Schema/p2p-network-node.json index ae7aeb2d4..17f1a4838 100644 --- a/Projects/Governance/Schemas/App-Schema/p2p-network-node.json +++ b/Projects/Governance/Schemas/App-Schema/p2p-network-node.json @@ -25,7 +25,7 @@ "config": true }, "initialValues": { - "config": "{ \n\"codeName\": \"localhost\", \n\"host\": \"localhost\", \n\"webSocketsPort\": \"18042\"\n}" + "config": "{ \n\"codeName\": \"localhost\", \n\"host\": \"localhost\", \n\"webPort\": \"31248\", \n\"webSocketsPort\": \"18042\"\n}" }, "addLeftIcons": true, "level": 1, diff --git a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js index 3397da39a..e48d2f4c5 100644 --- a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js +++ b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js @@ -484,10 +484,15 @@ function newGovernanceUserProfileSpace() { console.log('[WARN] Error fetching blockchain tokens of user profile ' + userProfile.name) userProfile.payload.blockchainTokens = undefined } else { + let commandResponse = JSON.parse(data) + if (commandResponse.result !== "Ok") { + console.log('[WARN] Web3 Error fetching blockchain tokens of user profile ' + userProfile.name) + return + } userProfile.payload.uiObject.setInfoMessage('Blockchain Balance Successfully Loaded.', UI.projects.governance.globals.designer.SET_INFO_COUNTER_FACTOR ) - userProfile.payload.blockchainTokens = Number(data) + userProfile.payload.blockchainTokens = Number(commandResponse.balance) console.log('[INFO] SA Balance of ' + userProfile.name + ' is ', userProfile.payload.blockchainTokens) userProfile.payload.reputation = Math.min(reputationByAddress.get(blockchainAccount.toLowerCase()) | 0, userProfile.payload.blockchainTokens) console.log('[INFO] Reputation of ' + userProfile.name + ' is ', userProfile.payload.reputation) @@ -514,10 +519,15 @@ function newGovernanceUserProfileSpace() { console.log('[WARN] Error fetching liquidity tokens for asset ' + asset + ' of user profile ' + userProfile.name) userProfile.payload.blockchainTokens = undefined } else { + let commandResponse = JSON.parse(data) + if (commandResponse.result !== "Ok") { + console.log('[WARN] Web3 Error fetching liquidity tokens for asset ' + asset + ' of user profile ' + userProfile.name) + return + } userProfile.payload.uiObject.setInfoMessage('Pancake Balance Successfully Loaded for asset ' + asset, UI.projects.governance.globals.designer.SET_INFO_COUNTER_FACTOR ) - userProfile.payload.liquidityTokens[asset] = Number(data) + userProfile.payload.liquidityTokens[asset] = Number(commandResponse.balance) console.log('[INFO] Liquidity of ' + userProfile.name + ' for asset ' + asset + ' is ', userProfile.payload.liquidityTokens[asset]) } } diff --git a/Projects/Network/NT/Globals/Memory.js b/Projects/Network/NT/Globals/Memory.js index 560d7ad6f..1d8692cee 100644 --- a/Projects/Network/NT/Globals/Memory.js +++ b/Projects/Network/NT/Globals/Memory.js @@ -4,12 +4,14 @@ exports.newNetworkGlobalsMemory = function () { */ let thisObject = { maps: { - EVENTS: new Map() // This is the registry of all events received that prevents processing them more than once. + EVENTS: new Map(), // This is the registry of all events received that prevents processing them more than once. + SIGNALS: new Map() // This is the registry of all signals received that prevents processing them more than once. }, arrays: { NETWORK_CLIENTS: [], // These are the Network Clients connected to this Network Node ordered by Ranking. NETWORK_PEERS: [], // These are the Network Peers connected to this Network Node ordered by Ranking. - EVENTS: [] // This is the registry of all events in order that prevents ordering them more than once. + EVENTS: [], // This is the registry of all events in order that prevents ordering them more than once. + SIGNALS: [] // This is the registry of all signals in order that prevents ordering them more than once. } } diff --git a/Projects/Network/NT/Modules/HttpInterface.js b/Projects/Network/NT/Modules/HttpInterface.js new file mode 100644 index 000000000..177191389 --- /dev/null +++ b/Projects/Network/NT/Modules/HttpInterface.js @@ -0,0 +1,52 @@ +exports.newNetworkModulesHttpInterface = function newNetworkModulesHttpInterface() { + /* + This module represent the HTTP API of the + NNetwork Node. All HTTP request are processed + by this module. + */ + let thisObject = { + initialize: initialize, + finalize: finalize + } + + return thisObject + + function finalize() { + } + + function initialize() { + let port = JSON.parse(NT.networkNode.p2pNetworkNode.node.config).webPort + /* + We will create an HTTP Server and leave it running forever. + */ + SA.nodeModules.http.createServer(onHttpRequest).listen(port) + } + + async function onHttpRequest(httpRequest, httpResponse) { + try { + let requestPathAndParameters = httpRequest.url.split('?') // Remove version information + let requestPath = requestPathAndParameters[0].split('/') + let endpointOrFile = requestPath[1] + + switch (endpointOrFile) { + case 'Signal': + { + let message = requestPath[2] + let response = await thisObject.clientInterface.messageReceived(message) + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(response), httpResponse) + } + break + case 'Stats': + { + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify([]), httpResponse) + } + break + default: + { + } + } + } catch (err) { + console.log(err.stack) + } + } +} diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index a712b680e..9643fb8d9 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -1389,6 +1389,12 @@ "propertyName": "webSocketsInterface", "functionName": "newNetworkModulesWebSocketsInterface", "fileName": "WebSocketsInterface.js" + }, + { + "name": "Http Interface", + "propertyName": "httpInterface", + "functionName": "newNetworkModulesHttpInterface", + "fileName": "HttpInterface.js" } ], "utilities": [] @@ -1528,6 +1534,12 @@ "functionName": "newSocialTradingModulesEvent", "fileName": "Event.js" }, + { + "name": "Signal", + "propertyName": "signal", + "functionName": "newSocialTradingModulesSignal", + "fileName": "Signal.js" + }, { "name": "Client Interface", "propertyName": "clientInterface", @@ -1595,6 +1607,24 @@ "propertyName": "eventTypes", "functionName": "newSocialTradingGlobalsEventTypes", "fileName": "EventTypes.js" + }, + { + "name": "Signal Types", + "propertyName": "eventTypes", + "functionName": "newSocialTradingGlobalsSignalTypes", + "fileName": "SignalTypes.js" + }, + { + "name": "Post Types", + "propertyName": "postTypes", + "functionName": "newSocialTradingGlobalsPostTypes", + "fileName": "PostTypes.js" + }, + { + "name": "Reaction Types", + "propertyName": "reactionTypes", + "functionName": "newSocialTradingGlobalsReactionTypes", + "fileName": "ReactionTypes.js" } ] }, @@ -1605,7 +1635,7 @@ "name": "Http Interface", "propertyName": "httpInterface", "functionName": "newDesktopModulesHttpInterface", - "fileName": "httpInterface.js" + "fileName": "HttpInterface.js" }, { "name": "Web Sockets Interface", diff --git a/Projects/Social-Trading/NT/Modules/ClientInterface.js b/Projects/Social-Trading/NT/Modules/ClientInterface.js index bd429b878..e99344c1a 100644 --- a/Projects/Social-Trading/NT/Modules/ClientInterface.js +++ b/Projects/Social-Trading/NT/Modules/ClientInterface.js @@ -1,15 +1,16 @@ exports.newSocialTradingModulesClientInterface = function newSocialTradingModulesClientInterface() { /* This module represents the Interface the Network Node have - with Network Clients connected to it. There are two things + with Network Clients connected to it. There are 3 things Network Clients can do with this Network Node: - * Send an Event that happened at the Network Client to the Network. * Make a Query to the Network Node for info needed at the Network Client. + * Send an Event that happened at the Network Client to the Network. + * Send a Signal that needs to be distributed. This interface is one layer above the communication protocol interface where actual messages are received through, like for instance the - websockets interface. + websockets interface or http interface. */ let thisObject = { messageReceived: messageReceived, @@ -47,7 +48,7 @@ exports.newSocialTradingModulesClientInterface = function newSocialTradingModule return response } - if (messageHeader.requestType !== 'Event' && messageHeader.requestType !== 'Query') { + if (messageHeader.requestType !== 'Event' && messageHeader.requestType !== 'Query' && messageHeader.requestType !== 'Signal') { let response = { result: 'Error', message: 'Client Interface requestType Not Supported.' @@ -56,15 +57,99 @@ exports.newSocialTradingModulesClientInterface = function newSocialTradingModule } switch (messageHeader.requestType) { + case 'Query': { + return await queryReceived(messageHeader.queryMessage, userProfile) + } case 'Event': { return await eventReceived(messageHeader.eventMessage, userProfile) } - case 'Query': { - return await queryReceived(messageHeader.queryMessage, userProfile) + case 'Signal': { + return await signalReceived(messageHeader.eventMessage) } } } + async function queryReceived(queryMessage, userProfile) { + /* + We expect here a JSON string with some or all of the following properties: + + { + "queryType": 10, + "emitterUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "targetUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "emitterUserProfileHandle": "Luis-Fernando-Molina", + "targetUserProfileHandle": "Luis-Fernando-Molina", + "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, + "initialIndex": 35, + "amountRequested": 50, + "direction": "Past" + } + */ + + let queryReceived + try { + queryReceived = JSON.parse(queryMessage) + } catch (err) { + let response = { + result: 'Error', + message: 'Client Interface queryMessage Not Correct JSON Format.' + } + return response + } + /* + At the Client Interface, queries need to be emitted by the same userProfile that is + connected at the Network Node. + */ + if (queryReceived.emitterUserProfileId !== userProfile.userProfileId) { + let response = { + result: 'Error', + message: 'Client Interface emitterUserProfileId !== userProfileId Connected to Network Node.' + } + return response + } + /* + Here we will process the query. This does not change the state of the Social Graph. + */ + try { + let query = NT.projects.socialTrading.modules.query.newSocialTradingModulesQuery() + query.initialize(queryReceived) + + // console.log((new Date()).toISOString(), '- Client Interface', '- Query Message Received', queryMessage) + + let response = { + result: 'Ok', + message: 'Client Interface Query Processed.', + data: query.execute() + } + + // console.log((new Date()).toISOString(), '- Client Interface', '- Query Response Sent', JSON.stringify(response)) + + return response + + } catch (err) { + /* + Any exception that happens while trying to execute the query. + */ + if (err.stack !== undefined) { + console.log('[ERROR] Client Interface -> err.stack = ' + err.stack) + } + let errorMessage = err.message + if (errorMessage === undefined) { errorMessage = err } + let response = { + result: 'Error', + message: 'Client Interface ' + errorMessage + } + return response + } + } + async function eventReceived(eventMessage, userProfile) { /* We expect here a JSON string with some or all of the following properties: @@ -160,16 +245,15 @@ exports.newSocialTradingModulesClientInterface = function newSocialTradingModule } } - async function queryReceived(queryMessage, userProfile) { + async function signalReceived(signalMessage) { /* We expect here a JSON string with some or all of the following properties: { - "queryType": 10, + "signalId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", + "signalType": 10, "emitterUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", "targetUserProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", - "emitterUserProfileHandle": "Luis-Fernando-Molina", - "targetUserProfileHandle": "Luis-Fernando-Molina", "emitterBotProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", "targetBBotProfileId": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", "emitterPostHash": "a8de78f0-c3e4-4a2a-b7e8-f659073969db", @@ -177,56 +261,60 @@ exports.newSocialTradingModulesClientInterface = function newSocialTradingModule "timestamp": 124234234234, "botAsset": "BTC", "botExchange": "Binance", - "botEnabled": true, - "initialIndex": 35, - "amountRequested": 50, - "direction": "Past" + "botEnabled": true } */ - let queryReceived + let signalReceived try { - queryReceived = JSON.parse(queryMessage) + signalReceived = JSON.parse(signalMessage) } catch (err) { let response = { result: 'Error', - message: 'Client Interface queryMessage Not Correct JSON Format.' + message: 'Client Interface signalMessage Not Correct JSON Format.' } return response } /* - At the Client Interface, queries need to be emitted by the same userProfile that is - connected at the Network Node. + We will not accept signals that don't have an signalId. */ - if (queryReceived.emitterUserProfileId !== userProfile.userProfileId) { + if (signalReceived.signalId === undefined) { let response = { result: 'Error', - message: 'Client Interface emitterUserProfileId !== userProfileId Connected to Network Node.' + message: 'signalId Not Provided.' } return response } /* - Here we will process the query. This does not change the state of the Social Graph. + We will not accept signals that have already been processed. + */ + + if (NT.projects.network.globals.memory.maps.SIGNALS.get(signalReceived.signalId) !== undefined) { + let response = { + result: 'Error', + message: 'Client Interface Signal Already Exists.' + } + return response + } + /* + Here we will process the signal and change the state of the Social Graph. */ try { - let query = NT.projects.socialTrading.modules.query.newSocialTradingModulesQuery() - query.initialize(queryReceived) - - // console.log((new Date()).toISOString(), '- Client Interface', '- Query Message Received', queryMessage) + let signal = NT.projects.socialTrading.modules.signal.newSocialTradingModulesSignal() + signal.initialize(signalReceived) + NT.projects.network.globals.memory.maps.SIGNALS.set(signalReceived.eventId, signal) + NT.projects.network.globals.memory.arrays.SIGNALS.push(signal) let response = { result: 'Ok', - message: 'Client Interface Query Processed.', - data: query.execute() + message: 'Client Interface Signal Processed.' } - - // console.log((new Date()).toISOString(), '- Client Interface', '- Query Response Sent', JSON.stringify(response)) - return response } catch (err) { /* - Any exception that happens while trying to execute the query. + 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] Client Interface -> err.stack = ' + err.stack) diff --git a/Projects/Social-Trading/NT/Modules/Event.js b/Projects/Social-Trading/NT/Modules/Event.js index 9902cbf0e..91f9b814b 100644 --- a/Projects/Social-Trading/NT/Modules/Event.js +++ b/Projects/Social-Trading/NT/Modules/Event.js @@ -1,8 +1,7 @@ exports.newSocialTradingModulesEvent = function newSocialTradingModulesEvent() { /* - An Event is anything that happens system wide that - needs to be delivered to relevant entities via - the Superalgos Network. + An Event is an action taken by users that triggers a + change at the social graph. An Event (in contrast to a Query) produces a change on the state of the social graph. @@ -115,7 +114,7 @@ exports.newSocialTradingModulesEvent = function newSocialTradingModulesEvent() { function postingEvents() { /* - Is is related to Posting? + Is it related to Posting? */ if ( thisObject.eventType === SA.projects.socialTrading.globals.eventTypes.NEW_USER_POST || @@ -202,7 +201,7 @@ exports.newSocialTradingModulesEvent = function newSocialTradingModulesEvent() { function followingEvents() { /* - Is is a following? + Is it a following? */ if ( thisObject.eventType === SA.projects.socialTrading.globals.eventTypes.FOLLOW_USER_PROFILE || @@ -292,7 +291,7 @@ exports.newSocialTradingModulesEvent = function newSocialTradingModulesEvent() { function reactionEvents() { /* - Is is a Reaction? + Is it a Reaction? */ let targetPost @@ -346,7 +345,7 @@ exports.newSocialTradingModulesEvent = function newSocialTradingModulesEvent() { function botEvents() { /* - Is is a Bot? + Is it a Bot? */ if ( thisObject.eventType === SA.projects.socialTrading.globals.eventTypes.ADD_BOT || diff --git a/Projects/Social-Trading/NT/Modules/Queries/PostReplies.js b/Projects/Social-Trading/NT/Modules/Queries/PostReplies.js index d9af6ea72..cb29d0d9b 100644 --- a/Projects/Social-Trading/NT/Modules/Queries/PostReplies.js +++ b/Projects/Social-Trading/NT/Modules/Queries/PostReplies.js @@ -72,6 +72,8 @@ exports.newSocialTradingModulesQueriesPostReplies = function newSocialTradingMod targetPostHash: post.targetPostHash, postType: post.postType, timestamp: post.timestamp, + signalType: post.signalType, + signalData: post.signalData, repliesCount: post.replies.size, reactions: Array.from(post.reactions) } diff --git a/Projects/Social-Trading/NT/Modules/Queries/ProfilePosts.js b/Projects/Social-Trading/NT/Modules/Queries/ProfilePosts.js index a1971be61..68c52ac33 100644 --- a/Projects/Social-Trading/NT/Modules/Queries/ProfilePosts.js +++ b/Projects/Social-Trading/NT/Modules/Queries/ProfilePosts.js @@ -67,6 +67,8 @@ exports.newSocialTradingModulesQueriesUserProfileStats = function newSocialTradi targetPostHash: post.targetPostHash, postType: post.postType, timestamp: post.timestamp, + signalType: post.signalType, + signalData: post.signalData, repliesCount: post.replies.size, reactions: Array.from(post.reactions) } diff --git a/Projects/Social-Trading/NT/Modules/Signal.js b/Projects/Social-Trading/NT/Modules/Signal.js new file mode 100644 index 000000000..80278e153 --- /dev/null +++ b/Projects/Social-Trading/NT/Modules/Signal.js @@ -0,0 +1,153 @@ +exports.newSocialTradingModulesSignal = function newSocialTradingModulesSignal() { + /* + A Signal is an action taken by a bot that needs to be + delivered to all the followers of that bot respecting + the rule to deliver it fist to the bots belonging to + users with the highers ranking. + + An Signal is attached to the Social Graph as a new post + from the Bot producing the signal. + */ + let thisObject = { + /* Unique Key */ + signalId: undefined, + /* Referenced Entities Unique Keys */ + emitterUserProfileId: undefined, + targetUserProfileId: undefined, + emitterBotProfileId: undefined, + targetBotProfileId: undefined, + emitterPostHash: undefined, + targetPostHash: undefined, + /* Signal Unique Properties */ + signalType: undefined, + signalData: undefined, + timestamp: undefined, + /* Bot Related Properties */ + botAsset: undefined, + botExchange: undefined, + botEnabled: undefined, + /* Framework Functions */ + initialize: initialize, + finalize: finalize + } + + return thisObject + + function finalize() { + + } + + function initialize(signalReceived) { + /* + Store the message properties into this object properties. + */ + thisObject.signalId = signalReceived.signalId + thisObject.emitterUserProfileId = signalReceived.emitterUserProfileId + thisObject.targetUserProfileId = signalReceived.targetUserProfileId + thisObject.emitterBotProfileId = signalReceived.emitterBotProfileId + thisObject.targetBotProfileId = signalReceived.targetBotProfileId + thisObject.emitterPostHash = signalReceived.emitterPostHash + thisObject.targetPostHash = signalReceived.targetPostHash + thisObject.signalType = signalReceived.signalType + thisObject.signalData = signalReceived.signalData + thisObject.timestamp = signalReceived.timestamp + thisObject.botAsset = signalReceived.botAsset + thisObject.botExchange = signalReceived.botExchange + thisObject.botEnabled = signalReceived.botEnabled + /* + Local Variables + */ + let emitterUserProfile + let targetUserProfile + let emitterBotProfile + let targetBotProfile + /* + Validate Emitter User Profile. + */ + if (thisObject.emitterUserProfileId === undefined) { + throw ('Emitter User Profile Id Not Provided.') + } + emitterUserProfile = SA.projects.network.globals.memory.maps.USER_PROFILES_BY_ID.get(thisObject.emitterUserProfileId) + if (emitterUserProfile === undefined) { + throw ('Emitter User Profile Not Found.') + } + /* + Validate Target User Profile. + */ + if (thisObject.targetUserProfileId !== undefined) { + targetUserProfile = SA.projects.network.globals.memory.maps.USER_PROFILES_BY_ID.get(thisObject.targetUserProfileId) + if (targetUserProfile === undefined) { + throw ('Target User Profile Not Found.') + } + } + /* + Validate Emitter Bot Profile. + */ + if (thisObject.emitterBotProfileId !== undefined) { + emitterBotProfile = emitterUserProfile.bots.get(thisObject.emitterBotProfileId) + if (emitterBotProfile === undefined) { + throw ('Emitter Bot Profile Not Found.') + } + } + /* + Validate Target Bot Profile. + */ + if (thisObject.targetBotProfileId !== undefined) { + targetBotProfile = emitterUserProfile.bots.get(thisObject.targetBotProfileId) + if (targetBotProfile === undefined) { + throw ('Target Bot Profile Not Found.') + } + } + + if (tradingSignals()) { + signalCounters() + return + } + + function tradingSignals() { + /* + Is it related to Trading? + */ + if ( + thisObject.signalType === SA.projects.socialTrading.globals.signalTypes.TRIGGER_ON || + thisObject.signalType === SA.projects.socialTrading.globals.signalTypes.TRIGGER_OFF || + thisObject.signalType === SA.projects.socialTrading.globals.signalTypes.TAKE_POSITION || + thisObject.signalType === SA.projects.socialTrading.globals.signalTypes.CLOSE_POSITION + ) { + /* + We will create a new Bot Post with the Signal info attached to it. + */ + if (emitterBotProfile === undefined) { + throw ('Emitter Bot Profile Id Not Provided.') + } + + emitterBotProfile.addPost( + thisObject.emitterUserProfileId, + thisObject.targetUserProfileId, + thisObject.emitterBotProfileId, + thisObject.targetBotProfileId, + thisObject.emitterPostHash, + thisObject.targetPostHash, + SA.projects.socialTrading.globals.postTypes.SIGNAL_POST, + thisObject.timestamp, + thisObject.signalType, + thisObject.signalData + ) + return true + } + } + + function signalCounters() { + emitterUserProfile.emitterSignalsCount++ + if (targetUserProfile !== undefined) { + targetUserProfile.targetSignalsCount++ + } + if (emitterBotProfile !== undefined) { + emitterUserProfile.emitterSignalsCount++ + } + if (targetBotProfile !== undefined) { + targetBotProfile.targetSignalsCount++ + } + } + } +} \ No newline at end of file diff --git a/Projects/Social-Trading/SA/Globals/PostTypes.js b/Projects/Social-Trading/SA/Globals/PostTypes.js new file mode 100644 index 000000000..8146c04c3 --- /dev/null +++ b/Projects/Social-Trading/SA/Globals/PostTypes.js @@ -0,0 +1,13 @@ +exports.newSocialTradingGlobalsPostTypes = function () { + + let thisObject = { + /* Post Types */ + NEW_POST: 0, + REPLY_TO_POST: 1, + REPOST: 2, + QUOTE_REPOST: 3, + SIGNAL_POST: 4 + } + + return thisObject +} \ No newline at end of file diff --git a/Projects/Social-Trading/SA/Globals/ReactionTypes.js b/Projects/Social-Trading/SA/Globals/ReactionTypes.js new file mode 100644 index 000000000..44c4b9c03 --- /dev/null +++ b/Projects/Social-Trading/SA/Globals/ReactionTypes.js @@ -0,0 +1,15 @@ +exports.newSocialTradingGlobalsReactionTypes = function () { + + let thisObject = { + /* Reaction Types */ + REACTION_LIKE: 0, + REACTION_LOVE: 1, + REACTION_HAHA: 2, + REACTION_WOW: 3, + REACTION_SAD: 4, + REACTION_ANGRY: 5, + REACTION_CARE: 6 + } + + return thisObject +} \ No newline at end of file diff --git a/Projects/Social-Trading/SA/Globals/SignalTypes.js b/Projects/Social-Trading/SA/Globals/SignalTypes.js new file mode 100644 index 000000000..a97750de1 --- /dev/null +++ b/Projects/Social-Trading/SA/Globals/SignalTypes.js @@ -0,0 +1,12 @@ +exports.newSocialTradingGlobalsSignalTypes = function () { + + let thisObject = { + /* Trading Signals */ + TRIGGER_ON: 10, + TRIGGER_OFF: 11, + TAKE_POSITION: 12, + CLOSE_POSITION: 13 + } + + return thisObject +} \ No newline at end of file diff --git a/Projects/Social-Trading/SA/Modules/Social-Graph/BotProfile.js b/Projects/Social-Trading/SA/Modules/Social-Graph/BotProfile.js index 886bcd53d..eee366470 100644 --- a/Projects/Social-Trading/SA/Modules/Social-Graph/BotProfile.js +++ b/Projects/Social-Trading/SA/Modules/Social-Graph/BotProfile.js @@ -75,7 +75,9 @@ exports.newSocialTradingModulesSocialGraphBotProfile = function newSocialTrading emitterPostHash, targetPostHash, postType, - timestamp + timestamp, + signalType, + signalData ) { if (thisObject.posts.get(emitterPostHash) !== undefined) { throw ('Post Already Exists.') @@ -89,7 +91,9 @@ exports.newSocialTradingModulesSocialGraphBotProfile = function newSocialTrading emitterPostHash, targetPostHash, postType, - timestamp + timestamp, + signalType, + signalData ) thisObject.posts.set(emitterPostHash, post) diff --git a/Projects/Social-Trading/SA/Modules/Social-Graph/Post.js b/Projects/Social-Trading/SA/Modules/Social-Graph/Post.js index f3678cc62..ecd515087 100644 --- a/Projects/Social-Trading/SA/Modules/Social-Graph/Post.js +++ b/Projects/Social-Trading/SA/Modules/Social-Graph/Post.js @@ -1,6 +1,6 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModulesSocialGraphPost() { /* - Posts represent a collection of multimedia content sorted somewhere else + Posts represent a collection of multimedia content stored somewhere else and identified by that content hash. Posts might belong to: * a User Profile. * a Bot Profile. @@ -23,6 +23,9 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModule /* Post Unique Properties */ postType: undefined, timestamp: undefined, + /* Signal Related Properties */ + signalType: undefined, + signalData: undefined, /* Maps */ replies: undefined, reactions: undefined, @@ -34,33 +37,16 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModule finalize: finalize } - const POST_TYPES = { - NEW_POST: 0, - REPLY_TO_POST: 1, - REPOST_: 2, - QUOTE_REPOST_: 3 - } - - const REACTION_TYPES = { - REACTION_LIKE: 0, - REACTION_LOVE: 1, - REACTION_HAHA: 2, - REACTION_WOW: 3, - REACTION_SAD: 4, - REACTION_ANGRY: 5, - REACTION_CARE: 6 - } - thisObject.replies = new Map() thisObject.reactions = new Map() - thisObject.reactions.set(REACTION_TYPES.REACTION_LIKE, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_LOVE, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_HAHA, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_WOW, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_SAD, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_ANGRY, 0) - thisObject.reactions.set(REACTION_TYPES.REACTION_CARE, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_LIKE, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_LOVE, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_HAHA, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_WOW, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_SAD, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_ANGRY, 0) + thisObject.reactions.set(SA.projects.socialTrading.globals.reactionTypes.REACTION_CARE, 0) return thisObject @@ -77,7 +63,9 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModule emitterPostHash, targetPostHash, postType, - timestamp + timestamp, + signalType, + signalData ) { thisObject.emitterUserProfileId = emitterUserProfileId @@ -88,13 +76,15 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModule thisObject.targetPostHash = targetPostHash thisObject.postType = postType thisObject.timestamp = timestamp + thisObject.signalType = signalType + thisObject.signalData = signalData /* Let's find the Target Post */ if ( - thisObject.postType === POST_TYPES.REPLY_TO_POST || - thisObject.postType === POST_TYPES.REPOST_ || - thisObject.postType === POST_TYPES.QUOTE_REPOST_ + thisObject.postType === SA.projects.socialTrading.globals.postTypes.REPLY_TO_POST || + thisObject.postType === SA.projects.socialTrading.globals.postTypes.REPOST || + thisObject.postType === SA.projects.socialTrading.globals.postTypes.QUOTE_REPOST ) { /* Validate Target User Profile. @@ -113,7 +103,7 @@ exports.newSocialTradingModulesSocialGraphPost = function newSocialTradingModule Let's add this post to the replies of the Target Post */ if ( - thisObject.postType === POST_TYPES.REPLY_TO_POST + thisObject.postType === SA.projects.socialTrading.globals.postTypes.REPLY_TO_POST ) { targetPost.replies.set(thisObject.targetPostHash, thisObject.targetPostHash) }