Merge branch 'master' into hunicus/add-preview-imgs

This commit is contained in:
hunicus 2024-03-10 17:47:23 +09:00 committed by GitHub
commit c590a623f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 45 additions and 13 deletions

View File

@ -293,7 +293,6 @@ export class Common {
} }
if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) { if (vout.value < (dustSize * DUST_RELAY_TX_FEE)) {
// under minimum output size // under minimum output size
console.log(`NON-STANDARD | dust | ${vout.value} | ${dustSize} ${dustSize * DUST_RELAY_TX_FEE} `, tx.txid);
return true; return true;
} }
} }
@ -456,6 +455,8 @@ export class Common {
flags |= TransactionFlags.no_rbf; flags |= TransactionFlags.no_rbf;
} }
let hasFakePubkey = false; let hasFakePubkey = false;
let P2WSHCount = 0;
let olgaSize = 0;
for (const vout of tx.vout) { for (const vout of tx.vout) {
switch (vout.scriptpubkey_type) { switch (vout.scriptpubkey_type) {
case 'p2pk': { case 'p2pk': {
@ -483,6 +484,20 @@ export class Common {
if (vout.scriptpubkey_address) { if (vout.scriptpubkey_address) {
reusedOutputAddresses[vout.scriptpubkey_address] = (reusedOutputAddresses[vout.scriptpubkey_address] || 0) + 1; 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; outValues[vout.value || Math.random()] = (outValues[vout.value || Math.random()] || 0) + 1;
} }
if (hasFakePubkey) { if (hasFakePubkey) {

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 = 71; private static currentVersion = 72;
private queryTimeout = 3600_000; private queryTimeout = 3600_000;
private statisticsAddedIndexed = false; private statisticsAddedIndexed = false;
private uniqueLogs: string[] = []; 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.$executeQuery('ALTER TABLE `federation_txos` ADD emergencyKey TINYINT NOT NULL DEFAULT 0');
await this.updateToSchemaVersion(71); 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);
}
} }
/** /**

View File

@ -107,6 +107,7 @@ class Mempool {
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
await redisCache.$addTransaction(this.mempoolCache[txid]); await redisCache.$addTransaction(this.mempoolCache[txid]);
} }
this.mempoolCache[txid].flags = Common.getTransactionFlags(this.mempoolCache[txid]);
} }
if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) { if (config.MEMPOOL.CACHE_ENABLED && config.REDIS.ENABLED) {
await redisCache.$flushTransactions(); await redisCache.$flushTransactions();

View File

@ -226,6 +226,7 @@ export const TransactionFlags = {
op_return: 0b00000001_00000000_00000000_00000000n, op_return: 0b00000001_00000000_00000000_00000000n,
fake_pubkey: 0b00000010_00000000_00000000_00000000n, fake_pubkey: 0b00000010_00000000_00000000_00000000n,
inscription: 0b00000100_00000000_00000000_00000000n, inscription: 0b00000100_00000000_00000000_00000000n,
fake_scripthash: 0b00001000_00000000_00000000_00000000n,
// heuristics // heuristics
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n, coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
consolidation: 0b00000010_00000000_00000000_00000000_00000000n, consolidation: 0b00000010_00000000_00000000_00000000_00000000n,

View File

@ -219,7 +219,7 @@
</ng-container> </ng-container>
<!-- LOGIN CTA --> <!-- LOGIN CTA -->
<ng-container *ngIf="!isLoggedIn()"> <ng-container *ngIf="stateService.isMempoolSpaceBuild && !isLoggedIn()">
<tr class="group-first group-last" style="border-top: 1px dashed grey"> <tr class="group-first group-last" style="border-top: 1px dashed grey">
<td class="item"></td> <td class="item"></td>
<td class="amt"></td> <td class="amt"></td>
@ -228,6 +228,15 @@
</td> </td>
</tr> </tr>
</ng-container> </ng-container>
<ng-container *ngIf="!stateService.isMempoolSpaceBuild">
<tr class="group-first group-last" style="border-top: 1px dashed grey">
<td class="item"></td>
<td class="amt"></td>
<td class="units d-flex">
<a [href]="'https://mempool.space/tx/' + tx.txid + '#accelerate'" class="btn btn-purple flex-grow-1">Accelerate on mempool.space</a>
</td>
</tr>
</ng-container>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -6,6 +6,7 @@ import { Transaction } from '../../interfaces/electrs.interface';
import { nextRoundNumber } from '../../shared/common.utils'; import { nextRoundNumber } from '../../shared/common.utils';
import { ServicesApiServices } from '../../services/services-api.service'; import { ServicesApiServices } from '../../services/services-api.service';
import { AudioService } from '../../services/audio.service'; import { AudioService } from '../../services/audio.service';
import { StateService } from '../../services/state.service';
export type AccelerationEstimate = { export type AccelerationEstimate = {
txSummary: TxSummary; txSummary: TxSummary;
@ -63,6 +64,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
maxRateOptions: RateOption[] = []; maxRateOptions: RateOption[] = [];
constructor( constructor(
public stateService: StateService,
private servicesApiService: ServicesApiServices, private servicesApiService: ServicesApiServices,
private storageService: StorageService, private storageService: StorageService,
private audioService: AudioService, private audioService: AudioService,

View File

@ -78,7 +78,8 @@
{{ pool.name }} {{ pool.name }}
</a> </a>
<ng-container *ngIf="auditStatus"> <ng-container *ngIf="auditStatus">
<span *ngIf="auditStatus.coinbase; else expected" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span> <span *ngIf="auditStatus.coinbase; else accelerated" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span>
<ng-template #accelerated><span *ngIf="auditStatus.accelerated || accelerationInfo || (tx && tx.acceleration) ; else expected" class="badge badge-accelerated mr-1" i18n="transaction.audit.accelerated">Accelerated</span></ng-template>
<ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template> <ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template>
<ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template> <ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template>
<ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template> <ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template>
@ -554,10 +555,6 @@
<ng-template [ngIf]="tx?.status?.confirmed || tx.acceleration || accelerationInfo"> <ng-template [ngIf]="tx?.status?.confirmed || tx.acceleration || accelerationInfo">
<app-tx-fee-rating *ngIf="!(tx.acceleration || accelerationInfo) && (tx.fee || tx.effectiveFeePerVsize)" class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating> <app-tx-fee-rating *ngIf="!(tx.acceleration || accelerationInfo) && (tx.fee || tx.effectiveFeePerVsize)" class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating>
<ng-container *ngIf="accelerationInfo || tx.acceleration">
&nbsp;
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
</ng-container>
</ng-template> </ng-template>
</div> </div>
<button *ngIf="cpfpInfo?.bestDescendant || cpfpInfo?.descendants?.length || cpfpInfo?.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button> <button *ngIf="cpfpInfo?.bestDescendant || cpfpInfo?.descendants?.length || cpfpInfo?.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>

View File

@ -89,6 +89,7 @@ const defaultEnv: Env = {
}) })
export class StateService { export class StateService {
isBrowser: boolean = isPlatformBrowser(this.platformId); isBrowser: boolean = isPlatformBrowser(this.platformId);
isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false;
network = ''; network = '';
lightning = false; lightning = false;
blockVSize: number; blockVSize: number;

View File

@ -21,12 +21,12 @@
<div class="selector"> <div class="selector">
<app-rate-unit-selector></app-rate-unit-selector> <app-rate-unit-selector></app-rate-unit-selector>
</div> </div>
<a *ngIf="servicesEnabled" class="btn btn-purple sponsor d-none d-sm-flex justify-content-center" [routerLink]="['/login' | relativeUrl]"> <a *ngIf="stateService.isMempoolSpaceBuild" class="btn btn-purple sponsor d-none d-sm-flex justify-content-center" [routerLink]="['/login' | relativeUrl]">
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span> <span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span> <span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
</a> </a>
</div> </div>
<a *ngIf="servicesEnabled" class="btn btn-purple sponsor d-flex d-sm-none justify-content-center ml-auto mr-auto mt-0 mb-2" [routerLink]="['/login' | relativeUrl]"> <a *ngIf="stateService.isMempoolSpaceBuild" class="btn btn-purple sponsor d-flex d-sm-none justify-content-center ml-auto mr-auto mt-0 mb-2" [routerLink]="['/login' | relativeUrl]">
<span *ngIf="loggedIn" i18n="shared.my-account">My Account</span> <span *ngIf="loggedIn" i18n="shared.my-account">My Account</span>
<span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span> <span *ngIf="!loggedIn" i18n="shared.sign-in">Sign In</span>
</a> </a>

View File

@ -30,7 +30,6 @@ export class GlobalFooterComponent implements OnInit {
loggedIn = false; loggedIn = false;
urlSubscription: Subscription; urlSubscription: Subscription;
isServicesPage = false; isServicesPage = false;
servicesEnabled = false;
constructor( constructor(
public stateService: StateService, public stateService: StateService,
@ -45,7 +44,6 @@ export class GlobalFooterComponent implements OnInit {
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.servicesEnabled = this.officialMempoolSpace && this.stateService.env.ACCELERATOR === true && this.stateService.network === '';
this.isServicesPage = this.router.url.includes('/services/'); this.isServicesPage = this.router.url.includes('/services/');
this.env = this.stateService.env; this.env = this.stateService.env;

View File

@ -40,6 +40,7 @@ export const TransactionFlags = {
op_return: 0b00000001_00000000_00000000_00000000n, op_return: 0b00000001_00000000_00000000_00000000n,
fake_pubkey: 0b00000010_00000000_00000000_00000000n, fake_pubkey: 0b00000010_00000000_00000000_00000000n,
inscription: 0b00000100_00000000_00000000_00000000n, inscription: 0b00000100_00000000_00000000_00000000n,
fake_scripthash: 0b00001000_00000000_00000000_00000000n,
// heuristics // heuristics
coinjoin: 0b00000001_00000000_00000000_00000000_00000000n, coinjoin: 0b00000001_00000000_00000000_00000000_00000000n,
consolidation: 0b00000010_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 }, 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 }, fake_pubkey: { key: 'fake_pubkey', label: 'Fake pubkey', flag: TransactionFlags.fake_pubkey },
inscription: { key: 'inscription', label: 'Inscription', flag: TransactionFlags.inscription, important: true }, inscription: { key: 'inscription', label: 'Inscription', flag: TransactionFlags.inscription, important: true },
fake_scripthash: { key: 'fake_scripthash', label: 'Fake scripthash', flag: TransactionFlags.fake_scripthash },
/* heuristics */ /* heuristics */
coinjoin: { key: 'coinjoin', label: 'Coinjoin', flag: TransactionFlags.coinjoin, important: true }, coinjoin: { key: 'coinjoin', label: 'Coinjoin', flag: TransactionFlags.coinjoin, important: true },
consolidation: { key: 'consolidation', label: 'Consolidation', flag: TransactionFlags.consolidation }, 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: 'Features', filters: ['rbf', 'no_rbf', 'v1', 'v2', 'v3', 'nonstandard'] },
{ label: 'Address Types', filters: ['p2pk', 'p2ms', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'] }, { label: 'Address Types', filters: ['p2pk', 'p2ms', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh', 'p2tr'] },
{ label: 'Behavior', filters: ['cpfp_parent', 'cpfp_child', 'replacement', 'acceleration'] }, { 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: 'Heuristics', filters: ['coinjoin', 'consolidation', 'batch_payout'] },
{ label: 'Sighash Flags', filters: ['sighash_all', 'sighash_none', 'sighash_single', 'sighash_default', 'sighash_acp'] }, { 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) })); ].map(group => ({ label: group.label, filters: group.filters.map(filter => TransactionFilters[filter] || null).filter(f => f != null) }));