Subscribe to chains by genesis hash (#395)

* Handle subscription by hash in the frontend

* Forward-ported backend changes

* Fix unit tests

* Remove unused `chains_by_label`

* fmt

* Updated but failing E2E tests

* subscribe by genesis hash in tests

* fmt

* Copy `BlockHash` instead of returning a ref

* Pin chains by genesisHash

Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
Maciej Hirsz
2021-09-02 17:54:19 +02:00
committed by GitHub
parent ec5db0fbbf
commit a4069e4b3d
16 changed files with 156 additions and 140 deletions
+1 -1
View File
@@ -228,7 +228,7 @@ export default class App extends React.Component<{}, {}> {
this.chainsCache = stable.inplace(
Array.from(this.appState.chains.values()),
(a, b) => {
const pinned = comparePinnedChains(a.label, b.label);
const pinned = comparePinnedChains(a.genesisHash, b.genesisHash);
if (pinned !== 0) {
return pinned;
+8 -8
View File
@@ -119,7 +119,7 @@ export class Connection {
// timestamp at which the last ping has been sent
private pingSent: Maybe<Types.Timestamp> = null;
// chain label to resubsribe to on reconnect
private resubscribeTo: Maybe<Types.ChainLabel> = getHashData().chain;
private resubscribeTo: Maybe<Types.GenesisHash> = getHashData().chain;
// flag whether or not FE should subscribe to consensus updates on reconnect
private resubscribeSendFinality: boolean = getHashData().tab === 'consensus';
@@ -132,7 +132,7 @@ export class Connection {
this.bindSocket();
}
public subscribe(chain: Types.ChainLabel) {
public subscribe(chain: Types.GenesisHash) {
if (
this.appState.subscribed != null &&
this.appState.subscribed !== chain
@@ -148,7 +148,7 @@ export class Connection {
this.socket.send(`subscribe:${chain}`);
}
public subscribeConsensus(chain: Types.ChainLabel) {
public subscribeConsensus(chain: Types.GenesisHash) {
if (this.appState.authorities.length <= VIS_AUTHORITIES_LIMIT) {
setHashData({ chain });
this.resubscribeSendFinality = true;
@@ -165,7 +165,7 @@ export class Connection {
});
}
public unsubscribeConsensus(chain: Types.ChainLabel) {
public unsubscribeConsensus(chain: Types.GenesisHash) {
this.resubscribeSendFinality = true;
this.socket.send(`no-more-finality:${chain}`);
}
@@ -334,13 +334,13 @@ export class Connection {
}
case ACTIONS.AddedChain: {
const [label, nodeCount] = message.payload;
const chain = chains.get(label);
const [label, genesisHash, nodeCount] = message.payload;
const chain = chains.get(genesisHash);
if (chain) {
chain.nodeCount = nodeCount;
} else {
chains.set(label, { label, nodeCount });
chains.set(genesisHash, { label, genesisHash, nodeCount });
}
this.appUpdate({ chains });
@@ -552,7 +552,7 @@ export class Connection {
}
if (topChain) {
this.subscribe(topChain.label);
this.subscribe(topChain.genesisHash);
}
}
+5 -4
View File
@@ -35,6 +35,7 @@ import {
Timestamp,
Milliseconds,
ChainLabel,
GenesisHash,
AuthoritySetInfo,
} from './types';
@@ -142,22 +143,22 @@ export namespace Variants {
export interface AddedChainMessage extends MessageBase {
action: typeof ACTIONS.AddedChain;
payload: [ChainLabel, NodeCount];
payload: [ChainLabel, GenesisHash, NodeCount];
}
export interface RemovedChainMessage extends MessageBase {
action: typeof ACTIONS.RemovedChain;
payload: ChainLabel;
payload: GenesisHash;
}
export interface SubscribedToMessage extends MessageBase {
action: typeof ACTIONS.SubscribedTo;
payload: ChainLabel;
payload: GenesisHash;
}
export interface UnsubscribedFromMessage extends MessageBase {
action: typeof ACTIONS.UnsubscribedFrom;
payload: ChainLabel;
payload: GenesisHash;
}
export interface PongMessage extends MessageBase {
+1 -1
View File
@@ -25,4 +25,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 = 31 as Types.FeedVersion;
export const VERSION: Types.FeedVersion = 32 as Types.FeedVersion;
+1
View File
@@ -19,6 +19,7 @@ import { Id } from './id';
export type FeedVersion = Opaque<number, 'FeedVersion'>;
export type ChainLabel = Opaque<string, 'ChainLabel'>;
export type GenesisHash = Opaque<string, 'GenesisHash'>;
export type FeedId = Id<'Feed'>;
export type NodeId = Id<'Node'>;
export type NodeName = Opaque<string, 'NodeName'>;
+5 -5
View File
@@ -24,7 +24,7 @@ import './AllChains.css';
export namespace AllChains {
export interface Props {
chains: ChainData[];
subscribed: Maybe<Types.ChainLabel>;
subscribed: Maybe<Types.GenesisHash>;
connection: Promise<Connection>;
}
}
@@ -45,10 +45,10 @@ export class AllChains extends React.Component<AllChains.Props, {}> {
}
private renderChain(chain: ChainData): React.ReactNode {
const { label, nodeCount } = chain;
const { label, genesisHash, nodeCount } = chain;
const className =
label === this.props.subscribed
genesisHash === this.props.subscribed
? 'AllChains-chain AllChains-chain-selected'
: 'AllChains-chain';
@@ -56,7 +56,7 @@ export class AllChains extends React.Component<AllChains.Props, {}> {
<a
key={label}
className={className}
onClick={this.subscribe.bind(this, label)}
onClick={this.subscribe.bind(this, genesisHash)}
>
{label}{' '}
<span className="AllChains-node-count" title="Node Count">
@@ -66,7 +66,7 @@ export class AllChains extends React.Component<AllChains.Props, {}> {
);
}
private async subscribe(chain: Types.ChainLabel) {
private async subscribe(chain: Types.GenesisHash) {
const connection = await this.props.connection;
connection.subscribe(chain);
+6 -6
View File
@@ -27,7 +27,7 @@ import './Chains.css';
export namespace Chains {
export interface Props {
chains: ChainData[];
subscribed: Maybe<Types.ChainLabel>;
subscribed: Maybe<Types.GenesisHash>;
connection: Promise<Connection>;
}
}
@@ -39,7 +39,7 @@ const RENDER_THROTTLE = 1000;
export class Chains extends React.Component<Chains.Props, {}> {
private lastRender = performance.now();
private clicked: Maybe<Types.ChainLabel>;
private clicked: Maybe<Types.GenesisHash>;
public shouldComponentUpdate(nextProps: Chains.Props) {
if (nextProps.subscribed !== this.clicked) {
@@ -83,11 +83,11 @@ export class Chains extends React.Component<Chains.Props, {}> {
}
private renderChain(chain: ChainData): React.ReactNode {
const { label, nodeCount } = chain;
const { label, genesisHash, nodeCount } = chain;
let className = 'Chains-chain';
if (label === this.props.subscribed) {
if (genesisHash === this.props.subscribed) {
className += ' Chains-chain-selected';
}
@@ -95,7 +95,7 @@ export class Chains extends React.Component<Chains.Props, {}> {
<a
key={label}
className={className}
onClick={this.subscribe.bind(this, label)}
onClick={this.subscribe.bind(this, genesisHash)}
>
{label}
<span className="Chains-node-count" title="Node Count">
@@ -105,7 +105,7 @@ export class Chains extends React.Component<Chains.Props, {}> {
);
}
private async subscribe(chain: Types.ChainLabel) {
private async subscribe(chain: Types.GenesisHash) {
if (chain === this.clicked) {
return;
}
@@ -427,12 +427,12 @@ export class Consensus extends React.Component<Consensus.Props, {}> {
);
}
private async subscribeConsensus(chain: Types.ChainLabel) {
private async subscribeConsensus(chain: Types.GenesisHash) {
const connection = await this.props.connection;
connection.subscribeConsensus(chain);
}
private async unsubscribeConsensus(chain: Types.ChainLabel) {
private async unsubscribeConsensus(chain: Types.GenesisHash) {
const connection = await this.props.connection;
connection.unsubscribeConsensus(chain);
}
+7 -4
View File
@@ -19,8 +19,10 @@ import { Types, Maybe, SortedCollection } from './common';
import { Column } from './components/List';
export const PINNED_CHAINS = {
Kusama: 2,
Polkadot: 1,
// Kusama
'0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe': 2,
// Polkadot
'0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3': 1,
};
export function comparePinnedChains(a: string, b: string) {
@@ -275,8 +277,8 @@ export interface State {
blockTimestamp: Types.Timestamp;
blockAverage: Maybe<Types.Milliseconds>;
timeDiff: Types.Milliseconds;
subscribed: Maybe<Types.ChainLabel>;
chains: Map<Types.ChainLabel, ChainData>;
subscribed: Maybe<Types.GenesisHash>;
chains: Map<Types.GenesisHash, ChainData>;
nodes: SortedCollection<Node>;
settings: Readonly<State.Settings>;
pins: Readonly<Set<Types.NodeName>>;
@@ -290,5 +292,6 @@ export type Update = <K extends keyof State>(
export interface ChainData {
label: Types.ChainLabel;
genesisHash: Types.GenesisHash;
nodeCount: Types.NodeCount;
}
+2 -2
View File
@@ -88,7 +88,7 @@ export function secondsWithPrecision(num: number): string {
export interface HashData {
tab?: string;
chain?: Types.ChainLabel;
chain?: Types.GenesisHash;
}
export function getHashData(): HashData {
@@ -99,7 +99,7 @@ export function getHashData(): HashData {
}
const [tab, rawChain] = hash.substr(1).split('/');
const chain = decodeURIComponent(rawChain) as Types.ChainLabel;
const chain = decodeURIComponent(rawChain) as Types.GenesisHash;
return { tab, chain };
}