Merge branch 'master' into docker_vars_test

This commit is contained in:
Felipe Knorr Kuhn 2023-05-26 07:16:29 -07:00 committed by GitHub
commit c382e03e4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 133 additions and 55 deletions

View File

@ -6,14 +6,15 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first
class Audit { class Audit {
auditBlock(transactions: TransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: TransactionExtended }) auditBlock(transactions: TransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: TransactionExtended })
: { censored: string[], added: string[], fresh: string[], score: number, similarity: number } { : { censored: string[], added: string[], fresh: string[], sigop: string[], score: number, similarity: number } {
if (!projectedBlocks?.[0]?.transactionIds || !mempool) { if (!projectedBlocks?.[0]?.transactionIds || !mempool) {
return { censored: [], added: [], fresh: [], score: 0, similarity: 1 }; return { censored: [], added: [], fresh: [], sigop: [], score: 0, similarity: 1 };
} }
const matches: string[] = []; // present in both mined block and template const matches: string[] = []; // present in both mined block and template
const added: string[] = []; // present in mined block, not in template const added: string[] = []; // present in mined block, not in template
const fresh: string[] = []; // missing, but firstSeen within PROPAGATION_MARGIN const fresh: string[] = []; // missing, but firstSeen within PROPAGATION_MARGIN
const sigop: string[] = []; // missing, but possibly has an adjusted vsize due to high sigop count
const isCensored = {}; // missing, without excuse const isCensored = {}; // missing, without excuse
const isDisplaced = {}; const isDisplaced = {};
let displacedWeight = 0; let displacedWeight = 0;
@ -37,6 +38,8 @@ class Audit {
// tx is recent, may have reached the miner too late for inclusion // tx is recent, may have reached the miner too late for inclusion
if (mempool[txid]?.firstSeen != null && (now - (mempool[txid]?.firstSeen || 0)) <= PROPAGATION_MARGIN) { if (mempool[txid]?.firstSeen != null && (now - (mempool[txid]?.firstSeen || 0)) <= PROPAGATION_MARGIN) {
fresh.push(txid); fresh.push(txid);
} else if (this.isPossibleHighSigop(mempool[txid])) {
sigop.push(txid);
} else { } else {
isCensored[txid] = true; isCensored[txid] = true;
} }
@ -137,10 +140,19 @@ class Audit {
censored: Object.keys(isCensored), censored: Object.keys(isCensored),
added, added,
fresh, fresh,
sigop,
score, score,
similarity, similarity,
}; };
} }
// Detect transactions with a possibly adjusted vsize due to high sigop count
// very rough heuristic based on number of OP_CHECKMULTISIG outputs
// will miss cases with other sources of sigops
isPossibleHighSigop(tx: TransactionExtended): boolean {
const numBareMultisig = tx.vout.reduce((count, vout) => count + (vout.scriptpubkey_asm.includes('OP_CHECKMULTISIG') ? 1 : 0), 0);
return (numBareMultisig * 400) > tx.vsize;
}
} }
export default new Audit(); export default new Audit();

View File

@ -306,7 +306,7 @@ class Blocks {
} }
const asciiScriptSig = transactionUtils.hex2ascii(txMinerInfo.vin[0].scriptsig); const asciiScriptSig = transactionUtils.hex2ascii(txMinerInfo.vin[0].scriptsig);
const address = txMinerInfo.vout[0].scriptpubkey_address; const addresses = txMinerInfo.vout.map((vout) => vout.scriptpubkey_address).filter((address) => address);
let pools: PoolTag[] = []; let pools: PoolTag[] = [];
if (config.DATABASE.ENABLED === true) { if (config.DATABASE.ENABLED === true) {
@ -316,11 +316,13 @@ class Blocks {
} }
for (let i = 0; i < pools.length; ++i) { for (let i = 0; i < pools.length; ++i) {
if (address !== undefined) { if (addresses.length) {
const addresses: string[] = typeof pools[i].addresses === 'string' ? const poolAddresses: string[] = typeof pools[i].addresses === 'string' ?
JSON.parse(pools[i].addresses) : pools[i].addresses; JSON.parse(pools[i].addresses) : pools[i].addresses;
if (addresses.indexOf(address) !== -1) { for (let y = 0; y < poolAddresses.length; y++) {
return pools[i]; if (addresses.indexOf(poolAddresses[y]) !== -1) {
return pools[i];
}
} }
} }

View File

@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
import { RowDataPacket } from 'mysql2'; import { RowDataPacket } from 'mysql2';
class DatabaseMigration { class DatabaseMigration {
private static currentVersion = 59; private static currentVersion = 60;
private queryTimeout = 3600_000; private queryTimeout = 3600_000;
private statisticsAddedIndexed = false; private statisticsAddedIndexed = false;
private uniqueLogs: string[] = []; private uniqueLogs: string[] = [];
@ -516,6 +516,11 @@ class DatabaseMigration {
// https://github.com/mempool/mempool/issues/3360 // https://github.com/mempool/mempool/issues/3360
await this.$executeQuery(`TRUNCATE prices`); await this.$executeQuery(`TRUNCATE prices`);
} }
if (databaseSchemaVersion < 60 && isBitcoin === true) {
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD sigop_txs JSON DEFAULT "[]"');
await this.updateToSchemaVersion(60);
}
} }
/** /**

View File

@ -21,6 +21,7 @@ class DiskCache {
private static RBF_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/rbfcache.json'; private static RBF_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/rbfcache.json';
private static CHUNK_FILES = 25; private static CHUNK_FILES = 25;
private isWritingCache = false; private isWritingCache = false;
private ignoreBlocksCache = false;
private semaphore: { resume: (() => void)[], locks: number } = { private semaphore: { resume: (() => void)[], locks: number } = {
resume: [], resume: [],
@ -218,8 +219,13 @@ class DiskCache {
} }
await memPool.$setMempool(data.mempool); await memPool.$setMempool(data.mempool);
blocks.setBlocks(data.blocks); if (!this.ignoreBlocksCache) {
blocks.setBlockSummaries(data.blockSummaries || []); blocks.setBlocks(data.blocks);
blocks.setBlockSummaries(data.blockSummaries || []);
} else {
logger.info('Re-saving cache with empty recent blocks data');
await this.$saveCacheToDisk(true);
}
} catch (e) { } catch (e) {
logger.warn('Failed to parse mempoool and blocks cache. Skipping. Reason: ' + (e instanceof Error ? e.message : e)); logger.warn('Failed to parse mempoool and blocks cache. Skipping. Reason: ' + (e instanceof Error ? e.message : e));
} }
@ -273,6 +279,10 @@ class DiskCache {
} }
} }
} }
public setIgnoreBlocksCache(): void {
this.ignoreBlocksCache = true;
}
} }
export default new DiskCache(); export default new DiskCache();

View File

@ -41,7 +41,7 @@ class PoolsParser {
public async migratePoolsJson(): Promise<void> { public async migratePoolsJson(): Promise<void> {
// We also need to wipe the backend cache to make sure we don't serve blocks with // We also need to wipe the backend cache to make sure we don't serve blocks with
// the wrong mining pool (usually happen with unknown blocks) // the wrong mining pool (usually happen with unknown blocks)
diskCache.wipeCache(); diskCache.setIgnoreBlocksCache();
await this.$insertUnknownPool(); await this.$insertUnknownPool();
@ -118,10 +118,6 @@ class PoolsParser {
* @param pool * @param pool
*/ */
private async $deleteBlocksForPool(pool: PoolTag): Promise<void> { private async $deleteBlocksForPool(pool: PoolTag): Promise<void> {
if (config.MEMPOOL.AUTOMATIC_BLOCK_REINDEXING === false) {
return;
}
// Get oldest blocks mined by the pool and assume pools-v2.json updates only concern most recent years // Get oldest blocks mined by the pool and assume pools-v2.json updates only concern most recent years
// Ignore early days of Bitcoin as there were no mining pool yet // Ignore early days of Bitcoin as there were no mining pool yet
const [oldestPoolBlock]: any[] = await DB.query(` const [oldestPoolBlock]: any[] = await DB.query(`

View File

@ -557,7 +557,7 @@ class WebsocketHandler {
} }
if (Common.indexingEnabled() && memPool.isInSync()) { if (Common.indexingEnabled() && memPool.isInSync()) {
const { censored, added, fresh, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); const { censored, added, fresh, sigop, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool);
const matchRate = Math.round(score * 100 * 100) / 100; const matchRate = Math.round(score * 100 * 100) / 100;
const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => {
@ -584,6 +584,7 @@ class WebsocketHandler {
addedTxs: added, addedTxs: added,
missingTxs: censored, missingTxs: censored,
freshTxs: fresh, freshTxs: fresh,
sigopTxs: sigop,
matchRate: matchRate, matchRate: matchRate,
}); });

View File

@ -32,6 +32,7 @@ export interface BlockAudit {
hash: string, hash: string,
missingTxs: string[], missingTxs: string[],
freshTxs: string[], freshTxs: string[],
sigopTxs: string[],
addedTxs: string[], addedTxs: string[],
matchRate: number, matchRate: number,
} }

View File

@ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces';
class BlocksAuditRepositories { class BlocksAuditRepositories {
public async $saveAudit(audit: BlockAudit): Promise<void> { public async $saveAudit(audit: BlockAudit): Promise<void> {
try { try {
await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, match_rate) await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, match_rate)
VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs), VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs),
JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), audit.matchRate]); JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), audit.matchRate]);
} catch (e: any) { } catch (e: any) {
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`); logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`);
@ -52,7 +52,7 @@ class BlocksAuditRepositories {
const [rows]: any[] = await DB.query( const [rows]: any[] = await DB.query(
`SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size, `SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size,
blocks.weight, blocks.tx_count, blocks.weight, blocks.tx_count,
transactions, template, missing_txs as missingTxs, added_txs as addedTxs, fresh_txs as freshTxs, match_rate as matchRate transactions, template, missing_txs as missingTxs, added_txs as addedTxs, fresh_txs as freshTxs, sigop_txs as sigopTxs, match_rate as matchRate
FROM blocks_audits FROM blocks_audits
JOIN blocks ON blocks.hash = blocks_audits.hash JOIN blocks ON blocks.hash = blocks_audits.hash
JOIN blocks_summaries ON blocks_summaries.id = blocks_audits.hash JOIN blocks_summaries ON blocks_summaries.id = blocks_audits.hash
@ -63,6 +63,7 @@ class BlocksAuditRepositories {
rows[0].missingTxs = JSON.parse(rows[0].missingTxs); rows[0].missingTxs = JSON.parse(rows[0].missingTxs);
rows[0].addedTxs = JSON.parse(rows[0].addedTxs); rows[0].addedTxs = JSON.parse(rows[0].addedTxs);
rows[0].freshTxs = JSON.parse(rows[0].freshTxs); rows[0].freshTxs = JSON.parse(rows[0].freshTxs);
rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs);
rows[0].transactions = JSON.parse(rows[0].transactions); rows[0].transactions = JSON.parse(rows[0].transactions);
rows[0].template = JSON.parse(rows[0].template); rows[0].template = JSON.parse(rows[0].template);

View File

@ -205,9 +205,9 @@
<img class="image" src="/resources/profile/nix-bitcoin.png" /> <img class="image" src="/resources/profile/nix-bitcoin.png" />
<span>NixOS</span> <span>NixOS</span>
</a> </a>
<a href="https://github.com/Start9Labs/embassy-os" target="_blank" title="EmbassyOS"> <a href="https://github.com/Start9Labs/start-os" target="_blank" title="StartOS">
<img class="image" src="/resources/profile/start9.png" /> <img class="image" src="/resources/profile/start9.png" />
<span>EmbassyOS</span> <span>StartOS</span>
</a> </a>
<a href="https://github.com/btcpayserver/btcpayserver" target="_blank" title="BTCPay Server"> <a href="https://github.com/btcpayserver/btcpayserver" target="_blank" title="BTCPay Server">
<img class="image not-rounded" src="/resources/profile/btcpayserver.svg" /> <img class="image not-rounded" src="/resources/profile/btcpayserver.svg" />

View File

@ -37,7 +37,7 @@ export default class TxView implements TransactionStripped {
value: number; value: number;
feerate: number; feerate: number;
rate?: number; rate?: number;
status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';
scene?: BlockScene; scene?: BlockScene;
@ -171,6 +171,7 @@ export default class TxView implements TransactionStripped {
case 'censored': case 'censored':
return auditColors.censored; return auditColors.censored;
case 'missing': case 'missing':
case 'sigop':
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
case 'fresh': case 'fresh':
return auditColors.missing; return auditColors.missing;

View File

@ -44,6 +44,7 @@
<td *ngSwitchCase="'found'"><span class="badge badge-success" i18n="transaction.audit.match">Match</span></td> <td *ngSwitchCase="'found'"><span class="badge badge-success" i18n="transaction.audit.match">Match</span></td>
<td *ngSwitchCase="'censored'"><span class="badge badge-danger" i18n="transaction.audit.removed">Removed</span></td> <td *ngSwitchCase="'censored'"><span class="badge badge-danger" i18n="transaction.audit.removed">Removed</span></td>
<td *ngSwitchCase="'missing'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td> <td *ngSwitchCase="'missing'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
<td *ngSwitchCase="'sigop'"><span class="badge badge-warning" i18n="transaction.audit.sigop">High sigop count</span></td>
<td *ngSwitchCase="'fresh'"><span class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span></td> <td *ngSwitchCase="'fresh'"><span class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span></td>
<td *ngSwitchCase="'added'"><span class="badge badge-warning" i18n="transaction.audit.added">Added</span></td> <td *ngSwitchCase="'added'"><span class="badge badge-warning" i18n="transaction.audit.added">Added</span></td>
<td *ngSwitchCase="'selected'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td> <td *ngSwitchCase="'selected'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>

View File

@ -335,6 +335,7 @@ export class BlockComponent implements OnInit, OnDestroy {
const isMissing = {}; const isMissing = {};
const isSelected = {}; const isSelected = {};
const isFresh = {}; const isFresh = {};
const isSigop = {};
this.numMissing = 0; this.numMissing = 0;
this.numUnexpected = 0; this.numUnexpected = 0;
@ -354,6 +355,9 @@ export class BlockComponent implements OnInit, OnDestroy {
for (const txid of blockAudit.freshTxs || []) { for (const txid of blockAudit.freshTxs || []) {
isFresh[txid] = true; isFresh[txid] = true;
} }
for (const txid of blockAudit.sigopTxs || []) {
isSigop[txid] = true;
}
// set transaction statuses // set transaction statuses
for (const tx of blockAudit.template) { for (const tx of blockAudit.template) {
tx.context = 'projected'; tx.context = 'projected';
@ -362,7 +366,7 @@ export class BlockComponent implements OnInit, OnDestroy {
} else if (inBlock[tx.txid]) { } else if (inBlock[tx.txid]) {
tx.status = 'found'; tx.status = 'found';
} else { } else {
tx.status = isFresh[tx.txid] ? 'fresh' : 'missing'; tx.status = isFresh[tx.txid] ? 'fresh' : (isSigop[tx.txid] ? 'sigop' : 'missing');
isMissing[tx.txid] = true; isMissing[tx.txid] = true;
this.numMissing++; this.numMissing++;
} }

View File

@ -24,6 +24,7 @@ import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/grap
export class MempoolGraphComponent implements OnInit, OnChanges { export class MempoolGraphComponent implements OnInit, OnChanges {
@Input() data: any[]; @Input() data: any[];
@Input() filterSize = 100000; @Input() filterSize = 100000;
@Input() limitFilterFee = 1;
@Input() height: number | string = 200; @Input() height: number | string = 200;
@Input() top: number | string = 20; @Input() top: number | string = 20;
@Input() right: number | string = 10; @Input() right: number | string = 10;
@ -40,7 +41,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
}; };
windowPreference: string; windowPreference: string;
hoverIndexSerie = 0; hoverIndexSerie = 0;
maxFee: number;
feeLimitIndex: number; feeLimitIndex: number;
maxFeeIndex: number;
feeLevelsOrdered = []; feeLevelsOrdered = [];
chartColorsOrdered = chartColors; chartColorsOrdered = chartColors;
inverted: boolean; inverted: boolean;
@ -98,8 +101,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
} }
generateArray(mempoolStats: OptimizedMempoolStats[]) { generateArray(mempoolStats: OptimizedMempoolStats[]) {
let finalArray: number[][][] = []; const finalArray: number[][][] = [];
let feesArray: number[][] = []; let feesArray: number[][] = [];
let maxTier = 0; let maxTier = 0;
for (let index = 37; index > -1; index--) { for (let index = 37; index > -1; index--) {
feesArray = []; feesArray = [];
@ -111,7 +115,8 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
}); });
finalArray.push(feesArray); finalArray.push(feesArray);
} }
this.feeLimitIndex = maxTier; this.maxFeeIndex = maxTier;
finalArray.reverse(); finalArray.reverse();
return finalArray; return finalArray;
} }
@ -124,7 +129,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
const newColors = []; const newColors = [];
for (let index = 0; index < series.length; index++) { for (let index = 0; index < series.length; index++) {
const value = series[index]; const value = series[index];
if (index < this.feeLimitIndex) { if (index >= this.feeLimitIndex && index <= this.maxFeeIndex) {
newColors.push(this.chartColorsOrdered[index]); newColors.push(this.chartColorsOrdered[index]);
seriesGraph.push({ seriesGraph.push({
zlevel: 0, zlevel: 0,
@ -383,21 +388,26 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
orderLevels() { orderLevels() {
this.feeLevelsOrdered = []; this.feeLevelsOrdered = [];
let maxIndex = Math.min(feeLevels.length, this.feeLimitIndex); const maxIndex = Math.min(feeLevels.length, this.maxFeeIndex);
for (let i = 0; i < maxIndex; i++) { for (let i = 0; i < feeLevels.length; i++) {
if (feeLevels[i] === this.limitFilterFee) {
this.feeLimitIndex = i;
}
if (feeLevels[i] <= feeLevels[this.maxFeeIndex]) {
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
if (i === maxIndex - 1) { if (i === maxIndex || feeLevels[i] == null) {
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)}+`); this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)}+`);
} else { } else {
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`); this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`);
} }
} else { } else {
if (i === maxIndex - 1) { if (i === maxIndex || feeLevels[i] == null) {
this.feeLevelsOrdered.push(`${feeLevels[i]}+`); this.feeLevelsOrdered.push(`${feeLevels[i]}+`);
} else { } else {
this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`); this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`);
} }
} }
}
} }
this.chartColorsOrdered = chartColors.slice(0, this.feeLevelsOrdered.length); this.chartColorsOrdered = chartColors.slice(0, this.feeLevelsOrdered.length);
} }

View File

@ -62,7 +62,7 @@
<div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees"> <div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees">
<ul> <ul>
<ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData"> <ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData">
<ng-template [ngIf]="feeData.fee <= 400"> <ng-template [ngIf]="feeData.fee <= (feeLevels[maxFeeIndex])">
<li (click)="filterFeeIndex = feeData.fee" <li (click)="filterFeeIndex = feeData.fee"
[class]="filterFeeIndex > feeData.fee ? 'inactive' : ''"> [class]="filterFeeIndex > feeData.fee ? 'inactive' : ''">
<span class="square" [ngStyle]="{'backgroundColor': feeData.color}"></span> <span class="square" [ngStyle]="{'backgroundColor': feeData.color}"></span>
@ -84,7 +84,8 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="incoming-transactions-graph"> <div class="incoming-transactions-graph">
<app-mempool-graph #mempoolgraph dir="ltr" [template]="'advanced'" [height]="500" [left]="65" [right]="10" <app-mempool-graph #mempoolgraph dir="ltr" [template]="'advanced'"
[limitFilterFee]="filterFeeIndex" [height]="500" [left]="65" [right]="10"
[data]="mempoolStats && mempoolStats.length ? mempoolStats : null"></app-mempool-graph> [data]="mempoolStats && mempoolStats.length ? mempoolStats : null"></app-mempool-graph>
</div> </div>
</div> </div>

View File

@ -30,7 +30,9 @@ export class StatisticsComponent implements OnInit {
spinnerLoading = false; spinnerLoading = false;
feeLevels = feeLevels; feeLevels = feeLevels;
chartColors = chartColors; chartColors = chartColors;
filterSize = 100000;
filterFeeIndex = 1; filterFeeIndex = 1;
maxFeeIndex: number;
dropDownOpen = false; dropDownOpen = false;
mempoolStats: OptimizedMempoolStats[] = []; mempoolStats: OptimizedMempoolStats[] = [];
@ -134,6 +136,16 @@ export class StatisticsComponent implements OnInit {
mempoolStats.reverse(); mempoolStats.reverse();
const labels = mempoolStats.map(stats => stats.added); const labels = mempoolStats.map(stats => stats.added);
let maxTier = 0;
for (let index = 37; index > -1; index--) {
mempoolStats.forEach((stats) => {
if (stats.vsizes[index] >= this.filterSize) {
maxTier = Math.max(maxTier, index);
}
});
}
this.maxFeeIndex = maxTier;
this.capExtremeVbytesValues(); this.capExtremeVbytesValues();
this.mempoolTransactionsWeightPerSecondData = { this.mempoolTransactionsWeightPerSecondData = {
@ -152,27 +164,42 @@ export class StatisticsComponent implements OnInit {
} }
setFeeLevelDropdownData() { setFeeLevelDropdownData() {
let _feeLevels = feeLevels let _feeLevels = feeLevels;
let _chartColors = chartColors; let _chartColors = chartColors;
if (!this.inverted) { if (!this.inverted) {
_feeLevels = [...feeLevels].reverse(); _feeLevels = [...feeLevels].reverse();
_chartColors = [...chartColors].reverse(); _chartColors = [...chartColors].reverse();
} }
_feeLevels.forEach((fee, i) => { _feeLevels.forEach((fee, i) => {
let range;
const nextIndex = this.inverted ? i + 1 : i - 1;
if (this.stateService.isLiquid()) {
if (_feeLevels[nextIndex] == null) {
range = `${(_feeLevels[i] / 10).toFixed(1)}+`;
} else {
range = `${(_feeLevels[i] / 10).toFixed(1)} - ${(_feeLevels[nextIndex] / 10).toFixed(1)}`;
}
} else {
if (_feeLevels[nextIndex] == null) {
range = `${_feeLevels[i]}+`;
} else {
range = `${_feeLevels[i]} - ${_feeLevels[nextIndex]}`;
}
}
if (this.inverted) { if (this.inverted) {
this.feeLevelDropdownData.push({ this.feeLevelDropdownData.push({
fee: fee, fee: fee,
range: this.stateService.isLiquid() ? `${(_feeLevels[i] / 10).toFixed(1)} - ${(_feeLevels[i + 1] / 10).toFixed(1)}` : `${_feeLevels[i]} - ${_feeLevels[i + 1]}`, range,
color: _chartColors[i], color: _chartColors[i],
}); });
} else { } else {
this.feeLevelDropdownData.push({ this.feeLevelDropdownData.push({
fee: fee, fee: fee,
range: this.stateService.isLiquid() ? `${(_feeLevels[i] / 10).toFixed(1)} - ${(_feeLevels[i - 1] / 10).toFixed(1)}` : `${_feeLevels[i]} - ${_feeLevels[i - 1]}`, range,
color: _chartColors[i - 1], color: _chartColors[i - 1],
}); });
} }
}) });
} }
/** /**

View File

@ -26,7 +26,6 @@
<div class="mempool-graph"> <div class="mempool-graph">
<app-mempool-graph <app-mempool-graph
[template]="'widget'" [template]="'widget'"
[filterSize]="1000000"
[data]="mempoolStats.value?.mempool" [data]="mempoolStats.value?.mempool"
[windowPreferenceOverride]="'2h'" [windowPreferenceOverride]="'2h'"
></app-mempool-graph> ></app-mempool-graph>

View File

@ -155,7 +155,7 @@ export interface TransactionStripped {
fee: number; fee: number;
vsize: number; vsize: number;
value: number; value: number;
status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected';
} }
interface RbfTransaction extends TransactionStripped { interface RbfTransaction extends TransactionStripped {

View File

@ -76,7 +76,7 @@ export interface TransactionStripped {
vsize: number; vsize: number;
value: number; value: number;
rate?: number; // effective fee rate rate?: number; // effective fee rate
status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';
} }

View File

@ -1312,8 +1312,13 @@ case $OS in
osSudo "${ROOT_USER}" pw usermod ${MEMPOOL_USER} -G "${CLN_GROUP}" osSudo "${ROOT_USER}" pw usermod ${MEMPOOL_USER} -G "${CLN_GROUP}"
osSudo "${ROOT_USER}" chsh -s `which zsh` "${CLN_USER}" osSudo "${ROOT_USER}" chsh -s `which zsh` "${CLN_USER}"
echo "export PATH=$PATH:$HOME/.local/bin" >> "${CLN_HOME}/.zshrc" echo "export PATH=$PATH:$HOME/.local/bin" >> "${CLN_HOME}/.zshrc"
osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}" osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/bitcoin"
osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning" "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}" osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/signet"
osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/testnet"
osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning"
osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning/bitcoin"
osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning/signet"
osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning/testnet"
osSudo "${ROOT_USER}" chown -R "${CLN_USER}:${CLN_GROUP}" "${CLN_HOME}" osSudo "${ROOT_USER}" chown -R "${CLN_USER}:${CLN_GROUP}" "${CLN_HOME}"
echo "[*] Creating symlink to .bitcoin folder" echo "[*] Creating symlink to .bitcoin folder"
osSudo "${CLN_USER}" ln -s "${BITCOIN_HOME}/.bitcoin" "${CLN_HOME}/.bitcoin" osSudo "${CLN_USER}" ln -s "${BITCOIN_HOME}/.bitcoin" "${CLN_HOME}/.bitcoin"
@ -1321,16 +1326,18 @@ case $OS in
echo "[*] Installing Core Lightning package" echo "[*] Installing Core Lightning package"
osPackageInstall ${CLN_PKG} osPackageInstall ${CLN_PKG}
echo "[*] Installing Core Lightning mainnet Cronjob" ######## FIXME: this code doesn't work properly, needs fixing
public_ipv4=$( ifconfig | grep 'inet ' | awk -F ' ' '{ print $2 }' | grep -v '^103\.165\.192\.' | grep -v '^127\.0\.0\.1' ) #
public_ipv6=$( ifconfig | grep 'inet6' | awk -F ' ' '{ print $2 }' | grep -v '^2001:df6:7280::' | grep -v '^fe80::' | grep -v '^::1' ) # echo "[*] Installing Core Lightning mainnet Cronjob"
# public_ipv4=$( ifconfig | grep 'inet ' | awk -F ' ' '{ print $2 }' | grep -v '^103\.165\.192\.' | grep -v '^127\.0\.0\.1' )
crontab_cln+="@reboot sleep 10 ; screen -dmS main lightningd --rpc-file-mode 0660 --alias `hostname` --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n" # public_ipv6=$( ifconfig | grep 'inet6' | awk -F ' ' '{ print $2 }' | grep -v '^2001:df6:7280::' | grep -v '^fe80::' | grep -v '^::1' )
crontab_cln+="@reboot sleep 10 ; screen -dmS tes lightningd --rpc-file-mode 0660 --alias `hostname` --network testnet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n" #
crontab_cln+="@reboot sleep 10 ; screen -dmS sig lightningd --rpc-file-mode 0660 --alias `hostname` --network signet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6 \n" # crontab_cln+="@reboot sleep 10 ; screen -dmS main lightningd --rpc-file-mode 0660 --alias `hostname` --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
crontab_cln+="@reboot sleep 20 ; /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n" # crontab_cln+="@reboot sleep 10 ; screen -dmS tes lightningd --rpc-file-mode 0660 --alias `hostname` --network testnet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
crontab_cln+="1 * * * * /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n" # crontab_cln+="@reboot sleep 10 ; screen -dmS sig lightningd --rpc-file-mode 0660 --alias `hostname` --network signet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6 \n"
echo "${crontab_cln}" | crontab -u "${CLN_USER}" - # crontab_cln+="@reboot sleep 20 ; /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
# crontab_cln+="1 * * * * /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
# echo "${crontab_cln}" | crontab -u "${CLN_USER}" -
;; ;;
Debian) Debian)
;; ;;
@ -1813,7 +1820,7 @@ echo "[*] Adding MySQL configuration"
case $OS in case $OS in
FreeBSD) FreeBSD)
osSudo "${ROOT_USER}" service mysql-server start osSudo "${ROOT_USER}" service mysql-server onestart
;; ;;
Debian) Debian)
osSudo "${ROOT_USER}" service mysql start osSudo "${ROOT_USER}" service mysql start

View File

@ -59,7 +59,7 @@ location = / {
} }
# cache /<lang>/main.f40e91d908a068a2.js forever since they never change # cache /<lang>/main.f40e91d908a068a2.js forever since they never change
location ~ ^/([a-z][a-z])/(.+\..+\.(js|css)) { location ~ ^/([a-z][a-z])/(.+\..+\.(js|css))$ {
try_files $uri =404; try_files $uri =404;
expires 1y; expires 1y;
} }
@ -92,7 +92,7 @@ location /resources/config. {
} }
# cache /main.f40e91d908a068a2.js forever since they never change # cache /main.f40e91d908a068a2.js forever since they never change
location ~* ^/.+\..+\.(js|css) { location ~* ^/.+\..+\.(js|css)$ {
try_files /$lang/$uri /en-US/$uri =404; try_files /$lang/$uri /en-US/$uri =404;
expires 1y; expires 1y;
} }