Display state of Grandpa (#134)

* Make it clear that settings apply only to list view

Since the consensus view will be added users could
mistake the settings as being applied there as well.

* Add Jdenticon

* Add Grandpa consensus visualisation

* Remove fade-in animation

* Update packages and yarn.lock

* Broadcast only delta of what changed

* Minor code improvements

* Use NodeId instead of Address in first dimension

By using the NodeId instead of the Address in the first
dimension of the consensus matrice we save quite some
space in the payload which is sent to the browser.

The commit also contains some minor refactoring.

* Refactoring and improving naming

* Display boxes only after size has been detected

This look a bit nicer, otherwise the UI will still adapt
the box sizes once everything has already been loaded up.

* Fix cache

* Send consensus info on first subscribe

So that frontend can immediately display the current state
and doesn't have to aggregate first.

* Increase cache size

* Send deltas only if block in cache

Otherwise the UI will update old blocks which
are still visible to an empty shell.

* Adjust cache size

* Make cache sizes dependent

* Ensure authority caches are aligned

If only one authority has already submitted consensus info
for a new block then the cache of that one is offset by one
from all other authorities.

* Handle restarts on authority set changes properly

* Fix backfill mechanism

* Extract function

* Display only blocks since last authority set change

* Handle authority set sent on connect

When nodes lose their connection to telemetry
or connect on first time they sent their current
authority set for the UI to have something to
display.

These sets don't contain an explicit block number,
because the set didn't change -- it just got resent.
In this case the set is `undefined`.

* Introduce Authority type

This is necessary to cover the case where one
node connects, submits its authority set containing
another node which has not yet connected to Telemetry.

In this case we still want to create a shell object
and fill it with the address.

* Handle corner case

In the case of only one block having been produced, two authorities,
and only one authority connected, the UI did not show up.

* Display placeholder if name not yet available

* Replace with camelCase

* Replace with correct types

* Replace grandpa icon

* Change consensus icon to cube (finalized block icon)

* Upgrade dependencies

* Implement thin backend instead of thick

* Cleanup and minor improvements

* Minor refactoring

* Extract common code into function

* Switch module to class

* Remove unused code

* Clean markup

* Remove unused code

* Revert "Upgrade dependencies"

This reverts commit bf4d9ea48b3417860ccf40f0c5122027ffc59689.

* Update polkadot-identicon in frontend

Change version number to `^1.1.45` and run
`npm update polkadot-identicon`.

* Run yarn install

* Update react-measure to 2.3.0

Changed version number, ran

	cd packages/frontend/ && npm update react-reasure &&
	cd ../../ && yarn install

* Improve typing by introducing partial type

* Reduce indexing operations

* Shorten function

* Shorten function

* Introduce initialiseConsensusViewByRef

* Remove dead conditional branch

* Return consensusView ref from initialiseConsensusView

*  Handle consensusView ref returned from initialiseConsensusView
This commit is contained in:
Michael Müller
2019-05-20 13:20:41 +02:00
committed by Maciej Hirsz
parent 4a48a6eecf
commit 5d82253257
28 changed files with 2324 additions and 52 deletions
+18
View File
@@ -46,6 +46,24 @@ export default class Aggregator {
feed.sendMessage(Feed.unsubscribedFrom(label));
}
});
feed.events.on('subscribe-consensus-info', (label: Types.ChainLabel) => {
const chain = this.chains.get(label);
if (chain) {
feed.sendMessage(Feed.subscribedTo(label));
chain.addFeed(feed);
}
});
feed.events.on('unsubscribe-consensus-info', (label: Types.ChainLabel) => {
const chain = this.chains.get(label);
if (chain) {
chain.removeFeed(feed);
feed.sendMessage(Feed.unsubscribedFrom(label));
}
});
}
public getExistingChain(label: Types.ChainLabel) : Maybe<Chain> {
+34 -1
View File
@@ -3,7 +3,7 @@ import Node from './Node';
import Feed from './Feed';
import FeedSet from './FeedSet';
import Block from './Block';
import { Maybe, Types, FeedMessage, NumStats } from '@dotstats/common';
import { Maybe, Types, NumStats } from '@dotstats/common';
const BLOCK_TIME_HISTORY = 10;
@@ -21,6 +21,8 @@ export default class Chain {
private blockTimes = new NumStats<Types.Milliseconds>(BLOCK_TIME_HISTORY);
private averageBlockTime: Maybe<Types.Milliseconds> = null;
public lastBroadcastedAuthoritySetInfo: Maybe<Types.AuthoritySetInfo> = null;
constructor(label: Types.ChainLabel) {
this.label = label;
}
@@ -39,6 +41,33 @@ export default class Chain {
node.events.on('block', () => this.updateBlock(node));
node.events.on('finalized', () => this.updateFinalized(node));
node.events.on('afg-finalized', (finalizedNumber, finalizedHash) => this.feeds.each(
f => f.sendConsensusMessage(Feed.afgFinalized(node, finalizedNumber, finalizedHash))
));
node.events.on('afg-received-prevote', (finalizedNumber, finalizedHash, voter) => this.feeds.each(
f => f.sendConsensusMessage(Feed.afgReceivedPrevote(node, finalizedNumber, finalizedHash, voter))
));
node.events.on('afg-received-precommit', (finalizedNumber, finalizedHash, voter) => this.feeds.each(
f => f.sendConsensusMessage(Feed.afgReceivedPrecommit(node, finalizedNumber, finalizedHash, voter))
));
node.events.on('authority-set-changed', (authorities, authoritySetId, blockNumber, blockHash) => {
let newSet;
if (this.lastBroadcastedAuthoritySetInfo == null) {
newSet = true;
} else {
const [lastBroadcastedAuthoritySetId] = this.lastBroadcastedAuthoritySetInfo;
newSet = authoritySetId !== lastBroadcastedAuthoritySetId;
}
if (node.isAuthority() && newSet) {
const addr = node.address != null ? node.address : "" as Types.Address;
const set = [authoritySetId, authorities, addr, blockNumber, blockHash] as Types.AuthoritySetInfo;
this.feeds.broadcast(Feed.afgAuthoritySet(set));
this.lastBroadcastedAuthoritySetInfo = set;
}
});
node.events.on('stats', () => this.feeds.broadcast(Feed.stats(node)));
node.events.on('hardware', () => this.feeds.broadcast(Feed.hardware(node)));
node.events.on('location', (location) => this.feeds.broadcast(Feed.locatedNode(node, location)));
@@ -70,6 +99,10 @@ export default class Chain {
feed.sendMessage(Feed.bestBlock(this.height, this.blockTimestamp, this.averageBlockTime));
feed.sendMessage(Feed.bestFinalizedBlock(this.finalized));
if (this.lastBroadcastedAuthoritySetInfo != null) {
feed.sendMessage(Feed.afgAuthoritySet(this.lastBroadcastedAuthoritySetInfo));
}
for (const node of this.nodes.values()) {
feed.sendMessage(Feed.addedNode(node));
feed.sendMessage(Feed.finalized(node));
+60
View File
@@ -18,6 +18,7 @@ export default class Feed {
private socket: WebSocket;
private messages: Array<FeedMessage.Message> = [];
private waitingForPong = false;
private sendFinality = false;
constructor(socket: WebSocket) {
this.id = nextId();
@@ -92,6 +93,49 @@ export default class Feed {
};
}
public static afgFinalized(node: Node, finalizedNumber: Types.BlockNumber, finalizedHash: Types.BlockHash): FeedMessage.Message {
const addr = node.address != null ? node.address : "" as Types.Address;
return {
action: Actions.AfgFinalized,
payload: [addr, finalizedNumber, finalizedHash]
};
}
public static afgReceivedPrevote(
node: Node,
targetNumber: Types.BlockNumber,
targetHash: Types.BlockHash,
voter: Types.Address
): FeedMessage.Message {
const addr = node.address != null ? node.address : "" as Types.Address;
return {
action: Actions.AfgReceivedPrevote,
payload: [addr, targetNumber, targetHash, voter]
};
}
public static afgReceivedPrecommit(
node: Node,
targetNumber: Types.BlockNumber,
targetHash: Types.BlockHash,
voter: Types.Address
): FeedMessage.Message {
const addr = node.address != null ? node.address : "" as Types.Address;
return {
action: Actions.AfgReceivedPrecommit,
payload: [addr, targetNumber, targetHash, voter]
};
}
public static afgAuthoritySet(
authoritySetInfo: Types.AuthoritySetInfo,
): FeedMessage.Message {
return {
action: Actions.AfgAuthoritySet,
payload: authoritySetInfo,
};
}
public static hardware(node: Node): FeedMessage.Message {
return {
action: Actions.NodeHardware,
@@ -155,6 +199,14 @@ export default class Feed {
}
}
public sendConsensusMessage(message: FeedMessage.Message) {
if (!this.sendFinality) {
return;
}
this.sendMessage(message);
}
public ping() {
if (this.waitingForPong) {
this.disconnect();
@@ -188,6 +240,14 @@ export default class Feed {
this.events.emit('subscribe', payload as Types.ChainLabel);
break;
case 'send-finality':
this.sendFinality = true;
break;
case 'no-more-finality':
this.sendFinality = false;
break;
case 'ping':
this.sendMessage(Feed.pong(payload));
break;
+81 -2
View File
@@ -2,7 +2,18 @@ import * as WebSocket from 'ws';
import * as EventEmitter from 'events';
import { noop, timestamp, idGenerator, Maybe, Types, NumStats } from '@dotstats/common';
import { parseMessage, getBestBlock, Message, BestBlock, SystemInterval } from './message';
import { BlockHash, BlockNumber, ConsensusView } from "@dotstats/common/build/types";
import {
parseMessage,
getBestBlock,
Message,
BestBlock,
SystemInterval,
AfgFinalized,
AfgReceivedPrecommit,
AfgReceivedPrevote,
AfgAuthoritySet,
} from './message';
import { locate, Location } from './location';
import MeanList from './MeanList';
import Block from './Block';
@@ -57,6 +68,9 @@ export default class Node {
private pingStart = 0 as Types.Timestamp;
private throttle = false;
private authorities: Types.Authorities = [] as Types.Authorities;
private authoritySetId: Types.AuthoritySetId = 0 as Types.AuthoritySetId;
constructor(
ip: string,
socket: WebSocket,
@@ -182,8 +196,9 @@ export default class Node {
public nodeDetails(): Types.NodeDetails {
const authority = this.authority ? this.address : null;
const addr = this.address ? this.address : '' as Types.Address;
return [this.name, this.implementation, this.version, authority, this.networkId];
return [this.name, addr, this.implementation, this.version, authority, this.networkId];
}
public nodeStats(): Types.NodeStats {
@@ -236,6 +251,19 @@ export default class Node {
if (message.msg === 'system.interval') {
this.onSystemInterval(message);
}
if (message.msg === 'afg.finalized') {
this.onAfgFinalized(message);
}
if (message.msg === 'afg.received_precommit') {
this.onAfgReceivedPrecommit(message);
}
if (message.msg === 'afg.received_prevote') {
this.onAfgReceivedPrevote(message);
}
if (message.msg === 'afg.authority_set') {
this.onAfgAuthoritySet(message);
}
}
private onSystemInterval(message: SystemInterval) {
@@ -283,6 +311,57 @@ export default class Node {
}
}
public isAuthority(): boolean {
return this.authority;
}
private onAfgReceivedPrecommit(message: AfgReceivedPrecommit) {
const {
target_number: targetNumber,
target_hash: targetHash,
} = message;
const voter = this.extractVoter(message.voter);
this.events.emit('afg-received-precommit', targetNumber, targetHash, voter);
}
private onAfgReceivedPrevote(message: AfgReceivedPrevote) {
const {
target_number: targetNumber,
target_hash: targetHash,
} = message;
const voter = this.extractVoter(message.voter);
this.events.emit('afg-received-prevote', targetNumber, targetHash, voter);
}
private onAfgAuthoritySet(message: AfgAuthoritySet) {
const {
authority_set_id: authoritySetId,
hash,
number,
} = message;
// we manually parse the authorities message, because the array was formatted as a
// string by substrate before sending it.
const authorities = JSON.parse(String(message.authorities)) as Types.Authorities;
if (JSON.stringify(this.authorities) !== String(message.authorities) ||
this.authoritySetId !== authoritySetId) {
this.events.emit('authority-set-changed', authorities, authoritySetId, number, hash);
}
}
private onAfgFinalized(message: AfgFinalized) {
const {
finalized_number: finalizedNumber,
finalized_hash: finalizedHash,
} = message;
this.events.emit('afg-finalized', finalizedNumber, finalizedHash);
}
private extractVoter(message_voter: String): Types.Address {
return String(message_voter.replace(/"/g, '')) as Types.Address;
}
private updateLatency(now: Types.Timestamp) {
// if (this.pingStart) {
// console.error(`${this.name} timed out on ping message.`);
+40
View File
@@ -39,6 +39,41 @@ export interface BestBlock {
ts: Date;
}
export interface AfgFinalized {
ts: Date;
finalized_number: Types.BlockNumber;
finalized_hash: Types.BlockHash;
msg: 'afg.finalized';
}
export interface AfgReceived {
ts: Date;
target_number: Maybe<Types.BlockNumber>;
target_hash: Maybe<Types.BlockHash>;
voter: Types.Address;
}
export interface AfgReceivedPrecommit extends AfgReceived {
msg: 'afg.received_precommit';
}
export interface AfgReceivedPrevote extends AfgReceived {
msg: 'afg.received_prevote';
}
export interface AfgReceivedCommit extends AfgReceived {
msg: 'afg.received_commit';
}
export interface AfgAuthoritySet {
msg: 'afg.authority_set';
ts: Date;
authorities: Types.Authorities;
authority_set_id: Types.AuthoritySetId;
number: Types.BlockNumber;
hash: Types.BlockHash;
}
export interface SystemConnected {
msg: 'system.connected';
name: Types.NodeName;
@@ -79,6 +114,11 @@ export type Message = MessageBase & (
| SystemInterval
| NodeStart
| BlockImport
| AfgFinalized
| AfgReceivedPrecommit
| AfgReceivedPrevote
| AfgReceivedCommit
| AfgAuthoritySet
);
+30 -1
View File
@@ -17,7 +17,8 @@ import {
BlockDetails,
Timestamp,
Milliseconds,
ChainLabel
ChainLabel,
AuthoritySetInfo,
} from './types';
export const Actions = {
@@ -37,6 +38,10 @@ export const Actions = {
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,
};
export type Action = typeof Actions[keyof typeof Actions];
@@ -126,6 +131,26 @@ export namespace Variants {
action: typeof Actions.Pong;
payload: string; // just echo whatever `ping` sent
}
export interface AfgFinalizedMessage extends MessageBase {
action: typeof Actions.AfgFinalized;
payload: [Address, BlockNumber, BlockHash];
}
export interface AfgAuthoritySet extends MessageBase {
action: typeof Actions.AfgAuthoritySet;
payload: AuthoritySetInfo;
}
export interface AfgReceivedPrecommit extends MessageBase {
action: typeof Actions.AfgReceivedPrecommit;
payload: [Address, BlockNumber, BlockHash, Address];
}
export interface AfgReceivedPrevote extends MessageBase {
action: typeof Actions.AfgReceivedPrevote;
payload: [Address, BlockNumber, BlockHash, Address];
}
}
export type Message =
@@ -144,6 +169,10 @@ export type Message =
| Variants.RemovedChainMessage
| Variants.SubscribedToMessage
| Variants.UnsubscribedFromMessage
| Variants.AfgFinalizedMessage
| Variants.AfgReceivedPrevote
| Variants.AfgReceivedPrecommit
| Variants.AfgAuthoritySet
| Variants.PongMessage;
/**
+31 -1
View File
@@ -27,7 +27,37 @@ export type NetworkId = Opaque<string, 'NetworkId'>;
export type NetworkState = Opaque<string | object, 'NetworkState'>;
export type BlockDetails = [BlockNumber, BlockHash, Milliseconds, Timestamp, Maybe<PropagationTime>];
export type NodeDetails = [NodeName, NodeImplementation, NodeVersion, Maybe<Address>, Maybe<NetworkId>];
export type NodeDetails = [NodeName, Address, NodeImplementation, NodeVersion, Maybe<Address>, Maybe<NetworkId>];
export type NodeStats = [PeerCount, TransactionCount];
export type NodeHardware = [Array<MemoryUse>, Array<CPUUse>, Array<BytesPerSecond>, Array<BytesPerSecond>, Array<Timestamp>];
export type NodeLocation = [Latitude, Longitude, City];
export declare type Authority = {
Address: Address,
NodeId: Maybe<NodeId>,
Name: Maybe<NodeName>,
};
export declare type Authorities = Array<Address>;
export declare type AuthoritySetId = Opaque<number, 'AuthoritySetId'>;
export declare type AuthoritySetInfo = [AuthoritySetId, Authorities, Address, BlockNumber, BlockHash];
export declare type ConsensusInfo = Array<[BlockNumber, ConsensusView]>;
export declare type ConsensusView = Map<Address, ConsensusState>;
export declare type ConsensusState = Map<Address, ConsensusDetail>;
export declare type ConsensusDetail = {
Precommit: Precommit;
ImplicitPrecommit: ImplicitPrecommit;
Prevote: Prevote;
ImplicitPrevote: ImplicitPrevote;
ImplicitPointer: ImplicitPointer;
Finalized: ImplicitFinalized;
ImplicitFinalized: Finalized;
FinalizedHash: BlockHash;
FinalizedHeight: BlockNumber;
};
export declare type Precommit = Opaque<boolean, 'Precommit'>;
export declare type Prevote = Opaque<boolean, 'Prevote'>;
export declare type Finalized = Opaque<boolean, 'Finalized'>;
export declare type ImplicitPrecommit = Opaque<boolean, 'ImplicitPrecommit'>;
export declare type ImplicitPrevote = Opaque<boolean, 'ImplicitPrevote'>;
export declare type ImplicitFinalized = Opaque<boolean, 'ImplicitFinalized'>;
export declare type ImplicitPointer = Opaque<BlockNumber, 'ImplicitPointer'>;
+576 -15
View File
@@ -39,11 +39,87 @@
}
}
},
"@babel/runtime": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz",
"integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==",
"requires": {
"regenerator-runtime": "^0.13.2"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
}
}
},
"@polkadot/util": {
"version": "0.41.1",
"resolved": "https://registry.npmjs.org/@polkadot/util/-/util-0.41.1.tgz",
"integrity": "sha512-1mFHxxdXyRgeFk0ygfJklzsf6HiEEMikrpUgr+3Dw5S7KromjPD8EbWkpcZQZ3Png5PTwGyjrx7MYY4Ajiu5xQ==",
"requires": {
"@babel/runtime": "^7.4.0",
"@types/bn.js": "^4.11.4",
"@types/deasync": "^0.1.0",
"@types/ip-regex": "^3.0.0",
"bn.js": "^4.11.8",
"camelcase": "^5.2.0",
"chalk": "^2.4.2",
"ip-regex": "^4.0.0",
"moment": "^2.24.0"
},
"dependencies": {
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
}
}
},
"@polkadot/wasm-crypto": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-0.5.1.tgz",
"integrity": "sha512-8h7uz85bvLVirtbxLkELr9H25oqAfgnQuDP6FP3QJeag7VfMtVvRF5U2JWGgsiUSiRg+UJnTuCleOSDnageRmg=="
},
"@polkadot/wasm-schnorrkel": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-schnorrkel/-/wasm-schnorrkel-0.3.1.tgz",
"integrity": "sha512-2J/lNZe7oDWvzUbsEfvKfiiAvBIBbIsppeBGahwSg2Y+cLweQgThXGizPpQoPo0tHxgMKdTi6jhPu7tLzCW/cA=="
},
"@tanem/svg-injector": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-1.2.1.tgz",
"integrity": "sha512-mA5Q5ulPoGQ+e08Vts1R6xw2QU0BKEnMH/KcqoYoS7Gk6imvMTpyFPeu1g+NOZObSIoAzA3/kRzY8m96cEBA2A=="
},
"@types/bn.js": {
"version": "4.11.5",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.5.tgz",
"integrity": "sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==",
"requires": {
"@types/node": "*"
}
},
"@types/deasync": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@types/deasync/-/deasync-0.1.0.tgz",
"integrity": "sha512-jxUH53LtGvbIL3TX2hD/XQuAgYJeATtx9kDXq5XtCZrWQABsiCQPjWi/KQXECUF+p9FuR6/tawnEDjXlEr4rFA=="
},
"@types/ip-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/ip-regex/-/ip-regex-3.0.0.tgz",
"integrity": "sha512-4j26qYCikXbIaypgqdGQhhIvYfTR+40i/05jVQB8oc57RbROEkBocdIUZO7fOhyqJsx7WRY36ySW8hoUZn7Fgw=="
},
"@types/jest": {
"version": "23.0.2",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.0.2.tgz",
@@ -58,8 +134,7 @@
"@types/node": {
"version": "10.3.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.2.tgz",
"integrity": "sha512-9NfEUDp3tgRhmoxzTpTo+lq+KIVFxZahuRX0LHF/9IzKHaWuoWsIrrJ61zw5cnnlGINX8lqJzXYfQTOICS5Q+A==",
"dev": true
"integrity": "sha512-9NfEUDp3tgRhmoxzTpTo+lq+KIVFxZahuRX0LHF/9IzKHaWuoWsIrrJ61zw5cnnlGINX8lqJzXYfQTOICS5Q+A=="
},
"@types/react": {
"version": "16.3.17",
@@ -1368,6 +1443,14 @@
}
}
},
"base-x": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz",
"integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
@@ -1397,6 +1480,42 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bip39": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/bip39/-/bip39-2.6.0.tgz",
"integrity": "sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg==",
"requires": {
"create-hash": "^1.1.0",
"pbkdf2": "^3.0.9",
"randombytes": "^2.0.1",
"safe-buffer": "^5.0.1",
"unorm": "^1.3.3"
}
},
"blake2js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/blake2js/-/blake2js-1.0.0.tgz",
"integrity": "sha1-tokMOU3blAXQbZp6089lZxHcrYk=",
"requires": {
"bindings": "^1.2.1",
"debug": "^2.2.0",
"nan": "^2.4.0",
"readable-stream": "^2.1.4"
}
},
"blakejs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
"integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -1627,6 +1746,14 @@
"electron-to-chromium": "^1.3.30"
}
},
"bs58": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
"integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
"requires": {
"base-x": "^3.0.2"
}
},
"bser": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
@@ -1785,6 +1912,11 @@
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000878.tgz",
"integrity": "sha512-/dCGTdLCnjVJno1mFRn7Y6eit3AYaeFzSrMQHCoK0LEQaWl5snuLex1Ky4b8/Qu2ig5NgTX4cJx65hH9546puA=="
},
"canvas-renderer": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.1.1.tgz",
"integrity": "sha512-/V0XetN7s1Mk3NO7x2wxPZYv0pLMQtGAhecuOuKR88beiYCUle1AbCcFZNLu+4NVzi9RVHS0rXtIgzPEaKidLw=="
},
"capture-exit": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz",
@@ -1841,6 +1973,31 @@
}
}
},
"change-case": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/change-case/-/change-case-3.1.0.tgz",
"integrity": "sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==",
"requires": {
"camel-case": "^3.0.0",
"constant-case": "^2.0.0",
"dot-case": "^2.1.0",
"header-case": "^1.0.0",
"is-lower-case": "^1.1.0",
"is-upper-case": "^1.1.0",
"lower-case": "^1.1.1",
"lower-case-first": "^1.0.0",
"no-case": "^2.3.2",
"param-case": "^2.1.0",
"pascal-case": "^2.0.0",
"path-case": "^2.1.0",
"sentence-case": "^2.1.0",
"snake-case": "^2.1.0",
"swap-case": "^1.1.0",
"title-case": "^2.1.0",
"upper-case": "^1.1.1",
"upper-case-first": "^1.1.0"
}
},
"chardet": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
@@ -2123,6 +2280,15 @@
"date-now": "^0.1.4"
}
},
"constant-case": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz",
"integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=",
"requires": {
"snake-case": "^2.1.0",
"upper-case": "^1.1.1"
}
},
"constants-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -2522,6 +2688,11 @@
"integrity": "sha512-G5HnoK8nOiAq3DXIEoY2n/8Vb7Lgrms+jGJl8E4EJpQEeVONEnPFJSl8IK505wPBoxxtrtHhrRm4WX2GgdqarA==",
"dev": true
},
"cuint": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
"integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs="
},
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -2857,6 +3028,14 @@
"domelementtype": "1"
}
},
"dot-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz",
"integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=",
"requires": {
"no-case": "^2.2.0"
}
},
"dot-prop": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -3544,6 +3723,11 @@
"loader-utils": "^1.0.2"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"filename-regex": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@@ -3766,7 +3950,8 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -3784,11 +3969,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"bundled": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3801,15 +3988,18 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -3912,7 +4102,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -3922,6 +4113,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -3934,17 +4126,20 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"bundled": true,
"optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -3961,6 +4156,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -4033,7 +4229,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -4043,6 +4240,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -4118,7 +4316,8 @@
},
"safe-buffer": {
"version": "5.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -4148,6 +4347,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -4165,6 +4365,7 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -4203,11 +4404,13 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"bundled": true,
"optional": true
},
"yallist": {
"version": "3.0.2",
"bundled": true
"bundled": true,
"optional": true
}
}
},
@@ -4221,6 +4424,11 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
},
"get-node-dimensions": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz",
"integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ=="
},
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@@ -4515,6 +4723,15 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
},
"header-case": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz",
"integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=",
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.1.3"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -4983,6 +5200,11 @@
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.1.0.tgz",
"integrity": "sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA=="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
@@ -5153,6 +5375,14 @@
"is-path-inside": "^1.0.0"
}
},
"is-lower-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz",
"integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=",
"requires": {
"lower-case": "^1.1.0"
}
},
"is-npm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
@@ -5276,6 +5506,14 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"is-upper-case": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz",
"integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=",
"requires": {
"upper-case": "^1.1.0"
}
},
"is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@@ -5315,6 +5553,11 @@
"whatwg-fetch": ">=0.10.0"
}
},
"isomorphic-ws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
"integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -5454,6 +5697,14 @@
"handlebars": "^4.0.3"
}
},
"jdenticon": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-2.1.1.tgz",
"integrity": "sha512-bFD210JMaK2RMHGSkdDXqdQzasMrVIGDfxUMXD/Uwq2t7g9Njb4T6kms5TrocsLU0CDmQCoB0laGJ6JETiprsg==",
"requires": {
"canvas-renderer": "~2.1.1"
}
},
"jest": {
"version": "22.4.2",
"resolved": "https://registry.npmjs.org/jest/-/jest-22.4.2.tgz",
@@ -6507,6 +6758,14 @@
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
},
"lower-case-first": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz",
"integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=",
"requires": {
"lower-case": "^1.1.2"
}
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -6769,6 +7028,11 @@
"minimist": "0.0.8"
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -6809,8 +7073,7 @@
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"optional": true
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
},
"nanomatch": {
"version": "1.2.13",
@@ -7102,6 +7365,116 @@
"mimic-fn": "^1.0.0"
}
},
"oo7": {
"version": "0.7.12",
"resolved": "https://registry.npmjs.org/oo7/-/oo7-0.7.12.tgz",
"integrity": "sha512-nVmxwLIiVCY4Aid6/+p2ujJ2mj3yh15zQ3xSl3UPui7U37DJJfD0KmuMjp+XOWH1HxTkKhIKDgR34eEhFHNxgA=="
},
"oo7-react": {
"version": "0.8.13",
"resolved": "https://registry.npmjs.org/oo7-react/-/oo7-react-0.8.13.tgz",
"integrity": "sha512-1WoJf/pYDiZAkd+rilH9xs6FR6k4LKck/uCPVzClTnp/uFvsR8pxlbTHvNPvOqspxob76qSbstUv7IVdO/TQ7Q==",
"requires": {
"oo7": "^0.7.12",
"react": "^16.5.2"
},
"dependencies": {
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
},
"dependencies": {
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
}
}
},
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
},
"oo7-substrate": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/oo7-substrate/-/oo7-substrate-0.8.0.tgz",
"integrity": "sha512-ltglsNXZNmiDsJ5q4LYx0tf0SN0CVjW5fSifwrBRe/objxG2fYXogZ4eiNdsLJrTddvskv+3o5JpvFw/D3PCUA==",
"requires": {
"@polkadot/util": "^0.41.1",
"@polkadot/wasm-crypto": "^0.5.1",
"@polkadot/wasm-schnorrkel": "^0.3.1",
"base-x": "^3.0.4",
"bip39": "^2.5.0",
"blakejs": "^1.1.0",
"bs58": "^4.0.1",
"buffer": "^5.2.1",
"change-case": "^3.0.2",
"isomorphic-fetch": "^2.2.1",
"isomorphic-ws": "^4.0.1",
"jdenticon": "^2.1.1",
"oo7": "^0.7.12",
"pbkdf2": "^3.0.17",
"ss58": "^1.0.2",
"text-encoding": "^0.7.0",
"text-encoding-polyfill": "^0.6.7",
"tweetnacl": "^1.0.0",
"ws": "^6.1.2",
"xxhashjs": "^0.2.2"
},
"dependencies": {
"buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
},
"pbkdf2": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
"integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
"requires": {
"create-hash": "^1.1.2",
"create-hmac": "^1.1.4",
"ripemd160": "^2.0.1",
"safe-buffer": "^5.0.1",
"sha.js": "^2.4.8"
}
},
"tweetnacl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz",
"integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A=="
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"opn": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.2.0.tgz",
@@ -7283,6 +7656,15 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"pascal-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz",
"integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=",
"requires": {
"camel-case": "^3.0.0",
"upper-case-first": "^1.1.0"
}
},
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -7293,6 +7675,14 @@
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
"integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
},
"path-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz",
"integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=",
"requires": {
"no-case": "^2.2.0"
}
},
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
@@ -7396,6 +7786,52 @@
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
},
"polkadot-identicon": {
"version": "1.1.45",
"resolved": "https://registry.npmjs.org/polkadot-identicon/-/polkadot-identicon-1.1.45.tgz",
"integrity": "sha512-UFdxnfvQPHnbKadKibbCGsFrO6XfOy+OeVf+yAyp24CAnSwVujl7OiIdBxnV1V5BVSU61Cfan96grj8VznZk/w==",
"requires": {
"blake2js": "^1.0.0",
"oo7": "^0.7.12",
"oo7-react": "^0.8.13",
"oo7-substrate": "^0.8.0",
"react": "^16.4.2",
"ss58": "^1.0.0"
},
"dependencies": {
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
},
"dependencies": {
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
}
}
},
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
},
"portfinder": {
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz",
@@ -8895,6 +9331,42 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.1.tgz",
"integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw=="
},
"react-is": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
},
"react-measure": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.3.0.tgz",
"integrity": "sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==",
"requires": {
"@babel/runtime": "^7.2.0",
"get-node-dimensions": "^1.2.1",
"prop-types": "^15.6.2",
"resize-observer-polyfill": "^1.5.0"
},
"dependencies": {
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
}
}
},
"react-scripts-ts": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/react-scripts-ts/-/react-scripts-ts-2.17.0.tgz",
@@ -9275,6 +9747,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz",
@@ -9438,6 +9915,15 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"scheduler": {
"version": "0.13.6",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"schema-utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
@@ -9499,6 +9985,15 @@
}
}
},
"sentence-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz",
"integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=",
"requires": {
"no-case": "^2.2.0",
"upper-case-first": "^1.1.2"
}
},
"serialize-javascript": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz",
@@ -9623,6 +10118,14 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
},
"snake-case": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz",
"integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=",
"requires": {
"no-case": "^2.2.0"
}
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -9883,6 +10386,15 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"ss58": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ss58/-/ss58-1.0.2.tgz",
"integrity": "sha512-hCpPJ12+GgsYsyVW2dXY6p3KfqZgLFA9zrKp5cgEx3x93JjPG4DDm6lu0Zp87FW0CSRan3gy7jlF4ZCjHqS7Ig==",
"requires": {
"blakejs": "^1.1.0",
"bs58": "^4.0.1"
}
},
"sshpk": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
@@ -10153,6 +10665,15 @@
"serviceworker-cache-polyfill": "^4.0.0"
}
},
"swap-case": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz",
"integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=",
"requires": {
"lower-case": "^1.1.1",
"upper-case": "^1.1.1"
}
},
"symbol-tree": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
@@ -10183,6 +10704,16 @@
"require-main-filename": "^1.0.1"
}
},
"text-encoding": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz",
"integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA=="
},
"text-encoding-polyfill": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/text-encoding-polyfill/-/text-encoding-polyfill-0.6.7.tgz",
"integrity": "sha512-/DZ1XJqhbqRkCop6s9ZFu8JrFRwmVuHg4quIRm+ziFkR3N3ec6ck6yBvJ1GYeEQZhLVwRW0rZE+C3SSJpy0RTg=="
},
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -10230,6 +10761,15 @@
"setimmediate": "^1.0.4"
}
},
"title-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz",
"integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=",
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.0.3"
}
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -10699,6 +11239,11 @@
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
},
"unorm": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz",
"integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw=="
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -10784,6 +11329,14 @@
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
},
"upper-case-first": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz",
"integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=",
"requires": {
"upper-case": "^1.1.1"
}
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@@ -11649,6 +12202,14 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"xxhashjs": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
"requires": {
"cuint": "^0.2.2"
}
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+3 -1
View File
@@ -20,9 +20,11 @@
},
"dependencies": {
"@fnando/sparkline": "maciejhirsz/sparkline",
"polkadot-identicon": "^1.1.36",
"@types/react-measure": "^2.0.5",
"polkadot-identicon": "^1.1.45",
"react": "16.4.0",
"react-dom": "16.4.0",
"react-measure": "^2.3.0",
"react-scripts-ts": "2.17.0",
"react-svg": "^4.1.1",
"stable": "^0.1.8"
+19
View File
@@ -0,0 +1,19 @@
// Jdenticon 2.1.1 | jdenticon.com | MIT licensed | (c) 2014-2018 Daniel Mester Pirttijärvi
(function(q,y,z){var t=z(q,q.jQuery);"undefined"!==typeof module&&"exports"in module?module.exports=t:"function"===typeof define&&define.amd?define([],function(){return t}):q[y]=t})(this,"jdenticon",function(q,y){function z(a,b,c){for(var d=document.createElementNS("http://www.w3.org/2000/svg",b),f=2;f+1<arguments.length;f+=2)d.setAttribute(arguments[f],arguments[f+1]);a.appendChild(d)}function t(a){this.b=Math.min(Number(a.getAttribute("width"))||100,Number(a.getAttribute("height"))||100);for(this.a=
a;a.firstChild;)a.removeChild(a.firstChild);a.setAttribute("viewBox","0 0 "+this.b+" "+this.b);a.setAttribute("preserveAspectRatio","xMidYMid meet")}function K(a){this.b=a;this.a='\x3csvg xmlns\x3d"http://www.w3.org/2000/svg" width\x3d"'+a+'" height\x3d"'+a+'" viewBox\x3d"0 0 '+a+" "+a+'" preserveAspectRatio\x3d"xMidYMid meet"\x3e'}function N(a){return function(a){for(var b=[],d=0;d<a.length;d++)for(var f=a[d],e=28;0<=e;e-=4)b.push((f>>>e&15).toString(16));return b.join("")}(function(a){for(var b=
1732584193,d=4023233417,f=2562383102,e=271733878,h=3285377520,k=[b,d,f,e,h],g=0;g<a.length;g++){for(var u=a[g],l=16;80>l;l++){var A=u[l-3]^u[l-8]^u[l-14]^u[l-16];u[l]=A<<1|A>>>31}for(l=0;80>l;l++)A=(b<<5|b>>>27)+(20>l?(d&f^~d&e)+1518500249:40>l?(d^f^e)+1859775393:60>l?(d&f^d&e^f&e)+2400959708:(d^f^e)+3395469782)+h+u[l],h=e,e=f,f=d<<30|d>>>2,d=b,b=A|0;k[0]=b=k[0]+b|0;k[1]=d=k[1]+d|0;k[2]=f=k[2]+f|0;k[3]=e=k[3]+e|0;k[4]=h=k[4]+h|0}return k}(function(a){function b(a,b){for(var c=[],d=-1,e=0;e<b;e++)d=
e/4|0,c[d]=(c[d]||0)+(f[a+e]<<8*(3-(e&3)));for(;16>++d;)c[d]=0;return c}var d=encodeURI(a),f=[];a=0;var e,h=[];for(e=0;e<d.length;e++){if("%"==d[e]){var k=r(d,e+1,2);e+=2}else k=d.charCodeAt(e);f[a++]=k}f[a++]=128;for(e=0;e+64<=a;e+=64)h.push(b(e,64));d=a-e;e=b(e,d);64<d+8&&(h.push(e),e=b(0,0));e[15]=8*a-8;h.push(e);return h}(a)))}function E(a,b){var c=a.canvas.width,d=a.canvas.height;a.save();this.b=a;b?this.a=b:(this.a=Math.min(c,d),a.translate((c-this.a)/2|0,(d-this.a)/2|0));a.clearRect(0,0,this.a,
this.a)}function v(a){a|=0;return 0>a?"00":16>a?"0"+a.toString(16):256>a?a.toString(16):"ff"}function F(a,b,c){c=0>c?c+6:6<c?c-6:c;return v(255*(1>c?a+(b-a)*c:3>c?b:4>c?a+(b-a)*(4-c):a))}function O(a){"undefined"!=typeof MutationObserver&&(new MutationObserver(function(b){for(var c=0;c<b.length;c++){for(var d=b[c],f=d.addedNodes,e=0;f&&e<f.length;e++){var h=f[e];if(1==h.nodeType)if(g.w(h))a(h);else{h=h.querySelectorAll(g.A);for(var k=0;k<h.length;k++)a(h[k])}}"attributes"==d.type&&g.w(d.target)&&
a(d.target)}})).observe(document.body,{childList:!0,attributes:!0,attributeFilter:[g.o,g.s,"width","height"],subtree:!0})}function r(a,b,c){return parseInt(a.substr(b,c),16)}function p(a){return(10*a+.5|0)/10}function L(){this.j=""}function G(a){this.b={};this.h=a;this.a=a.b}function M(a){this.h=a;this.c=w.a}function P(a,b){a=b.O(a);return[m.i(a,b.H,b.G(0)),m.i(a,b.v,b.u(.5)),m.i(a,b.H,b.G(1)),m.i(a,b.v,b.u(1)),m.i(a,b.v,b.u(0))]}function B(a,b){this.x=a;this.y=b}function w(a,b,c,d){this.b=a;this.c=
b;this.h=c;this.a=d}function H(a,b,c,d,f,e,h){function k(e,f,k,h,g){h=h?r(b,h,1):0;f=f[r(b,k,1)%f.length];a.D(p[n[e]]);for(e=0;e<g.length;e++)m.c=new w(c+g[e][0]*l,d+g[e][1]*l,l,h++%4),f(m,l,e);a.F()}function g(a){if(0<=a.indexOf(q))for(var b=0;b<a.length;b++)if(0<=n.indexOf(a[b]))return!0}h.C&&a.m(h.C);e=.5+f*(void 0===e?.08:e)|0;f-=2*e;var m=new M(a),l=0|f/4;c+=0|e+f/2-2*l;d+=0|e+f/2-2*l;var p=P(r(b,-7)/268435455,h),n=[];for(f=0;3>f;f++){var q=r(b,8+f,1)%p.length;if(g([0,4])||g([2,3]))q=1;n.push(q)}k(0,
I.I,2,3,[[1,0],[2,0],[2,3],[1,3],[0,1],[3,1],[3,2],[0,2]]);k(1,I.I,4,5,[[0,0],[3,0],[3,3],[0,3]]);k(2,I.M,1,null,[[1,1],[2,1],[2,2],[1,2]]);a.finish()}function J(){function a(a,b){var d=c[a];d&&1<d.length||(d=b);return function(a){a=d[0]+a*(d[1]-d[0]);return 0>a?0:1<a?1:a}}var b=n.config||q.jdenticon_config||{},c=b.lightness||{},d=b.saturation||{},f="color"in d?d.color:d;d=d.grayscale;return{O:function(a){var c=b.hues,d;c&&0<c.length&&(d=c[0|.999*a*c.length]);return"number"==typeof d?(d/360%1+1)%
1:a},v:"number"==typeof f?f:.5,H:"number"==typeof d?d:0,u:a("color",[.4,.8]),G:a("grayscale",[.3,.9]),C:m.parse(b.backColor)}}function C(a){return/^[0-9a-f]{11,}$/i.test(a)&&a}function D(a){return N(null==a?"":""+a)}function x(a,b,c){if("string"===typeof a){if(g.J){a=document.querySelectorAll(a);for(var d=0;d<a.length;d++)x(a[d],b,c)}}else if(d=g.w(a))if(b=C(b)||null!=b&&D(b)||C(a.getAttribute(g.s))||a.hasAttribute(g.o)&&D(a.getAttribute(g.o)))a=d==g.B?new G(new t(a)):new E(a.getContext("2d")),H(a,
b,0,0,a.a,c,J())}function n(){g.J&&x(g.A)}function Q(){var a=(n.config||q.jdenticon_config||{}).replaceMode;"never"!=a&&(n(),"observe"==a&&O(x))}t.prototype={m:function(a,b){b&&z(this.a,"rect","width","100%","height","100%","fill",a,"opacity",b)},c:function(a,b){z(this.a,"path","fill",a,"d",b)}};K.prototype={m:function(a,b){b&&(this.a+='\x3crect width\x3d"100%" height\x3d"100%" fill\x3d"'+a+'" opacity\x3d"'+b.toFixed(2)+'"/\x3e')},c:function(a,b){this.a+='\x3cpath fill\x3d"'+a+'" d\x3d"'+b+'"/\x3e'},
toString:function(){return this.a+"\x3c/svg\x3e"}};var g={B:1,L:2,s:"data-jdenticon-hash",o:"data-jdenticon-value",J:"undefined"!==typeof document&&"querySelectorAll"in document,w:function(a){if(a){var b=a.tagName;if(/svg/i.test(b))return g.B;if(/canvas/i.test(b)&&"getContext"in a)return g.L}}};g.A="["+g.s+"],["+g.o+"]";E.prototype={m:function(a){var b=this.b,c=this.a;b.fillStyle=m.K(a);b.fillRect(0,0,c,c)},D:function(a){this.b.fillStyle=m.K(a);this.b.beginPath()},F:function(){this.b.fill()},f:function(a){var b=
this.b,c;b.moveTo(a[0].x,a[0].y);for(c=1;c<a.length;c++)b.lineTo(a[c].x,a[c].y);b.closePath()},g:function(a,b,c){var d=this.b;b/=2;d.moveTo(a.x+b,a.y+b);d.arc(a.x+b,a.y+b,b,0,2*Math.PI,c);d.closePath()},finish:function(){this.b.restore()}};var m={P:function(a,b,c){return"#"+v(a)+v(b)+v(c)},parse:function(a){if(/^#[0-9a-f]{3,8}$/i.test(a)){if(6>a.length){var b=a[1],c=a[2],d=a[3];a=a[4]||"";return"#"+b+b+c+c+d+d+a+a}if(7==a.length||8<a.length)return a}},K:function(a){var b=r(a,7,2);return isNaN(b)?
a:"rgba("+r(a,1,2)+","+r(a,3,2)+","+r(a,5,2)+","+(b/255).toFixed(2)+")"},N:function(a,b,c){if(0==b)return a=v(255*c),"#"+a+a+a;b=.5>=c?c*(b+1):c+b-c*b;c=2*c-b;return"#"+F(c,b,6*a+2)+F(c,b,6*a)+F(c,b,6*a-2)},i:function(a,b,c){var d=[.55,.5,.5,.46,.6,.55,.55][6*a+.5|0];return m.N(a,b,.5>c?c*d*2:d+(c-.5)*(1-d)*2)}},I={M:[function(a,b){var c=.42*b;a.f([0,0,b,0,b,b-2*c,b-c,b,0,b])},function(a,b){var c=0|.5*b;a.b(b-c,0,c,0|.8*b,2)},function(a,b){var c=0|b/3;a.a(c,c,b-c,b-c)},function(a,b){var c=.1*b,d=
6>b?1:8>b?2:0|.25*b;c=1<c?0|c:.5<c?1:c;a.a(d,d,b-c-d,b-c-d)},function(a,b){var c=0|.15*b,d=0|.5*b;a.g(b-d-c,b-d-c,d)},function(a,b){var c=.1*b,d=4*c;3<d&&(d|=0);a.a(0,0,b,b);a.f([d,d,b-c,d,d+(b-d-c)/2,b-c],!0)},function(a,b){a.f([0,0,b,0,b,.7*b,.4*b,.4*b,.7*b,b,0,b])},function(a,b){a.b(b/2,b/2,b/2,b/2,3)},function(a,b){a.a(0,0,b,b/2);a.a(0,b/2,b/2,b/2);a.b(b/2,b/2,b/2,b/2,1)},function(a,b){var c=.14*b,d=4>b?1:6>b?2:0|.35*b;c=8>b?c:0|c;a.a(0,0,b,b);a.a(d,d,b-d-c,b-d-c,!0)},function(a,b){var c=.12*
b,d=3*c;a.a(0,0,b,b);a.g(d,d,b-c-d,!0)},function(a,b){a.b(b/2,b/2,b/2,b/2,3)},function(a,b){var c=.25*b;a.a(0,0,b,b);a.l(c,c,b-c,b-c,!0)},function(a,b,c){var d=.4*b;c||a.g(d,d,1.2*b)}],I:[function(a,b){a.b(0,0,b,b,0)},function(a,b){a.b(0,b/2,b,b/2,0)},function(a,b){a.l(0,0,b,b)},function(a,b){var c=b/6;a.g(c,c,b-2*c)}]};L.prototype={f:function(a){for(var b="M"+p(a[0].x)+" "+p(a[0].y),c=1;c<a.length;c++)b+="L"+p(a[c].x)+" "+p(a[c].y);this.j+=b+"Z"},g:function(a,b,c){c=c?0:1;var d=p(b/2),f=p(b);this.j+=
"M"+p(a.x)+" "+p(a.y+b/2)+"a"+d+","+d+" 0 1,"+c+" "+f+",0a"+d+","+d+" 0 1,"+c+" "+-f+",0"}};G.prototype={m:function(a){a=/^(#......)(..)?/.exec(a);this.h.m(a[1],a[2]?r(a[2],0)/255:1)},D:function(a){this.c=this.b[a]||(this.b[a]=new L)},F:function(){},f:function(a){this.c.f(a)},g:function(a,b,c){this.c.g(a,b,c)},finish:function(){for(var a in this.b)this.h.c(a,this.b[a].j)}};M.prototype={f:function(a,b){var c=b?-2:2,d=this.c,f=[];for(b=b?a.length-2:0;b<a.length&&0<=b;b+=c)f.push(d.l(a[b],a[b+1]));this.h.f(f)},
g:function(a,b,c,d){this.h.g(this.c.l(a,b,c,c),c,d)},a:function(a,b,c,d,f){this.f([a,b,a+c,b,a+c,b+d,a,b+d],f)},b:function(a,b,c,d,f,e){a=[a+c,b,a+c,b+d,a,b+d,a,b];a.splice((f||0)%4*2,2);this.f(a,e)},l:function(a,b,c,d,f){this.f([a+c/2,b,a+c,b+d/2,a+c/2,b+d,a,b+d/2],f)}};w.prototype={l:function(a,b,c,d){var f=this.b+this.h,e=this.c+this.h;return 1===this.a?new B(f-b-(d||0),this.c+a):2===this.a?new B(f-a-(c||0),e-b-(d||0)):3===this.a?new B(this.b+b,e-a-(c||0)):new B(this.b+a,this.c+b)}};w.a=new w(0,
0,0,0);n.drawIcon=function(a,b,c,d){if(!a)throw Error("No canvas specified.");a=new E(a,c);H(a,C(b)||D(b),0,0,c,d||0,J())};n.toSvg=function(a,b,c){var d=new K(b);H(new G(d),C(a)||D(a),0,0,b,c,J());return d.toString()};n.update=x;n.version="2.1.1";y&&(y.fn.jdenticon=function(a,b){this.each(function(c,d){x(d,a,b)});return this});"function"===typeof setTimeout&&setTimeout(Q,0);return n});
+1
View File
@@ -12,6 +12,7 @@
color: #111;
}
</style>
<script type="text/javascript" src="./Jdenticon.min.js"></script>
</head>
<body>
<noscript>
+228
View File
@@ -0,0 +1,228 @@
import { Types, Maybe } from '@dotstats/common';
import { State } from './state';
export class AfgHandling {
private updateState: (state: any) => void;
private getState: () => State;
constructor(
updateState: (state: any) => void,
getState: () => State,
) {
this.updateState = updateState;
this.getState = getState;
}
public receivedAuthoritySet(
authoritySetId: Types.AuthoritySetId,
authorities: Types.Authorities,
) {
if (authoritySetId !== this.getState().authoritySetId) {
// the visualization is restarted when we receive a new auhority set
this.updateState({authoritySetId, authorities, consensusInfo: []});
}
return null;
}
public receivedFinalized(
addr: Types.Address,
finalizedNumber: Types.BlockNumber,
finalizedHash: Types.BlockHash,
) {
const consensusInfo = this.getState().consensusInfo;
this.markFinalized(addr, finalizedNumber, finalizedHash);
const op = (i: Types.BlockNumber, view: Types.ConsensusView) => {
const consensusDetail = view[addr][addr];
if (consensusDetail.Finalized || consensusDetail.ImplicitFinalized) {
return false;
}
this.markImplicitlyFinalized(i, addr, finalizedNumber, addr);
return true;
};
this.backfill(consensusInfo, finalizedNumber, op, addr, addr);
}
public receivedPre(
addr: Types.Address,
height: Types.BlockNumber,
hash: Types.BlockHash,
voter: Types.Address,
what: string,
) {
const data = what === "prevote" ? { Prevote: true } : { Precommit: true };
this.updateConsensusInfo(height, addr, voter, data as Partial<Types.ConsensusDetail>);
const op = (i: Types.BlockNumber, view: Types.ConsensusView) => {
const consensusDetail = view[addr][voter];
if (consensusDetail.Prevote || consensusDetail.ImplicitPrevote) {
return false;
}
this.markImplicitlyPre(i, addr, height, what, voter);
return true;
};
this.backfill(this.getState().consensusInfo, height, op, addr, voter);
}
private markFinalized(
addr: Types.Address,
finalizedHeight: Types.BlockNumber,
finalizedHash: Types.BlockHash,
) {
const data = {
Finalized: true,
FinalizedHash: finalizedHash,
FinalizedHeight: finalizedHeight,
// this is extrapolated. if this app was just started up we
// might not yet have received prevotes/precommits. but
// those are a necessary precondition for finalization, so
// we can set them and display them in the ui.
Prevote: true,
Precommit: true,
} as Types.ConsensusDetail;
this.updateConsensusInfo(finalizedHeight, addr, addr, data);
}
// A Prevote or Precommit on a block implicitly includes
// a vote on all preceding blocks. This function marks
// the preceding blocks as implicitly voted on and stores
// a pointer to the block which contains the explicit vote.
private markImplicitlyPre(
height: Types.BlockNumber,
addr: Types.Address,
where: Types.BlockNumber,
what: string,
voter: Types.Address,
) {
const consensusInfo = this.getState().consensusInfo;
const consensusView = this.initialiseConsensusView(consensusInfo, height, addr, voter);
if (what === "prevote") {
consensusView[addr][voter].ImplicitPrevote = true;
} else if (what === "precommit") {
consensusView[addr][voter].ImplicitPrecommit = true;
}
consensusView[addr][voter].ImplicitPointer = where;
this.updateState({consensusInfo});
}
// Finalizing a block implicitly includes finalizing all
// preceding blocks. This function marks the preceding
// blocks as implicitly finalized on and stores a pointer
// to the block which contains the explicit finalization.
private markImplicitlyFinalized(
height: Types.BlockNumber,
addr: Types.Address,
to: Types.BlockNumber,
voter: Types.Address,
) {
const consensusInfo = this.getState().consensusInfo;
const consensusView = this.initialiseConsensusView(consensusInfo, height, addr, voter);
const consensusDetail = {
Finalized: true,
FinalizedHeight: height,
ImplicitFinalized: true,
ImplicitPointer: to,
// this is extrapolated. if this app was just started up we
// might not yet have received prevotes/precommits. but
// those are a necessary precondition for finalization, so
// we can set them and display them in the ui.
Prevote: true,
Precommit: true,
ImplicitPrevote: true,
ImplicitPrecommit: true,
};
consensusView[addr][voter] = consensusDetail;
this.updateState({consensusInfo});
}
// Initializes the `ConsensusView` with empty objects.
private initialiseConsensusView(
consensusInfo: Types.ConsensusInfo,
height: Types.BlockNumber,
own: Types.Address,
other: Types.Address,
) : Types.ConsensusView {
const found =
consensusInfo.find(([blockNumber,]) => blockNumber === height);
let consensusView;
if (found) {
[, consensusView] = found;
} else {
consensusView = {} as Types.ConsensusView;
consensusInfo.unshift([height, consensusView]);
}
this.initialiseConsensusViewByRef(consensusView, own, other);
return consensusView;
}
// Initializes the `ConsensusView` with empty objects.
private initialiseConsensusViewByRef(
consensusView: Types.ConsensusView,
own: Types.Address,
other: Types.Address,
) {
if (!consensusView[own]) {
consensusView[own] = {} as Types.ConsensusState;
}
if (!consensusView[own][other]) {
consensusView[own][other] = {} as Types.ConsensusDetail;
}
}
// Fill the block cache back from the `to` number to the last block.
// The function `f` is used to evaluate if we should continue backfilling.
// `f` returns false when backfilling the cache should be stopped, true to continue.
//
// Returns block number until which we backfilled.
private backfill(
consensusInfo: Types.ConsensusInfo,
to: Types.BlockNumber,
f: Maybe<(i: Types.BlockNumber, consensusView: Types.ConsensusView) => boolean>,
own: Types.Address,
other: Types.Address,
): Types.BlockNumber {
for (const [height, consensusView] of consensusInfo) {
if (height >= to) {
continue;
}
this.initialiseConsensusViewByRef(consensusView, own, other);
const cont = f ? f(height, consensusView) : true;
if (!cont) {
break;
}
}
return to;
}
private updateConsensusInfo(
height: Types.BlockNumber,
addr: Types.Address,
voter: Types.Address,
data: Partial<Types.ConsensusDetail>,
) {
const consensusInfo = this.getState().consensusInfo;
const consensusView = this.initialiseConsensusView(consensusInfo, height, addr, voter);
for (const k in data) {
if (data.hasOwnProperty(k)) {
consensusView[addr][voter][k] = data[k];
}
}
this.updateState({consensusInfo});
}
}
+5 -1
View File
@@ -53,6 +53,10 @@ export default class App extends React.Component<{}, State> {
status: 'offline',
best: 0 as Types.BlockNumber,
finalized: 0 as Types.BlockNumber,
consensusInfo: new Array() as Types.ConsensusInfo,
authorities: new Array() as Types.Authorities,
authoritySetId: -1 as Types.AuthoritySetId,
sendFinality: false,
blockTimestamp: 0 as Types.Timestamp,
blockAverage: null,
timeDiff: 0 as Types.Milliseconds,
@@ -90,7 +94,7 @@ export default class App extends React.Component<{}, State> {
<div className="App">
<OfflineIndicator status={status} />
<Chains chains={chains} subscribed={subscribed} connection={this.connection} />
<Chain appState={this.state} settings={this.settings} pins={this.pins} />
<Chain appState={this.state} connection={this.connection} settings={this.settings} pins={this.pins} />
</div>
);
}
+52 -2
View File
@@ -2,6 +2,7 @@ import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from '@dotstats/
import { State, Update, Node } from './state';
import { PersistentSet } from './persist';
import { getHashData, setHashData } from './utils';
import { AfgHandling } from './AfgHandling';
const { Actions } = FeedMessage;
@@ -63,6 +64,7 @@ export class Connection {
private pingTimeout: NodeJS.Timer;
private pingSent: Maybe<Types.Timestamp> = null;
private resubscribeTo: Maybe<Types.ChainLabel> = getHashData().chain;
private resubscribeSendFinality: boolean = getHashData().tab === 'consensus';
private socket: WebSocket;
private state: Readonly<State>;
private readonly update: Update;
@@ -80,10 +82,26 @@ export class Connection {
this.socket.send(`subscribe:${chain}`);
}
public subscribeConsensus(chain: Types.ChainLabel) {
setHashData({ chain });
this.resubscribeSendFinality = true;
this.socket.send(`send-finality:${chain}`);
}
public unsubscribeConsensus(chain: Types.ChainLabel) {
setHashData({ chain });
this.resubscribeSendFinality = true;
this.socket.send(`no-more-finality:${chain}`);
}
public handleMessages = (messages: FeedMessage.Message[]) => {
const { nodes, chains } = this.state;
const ref = nodes.ref();
const updateState = (state: any) => { this.state = this.update(state); };
const getState = () => this.state;
const afg = new AfgHandling(updateState, getState);
for (const message of messages) {
switch (message.action) {
case Actions.FeedVersion: {
@@ -228,6 +246,34 @@ export class Connection {
break;
}
case Actions.AfgFinalized: {
const [nodeAddress, finalizedNumber, finalizedHash] = message.payload;
afg.receivedFinalized( nodeAddress, finalizedNumber, finalizedHash);
break;
}
case Actions.AfgReceivedPrevote: {
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
afg.receivedPre(nodeAddress, blockNumber, blockHash, voter, "prevote");
break;
}
case Actions.AfgReceivedPrecommit: {
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
afg.receivedPre(nodeAddress, blockNumber, blockHash, voter, "precommit");
break;
}
case Actions.AfgAuthoritySet: {
const [authoritySetId, authorities] = message.payload;
afg.receivedAuthoritySet(authoritySetId, authorities);
break;
}
default: {
break;
}
@@ -255,7 +301,8 @@ export class Connection {
if (this.state.subscribed) {
this.resubscribeTo = this.state.subscribed;
this.state = this.update({ subscribed: null });
this.resubscribeSendFinality = this.state.sendFinality;
this.state = this.update({ subscribed: null, sendFinality: false });
}
this.socket.addEventListener('message', this.handleFeedData);
@@ -313,7 +360,7 @@ export class Connection {
private autoSubscribe() {
const { subscribed, chains } = this.state;
const { resubscribeTo } = this;
const { resubscribeTo, resubscribeSendFinality } = this;
if (subscribed) {
return;
@@ -322,6 +369,9 @@ export class Connection {
if (resubscribeTo) {
if (chains.has(resubscribeTo)) {
this.subscribe(resubscribeTo);
if (resubscribeSendFinality) {
this.subscribeConsensus(resubscribeTo);
}
return;
}
}
@@ -1,9 +1,10 @@
import * as React from 'react';
import { Connection } from '../../Connection';
import { Types } from '@dotstats/common';
import { State as AppState } from '../../state';
import { formatNumber, secondsWithPrecision, getHashData } from '../../utils';
import { Tab } from './';
import { Tile, Ago, List, Map, Settings } from '../';
import { Tile, Ago, List, Map, Settings, Consensus } from '../';
import { PersistentObject, PersistentSet } from '../../persist';
import blockIcon from '../../icons/cube.svg';
@@ -13,14 +14,16 @@ import lastTimeIcon from '../../icons/watch.svg';
import listIcon from '../../icons/list-alt-regular.svg';
import worldIcon from '../../icons/location.svg';
import settingsIcon from '../../icons/settings.svg';
import consensusIcon from '../../icons/cube-alt.svg';
import './Chain.css';
export namespace Chain {
export type Display = 'list' | 'map' | 'settings';
export type Display = 'list' | 'map' | 'settings' | 'consensus';
export interface Props {
appState: Readonly<AppState>;
connection: Promise<Connection>;
settings: PersistentObject<AppState.Settings>;
pins: PersistentSet<Types.NodeName>;
}
@@ -43,6 +46,9 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
case 'settings':
display = 'settings';
break;
case 'consensus':
display = 'consensus';
break;
}
this.state = {
@@ -65,6 +71,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
<div className="Chain-tabs">
<Tab icon={listIcon} label="List" display="list" tab="" current={currentTab} setDisplay={this.setDisplay} />
<Tab icon={worldIcon} label="Map" display="map" tab="map" current={currentTab} setDisplay={this.setDisplay} />
<Tab icon={consensusIcon} label="Consensus" display="consensus" tab="consensus" current={currentTab} setDisplay={this.setDisplay} />
<Tab icon={settingsIcon} label="Settings" display="settings" tab="settings" current={currentTab} setDisplay={this.setDisplay} />
</div>
</div>
@@ -84,7 +91,11 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
return <Settings settings={this.props.settings} />;
}
const { appState, pins } = this.props;
const { appState, connection, pins } = this.props;
if (display === 'consensus') {
return <Consensus appState={appState} connection={connection} />;
}
return (
display === 'list'
@@ -0,0 +1,144 @@
.ConsensusList {
opacity: 0.0; /* the box should only show up once flexing has been applied */
}
.ConsensusList table {
border-spacing: 0px;
}
.flexContainerLargeRow {
display: flex;
align-items: stretch;
flex-direction: row;
opacity: 1.0;
}
.flexContainerLargeRow .firstInRow {
width: 100%
}
.flexContainerLargeRow .firstInRow .emptylegend,
.flexContainerLargeRow .firstInRow .nameLegend {
width: 99%;
flex-grow: 1000000000;
align-self: stretch;
}
.flexContainerSmallRow {
display: flex;
align-items: stretch;
flex-direction: row;
flex-wrap: wrap;
opacity: 1.0;
}
.flexContainerSmallRow div {
align-self: stretch;
flex: 1;
}
.flexContainerSmallRow table .legend {
width: 100%;
}
.ConsensusList {
margin-bottom: 2px;
}
.allRows {
width: 100%;
min-width: 1350px;
min-height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
.SmallRow {
float: left;
clear: both;
font-size: 8px !important;
width: 100%;
}
.SmallRow svg {
width: 14px;
height: 14px;
}
.hatching svg {
width: 12px !important;
height: 12px !important;
}
.SmallRow .hatching svg {
width: 10px !important;
height: 10px !important;
}
.matrixXLegend .Tooltip-container {
height: auto !important;
}
.legend {
text-align: center !important;
}
.nameLegend {
border-right: none;
border-bottom: 1px dotted #555;
}
.SmallRow .nameLegend {
display: none;
}
.SmallRow .finalizedInfo .Tooltip-container {
float: none;
display: inline-block !important;
vertical-align: middle;
}
.SmallRow .finalizedInfo {
min-height: 40px;
min-width: 40px;
}
.SmallRow .explicit,
.SmallRow .implicit {
height: 12px;
}
.SmallRow .finalizedInfo .explicit,
.SmallRow .finalizedInfo .implicit {
margin-right: 6px;
}
.nodeAddress {
margin-top: 4px;
}
.first_false .legend .nodeAddress,
.SmallRow .legend .nodeAddress,
th.finalizedInfo .Tooltip-container {
float: none !important;
text-align: center !important;
}
.noStretchOnLastRow::after {
content: '';
flex-grow: 1000000000;
}
.flexContainerLargeRow .noStretchOnLastRow .firstInRow table {
width: auto !important;
}
.flexContainerLargeRow .noStretchOnLastRow .firstInRow .emptylegend {
width: auto !important;
}
.flexContainerLargeRow .noStretchOnLastRow .firstInRow {
width: auto !important;
}
@@ -0,0 +1,247 @@
import * as React from 'react';
import { Types } from '@dotstats/common';
import { Connection } from '../../Connection';
import Measure, {BoundingRect, ContentRect} from 'react-measure';
import { ConsensusBlock } from './';
import { State as AppState } from '../../state';
import './Consensus.css';
export namespace Consensus {
export interface Props {
appState: Readonly<AppState>;
connection: Promise<Connection>;
}
export interface State {
dimensions: BoundingRect;
largeBlockWithLegend: BoundingRect,
largeBlock: BoundingRect,
countBlocksInLargeRow: number,
largeRowsAddFlexClass: boolean,
smallBlock: BoundingRect,
smallBlocksRows: number,
countBlocksInSmallRow: number,
smallRowsAddFlexClass: boolean,
}
}
export class Consensus extends React.Component<Consensus.Props, {}> {
public state = {
// entire area available for rendering the visualization
dimensions: { width: -1, height: -1 } as BoundingRect,
largeBlockWithLegend: { width: -1, height: -1 } as BoundingRect,
largeBlock: { width: -1, height: -1 } as BoundingRect,
countBlocksInLargeRow: 2,
largeRowsAddFlexClass: false,
smallBlock: { width: -1, height: -1 } as BoundingRect,
smallBlocksRows: 1,
countBlocksInSmallRow: 1,
smallRowsAddFlexClass: false,
};
public componentDidMount() {
if (this.props.appState.subscribed != null) {
const chain = this.props.appState.subscribed;
this.subscribeConsensus(chain);
}
}
public componentWillUnmount() {
if (this.props.appState.subscribed != null) {
const chain = this.props.appState.subscribed;
this.unsubscribeConsensus(chain);
}
}
public largeBlocksSizeDetected(state: Consensus.State): boolean {
const countBlocks = Object.keys(this.props.appState.consensusInfo).length;
if (countBlocks === 1) {
return state.largeBlockWithLegend.width > -1 && state.largeBlockWithLegend.height > -1;
}
// if there is more than one block then the size of the first block (with legend)
// will be different from the succeeding blocks (without legend)
return state.largeBlockWithLegend.width > -1 && state.largeBlockWithLegend.height > -1 &&
state.largeBlock.width > -1 && state.largeBlock.height > -1;
}
public smallBlocksSizeDetected(state: Consensus.State): boolean {
return state.smallBlock.width > -1 && state.largeBlockWithLegend.height > -1;
}
public calculateBoxCount(wasResized: boolean) {
// if the css class for flexing has already been added we don't calculate
// any box measurements then, because the box sizes would be skewed then.
if ((wasResized || this.state.largeRowsAddFlexClass === false) && this.largeBlocksSizeDetected(this.state)) {
// we need to add +2 because of the last block which doesn't contain a border.
let countBlocks = (this.state.dimensions.width - this.state.largeBlockWithLegend.width + 2) /
(this.state.largeBlock.width + 2);
// +1 because the firstRect was subtracted above and needs to be counted back in.
// default count is 2 because we need two blocks to measure properly (one with legend
// and one without. these measures are necessary to calculate the number of blocks
// which fit.
countBlocks = Math.floor(countBlocks + 1) < 1 ? 2 : Math.floor(countBlocks + 1);
this.setState({largeRowsAddFlexClass: true, countBlocksInLargeRow: countBlocks });
}
if ((wasResized || this.state.smallRowsAddFlexClass === false) && this.smallBlocksSizeDetected(this.state)) {
const howManyRows = 2;
const heightLeft = this.state.dimensions.height - (this.state.largeBlock.height * howManyRows);
let smallBlocksRows = heightLeft / this.state.smallBlock.height;
smallBlocksRows = smallBlocksRows < 1 ? 1 : Math.floor(smallBlocksRows);
let countBlocksInSmallRow = this.state.dimensions.width / this.state.smallBlock.width;
countBlocksInSmallRow = countBlocksInSmallRow < 1 ? 1 : Math.floor(countBlocksInSmallRow);
this.setState({ smallRowsAddFlexClass: true, countBlocksInSmallRow, smallBlocksRows });
}
}
public render() {
this.calculateBoxCount(false);
const lastBlocks = this.props.appState.consensusInfo;
let from = 0;
let to = this.state.countBlocksInLargeRow;
const firstLargeRow = this.getLargeRow(lastBlocks.slice(from, to), 0);
from = to;
to = to + this.state.countBlocksInLargeRow;
const secondLargeRow = this.getLargeRow(lastBlocks.slice(from, to), 1);
from = to;
to = to + (this.state.smallBlocksRows * this.state.countBlocksInSmallRow);
const smallRow = this.getSmallRow(lastBlocks.slice(from, to));
return (
<React.Fragment>
<Measure bounds={true} onResize={this.handleOnResize}>
{({ measureRef }) => (
<div className="allRows" ref={measureRef}>
{firstLargeRow}
{secondLargeRow}
{smallRow}
</div>
)}
</Measure>
</React.Fragment>
);
}
private handleOnResize = (contentRect: ContentRect) => {
this.setState({ dimensions: contentRect.bounds as BoundingRect });
this.calculateBoxCount(true);
};
private getAuthorities(): Types.Authority[] {
// find the node for each of these authority addresses
if (this.props.appState.authorities == null) {
return [];
}
return this.props.appState.authorities.map(address => {
const node2 = this.props.appState.nodes.sorted().filter(node => node.address === address)[0];
if (!node2) {
return {Address: address, NodeId: null, Name: null} as Types.Authority;
}
return {Address: address, NodeId: node2.id, Name: node2.name} as Types.Authority;
});
}
private getLargeRow(blocks: Types.ConsensusInfo, id: number) {
const largeBlockSizeChanged = (isFirstBlock: boolean, rect: BoundingRect) => {
if (this.largeBlocksSizeDetected(this.state)) {
return;
}
if (isFirstBlock) {
this.setState({largeBlockWithLegend: {width: rect.width, height: rect.height} });
} else {
this.setState({largeBlock: {width: rect.width, height: rect.height} });
}
};
const stretchLastRowMajor = blocks.length < this.state.countBlocksInLargeRow ?
'noStretchOnLastRow' : '';
const flexClass = this.state.largeRowsAddFlexClass ? 'flexContainerLargeRow' : '';
return <div
className={`ConsensusList LargeRow ${flexClass} ${stretchLastRowMajor}`}
key={`consensusList_${id}`}>
{blocks.map((item, i) => {
const [height, consensusView] = item;
return <ConsensusBlock
changeBlocks={largeBlockSizeChanged}
firstInRow={i === 0}
lastInRow={false}
compact={false}
key={height}
height={height}
consensusView={consensusView}
authorities={this.getAuthorities()}
authoritySetId={this.props.appState.authoritySetId}
/>;
})}
</div>;
}
private getSmallRow(blocks: Types.ConsensusInfo) {
const smallBlockSizeChanged = (isFirstBlock: boolean, rect: BoundingRect) => {
if (this.smallBlocksSizeDetected(this.state)) {
return;
}
const dimensionsChanged = this.state.smallBlock.height !== rect.height &&
this.state.smallBlock.width !== rect.width;
if (dimensionsChanged) {
this.setState({ smallBlock: {width: rect.width, height: rect.height} });
}
};
const stretchLastRow =
blocks.length < this.state.countBlocksInSmallRow * this.state.smallBlocksRows ?
'noStretchOnLastRow' : '';
const classes = `ConsensusList SmallRow ${this.state.smallRowsAddFlexClass ? 'flexContainerSmallRow' : ''} ${stretchLastRow}`;
return <div className={classes} key="smallRow">
{blocks.map((item, i) => {
const [height, consensusView] = item;
let lastInRow = (i+1) % this.state.countBlocksInSmallRow === 0 ? true : false;
if (lastInRow && i === 0) {
// should not be marked as last one in row if it's the very first in row
lastInRow = false;
}
return <ConsensusBlock
changeBlocks={smallBlockSizeChanged}
firstInRow={i === 0}
lastInRow={lastInRow}
compact={true}
key={height}
height={height}
consensusView={consensusView}
authorities={this.getAuthorities()}
authoritySetId={this.props.appState.authoritySetId} />;
})
}
</div>;
}
private async subscribeConsensus(chain: Types.ChainLabel) {
const connection = await this.props.connection;
connection.subscribeConsensus(chain);
}
private async unsubscribeConsensus(chain: Types.ChainLabel) {
const connection = await this.props.connection;
connection.unsubscribeConsensus(chain);
}
}
@@ -0,0 +1,201 @@
.BlockConsensusMatrice {
background-color: #222;
font-family: monospace, sans-serif;
border-spacing: 0px;
border-right: 2px solid lightgrey;
border-bottom: 1px solid #999;
}
.LargeRow .BlockConsensusMatrice:last-child {
border-right: none;
}
.SmallRow .lastInRow {
clear: right;
width: 99%;
page-break-after: always;
}
.BlockConsensusMatrice th {
font-weight: normal;
border-bottom: 1px dashed #999;
}
.finalizedInfo, .legend {
border-bottom: 1px dotted #555555;
}
.finalizedInfo {
white-space: nowrap;
}
.finalizedInfo .Tooltip-container {
display: inline-block;
white-space: nowrap;
vertical-align: middle;
}
.BlockConsensusMatrice .matrice {
width: 28px;
height: 28px;
}
.BlockConsensusMatrice .matrice {
font-weight: normal;
border-right: 1px dotted #555555;
border-bottom: 1px dotted #555;
}
.BlockConsensusMatrice tr .matrice:last-child {
border-right: none;
}
.BlockConsensusMatrice .matrixXLegend {
text-align: center;
border-right: 1px dotted #555555;
}
.BlockConsensusMatrice .matrixXLegend:last-child {
border-right: none;
}
.matrice {
text-align: center !important;
min-width: 35px;
}
.SmallRow .matrixXLegend,
.SmallRow .matrice {
min-width: 26px;
min-height: 26px;
}
.finalizedInfo {
text-align: center !important;
}
.SmallRow .finalizedInfo {
min-width: 40px;
}
.finalizedInfo {
text-align: right;
border-right: 1px dashed #999;
min-width: 48px;
}
.finalizedInfo .Tooltip-container {
float: none;
}
.explicit {
fill: #E70E81;
}
.nodeName {
float: left;
padding-right: 10px;
padding-top: 4px;
}
.flexContainerLargeRow .firstInRow .nodeContent {
white-space: nowrap;
}
.flexContainerLargeRow .firstInRow .nodeName {
display: inline-block !important;
float: none !important;
vertical-align: middle;
margin-bottom: 3px;
}
.flexContainerLargeRow .firstInRow .nodeAddress {
display: inline-block !important;
float: none !important;
vertical-align: middle;
margin-right: 3px;
}
.legend {
border-right: 1px solid #999;
white-space: nowrap;
}
.first_false .nodeName {
display: none;
}
.legend .nodeAddress {
float: right;
}
.Row {
color: #999;
cursor: pointer;
}
.Row th, .Row td {
text-align: left;
padding: 2px;
}
.Row td {
position: relative;
}
.Row .Row-truncate {
position: absolute;
left: 0;
right: 0;
top: 0;
padding: inherit;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.Row .Row-Tooltip {
position: initial;
padding: inherit;
}
.Row:hover {
background-color: #161616;
}
.nodeAddress svg {
cursor: pointer;
}
.nodeAddress svg:hover {
transform: scale(2);
}
.matrice .Icon ~ .Icon {
margin-left: -4px;
}
.SmallRow .matrice .Prevote svg {
margin-left: 3px;
margin-bottom: -11px;
}
.SmallRow .matrice .Precommit svg {
margin-left: -1px;
margin-top: -6px;
margin-bottom: 0px;
}
.jdenticonPlaceholder {
width: 28px;
}
.SmallRow .jdenticonPlaceholder {
width: 14px;
float: right;
}
.even {
background-color: #333;
}
@@ -0,0 +1,230 @@
import * as React from 'react';
import Measure, {BoundingRect, ContentRect} from 'react-measure';
import { Types, Maybe } from '@dotstats/common';
import Identicon from 'polkadot-identicon';
import { Icon, Tooltip } from '../';
import Jdenticon from './Jdenticon';
import checkIcon from '../../icons/check.svg';
import finalizedIcon from '../../icons/finalized.svg';
import hatchingIcon from '../../icons/hatching.svg';
import './ConsensusBlock.css';
export namespace ConsensusBlock {
export interface Props {
authorities: Types.Authority[];
authoritySetId: Types.AuthoritySetId;
height: Types.BlockNumber;
firstInRow: boolean;
lastInRow: boolean;
compact: boolean;
consensusView: Types.ConsensusView;
changeBlocks: (first: boolean, boundsRect: BoundingRect) => void;
}
}
export class ConsensusBlock extends React.Component<ConsensusBlock.Props, {}> {
public render() {
const finalizedByWhom = this.props.authorities.filter(authority => this.isFinalized(authority));
const ratio = finalizedByWhom.length + '/' + this.props.authorities.length;
const tooltip = `${ratio} authorities finalized this block. Authority Set Id: ${this.props.authoritySetId}.`;
let titleFinal = <span>{ratio}</span>;
const majorityFinalized = finalizedByWhom.length / this.props.authorities.length >= 2/3;
if (majorityFinalized && !this.props.compact) {
titleFinal = <span>FINAL</span>;
} else if (majorityFinalized && this.props.compact) {
const hash = this.getFinalizedHash(finalizedByWhom[0]);
titleFinal =
<Tooltip text={'Block hash: ' + hash ? String(hash) : ''} copy={true}>
<Jdenticon hash={hash ? String(hash) : ''} size={this.props.compact ? '14px' : '28px'}/>
</Tooltip>;
}
const handleOnResize = (contentRect: ContentRect) => {
this.props.changeBlocks(this.props.firstInRow, contentRect.bounds as BoundingRect);
};
return (<Measure bounds={true} onResize={handleOnResize}>{({ measureRef }) => (
<div
className={`BlockConsensusMatrice ${this.props.firstInRow ? 'firstInRow' : ''} ${this.props.lastInRow ? 'lastInRow' : ''}`}
key={'block_' + this.props.height}>
<table ref={measureRef}>
<thead>
<tr className="Row">
{this.props.firstInRow && !this.props.compact ?
<th className="emptylegend">&nbsp;</th> : ''}
<th className="legend">
<Tooltip text={`Block number: ${this.props.height}`}>
{this.displayBlockNumber()}
</Tooltip>
</th>
<th className='finalizedInfo'>
<Tooltip text={tooltip}>{titleFinal}</Tooltip>
</th>
{this.props.authorities.map(authority =>
<th
className="matrixXLegend"
key={`${this.props.height}_matrice_x_${authority.Address}`}>
{this.getAuthorityContent(authority)}
</th>)}
</tr>
</thead>
<tbody>
{this.props.authorities.map((authority, row) =>
this.renderMatriceRow(authority, this.props.authorities, row))}
</tbody>
</table>
</div>)}
</Measure>);
}
private displayBlockNumber(): string {
const blockNumber = String(this.props.height);
return blockNumber.length > 2 ?
'…' + blockNumber.substr(blockNumber.length - 2, blockNumber.length) : blockNumber;
}
private isFinalized(authority: Types.Authority): boolean {
if (!authority || authority.NodeId == null || authority.Address == null) {
return false;
}
const { Address: addr } = authority;
const consensus = this.props.consensusView;
return consensus != null && addr in consensus && addr in consensus[addr]
&& consensus[addr][addr].Finalized === true;
}
private getFinalizedHash(authority: Types.Authority): Maybe<Types.BlockHash> {
if (this.isFinalized(authority)) {
const { Address: addr } = authority;
return this.props.consensusView[addr][addr].FinalizedHash;
}
return null;
}
private renderMatriceRow(authority: Types.Authority, authorities: Types.Authority[], row: number): JSX.Element {
let finalizedInfo = <Tooltip text="No information available yet.">&nbsp;</Tooltip>;
let finalizedHash;
if (authority.NodeId != null && this.isFinalized(authority)) {
const matrice = this.props.consensusView[authority.Address][authority.Address];
finalizedInfo = matrice.ImplicitFinalized ?
<Tooltip text={`${authority.Name} finalized this block in ${matrice.ImplicitPointer}`}>
<Icon className="implicit" src={finalizedIcon} alt="" />
</Tooltip>
:
<Tooltip text={`${authority.Name} finalized this block in this block`}>
<Icon className="explicit" src={finalizedIcon} alt="" />
</Tooltip>
finalizedHash = matrice.FinalizedHash ?
<Tooltip text={`Block hash: ${matrice.FinalizedHash}`} copy={true}>
<Jdenticon hash={matrice.FinalizedHash} size="28px"/>
</Tooltip> : <div className="jdenticonPlaceholder">&nbsp;</div>;
}
const name = authority.Name ? <span>{authority.Name}</span> : <em>no name received yet</em>;
const firstName = this.props.firstInRow ? <td className="nameLegend">{name}</td> : '';
return <tr className="Row">
{firstName}
<td className="legend">{this.getAuthorityContent(authority)}</td>
<td className="finalizedInfo">{finalizedInfo}{finalizedHash}</td>
{
authorities.map((columnNode, column) => {
const evenOdd = ((row % 2) + column) % 2 === 0 ? 'even' : 'odd';
return <td key={'matrice_' + authority.Address + '_' + columnNode.Address}
className={`matrice ${evenOdd}`}>{this.getMatriceContent(authority, columnNode)}</td>
})
}
</tr>;
}
private getAuthorityContent(authority: Types.Authority): JSX.Element {
return <div className="nodeContent">
<div className="nodeAddress">
<Tooltip text={authority.Address} copy={true}>
<Identicon account={authority.Address} size={this.props.compact ? 14 : 28} />
</Tooltip>
</div>
</div>;
}
private format(consensusDetail: Types.ConsensusDetail): string {
const txt = [];
if (consensusDetail.Prevote) {
txt.push('Prevote on this chain in this block');
} else if (consensusDetail.ImplicitPrevote) {
txt.push('Prevote on this chain in block ' + consensusDetail.ImplicitPointer);
}
if (consensusDetail.Precommit) {
txt.push('Precommit on this chain in this block');
} else if (consensusDetail.ImplicitPrecommit) {
txt.push('Precommit on this chain in block ' + consensusDetail.ImplicitPointer);
}
if (consensusDetail.Finalized && consensusDetail.ImplicitFinalized) {
txt.push('Finalized this chain in block ' + consensusDetail.ImplicitPointer);
} else if (consensusDetail.Finalized && !consensusDetail.ImplicitFinalized) {
txt.push('Finalized this chain in this block');
}
return txt.join(', '); // + JSON.stringify((consensusDetail));
}
private getMatriceContent(rowAuthority: Types.Authority, columnAuthority: Types.Authority) {
const consensusInfo = this.props.consensusView &&
rowAuthority.Address &&
rowAuthority.Address in this.props.consensusView &&
columnAuthority.Address in this.props.consensusView[rowAuthority.Address] ?
this.props.consensusView[rowAuthority.Address][columnAuthority.Address] : null;
let tooltipText = consensusInfo ?
rowAuthority.Name + ' has seen this of ' + columnAuthority.Name + ': ' +
this.format(consensusInfo) : 'No information available yet.';
if (rowAuthority.Address === columnAuthority.Address) {
tooltipText = 'Self-referential.';
}
const prevote = consensusInfo && consensusInfo.Prevote;
const implicitPrevote = consensusInfo && consensusInfo.ImplicitPrevote;
const precommit = consensusInfo && consensusInfo.Precommit;
const implicitPrecommit = consensusInfo && consensusInfo.ImplicitPrecommit;
if (rowAuthority.Address !== columnAuthority.Address) {
let statPrevote;
let statPrecommit;
if (implicitPrevote) {
statPrevote = <Icon src={checkIcon} className="implicit" alt="Implicit Prevote"/>;
}
if (implicitPrecommit) {
statPrecommit = <Icon src={checkIcon} className="implicit" alt="Implicit Precommit"/>;
}
if (prevote) {
statPrevote = <Icon src={checkIcon} className="explicit" alt="Prevote"/>;
}
if (precommit) {
statPrecommit = <Icon src={checkIcon} className="explicit" alt="Precommit"/>;
}
const stat = [statPrevote, statPrecommit];
return <Tooltip text={tooltipText}>{stat}</Tooltip>
} else {
return <Tooltip text={tooltipText}>
<Icon src={hatchingIcon} className="hatching" alt=""/>
</Tooltip>
}
}
}
@@ -0,0 +1,9 @@
.Jdenticon {
cursor: pointer;
vertical-align: middle;
background-color: #fff;
}
.Jdenticon:hover {
transform: scale(2);
}
@@ -0,0 +1,43 @@
import * as React from 'react';
import './Jdenticon.css';
export interface Props {
hash: string,
size: string
}
class Jdenticon extends React.Component<Props, {}> {
private element = null;
public componentDidUpdate() {
const jdenticon = (window as any).jdenticon;
if (jdenticon) {
jdenticon.update(this.element);
}
}
public componentDidMount() {
const jdenticon = (window as any).jdenticon;
if (jdenticon) {
jdenticon.update(this.element);
}
}
public render() {
const { hash, size } = this.props;
return <svg
className="Jdenticon"
ref={element => this.handleRef(element)}
width={size}
height={size}
data-jdenticon-value={hash}
/>;
}
private handleRef(element: any) {
this.element = element;
}
}
export default Jdenticon;
@@ -0,0 +1,2 @@
export * from './Consensus';
export * from './ConsensusBlock';
@@ -27,6 +27,7 @@ export class Settings extends React.Component<Settings.Props, {}> {
return (
<div className="Settings">
<div className="Settings-category">
<h1>List View</h1>
<h2>Visible Columns</h2>
{
Row.columns
@@ -3,6 +3,7 @@ export * from './Chain';
export * from './List';
export * from './Map';
export * from './Settings';
export * from './Consensus';
export * from './Icon';
export * from './Tile';
export * from './Ago';
@@ -0,0 +1,9 @@
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="12"
height="16"
viewBox="0 0 12 16"
version="1.1">
<path fill-rule="evenodd" d="M 4.5866667,8.6733331 V 12.86 h -2 V 3.1399999 H 9.4133332 V 4.8066665 H 4.5866667 v 2.2 h 4.2533332 v 1.6666666 z" />
</svg>

After

Width:  |  Height:  |  Size: 314 B

+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1">
<g
id="layer1"
transform="translate(0,-197)">
<g
id="g4832"
style="fill:#636363;fill-opacity:1"
transform="matrix(2.5100945,0,0,2.5100945,-166.26883,-167.37398)">
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="206.76236"
x="-218.73599"
height="5"
width="162.6855"
id="rect65" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="224.44003"
x="-236.41367"
height="5"
width="162.6855"
id="rect65-3" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="242.11771"
x="-254.09134"
height="5"
width="162.6855"
id="rect65-3-6" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="189.08469"
x="-201.05832"
height="5"
width="162.6855"
id="rect65-3-6-7" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="171.40703"
x="-183.38066"
height="5"
width="162.6855"
id="rect65-3-6-7-5" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="153.72935"
x="-165.70299"
height="5"
width="162.6855"
id="rect65-3-6-7-5-3" />
<rect
transform="rotate(-45)"
style="fill:#636363;fill-opacity:1;stroke-width:0.21728761"
y="259.79538"
x="-271.76901"
height="5"
width="162.6855"
id="rect65-3-6-5" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+16 -1
View File
@@ -19,6 +19,7 @@ export class Node {
}
public readonly id: Types.NodeId;
public readonly address: Types.Address;
public readonly name: Types.NodeName;
public readonly implementation: Types.NodeImplementation;
public readonly version: Types.NodeVersion;
@@ -48,6 +49,7 @@ export class Node {
public city: Maybe<Types.City>;
private readonly subscriptions = new Set<(node: Node) => void>();
private readonly subscriptionsConsensus = new Set<(node: Node) => void>();
constructor(
pinned: boolean,
@@ -58,12 +60,13 @@ export class Node {
blockDetails: Types.BlockDetails,
location: Maybe<Types.NodeLocation>
) {
const [name, implementation, version, validator, networkId] = nodeDetails;
const [name, address, implementation, version, validator, networkId] = nodeDetails;
this.pinned = pinned;
this.id = id;
this.name = name;
this.address = address;
this.implementation = implementation;
this.version = version;
this.validator = validator;
@@ -148,6 +151,14 @@ export class Node {
this.subscriptions.delete(handler);
}
public subscribeConsensus(handler: (node: Node) => void) {
this.subscriptionsConsensus.add(handler);
}
public unsubscribeConsensus(handler: (node: Node) => void) {
this.subscriptionsConsensus.delete(handler);
}
private trigger() {
for (const handler of this.subscriptions.values()) {
handler(this);
@@ -182,6 +193,10 @@ export interface State {
status: 'online' | 'offline' | 'upgrade-requested';
best: Types.BlockNumber;
finalized: Types.BlockNumber;
consensusInfo: Types.ConsensusInfo;
authorities: Types.Address[];
authoritySetId: Types.AuthoritySetId;
sendFinality: boolean;
blockTimestamp: Types.Timestamp;
blockAverage: Maybe<Types.Milliseconds>;
timeDiff: Types.Milliseconds;
+153 -24
View File
@@ -18,15 +18,61 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/runtime@^7.2.0":
version "7.4.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc"
integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.4.0":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
dependencies:
regenerator-runtime "^0.13.2"
"@fnando/sparkline@maciejhirsz/sparkline":
version "0.3.10"
resolved "https://codeload.github.com/maciejhirsz/sparkline/tar.gz/2bdb002b171436be078a84f1e4e617a44ef1fb42"
"@polkadot/util@^0.41.1":
version "0.41.1"
resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-0.41.1.tgz#b67762a0a7408138d843adc7240944e704e819e7"
integrity sha512-1mFHxxdXyRgeFk0ygfJklzsf6HiEEMikrpUgr+3Dw5S7KromjPD8EbWkpcZQZ3Png5PTwGyjrx7MYY4Ajiu5xQ==
dependencies:
"@babel/runtime" "^7.4.0"
"@types/bn.js" "^4.11.4"
"@types/deasync" "^0.1.0"
"@types/ip-regex" "^3.0.0"
bn.js "^4.11.8"
camelcase "^5.2.0"
chalk "^2.4.2"
ip-regex "^4.0.0"
moment "^2.24.0"
"@polkadot/wasm-crypto@^0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-0.5.1.tgz#3d5734d6dc2f5174a411689f78538287d21bd710"
integrity sha512-8h7uz85bvLVirtbxLkELr9H25oqAfgnQuDP6FP3QJeag7VfMtVvRF5U2JWGgsiUSiRg+UJnTuCleOSDnageRmg==
"@polkadot/wasm-schnorrkel@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@polkadot/wasm-schnorrkel/-/wasm-schnorrkel-0.3.1.tgz#08287848f769dee4955517348905888bd289f277"
integrity sha512-2J/lNZe7oDWvzUbsEfvKfiiAvBIBbIsppeBGahwSg2Y+cLweQgThXGizPpQoPo0tHxgMKdTi6jhPu7tLzCW/cA==
"@tanem/svg-injector@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@tanem/svg-injector/-/svg-injector-1.2.1.tgz#3120e90246d0eb3c4fc6c61586a6f028a3c658ae"
integrity sha512-mA5Q5ulPoGQ+e08Vts1R6xw2QU0BKEnMH/KcqoYoS7Gk6imvMTpyFPeu1g+NOZObSIoAzA3/kRzY8m96cEBA2A==
"@types/bn.js@^4.11.4":
version "4.11.5"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.5.tgz#40e36197433f78f807524ec623afcf0169ac81dc"
integrity sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==
dependencies:
"@types/node" "*"
"@types/body-parser@*":
version "1.17.0"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
@@ -42,6 +88,11 @@
dependencies:
"@types/node" "*"
"@types/deasync@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@types/deasync/-/deasync-0.1.0.tgz#b2bd6c3fcde3cf67b8be4c2f70136ba8f157b45a"
integrity sha512-jxUH53LtGvbIL3TX2hD/XQuAgYJeATtx9kDXq5XtCZrWQABsiCQPjWi/KQXECUF+p9FuR6/tawnEDjXlEr4rFA==
"@types/events@*":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
@@ -65,6 +116,11 @@
"@types/express-serve-static-core" "*"
"@types/serve-static" "*"
"@types/ip-regex@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/ip-regex/-/ip-regex-3.0.0.tgz#f06748a44dd47810bb24555958b4b410cfe5abab"
integrity sha512-4j26qYCikXbIaypgqdGQhhIvYfTR+40i/05jVQB8oc57RbROEkBocdIUZO7fOhyqJsx7WRY36ySW8hoUZn7Fgw==
"@types/jest@^23.0.2":
version "23.3.12"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.12.tgz#7e0ced251fa94c3bc2d1023d4b84b2992fa06376"
@@ -102,6 +158,13 @@
dependencies:
"@types/react" "*"
"@types/react-measure@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@types/react-measure/-/react-measure-2.0.5.tgz#c1d304e3cab3a1c393342bf377b040628e6c29a8"
integrity sha512-T1Bpt8FlWbDhoInUaNrjTOiVRpRJmrRcqhFJxLGBq1VjaqBLHCvUPapgdKMWEIX4Oqsa1SSKjtNkNJGy6WAAZg==
dependencies:
"@types/react" "*"
"@types/react-svg@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/react-svg/-/react-svg-3.0.0.tgz#ebbd0a095339ba20d9ba1d8fb3441eef9aeb5d11"
@@ -1386,7 +1449,7 @@ bluebird@^3.4.7, bluebird@^3.5.1:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
@@ -1620,6 +1683,14 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@@ -1710,6 +1781,11 @@ camelcase@^4.0.0, camelcase@^4.1.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
camelcase@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-api@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
@@ -1730,6 +1806,11 @@ caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.300008
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000929.tgz#7b391b781a9c3097ecc39ea053301aea8ea16317"
integrity sha512-n2w1gPQSsYyorSVYqPMqbSaz1w7o9ZC8VhOEGI9T5MfGDzp7sbopQxG6GaQmYsaq13Xfx/mkxJUWC1Dz3oZfzw==
canvas-renderer@~2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/canvas-renderer/-/canvas-renderer-2.1.1.tgz#d91fe9511ab48056ff9fa8a04514bede76535f55"
integrity sha512-/V0XetN7s1Mk3NO7x2wxPZYv0pLMQtGAhecuOuKR88beiYCUle1AbCcFZNLu+4NVzi9RVHS0rXtIgzPEaKidLw==
capture-exit@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f"
@@ -1771,7 +1852,7 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1:
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -3642,6 +3723,11 @@ get-caller-file@^1.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-node-dimensions@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz#fb7b4bb57060fb4247dd51c9d690dfbec56b0823"
integrity sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==
get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -4237,6 +4323,11 @@ ip-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-3.0.0.tgz#0a934694b4066558c46294244a23cc33116bf732"
integrity sha512-T8wDtjy+Qf2TAPDQmBp0eGKJ8GavlWlUnamr3wRn6vvdZlKVuJXXMlSncYFRYgVHOM3If5NR1H4+OvVQU9Idvg==
ip-regex@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.1.0.tgz#5ad62f685a14edb421abebc2fff8db94df67b455"
integrity sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==
ip-validator@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/ip-validator/-/ip-validator-0.0.1.tgz#659eb6001d38c35beab51455a2f751b843299eb5"
@@ -4730,6 +4821,13 @@ istanbul-reports@^1.5.1:
dependencies:
handlebars "^4.0.3"
jdenticon@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jdenticon/-/jdenticon-2.1.1.tgz#f9288e87b3466e2c5f05948c68db58e1c9e1b800"
integrity sha512-bFD210JMaK2RMHGSkdDXqdQzasMrVIGDfxUMXD/Uwq2t7g9Njb4T6kms5TrocsLU0CDmQCoB0laGJ6JETiprsg==
dependencies:
canvas-renderer "~2.1.1"
jest-changed-files@^22.2.0:
version "22.4.3"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.4.3.tgz#8882181e022c38bd46a2e4d18d44d19d90a90fb2"
@@ -6050,6 +6148,11 @@ mock-socket@^8.0.2:
dependencies:
url-parse "^1.2.0"
moment@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
moo@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e"
@@ -6454,27 +6557,33 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
oo7-react@^0.8.10:
version "0.8.10"
resolved "https://registry.yarnpkg.com/oo7-react/-/oo7-react-0.8.10.tgz#c04bd418d87f3b5bd32949cefb116eee5a4bb86c"
integrity sha512-kLP9umZt5StkWlZ2TahDIMTg9umsPzMFeXoIi5ClujwrQD+2uRqLaYWiPDprzYlSMpOZgQ0oqZtBW6tUCdG8GA==
oo7-react@^0.8.13:
version "0.8.13"
resolved "https://registry.yarnpkg.com/oo7-react/-/oo7-react-0.8.13.tgz#fbc63eac0397d72a1a5d768481fbbda533503e2e"
integrity sha512-1WoJf/pYDiZAkd+rilH9xs6FR6k4LKck/uCPVzClTnp/uFvsR8pxlbTHvNPvOqspxob76qSbstUv7IVdO/TQ7Q==
dependencies:
oo7 "^0.7.9"
oo7 "^0.7.12"
react "^16.5.2"
oo7-substrate@^0.4.12:
version "0.4.12"
resolved "https://registry.yarnpkg.com/oo7-substrate/-/oo7-substrate-0.4.12.tgz#f27ffa10b0143e75ba2677ddbebbaf9aa5096078"
integrity sha512-oz0UePdDViE8y9oLgWBImwhIRkElcaqHmG9dIAnHd12O1FPWGos1LHeDSKVblXfvtiAs18n2OMe5JJTNBGd6yQ==
oo7-substrate@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/oo7-substrate/-/oo7-substrate-0.8.0.tgz#690a6ec145287ae0cbb5647146f3ce76a8a28ff3"
integrity sha512-ltglsNXZNmiDsJ5q4LYx0tf0SN0CVjW5fSifwrBRe/objxG2fYXogZ4eiNdsLJrTddvskv+3o5JpvFw/D3PCUA==
dependencies:
"@polkadot/util" "^0.41.1"
"@polkadot/wasm-crypto" "^0.5.1"
"@polkadot/wasm-schnorrkel" "^0.3.1"
base-x "^3.0.4"
bip39 "^2.5.0"
blakejs "^1.1.0"
bs58 "^4.0.1"
buffer "^5.2.1"
change-case "^3.0.2"
isomorphic-fetch "^2.2.1"
isomorphic-ws "^4.0.1"
oo7 "^0.7.9"
jdenticon "^2.1.1"
oo7 "^0.7.12"
pbkdf2 "^3.0.17"
ss58 "^1.0.2"
text-encoding "^0.7.0"
text-encoding-polyfill "^0.6.7"
@@ -6482,10 +6591,10 @@ oo7-substrate@^0.4.12:
ws "^6.1.2"
xxhashjs "^0.2.2"
oo7@^0.7.9:
version "0.7.9"
resolved "https://registry.yarnpkg.com/oo7/-/oo7-0.7.9.tgz#ea9c14121fd3a24934b12d58b5f2a185a9609059"
integrity sha512-QcK4x8xY+rZeBBqS1HDe+kyf0g1XEdiqXcEIJl7dBxau+ICU8bGdcJ+d+POMj8/fRH6o9oyaIe2QujytxzERLg==
oo7@^0.7.12:
version "0.7.12"
resolved "https://registry.yarnpkg.com/oo7/-/oo7-0.7.12.tgz#bd7a01ba4e815c1fcde9bea2a9a8226d06ec781e"
integrity sha512-nVmxwLIiVCY4Aid6/+p2ujJ2mj3yh15zQ3xSl3UPui7U37DJJfD0KmuMjp+XOWH1HxTkKhIKDgR34eEhFHNxgA==
opn@5.2.0:
version "5.2.0"
@@ -6773,7 +6882,7 @@ path-type@^2.0.0:
dependencies:
pify "^2.0.0"
pbkdf2@^3.0.3, pbkdf2@^3.0.9:
pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9:
version "3.0.17"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
@@ -6828,15 +6937,15 @@ pn@^1.1.0:
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
polkadot-identicon@^1.1.36:
version "1.1.36"
resolved "https://registry.yarnpkg.com/polkadot-identicon/-/polkadot-identicon-1.1.36.tgz#ef14ebdc788367b20acf05a1613c806211fd44d0"
integrity sha512-wDOpqHG39t41R0mHj7PqMHnZGRgPfjtxMMBGaKTO05cp+xwOtamDUdx2e0LaG4KYZaq8KbkDXK9XKdASTGuDKw==
polkadot-identicon@^1.1.45:
version "1.1.45"
resolved "https://registry.yarnpkg.com/polkadot-identicon/-/polkadot-identicon-1.1.45.tgz#9c8f5644be15f33e47200120e81cca068e4d26ed"
integrity sha512-UFdxnfvQPHnbKadKibbCGsFrO6XfOy+OeVf+yAyp24CAnSwVujl7OiIdBxnV1V5BVSU61Cfan96grj8VznZk/w==
dependencies:
blake2js "^1.0.0"
oo7 "^0.7.9"
oo7-react "^0.8.10"
oo7-substrate "^0.4.12"
oo7 "^0.7.12"
oo7-react "^0.8.13"
oo7-substrate "^0.8.0"
react "^16.4.2"
ss58 "^1.0.0"
@@ -7509,6 +7618,16 @@ react-is@^16.6.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa"
integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==
react-measure@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.3.0.tgz#75835d39abec9ae13517f35a819c160997a7a44e"
integrity sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==
dependencies:
"@babel/runtime" "^7.2.0"
get-node-dimensions "^1.2.1"
prop-types "^15.6.2"
resize-observer-polyfill "^1.5.0"
react-scripts-ts@2.17.0:
version "2.17.0"
resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.17.0.tgz#398bae19a30c9b39b3dfe0720ebb40c60c2f6574"
@@ -7737,6 +7856,11 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
regenerator-transform@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@@ -7906,6 +8030,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resize-observer-polyfill@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"