From 983919fd87c0cecf8fd1bb58a39054239ef2aeef Mon Sep 17 00:00:00 2001 From: Maciej Hirsz <1096222+maciejhirsz@users.noreply.github.com> Date: Tue, 2 Jul 2019 14:52:06 +0200 Subject: [PATCH] feat: Instead of delisting, push stale nodes to bottom (#161) --- packages/backend/src/Chain.ts | 15 ++----- packages/backend/src/Feed.ts | 7 ++++ packages/common/src/feed.ts | 39 +++++++++++-------- packages/common/src/index.ts | 2 +- packages/frontend/src/Connection.ts | 15 ++++++- packages/frontend/src/components/List/Row.css | 4 ++ packages/frontend/src/components/List/Row.tsx | 4 ++ packages/frontend/src/state.ts | 16 +++++++- 8 files changed, 70 insertions(+), 32 deletions(-) diff --git a/packages/backend/src/Chain.ts b/packages/backend/src/Chain.ts index bd03763..147fe03 100644 --- a/packages/backend/src/Chain.ts +++ b/packages/backend/src/Chain.ts @@ -29,13 +29,12 @@ export default class Chain { } public get nodeCount(): Types.NodeCount { - return this.count as Types.NodeCount; + return this.nodes.size as Types.NodeCount; } public addNode(node: Node) { console.log(`[${this.label}] new node: ${node.name}`); - this.count += 1; this.nodes.add(node); this.feeds.broadcast(Feed.addedNode(node)); @@ -83,12 +82,7 @@ export default class Chain { node.events.removeAllListeners(); this.nodes.delete(node); - - if (!node.isStale) { - this.count -= 1; - this.feeds.broadcast(Feed.removedNode(node)); - } - + this.feeds.broadcast(Feed.removedNode(node)); this.events.emit('disconnect', this.nodeCount); if (this.height === node.best.number) { @@ -97,8 +91,7 @@ export default class Chain { } public staleNode(node: Node) { - this.count -= 1; - this.feeds.broadcast(Feed.removedNode(node)); + this.feeds.broadcast(Feed.staleNode(node)); if (this.height === node.best.number) { this.downgradeBlock(); @@ -174,8 +167,6 @@ export default class Chain { if (node.isStale) { node.isStale = false; - this.feeds.broadcast(Feed.addedNode(node)); - this.count += 1; } else { this.feeds.broadcast(Feed.imported(node)); } diff --git a/packages/backend/src/Feed.ts b/packages/backend/src/Feed.ts index 2b9a39e..251d472 100644 --- a/packages/backend/src/Feed.ts +++ b/packages/backend/src/Feed.ts @@ -65,6 +65,13 @@ export default class Feed { }; } + public static staleNode(node: Node): FeedMessage.Message { + return { + action: Actions.StaleNode, + payload: node.id + } + } + public static locatedNode(node: Node, location: Location): FeedMessage.Message { return { action: Actions.LocatedNode, diff --git a/packages/common/src/feed.ts b/packages/common/src/feed.ts index 285fd9c..7009308 100644 --- a/packages/common/src/feed.ts +++ b/packages/common/src/feed.ts @@ -22,26 +22,27 @@ import { } from './types'; export const Actions = { - FeedVersion : 0x00 as 0x00, - BestBlock : 0x01 as 0x01, - BestFinalized : 0x02 as 0x02, - AddedNode : 0x03 as 0x03, - RemovedNode : 0x04 as 0x04, - LocatedNode : 0x05 as 0x05, - ImportedBlock : 0x06 as 0x06, - FinalizedBlock : 0x07 as 0x07, - NodeStats : 0x08 as 0x08, - NodeHardware : 0x09 as 0x09, - TimeSync : 0x0A as 0x0A, - AddedChain : 0x0B as 0x0B, - RemovedChain : 0x0C as 0x0C, - SubscribedTo : 0x0D as 0x0D, - UnsubscribedFrom : 0x0E as 0x0E, - Pong : 0x0F as 0x0F, + FeedVersion : 0x00 as 0x00, + BestBlock : 0x01 as 0x01, + BestFinalized : 0x02 as 0x02, + AddedNode : 0x03 as 0x03, + RemovedNode : 0x04 as 0x04, + LocatedNode : 0x05 as 0x05, + ImportedBlock : 0x06 as 0x06, + FinalizedBlock : 0x07 as 0x07, + NodeStats : 0x08 as 0x08, + NodeHardware : 0x09 as 0x09, + TimeSync : 0x0A as 0x0A, + AddedChain : 0x0B as 0x0B, + RemovedChain : 0x0C as 0x0C, + SubscribedTo : 0x0D as 0x0D, + UnsubscribedFrom : 0x0E as 0x0E, + Pong : 0x0F as 0x0F, AfgFinalized : 0x10 as 0x10, AfgReceivedPrevote : 0x11 as 0x11, AfgReceivedPrecommit : 0x12 as 0x12, AfgAuthoritySet : 0x13 as 0x13, + StaleNode : 0x14 as 0x14, }; export type Action = typeof Actions[keyof typeof Actions]; @@ -151,6 +152,11 @@ export namespace Variants { action: typeof Actions.AfgReceivedPrevote; payload: [Address, BlockNumber, BlockHash, Address]; } + + export interface StaleNodeMessage extends MessageBase { + action: typeof Actions.StaleNode; + payload: NodeId; + } } export type Message = @@ -173,6 +179,7 @@ export type Message = | Variants.AfgReceivedPrevote | Variants.AfgReceivedPrecommit | Variants.AfgAuthoritySet + | Variants.StaleNodeMessage | Variants.PongMessage; /** diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 9556cda..7ffa37d 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -9,4 +9,4 @@ import * as FeedMessage from './feed'; export { Types, FeedMessage }; // Increment this if breaking changes were made to types in `feed.ts` -export const VERSION: Types.FeedVersion = 22 as Types.FeedVersion; +export const VERSION: Types.FeedVersion = 25 as Types.FeedVersion; diff --git a/packages/frontend/src/Connection.ts b/packages/frontend/src/Connection.ts index e23199f..13e616d 100644 --- a/packages/frontend/src/Connection.ts +++ b/packages/frontend/src/Connection.ts @@ -15,6 +15,8 @@ export class Connection { return new Connection(await Connection.socket(), update, pins); } + private static readonly utf8decoder = new TextDecoder('utf-8'); + private static readonly address = window.location.protocol === 'https:' ? `wss://${window.location.hostname}/feed/` : `ws://${window.location.hostname}:8080`; @@ -55,6 +57,7 @@ export class Connection { const socket = new WebSocket(Connection.address); + socket.binaryType = "arraybuffer"; socket.addEventListener('open', onSuccess); socket.addEventListener('error', onFailure); socket.addEventListener('close', onFailure); @@ -173,6 +176,14 @@ export class Connection { break; } + case Actions.StaleNode: { + const id = message.payload; + + nodes.mutAndSort(id, (node) => node.setStale(true)); + + break; + } + case Actions.LocatedNode: { const [id, lat, lon, city] = message.payload; @@ -376,7 +387,9 @@ export class Connection { } private handleFeedData = (event: MessageEvent) => { - const data = event.data as FeedMessage.Data; + const data = typeof event.data === 'string' + ? event.data as any as FeedMessage.Data + : Connection.utf8decoder.decode(event.data) as any as FeedMessage.Data; this.handleMessages(FeedMessage.deserialize(data)); } diff --git a/packages/frontend/src/components/List/Row.css b/packages/frontend/src/components/List/Row.css index 4276873..fee1a04 100644 --- a/packages/frontend/src/components/List/Row.css +++ b/packages/frontend/src/components/List/Row.css @@ -60,6 +60,10 @@ color: #E6007A; } +.Row-stale { + font-style: italic; +} + .Row:hover { background-color: #1E1E1E; } diff --git a/packages/frontend/src/components/List/Row.tsx b/packages/frontend/src/components/List/Row.tsx index dab7ec3..6cfe576 100644 --- a/packages/frontend/src/components/List/Row.tsx +++ b/packages/frontend/src/components/List/Row.tsx @@ -374,6 +374,10 @@ export class Row extends React.Component { className += ' Row-pinned'; } + if (node.stale) { + className += ' Row-stale'; + } + return ( { diff --git a/packages/frontend/src/state.ts b/packages/frontend/src/state.ts index 5446c60..ed5dc20 100644 --- a/packages/frontend/src/state.ts +++ b/packages/frontend/src/state.ts @@ -2,7 +2,7 @@ import { Types, Maybe, SortedCollection } from '@dotstats/common'; export class Node { public static compare(a: Node, b: Node): number { - if (a.pinned === b.pinned) { + if (a.pinned === b.pinned && a.stale === b.stale) { if (a.height === b.height) { const aPropagation = a.propagationTime == null ? Infinity : a.propagationTime as number; const bPropagation = b.propagationTime == null ? Infinity : b.propagationTime as number; @@ -11,7 +11,10 @@ export class Node { return aPropagation - bPropagation; } } else { - return +b.pinned - +a.pinned; + const bSort = (b.pinned ? -2 : 0) + +b.stale; + const aSort = (a.pinned ? -2 : 0) + +a.stale; + + return aSort - bSort; } // Descending sort by block number @@ -26,6 +29,7 @@ export class Node { public readonly validator: Maybe; public readonly networkId: Maybe; + public stale: boolean; public pinned: boolean; public peers: Types.PeerCount; public txs: Types.TransactionCount; @@ -110,6 +114,7 @@ export class Node { this.blockTime = blockTime; this.blockTimestamp = blockTimestamp; this.propagationTime = propagationTime; + this.stale = false; this.trigger(); } @@ -143,6 +148,13 @@ export class Node { } } + public setStale(stale: boolean) { + if (this.stale !== stale) { + this.stale = stale; + this.trigger(); + } + } + public subscribe(handler: (node: Node) => void) { this.subscriptions.add(handler); }