replace client recent blocks on reorg
This commit is contained in:
parent
9cf961c667
commit
e8c703fdbc
@ -25,6 +25,7 @@ import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmen
|
|||||||
import PricesRepository from '../repositories/PricesRepository';
|
import PricesRepository from '../repositories/PricesRepository';
|
||||||
import priceUpdater from '../tasks/price-updater';
|
import priceUpdater from '../tasks/price-updater';
|
||||||
import chainTips from './chain-tips';
|
import chainTips from './chain-tips';
|
||||||
|
import websocketHandler from './websocket-handler';
|
||||||
|
|
||||||
class Blocks {
|
class Blocks {
|
||||||
private blocks: BlockExtended[] = [];
|
private blocks: BlockExtended[] = [];
|
||||||
@ -686,6 +687,8 @@ class Blocks {
|
|||||||
this.updateTimerProgress(timer, `reindexed difficulty adjustments`);
|
this.updateTimerProgress(timer, `reindexed difficulty adjustments`);
|
||||||
logger.info(`Re-indexed 10 blocks and summaries. Also re-indexed the last difficulty adjustments. Will re-index latest hashrates in a few seconds.`, logger.tags.mining);
|
logger.info(`Re-indexed 10 blocks and summaries. Also re-indexed the last difficulty adjustments. Will re-index latest hashrates in a few seconds.`, logger.tags.mining);
|
||||||
indexer.reindex();
|
indexer.reindex();
|
||||||
|
|
||||||
|
websocketHandler.handleReorg();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +333,40 @@ class WebsocketHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleReorg(): void {
|
||||||
|
if (!this.wss) {
|
||||||
|
throw new Error('WebSocket.Server is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
const da = difficultyAdjustment.getDifficultyAdjustment();
|
||||||
|
|
||||||
|
// update init data
|
||||||
|
this.updateSocketDataFields({
|
||||||
|
'blocks': blocks.getBlocks(),
|
||||||
|
'da': da?.previousTime ? da : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wss.clients.forEach((client) => {
|
||||||
|
if (client.readyState !== WebSocket.OPEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = {};
|
||||||
|
|
||||||
|
if (client['want-blocks']) {
|
||||||
|
response['blocks'] = this.socketData['blocks'];
|
||||||
|
}
|
||||||
|
if (client['want-stats']) {
|
||||||
|
response['da'] = this.socketData['da'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(response).length) {
|
||||||
|
const serializedResponse = this.serializeResponse(response);
|
||||||
|
client.send(serializedResponse);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number,
|
async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number,
|
||||||
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]): Promise<void> {
|
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]): Promise<void> {
|
||||||
if (!this.wss) {
|
if (!this.wss) {
|
||||||
|
@ -112,7 +112,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
|
|||||||
this.error = error;
|
this.error = error;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.latestBlock$ = this.stateService.blocks$.pipe(map((([block]) => block)));
|
this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0]));
|
||||||
|
|
||||||
this.stateService.bsqPrice$
|
this.stateService.bsqPrice$
|
||||||
.subscribe((bsqPrice) => {
|
.subscribe((bsqPrice) => {
|
||||||
|
@ -27,7 +27,7 @@ export class BisqTransfersComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.latestBlock$ = this.stateService.blocks$.pipe(map(([block]) => block));
|
this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
|
@ -129,18 +129,19 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
this.blocksSubscription = this.stateService.blocks$
|
||||||
.subscribe(([block]) => {
|
.subscribe((blocks) => {
|
||||||
this.latestBlock = block;
|
this.latestBlock = blocks[0];
|
||||||
this.latestBlocks.unshift(block);
|
this.latestBlocks = blocks;
|
||||||
this.latestBlocks = this.latestBlocks.slice(0, this.stateService.env.KEEP_BLOCKS_AMOUNT);
|
|
||||||
this.setNextAndPreviousBlockLink();
|
this.setNextAndPreviousBlockLink();
|
||||||
|
|
||||||
if (block.id === this.blockHash) {
|
for (const block of blocks) {
|
||||||
this.block = block;
|
if (block.id === this.blockHash) {
|
||||||
block.extras.minFee = this.getMinBlockFee(block);
|
this.block = block;
|
||||||
block.extras.maxFee = this.getMaxBlockFee(block);
|
block.extras.minFee = this.getMinBlockFee(block);
|
||||||
if (block?.extras?.reward != undefined) {
|
block.extras.maxFee = this.getMaxBlockFee(block);
|
||||||
this.fees = block.extras.reward / 100000000 - this.blockSubsidy;
|
if (block?.extras?.reward != undefined) {
|
||||||
|
this.fees = block.extras.reward / 100000000 - this.blockSubsidy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -41,6 +41,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
networkSubscription: Subscription;
|
networkSubscription: Subscription;
|
||||||
tabHiddenSubscription: Subscription;
|
tabHiddenSubscription: Subscription;
|
||||||
markBlockSubscription: Subscription;
|
markBlockSubscription: Subscription;
|
||||||
|
txConfirmedSubscription: Subscription;
|
||||||
loadingBlocks$: Observable<boolean>;
|
loadingBlocks$: Observable<boolean>;
|
||||||
blockStyles = [];
|
blockStyles = [];
|
||||||
emptyBlockStyles = [];
|
emptyBlockStyles = [];
|
||||||
@ -104,31 +105,22 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
|
this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
|
||||||
if (!this.static) {
|
if (!this.static) {
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
this.blocksSubscription = this.stateService.blocks$
|
||||||
.subscribe(([block, txConfirmed]) => {
|
.subscribe((blocks) => {
|
||||||
if (this.blocks.some((b) => b.height === block.height)) {
|
if (!blocks?.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const latestHeight = blocks[0].height;
|
||||||
|
const animate = latestHeight > blocks[0].height;
|
||||||
|
|
||||||
if (this.blocks.length && block.height !== this.blocks[0].height + 1) {
|
for (const block of blocks) {
|
||||||
this.blocks = [];
|
block.extras.minFee = this.getMinBlockFee(block);
|
||||||
this.blocksFilled = false;
|
block.extras.maxFee = this.getMaxBlockFee(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
block.extras.minFee = this.getMinBlockFee(block);
|
this.blocks = blocks;
|
||||||
block.extras.maxFee = this.getMaxBlockFee(block);
|
|
||||||
|
|
||||||
this.blocks.unshift(block);
|
|
||||||
this.blocks = this.blocks.slice(0, this.dynamicBlocksAmount);
|
|
||||||
|
|
||||||
if (txConfirmed && block.height > this.chainTip) {
|
|
||||||
this.markHeight = block.height;
|
|
||||||
this.moveArrowToPosition(true, true);
|
|
||||||
} else {
|
|
||||||
this.moveArrowToPosition(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blockStyles = [];
|
this.blockStyles = [];
|
||||||
if (this.blocksFilled && block.height > this.chainTip) {
|
if (animate) {
|
||||||
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -this.blockOffset : -this.dividerBlockOffset)));
|
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -this.blockOffset : -this.dividerBlockOffset)));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.blockStyles = [];
|
this.blockStyles = [];
|
||||||
@ -139,13 +131,18 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i)));
|
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.blocks.length === this.dynamicBlocksAmount) {
|
this.chainTip = latestHeight;
|
||||||
this.blocksFilled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chainTip = Math.max(this.chainTip, block.height);
|
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.txConfirmedSubscription = this.stateService.txConfirmed$.subscribe(([txid, block]) => {
|
||||||
|
if (txid) {
|
||||||
|
this.markHeight = block.height;
|
||||||
|
this.moveArrowToPosition(true, true);
|
||||||
|
} else {
|
||||||
|
this.moveArrowToPosition(true, false);
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.blockPageSubscription = this.cacheService.loadedBlocks$.subscribe((block) => {
|
this.blockPageSubscription = this.cacheService.loadedBlocks$.subscribe((block) => {
|
||||||
if (block.height <= this.height && block.height > this.height - this.count) {
|
if (block.height <= this.height && block.height > this.height - this.count) {
|
||||||
@ -164,9 +161,9 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.static) {
|
if (this.static) {
|
||||||
this.updateStaticBlocks();
|
this.updateStaticBlocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
@ -190,6 +187,9 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
if (this.blockPageSubscription) {
|
if (this.blockPageSubscription) {
|
||||||
this.blockPageSubscription.unsubscribe();
|
this.blockPageSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
if (this.txConfirmedSubscription) {
|
||||||
|
this.txConfirmedSubscription.unsubscribe();
|
||||||
|
}
|
||||||
this.networkSubscription.unsubscribe();
|
this.networkSubscription.unsubscribe();
|
||||||
this.tabHiddenSubscription.unsubscribe();
|
this.tabHiddenSubscription.unsubscribe();
|
||||||
this.markBlockSubscription.unsubscribe();
|
this.markBlockSubscription.unsubscribe();
|
||||||
|
@ -39,13 +39,10 @@ export class ClockFaceComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
})
|
})
|
||||||
).subscribe();
|
).subscribe();
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
this.blocksSubscription = this.stateService.blocks$
|
||||||
.subscribe(([block]) => {
|
.subscribe((blocks) => {
|
||||||
if (block) {
|
this.blockTimes = blocks.map(block => [block.height, new Date(block.timestamp * 1000)]);
|
||||||
this.blockTimes.push([block.height, new Date(block.timestamp * 1000)]);
|
this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime());
|
||||||
// using block-reported times, so ensure they are sorted chronologically
|
this.updateSegments();
|
||||||
this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime());
|
|
||||||
this.updateSegments();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,14 +60,11 @@ export class ClockComponent implements OnInit {
|
|||||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
||||||
|
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
this.blocksSubscription = this.stateService.blocks$
|
||||||
.subscribe(([block]) => {
|
.subscribe((blocks) => {
|
||||||
if (block) {
|
this.blocks = blocks.slice(0, 16);
|
||||||
this.blocks.unshift(block);
|
if (this.blocks[this.blockIndex]) {
|
||||||
this.blocks = this.blocks.slice(0, 16);
|
this.blockStyle = this.getStyleForBlock(this.blocks[this.blockIndex]);
|
||||||
if (this.blocks[this.blockIndex]) {
|
this.cd.markForCheck();
|
||||||
this.blockStyle = this.getStyleForBlock(this.blocks[this.blockIndex]);
|
|
||||||
this.cd.markForCheck();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -38,11 +38,12 @@ export class DifficultyMiningComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||||
this.difficultyEpoch$ = combineLatest([
|
this.difficultyEpoch$ = combineLatest([
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
this.stateService.blocks$,
|
||||||
this.stateService.difficultyAdjustment$,
|
this.stateService.difficultyAdjustment$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([block, da]) => {
|
map(([blocks, da]) => {
|
||||||
|
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0);
|
||||||
let colorAdjustments = '#ffffff66';
|
let colorAdjustments = '#ffffff66';
|
||||||
if (da.difficultyChange > 0) {
|
if (da.difficultyChange > 0) {
|
||||||
colorAdjustments = '#3bcc49';
|
colorAdjustments = '#3bcc49';
|
||||||
@ -63,7 +64,7 @@ export class DifficultyMiningComponent implements OnInit {
|
|||||||
colorPreviousAdjustments = '#ffffff66';
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocksUntilHalving = 210000 - (block.height % 210000);
|
const blocksUntilHalving = 210000 - (maxHeight % 210000);
|
||||||
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000);
|
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -67,11 +67,12 @@ export class DifficultyComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||||
this.difficultyEpoch$ = combineLatest([
|
this.difficultyEpoch$ = combineLatest([
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
this.stateService.blocks$,
|
||||||
this.stateService.difficultyAdjustment$,
|
this.stateService.difficultyAdjustment$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([block, da]) => {
|
map(([blocks, da]) => {
|
||||||
|
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0);
|
||||||
let colorAdjustments = '#ffffff66';
|
let colorAdjustments = '#ffffff66';
|
||||||
if (da.difficultyChange > 0) {
|
if (da.difficultyChange > 0) {
|
||||||
colorAdjustments = '#3bcc49';
|
colorAdjustments = '#3bcc49';
|
||||||
@ -92,7 +93,7 @@ export class DifficultyComponent implements OnInit {
|
|||||||
colorPreviousAdjustments = '#ffffff66';
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocksUntilHalving = 210000 - (block.height % 210000);
|
const blocksUntilHalving = 210000 - (maxHeight % 210000);
|
||||||
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000);
|
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000);
|
||||||
const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH;
|
const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH;
|
||||||
const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks);
|
const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks);
|
||||||
|
@ -124,7 +124,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => combineLatest([
|
switchMap(() => combineLatest([
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
this.stateService.blocks$.pipe(map((blocks) => blocks[0])),
|
||||||
this.stateService.mempoolBlocks$
|
this.stateService.mempoolBlocks$
|
||||||
.pipe(
|
.pipe(
|
||||||
map((mempoolBlocks) => {
|
map((mempoolBlocks) => {
|
||||||
@ -186,8 +186,11 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.blockSubscription = this.stateService.blocks$
|
this.blockSubscription = this.stateService.blocks$.pipe(map((blocks) => blocks[0]))
|
||||||
.subscribe(([block]) => {
|
.subscribe((block) => {
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.chainTip === -1) {
|
if (this.chainTip === -1) {
|
||||||
this.animateEntry = block.height === this.stateService.latestBlockHeight;
|
this.animateEntry = block.height === this.stateService.latestBlockHeight;
|
||||||
} else {
|
} else {
|
||||||
@ -221,8 +224,8 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]);
|
this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]);
|
||||||
} else {
|
} else {
|
||||||
this.stateService.blocks$
|
this.stateService.blocks$
|
||||||
.pipe(take(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT))
|
.pipe(map((blocks) => blocks[0]))
|
||||||
.subscribe(([block]) => {
|
.subscribe((block) => {
|
||||||
if (this.stateService.latestBlockHeight === block.height) {
|
if (this.stateService.latestBlockHeight === block.height) {
|
||||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'), block.id], { state: { data: { block } }});
|
this.router.navigate([this.relativeUrlPipe.transform('/block/'), block.id], { state: { data: { block } }});
|
||||||
}
|
}
|
||||||
@ -297,7 +300,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
while (blocks.length > blocksAmount) {
|
while (blocks.length > blocksAmount) {
|
||||||
const block = blocks.pop();
|
const block = blocks.pop();
|
||||||
if (!this.count) {
|
if (!this.count) {
|
||||||
const lastBlock = blocks[blocks.length - 1];
|
const lastBlock = blocks[0];
|
||||||
lastBlock.blockSize += block.blockSize;
|
lastBlock.blockSize += block.blockSize;
|
||||||
lastBlock.blockVSize += block.blockVSize;
|
lastBlock.blockVSize += block.blockVSize;
|
||||||
lastBlock.nTx += block.nTx;
|
lastBlock.nTx += block.nTx;
|
||||||
@ -308,7 +311,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blocks.length) {
|
if (blocks.length) {
|
||||||
blocks[blocks.length - 1].isStack = blocks[blocks.length - 1].blockVSize > this.stateService.blockVSize;
|
blocks[0].isStack = blocks[0].blockVSize > this.stateService.blockVSize;
|
||||||
}
|
}
|
||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export class PoolComponent implements OnInit {
|
|||||||
|
|
||||||
auditAvailable = false;
|
auditAvailable = false;
|
||||||
|
|
||||||
loadMoreSubject: BehaviorSubject<number> = new BehaviorSubject(this.blocks[this.blocks.length - 1]?.height);
|
loadMoreSubject: BehaviorSubject<number> = new BehaviorSubject(this.blocks[0]?.height);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@Inject(LOCALE_ID) public locale: string,
|
||||||
@ -68,7 +68,7 @@ export class PoolComponent implements OnInit {
|
|||||||
return this.apiService.getPoolStats$(slug);
|
return this.apiService.getPoolStats$(slug);
|
||||||
}),
|
}),
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height);
|
this.loadMoreSubject.next(this.blocks[0]?.height);
|
||||||
}),
|
}),
|
||||||
map((poolStats) => {
|
map((poolStats) => {
|
||||||
this.seoService.setTitle(poolStats.pool.name);
|
this.seoService.setTitle(poolStats.pool.name);
|
||||||
@ -91,7 +91,7 @@ export class PoolComponent implements OnInit {
|
|||||||
if (this.slug === undefined) {
|
if (this.slug === undefined) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return this.apiService.getPoolBlocks$(this.slug, this.blocks[this.blocks.length - 1]?.height);
|
return this.apiService.getPoolBlocks$(this.slug, this.blocks[0]?.height);
|
||||||
}),
|
}),
|
||||||
tap((newBlocks) => {
|
tap((newBlocks) => {
|
||||||
this.blocks = this.blocks.concat(newBlocks);
|
this.blocks = this.blocks.concat(newBlocks);
|
||||||
@ -237,7 +237,7 @@ export class PoolComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height);
|
this.loadMoreSubject.next(this.blocks[0]?.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByBlock(index: number, block: BlockExtended) {
|
trackByBlock(index: number, block: BlockExtended) {
|
||||||
|
@ -29,11 +29,12 @@ export class RewardStatsComponent implements OnInit {
|
|||||||
// Or when we receive a newer block, newer than the latest reward stats api call
|
// Or when we receive a newer block, newer than the latest reward stats api call
|
||||||
this.stateService.blocks$
|
this.stateService.blocks$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((block) => {
|
switchMap((blocks) => {
|
||||||
if (block[0].height <= this.lastBlockHeight) {
|
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0);
|
||||||
|
if (maxHeight <= this.lastBlockHeight) {
|
||||||
return []; // Return an empty stream so the last pipe is not executed
|
return []; // Return an empty stream so the last pipe is not executed
|
||||||
}
|
}
|
||||||
this.lastBlockHeight = block[0].height;
|
this.lastBlockHeight = maxHeight;
|
||||||
return this.apiService.getRewardStats$();
|
return this.apiService.getRewardStats$();
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild, Inpu
|
|||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { MarkBlockState, StateService } from '../../services/state.service';
|
import { MarkBlockState, StateService } from '../../services/state.service';
|
||||||
import { specialBlocks } from '../../app.constants';
|
import { specialBlocks } from '../../app.constants';
|
||||||
|
import { BlockExtended } from '../../interfaces/node-api.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-start',
|
selector: 'app-start',
|
||||||
@ -55,8 +56,8 @@ export class StartComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
|
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
|
||||||
this.blockCounterSubscription = this.stateService.blocks$.subscribe(() => {
|
this.blockCounterSubscription = this.stateService.blocks$.subscribe((blocks) => {
|
||||||
this.blockCount++;
|
this.blockCount = blocks.length;
|
||||||
this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8);
|
this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8);
|
||||||
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
|
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
|
||||||
if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) {
|
if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) {
|
||||||
@ -110,9 +111,12 @@ export class StartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.stateService.blocks$
|
this.stateService.blocks$
|
||||||
.subscribe((blocks: any) => {
|
.subscribe((blocks: BlockExtended[]) => {
|
||||||
this.countdown = 0;
|
this.countdown = 0;
|
||||||
const block = blocks[0];
|
const block = blocks[0];
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const sb in specialBlocks) {
|
for (const sb in specialBlocks) {
|
||||||
if (specialBlocks[sb].networks.includes(this.stateService.network || 'mainnet')) {
|
if (specialBlocks[sb].networks.includes(this.stateService.network || 'mainnet')) {
|
||||||
|
@ -49,7 +49,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
txReplacedSubscription: Subscription;
|
txReplacedSubscription: Subscription;
|
||||||
txRbfInfoSubscription: Subscription;
|
txRbfInfoSubscription: Subscription;
|
||||||
mempoolPositionSubscription: Subscription;
|
mempoolPositionSubscription: Subscription;
|
||||||
blocksSubscription: Subscription;
|
|
||||||
queryParamsSubscription: Subscription;
|
queryParamsSubscription: Subscription;
|
||||||
urlFragmentSubscription: Subscription;
|
urlFragmentSubscription: Subscription;
|
||||||
mempoolBlocksSubscription: Subscription;
|
mempoolBlocksSubscription: Subscription;
|
||||||
@ -391,9 +390,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.blocksSubscription = this.stateService.blocks$.subscribe(([block, txConfirmed]) => {
|
this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => {
|
||||||
this.latestBlock = block;
|
|
||||||
|
|
||||||
if (txConfirmed && this.tx && !this.tx.status.confirmed && txConfirmed === this.tx.txid) {
|
if (txConfirmed && this.tx && !this.tx.status.confirmed && txConfirmed === this.tx.txid) {
|
||||||
this.tx.status = {
|
this.tx.status = {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
@ -593,7 +590,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.fetchCachedTxSubscription.unsubscribe();
|
this.fetchCachedTxSubscription.unsubscribe();
|
||||||
this.txReplacedSubscription.unsubscribe();
|
this.txReplacedSubscription.unsubscribe();
|
||||||
this.txRbfInfoSubscription.unsubscribe();
|
this.txRbfInfoSubscription.unsubscribe();
|
||||||
this.blocksSubscription.unsubscribe();
|
|
||||||
this.queryParamsSubscription.unsubscribe();
|
this.queryParamsSubscription.unsubscribe();
|
||||||
this.flowPrefSubscription.unsubscribe();
|
this.flowPrefSubscription.unsubscribe();
|
||||||
this.urlFragmentSubscription.unsubscribe();
|
this.urlFragmentSubscription.unsubscribe();
|
||||||
|
@ -56,7 +56,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.latestBlock$ = this.stateService.blocks$.pipe(map(([block]) => block));
|
this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0]));
|
||||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||||
|
|
||||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||||
|
@ -132,8 +132,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.blocks$ = this.stateService.blocks$
|
this.blocks$ = this.stateService.blocks$
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(([block]) => {
|
tap((blocks) => {
|
||||||
this.latestBlockHeight = block.height;
|
this.latestBlockHeight = blocks[0].height;
|
||||||
}),
|
}),
|
||||||
scan((acc, [block]) => {
|
scan((acc, [block]) => {
|
||||||
if (acc.find((b) => b.height == block.height)) {
|
if (acc.find((b) => b.height == block.height)) {
|
||||||
|
@ -18,6 +18,7 @@ export class CacheService {
|
|||||||
txCache: { [txid: string]: Transaction } = {};
|
txCache: { [txid: string]: Transaction } = {};
|
||||||
|
|
||||||
network: string;
|
network: string;
|
||||||
|
blockHashCache: { [hash: string]: BlockExtended } = {};
|
||||||
blockCache: { [height: number]: BlockExtended } = {};
|
blockCache: { [height: number]: BlockExtended } = {};
|
||||||
blockLoading: { [height: number]: boolean } = {};
|
blockLoading: { [height: number]: boolean } = {};
|
||||||
copiesInBlockQueue: { [height: number]: number } = {};
|
copiesInBlockQueue: { [height: number]: number } = {};
|
||||||
@ -27,8 +28,10 @@ export class CacheService {
|
|||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
) {
|
) {
|
||||||
this.stateService.blocks$.subscribe(([block]) => {
|
this.stateService.blocks$.subscribe((blocks) => {
|
||||||
this.addBlockToCache(block);
|
for (const block of blocks) {
|
||||||
|
this.addBlockToCache(block);
|
||||||
|
}
|
||||||
this.clearBlocks();
|
this.clearBlocks();
|
||||||
});
|
});
|
||||||
this.stateService.chainTip$.subscribe((height) => {
|
this.stateService.chainTip$.subscribe((height) => {
|
||||||
@ -56,8 +59,11 @@ export class CacheService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addBlockToCache(block: BlockExtended) {
|
addBlockToCache(block: BlockExtended) {
|
||||||
this.blockCache[block.height] = block;
|
if (!this.blockHashCache[block.id]) {
|
||||||
this.bumpBlockPriority(block.height);
|
this.blockHashCache[block.id] = block;
|
||||||
|
this.blockCache[block.height] = block;
|
||||||
|
this.bumpBlockPriority(block.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadBlock(height) {
|
async loadBlock(height) {
|
||||||
@ -105,7 +111,9 @@ export class CacheService {
|
|||||||
} else if ((this.tip - height) < KEEP_RECENT_BLOCKS) {
|
} else if ((this.tip - height) < KEEP_RECENT_BLOCKS) {
|
||||||
this.bumpBlockPriority(height);
|
this.bumpBlockPriority(height);
|
||||||
} else {
|
} else {
|
||||||
|
const block = this.blockCache[height];
|
||||||
delete this.blockCache[height];
|
delete this.blockCache[height];
|
||||||
|
delete this.blockHashCache[block.id];
|
||||||
delete this.copiesInBlockQueue[height];
|
delete this.copiesInBlockQueue[height];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +121,7 @@ export class CacheService {
|
|||||||
|
|
||||||
// remove all blocks from the cache
|
// remove all blocks from the cache
|
||||||
resetBlockCache() {
|
resetBlockCache() {
|
||||||
|
this.blockHashCache = {};
|
||||||
this.blockCache = {};
|
this.blockCache = {};
|
||||||
this.blockLoading = {};
|
this.blockLoading = {};
|
||||||
this.copiesInBlockQueue = {};
|
this.copiesInBlockQueue = {};
|
||||||
|
@ -90,10 +90,11 @@ export class StateService {
|
|||||||
blockVSize: number;
|
blockVSize: number;
|
||||||
env: Env;
|
env: Env;
|
||||||
latestBlockHeight = -1;
|
latestBlockHeight = -1;
|
||||||
|
blocks: BlockExtended[] = [];
|
||||||
|
|
||||||
networkChanged$ = new ReplaySubject<string>(1);
|
networkChanged$ = new ReplaySubject<string>(1);
|
||||||
lightningChanged$ = new ReplaySubject<boolean>(1);
|
lightningChanged$ = new ReplaySubject<boolean>(1);
|
||||||
blocks$: ReplaySubject<[BlockExtended, string]>;
|
blocks$ = new BehaviorSubject<BlockExtended[]>([]);
|
||||||
transactions$ = new ReplaySubject<TransactionStripped>(6);
|
transactions$ = new ReplaySubject<TransactionStripped>(6);
|
||||||
conversions$ = new ReplaySubject<any>(1);
|
conversions$ = new ReplaySubject<any>(1);
|
||||||
bsqPrice$ = new ReplaySubject<number>(1);
|
bsqPrice$ = new ReplaySubject<number>(1);
|
||||||
@ -102,6 +103,7 @@ export class StateService {
|
|||||||
mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
|
mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
|
||||||
mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
|
mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
|
||||||
liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
|
liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
|
||||||
|
txConfirmed$ = new Subject<[string, BlockExtended]>();
|
||||||
txReplaced$ = new Subject<ReplacedTransaction>();
|
txReplaced$ = new Subject<ReplacedTransaction>();
|
||||||
txRbfInfo$ = new Subject<RbfTree>();
|
txRbfInfo$ = new Subject<RbfTree>();
|
||||||
rbfLatest$ = new Subject<RbfTree[]>();
|
rbfLatest$ = new Subject<RbfTree[]>();
|
||||||
@ -167,8 +169,6 @@ export class StateService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT);
|
|
||||||
|
|
||||||
this.liveMempoolBlockTransactions$ = merge(
|
this.liveMempoolBlockTransactions$ = merge(
|
||||||
this.mempoolBlockTransactions$.pipe(map(transactions => { return { transactions }; })),
|
this.mempoolBlockTransactions$.pipe(map(transactions => { return { transactions }; })),
|
||||||
this.mempoolBlockDelta$.pipe(map(delta => { return { delta }; })),
|
this.mempoolBlockDelta$.pipe(map(delta => { return { delta }; })),
|
||||||
@ -341,4 +341,15 @@ export class StateService {
|
|||||||
this.chainTip$.next(height);
|
this.chainTip$.next(height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetBlocks(blocks: BlockExtended[]): void {
|
||||||
|
this.blocks = blocks.reverse();
|
||||||
|
this.blocks$.next(blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
addBlock(block: BlockExtended): void {
|
||||||
|
this.blocks.unshift(block);
|
||||||
|
this.blocks = this.blocks.slice(0, this.env.KEEP_BLOCKS_AMOUNT);
|
||||||
|
this.blocks$.next(this.blocks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,13 +239,8 @@ export class WebsocketService {
|
|||||||
|
|
||||||
if (response.blocks && response.blocks.length) {
|
if (response.blocks && response.blocks.length) {
|
||||||
const blocks = response.blocks;
|
const blocks = response.blocks;
|
||||||
let maxHeight = 0;
|
this.stateService.resetBlocks(blocks);
|
||||||
blocks.forEach((block: BlockExtended) => {
|
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), this.stateService.latestBlockHeight);
|
||||||
if (block.height > this.stateService.latestBlockHeight) {
|
|
||||||
maxHeight = Math.max(maxHeight, block.height);
|
|
||||||
this.stateService.blocks$.next([block, '']);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.stateService.updateChainTip(maxHeight);
|
this.stateService.updateChainTip(maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +255,8 @@ export class WebsocketService {
|
|||||||
if (response.block) {
|
if (response.block) {
|
||||||
if (response.block.height === this.stateService.latestBlockHeight + 1) {
|
if (response.block.height === this.stateService.latestBlockHeight + 1) {
|
||||||
this.stateService.updateChainTip(response.block.height);
|
this.stateService.updateChainTip(response.block.height);
|
||||||
this.stateService.blocks$.next([response.block, response.txConfirmed || '']);
|
this.stateService.addBlock(response.block);
|
||||||
|
this.stateService.txConfirmed$.next([response.txConfirmed, response.block]);
|
||||||
} else if (response.block.height > this.stateService.latestBlockHeight + 1) {
|
} else if (response.block.height > this.stateService.latestBlockHeight + 1) {
|
||||||
reinitBlocks = true;
|
reinitBlocks = true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user