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 f493e4eb3..b8478c538 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 = 71; + private static currentVersion = 72; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -606,6 +606,12 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `federation_txos` ADD emergencyKey TINYINT NOT NULL DEFAULT 0'); await this.updateToSchemaVersion(71); } + + if (databaseSchemaVersion < 72 && isBitcoin === true) { + // reindex Goggles flags for mined block templates above height 833000 + await this.$executeQuery('UPDATE blocks_summaries SET version = 0 WHERE height >= 832000;'); + await this.updateToSchemaVersion(72); + } } /** 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/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/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/services/state.service.ts b/frontend/src/app/services/state.service.ts index e54d89403..8957af736 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -89,6 +89,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