@shardus/monitor-client
Advanced tools
Comparing version 2.2.2 to 2.3.0
{ | ||
"editor.formatOnSave": true | ||
} | ||
"editor.formatOnSave": true | ||
} |
{ | ||
"name": "@shardus/monitor-client", | ||
"version": "2.2.2", | ||
"version": "2.3.0", | ||
"description": "", | ||
@@ -21,4 +21,5 @@ "main": "entry.js", | ||
"devDependencies": { | ||
"np": "^7.6.1" | ||
"np": "^7.6.1", | ||
"prettier": "^2.7.1" | ||
} | ||
} | ||
} |
@@ -1,3 +0,2 @@ | ||
; (function main() { | ||
const url = new URL(window.location.href) | ||
;(function main() { | ||
const monitorServerUrl = window.origin + '/api' | ||
@@ -12,3 +11,4 @@ console.log('Monitor server', monitorServerUrl) | ||
G.Y = 0 | ||
G.nodeRadius = 200 | ||
G.MAX_EDGES_FOR_NODE = 10 | ||
G.REFRESH_TIME = 10000 | ||
G.maxId = parseInt('ffff', 16) | ||
@@ -40,2 +40,3 @@ G.lastUpdatedTimestamp = 0 | ||
rejected: 0, | ||
rejectedTps: 0, | ||
netLoad: 0, | ||
@@ -48,2 +49,3 @@ load: 0, | ||
shouldShowMaxLoad: false, | ||
animateTransactions: true, | ||
} | ||
@@ -117,2 +119,20 @@ }, | ||
}, | ||
getNewVisEdge(node1, node2) { | ||
return { | ||
id: this.getVisEdgeId(node1.id, node2.id), | ||
from: node1.id, | ||
to: node2.id, | ||
physics: false, | ||
hidden: true, | ||
} | ||
}, | ||
getTruncatedNodeId(nodeId) { | ||
const hashLength = 10 | ||
return nodeId.substring(0, hashLength) | ||
}, | ||
getVisEdgeId(node1Id, node2Id) { | ||
// Store just part of the hashes to make these more readable | ||
return `${this.getTruncatedNodeId(node1Id)}->${this.getTruncatedNodeId(node2Id)}` | ||
}, | ||
getNewArchiverVisNodes(archivers) { | ||
@@ -153,3 +173,3 @@ let visNodes = archivers.map((archiver, index) => { | ||
}, | ||
async fetchChanges(timestamp) { | ||
async fetchChanges() { | ||
let res = await requestWithToken( | ||
@@ -166,3 +186,3 @@ `${monitorServerUrl}/report?timestamp=${G.lastUpdatedTimestamp}` | ||
} | ||
G.data.add(newVisNodes) | ||
G.visNodes.add(newVisNodes) | ||
}, | ||
@@ -179,2 +199,3 @@ updateNetworkStatus(report) { | ||
this.networkStatus.rejected = report.totalRejected | ||
this.networkStatus.rejectedTps = report.rejectedTps | ||
this.networkStatus.active = Object.keys(G.nodes.active).length | ||
@@ -216,3 +237,3 @@ this.networkStatus.syncing = Object.keys(G.nodes.syncing).length | ||
console.log('removed crashed ids', removedNodeIds) | ||
G.data.remove(removedNodeIds) | ||
G.visNodes.remove(removedNodeIds) | ||
} catch (e) { | ||
@@ -239,3 +260,3 @@ console.log('Error while trying to remove crashed nodes', e) | ||
console.log('removed ids', removedNodeIds) | ||
G.data.remove(removedNodeIds) | ||
G.visNodes.remove(removedNodeIds) | ||
} catch (e) { | ||
@@ -273,3 +294,4 @@ console.log('Error while trying to remove nodes', e) | ||
console.log( | ||
`Total of ${Object.keys(changes.nodes.active).length}/${Object.keys(G.nodes.active).length | ||
`Total of ${Object.keys(changes.nodes.active).length}/${ | ||
Object.keys(G.nodes.active).length | ||
} nodes updated.` | ||
@@ -285,6 +307,5 @@ ) | ||
let node = changes.nodes.active[nodeId] | ||
if (!G.nodes.active[nodeId]) { | ||
// console.log('New active node', node) | ||
G.nodes.active[nodeId] = node | ||
if (!G.nodes.syncing[nodeId]) newNodesMap[nodeId] = node | ||
G.nodes.active[nodeId] = node | ||
if (!G.nodes.active[nodeId] && !G.nodes.syncing[nodeId]) { | ||
newNodesMap[nodeId] = node | ||
continue | ||
@@ -304,3 +325,3 @@ } | ||
delete G.nodes.syncing[crashedSyncingNode.nodeId] | ||
G.data.remove(crashedSyncingNode.nodeId) | ||
G.visNodes.remove(crashedSyncingNode.nodeId) | ||
} | ||
@@ -329,3 +350,3 @@ if (!G.nodes.syncing[nodeId]) { | ||
updatedNodes = Object.values(updatedNodesMap) | ||
G.data.update(updatedNodes) | ||
G.visNodes.update(updatedNodes) | ||
@@ -373,3 +394,2 @@ // delete removed nodes | ||
nodes: { | ||
// size: 2, | ||
size: nodeSize, | ||
@@ -394,3 +414,3 @@ }, | ||
} | ||
G.data.update(updatedNodes) | ||
G.visNodes.update(updatedNodes) | ||
} catch (e) { | ||
@@ -405,11 +425,2 @@ console.log('Error while trying to update nodes.', e) | ||
}, | ||
// mode(list) { | ||
// const arr = [...list] | ||
// return arr | ||
// .sort( | ||
// (a, b) => | ||
// arr.filter((v) => v === a).length - arr.filter((v) => v === b).length | ||
// ) | ||
// .pop() | ||
// }, | ||
mode(arr) { | ||
@@ -487,6 +498,131 @@ return arr.reduce( | ||
}, | ||
drawCanvasNode({ ctx, x, y, width, height, style, isEoa, indicator }) { | ||
const drawInternalNode = () => { | ||
ctx.fillStyle = style.color | ||
ctx.strokeStyle = style.borderColor | ||
ctx.lineWidth = style.borderWidth | ||
ctx.beginPath() | ||
ctx.arc(x, y, width / 2, 0, 2 * Math.PI) | ||
ctx.stroke() | ||
ctx.fill() | ||
} | ||
const drawIndicator = (indicator) => { | ||
if (indicator == null) return | ||
ctx.fillStyle = indicator === 'up' ? '#f1c40f' : '#3498db' | ||
ctx.strokeStyle = indicator === 'up' ? '#f1c40f' : '#3498db' | ||
ctx.lineWidth = style.borderWidth | ||
// Determines if triangle is positioned higher or lower than node | ||
const multiplier = indicator === 'up' ? -1 : 1 | ||
// Margin between the node and the triangle | ||
const margin = 5 | ||
// Draw a triangle | ||
ctx.beginPath() | ||
ctx.moveTo(x, y + multiplier * (height + margin)) | ||
ctx.lineTo(x - width / 3, y + multiplier * (height / 2 + margin)) | ||
ctx.lineTo(x + width / 3, y + multiplier * (height / 2 + margin)) | ||
ctx.stroke() | ||
ctx.fill() | ||
} | ||
if (!isEoa) { | ||
drawInternalNode() | ||
} | ||
drawIndicator(indicator) | ||
}, | ||
animateTraffic() { | ||
const memoizedEdges = {} | ||
const animateInterval = () => { | ||
const activeNodes = Object.values(G.nodes.active) | ||
const gossipDelay = 0.8 // Delay before gossip animation starts | ||
const animationDuration = G.REFRESH_TIME * 0.8 // Need some buffer | ||
// All edges leading into nodes that have traffic | ||
const edgesWithTraffic = activeNodes | ||
.filter(({ txInjected }) => txInjected > 0) | ||
.map(({ nodeId, txInjected }) => ({ | ||
edge: this.getVisEdgeId(`eoa-${nodeId}`, nodeId), | ||
numTraffic: txInjected / 2, | ||
trafficStyle: { | ||
strokeStyle: '#f837d8', | ||
fillStyle: '#a1208b', | ||
}, | ||
})) | ||
const edgesWithGossip = activeNodes | ||
.filter(({ txInjected }) => txInjected > 0) | ||
.map(({ nodeId }) => { | ||
// Using performance tools, edgesForNode is relatively expensive | ||
if (memoizedEdges[nodeId]) { | ||
return memoizedEdges[nodeId] | ||
} | ||
return this.edgesForNode(nodeId) | ||
}) | ||
.flat() | ||
.map((edge) => ({ | ||
edge: edge.id, | ||
numTraffic: 1, | ||
trafficStyle: { | ||
strokeStyle: '#f8b437', | ||
fillStyle: '#f88737', | ||
}, | ||
delay: gossipDelay * animationDuration, | ||
})) | ||
if (this.animateTransactions) { | ||
G.network.animateTraffic({ | ||
edgesTrafficList: [...edgesWithTraffic, ...edgesWithGossip], | ||
animationDuration: animationDuration, | ||
}) | ||
} | ||
} | ||
// Animate twice on two canvases so animations aren't janky | ||
animateInterval() | ||
setInterval(animateInterval, G.REFRESH_TIME) | ||
setTimeout(() => { | ||
animateInterval() | ||
setInterval(animateInterval, G.REFRESH_TIME) | ||
}, G.REFRESH_TIME * 0.5) | ||
}, | ||
edgesForNode(nodeId) { | ||
const edges = G.visEdges.get({ | ||
filter: (item) => item.from === nodeId, | ||
}) | ||
return edges | ||
}, | ||
// See ctxRenderer for more details: https://visjs.github.io/vis-network/docs/network/nodes.html# | ||
visContextRenderer({ ctx, id, x, y, style }) { | ||
const width = 6 | ||
const height = width | ||
const currentNode = G.nodes.active[id] | ||
const indicator = | ||
currentNode != null ? currentNode.lastScalingTypeRequested : undefined | ||
const isEoa = id.startsWith('eoa-') | ||
return { | ||
drawNode: () => { | ||
this.drawCanvasNode({ ctx, x, y, width, height, style, isEoa, indicator }) | ||
}, | ||
nodeDimensions: { width, height }, | ||
} | ||
}, | ||
async start() { | ||
let res = await requestWithToken(`${monitorServerUrl}/report`) | ||
let newNodesMap = {} | ||
let newNodes = [] | ||
for (let nodeId in res.data.nodes.active) { | ||
@@ -510,4 +646,53 @@ // remove if active node exists in the syncing list | ||
} | ||
newNodes = Object.values(newNodesMap) | ||
G.data = new vis.DataSet(newNodes) | ||
const newNodes = Object.values(newNodesMap) | ||
const newEdges = [] | ||
newNodes.forEach((node, index) => { | ||
// This is not how things work IRL. For the simulation, each node is only connected to the next MAX_EDGES_FOR_NODE nodes. | ||
// We cap to MAX_EDGES_FOR_NODE because connecting every node will create numNodes! edges which may be too large | ||
for ( | ||
let nextNodeIndex = index + 1; | ||
nextNodeIndex <= G.MAX_EDGES_FOR_NODE; | ||
nextNodeIndex++ | ||
) { | ||
const safeNextNodeIndex = nextNodeIndex % newNodes.length | ||
const destinationNode = newNodes[safeNextNodeIndex] | ||
if (node.id === destinationNode.id) { | ||
continue | ||
} | ||
const edge = this.getNewVisEdge(node, destinationNode) | ||
const edgeAdded = newEdges.some(({ id }) => id === edge.id) | ||
if (edgeAdded) { | ||
continue | ||
} | ||
newEdges.push(edge) | ||
} | ||
}) | ||
// Create EOA nodes that send transactions to the network. Each node has 1 EOA | ||
newNodes.forEach((node) => { | ||
// Distance from its node | ||
const distance = 2 | ||
const eoaNode = { | ||
...node, | ||
id: `eoa-${node.id}`, | ||
x: node.x * distance, | ||
y: node.y * distance, | ||
isEoa: true, | ||
} | ||
const edge = this.getNewVisEdge(eoaNode, node) | ||
newNodes.push(eoaNode) | ||
newEdges.push(edge) | ||
}) | ||
G.visNodes = new vis.DataSet(newNodes) | ||
G.visEdges = new vis.DataSet(newEdges) | ||
this.updateNetworkStatus(res.data) | ||
@@ -520,7 +705,11 @@ | ||
let data = { | ||
nodes: G.data, | ||
nodes: G.visNodes, | ||
edges: G.visEdges, | ||
} | ||
const options = { | ||
nodes: { | ||
shape: 'dot', | ||
shape: 'custom', | ||
ctxRenderer: (params) => { | ||
return this.visContextRenderer(params) | ||
}, | ||
size: this.getNodeSize(Object.keys(G.nodes.active).length), | ||
@@ -549,4 +738,6 @@ font: { | ||
}) | ||
await this.drawArchiverNetwork() | ||
setInterval(this.updateNodes, 10000) | ||
setInterval(this.updateNodes, G.REFRESH_TIME) | ||
this.animateTraffic() | ||
}, | ||
@@ -553,0 +744,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2335328
37
4768
2