mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
dashboard: send current block to the dashboard client
This commit is contained in:
parent
abc3abb4ab
commit
9bc4793401
12 changed files with 294 additions and 38 deletions
53
dashboard/assets/components/Chain.jsx
Normal file
53
dashboard/assets/components/Chain.jsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// @flow
|
||||
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import type {Chain as ChainType} from '../types/content';
|
||||
|
||||
export const inserter = () => (update: ChainType, prev: ChainType) => {
|
||||
if (!update.currentBlock) {
|
||||
return;
|
||||
}
|
||||
if (!prev.currentBlock) {
|
||||
prev.currentBlock = {};
|
||||
}
|
||||
prev.currentBlock.number = update.currentBlock.number;
|
||||
prev.currentBlock.timestamp = update.currentBlock.timestamp;
|
||||
return prev;
|
||||
};
|
||||
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {};
|
||||
|
||||
// themeStyles returns the styles generated from the theme for the component.
|
||||
const themeStyles = theme => ({});
|
||||
|
||||
export type Props = {
|
||||
content: Content,
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
// Logs renders the log page.
|
||||
class Chain extends Component<Props, State> {
|
||||
render() {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Chain;
|
||||
|
|
@ -25,6 +25,7 @@ import Header from 'Header';
|
|||
import Body from 'Body';
|
||||
import {inserter as logInserter, SAME} from 'Logs';
|
||||
import {inserter as peerInserter} from 'Network';
|
||||
import {inserter as chainInserter} from 'Chain';
|
||||
import {MENU} from '../common';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
|
|
@ -83,17 +84,24 @@ const appender = <T>(limit: number, mapper = replacer) => (update: Array<T>, pre
|
|||
// the execution of unnecessary operations (e.g. copy of the log array).
|
||||
const defaultContent: () => Content = () => ({
|
||||
general: {
|
||||
version: null,
|
||||
commit: null,
|
||||
commit: null,
|
||||
version: null,
|
||||
genesis: '',
|
||||
},
|
||||
home: {},
|
||||
chain: {
|
||||
currentBlock: {
|
||||
number: 0,
|
||||
timestamp: 0,
|
||||
},
|
||||
},
|
||||
home: {},
|
||||
chain: {},
|
||||
txpool: {},
|
||||
network: {
|
||||
peers: {
|
||||
bundles: {},
|
||||
},
|
||||
diff: [],
|
||||
diff: [],
|
||||
activePeerCount: 0,
|
||||
},
|
||||
system: {
|
||||
activeMemory: [],
|
||||
|
|
@ -119,11 +127,12 @@ const defaultContent: () => Content = () => ({
|
|||
// TODO (kurkomisi): Define a tricky type which embraces the content and the updaters.
|
||||
const updaters = {
|
||||
general: {
|
||||
version: replacer,
|
||||
commit: replacer,
|
||||
version: replacer,
|
||||
commit: replacer,
|
||||
genesis: replacer,
|
||||
},
|
||||
home: null,
|
||||
chain: null,
|
||||
chain: chainInserter(),
|
||||
txpool: null,
|
||||
network: peerInserter(200),
|
||||
system: {
|
||||
|
|
@ -241,6 +250,7 @@ class Dashboard extends Component<Props, State> {
|
|||
<div className={this.props.classes.dashboard} style={styles.dashboard}>
|
||||
<Header
|
||||
switchSideBar={this.switchSideBar}
|
||||
content={this.state.content}
|
||||
/>
|
||||
<Body
|
||||
opened={this.state.sideBar}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ import ChartRow from 'ChartRow';
|
|||
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from 'CustomTooltip';
|
||||
import {chartStrokeWidth, styles as commonStyles} from '../common';
|
||||
import type {General, System} from '../types/content';
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faNetworkWired} from "@fortawesome/free-solid-svg-icons";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
|
||||
const FOOTER_SYNC_ID = 'footerSyncId';
|
||||
|
||||
|
|
@ -154,6 +157,23 @@ class Footer extends Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const {general, system} = this.props;
|
||||
let network = '';
|
||||
switch (general.genesis) {
|
||||
case '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3':
|
||||
network = 'main';
|
||||
break;
|
||||
case '0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d':
|
||||
network = 'ropsten';
|
||||
break;
|
||||
case '0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177':
|
||||
network = 'rinkeby';
|
||||
break;
|
||||
case '0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a':
|
||||
network = 'görli';
|
||||
break;
|
||||
default:
|
||||
network = `unknown (${general.genesis.substring(0, 8)})`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
|
||||
|
|
@ -202,6 +222,9 @@ class Footer extends Component<Props, State> {
|
|||
</a>
|
||||
</Typography>
|
||||
)}
|
||||
<Typography style={styles.headerText}>
|
||||
<span style={commonStyles.light}>Network</span> {network}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,16 +23,25 @@ import AppBar from '@material-ui/core/AppBar';
|
|||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faBars} from '@fortawesome/free-solid-svg-icons';
|
||||
import {faBars, faSortAmountUp, faClock, faUsers, faSync} from '@fortawesome/free-solid-svg-icons';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import type {Content} from '../types/content';
|
||||
|
||||
|
||||
const magnitude = [31536000, 604800, 86400, 3600, 60, 1];
|
||||
const label = ['y', 'w', 'd', 'h', 'm', 's'];
|
||||
|
||||
// styles contains the constant styles of the component.
|
||||
const styles = {
|
||||
header: {
|
||||
height: '8%',
|
||||
},
|
||||
headerText: {
|
||||
marginRight: 15,
|
||||
},
|
||||
toolbar: {
|
||||
height: '100%',
|
||||
height: '100%',
|
||||
minHeight: 'unset',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -50,16 +59,52 @@ const themeStyles = (theme: Object) => ({
|
|||
title: {
|
||||
paddingLeft: theme.spacing.unit,
|
||||
fontSize: 3 * theme.spacing.unit,
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export type Props = {
|
||||
classes: Object, // injected by withStyles()
|
||||
switchSideBar: () => void,
|
||||
content: Content,
|
||||
networkID: number,
|
||||
};
|
||||
|
||||
type State = {
|
||||
since: string,
|
||||
}
|
||||
// Header renders the header of the dashboard.
|
||||
class Header extends Component<Props> {
|
||||
class Header extends Component<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {since: ''};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.interval = setInterval(() => this.setState(() => {
|
||||
// time (seconds) since last block.
|
||||
let timeDiff = Math.floor((Date.now() - this.props.content.chain.currentBlock.timestamp * 1000) / 1000);
|
||||
let since = '';
|
||||
let i = 0;
|
||||
for (; i < magnitude.length && timeDiff < magnitude[i]; i++);
|
||||
for (let j = 2; i < magnitude.length && j > 0; j--, i++) {
|
||||
const t = Math.floor(timeDiff / magnitude[i]);
|
||||
if (t > 0) {
|
||||
since += `${t}${label[i]} `;
|
||||
timeDiff %= magnitude[i];
|
||||
}
|
||||
}
|
||||
if (since === '') {
|
||||
since = 'now';
|
||||
}
|
||||
this.setState({since: since});
|
||||
}), 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {classes} = this.props;
|
||||
|
||||
|
|
@ -72,6 +117,15 @@ class Header extends Component<Props> {
|
|||
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||
Go Ethereum Dashboard
|
||||
</Typography>
|
||||
<Typography style={styles.headerText}>
|
||||
<FontAwesomeIcon icon={faSortAmountUp} /> {this.props.content.chain.currentBlock.number}
|
||||
</Typography>
|
||||
<Typography style={styles.headerText}>
|
||||
<FontAwesomeIcon icon={faClock} /> {this.state.since}
|
||||
</Typography>
|
||||
<Typography style={styles.headerText}>
|
||||
<FontAwesomeIcon icon={faUsers} /> {this.props.content.network.activePeerCount}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import React, {Component} from 'react';
|
|||
|
||||
import withStyles from '@material-ui/core/styles/withStyles';
|
||||
|
||||
import Chain from 'Chain';
|
||||
import Network from 'Network';
|
||||
import Logs from 'Logs';
|
||||
import Footer from 'Footer';
|
||||
|
|
@ -95,7 +96,9 @@ class Main extends Component<Props, State> {
|
|||
children = <div>Work in progress.</div>;
|
||||
break;
|
||||
case MENU.get('chain').id:
|
||||
children = <div>Work in progress.</div>;
|
||||
children = <Chain
|
||||
content={this.props.content.chain}
|
||||
/>;
|
||||
break;
|
||||
case MENU.get('txpool').id:
|
||||
children = <div>Work in progress.</div>;
|
||||
|
|
|
|||
|
|
@ -379,6 +379,17 @@ export const inserter = (sampleLimit: number) => (update: NetworkType, prev: Net
|
|||
}
|
||||
});
|
||||
}
|
||||
prev.activePeerCount = 0;
|
||||
Object.entries(prev.peers.bundles).forEach(([addr, bundle]) => {
|
||||
if (!bundle.knownPeers || Object.keys(bundle.knownPeers).length < 1) {
|
||||
return;
|
||||
}
|
||||
Object.entries(bundle.knownPeers).forEach(([enode, peer]) => {
|
||||
if (peer.active === true) {
|
||||
prev.activePeerCount++;
|
||||
}
|
||||
});
|
||||
});
|
||||
return prev;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ export type ChartEntry = {
|
|||
};
|
||||
|
||||
export type General = {
|
||||
version: ?string,
|
||||
commit: ?string,
|
||||
version: ?string,
|
||||
commit: ?string,
|
||||
genesis: ?string,
|
||||
};
|
||||
|
||||
export type Home = {
|
||||
|
|
@ -42,16 +43,22 @@ export type Home = {
|
|||
};
|
||||
|
||||
export type Chain = {
|
||||
/* TODO (kurkomisi) */
|
||||
currentBlock: Block,
|
||||
};
|
||||
|
||||
export type Block = {
|
||||
number: number,
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export type TxPool = {
|
||||
/* TODO (kurkomisi) */
|
||||
};
|
||||
|
||||
export type Network = {
|
||||
peers: Peers,
|
||||
diff: Array<PeerEvent>
|
||||
peers: Peers,
|
||||
diff: Array<PeerEvent>,
|
||||
activePeerCount: number,
|
||||
};
|
||||
|
||||
export type PeerEvent = {
|
||||
|
|
|
|||
|
|
@ -2219,9 +2219,9 @@ d3-collection@1:
|
|||
integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
|
||||
|
||||
d3-color@1:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f"
|
||||
integrity sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.5.tgz#5810ea1808f2f993d04508cb2fad764f48134788"
|
||||
integrity sha512-u4CaFaqQKRofuhr9uo/xLdaGvvzdsMX7MgP42XgQJHLBRWnn0C0T+48rvj80cN9KXAauHEMEfe7ehacIoxmP/g==
|
||||
|
||||
d3-format@1:
|
||||
version "1.3.2"
|
||||
|
|
@ -2559,9 +2559,9 @@ ee-first@1.1.1:
|
|||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
electron-to-chromium@^1.3.164:
|
||||
version "1.3.174"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.174.tgz#011c68d4130e995cbb69c533e147d7f4856e9f39"
|
||||
integrity sha512-OEh3EARo2B07ZRtxB0u9GqWyWmTeNS+diMp5bjw4kqMjgpzqM0w1zUOyErDsyWxTdArbvZ79T/w5n3WsBVHLfA==
|
||||
version "1.3.175"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.175.tgz#a269716af5e549f9f3989ae38ba484881dcb3702"
|
||||
integrity sha512-cQ0o7phcPA0EYkN4juZy/rq4XVxl/1ywQnytDT9hZTC0cHfaOBqWK2XlWp9Mk8xRGntgnmxlHTOux4HLb2ZNnA==
|
||||
|
||||
elliptic@^6.0.0:
|
||||
version "6.5.0"
|
||||
|
|
@ -3498,9 +3498,9 @@ got@^8.3.2:
|
|||
url-to-options "^1.0.1"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b"
|
||||
integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==
|
||||
|
||||
handle-thing@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
|
|
|||
77
dashboard/chain.go
Normal file
77
dashboard/chain.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package dashboard
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
type block struct {
|
||||
Number int64 `json:"number,omitempty"`
|
||||
Time uint64 `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (db *Dashboard) collectChainData() {
|
||||
defer db.wg.Done()
|
||||
|
||||
var (
|
||||
currentBlock *block
|
||||
chainCh chan core.ChainHeadEvent
|
||||
chainSub event.Subscription
|
||||
)
|
||||
switch {
|
||||
case db.ethServ != nil:
|
||||
chain := db.ethServ.BlockChain()
|
||||
currentBlock = &block{
|
||||
Number: chain.CurrentHeader().Number.Int64(),
|
||||
Time: chain.CurrentHeader().Time,
|
||||
}
|
||||
chainCh = make(chan core.ChainHeadEvent)
|
||||
chainSub = chain.SubscribeChainHeadEvent(chainCh)
|
||||
case db.lesServ != nil:
|
||||
chain := db.lesServ.BlockChain()
|
||||
currentBlock = &block{
|
||||
Number: chain.CurrentHeader().Number.Int64(),
|
||||
Time: chain.CurrentHeader().Time,
|
||||
}
|
||||
chainCh = make(chan core.ChainHeadEvent)
|
||||
chainSub = chain.SubscribeChainHeadEvent(chainCh)
|
||||
default:
|
||||
errc := <-db.quit
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
defer chainSub.Unsubscribe()
|
||||
|
||||
db.chainLock.Lock()
|
||||
db.history.Chain = &ChainMessage{
|
||||
CurrentBlock: currentBlock,
|
||||
}
|
||||
db.chainLock.Unlock()
|
||||
db.sendToAll(&Message{Chain: &ChainMessage{CurrentBlock: currentBlock}})
|
||||
|
||||
for {
|
||||
select {
|
||||
case e := <-chainCh:
|
||||
currentBlock := &block{
|
||||
Number: e.Block.Number().Int64(),
|
||||
Time: e.Block.Time(),
|
||||
}
|
||||
db.chainLock.Lock()
|
||||
db.history.Chain = &ChainMessage{
|
||||
CurrentBlock: currentBlock,
|
||||
}
|
||||
db.chainLock.Unlock()
|
||||
|
||||
db.sendToAll(&Message{Chain: &ChainMessage{CurrentBlock: currentBlock}})
|
||||
case err := <-chainSub.Err():
|
||||
log.Warn("Chain subscription error", "err", err)
|
||||
errc := <-db.quit
|
||||
errc <- nil
|
||||
return
|
||||
case errc := <-db.quit:
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ package dashboard
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
|
|
@ -46,6 +47,7 @@ import (
|
|||
|
||||
const (
|
||||
sampleLimit = 200 // Maximum number of data samples
|
||||
dataCollectorCount = 4
|
||||
)
|
||||
|
||||
// Dashboard contains the dashboard internals.
|
||||
|
|
@ -58,10 +60,11 @@ type Dashboard struct {
|
|||
|
||||
history *Message // Stored historical data
|
||||
|
||||
lock sync.Mutex // Lock protecting the dashboard's internals
|
||||
sysLock sync.RWMutex // Lock protecting the stored system data
|
||||
peerLock sync.RWMutex // Lock protecting the stored peer data
|
||||
logLock sync.RWMutex // Lock protecting the stored log data
|
||||
lock sync.Mutex // Lock protecting the dashboard's internals
|
||||
chainLock sync.RWMutex // Lock protecting the stored blockchain data
|
||||
sysLock sync.RWMutex // Lock protecting the stored system data
|
||||
peerLock sync.RWMutex // Lock protecting the stored peer data
|
||||
logLock sync.RWMutex // Lock protecting the stored log data
|
||||
|
||||
geodb *geoDB // geoip database instance for IP to geographical information conversions
|
||||
logdir string // Directory containing the log files
|
||||
|
|
@ -95,6 +98,12 @@ func New(config *Config, ethServ *eth.Ethereum, lesServ *les.LightEthereum, comm
|
|||
if len(params.VersionMeta) > 0 {
|
||||
versionMeta = fmt.Sprintf(" (%s)", params.VersionMeta)
|
||||
}
|
||||
var genesis common.Hash
|
||||
if ethServ != nil {
|
||||
genesis = ethServ.BlockChain().Genesis().Hash()
|
||||
} else if lesServ != nil {
|
||||
genesis = lesServ.BlockChain().Genesis().Hash()
|
||||
}
|
||||
return &Dashboard{
|
||||
conns: make(map[uint32]*client),
|
||||
config: config,
|
||||
|
|
@ -103,6 +112,7 @@ func New(config *Config, ethServ *eth.Ethereum, lesServ *les.LightEthereum, comm
|
|||
General: &GeneralMessage{
|
||||
Commit: commit,
|
||||
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
|
||||
Genesis: genesis,
|
||||
},
|
||||
System: &SystemMessage{
|
||||
ActiveMemory: emptyChartEntries(sampleLimit),
|
||||
|
|
@ -143,7 +153,8 @@ func (db *Dashboard) APIs() []rpc.API { return nil }
|
|||
func (db *Dashboard) Start(server *p2p.Server) error {
|
||||
log.Info("Starting dashboard")
|
||||
|
||||
db.wg.Add(3)
|
||||
db.wg.Add(dataCollectorCount)
|
||||
go db.collectChainData()
|
||||
go db.collectSystemData()
|
||||
go db.streamLogs()
|
||||
go db.collectPeerData()
|
||||
|
|
@ -175,8 +186,8 @@ func (db *Dashboard) Stop() error {
|
|||
errs = append(errs, err)
|
||||
}
|
||||
// Close the collectors.
|
||||
errc := make(chan error, 1)
|
||||
for i := 0; i < 3; i++ {
|
||||
errc := make(chan error, dataCollectorCount)
|
||||
for i := 0; i < dataCollectorCount; i++ {
|
||||
db.quit <- errc
|
||||
if err := <-errc; err != nil {
|
||||
errs = append(errs, err)
|
||||
|
|
@ -250,20 +261,21 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
|||
}()
|
||||
|
||||
// Send the past data.
|
||||
db.chainLock.RLock()
|
||||
db.sysLock.RLock()
|
||||
db.peerLock.RLock()
|
||||
db.logLock.RLock()
|
||||
|
||||
h := deepcopy.Copy(db.history).(*Message)
|
||||
|
||||
db.chainLock.RUnlock()
|
||||
db.sysLock.RUnlock()
|
||||
db.peerLock.RUnlock()
|
||||
db.logLock.RUnlock()
|
||||
|
||||
client.msg <- h
|
||||
|
||||
// Start tracking the connection and drop at connection loss.
|
||||
db.lock.Lock()
|
||||
client.msg <- h
|
||||
db.conns[id] = client
|
||||
db.lock.Unlock()
|
||||
defer func() {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package dashboard
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
|
|
@ -37,8 +38,9 @@ type ChartEntry struct {
|
|||
}
|
||||
|
||||
type GeneralMessage struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Genesis common.Hash `json:"genesis,omitempty"`
|
||||
}
|
||||
|
||||
type HomeMessage struct {
|
||||
|
|
@ -46,7 +48,7 @@ type HomeMessage struct {
|
|||
}
|
||||
|
||||
type ChainMessage struct {
|
||||
/* TODO (kurkomisi) */
|
||||
CurrentBlock *block `json:"currentBlock,omitempty"`
|
||||
}
|
||||
|
||||
type TxPoolMessage struct {
|
||||
|
|
|
|||
|
|
@ -339,6 +339,8 @@ func (db *Dashboard) collectPeerData() {
|
|||
db.geodb, err = openGeoDB()
|
||||
if err != nil {
|
||||
log.Warn("Failed to open geodb", "err", err)
|
||||
errc := <-db.quit
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
defer db.geodb.close()
|
||||
|
|
@ -517,6 +519,8 @@ func (db *Dashboard) collectPeerData() {
|
|||
newPeerEvents = newPeerEvents[:0]
|
||||
case err := <-db.subPeer.Err():
|
||||
log.Warn("Peer subscription error", "err", err)
|
||||
errc := <-db.quit
|
||||
errc <- nil
|
||||
return
|
||||
case errc := <-db.quit:
|
||||
errc <- nil
|
||||
|
|
|
|||
Loading…
Reference in a new issue