Skip to content

Commit 8dd58db

Browse files
committed
Live 2H graph is now fetched through the websocket.
Tell the web socket what to fetch with "want" request.
1 parent 2b6426a commit 8dd58db

File tree

10 files changed

+122
-105
lines changed

10 files changed

+122
-105
lines changed

backend/src/api/mempool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Mempool {
4141
return this.vBytesPerSecond;
4242
}
4343

44-
public async getMemPoolInfo() {
44+
public async updateMemPoolInfo() {
4545
try {
4646
this.mempoolInfo = await bitcoinApi.getMempoolInfo();
4747
} catch (err) {

backend/src/api/statistics.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import { ITransaction, IMempoolStats } from '../interfaces';
55

66
class Statistics {
77
protected intervalTimer: NodeJS.Timer | undefined;
8+
protected newStatisticsEntryCallback: Function | undefined;
9+
10+
public setNewStatisticsEntryCallback(fn: Function) {
11+
this.newStatisticsEntryCallback = fn;
12+
}
813

914
constructor() {
1015
}
@@ -21,7 +26,7 @@ class Statistics {
2126
}, difference);
2227
}
2328

24-
private runStatistics(): void {
29+
private async runStatistics(): Promise<void> {
2530
const currentMempool = memPool.getMempool();
2631
const txPerSecond = memPool.getTxPerSecond();
2732
const vBytesPerSecond = memPool.getVBytesPerSecond();
@@ -81,7 +86,7 @@ class Statistics {
8186
}
8287
});
8388

84-
this.$create({
89+
const insertId = await this.$create({
8590
added: 'NOW()',
8691
unconfirmed_transactions: memPoolArray.length,
8792
tx_per_second: txPerSecond,
@@ -131,9 +136,14 @@ class Statistics {
131136
vsize_1800: weightVsizeFees['1800'] || 0,
132137
vsize_2000: weightVsizeFees['2000'] || 0,
133138
});
139+
140+
if (this.newStatisticsEntryCallback && insertId) {
141+
const newStats = await this.$get(insertId);
142+
this.newStatisticsEntryCallback(newStats);
143+
}
134144
}
135145

136-
private async $create(statistics: IMempoolStats): Promise<void> {
146+
private async $create(statistics: IMempoolStats): Promise<number | undefined> {
137147
try {
138148
const connection = await DB.pool.getConnection();
139149
const query = `INSERT INTO statistics(
@@ -232,26 +242,14 @@ class Statistics {
232242
statistics.vsize_1800,
233243
statistics.vsize_2000,
234244
];
235-
await connection.query(query, params);
245+
const [result]: any = await connection.query(query, params);
236246
connection.release();
247+
return result.insertId;
237248
} catch (e) {
238249
console.log('$create() error', e);
239250
}
240251
}
241252

242-
public async $listLatestFromId(fromId: number): Promise<IMempoolStats[]> {
243-
try {
244-
const connection = await DB.pool.getConnection();
245-
const query = `SELECT * FROM statistics WHERE id > ? ORDER BY id DESC`;
246-
const [rows] = await connection.query<any>(query, [fromId]);
247-
connection.release();
248-
return rows;
249-
} catch (e) {
250-
console.log('$listLatestFromId() error', e);
251-
return [];
252-
}
253-
}
254-
255253
private getQueryForDays(days: number, groupBy: number) {
256254

257255
return `SELECT id, added, unconfirmed_transactions,
@@ -297,6 +295,18 @@ class Statistics {
297295
AVG(vsize_2000) AS vsize_2000 FROM statistics GROUP BY UNIX_TIMESTAMP(added) DIV ${groupBy} ORDER BY id DESC LIMIT ${days}`;
298296
}
299297

298+
public async $get(id: number): Promise<IMempoolStats | undefined> {
299+
try {
300+
const connection = await DB.pool.getConnection();
301+
const query = `SELECT * FROM statistics WHERE id = ?`;
302+
const [rows] = await connection.query<any>(query, [id]);
303+
connection.release();
304+
return rows[0];
305+
} catch (e) {
306+
console.log('$list2H() error', e);
307+
}
308+
}
309+
300310
public async $list2H(): Promise<IMempoolStats[]> {
301311
try {
302312
const connection = await DB.pool.getConnection();

backend/src/index.ts

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import memPool from './api/mempool';
1212
import blocks from './api/blocks';
1313
import projectedBlocks from './api/projected-blocks';
1414
import statistics from './api/statistics';
15-
import { IBlock, IMempool } from './interfaces';
15+
import { IBlock, IMempool, ITransaction, IMempoolStats } from './interfaces';
1616

1717
import routes from './routes';
1818
import fiatConversion from './api/fiat-conversion';
@@ -57,7 +57,7 @@ class MempoolSpace {
5757

5858
private async runMempoolIntervalFunctions() {
5959
await blocks.updateBlocks();
60-
await memPool.getMemPoolInfo();
60+
await memPool.updateMemPoolInfo();
6161
await memPool.updateMempool();
6262
setTimeout(this.runMempoolIntervalFunctions.bind(this), config.MEMPOOL_REFRESH_RATE_MS);
6363
}
@@ -79,7 +79,6 @@ class MempoolSpace {
7979
this.wss.on('connection', (client: WebSocket) => {
8080
let theBlocks = blocks.getBlocks();
8181
theBlocks = theBlocks.concat([]).splice(theBlocks.length - config.INITIAL_BLOCK_AMOUNT);
82-
8382
const formatedBlocks = theBlocks.map((b) => blocks.formatBlock(b));
8483

8584
client.send(JSON.stringify({
@@ -94,6 +93,14 @@ class MempoolSpace {
9493
client.on('message', async (message: any) => {
9594
try {
9695
const parsedMessage = JSON.parse(message);
96+
97+
if (parsedMessage.action === 'want') {
98+
client['want-stats'] = parsedMessage.data.indexOf('stats') > -1;
99+
client['want-blocks'] = parsedMessage.data.indexOf('blocks') > -1;
100+
client['want-projected-blocks'] = parsedMessage.data.indexOf('projected-blocks') > -1;
101+
client['want-live-2h-chart'] = parsedMessage.data.indexOf('live-2h-chart') > -1;
102+
}
103+
97104
if (parsedMessage.action === 'track-tx' && parsedMessage.txId && /^[a-fA-F0-9]{64}$/.test(parsedMessage.txId)) {
98105
const tx = await memPool.getRawTransaction(parsedMessage.txId);
99106
if (tx) {
@@ -168,26 +175,29 @@ class MempoolSpace {
168175
return;
169176
}
170177

178+
const response = {};
179+
171180
if (client['trackingTx'] === true && client['blockHeight'] === 0) {
172-
if (block.tx.some((tx) => tx === client['txId'])) {
181+
if (block.tx.some((tx: ITransaction) => tx === client['txId'])) {
173182
client['blockHeight'] = block.height;
174183
}
175184
}
176185

177-
client.send(JSON.stringify({
178-
'block': formattedBlocks,
179-
'track-tx': {
180-
tracking: client['trackingTx'] || false,
181-
blockHeight: client['blockHeight'],
182-
}
183-
}));
186+
response['track-tx'] = {
187+
tracking: client['trackingTx'] || false,
188+
blockHeight: client['blockHeight'],
189+
};
190+
191+
response['block'] = formattedBlocks;
192+
193+
client.send(JSON.stringify(response));
184194
});
185195
});
186196

187197
memPool.setMempoolChangedCallback((newMempool: IMempool) => {
188198
projectedBlocks.updateProjectedBlocks(newMempool);
189199

190-
let pBlocks = projectedBlocks.getProjectedBlocks();
200+
const pBlocks = projectedBlocks.getProjectedBlocks();
191201
const mempoolInfo = memPool.getMempoolInfo();
192202
const txPerSecond = memPool.getTxPerSecond();
193203
const vBytesPerSecond = memPool.getVBytesPerSecond();
@@ -197,20 +207,41 @@ class MempoolSpace {
197207
return;
198208
}
199209

200-
if (client['trackingTx'] && client['blockHeight'] === 0) {
201-
pBlocks = projectedBlocks.getProjectedBlocks(client['txId']);
202-
}
210+
const response = {};
203211

204-
client.send(JSON.stringify({
205-
'projectedBlocks': pBlocks,
206-
'mempoolInfo': mempoolInfo,
207-
'txPerSecond': txPerSecond,
208-
'vBytesPerSecond': vBytesPerSecond,
209-
'track-tx': {
212+
if (client['want-stats']) {
213+
response['mempoolInfo'] = mempoolInfo;
214+
response['txPerSecond'] = txPerSecond;
215+
response['vBytesPerSecond'] = vBytesPerSecond;
216+
response['track-tx'] = {
210217
tracking: client['trackingTx'] || false,
211218
blockHeight: client['blockHeight'],
212-
}
213-
}));
219+
};
220+
}
221+
222+
if (client['want-projected-blocks'] && client['trackingTx'] && client['blockHeight'] === 0) {
223+
response['projectedBlocks'] = projectedBlocks.getProjectedBlocks(client['txId']);
224+
} else if (client['want-projected-blocks']) {
225+
response['projectedBlocks'] = pBlocks;
226+
}
227+
228+
if (Object.keys(response).length) {
229+
client.send(JSON.stringify(response));
230+
}
231+
});
232+
});
233+
234+
statistics.setNewStatisticsEntryCallback((stats: IMempoolStats) => {
235+
this.wss.clients.forEach((client: WebSocket) => {
236+
if (client.readyState !== WebSocket.OPEN) {
237+
return;
238+
}
239+
240+
if (client['want-live-2h-chart']) {
241+
client.send(JSON.stringify({
242+
'live-2h-chart': stats
243+
}));
244+
}
214245
});
215246
});
216247
}
@@ -220,7 +251,6 @@ class MempoolSpace {
220251
.get(config.API_ENDPOINT + 'transactions/height/:id', routes.$getgetTransactionsForBlock)
221252
.get(config.API_ENDPOINT + 'transactions/projected/:id', routes.getgetTransactionsForProjectedBlock)
222253
.get(config.API_ENDPOINT + 'fees/recommended', routes.getRecommendedFees)
223-
.get(config.API_ENDPOINT + 'statistics/live', routes.getLiveResult)
224254
.get(config.API_ENDPOINT + 'statistics/2h', routes.get2HStatistics)
225255
.get(config.API_ENDPOINT + 'statistics/24h', routes.get24HStatistics)
226256
.get(config.API_ENDPOINT + 'statistics/1w', routes.get1WHStatistics)

backend/src/routes.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ import projectedBlocks from './api/projected-blocks';
55
class Routes {
66
constructor() {}
77

8-
public async getLiveResult(req, res) {
9-
const result = await statistics.$listLatestFromId(req.query.lastId);
10-
res.send(result);
11-
}
12-
138
public async get2HStatistics(req, res) {
149
const result = await statistics.$list2H();
1510
res.send(result);
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Component, OnInit } from '@angular/core';
2+
import { ApiService } from '../services/api.service';
23

34
@Component({
45
selector: 'app-about',
@@ -7,9 +8,12 @@ import { Component, OnInit } from '@angular/core';
78
})
89
export class AboutComponent implements OnInit {
910

10-
constructor() { }
11+
constructor(
12+
private apiService: ApiService,
13+
) { }
1114

1215
ngOnInit() {
16+
this.apiService.sendWebSocket({'action': 'want', data: []});
1317
}
1418

1519
}

frontend/src/app/blockchain/blockchain.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export class BlockchainComponent implements OnInit, OnDestroy {
2626
) {}
2727

2828
ngOnInit() {
29+
this.apiService.sendWebSocket({'action': 'want', data: ['stats', 'blocks', 'projected-blocks']});
30+
2931
this.txTrackingSubscription = this.memPoolService.txTracking$
3032
.subscribe((response: ITxTracking) => {
3133
this.txTrackingLoading = false;

frontend/src/app/blockchain/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface IMempoolDefaultResponse {
1212
blocks?: IBlock[];
1313
block?: IBlock;
1414
projectedBlocks?: IProjectedBlock[];
15+
'live-2h-chart'?: IMempoolStats;
1516
txPerSecond?: number;
1617
vBytesPerSecond: number;
1718
'track-tx'?: ITrackTx;

frontend/src/app/services/api.service.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const API_BASE_URL = '/api/v1';
1313
providedIn: 'root'
1414
})
1515
export class ApiService {
16-
private websocketSubject: Observable<IMempoolDefaultResponse> = webSocket<IMempoolDefaultResponse | any>(WEB_SOCKET_URL)
16+
private websocketSubject: Observable<IMempoolDefaultResponse> = webSocket<IMempoolDefaultResponse | any>(WEB_SOCKET_URL);
1717

1818
constructor(
1919
private httpClient: HttpClient,
@@ -91,12 +91,16 @@ export class ApiService {
9191
notFound: txShowTxNotFound,
9292
});
9393
}
94-
}),
94+
95+
if (response['live-2h-chart']) {
96+
this.memPoolService.live2Chart$.next(response['live-2h-chart']);
97+
}
98+
},
9599
(err: Error) => {
96100
console.log(err);
97101
console.log('Error, retrying in 10 sec');
98102
setTimeout(() => this.startSubscription(), 10000);
99-
};
103+
});
100104
}
101105

102106
sendWebSocket(data: any) {
@@ -112,15 +116,6 @@ export class ApiService {
112116
return this.httpClient.get<IBlockTransaction[]>(API_BASE_URL + '/transactions/projected/' + index);
113117
}
114118

115-
listLiveStatistics$(lastId: number): Observable<IMempoolStats[]> {
116-
const params = new HttpParams()
117-
.set('lastId', lastId.toString());
118-
119-
return this.httpClient.get<IMempoolStats[]>(API_BASE_URL + '/statistics/live', {
120-
params: params
121-
});
122-
}
123-
124119
list2HStatistics$(): Observable<IMempoolStats[]> {
125120
return this.httpClient.get<IMempoolStats[]>(API_BASE_URL + '/statistics/2h');
126121
}

frontend/src/app/services/mem-pool.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable } from '@angular/core';
2-
import { ReplaySubject, BehaviorSubject } from 'rxjs';
3-
import { IMempoolInfo, IBlock, IProjectedBlock, ITransaction } from '../blockchain/interfaces';
2+
import { ReplaySubject, BehaviorSubject, Subject } from 'rxjs';
3+
import { IMempoolInfo, IBlock, IProjectedBlock, ITransaction, IMempoolStats } from '../blockchain/interfaces';
44

55
export interface IMemPoolState {
66
memPoolInfo: IMempoolInfo;
@@ -24,6 +24,7 @@ export class MemPoolService {
2424
txIdSearch$ = new ReplaySubject<string>();
2525
conversions$ = new ReplaySubject<any>();
2626
mempoolWeight$ = new ReplaySubject<number>();
27+
live2Chart$ = new Subject<IMempoolStats>();
2728
txTracking$ = new BehaviorSubject<ITxTracking>({
2829
enabled: false,
2930
tx: null,

0 commit comments

Comments
 (0)