Merge branch 'master' into docker_vars_test
This commit is contained in:
		
						commit
						c382e03e4a
					
				| @ -6,14 +6,15 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first | ||||
| 
 | ||||
| class Audit { | ||||
|   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) { | ||||
|       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 added: string[] = []; // present in mined block, not in template
 | ||||
|     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 isDisplaced = {}; | ||||
|     let displacedWeight = 0; | ||||
| @ -37,6 +38,8 @@ class Audit { | ||||
|         // tx is recent, may have reached the miner too late for inclusion
 | ||||
|         if (mempool[txid]?.firstSeen != null && (now - (mempool[txid]?.firstSeen || 0)) <= PROPAGATION_MARGIN) { | ||||
|           fresh.push(txid); | ||||
|         } else if (this.isPossibleHighSigop(mempool[txid])) { | ||||
|           sigop.push(txid); | ||||
|         } else { | ||||
|           isCensored[txid] = true; | ||||
|         } | ||||
| @ -137,10 +140,19 @@ class Audit { | ||||
|       censored: Object.keys(isCensored), | ||||
|       added, | ||||
|       fresh, | ||||
|       sigop, | ||||
|       score, | ||||
|       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(); | ||||
| @ -306,7 +306,7 @@ class Blocks { | ||||
|     } | ||||
| 
 | ||||
|     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[] = []; | ||||
|     if (config.DATABASE.ENABLED === true) { | ||||
| @ -316,11 +316,13 @@ class Blocks { | ||||
|     } | ||||
| 
 | ||||
|     for (let i = 0; i < pools.length; ++i) { | ||||
|       if (address !== undefined) { | ||||
|         const addresses: string[] = typeof pools[i].addresses === 'string' ? | ||||
|       if (addresses.length) { | ||||
|         const poolAddresses: string[] = typeof pools[i].addresses === 'string' ? | ||||
|           JSON.parse(pools[i].addresses) : pools[i].addresses; | ||||
|         if (addresses.indexOf(address) !== -1) { | ||||
|           return pools[i]; | ||||
|         for (let y = 0; y < poolAddresses.length; y++) { | ||||
|           if (addresses.indexOf(poolAddresses[y]) !== -1) { | ||||
|             return pools[i]; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|  | ||||
| @ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; | ||||
| import { RowDataPacket } from 'mysql2'; | ||||
| 
 | ||||
| class DatabaseMigration { | ||||
|   private static currentVersion = 59; | ||||
|   private static currentVersion = 60; | ||||
|   private queryTimeout = 3600_000; | ||||
|   private statisticsAddedIndexed = false; | ||||
|   private uniqueLogs: string[] = []; | ||||
| @ -516,6 +516,11 @@ class DatabaseMigration { | ||||
|       // https://github.com/mempool/mempool/issues/3360
 | ||||
|       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); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | ||||
| @ -21,6 +21,7 @@ class DiskCache { | ||||
|   private static RBF_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/rbfcache.json'; | ||||
|   private static CHUNK_FILES = 25; | ||||
|   private isWritingCache = false; | ||||
|   private ignoreBlocksCache = false; | ||||
| 
 | ||||
|   private semaphore: { resume: (() => void)[], locks: number } = { | ||||
|     resume: [], | ||||
| @ -218,8 +219,13 @@ class DiskCache { | ||||
|       } | ||||
| 
 | ||||
|       await memPool.$setMempool(data.mempool); | ||||
|       blocks.setBlocks(data.blocks); | ||||
|       blocks.setBlockSummaries(data.blockSummaries || []); | ||||
|       if (!this.ignoreBlocksCache) { | ||||
|         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) { | ||||
|       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(); | ||||
|  | ||||
| @ -41,7 +41,7 @@ class PoolsParser { | ||||
|   public async migratePoolsJson(): Promise<void> { | ||||
|     // 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)
 | ||||
|     diskCache.wipeCache(); | ||||
|     diskCache.setIgnoreBlocksCache(); | ||||
| 
 | ||||
|     await this.$insertUnknownPool(); | ||||
| 
 | ||||
| @ -118,10 +118,6 @@ class PoolsParser { | ||||
|    * @param pool  | ||||
|    */ | ||||
|   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
 | ||||
|     // Ignore early days of Bitcoin as there were no mining pool yet
 | ||||
|     const [oldestPoolBlock]: any[] = await DB.query(` | ||||
|  | ||||
| @ -557,7 +557,7 @@ class WebsocketHandler { | ||||
|       } | ||||
| 
 | ||||
|       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 stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { | ||||
| @ -584,6 +584,7 @@ class WebsocketHandler { | ||||
|           addedTxs: added, | ||||
|           missingTxs: censored, | ||||
|           freshTxs: fresh, | ||||
|           sigopTxs: sigop, | ||||
|           matchRate: matchRate, | ||||
|         }); | ||||
| 
 | ||||
|  | ||||
| @ -32,6 +32,7 @@ export interface BlockAudit { | ||||
|   hash: string, | ||||
|   missingTxs: string[], | ||||
|   freshTxs: string[], | ||||
|   sigopTxs: string[], | ||||
|   addedTxs: string[], | ||||
|   matchRate: number, | ||||
| } | ||||
|  | ||||
| @ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces'; | ||||
| class BlocksAuditRepositories { | ||||
|   public async $saveAudit(audit: BlockAudit): Promise<void> { | ||||
|     try { | ||||
|       await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, match_rate)
 | ||||
|         VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs),
 | ||||
|           JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), audit.matchRate]); | ||||
|       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),
 | ||||
|           JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), audit.matchRate]); | ||||
|     } catch (e: any) { | ||||
|       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`); | ||||
| @ -52,7 +52,7 @@ class BlocksAuditRepositories { | ||||
|       const [rows]: any[] = await DB.query( | ||||
|         `SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size,
 | ||||
|         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 | ||||
|         JOIN blocks ON blocks.hash = 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].addedTxs = JSON.parse(rows[0].addedTxs); | ||||
|         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].template = JSON.parse(rows[0].template); | ||||
| 
 | ||||
|  | ||||
| @ -205,9 +205,9 @@ | ||||
|         <img class="image" src="/resources/profile/nix-bitcoin.png" /> | ||||
|         <span>NixOS</span> | ||||
|       </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" /> | ||||
|         <span>EmbassyOS</span> | ||||
|         <span>StartOS</span> | ||||
|       </a> | ||||
|       <a href="https://github.com/btcpayserver/btcpayserver" target="_blank" title="BTCPay Server"> | ||||
|         <img class="image not-rounded" src="/resources/profile/btcpayserver.svg" /> | ||||
|  | ||||
| @ -37,7 +37,7 @@ export default class TxView implements TransactionStripped { | ||||
|   value: number; | ||||
|   feerate: number; | ||||
|   rate?: number; | ||||
|   status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
|   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
|   context?: 'projected' | 'actual'; | ||||
|   scene?: BlockScene; | ||||
| 
 | ||||
| @ -171,6 +171,7 @@ export default class TxView implements TransactionStripped { | ||||
|       case 'censored': | ||||
|         return auditColors.censored; | ||||
|       case 'missing': | ||||
|       case 'sigop': | ||||
|         return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; | ||||
|       case 'fresh': | ||||
|         return auditColors.missing; | ||||
|  | ||||
| @ -44,6 +44,7 @@ | ||||
|           <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="'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="'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> | ||||
|  | ||||
| @ -335,6 +335,7 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|           const isMissing = {}; | ||||
|           const isSelected = {}; | ||||
|           const isFresh = {}; | ||||
|           const isSigop = {}; | ||||
|           this.numMissing = 0; | ||||
|           this.numUnexpected = 0; | ||||
| 
 | ||||
| @ -354,6 +355,9 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|             for (const txid of blockAudit.freshTxs || []) { | ||||
|               isFresh[txid] = true; | ||||
|             } | ||||
|             for (const txid of blockAudit.sigopTxs || []) { | ||||
|               isSigop[txid] = true; | ||||
|             } | ||||
|             // set transaction statuses
 | ||||
|             for (const tx of blockAudit.template) { | ||||
|               tx.context = 'projected'; | ||||
| @ -362,7 +366,7 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|               } else if (inBlock[tx.txid]) { | ||||
|                 tx.status = 'found'; | ||||
|               } else { | ||||
|                 tx.status = isFresh[tx.txid] ? 'fresh' : 'missing'; | ||||
|                 tx.status = isFresh[tx.txid] ? 'fresh' : (isSigop[tx.txid] ? 'sigop' : 'missing'); | ||||
|                 isMissing[tx.txid] = true; | ||||
|                 this.numMissing++; | ||||
|               } | ||||
|  | ||||
| @ -24,6 +24,7 @@ import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/grap | ||||
| export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|   @Input() data: any[]; | ||||
|   @Input() filterSize = 100000; | ||||
|   @Input() limitFilterFee = 1; | ||||
|   @Input() height: number | string = 200; | ||||
|   @Input() top: number | string = 20; | ||||
|   @Input() right: number | string = 10; | ||||
| @ -40,7 +41,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|   }; | ||||
|   windowPreference: string; | ||||
|   hoverIndexSerie = 0; | ||||
|   maxFee: number; | ||||
|   feeLimitIndex: number; | ||||
|   maxFeeIndex: number; | ||||
|   feeLevelsOrdered = []; | ||||
|   chartColorsOrdered = chartColors; | ||||
|   inverted: boolean; | ||||
| @ -98,8 +101,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|   } | ||||
| 
 | ||||
|   generateArray(mempoolStats: OptimizedMempoolStats[]) { | ||||
|     let finalArray: number[][][] = []; | ||||
|     const finalArray: number[][][] = []; | ||||
|     let feesArray: number[][] = []; | ||||
| 
 | ||||
|     let maxTier = 0; | ||||
|     for (let index = 37; index > -1; index--) { | ||||
|       feesArray = []; | ||||
| @ -111,7 +115,8 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|       }); | ||||
|       finalArray.push(feesArray); | ||||
|     } | ||||
|     this.feeLimitIndex = maxTier; | ||||
|     this.maxFeeIndex = maxTier; | ||||
| 
 | ||||
|     finalArray.reverse(); | ||||
|     return finalArray; | ||||
|   } | ||||
| @ -124,7 +129,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|     const newColors = []; | ||||
|     for (let index = 0; index < series.length; index++) { | ||||
|       const value = series[index]; | ||||
|       if (index < this.feeLimitIndex) { | ||||
|       if (index >= this.feeLimitIndex && index <= this.maxFeeIndex) { | ||||
|         newColors.push(this.chartColorsOrdered[index]); | ||||
|         seriesGraph.push({ | ||||
|           zlevel: 0, | ||||
| @ -383,21 +388,26 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
| 
 | ||||
|   orderLevels() { | ||||
|     this.feeLevelsOrdered = []; | ||||
|     let maxIndex = Math.min(feeLevels.length, this.feeLimitIndex); | ||||
|     for (let i = 0; i < maxIndex; i++) { | ||||
|     const maxIndex = Math.min(feeLevels.length, this.maxFeeIndex); | ||||
|     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 (i === maxIndex - 1) { | ||||
|           if (i === maxIndex || feeLevels[i] == null) { | ||||
|             this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)}+`); | ||||
|           } else { | ||||
|             this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1]  / 10).toFixed(1)}`); | ||||
|           } | ||||
|         } else { | ||||
|           if (i === maxIndex - 1) { | ||||
|           if (i === maxIndex || feeLevels[i] == null) { | ||||
|             this.feeLevelsOrdered.push(`${feeLevels[i]}+`); | ||||
|           } else { | ||||
|             this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     this.chartColorsOrdered =  chartColors.slice(0, this.feeLevelsOrdered.length); | ||||
|   } | ||||
|  | ||||
| @ -62,7 +62,7 @@ | ||||
|                 <div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees"> | ||||
|                   <ul> | ||||
|                     <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" | ||||
|                           [class]="filterFeeIndex > feeData.fee ? 'inactive' : ''"> | ||||
|                           <span class="square" [ngStyle]="{'backgroundColor': feeData.color}"></span> | ||||
| @ -84,7 +84,8 @@ | ||||
|         </div> | ||||
|         <div class="card-body"> | ||||
|           <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> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
| @ -30,7 +30,9 @@ export class StatisticsComponent implements OnInit { | ||||
|   spinnerLoading = false; | ||||
|   feeLevels = feeLevels; | ||||
|   chartColors = chartColors; | ||||
|   filterSize = 100000; | ||||
|   filterFeeIndex = 1; | ||||
|   maxFeeIndex: number; | ||||
|   dropDownOpen = false; | ||||
| 
 | ||||
|   mempoolStats: OptimizedMempoolStats[] = []; | ||||
| @ -134,6 +136,16 @@ export class StatisticsComponent implements OnInit { | ||||
|     mempoolStats.reverse(); | ||||
|     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.mempoolTransactionsWeightPerSecondData = { | ||||
| @ -152,27 +164,42 @@ export class StatisticsComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   setFeeLevelDropdownData() { | ||||
|     let _feeLevels = feeLevels | ||||
|     let _feeLevels = feeLevels; | ||||
|     let _chartColors = chartColors; | ||||
|     if (!this.inverted) { | ||||
|       _feeLevels = [...feeLevels].reverse(); | ||||
|       _chartColors = [...chartColors].reverse(); | ||||
|     } | ||||
|     _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) { | ||||
|         this.feeLevelDropdownData.push({ | ||||
|           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], | ||||
|         }); | ||||
|       } else { | ||||
|         this.feeLevelDropdownData.push({ | ||||
|           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], | ||||
|         }); | ||||
|       } | ||||
|     }) | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | ||||
| @ -26,7 +26,6 @@ | ||||
|               <div class="mempool-graph"> | ||||
|                 <app-mempool-graph | ||||
|                 [template]="'widget'" | ||||
|                 [filterSize]="1000000" | ||||
|                 [data]="mempoolStats.value?.mempool" | ||||
|                 [windowPreferenceOverride]="'2h'" | ||||
|                 ></app-mempool-graph> | ||||
|  | ||||
| @ -155,7 +155,7 @@ export interface TransactionStripped { | ||||
|   fee: number; | ||||
|   vsize: number; | ||||
|   value: number; | ||||
|   status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
|   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
| } | ||||
| 
 | ||||
| interface RbfTransaction extends TransactionStripped { | ||||
|  | ||||
| @ -76,7 +76,7 @@ export interface TransactionStripped { | ||||
|   vsize: number; | ||||
|   value: number; | ||||
|   rate?: number; // effective fee rate
 | ||||
|   status?: 'found' | 'missing' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
|   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected'; | ||||
|   context?: 'projected' | 'actual'; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1312,8 +1312,13 @@ case $OS in | ||||
|         osSudo "${ROOT_USER}" pw usermod ${MEMPOOL_USER} -G "${CLN_GROUP}" | ||||
|         osSudo "${ROOT_USER}" chsh -s `which zsh` "${CLN_USER}" | ||||
|         echo "export PATH=$PATH:$HOME/.local/bin" >> "${CLN_HOME}/.zshrc" | ||||
|         osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}" | ||||
|         osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning" "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}" | ||||
|         osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/bitcoin" | ||||
|         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}" | ||||
|         echo "[*] Creating symlink to .bitcoin folder" | ||||
|         osSudo "${CLN_USER}" ln -s "${BITCOIN_HOME}/.bitcoin" "${CLN_HOME}/.bitcoin" | ||||
| @ -1321,16 +1326,18 @@ case $OS in | ||||
|         echo "[*] Installing Core Lightning package" | ||||
|         osPackageInstall ${CLN_PKG} | ||||
| 
 | ||||
|         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' ) | ||||
|         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 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 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 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}" - | ||||
| ######## FIXME: this code doesn't work properly, needs fixing | ||||
| # | ||||
| #        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' ) | ||||
| #        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 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 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 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) | ||||
|     ;; | ||||
| @ -1813,7 +1820,7 @@ echo "[*] Adding MySQL configuration" | ||||
| case $OS in | ||||
| 
 | ||||
|     FreeBSD) | ||||
|         osSudo "${ROOT_USER}" service mysql-server start | ||||
|         osSudo "${ROOT_USER}" service mysql-server onestart | ||||
|     ;; | ||||
|     Debian) | ||||
|         osSudo "${ROOT_USER}" service mysql start | ||||
|  | ||||
| @ -59,7 +59,7 @@ location = / { | ||||
| } | ||||
| 
 | ||||
| # 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; | ||||
| 	expires 1y; | ||||
| } | ||||
| @ -92,7 +92,7 @@ location /resources/config. { | ||||
| } | ||||
| 
 | ||||
| # cache /main.f40e91d908a068a2.js forever since they never change | ||||
| location ~* ^/.+\..+\.(js|css) { | ||||
| location ~* ^/.+\..+\.(js|css)$ { | ||||
| 	try_files /$lang/$uri /en-US/$uri =404; | ||||
| 	expires 1y; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user