diff --git a/backend/src/api/acceleration.ts b/backend/src/api/acceleration.ts index 2b032d09f..fff8ffd3f 100644 --- a/backend/src/api/acceleration.ts +++ b/backend/src/api/acceleration.ts @@ -33,7 +33,7 @@ interface GraphTx { vsize: number; weight: number; fees: { - base: number; + base: number; // in sats }; depends: string[]; spentby: string[]; @@ -42,7 +42,7 @@ interface GraphTx { interface MempoolTx extends GraphTx { ancestorcount: number; ancestorsize: number; - fees: { + fees: { // in sats base: number; ancestor: number; }; @@ -317,7 +317,7 @@ class AccelerationCosts { throw new Error('invalid_tx_dependencies'); } - let totalFee = Math.round(tx.fees.ancestor * 100_000_000); + let totalFee = tx.fees.ancestor; // transaction is already CPFP-d above the target rate by some descendant if (includedInCluster) { @@ -325,7 +325,7 @@ class AccelerationCosts { let clusterFee = 0; includedInCluster.forEach(entry => { clusterSize += entry.vsize; - clusterFee += (entry.fees.base * 100_000_000); + clusterFee += entry.fees.base; }); const clusterRate = clusterFee / clusterSize; totalFee = Math.ceil(tx.ancestorsize * clusterRate); @@ -448,8 +448,8 @@ class AccelerationCosts { * @param tx */ private setAncestorScores(tx: MempoolTx): void { - tx.individualRate = (tx.fees.base * 100_000_000) / tx.vsize; - tx.ancestorRate = (tx.fees.ancestor * 100_000_000) / tx.ancestorsize; + tx.individualRate = tx.fees.base / tx.vsize; + tx.ancestorRate = tx.fees.ancestor / tx.ancestorsize; tx.score = Math.min(tx.individualRate, tx.ancestorRate); } diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 72178df3e..3021d947a 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -293,7 +293,6 @@ export class Common { } if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) { // under minimum output size - console.log(`NON-STANDARD | dust | ${vout.value} | ${dustSize} ${dustSize * DUST_RELAY_TX_FEE} `, tx.txid); return true; } } @@ -456,6 +455,8 @@ export class Common { flags |= TransactionFlags.no_rbf; } let hasFakePubkey = false; + let P2WSHCount = 0; + let olgaSize = 0; for (const vout of tx.vout) { switch (vout.scriptpubkey_type) { case 'p2pk': { @@ -483,6 +484,20 @@ export class Common { if (vout.scriptpubkey_address) { reusedOutputAddresses[vout.scriptpubkey_address] = (reusedOutputAddresses[vout.scriptpubkey_address] || 0) + 1; } + if (vout.scriptpubkey_type === 'v0_p2wsh') { + if (!P2WSHCount) { + olgaSize = parseInt(vout.scriptpubkey.slice(4, 8), 16); + } + P2WSHCount++; + if (P2WSHCount === Math.ceil((olgaSize + 2) / 32)) { + const nullBytes = (P2WSHCount * 32) - olgaSize - 2; + if (vout.scriptpubkey.endsWith(''.padEnd(nullBytes * 2, '0'))) { + flags |= TransactionFlags.fake_scripthash; + } + } + } else { + P2WSHCount = 0; + } outValues[vout.value || Math.random()] = (outValues[vout.value || Math.random()] || 0) + 1; } if (hasFakePubkey) { diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index a8eeddb04..a32845594 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 72; + private static currentVersion = 74; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -607,7 +607,20 @@ class DatabaseMigration { await this.updateToSchemaVersion(71); } - if (databaseSchemaVersion < 72) { + if (databaseSchemaVersion < 72 && isBitcoin === true) { + // reindex Goggles flags for mined block templates above height 832000 + await this.$executeQuery('UPDATE blocks_summaries SET version = 0 WHERE height >= 832000;'); + await this.updateToSchemaVersion(72); + } + + if (databaseSchemaVersion < 73 && config.MEMPOOL.NETWORK === 'mainnet') { + // Clear bad data + await this.$executeQuery(`TRUNCATE accelerations`); + this.uniqueLog(logger.notice, `'accelerations' table has been truncated`); + await this.updateToSchemaVersion(73); + } + + if (databaseSchemaVersion < 74) { await this.$executeQuery('ALTER TABLE `prices` ADD `BGN` float DEFAULT "-1"'); await this.$executeQuery('ALTER TABLE `prices` ADD `BRL` float DEFAULT "-1"'); await this.$executeQuery('ALTER TABLE `prices` ADD `CNY` float DEFAULT "-1"'); @@ -634,7 +647,7 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `prices` ADD `THB` float DEFAULT "-1"'); await this.$executeQuery('ALTER TABLE `prices` ADD `TRY` float DEFAULT "-1"'); await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"'); - await this.updateToSchemaVersion(72); + await this.updateToSchemaVersion(74); } } diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index a5bc8407a..4c2f248ac 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -107,6 +107,7 @@ class Mempool { if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { await redisCache.$addTransaction(this.mempoolCache[txid]); } + this.mempoolCache[txid].flags = Common.getTransactionFlags(this.mempoolCache[txid]); } if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { await redisCache.$flushTransactions(); diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index b68b137bb..d04858607 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -226,6 +226,7 @@ export const TransactionFlags = { op_return: 0b00000001_00000000_00000000_00000000n, fake_pubkey: 0b00000010_00000000_00000000_00000000n, inscription: 0b00000100_00000000_00000000_00000000n, + fake_scripthash: 0b00001000_00000000_00000000_00000000n, // heuristics coinjoin: 0b00000001_00000000_00000000_00000000_00000000n, consolidation: 0b00000010_00000000_00000000_00000000_00000000n, diff --git a/backend/src/repositories/AccelerationRepository.ts b/backend/src/repositories/AccelerationRepository.ts index b30e563ef..c98d007c5 100644 --- a/backend/src/repositories/AccelerationRepository.ts +++ b/backend/src/repositories/AccelerationRepository.ts @@ -70,14 +70,26 @@ class AccelerationRepository { JOIN pools on pools.unique_id = accelerations.pool `; let params: any[] = []; + let hasFilter = false; if (interval) { query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `; - } else if (height != null) { - query += ` WHERE accelerations.height = ? `; + hasFilter = true; + } + + if (height != null) { + if (hasFilter) { + query += ` AND accelerations.height = ? `; + } else { + query += ` WHERE accelerations.height = ? `; + } params.push(height); } else if (poolSlug != null) { - query += ` WHERE pools.slug = ? `; + if (hasFilter) { + query += ` AND pools.slug = ? `; + } else { + query += ` WHERE pools.slug = ? `; + } params.push(poolSlug); } diff --git a/frontend/src/app/components/about/about.component.ts b/frontend/src/app/components/about/about.component.ts index 3fea849a1..44bee5828 100644 --- a/frontend/src/app/components/about/about.component.ts +++ b/frontend/src/app/components/about/about.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, ElementRef, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { StateService } from '../../services/state.service'; import { Observable } from 'rxjs'; import { ApiService } from '../../services/api.service'; @@ -33,6 +34,7 @@ export class AboutComponent implements OnInit { constructor( private websocketService: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, public stateService: StateService, private enterpriseService: EnterpriseService, private apiService: ApiService, @@ -46,6 +48,7 @@ export class AboutComponent implements OnInit { this.backendInfo$ = this.stateService.backendInfo$; this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`); this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project®\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`); + this.ogService.setManualOgImage('about.jpg'); this.websocketService.want(['blocks']); this.profiles$ = this.apiService.getAboutPageProfiles$().pipe( diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html index b4488d33d..a848a645b 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html @@ -219,7 +219,7 @@ - + @@ -228,6 +228,15 @@ + + + + + + Accelerate on mempool.space + + + diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts index 43ccce3b7..3e8dbb6ff 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts @@ -6,6 +6,7 @@ import { Transaction } from '../../interfaces/electrs.interface'; import { nextRoundNumber } from '../../shared/common.utils'; import { ServicesApiServices } from '../../services/services-api.service'; import { AudioService } from '../../services/audio.service'; +import { StateService } from '../../services/state.service'; export type AccelerationEstimate = { txSummary: TxSummary; @@ -63,6 +64,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges maxRateOptions: RateOption[] = []; constructor( + public stateService: StateService, private servicesApiService: ServicesApiServices, private storageService: StorageService, private audioService: AudioService, diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index 91b77f5e8..877f4414d 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { SeoService } from '../../../services/seo.service'; +import { OpenGraphService } from '../../../services/opengraph.service'; import { WebsocketService } from '../../../services/websocket.service'; import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; import { StateService } from '../../../services/state.service'; @@ -34,11 +35,13 @@ export class AcceleratorDashboardComponent implements OnInit { constructor( private seoService: SeoService, + private ogService: OpenGraphService, private websocketService: WebsocketService, private serviceApiServices: ServicesApiServices, private stateService: StateService, ) { this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`); + this.ogService.setManualOgImage('accelerator.jpg'); } ngOnInit(): void { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index 196a0341e..23280e1ef 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -6,6 +6,7 @@ import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -39,6 +40,7 @@ export class BlocksList implements OnInit { public stateService: StateService, private cd: ChangeDetectorRef, private seoService: SeoService, + private ogService: OpenGraphService, ) { this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool'; } @@ -57,6 +59,7 @@ export class BlocksList implements OnInit { if (!this.widget) { this.seoService.setTitle($localize`:@@m8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); + this.ogService.setManualOgImage('recent-blocks.jpg'); } if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) { this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`); diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index cfc8ef230..96058e7bf 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -1,5 +1,6 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { WebsocketService } from '../../services/websocket.service'; import { StateService } from '../../services/state.service'; import { EventType, NavigationStart, Router } from '@angular/router'; @@ -15,12 +16,14 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { constructor( private seoService: SeoService, + private ogService: OpenGraphService, private websocketService: WebsocketService, private stateService: StateService, private router: Router ) { this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`); this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`); + this.ogService.setManualOgImage('mining.jpg'); } ngOnInit(): void { diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts index b98390731..05f77c063 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-privacy-policy', @@ -13,10 +14,12 @@ export class PrivacyPolicyComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Privacy Policy'); this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project®.'); + this.ogService.setManualOgImage('privacy.jpg'); } } diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.ts b/frontend/src/app/components/push-transaction/push-transaction.component.ts index cbc5d905a..a85cd47db 100644 --- a/frontend/src/app/components/push-transaction/push-transaction.component.ts +++ b/frontend/src/app/components/push-transaction/push-transaction.component.ts @@ -3,6 +3,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -21,6 +22,7 @@ export class PushTransactionComponent implements OnInit { private apiService: ApiService, public stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { @@ -30,6 +32,7 @@ export class PushTransactionComponent implements OnInit { this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`); this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`); + this.ogService.setManualOgImage('broadcast-tx.jpg'); } postTx() { diff --git a/frontend/src/app/components/rbf-list/rbf-list.component.ts b/frontend/src/app/components/rbf-list/rbf-list.component.ts index 0721c7fdf..ff30dd1c9 100644 --- a/frontend/src/app/components/rbf-list/rbf-list.component.ts +++ b/frontend/src/app/components/rbf-list/rbf-list.component.ts @@ -7,6 +7,7 @@ import { RbfTree } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ @@ -29,6 +30,7 @@ export class RbfList implements OnInit, OnDestroy { public stateService: StateService, private websocketService: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { @@ -57,6 +59,7 @@ export class RbfList implements OnInit, OnDestroy { this.seoService.setTitle($localize`:@@5e3d5a82750902f159122fcca487b07f1af3141f:RBF Replacements`); this.seoService.setDescription($localize`:@@meta.description.rbf-list:See the most recent RBF replacements on the Bitcoin${seoDescriptionNetwork(this.stateService.network)} network, updated in real-time.`); + this.ogService.setManualOgImage('rbf.jpg'); } ngOnDestroy(): void { diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts index 708ebad76..71a86c759 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-terms-of-service', @@ -12,10 +13,12 @@ export class TermsOfServiceComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Terms of Service'); this.seoService.setDescription('Out of respect for the Bitcoin community, the mempool.space website is Bitcoin Only and does not display any advertising.'); + this.ogService.setManualOgImage('tos.jpg'); } } diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts index b8f53afcf..ad8b6b372 100644 --- a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts +++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-trademark-policy', @@ -13,10 +14,12 @@ export class TrademarkPolicyComponent { constructor( private stateService: StateService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { this.seoService.setTitle('Trademark Policy'); this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project® and what we consider to be lawful usage of those trademarks.'); + this.ogService.setManualOgImage('trademark-policy.jpg'); } } diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 188868b11..adff17e8f 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -78,7 +78,8 @@ {{ pool.name }} - Coinbase + Coinbase + Accelerated Expected in Block Seen in Mempool Not seen in Mempool @@ -554,10 +555,6 @@ - -   - Accelerated - diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index 6d6c3b0c1..e3737b545 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -3,6 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { Env, StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; @Component({ selector: 'app-docs', @@ -24,6 +25,7 @@ export class DocsComponent implements OnInit { private stateService: StateService, private websocket: WebsocketService, private seoService: SeoService, + private ogService: OpenGraphService, ) { } ngOnInit(): void { @@ -44,6 +46,7 @@ export class DocsComponent implements OnInit { this.activeTab = 0; this.seoService.setTitle($localize`:@@meta.title.docs.faq:FAQ`); this.seoService.setDescription($localize`:@@meta.description.docs.faq:Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more.`); + this.ogService.setManualOgImage('faq.jpg'); } else if( url[1].path === "rest" ) { this.activeTab = 1; this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`); diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index 63cdab42d..a8c931da8 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -3,6 +3,7 @@ import { Observable } from 'rxjs'; import { share } from 'rxjs/operators'; import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface'; import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; import { StateService } from '../../services/state.service'; import { LightningApiService } from '../lightning-api.service'; @@ -21,14 +22,16 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { constructor( private lightningApiService: LightningApiService, private seoService: SeoService, + private ogService: OpenGraphService, private stateService: StateService, ) { } ngOnInit(): void { this.onResize(); - + this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`); this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`); + this.ogService.setManualOgImage('lightning.jpg'); this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share()); this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share()); diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts index 9e2fef781..5e429ed70 100644 --- a/frontend/src/app/services/opengraph.service.ts +++ b/frontend/src/app/services/opengraph.service.ts @@ -25,7 +25,7 @@ export class OpenGraphService { ) { // save og:image tag from original template const initialOgImageTag = metaService.getTag("property='og:image'"); - this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/mempool-space-preview.png'; + this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/previews/mempool-space-preview.jpg'; this.router.events.pipe( filter(event => event instanceof NavigationEnd), map(() => this.activatedRoute), @@ -53,7 +53,7 @@ export class OpenGraphService { const lang = this.LanguageService.getLanguage(); const ogImageUrl = `${window.location.protocol}//${window.location.host}/render/${lang}/preview${this.router.url}`; this.metaService.updateTag({ property: 'og:image', content: ogImageUrl }); - this.metaService.updateTag({ property: 'twitter:image:src', content: ogImageUrl }); + this.metaService.updateTag({ name: 'twitter:image', content: ogImageUrl }); this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); this.metaService.updateTag({ property: 'og:image:width', content: '1200' }); this.metaService.updateTag({ property: 'og:image:height', content: '600' }); @@ -61,12 +61,21 @@ export class OpenGraphService { clearOgImage() { this.metaService.updateTag({ property: 'og:image', content: this.defaultImageUrl }); - this.metaService.updateTag({ property: 'twitter:image:src', content: this.defaultImageUrl }); + this.metaService.updateTag({ name: 'twitter:image', content: this.defaultImageUrl }); this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); this.metaService.updateTag({ property: 'og:image:width', content: '1000' }); this.metaService.updateTag({ property: 'og:image:height', content: '500' }); } + setManualOgImage(imageFilename) { + const ogImage = `${window.location.protocol}//${window.location.host}/resources/previews/${imageFilename}`; + this.metaService.updateTag({ property: 'og:image', content: ogImage }); + this.metaService.updateTag({ property: 'og:image:type', content: 'image/jpeg' }); + this.metaService.updateTag({ property: 'og:image:width', content: '2000' }); + this.metaService.updateTag({ property: 'og:image:height', content: '1000' }); + this.metaService.updateTag({ name: 'twitter:image', content: ogImage }); + } + /// register an event that needs to resolve before we can take a screenshot waitFor(event) { if (!this.previewLoadingEvents[event]) { diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 7830690ff..cb9a321d6 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -39,14 +39,14 @@ export class SeoService { setTitle(newTitle: string): void { this.titleService.setTitle(newTitle + ' - ' + this.getTitle()); this.metaService.updateTag({ property: 'og:title', content: newTitle}); - this.metaService.updateTag({ property: 'twitter:title', content: newTitle}); + this.metaService.updateTag({ name: 'twitter:title', content: newTitle}); this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'}); } resetTitle(): void { this.titleService.setTitle(this.getTitle()); this.metaService.updateTag({ property: 'og:title', content: this.getTitle()}); - this.metaService.updateTag({ property: 'twitter:title', content: this.getTitle()}); + this.metaService.updateTag({ name: 'twitter:title', content: this.getTitle()}); this.metaService.updateTag({ property: 'og:meta:ready', content: 'ready'}); } diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 9daebc209..e18a863bf 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -91,6 +91,7 @@ const defaultEnv: Env = { }) export class StateService { isBrowser: boolean = isPlatformBrowser(this.platformId); + isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false; network = ''; lightning = false; blockVSize: number; diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index 271279589..b92e8a339 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -21,12 +21,12 @@
- - diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.ts b/frontend/src/app/shared/components/global-footer/global-footer.component.ts index 2e3f98e2d..e76a5de0d 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.ts +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.ts @@ -30,7 +30,6 @@ export class GlobalFooterComponent implements OnInit { loggedIn = false; urlSubscription: Subscription; isServicesPage = false; - servicesEnabled = false; constructor( public stateService: StateService, @@ -45,7 +44,6 @@ export class GlobalFooterComponent implements OnInit { ) {} ngOnInit(): void { - this.servicesEnabled = this.officialMempoolSpace && this.stateService.env.ACCELERATOR === true && this.stateService.network === ''; this.isServicesPage = this.router.url.includes('/services/'); this.env = this.stateService.env; diff --git a/frontend/src/app/shared/filters.utils.ts b/frontend/src/app/shared/filters.utils.ts index da22efb66..ad4730059 100644 --- a/frontend/src/app/shared/filters.utils.ts +++ b/frontend/src/app/shared/filters.utils.ts @@ -40,6 +40,7 @@ export const TransactionFlags = { op_return: 0b00000001_00000000_00000000_00000000n, fake_pubkey: 0b00000010_00000000_00000000_00000000n, inscription: 0b00000100_00000000_00000000_00000000n, + fake_scripthash: 0b00001000_00000000_00000000_00000000n, // heuristics coinjoin: 0b00000001_00000000_00000000_00000000_00000000n, consolidation: 0b00000010_00000000_00000000_00000000_00000000n, @@ -85,6 +86,7 @@ export const TransactionFilters: { [key: string]: Filter } = { op_return: { key: 'op_return', label: 'OP_RETURN', flag: TransactionFlags.op_return, important: true }, fake_pubkey: { key: 'fake_pubkey', label: 'Fake pubkey', flag: TransactionFlags.fake_pubkey }, inscription: { key: 'inscription', label: 'Inscription', flag: TransactionFlags.inscription, important: true }, + fake_scripthash: { key: 'fake_scripthash', label: 'Fake scripthash', flag: TransactionFlags.fake_scripthash }, /* heuristics */ coinjoin: { key: 'coinjoin', label: 'Coinjoin', flag: TransactionFlags.coinjoin, important: true }, consolidation: { key: 'consolidation', label: 'Consolidation', flag: TransactionFlags.consolidation }, @@ -101,7 +103,7 @@ export const FilterGroups: { label: string, filters: Filter[]}[] = [ { label: 'Features', filters: ['rbf', 'no_rbf', 'v1', 'v2', 'v3', 'nonstandard'] }, { label: 'Address Types', filters: ['p2pk', 'p2ms', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'] }, { label: 'Behavior', filters: ['cpfp_parent', 'cpfp_child', 'replacement', 'acceleration'] }, - { label: 'Data', filters: ['op_return', 'fake_pubkey', 'inscription'] }, + { label: 'Data', filters: ['op_return', 'fake_pubkey', 'fake_scripthash', 'inscription'] }, { label: 'Heuristics', filters: ['coinjoin', 'consolidation', 'batch_payout'] }, { label: 'Sighash Flags', filters: ['sighash_all', 'sighash_none', 'sighash_single', 'sighash_default', 'sighash_acp'] }, ].map(group => ({ label: group.label, filters: group.filters.map(filter => TransactionFilters[filter] || null).filter(f => f != null) })); \ No newline at end of file diff --git a/frontend/src/index.mempool.html b/frontend/src/index.mempool.html index def14434e..838af21d0 100644 --- a/frontend/src/index.mempool.html +++ b/frontend/src/index.mempool.html @@ -8,17 +8,17 @@ - - - - + + + + - + diff --git a/frontend/src/resources/previews/about.jpg b/frontend/src/resources/previews/about.jpg new file mode 100644 index 000000000..720245d9c Binary files /dev/null and b/frontend/src/resources/previews/about.jpg differ diff --git a/frontend/src/resources/previews/accelerator.jpg b/frontend/src/resources/previews/accelerator.jpg new file mode 100644 index 000000000..3a10b7221 Binary files /dev/null and b/frontend/src/resources/previews/accelerator.jpg differ diff --git a/frontend/src/resources/previews/blocks.jpg b/frontend/src/resources/previews/blocks.jpg new file mode 100644 index 000000000..91c44f0de Binary files /dev/null and b/frontend/src/resources/previews/blocks.jpg differ diff --git a/frontend/src/resources/previews/faq.jpg b/frontend/src/resources/previews/faq.jpg new file mode 100644 index 000000000..4e198ed49 Binary files /dev/null and b/frontend/src/resources/previews/faq.jpg differ diff --git a/frontend/src/resources/previews/lightning.jpg b/frontend/src/resources/previews/lightning.jpg new file mode 100644 index 000000000..59431708c Binary files /dev/null and b/frontend/src/resources/previews/lightning.jpg differ diff --git a/frontend/src/resources/previews/mempool-space-preview.jpg b/frontend/src/resources/previews/mempool-space-preview.jpg new file mode 100644 index 000000000..ca76ba008 Binary files /dev/null and b/frontend/src/resources/previews/mempool-space-preview.jpg differ diff --git a/frontend/src/resources/previews/mining.jpg b/frontend/src/resources/previews/mining.jpg new file mode 100644 index 000000000..f36f0ae57 Binary files /dev/null and b/frontend/src/resources/previews/mining.jpg differ diff --git a/frontend/src/resources/previews/privacy-policy.jpg b/frontend/src/resources/previews/privacy-policy.jpg new file mode 100644 index 000000000..f6d32bf69 Binary files /dev/null and b/frontend/src/resources/previews/privacy-policy.jpg differ diff --git a/frontend/src/resources/previews/rbf.jpg b/frontend/src/resources/previews/rbf.jpg new file mode 100644 index 000000000..f96cf59cb Binary files /dev/null and b/frontend/src/resources/previews/rbf.jpg differ diff --git a/frontend/src/resources/previews/terms-of-service.jpg b/frontend/src/resources/previews/terms-of-service.jpg new file mode 100644 index 000000000..26865848b Binary files /dev/null and b/frontend/src/resources/previews/terms-of-service.jpg differ diff --git a/frontend/src/resources/previews/trademark-policy.jpg b/frontend/src/resources/previews/trademark-policy.jpg new file mode 100644 index 000000000..d9fd24155 Binary files /dev/null and b/frontend/src/resources/previews/trademark-policy.jpg differ diff --git a/frontend/src/resources/previews/tx-push.jpg b/frontend/src/resources/previews/tx-push.jpg new file mode 100644 index 000000000..f035f9d80 Binary files /dev/null and b/frontend/src/resources/previews/tx-push.jpg differ diff --git a/production/elements.conf b/production/elements.conf index 251cf23d2..14838a5ae 100644 --- a/production/elements.conf +++ b/production/elements.conf @@ -11,6 +11,30 @@ txindex=1 [liquidv1] validatepegin=1 mainchainrpcport=8332 +#addnode=[2401:b140:1::92:201]:7042 +#addnode=[2401:b140:1::92:202]:7042 +#addnode=[2401:b140:1::92:203]:7042 +#addnode=[2401:b140:1::92:204]:7042 +#addnode=[2401:b140:1::92:205]:7042 +#addnode=[2401:b140:1::92:206]:7042 +#addnode=[2401:b140:2::92:201]:7042 +#addnode=[2401:b140:2::92:202]:7042 +#addnode=[2401:b140:2::92:203]:7042 +#addnode=[2401:b140:2::92:204]:7042 +#addnode=[2401:b140:2::92:205]:7042 +#addnode=[2401:b140:2::92:206]:7042 +#addnode=[2401:b140:3::92:201]:7042 +#addnode=[2401:b140:3::92:202]:7042 +#addnode=[2401:b140:3::92:203]:7042 +#addnode=[2401:b140:3::92:204]:7042 +#addnode=[2401:b140:3::92:205]:7042 +#addnode=[2401:b140:3::92:206]:7042 +#addnode=[2401:b140:4::92:201]:7042 +#addnode=[2401:b140:4::92:202]:7042 +#addnode=[2401:b140:4::92:203]:7042 +#addnode=[2401:b140:4::92:204]:7042 +#addnode=[2401:b140:4::92:205]:7042 +#addnode=[2401:b140:4::92:206]:7042 [liquidtestnet] rpcport=7040 @@ -34,3 +58,27 @@ signblockscript=51210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee3 evbparams=dynafed:0::: addnode=liquid-testnet.blockstream.com:18892 addnode=liquidtestnet.com:18891 +#addnode=[2401:b140:1::92:201]:18891 +#addnode=[2401:b140:1::92:202]:18891 +#addnode=[2401:b140:1::92:203]:18891 +#addnode=[2401:b140:1::92:204]:18891 +#addnode=[2401:b140:1::92:205]:18891 +#addnode=[2401:b140:1::92:206]:18891 +#addnode=[2401:b140:2::92:201]:18891 +#addnode=[2401:b140:2::92:202]:18891 +#addnode=[2401:b140:2::92:203]:18891 +#addnode=[2401:b140:2::92:204]:18891 +#addnode=[2401:b140:2::92:205]:18891 +#addnode=[2401:b140:2::92:206]:18891 +#addnode=[2401:b140:3::92:201]:18891 +#addnode=[2401:b140:3::92:202]:18891 +#addnode=[2401:b140:3::92:203]:18891 +#addnode=[2401:b140:3::92:204]:18891 +#addnode=[2401:b140:3::92:205]:18891 +#addnode=[2401:b140:3::92:206]:18891 +#addnode=[2401:b140:4::92:201]:18891 +#addnode=[2401:b140:4::92:202]:18891 +#addnode=[2401:b140:4::92:203]:18891 +#addnode=[2401:b140:4::92:204]:18891 +#addnode=[2401:b140:4::92:205]:18891 +#addnode=[2401:b140:4::92:206]:18891 diff --git a/unfurler/src/routes.ts b/unfurler/src/routes.ts index f9280369c..c266217c8 100644 --- a/unfurler/src/routes.ts +++ b/unfurler/src/routes.ts @@ -7,6 +7,18 @@ interface Match { } const routes = { + about: { + title: "About", + fallbackImg: '/resources/previews/about.jpg', + }, + acceleration: { + title: "Acceleration", + fallbackImg: '/resources/previews/accelerator.jpg', + }, + accelerator: { + title: "Mempool Accelerator", + fallbackImg: '/resources/previews/accelerator.jpg', + }, block: { render: true, params: 1, @@ -14,6 +26,20 @@ const routes = { return `Block: ${path[0]}`; } }, + blocks: { + title: "Blocks", + fallbackImg: '/resources/previews/blocks.jpg', + }, + docs: { + title: "Docs", + fallbackImg: '/resources/previews/faq.jpg', + routes: { + faq: { + title: "FAQ", + fallbackImg: '/resources/previews/faq.jpg', + } + } + }, address: { render: true, params: 1, @@ -26,11 +52,17 @@ const routes = { params: 1, getTitle(path) { return `Transaction: ${path[0]}`; + }, + routes: { + push: { + title: "Push Transaction", + fallbackImg: '/resources/previews/tx-push.jpg', + } } }, lightning: { title: "Lightning", - fallbackImg: '/resources/previews/lightning.png', + fallbackImg: '/resources/previews/lightning.jpg', routes: { node: { render: true, @@ -68,7 +100,7 @@ const routes = { }, mining: { title: "Mining", - fallbackImg: '/resources/previews/mining.png', + fallbackImg: '/resources/previews/mining.jpg', routes: { pool: { render: true, @@ -78,12 +110,28 @@ const routes = { } } } - } + }, + "privacy-policy": { + title: "Privacy Policy", + fallbackImg: '/resources/previews/privacy-policy.jpg', + }, + rbf: { + title: "RBF", + fallbackImg: '/resources/previews/rbf.jpg', + }, + "terms-of-service": { + title: "Terms of Service", + fallbackImg: '/resources/previews/terms-of-service.jpg', + }, + "trademark-policy": { + title: "Trademark Policy", + fallbackImg: '/resources/previews/trademark-policy.jpg', + }, }; const networks = { bitcoin: { - fallbackImg: '/resources/previews/dashboard.png', + fallbackImg: '/resources/previews/mempool-space-preview.jpg', routes: { ...routes // all routes supported } @@ -147,4 +195,4 @@ export function matchRoute(network: string, path: string): Match { } return match; -} \ No newline at end of file +}