Merge branch 'master' into mononaut/sighash-highlighting

This commit is contained in:
wiz
2025-05-08 21:13:03 +09:00
committed by GitHub
4 changed files with 78 additions and 26 deletions

View File

@@ -256,31 +256,36 @@ class Mining {
const blockStats: any = await BlocksRepository.$blockCountBetweenTimestamp( const blockStats: any = await BlocksRepository.$blockCountBetweenTimestamp(
null, fromTimestamp / 1000, toTimestamp / 1000); null, fromTimestamp / 1000, toTimestamp / 1000);
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount,
blockStats.lastBlockHeight);
let pools = await PoolsRepository.$getPoolsInfoBetween(fromTimestamp / 1000, toTimestamp / 1000); if (blockStats.blockCount <= 0) {
const totalBlocks = pools.reduce((acc, pool) => acc + pool.blockCount, 0); logger.debug(`No block found between ${fromTimestamp / 1000} and ${toTimestamp / 1000}, skipping hashrate indexing for this period`, logger.tags.mining);
if (totalBlocks > 0) { } else {
pools = pools.map((pool: any) => { const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount,
pool.hashrate = (pool.blockCount / totalBlocks) * lastBlockHashrate; blockStats.lastBlockHeight);
pool.share = (pool.blockCount / totalBlocks);
return pool;
});
for (const pool of pools) { let pools = await PoolsRepository.$getPoolsInfoBetween(fromTimestamp / 1000, toTimestamp / 1000);
hashrates.push({ const totalBlocks = pools.reduce((acc, pool) => acc + pool.blockCount, 0);
hashrateTimestamp: toTimestamp / 1000, if (totalBlocks > 0) {
avgHashrate: pool['hashrate'] , pools = pools.map((pool: any) => {
poolId: pool.poolId, pool.hashrate = (pool.blockCount / totalBlocks) * lastBlockHashrate;
share: pool['share'], pool.share = (pool.blockCount / totalBlocks);
type: 'weekly', return pool;
}); });
}
newlyIndexed += hashrates.length / Math.max(1, pools.length); for (const pool of pools) {
await HashratesRepository.$saveHashrates(hashrates); hashrates.push({
hashrates.length = 0; hashrateTimestamp: toTimestamp / 1000,
avgHashrate: pool['hashrate'] ,
poolId: pool.poolId,
share: pool['share'],
type: 'weekly',
});
}
newlyIndexed += hashrates.length / Math.max(1, pools.length);
await HashratesRepository.$saveHashrates(hashrates);
hashrates.length = 0;
}
} }
const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer)); const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));

View File

@@ -79,6 +79,10 @@ class FundingTxFetcher {
} }
const parts = channelId.split('x'); const parts = channelId.split('x');
if (parts.length < 3) {
logger.debug(`Channel ID ${channelId} does not seem valid, should contains at least 3 parts separated by 'x'`, logger.tags.ln);
return null;
}
const blockHeight = parts[0]; const blockHeight = parts[0];
const txIdx = parts[1]; const txIdx = parts[1];
const outputIdx = parts[2]; const outputIdx = parts[2];
@@ -99,6 +103,10 @@ class FundingTxFetcher {
} }
const txid = block.tx[txIdx]; const txid = block.tx[txIdx];
if (!txid) {
logger.debug(`Cannot cache ${channelId} funding tx. TX index ${txIdx} does not exist in block ${block.hash ?? block.id}`, logger.tags.ln);
return null;
}
const rawTx = await bitcoinClient.getRawTransaction(txid); const rawTx = await bitcoinClient.getRawTransaction(txid);
const tx = await bitcoinClient.decodeRawTransaction(rawTx); const tx = await bitcoinClient.decodeRawTransaction(rawTx);

View File

@@ -12,7 +12,7 @@ export function hexToColor(hex: string): Color {
} }
export function colorToHex(color: Color): string { export function colorToHex(color: Color): string {
return [color.r, color.g, color.b].map(c => Math.round(c * 255).toString(16)).join(''); return [color.r, color.g, color.b].map(c => Math.max(0, Math.min(Math.round(c * 255), 255)).toString(16)).join('');
} }
export function desaturate(color: Color, amount: number): Color { export function desaturate(color: Color, amount: number): Color {
@@ -35,6 +35,8 @@ export function darken(color: Color, amount: number): Color {
} }
export function mix(color1: Color, color2: Color, amount: number): Color { export function mix(color1: Color, color2: Color, amount: number): Color {
// clamp to 0-1
amount = Math.max(0, Math.min(amount, 1));
return { return {
r: color1.r * (1 - amount) + color2.r * amount, r: color1.r * (1 - amount) + color2.r * amount,
g: color1.g * (1 - amount) + color2.g * amount, g: color1.g * (1 - amount) + color2.g * amount,

View File

@@ -8,9 +8,12 @@ import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pip
import { renderSats } from '@app/shared/common.utils'; import { renderSats } from '@app/shared/common.utils';
import { colorToHex, hexToColor, mix } from '@components/block-overview-graph/utils'; import { colorToHex, hexToColor, mix } from '@components/block-overview-graph/utils';
import { TimeService } from '@app/services/time.service'; import { TimeService } from '@app/services/time.service';
import { WebsocketService } from '@app/services/websocket.service';
import { Acceleration } from '@interfaces/node-api.interface';
import { defaultAuditColors } from '@components/block-overview-graph/utils';
const newColorHex = '1bd8f4'; const newColorHex = '1BF4AF';
const oldColorHex = '9339f4'; const oldColorHex = '3C39F4';
const pendingColorHex = 'eba814'; const pendingColorHex = 'eba814';
const newColor = hexToColor(newColorHex); const newColor = hexToColor(newColorHex);
const oldColor = hexToColor(oldColorHex); const oldColor = hexToColor(oldColorHex);
@@ -61,8 +64,10 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
@Input() widget: boolean = false; @Input() widget: boolean = false;
subscription: Subscription; subscription: Subscription;
accelerationsSubscription: Subscription;
lastUpdate: number = 0; lastUpdate: number = 0;
updateInterval; updateInterval;
accelerationMap: Record<string, Acceleration> = {};
chartOptions: EChartsOption = {}; chartOptions: EChartsOption = {};
chartInitOptions = { chartInitOptions = {
@@ -80,6 +85,7 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
private router: Router, private router: Router,
private relativeUrlPipe: RelativeUrlPipe, private relativeUrlPipe: RelativeUrlPipe,
private timeService: TimeService, private timeService: TimeService,
private websocketService: WebsocketService,
) { ) {
// re-render the chart every 10 seconds, to keep the age colors up to date // re-render the chart every 10 seconds, to keep the age colors up to date
this.updateInterval = setInterval(() => { this.updateInterval = setInterval(() => {
@@ -87,6 +93,18 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
this.prepareChartOptions(this.utxos); this.prepareChartOptions(this.utxos);
} }
}, 10000); }, 10000);
this.websocketService.startTrackAccelerations();
this.accelerationsSubscription = this.stateService.liveAccelerations$.subscribe((accelerations) => {
this.accelerationMap = accelerations.reduce((acc, acceleration) => {
acc[acceleration.txid] = acceleration;
return acc;
}, {});
this.applyAccelerations();
this.prepareChartOptions(this.utxos);
});
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@@ -95,10 +113,20 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
return; return;
} }
if (changes.utxos) { if (changes.utxos) {
this.applyAccelerations();
this.prepareChartOptions(this.utxos); this.prepareChartOptions(this.utxos);
} }
} }
applyAccelerations(): void {
for (const utxo of this.utxos) {
delete utxo.status['accelerated'];
if (this.accelerationMap[utxo.txid]) {
utxo.status['accelerated'] = true;
}
}
}
prepareChartOptions(utxos: Utxo[]): void { prepareChartOptions(utxos: Utxo[]): void {
if (!utxos || utxos.length === 0) { if (!utxos || utxos.length === 0) {
return; return;
@@ -311,7 +339,12 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
<br> <br>
${valueStr} ${valueStr}
<br> <br>
${utxo.status.confirmed ? 'Confirmed ' + this.timeService.calculate(utxo.status.block_time, 'since', true, 1, 'minute').text : 'Pending'} ${utxo.status.confirmed
? 'Confirmed ' + this.timeService.calculate(utxo.status.block_time, 'since', true, 1, 'minute').text
: utxo.status['accelerated']
? 'Accelerated'
: 'Pending'
}
`; `;
}, },
} }
@@ -322,7 +355,9 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
} }
getColor(utxo: Utxo): string { getColor(utxo: Utxo): string {
if (utxo.status.confirmed) { if (utxo.status['accelerated']) {
return colorToHex(defaultAuditColors.accelerated);
} else if (utxo.status.confirmed) {
const age = Date.now() / 1000 - utxo.status.block_time; const age = Date.now() / 1000 - utxo.status.block_time;
const oneHour = 60 * 60; const oneHour = 60 * 60;
const fourYears = 4 * 365 * 24 * 60 * 60; const fourYears = 4 * 365 * 24 * 60 * 60;
@@ -366,6 +401,8 @@ export class UtxoGraphComponent implements OnChanges, OnDestroy {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
clearInterval(this.updateInterval); clearInterval(this.updateInterval);
this.websocketService.stopTrackAccelerations();
this.accelerationsSubscription.unsubscribe();
} }
isMobile(): boolean { isMobile(): boolean {