Compare commits
63 Commits
mononaut/a
...
natsoni/im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
428cc9eacd | ||
|
|
638acffbad | ||
|
|
ae9125d316 | ||
|
|
07fd3d3409 | ||
|
|
f7360433a1 | ||
|
|
f6fac92180 | ||
|
|
82d1502bfa | ||
|
|
8ab104d191 | ||
|
|
263742132c | ||
|
|
e3c3f31ddb | ||
|
|
70d1f52268 | ||
|
|
e44f30d7a7 | ||
|
|
099d84a395 | ||
|
|
12285465d9 | ||
|
|
fad39e0bea | ||
|
|
3bea10ea35 | ||
|
|
1ea45e9e96 | ||
|
|
8c2d0e1d6c | ||
|
|
009fba3dd5 | ||
|
|
a0fc4861d4 | ||
|
|
62085581dd | ||
|
|
05efa8c300 | ||
|
|
eee99a6407 | ||
|
|
98cee4a6cd | ||
|
|
0302999806 | ||
|
|
1876d67e74 | ||
|
|
c0bb75e5b1 | ||
|
|
4059a902a1 | ||
|
|
4cc19a7235 | ||
|
|
c874d642c5 | ||
|
|
f0af1703da | ||
|
|
5452d7f524 | ||
|
|
ff9e2456b9 | ||
|
|
4e581347c8 | ||
|
|
820777236e | ||
|
|
beeb5eb08c | ||
|
|
b78aca0282 | ||
|
|
9572f2d554 | ||
|
|
ef13596b59 | ||
|
|
80da024bbb | ||
|
|
f75f85f914 | ||
|
|
b3ac107b0b | ||
|
|
f8cedaa7a3 | ||
|
|
72bb92dd8b | ||
|
|
e3c4e219f3 | ||
|
|
aa3fa4478a | ||
|
|
c9171224e1 | ||
|
|
248cef7718 | ||
|
|
26c03eee88 | ||
|
|
db10ab9aae | ||
|
|
2ee7b9531a | ||
|
|
5f6af83944 | ||
|
|
8d2204a53f | ||
|
|
96bec279a9 | ||
|
|
5178ae43f6 | ||
|
|
ca26154426 | ||
|
|
021f0b32a1 | ||
|
|
b8cfeb579b | ||
|
|
fc5b99f93f | ||
|
|
ce4b0ed0f3 | ||
|
|
a31729b8b8 | ||
|
|
79e494150c | ||
|
|
104c7f4285 |
12
LICENSE
12
LICENSE
@@ -1,5 +1,5 @@
|
||||
The Mempool Open Source Project®
|
||||
Copyright (c) 2019-2023 Mempool Space K.K. and other shadowy super-coders
|
||||
Copyright (c) 2019-2024 Mempool Space K.K. and other shadowy super-coders
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License as published by the Free
|
||||
@@ -12,10 +12,12 @@ or any other contributor to The Mempool Open Source Project.
|
||||
|
||||
The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®,
|
||||
Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full
|
||||
Bitcoin ecosystem™, Mempool Goggles™, the mempool Logo, the mempool Square logo,
|
||||
the mempool Blocks logo, the mempool Blocks 3 | 2 logo, the mempool.space Vertical
|
||||
Logo, and the mempool.space Horizontal logo are registered trademarks or trademarks
|
||||
of Mempool Space K.K in Japan, the United States, and/or other countries.
|
||||
Bitcoin ecosystem™, Mempool Goggles™, the mempool Logo, the mempool Square Logo,
|
||||
the mempool block visualization Logo, the mempool Blocks Logo, the mempool
|
||||
transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo,
|
||||
the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are
|
||||
registered trademarks or trademarks of Mempool Space K.K in Japan,
|
||||
the United States, and/or other countries.
|
||||
|
||||
See our full Trademark Policy and Guidelines for more details, published on
|
||||
<https://mempool.space/trademark-policy>.
|
||||
|
||||
@@ -77,7 +77,7 @@ Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
#### Build
|
||||
|
||||
_Make sure to use Node.js 16.10 and npm 7._
|
||||
_Make sure to use Node.js 20.x and npm 9.x or newer_
|
||||
|
||||
_The build process requires [Rust](https://www.rust-lang.org/tools/install) to be installed._
|
||||
|
||||
|
||||
19
backend/package-lock.json
generated
19
backend/package-lock.json
generated
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "mempool-backend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mempool-backend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"hasInstallScript": true,
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@mempool/electrum-client": "1.1.9",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "~1.7.2",
|
||||
"axios": "~1.7.4",
|
||||
"bitcoinjs-lib": "~6.1.3",
|
||||
"crypto-js": "~4.2.0",
|
||||
"express": "~4.19.2",
|
||||
@@ -2277,9 +2278,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
|
||||
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -9438,9 +9439,9 @@
|
||||
"integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
|
||||
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mempool-backend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"homepage": "https://mempool.space",
|
||||
@@ -42,7 +42,7 @@
|
||||
"@babel/core": "^7.25.2",
|
||||
"@mempool/electrum-client": "1.1.9",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "~1.7.2",
|
||||
"axios": "~1.7.4",
|
||||
"bitcoinjs-lib": "~6.1.3",
|
||||
"crypto-js": "~4.2.0",
|
||||
"express": "~4.19.2",
|
||||
|
||||
@@ -2,6 +2,7 @@ import config from '../config';
|
||||
import logger from '../logger';
|
||||
import { MempoolTransactionExtended, MempoolBlockWithTransactions } from '../mempool.interfaces';
|
||||
import rbfCache from './rbf-cache';
|
||||
import transactionUtils from './transaction-utils';
|
||||
|
||||
const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first seen after which it is assumed to have propagated to all miners
|
||||
|
||||
@@ -15,7 +16,8 @@ class Audit {
|
||||
const matches: string[] = []; // present in both mined block and template
|
||||
const added: string[] = []; // present in mined block, not in template
|
||||
const unseen: string[] = []; // present in the mined block, not in our mempool
|
||||
const prioritized: string[] = []; // higher in the block than would be expected by in-band feerate alone
|
||||
let prioritized: string[] = []; // higher in the block than would be expected by in-band feerate alone
|
||||
let deprioritized: string[] = []; // lower in the block than would be expected by in-band feerate alone
|
||||
const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN
|
||||
const rbf: string[] = []; // either missing or present, and either part of a full-rbf replacement, or a conflict with the mined block
|
||||
const accelerated: string[] = []; // prioritized by the mempool accelerator
|
||||
@@ -133,23 +135,7 @@ class Audit {
|
||||
totalWeight += tx.weight;
|
||||
}
|
||||
|
||||
|
||||
// identify "prioritized" transactions
|
||||
let lastEffectiveRate = 0;
|
||||
// Iterate over the mined template from bottom to top (excluding the coinbase)
|
||||
// Transactions should appear in ascending order of mining priority.
|
||||
for (let i = transactions.length - 1; i > 0; i--) {
|
||||
const blockTx = transactions[i];
|
||||
// If a tx has a lower in-band effective fee rate than the previous tx,
|
||||
// it must have been prioritized out-of-band (in order to have a higher mining priority)
|
||||
// so exclude from the analysis.
|
||||
if ((blockTx.effectiveFeePerVsize || 0) < lastEffectiveRate) {
|
||||
prioritized.push(blockTx.txid);
|
||||
// accelerated txs may or may not have their prioritized fee rate applied, so don't use them as a reference
|
||||
} else if (!isAccelerated[blockTx.txid]) {
|
||||
lastEffectiveRate = blockTx.effectiveFeePerVsize || 0;
|
||||
}
|
||||
}
|
||||
({ prioritized, deprioritized } = transactionUtils.identifyPrioritizedTransactions(transactions, 'effectiveFeePerVsize'));
|
||||
|
||||
// transactions missing from near the end of our template are probably not being censored
|
||||
let overflowWeightRemaining = overflowWeight - (config.MEMPOOL.BLOCK_WEIGHT_UNITS - totalWeight);
|
||||
|
||||
@@ -323,6 +323,7 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
'witness_v1_taproot': 'v1_p2tr',
|
||||
'nonstandard': 'nonstandard',
|
||||
'multisig': 'multisig',
|
||||
'anchor': 'anchor',
|
||||
'nulldata': 'op_return'
|
||||
};
|
||||
|
||||
|
||||
@@ -219,10 +219,10 @@ class Blocks {
|
||||
};
|
||||
}
|
||||
|
||||
public summarizeBlockTransactions(hash: string, transactions: TransactionExtended[]): BlockSummary {
|
||||
public summarizeBlockTransactions(hash: string, height: number, transactions: TransactionExtended[]): BlockSummary {
|
||||
return {
|
||||
id: hash,
|
||||
transactions: Common.classifyTransactions(transactions),
|
||||
transactions: Common.classifyTransactions(transactions, height),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -616,7 +616,7 @@ class Blocks {
|
||||
// add CPFP
|
||||
const cpfpSummary = calculateGoodBlockCpfp(height, txs, []);
|
||||
// classify
|
||||
const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, cpfpSummary.transactions);
|
||||
const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, height, cpfpSummary.transactions);
|
||||
await BlocksSummariesRepository.$saveTransactions(height, blockHash, classifiedTxs, 2);
|
||||
if (unclassifiedBlocks[height].version < 2 && targetSummaryVersion === 2) {
|
||||
const cpfpClusters = await CpfpRepository.$getClustersAt(height);
|
||||
@@ -653,7 +653,7 @@ class Blocks {
|
||||
}
|
||||
const cpfpSummary = calculateGoodBlockCpfp(height, templateTxs?.filter(tx => tx['effectiveFeePerVsize'] != null) as MempoolTransactionExtended[], []);
|
||||
// classify
|
||||
const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, cpfpSummary.transactions);
|
||||
const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, height, cpfpSummary.transactions);
|
||||
const classifiedTxMap: { [txid: string]: TransactionClassified } = {};
|
||||
for (const tx of classifiedTxs) {
|
||||
classifiedTxMap[tx.txid] = tx;
|
||||
@@ -912,7 +912,7 @@ class Blocks {
|
||||
}
|
||||
const cpfpSummary: CpfpSummary = calculateGoodBlockCpfp(block.height, transactions, accelerations.map(a => ({ txid: a.txid, max_bid: a.feeDelta })));
|
||||
const blockExtended: BlockExtended = await this.$getBlockExtended(block, cpfpSummary.transactions);
|
||||
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
|
||||
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, block.height, cpfpSummary.transactions);
|
||||
this.updateTimerProgress(timer, `got block data for ${this.currentBlockHeight}`);
|
||||
|
||||
if (Common.indexingEnabled()) {
|
||||
@@ -1169,7 +1169,7 @@ class Blocks {
|
||||
transactions: cpfpSummary.transactions.map(tx => {
|
||||
let flags: number = 0;
|
||||
try {
|
||||
flags = Common.getTransactionFlags(tx);
|
||||
flags = Common.getTransactionFlags(tx, height);
|
||||
} catch (e) {
|
||||
logger.warn('Failed to classify transaction: ' + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
@@ -1188,7 +1188,7 @@ class Blocks {
|
||||
} else {
|
||||
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||
const txs = (await bitcoinApi.$getTxsForBlock(hash)).map(tx => transactionUtils.extendTransaction(tx));
|
||||
summary = this.summarizeBlockTransactions(hash, txs);
|
||||
summary = this.summarizeBlockTransactions(hash, height || 0, txs);
|
||||
summaryVersion = 1;
|
||||
} else {
|
||||
// Call Core RPC
|
||||
@@ -1324,7 +1324,7 @@ class Blocks {
|
||||
let summaryVersion = 0;
|
||||
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||
const txs = (await bitcoinApi.$getTxsForBlock(cleanBlock.hash)).map(tx => transactionUtils.extendTransaction(tx));
|
||||
summary = this.summarizeBlockTransactions(cleanBlock.hash, txs);
|
||||
summary = this.summarizeBlockTransactions(cleanBlock.hash, cleanBlock.height, txs);
|
||||
summaryVersion = 1;
|
||||
} else {
|
||||
// Call Core RPC
|
||||
|
||||
@@ -10,7 +10,6 @@ import logger from '../logger';
|
||||
import { getVarIntLength, opcodes, parseMultisigScript } from '../utils/bitcoin-script';
|
||||
|
||||
// Bitcoin Core default policy settings
|
||||
const TX_MAX_STANDARD_VERSION = 2;
|
||||
const MAX_STANDARD_TX_WEIGHT = 400_000;
|
||||
const MAX_BLOCK_SIGOPS_COST = 80_000;
|
||||
const MAX_STANDARD_TX_SIGOPS_COST = (MAX_BLOCK_SIGOPS_COST / 5);
|
||||
@@ -200,10 +199,13 @@ export class Common {
|
||||
*
|
||||
* returns true early if any standardness rule is violated, otherwise false
|
||||
* (except for non-mandatory-script-verify-flag and p2sh script evaluation rules which are *not* enforced)
|
||||
*
|
||||
* As standardness rules change, we'll need to apply the rules in force *at the time* to older blocks.
|
||||
* For now, just pull out individual rules into versioned functions where necessary.
|
||||
*/
|
||||
static isNonStandard(tx: TransactionExtended): boolean {
|
||||
static isNonStandard(tx: TransactionExtended, height?: number): boolean {
|
||||
// version
|
||||
if (tx.version > TX_MAX_STANDARD_VERSION) {
|
||||
if (this.isNonStandardVersion(tx, height)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -250,6 +252,8 @@ export class Common {
|
||||
}
|
||||
} else if (['unknown', 'provably_unspendable', 'empty'].includes(vin.prevout?.scriptpubkey_type || '')) {
|
||||
return true;
|
||||
} else if (this.isNonStandardAnchor(tx, height)) {
|
||||
return true;
|
||||
}
|
||||
// TODO: bad-witness-nonstandard
|
||||
}
|
||||
@@ -335,6 +339,49 @@ export class Common {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Individual versioned standardness rules
|
||||
|
||||
static V3_STANDARDNESS_ACTIVATION_HEIGHT = {
|
||||
'testnet4': 42_000,
|
||||
'testnet': 2_900_000,
|
||||
'signet': 211_000,
|
||||
'': 863_500,
|
||||
};
|
||||
static isNonStandardVersion(tx: TransactionExtended, height?: number): boolean {
|
||||
let TX_MAX_STANDARD_VERSION = 3;
|
||||
if (
|
||||
height != null
|
||||
&& this.V3_STANDARDNESS_ACTIVATION_HEIGHT[config.MEMPOOL.NETWORK]
|
||||
&& height <= this.V3_STANDARDNESS_ACTIVATION_HEIGHT[config.MEMPOOL.NETWORK]
|
||||
) {
|
||||
// V3 transactions were non-standard to spend before v28.x (scheduled for 2024/09/30 https://github.com/bitcoin/bitcoin/issues/29891)
|
||||
TX_MAX_STANDARD_VERSION = 2;
|
||||
}
|
||||
|
||||
if (tx.version > TX_MAX_STANDARD_VERSION) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT = {
|
||||
'testnet4': 42_000,
|
||||
'testnet': 2_900_000,
|
||||
'signet': 211_000,
|
||||
'': 863_500,
|
||||
};
|
||||
static isNonStandardAnchor(tx: TransactionExtended, height?: number): boolean {
|
||||
if (
|
||||
height != null
|
||||
&& this.ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT[config.MEMPOOL.NETWORK]
|
||||
&& height <= this.ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT[config.MEMPOOL.NETWORK]
|
||||
) {
|
||||
// anchor outputs were non-standard to spend before v28.x (scheduled for 2024/09/30 https://github.com/bitcoin/bitcoin/issues/29891)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static getNonWitnessSize(tx: TransactionExtended): number {
|
||||
let weight = tx.weight;
|
||||
let hasWitness = false;
|
||||
@@ -415,7 +462,7 @@ export class Common {
|
||||
return flags;
|
||||
}
|
||||
|
||||
static getTransactionFlags(tx: TransactionExtended): number {
|
||||
static getTransactionFlags(tx: TransactionExtended, height?: number): number {
|
||||
let flags = tx.flags ? BigInt(tx.flags) : 0n;
|
||||
|
||||
// Update variable flags (CPFP, RBF)
|
||||
@@ -548,7 +595,7 @@ export class Common {
|
||||
if (hasFakePubkey) {
|
||||
flags |= TransactionFlags.fake_pubkey;
|
||||
}
|
||||
|
||||
|
||||
// fast but bad heuristic to detect possible coinjoins
|
||||
// (at least 5 inputs and 5 outputs, less than half of which are unique amounts, with no address reuse)
|
||||
const addressReuse = Object.keys(reusedOutputAddresses).reduce((acc, key) => Math.max(acc, (reusedInputAddresses[key] || 0) + (reusedOutputAddresses[key] || 0)), 0) > 1;
|
||||
@@ -564,17 +611,17 @@ export class Common {
|
||||
flags |= TransactionFlags.batch_payout;
|
||||
}
|
||||
|
||||
if (this.isNonStandard(tx)) {
|
||||
if (this.isNonStandard(tx, height)) {
|
||||
flags |= TransactionFlags.nonstandard;
|
||||
}
|
||||
|
||||
return Number(flags);
|
||||
}
|
||||
|
||||
static classifyTransaction(tx: TransactionExtended): TransactionClassified {
|
||||
static classifyTransaction(tx: TransactionExtended, height?: number): TransactionClassified {
|
||||
let flags = 0;
|
||||
try {
|
||||
flags = Common.getTransactionFlags(tx);
|
||||
flags = Common.getTransactionFlags(tx, height);
|
||||
} catch (e) {
|
||||
logger.warn('Failed to add classification flags to transaction: ' + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
@@ -585,8 +632,8 @@ export class Common {
|
||||
};
|
||||
}
|
||||
|
||||
static classifyTransactions(txs: TransactionExtended[]): TransactionClassified[] {
|
||||
return txs.map(Common.classifyTransaction);
|
||||
static classifyTransactions(txs: TransactionExtended[], height?: number): TransactionClassified[] {
|
||||
return txs.map(tx => Common.classifyTransaction(tx, height));
|
||||
}
|
||||
|
||||
static stripTransaction(tx: TransactionExtended): TransactionStripped {
|
||||
|
||||
@@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
|
||||
import { RowDataPacket } from 'mysql2';
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 81;
|
||||
private static currentVersion = 83;
|
||||
private queryTimeout = 3600_000;
|
||||
private statisticsAddedIndexed = false;
|
||||
private uniqueLogs: string[] = [];
|
||||
@@ -700,6 +700,16 @@ class DatabaseMigration {
|
||||
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD unseen_txs JSON DEFAULT "[]"');
|
||||
await this.updateToSchemaVersion(81);
|
||||
}
|
||||
|
||||
if (databaseSchemaVersion < 82 && isBitcoin === true && config.MEMPOOL.NETWORK === 'mainnet') {
|
||||
await this.$fixBadV1AuditBlocks();
|
||||
await this.updateToSchemaVersion(82);
|
||||
}
|
||||
|
||||
if (databaseSchemaVersion < 83) {
|
||||
await this.$addPerSecondVsizeToStatistics();
|
||||
await this.updateToSchemaVersion(83);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1314,6 +1324,69 @@ class DatabaseMigration {
|
||||
logger.warn(`Failed to migrate cpfp transaction data`);
|
||||
}
|
||||
}
|
||||
|
||||
private async $fixBadV1AuditBlocks(): Promise<void> {
|
||||
const badBlocks = [
|
||||
'000000000000000000011ad49227fc8c9ba0ca96ad2ebce41a862f9a244478dc',
|
||||
'000000000000000000010ac1f68b3080153f2826ffddc87ceffdd68ed97d6960',
|
||||
'000000000000000000024cbdafeb2660ae8bd2947d166e7fe15d1689e86b2cf7',
|
||||
'00000000000000000002e1dbfbf6ae057f331992a058b822644b368034f87286',
|
||||
'0000000000000000000019973b2778f08ad6d21e083302ff0833d17066921ebb',
|
||||
];
|
||||
|
||||
for (const hash of badBlocks) {
|
||||
try {
|
||||
await this.$executeQuery(`
|
||||
UPDATE blocks_audits
|
||||
SET prioritized_txs = '[]'
|
||||
WHERE hash = '${hash}'
|
||||
`, true);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async $addPerSecondVsizeToStatistics(): Promise<void> {
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_2` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_3` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_4` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_5` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_6` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_8` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_10` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_12` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_15` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_20` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_30` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_40` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_50` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_60` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_70` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_80` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_90` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_100` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_125` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_150` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_175` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_200` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_250` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_300` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_350` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_400` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_500` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_600` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_700` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_800` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_900` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1000` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1200` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1400` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1600` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1800` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_2000` int(11) NOT NULL DEFAULT 0');
|
||||
}
|
||||
}
|
||||
|
||||
export default new DatabaseMigration();
|
||||
|
||||
@@ -12,6 +12,7 @@ import rbfCache from './rbf-cache';
|
||||
import { Acceleration } from './services/acceleration';
|
||||
import redisCache from './redis-cache';
|
||||
import blocks from './blocks';
|
||||
import { logFees } from './statistics/statistics';
|
||||
|
||||
class Mempool {
|
||||
private inSync: boolean = false;
|
||||
@@ -34,6 +35,7 @@ class Mempool {
|
||||
|
||||
private vBytesPerSecondArray: VbytesPerSecond[] = [];
|
||||
private vBytesPerSecond: number = 0;
|
||||
private vBytesPerSecondByFeeRate: { [feePerWU: number]: number } = {};
|
||||
private mempoolProtection = 0;
|
||||
private latestTransactions: any[] = [];
|
||||
|
||||
@@ -193,6 +195,10 @@ class Mempool {
|
||||
return this.vBytesPerSecond;
|
||||
}
|
||||
|
||||
public getVBytesPerSecondByFeeRate(): { [feePerWU: number]: number } {
|
||||
return this.vBytesPerSecondByFeeRate;
|
||||
}
|
||||
|
||||
public getFirstSeenForTransactions(txIds: string[]): number[] {
|
||||
const txTimes: number[] = [];
|
||||
txIds.forEach((txId: string) => {
|
||||
@@ -270,6 +276,7 @@ class Mempool {
|
||||
this.vBytesPerSecondArray.push({
|
||||
unixTime: new Date().getTime(),
|
||||
vSize: transaction.vsize,
|
||||
effectiveFeePerVsize: transaction.effectiveFeePerVsize
|
||||
});
|
||||
}
|
||||
hasChange = true;
|
||||
@@ -588,6 +595,21 @@ class Mempool {
|
||||
this.vBytesPerSecond = Math.round(
|
||||
this.vBytesPerSecondArray.map((data) => data.vSize).reduce((a, b) => a + b) / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD
|
||||
);
|
||||
|
||||
if (!Common.isLiquid()) {
|
||||
this.vBytesPerSecondByFeeRate = {};
|
||||
for (const tx of this.vBytesPerSecondArray) {
|
||||
for (let i = 0; i < logFees.length; i++) {
|
||||
if (tx.effectiveFeePerVsize < logFees[i + 1] || i === logFees.length - 1) {
|
||||
this.vBytesPerSecondByFeeRate[logFees[i]] = (this.vBytesPerSecondByFeeRate[logFees[i]] || 0) + tx.vSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const feeRate of Object.keys(this.vBytesPerSecondByFeeRate)) {
|
||||
this.vBytesPerSecondByFeeRate[feeRate] = Math.round(this.vBytesPerSecondByFeeRate[feeRate] / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,45 @@ class StatisticsApi {
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000
|
||||
)
|
||||
VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`;
|
||||
@@ -67,56 +105,94 @@ class StatisticsApi {
|
||||
public async $create(statistics: Statistic, convertToDatetime = false): Promise<number | undefined> {
|
||||
try {
|
||||
const query = `INSERT INTO statistics(
|
||||
added,
|
||||
unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
mempool_byte_weight,
|
||||
fee_data,
|
||||
total_fee,
|
||||
min_fee,
|
||||
vsize_1,
|
||||
vsize_2,
|
||||
vsize_3,
|
||||
vsize_4,
|
||||
vsize_5,
|
||||
vsize_6,
|
||||
vsize_8,
|
||||
vsize_10,
|
||||
vsize_12,
|
||||
vsize_15,
|
||||
vsize_20,
|
||||
vsize_30,
|
||||
vsize_40,
|
||||
vsize_50,
|
||||
vsize_60,
|
||||
vsize_70,
|
||||
vsize_80,
|
||||
vsize_90,
|
||||
vsize_100,
|
||||
vsize_125,
|
||||
vsize_150,
|
||||
vsize_175,
|
||||
vsize_200,
|
||||
vsize_250,
|
||||
vsize_300,
|
||||
vsize_350,
|
||||
vsize_400,
|
||||
vsize_500,
|
||||
vsize_600,
|
||||
vsize_700,
|
||||
vsize_800,
|
||||
vsize_900,
|
||||
vsize_1000,
|
||||
vsize_1200,
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000
|
||||
)
|
||||
VALUES (${convertToDatetime ? `FROM_UNIXTIME(${statistics.added})` : statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
added,
|
||||
unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
mempool_byte_weight,
|
||||
fee_data,
|
||||
total_fee,
|
||||
min_fee,
|
||||
vsize_1,
|
||||
vsize_2,
|
||||
vsize_3,
|
||||
vsize_4,
|
||||
vsize_5,
|
||||
vsize_6,
|
||||
vsize_8,
|
||||
vsize_10,
|
||||
vsize_12,
|
||||
vsize_15,
|
||||
vsize_20,
|
||||
vsize_30,
|
||||
vsize_40,
|
||||
vsize_50,
|
||||
vsize_60,
|
||||
vsize_70,
|
||||
vsize_80,
|
||||
vsize_90,
|
||||
vsize_100,
|
||||
vsize_125,
|
||||
vsize_150,
|
||||
vsize_175,
|
||||
vsize_200,
|
||||
vsize_250,
|
||||
vsize_300,
|
||||
vsize_350,
|
||||
vsize_400,
|
||||
vsize_500,
|
||||
vsize_600,
|
||||
vsize_700,
|
||||
vsize_800,
|
||||
vsize_900,
|
||||
vsize_1000,
|
||||
vsize_1200,
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000
|
||||
)
|
||||
VALUES (${convertToDatetime ? `FROM_UNIXTIME(${statistics.added})` : statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
const params: (string | number)[] = [
|
||||
statistics.unconfirmed_transactions,
|
||||
statistics.tx_per_second,
|
||||
@@ -163,6 +239,44 @@ class StatisticsApi {
|
||||
statistics.vsize_1600,
|
||||
statistics.vsize_1800,
|
||||
statistics.vsize_2000,
|
||||
statistics.vsize_ps_1,
|
||||
statistics.vsize_ps_2,
|
||||
statistics.vsize_ps_3,
|
||||
statistics.vsize_ps_4,
|
||||
statistics.vsize_ps_5,
|
||||
statistics.vsize_ps_6,
|
||||
statistics.vsize_ps_8,
|
||||
statistics.vsize_ps_10,
|
||||
statistics.vsize_ps_12,
|
||||
statistics.vsize_ps_15,
|
||||
statistics.vsize_ps_20,
|
||||
statistics.vsize_ps_30,
|
||||
statistics.vsize_ps_40,
|
||||
statistics.vsize_ps_50,
|
||||
statistics.vsize_ps_60,
|
||||
statistics.vsize_ps_70,
|
||||
statistics.vsize_ps_80,
|
||||
statistics.vsize_ps_90,
|
||||
statistics.vsize_ps_100,
|
||||
statistics.vsize_ps_125,
|
||||
statistics.vsize_ps_150,
|
||||
statistics.vsize_ps_175,
|
||||
statistics.vsize_ps_200,
|
||||
statistics.vsize_ps_250,
|
||||
statistics.vsize_ps_300,
|
||||
statistics.vsize_ps_350,
|
||||
statistics.vsize_ps_400,
|
||||
statistics.vsize_ps_500,
|
||||
statistics.vsize_ps_600,
|
||||
statistics.vsize_ps_700,
|
||||
statistics.vsize_ps_800,
|
||||
statistics.vsize_ps_900,
|
||||
statistics.vsize_ps_1000,
|
||||
statistics.vsize_ps_1200,
|
||||
statistics.vsize_ps_1400,
|
||||
statistics.vsize_ps_1600,
|
||||
statistics.vsize_ps_1800,
|
||||
statistics.vsize_ps_2000,
|
||||
];
|
||||
const [result]: any = await DB.query(query, params);
|
||||
return result.insertId;
|
||||
@@ -214,7 +328,45 @@ class StatisticsApi {
|
||||
CAST(avg(vsize_1400) as DOUBLE) as vsize_1400,
|
||||
CAST(avg(vsize_1600) as DOUBLE) as vsize_1600,
|
||||
CAST(avg(vsize_1800) as DOUBLE) as vsize_1800,
|
||||
CAST(avg(vsize_2000) as DOUBLE) as vsize_2000 \
|
||||
CAST(avg(vsize_2000) as DOUBLE) as vsize_2000,
|
||||
CAST(avg(vsize_ps_1) as DOUBLE) as vsize_ps_1,
|
||||
CAST(avg(vsize_ps_2) as DOUBLE) as vsize_ps_2,
|
||||
CAST(avg(vsize_ps_3) as DOUBLE) as vsize_ps_3,
|
||||
CAST(avg(vsize_ps_4) as DOUBLE) as vsize_ps_4,
|
||||
CAST(avg(vsize_ps_5) as DOUBLE) as vsize_ps_5,
|
||||
CAST(avg(vsize_ps_6) as DOUBLE) as vsize_ps_6,
|
||||
CAST(avg(vsize_ps_8) as DOUBLE) as vsize_ps_8,
|
||||
CAST(avg(vsize_ps_10) as DOUBLE) as vsize_ps_10,
|
||||
CAST(avg(vsize_ps_12) as DOUBLE) as vsize_ps_12,
|
||||
CAST(avg(vsize_ps_15) as DOUBLE) as vsize_ps_15,
|
||||
CAST(avg(vsize_ps_20) as DOUBLE) as vsize_ps_20,
|
||||
CAST(avg(vsize_ps_30) as DOUBLE) as vsize_ps_30,
|
||||
CAST(avg(vsize_ps_40) as DOUBLE) as vsize_ps_40,
|
||||
CAST(avg(vsize_ps_50) as DOUBLE) as vsize_ps_50,
|
||||
CAST(avg(vsize_ps_60) as DOUBLE) as vsize_ps_60,
|
||||
CAST(avg(vsize_ps_70) as DOUBLE) as vsize_ps_70,
|
||||
CAST(avg(vsize_ps_80) as DOUBLE) as vsize_ps_80,
|
||||
CAST(avg(vsize_ps_90) as DOUBLE) as vsize_ps_90,
|
||||
CAST(avg(vsize_ps_100) as DOUBLE) as vsize_ps_100,
|
||||
CAST(avg(vsize_ps_125) as DOUBLE) as vsize_ps_125,
|
||||
CAST(avg(vsize_ps_150) as DOUBLE) as vsize_ps_150,
|
||||
CAST(avg(vsize_ps_175) as DOUBLE) as vsize_ps_175,
|
||||
CAST(avg(vsize_ps_200) as DOUBLE) as vsize_ps_200,
|
||||
CAST(avg(vsize_ps_250) as DOUBLE) as vsize_ps_250,
|
||||
CAST(avg(vsize_ps_300) as DOUBLE) as vsize_ps_300,
|
||||
CAST(avg(vsize_ps_350) as DOUBLE) as vsize_ps_350,
|
||||
CAST(avg(vsize_ps_400) as DOUBLE) as vsize_ps_400,
|
||||
CAST(avg(vsize_ps_500) as DOUBLE) as vsize_ps_500,
|
||||
CAST(avg(vsize_ps_600) as DOUBLE) as vsize_ps_600,
|
||||
CAST(avg(vsize_ps_700) as DOUBLE) as vsize_ps_700,
|
||||
CAST(avg(vsize_ps_800) as DOUBLE) as vsize_ps_800,
|
||||
CAST(avg(vsize_ps_900) as DOUBLE) as vsize_ps_900,
|
||||
CAST(avg(vsize_ps_1000) as DOUBLE) as vsize_ps_1000,
|
||||
CAST(avg(vsize_ps_1200) as DOUBLE) as vsize_ps_1200,
|
||||
CAST(avg(vsize_ps_1400) as DOUBLE) as vsize_ps_1400,
|
||||
CAST(avg(vsize_ps_1600) as DOUBLE) as vsize_ps_1600,
|
||||
CAST(avg(vsize_ps_1800) as DOUBLE) as vsize_ps_1800,
|
||||
CAST(avg(vsize_ps_2000) as DOUBLE) as vsize_ps_2000 \
|
||||
FROM statistics \
|
||||
${interval === 'all' ? '' : `WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`} \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
@@ -264,7 +416,45 @@ class StatisticsApi {
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000 \
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000 \
|
||||
FROM statistics \
|
||||
${interval === 'all' ? '' : `WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`} \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
@@ -452,7 +642,47 @@ class StatisticsApi {
|
||||
s.vsize_1600,
|
||||
s.vsize_1800,
|
||||
s.vsize_2000,
|
||||
]
|
||||
],
|
||||
vsizes_ps: [
|
||||
s.vsize_ps_1,
|
||||
s.vsize_ps_2,
|
||||
s.vsize_ps_3,
|
||||
s.vsize_ps_4,
|
||||
s.vsize_ps_5,
|
||||
s.vsize_ps_6,
|
||||
s.vsize_ps_8,
|
||||
s.vsize_ps_10,
|
||||
s.vsize_ps_12,
|
||||
s.vsize_ps_15,
|
||||
s.vsize_ps_20,
|
||||
s.vsize_ps_30,
|
||||
s.vsize_ps_40,
|
||||
s.vsize_ps_50,
|
||||
s.vsize_ps_60,
|
||||
s.vsize_ps_70,
|
||||
s.vsize_ps_80,
|
||||
s.vsize_ps_90,
|
||||
s.vsize_ps_100,
|
||||
s.vsize_ps_125,
|
||||
s.vsize_ps_150,
|
||||
s.vsize_ps_175,
|
||||
s.vsize_ps_200,
|
||||
s.vsize_ps_250,
|
||||
s.vsize_ps_300,
|
||||
s.vsize_ps_350,
|
||||
s.vsize_ps_400,
|
||||
s.vsize_ps_500,
|
||||
s.vsize_ps_600,
|
||||
s.vsize_ps_700,
|
||||
s.vsize_ps_800,
|
||||
s.vsize_ps_900,
|
||||
s.vsize_ps_1000,
|
||||
s.vsize_ps_1200,
|
||||
s.vsize_ps_1400,
|
||||
s.vsize_ps_1600,
|
||||
s.vsize_ps_1800,
|
||||
s.vsize_ps_2000,
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -506,6 +736,44 @@ class StatisticsApi {
|
||||
vsize_1600: s.vsizes[35],
|
||||
vsize_1800: s.vsizes[36],
|
||||
vsize_2000: s.vsizes[37],
|
||||
vsize_ps_1: s.vsizes_ps?.[0] || 0,
|
||||
vsize_ps_2: s.vsizes_ps?.[1] || 0,
|
||||
vsize_ps_3: s.vsizes_ps?.[2] || 0,
|
||||
vsize_ps_4: s.vsizes_ps?.[3] || 0,
|
||||
vsize_ps_5: s.vsizes_ps?.[4] || 0,
|
||||
vsize_ps_6: s.vsizes_ps?.[5] || 0,
|
||||
vsize_ps_8: s.vsizes_ps?.[6] || 0,
|
||||
vsize_ps_10: s.vsizes_ps?.[7] || 0,
|
||||
vsize_ps_12: s.vsizes_ps?.[8] || 0,
|
||||
vsize_ps_15: s.vsizes_ps?.[9] || 0,
|
||||
vsize_ps_20: s.vsizes_ps?.[10] || 0,
|
||||
vsize_ps_30: s.vsizes_ps?.[11] || 0,
|
||||
vsize_ps_40: s.vsizes_ps?.[12] || 0,
|
||||
vsize_ps_50: s.vsizes_ps?.[13] || 0,
|
||||
vsize_ps_60: s.vsizes_ps?.[14] || 0,
|
||||
vsize_ps_70: s.vsizes_ps?.[15] || 0,
|
||||
vsize_ps_80: s.vsizes_ps?.[16] || 0,
|
||||
vsize_ps_90: s.vsizes_ps?.[17] || 0,
|
||||
vsize_ps_100: s.vsizes_ps?.[18] || 0,
|
||||
vsize_ps_125: s.vsizes_ps?.[19] || 0,
|
||||
vsize_ps_150: s.vsizes_ps?.[20] || 0,
|
||||
vsize_ps_175: s.vsizes_ps?.[21] || 0,
|
||||
vsize_ps_200: s.vsizes_ps?.[22] || 0,
|
||||
vsize_ps_250: s.vsizes_ps?.[23] || 0,
|
||||
vsize_ps_300: s.vsizes_ps?.[24] || 0,
|
||||
vsize_ps_350: s.vsizes_ps?.[25] || 0,
|
||||
vsize_ps_400: s.vsizes_ps?.[26] || 0,
|
||||
vsize_ps_500: s.vsizes_ps?.[27] || 0,
|
||||
vsize_ps_600: s.vsizes_ps?.[28] || 0,
|
||||
vsize_ps_700: s.vsizes_ps?.[29] || 0,
|
||||
vsize_ps_800: s.vsizes_ps?.[30] || 0,
|
||||
vsize_ps_900: s.vsizes_ps?.[31] || 0,
|
||||
vsize_ps_1000: s.vsizes_ps?.[32] || 0,
|
||||
vsize_ps_1200: s.vsizes_ps?.[33] || 0,
|
||||
vsize_ps_1400: s.vsizes_ps?.[34] || 0,
|
||||
vsize_ps_1600: s.vsizes_ps?.[35] || 0,
|
||||
vsize_ps_1800: s.vsizes_ps?.[36] || 0,
|
||||
vsize_ps_2000: s.vsizes_ps?.[37] || 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { TransactionExtended, OptimizedStatistic } from '../../mempool.interface
|
||||
import { Common } from '../common';
|
||||
import statisticsApi from './statistics-api';
|
||||
|
||||
export const logFees = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||
|
||||
class Statistics {
|
||||
protected intervalTimer: NodeJS.Timer | undefined;
|
||||
protected lastRun: number = 0;
|
||||
@@ -42,6 +45,7 @@ class Statistics {
|
||||
const currentMempool = memPool.getMempool();
|
||||
const txPerSecond = memPool.getTxPerSecond();
|
||||
const vBytesPerSecond = memPool.getVBytesPerSecond();
|
||||
const vBytesPerSecondByFeeRate = memPool.getVBytesPerSecondByFeeRate();
|
||||
|
||||
logger.debug('Running statistics');
|
||||
|
||||
@@ -73,9 +77,6 @@ class Statistics {
|
||||
const totalWeight = memPoolArray.map((tx) => tx.vsize).reduce((acc, curr) => acc + curr) * 4;
|
||||
const totalFee = memPoolArray.map((tx) => tx.fee).reduce((acc, curr) => acc + curr);
|
||||
|
||||
const logFees = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||
|
||||
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
||||
const lastItem = logFees.length - 1;
|
||||
|
||||
@@ -147,6 +148,44 @@ class Statistics {
|
||||
vsize_1600: weightVsizeFees['1600'] || 0,
|
||||
vsize_1800: weightVsizeFees['1800'] || 0,
|
||||
vsize_2000: weightVsizeFees['2000'] || 0,
|
||||
vsize_ps_1: vBytesPerSecondByFeeRate['1'] || 0,
|
||||
vsize_ps_2: vBytesPerSecondByFeeRate['2'] || 0,
|
||||
vsize_ps_3: vBytesPerSecondByFeeRate['3'] || 0,
|
||||
vsize_ps_4: vBytesPerSecondByFeeRate['4'] || 0,
|
||||
vsize_ps_5: vBytesPerSecondByFeeRate['5'] || 0,
|
||||
vsize_ps_6: vBytesPerSecondByFeeRate['6'] || 0,
|
||||
vsize_ps_8: vBytesPerSecondByFeeRate['8'] || 0,
|
||||
vsize_ps_10: vBytesPerSecondByFeeRate['10'] || 0,
|
||||
vsize_ps_12: vBytesPerSecondByFeeRate['12'] || 0,
|
||||
vsize_ps_15: vBytesPerSecondByFeeRate['15'] || 0,
|
||||
vsize_ps_20: vBytesPerSecondByFeeRate['20'] || 0,
|
||||
vsize_ps_30: vBytesPerSecondByFeeRate['30'] || 0,
|
||||
vsize_ps_40: vBytesPerSecondByFeeRate['40'] || 0,
|
||||
vsize_ps_50: vBytesPerSecondByFeeRate['50'] || 0,
|
||||
vsize_ps_60: vBytesPerSecondByFeeRate['60'] || 0,
|
||||
vsize_ps_70: vBytesPerSecondByFeeRate['70'] || 0,
|
||||
vsize_ps_80: vBytesPerSecondByFeeRate['80'] || 0,
|
||||
vsize_ps_90: vBytesPerSecondByFeeRate['90'] || 0,
|
||||
vsize_ps_100: vBytesPerSecondByFeeRate['100'] || 0,
|
||||
vsize_ps_125: vBytesPerSecondByFeeRate['125'] || 0,
|
||||
vsize_ps_150: vBytesPerSecondByFeeRate['150'] || 0,
|
||||
vsize_ps_175: vBytesPerSecondByFeeRate['175'] || 0,
|
||||
vsize_ps_200: vBytesPerSecondByFeeRate['200'] || 0,
|
||||
vsize_ps_250: vBytesPerSecondByFeeRate['250'] || 0,
|
||||
vsize_ps_300: vBytesPerSecondByFeeRate['300'] || 0,
|
||||
vsize_ps_350: vBytesPerSecondByFeeRate['350'] || 0,
|
||||
vsize_ps_400: vBytesPerSecondByFeeRate['400'] || 0,
|
||||
vsize_ps_500: vBytesPerSecondByFeeRate['500'] || 0,
|
||||
vsize_ps_600: vBytesPerSecondByFeeRate['600'] || 0,
|
||||
vsize_ps_700: vBytesPerSecondByFeeRate['700'] || 0,
|
||||
vsize_ps_800: vBytesPerSecondByFeeRate['800'] || 0,
|
||||
vsize_ps_900: vBytesPerSecondByFeeRate['900'] || 0,
|
||||
vsize_ps_1000: vBytesPerSecondByFeeRate['1000'] || 0,
|
||||
vsize_ps_1200: vBytesPerSecondByFeeRate['1200'] || 0,
|
||||
vsize_ps_1400: vBytesPerSecondByFeeRate['1400'] || 0,
|
||||
vsize_ps_1600: vBytesPerSecondByFeeRate['1600'] || 0,
|
||||
vsize_ps_1800: vBytesPerSecondByFeeRate['1800'] || 0,
|
||||
vsize_ps_2000: vBytesPerSecondByFeeRate['2000'] || 0,
|
||||
});
|
||||
|
||||
if (this.newStatisticsEntryCallback && insertId) {
|
||||
|
||||
@@ -338,6 +338,87 @@ class TransactionUtils {
|
||||
const positionOfScript = hasAnnex ? witness.length - 3 : witness.length - 2;
|
||||
return witness[positionOfScript];
|
||||
}
|
||||
|
||||
// calculate the most parsimonious set of prioritizations given a list of block transactions
|
||||
// (i.e. the most likely prioritizations and deprioritizations)
|
||||
public identifyPrioritizedTransactions(transactions: any[], rateKey: string): { prioritized: string[], deprioritized: string[] } {
|
||||
// find the longest increasing subsequence of transactions
|
||||
// (adapted from https://en.wikipedia.org/wiki/Longest_increasing_subsequence#Efficient_algorithms)
|
||||
// should be O(n log n)
|
||||
const X = transactions.slice(1).reverse().map((tx) => ({ txid: tx.txid, rate: tx[rateKey] })); // standard block order is by *decreasing* effective fee rate, but we want to iterate in increasing order (and skip the coinbase)
|
||||
if (X.length < 2) {
|
||||
return { prioritized: [], deprioritized: [] };
|
||||
}
|
||||
const N = X.length;
|
||||
const P: number[] = new Array(N);
|
||||
const M: number[] = new Array(N + 1);
|
||||
M[0] = -1; // undefined so can be set to any value
|
||||
|
||||
let L = 0;
|
||||
for (let i = 0; i < N; i++) {
|
||||
// Binary search for the smallest positive l ≤ L
|
||||
// such that X[M[l]].effectiveFeePerVsize > X[i].effectiveFeePerVsize
|
||||
let lo = 1;
|
||||
let hi = L + 1;
|
||||
while (lo < hi) {
|
||||
const mid = lo + Math.floor((hi - lo) / 2); // lo <= mid < hi
|
||||
if (X[M[mid]].rate > X[i].rate) {
|
||||
hi = mid;
|
||||
} else { // if X[M[mid]].effectiveFeePerVsize < X[i].effectiveFeePerVsize
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// After searching, lo == hi is 1 greater than the
|
||||
// length of the longest prefix of X[i]
|
||||
const newL = lo;
|
||||
|
||||
// The predecessor of X[i] is the last index of
|
||||
// the subsequence of length newL-1
|
||||
P[i] = M[newL - 1];
|
||||
M[newL] = i;
|
||||
|
||||
if (newL > L) {
|
||||
// If we found a subsequence longer than any we've
|
||||
// found yet, update L
|
||||
L = newL;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct the longest increasing subsequence
|
||||
// It consists of the values of X at the L indices:
|
||||
// ..., P[P[M[L]]], P[M[L]], M[L]
|
||||
const LIS: any[] = new Array(L);
|
||||
let k = M[L];
|
||||
for (let j = L - 1; j >= 0; j--) {
|
||||
LIS[j] = X[k];
|
||||
k = P[k];
|
||||
}
|
||||
|
||||
const lisMap = new Map<string, number>();
|
||||
LIS.forEach((tx, index) => lisMap.set(tx.txid, index));
|
||||
|
||||
const prioritized: string[] = [];
|
||||
const deprioritized: string[] = [];
|
||||
|
||||
let lastRate = X[0].rate;
|
||||
|
||||
for (const tx of X) {
|
||||
if (lisMap.has(tx.txid)) {
|
||||
lastRate = tx.rate;
|
||||
} else {
|
||||
if (Math.abs(tx.rate - lastRate) < 0.1) {
|
||||
// skip if the rate is almost the same as the previous transaction
|
||||
} else if (tx.rate <= lastRate) {
|
||||
prioritized.push(tx.txid);
|
||||
} else {
|
||||
deprioritized.push(tx.txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { prioritized, deprioritized };
|
||||
}
|
||||
}
|
||||
|
||||
export default new TransactionUtils();
|
||||
|
||||
@@ -439,6 +439,46 @@ export interface Statistic {
|
||||
vsize_1600: number;
|
||||
vsize_1800: number;
|
||||
vsize_2000: number;
|
||||
|
||||
vsize_ps_1: number;
|
||||
vsize_ps_2: number;
|
||||
vsize_ps_3: number;
|
||||
vsize_ps_4: number;
|
||||
vsize_ps_5: number;
|
||||
vsize_ps_6: number;
|
||||
vsize_ps_8: number;
|
||||
vsize_ps_10: number;
|
||||
vsize_ps_12: number;
|
||||
vsize_ps_15: number;
|
||||
vsize_ps_20: number;
|
||||
vsize_ps_30: number;
|
||||
vsize_ps_40: number;
|
||||
vsize_ps_50: number;
|
||||
vsize_ps_60: number;
|
||||
vsize_ps_70: number;
|
||||
vsize_ps_80: number;
|
||||
vsize_ps_90: number;
|
||||
vsize_ps_100: number;
|
||||
vsize_ps_125: number;
|
||||
vsize_ps_150: number;
|
||||
vsize_ps_175: number;
|
||||
vsize_ps_200: number;
|
||||
vsize_ps_250: number;
|
||||
vsize_ps_300: number;
|
||||
vsize_ps_350: number;
|
||||
vsize_ps_400: number;
|
||||
vsize_ps_500: number;
|
||||
vsize_ps_600: number;
|
||||
vsize_ps_700: number;
|
||||
vsize_ps_800: number;
|
||||
vsize_ps_900: number;
|
||||
vsize_ps_1000: number;
|
||||
vsize_ps_1200: number;
|
||||
vsize_ps_1400: number;
|
||||
vsize_ps_1600: number;
|
||||
vsize_ps_1800: number;
|
||||
vsize_ps_2000: number;
|
||||
|
||||
}
|
||||
|
||||
export interface OptimizedStatistic {
|
||||
@@ -449,6 +489,7 @@ export interface OptimizedStatistic {
|
||||
mempool_byte_weight: number;
|
||||
min_fee: number;
|
||||
vsizes: number[];
|
||||
vsizes_ps: number[];
|
||||
}
|
||||
|
||||
export interface TxTrackingInfo {
|
||||
@@ -481,6 +522,7 @@ export interface WebsocketResponse {
|
||||
export interface VbytesPerSecond {
|
||||
unixTime: number;
|
||||
vSize: number;
|
||||
effectiveFeePerVsize: number;
|
||||
}
|
||||
|
||||
export interface RequiredSpec { [name: string]: RequiredParams; }
|
||||
|
||||
@@ -132,11 +132,12 @@ class BlocksAuditRepositories {
|
||||
firstSeen = tx.time;
|
||||
}
|
||||
});
|
||||
const wasSeen = blockAudit.version === 1 ? !blockAudit.unseenTxs.includes(txid) : (isExpected || isPrioritized || isAccelerated);
|
||||
|
||||
return {
|
||||
seen: isExpected || isPrioritized || isAccelerated,
|
||||
seen: wasSeen,
|
||||
expected: isExpected,
|
||||
added: isAdded,
|
||||
added: isAdded && (blockAudit.version === 0 || !wasSeen),
|
||||
prioritized: isPrioritized,
|
||||
conflict: isConflict,
|
||||
accelerated: isAccelerated,
|
||||
|
||||
@@ -1106,7 +1106,7 @@ class BlocksRepository {
|
||||
let summaryVersion = 0;
|
||||
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||
const txs = (await bitcoinApi.$getTxsForBlock(dbBlk.id)).map(tx => transactionUtils.extendTransaction(tx));
|
||||
summary = blocks.summarizeBlockTransactions(dbBlk.id, txs);
|
||||
summary = blocks.summarizeBlockTransactions(dbBlk.id, dbBlk.height, txs);
|
||||
summaryVersion = 1;
|
||||
} else {
|
||||
// Call Core RPC
|
||||
|
||||
@@ -158,7 +158,7 @@ export function parseMultisigScript(script: string): void | { m: number, n: numb
|
||||
if (!opN) {
|
||||
return;
|
||||
}
|
||||
if (!opN.startsWith('OP_PUSHNUM_')) {
|
||||
if (opN !== 'OP_0' && !opN.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const n = parseInt(opN.match(/[0-9]+/)?.[0] || '', 10);
|
||||
@@ -178,7 +178,7 @@ export function parseMultisigScript(script: string): void | { m: number, n: numb
|
||||
if (!opM) {
|
||||
return;
|
||||
}
|
||||
if (!opM.startsWith('OP_PUSHNUM_')) {
|
||||
if (opM !== 'OP_0' && !opM.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const m = parseInt(opM.match(/[0-9]+/)?.[0] || '', 10);
|
||||
|
||||
@@ -33,7 +33,7 @@ $ npm run config:defaults:liquid
|
||||
|
||||
### 3. Run the Frontend
|
||||
|
||||
_Make sure to use Node.js 16.10 and npm 7._
|
||||
_Make sure to use Node.js 20.x and npm 9.x or newer._
|
||||
|
||||
Install project dependencies and run the frontend server:
|
||||
|
||||
@@ -70,7 +70,7 @@ Set up the [Mempool backend](../backend/) first, if you haven't already.
|
||||
|
||||
### 1. Build the Frontend
|
||||
|
||||
_Make sure to use Node.js 16.10 and npm 7._
|
||||
_Make sure to use Node.js 20.x and npm 9.x or newer._
|
||||
|
||||
Build the frontend:
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@
|
||||
"translation": "src/locale/messages.fr.xlf",
|
||||
"baseHref": "/fr/"
|
||||
},
|
||||
"hr": {
|
||||
"translation": "src/locale/messages.hr.xlf",
|
||||
"baseHref": "/hr/"
|
||||
},
|
||||
"ja": {
|
||||
"translation": "src/locale/messages.ja.xlf",
|
||||
"baseHref": "/ja/"
|
||||
|
||||
@@ -750,7 +750,7 @@
|
||||
},
|
||||
"backendInfo": {
|
||||
"hostname": "node205.tk7.mempool.space",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"gitCommit": "abbc8a134",
|
||||
"lightning": false
|
||||
},
|
||||
|
||||
59
frontend/package-lock.json
generated
59
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mempool-frontend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mempool-frontend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"dependencies": {
|
||||
"@angular-devkit/build-angular": "^17.3.1",
|
||||
@@ -32,6 +32,7 @@
|
||||
"bootstrap": "~4.6.2",
|
||||
"browserify": "^17.0.0",
|
||||
"clipboard": "^2.0.11",
|
||||
"cypress": "^13.14.0",
|
||||
"domino": "^2.1.6",
|
||||
"echarts": "~5.5.0",
|
||||
"esbuild": "^0.23.0",
|
||||
@@ -42,7 +43,7 @@
|
||||
"rxjs": "~7.8.1",
|
||||
"tinyify": "^4.0.0",
|
||||
"tlite": "^0.1.9",
|
||||
"tslib": "~2.6.0",
|
||||
"tslib": "~2.7.0",
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -62,7 +63,7 @@
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^2.5.0",
|
||||
"@types/cypress": "^1.1.3",
|
||||
"cypress": "^13.13.0",
|
||||
"cypress": "^13.14.0",
|
||||
"cypress-fail-on-console-error": "~5.1.0",
|
||||
"cypress-wait-until": "^2.0.1",
|
||||
"mock-socket": "~9.3.1",
|
||||
@@ -699,6 +700,11 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
},
|
||||
"node_modules/@angular-devkit/build-webpack": {
|
||||
"version": "0.1703.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.1.tgz",
|
||||
@@ -8040,13 +8046,13 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "13.13.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.0.tgz",
|
||||
"integrity": "sha512-ou/MQUDq4tcDJI2FsPaod2FZpex4kpIK43JJlcBgWrX8WX7R/05ZxGTuxedOuZBfxjZxja+fbijZGyxiLP6CFA==",
|
||||
"version": "13.14.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.0.tgz",
|
||||
"integrity": "sha512-r0+nhd033x883YL6068futewUsl02Q7rWiinyAAIBDW/OOTn+UMILWgNuCiY3vtJjd53efOqq5R9dctQk/rKiw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@cypress/request": "^3.0.0",
|
||||
"@cypress/request": "^3.0.1",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/sinonjs__fake-timers": "8.1.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
@@ -8805,9 +8811,9 @@
|
||||
"integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg=="
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"version": "6.5.7",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz",
|
||||
"integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
@@ -16925,9 +16931,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
|
||||
},
|
||||
"node_modules/tuf-js": {
|
||||
"version": "2.2.0",
|
||||
@@ -18849,6 +18855,11 @@
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -24127,12 +24138,12 @@
|
||||
"peer": true
|
||||
},
|
||||
"cypress": {
|
||||
"version": "13.13.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.0.tgz",
|
||||
"integrity": "sha512-ou/MQUDq4tcDJI2FsPaod2FZpex4kpIK43JJlcBgWrX8WX7R/05ZxGTuxedOuZBfxjZxja+fbijZGyxiLP6CFA==",
|
||||
"version": "13.14.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.0.tgz",
|
||||
"integrity": "sha512-r0+nhd033x883YL6068futewUsl02Q7rWiinyAAIBDW/OOTn+UMILWgNuCiY3vtJjd53efOqq5R9dctQk/rKiw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^3.0.0",
|
||||
"@cypress/request": "^3.0.1",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/sinonjs__fake-timers": "8.1.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
@@ -24723,9 +24734,9 @@
|
||||
"integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg=="
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"version": "6.5.7",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz",
|
||||
"integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
@@ -30763,9 +30774,9 @@
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
|
||||
},
|
||||
"tuf-js": {
|
||||
"version": "2.2.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mempool-frontend",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"homepage": "https://mempool.space",
|
||||
@@ -95,7 +95,7 @@
|
||||
"esbuild": "^0.23.0",
|
||||
"tinyify": "^4.0.0",
|
||||
"tlite": "^0.1.9",
|
||||
"tslib": "~2.6.0",
|
||||
"tslib": "~2.7.0",
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -115,7 +115,7 @@
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^2.5.0",
|
||||
"@types/cypress": "^1.1.3",
|
||||
"cypress": "^13.13.0",
|
||||
"cypress": "^13.14.0",
|
||||
"cypress-fail-on-console-error": "~5.1.0",
|
||||
"cypress-wait-until": "^2.0.1",
|
||||
"mock-socket": "~9.3.1",
|
||||
|
||||
@@ -151,7 +151,7 @@ export const languages: Language[] = [
|
||||
{ code: 'fr', name: 'Français' }, // French
|
||||
// { code: 'gl', name: 'Galego' }, // Galician
|
||||
{ code: 'ko', name: '한국어' }, // Korean
|
||||
// { code: 'hr', name: 'Hrvatski' }, // Croatian
|
||||
{ code: 'hr', name: 'Hrvatski' }, // Croatian
|
||||
// { code: 'id', name: 'Bahasa Indonesia' },// Indonesian
|
||||
{ code: 'hi', name: 'हिन्दी' }, // Hindi
|
||||
{ code: 'ne', name: 'नेपाली' }, // Nepalese
|
||||
|
||||
@@ -135,7 +135,7 @@ export function parseMultisigScript(script: string): void | { m: number, n: numb
|
||||
return;
|
||||
}
|
||||
const opN = ops.pop();
|
||||
if (!opN.startsWith('OP_PUSHNUM_')) {
|
||||
if (opN !== 'OP_0' && !opN.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const n = parseInt(opN.match(/[0-9]+/)[0], 10);
|
||||
@@ -152,7 +152,7 @@ export function parseMultisigScript(script: string): void | { m: number, n: numb
|
||||
}
|
||||
}
|
||||
const opM = ops.pop();
|
||||
if (!opM.startsWith('OP_PUSHNUM_')) {
|
||||
if (opM !== 'OP_0' && !opM.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const m = parseInt(opM.match(/[0-9]+/)[0], 10);
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<span>Spiral</span>
|
||||
</a>
|
||||
<a href="https://foundrydigital.com/" target="_blank" title="Foundry">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" style="zoom: 1;" width="32" height="76" viewBox="0 0 32 76">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" style="zoom: 1;" width="32" height="90" viewBox="0 -5 32 90" class="image">
|
||||
<defs>
|
||||
<style>
|
||||
.d {
|
||||
@@ -125,17 +125,14 @@
|
||||
<span>Blockstream</span>
|
||||
</a>
|
||||
<a href="https://unchained.com/" target="_blank" title="Unchained">
|
||||
<svg id="Layer_1" width="78" height="78" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 156.68 156.68"><defs><style>.cls-unchained-1{fill:#fff;}</style></defs><path class="cls-unchained-1" d="m78.34,0C35.07,0,0,35.07,0,78.34s35.07,78.34,78.34,78.34,78.34-35.07,78.34-78.34S121.6,0,78.34,0ZM20.23,109.5c-4.99-9.28-7.81-19.89-7.81-31.16C12.42,41.93,41.93,12.42,78.34,12.42c33.15,0,60.58,24.46,65.23,56.32h-37.48c-45.29,0-71.19,20.05-85.85,40.76Zm58.11,34.76c-12.42,0-24.04-3.44-33.96-9.41,3.94-8.85,9.11-18.7,15.84-28.9,20.99-31.8,52.2-31.19,76.49-31.19h7.45c.06,1.18.1,2.38.1,3.58,0,36.41-29.51,65.92-65.92,65.92Z"/><path class="cls-unchained-1" d="m91.98,42.4l-3.62-1.18c-3.94-1.29-7.03-4.38-8.32-8.32l-1.18-3.63c-.13-.39-.68-.39-.81,0l-1.18,3.63c-1.29,3.94-4.38,7.03-8.32,8.32l-3.62,1.18c-.39.13-.39.68,0,.81l3.62,1.18c3.94,1.29,7.03,4.38,8.32,8.32l1.18,3.63c.13.39.68.39.81,0l1.18-3.63c1.29-3.94,4.38-7.03,8.32-8.32l3.62-1.18c.39-.13.39-.68,0-.81Z"/></svg>
|
||||
<svg id="Layer_1" width="78" height="78" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 156.68 156.68" class="image">
|
||||
<defs><style>.cls-unchained-1{fill:#fff;}</style></defs><path class="cls-unchained-1" d="m78.34,0C35.07,0,0,35.07,0,78.34s35.07,78.34,78.34,78.34,78.34-35.07,78.34-78.34S121.6,0,78.34,0ZM20.23,109.5c-4.99-9.28-7.81-19.89-7.81-31.16C12.42,41.93,41.93,12.42,78.34,12.42c33.15,0,60.58,24.46,65.23,56.32h-37.48c-45.29,0-71.19,20.05-85.85,40.76Zm58.11,34.76c-12.42,0-24.04-3.44-33.96-9.41,3.94-8.85,9.11-18.7,15.84-28.9,20.99-31.8,52.2-31.19,76.49-31.19h7.45c.06,1.18.1,2.38.1,3.58,0,36.41-29.51,65.92-65.92,65.92Z"/><path class="cls-unchained-1" d="m91.98,42.4l-3.62-1.18c-3.94-1.29-7.03-4.38-8.32-8.32l-1.18-3.63c-.13-.39-.68-.39-.81,0l-1.18,3.63c-1.29,3.94-4.38,7.03-8.32,8.32l-3.62,1.18c-.39.13-.39.68,0,.81l3.62,1.18c3.94,1.29,7.03,4.38,8.32,8.32l1.18,3.63c.13.39.68.39.81,0l1.18-3.63c1.29-3.94,4.38-7.03,8.32-8.32l3.62-1.18c.39-.13.39-.68,0-.81Z"/>
|
||||
</svg>
|
||||
<span>Unchained</span>
|
||||
</a>
|
||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360" class="image">
|
||||
<rect style="fill: black" width="360" height="360" />
|
||||
<g transform="matrix(0.62 0 0 0.62 180 180)">
|
||||
<path style="fill: rgb(0,220,250)" transform=" translate(-162, -162)" d="M 211.74 0 C 154.74 0 106.35 43.84 100.25 100.25 C 43.84 106.35 1.4210854715202004e-14 154.76 1.4210854715202004e-14 211.74 C 0.044122601308501076 273.7212006364817 50.27879936351834 323.95587739869154 112.26 324 C 169.26 324 217.84 280.15999999999997 223.75 223.75 C 280.15999999999997 217.65 324 169.24 324 112.26 C 323.95587739869154 50.278799363518324 273.72120063648174 0.04412260130848722 211.74 -1.4210854715202004e-14 z M 297.74 124.84 C 291.9644950552469 162.621439649343 262.2969457716857 192.26062994820046 224.51 198 L 224.51 124.84 z M 26.3 199.16 C 31.986912917108594 161.30935034910615 61.653433460549415 131.56986937804106 99.48999999999998 125.78999999999999 L 99.49 199 L 26.3 199 z M 198.21 224.51 C 191.87736076583954 267.0991541201681 155.312384597087 298.62923417787493 112.255 298.62923417787493 C 69.19761540291302 298.62923417787493 32.63263923416048 267.0991541201682 26.3 224.51 z M 199.16 124.83999999999999 L 199.16 199 L 124.84 199 L 124.84 124.84 z M 297.7 99.48999999999998 L 125.78999999999999 99.48999999999998 C 132.12263923416046 56.90084587983182 168.687615402913 25.37076582212505 211.745 25.37076582212505 C 254.80238459708698 25.37076582212505 291.3673607658395 56.900845879831834 297.7 99.49 z" stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
<span>Gemini</span>
|
||||
<a href="https://bitkey.world/" target="_blank" title="Bitkey">
|
||||
<img class="image" src="/resources/profile/bitkey.svg" />
|
||||
<span>Bitkey</span>
|
||||
</a>
|
||||
<a href="https://bullbitcoin.com/" target="_blank" title="Bull Bitcoin">
|
||||
<svg aria-hidden="true" class="image" viewBox="0 -5 40 40" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -150,7 +147,7 @@
|
||||
<span>Bull Bitcoin</span>
|
||||
</a>
|
||||
<a href="https://exodus.com/" target="_blank" title="Exodus">
|
||||
<svg width="80" height="80" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="80" height="80" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg" class="image">
|
||||
<circle cx="250" cy="250" r="250" fill="#1F2033"/>
|
||||
<g clip-path="url(#clip0_2_14)">
|
||||
<path d="M411.042 178.303L271.79 87V138.048L361.121 196.097L350.612 229.351H271.79V271.648H350.612L361.121 304.903L271.79 362.952V414L411.042 322.989L388.271 250.646L411.042 178.303Z" fill="url(#paint0_linear_2_14)"/>
|
||||
@@ -191,6 +188,19 @@
|
||||
</svg>
|
||||
<span>Exodus</span>
|
||||
</a>
|
||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360" class="image">
|
||||
<rect style="fill: black" width="360" height="360" />
|
||||
<g transform="matrix(0.62 0 0 0.62 180 180)">
|
||||
<path style="fill: rgb(0,220,250)" transform=" translate(-162, -162)" d="M 211.74 0 C 154.74 0 106.35 43.84 100.25 100.25 C 43.84 106.35 1.4210854715202004e-14 154.76 1.4210854715202004e-14 211.74 C 0.044122601308501076 273.7212006364817 50.27879936351834 323.95587739869154 112.26 324 C 169.26 324 217.84 280.15999999999997 223.75 223.75 C 280.15999999999997 217.65 324 169.24 324 112.26 C 323.95587739869154 50.278799363518324 273.72120063648174 0.04412260130848722 211.74 -1.4210854715202004e-14 z M 297.74 124.84 C 291.9644950552469 162.621439649343 262.2969457716857 192.26062994820046 224.51 198 L 224.51 124.84 z M 26.3 199.16 C 31.986912917108594 161.30935034910615 61.653433460549415 131.56986937804106 99.48999999999998 125.78999999999999 L 99.49 199 L 26.3 199 z M 198.21 224.51 C 191.87736076583954 267.0991541201681 155.312384597087 298.62923417787493 112.255 298.62923417787493 C 69.19761540291302 298.62923417787493 32.63263923416048 267.0991541201682 26.3 224.51 z M 199.16 124.83999999999999 L 199.16 199 L 124.84 199 L 124.84 124.84 z M 297.7 99.48999999999998 L 125.78999999999999 99.48999999999998 C 132.12263923416046 56.90084587983182 168.687615402913 25.37076582212505 211.745 25.37076582212505 C 254.80238459708698 25.37076582212505 291.3673607658395 56.900845879831834 297.7 99.49 z" stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
<span>Gemini</span>
|
||||
</a>
|
||||
<a href="https://leather.io/" target="_blank" title="Leather">
|
||||
<img class="image" src="/resources/profile/leather.svg" />
|
||||
<span>Leather</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -435,7 +445,7 @@
|
||||
Trademark Notice<br>
|
||||
</div>
|
||||
<p>
|
||||
The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo, the mempool Square logo, the mempool Blocks logo, the mempool Blocks 3 | 2 logo, the mempool.space Vertical Logo, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
|
||||
The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.
|
||||
</p>
|
||||
<p>
|
||||
While our software is available under an open source software license, the copyright license does not include an implied right or license to use our trademarks. See our <a href="https://mempool.space/trademark-policy">Trademark Policy and Guidelines</a> for more details, published on <https://mempool.space/trademark-policy>.
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
.image.not-rounded {
|
||||
border-radius: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
@@ -158,9 +156,8 @@
|
||||
margin: 40px 29px 10px;
|
||||
&.image.coldcard {
|
||||
border-radius: 0;
|
||||
width: auto;
|
||||
max-height: 50px;
|
||||
margin: 40px 29px 14px 29px;
|
||||
height: auto;
|
||||
margin: 20px 29px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,3 +251,12 @@
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.enterprise-sponsor {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
@@ -67,13 +67,17 @@ export class ActiveAccelerationBox implements OnChanges {
|
||||
|
||||
const acceleratingPools = (poolList || []).filter(id => pools[id]).sort((a,b) => pools[a].lastEstimatedHashrate - pools[b].lastEstimatedHashrate);
|
||||
const totalAcceleratedHashrate = acceleratingPools.reduce((total, pool) => total + pools[pool].lastEstimatedHashrate, 0);
|
||||
const lightenStep = acceleratingPools.length ? (0.48 / acceleratingPools.length) : 0;
|
||||
// Find the first pool with at least 1% of the total network hashrate
|
||||
const firstSignificantPool = acceleratingPools.findIndex(pool => pools[pool].lastEstimatedHashrate > this.miningStats.lastEstimatedHashrate / 100);
|
||||
const numSignificantPools = acceleratingPools.length - firstSignificantPool;
|
||||
acceleratingPools.forEach((poolId, index) => {
|
||||
const pool = pools[poolId];
|
||||
const poolShare = ((pool.lastEstimatedHashrate / this.miningStats.lastEstimatedHashrate) * 100).toFixed(1);
|
||||
data.push(getDataItem(
|
||||
pool.lastEstimatedHashrate,
|
||||
toRGB(lighten({ r: 147, g: 57, b: 244 }, index * lightenStep)),
|
||||
index >= firstSignificantPool
|
||||
? toRGB(lighten({ r: 147, g: 57, b: 244 }, 1 - (index - firstSignificantPool) / (numSignificantPools - 1)))
|
||||
: 'white',
|
||||
`<b style="color: white">${pool.name} (${poolShare}%)</b>`,
|
||||
true,
|
||||
) as PieSeriesOption);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="sparkles" #sparkleAnchor>
|
||||
<div *ngFor="let sparkle of sparkles" class="sparkle" [style]="sparkle.style">
|
||||
<span class="inner-sparkle" [style]="sparkle.rotation">+</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
.sparkles {
|
||||
position: absolute;
|
||||
top: var(--block-size);
|
||||
height: 50px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.sparkle {
|
||||
position: absolute;
|
||||
color: rgba(152, 88, 255, 0.75);
|
||||
opacity: 0;
|
||||
transform: scale(0.8) rotate(0deg);
|
||||
animation: pop ease 2000ms forwards, sparkle ease 500ms infinite;
|
||||
}
|
||||
|
||||
.inner-sparkle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes pop {
|
||||
0% {
|
||||
transform: scale(0.8) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
transform: scale(1) rotate(72deg);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sparkle {
|
||||
0% {
|
||||
color: rgba(152, 88, 255, 0.75);
|
||||
}
|
||||
50% {
|
||||
color: rgba(198, 162, 255, 0.75);
|
||||
}
|
||||
100% {
|
||||
color: rgba(152, 88, 255, 0.75);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-acceleration-sparkles',
|
||||
templateUrl: './acceleration-sparkles.component.html',
|
||||
styleUrls: ['./acceleration-sparkles.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AccelerationSparklesComponent implements OnChanges {
|
||||
@Input() arrow: ElementRef<HTMLDivElement>;
|
||||
@Input() run: boolean = false;
|
||||
|
||||
@ViewChild('sparkleAnchor')
|
||||
sparkleAnchor: ElementRef<HTMLDivElement>;
|
||||
|
||||
constructor(
|
||||
private cd: ChangeDetectorRef,
|
||||
) {}
|
||||
|
||||
endTimeout: any;
|
||||
lastSparkle: number = 0;
|
||||
sparkleWidth: number = 0;
|
||||
sparkles: any[] = [];
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.run) {
|
||||
if (this.endTimeout) {
|
||||
clearTimeout(this.endTimeout);
|
||||
this.endTimeout = null;
|
||||
}
|
||||
if (this.run) {
|
||||
this.doSparkle();
|
||||
} else {
|
||||
this.endTimeout = setTimeout(() => {
|
||||
this.sparkles = [];
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doSparkle(): void {
|
||||
if (this.run) {
|
||||
const now = performance.now();
|
||||
if (now - this.lastSparkle > 20) {
|
||||
this.lastSparkle = now;
|
||||
if (this.arrow?.nativeElement && this.sparkleAnchor?.nativeElement) {
|
||||
const anchor = this.sparkleAnchor.nativeElement.getBoundingClientRect().right;
|
||||
const right = this.arrow.nativeElement.getBoundingClientRect().right;
|
||||
const dx = (anchor - right) + 30;
|
||||
const numSparkles = Math.ceil(Math.random() * 3);
|
||||
for (let i = 0; i < numSparkles; i++) {
|
||||
this.sparkles.push({
|
||||
style: {
|
||||
right: (dx + (Math.random() * 10)) + 'px',
|
||||
top: (15 + (Math.random() * 30)) + 'px',
|
||||
},
|
||||
rotation: {
|
||||
transform: `rotate(${Math.random() * 360}deg)`,
|
||||
}
|
||||
});
|
||||
}
|
||||
while (this.sparkles.length > 200) {
|
||||
this.sparkles.shift();
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
this.doSparkle();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export class AddressLabelsComponent implements OnChanges {
|
||||
}
|
||||
|
||||
handleVin() {
|
||||
const address = new AddressTypeInfo(this.network || 'mainnet', this.vin.prevout?.scriptpubkey_address, this.vin.prevout?.scriptpubkey_type as AddressType, [this.vin])
|
||||
const address = new AddressTypeInfo(this.network || 'mainnet', this.vin.prevout?.scriptpubkey_address, this.vin.prevout?.scriptpubkey_type as AddressType, [this.vin]);
|
||||
if (address?.scripts.size) {
|
||||
const script = address?.scripts.values().next().value;
|
||||
if (script.template?.label) {
|
||||
|
||||
@@ -198,7 +198,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
}
|
||||
|
||||
// initialize the scene without any entry transition
|
||||
setup(transactions: TransactionStripped[]): void {
|
||||
setup(transactions: TransactionStripped[], sort: boolean = false): void {
|
||||
const filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false);
|
||||
if (filtersAvailable !== this.filtersAvailable) {
|
||||
this.setFilterFlags();
|
||||
@@ -206,7 +206,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
this.filtersAvailable = filtersAvailable;
|
||||
if (this.scene) {
|
||||
this.clearUpdateQueue();
|
||||
this.scene.setup(transactions);
|
||||
this.scene.setup(transactions, sort);
|
||||
this.readyNextFrame = true;
|
||||
this.start();
|
||||
this.updateSearchHighlight();
|
||||
|
||||
@@ -88,16 +88,19 @@ export default class BlockScene {
|
||||
}
|
||||
|
||||
// set up the scene with an initial set of transactions, without any transition animation
|
||||
setup(txs: TransactionStripped[]) {
|
||||
setup(txs: TransactionStripped[], sort: boolean = false) {
|
||||
// clean up any old transactions
|
||||
Object.values(this.txs).forEach(tx => {
|
||||
tx.destroy();
|
||||
delete this.txs[tx.txid];
|
||||
});
|
||||
this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight });
|
||||
txs.forEach(tx => {
|
||||
const txView = new TxView(tx, this);
|
||||
this.txs[tx.txid] = txView;
|
||||
let txViews = txs.map(tx => new TxView(tx, this));
|
||||
if (sort) {
|
||||
txViews = txViews.sort(feeRateDescending);
|
||||
}
|
||||
txViews.forEach(txView => {
|
||||
this.txs[txView.txid] = txView;
|
||||
this.place(txView);
|
||||
this.saveGridToScreenPosition(txView);
|
||||
this.applyTxUpdate(txView, {
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class TxView implements TransactionStripped {
|
||||
flags: number;
|
||||
bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n;
|
||||
time?: number;
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'added_prioritized' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated';
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'added_prioritized' | 'prioritized' | 'added_deprioritized' | 'deprioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated';
|
||||
context?: 'projected' | 'actual';
|
||||
scene?: BlockScene;
|
||||
|
||||
|
||||
@@ -142,6 +142,10 @@ export function defaultColorFunction(
|
||||
return auditColors.added_prioritized;
|
||||
case 'prioritized':
|
||||
return auditColors.prioritized;
|
||||
case 'added_deprioritized':
|
||||
return auditColors.added_prioritized;
|
||||
case 'deprioritized':
|
||||
return auditColors.prioritized;
|
||||
case 'selected':
|
||||
return colors.marginal[levelIndex] || colors.marginal[defaultMempoolFeeColors.length - 1];
|
||||
case 'accelerated':
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
<span class="badge badge-warning" i18n="tx-features.tag.added|Added">Added</span>
|
||||
<span class="badge badge-warning ml-1" i18n="tx-features.tag.prioritized|Prioritized">Prioritized</span>
|
||||
</ng-container>
|
||||
<span *ngSwitchCase="'deprioritized'" class="badge badge-warning" i18n="tx-features.tag.prioritized|Deprioritized">Deprioritized</span>
|
||||
<ng-container *ngSwitchCase="'added_deprioritized'">
|
||||
<span class="badge badge-warning" i18n="tx-features.tag.added|Added">Added</span>
|
||||
<span class="badge badge-warning ml-1" i18n="tx-features.tag.prioritized|Deprioritized">Deprioritized</span>
|
||||
</ng-container>
|
||||
<span *ngSwitchCase="'selected'" class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span>
|
||||
<span *ngSwitchCase="'rbf'" class="badge badge-warning" i18n="tx-features.tag.conflict|Conflict">Conflict</span>
|
||||
<span *ngSwitchCase="'accelerated'" class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { PriceService, Price } from '../../services/price.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
import { PreloadService } from '../../services/preload.service';
|
||||
import { identifyPrioritizedTransactions } from '../../shared/transaction.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
@@ -524,6 +525,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
const isUnseen = {};
|
||||
const isAdded = {};
|
||||
const isPrioritized = {};
|
||||
const isDeprioritized = {};
|
||||
const isCensored = {};
|
||||
const isMissing = {};
|
||||
const isSelected = {};
|
||||
@@ -535,6 +537,17 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
this.numUnexpected = 0;
|
||||
|
||||
if (blockAudit?.template) {
|
||||
// augment with locally calculated *de*prioritized transactions if possible
|
||||
const { prioritized, deprioritized } = identifyPrioritizedTransactions(transactions);
|
||||
// but if the local calculation produces returns unexpected results, don't use it
|
||||
let useLocalDeprioritized = deprioritized.length < (transactions.length * 0.1);
|
||||
for (const tx of prioritized) {
|
||||
if (!isPrioritized[tx] && !isAccelerated[tx]) {
|
||||
useLocalDeprioritized = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const tx of blockAudit.template) {
|
||||
inTemplate[tx.txid] = true;
|
||||
if (tx.acc) {
|
||||
@@ -550,9 +563,14 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
for (const txid of blockAudit.addedTxs) {
|
||||
isAdded[txid] = true;
|
||||
}
|
||||
for (const txid of blockAudit.prioritizedTxs || []) {
|
||||
for (const txid of blockAudit.prioritizedTxs) {
|
||||
isPrioritized[txid] = true;
|
||||
}
|
||||
if (useLocalDeprioritized) {
|
||||
for (const txid of deprioritized || []) {
|
||||
isDeprioritized[txid] = true;
|
||||
}
|
||||
}
|
||||
for (const txid of blockAudit.missingTxs) {
|
||||
isCensored[txid] = true;
|
||||
}
|
||||
@@ -608,6 +626,12 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
tx.status = 'prioritized';
|
||||
}
|
||||
} else if (isDeprioritized[tx.txid]) {
|
||||
if (isAdded[tx.txid] || (blockAudit.version > 0 && isUnseen[tx.txid])) {
|
||||
tx.status = 'added_deprioritized';
|
||||
} else {
|
||||
tx.status = 'deprioritized';
|
||||
}
|
||||
} else if (isAdded[tx.txid] && (blockAudit.version === 0 || isUnseen[tx.txid])) {
|
||||
tx.status = 'added';
|
||||
} else if (inTemplate[tx.txid]) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div class="faucet-container text-center">
|
||||
|
||||
|
||||
@if (txid) {
|
||||
<div class="alert alert-success w-100 text-truncate">
|
||||
<fa-icon [icon]="['fas', 'circle-check']"></fa-icon>
|
||||
@@ -36,6 +36,13 @@
|
||||
<app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login>
|
||||
</div>
|
||||
}
|
||||
@else if (error === 'account_limited') {
|
||||
<div class="alert alert-mempool d-block text-center w-100">
|
||||
<div class="d-inline align-middle">
|
||||
<span class="mb-2 mr-2">Your Twitter account does not allow you to access the faucet</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@else if (error) {
|
||||
<!-- User can request -->
|
||||
<app-mempool-error class="w-100" [error]="error"></app-mempool-error>
|
||||
@@ -81,7 +88,7 @@
|
||||
}
|
||||
|
||||
<!-- Send back coins -->
|
||||
@if (status?.address) {
|
||||
@if (status?.address) {
|
||||
<div class="mt-4 alert alert-info w-100">If you no longer need your testnet4 coins, please consider <a class="text-primary" [routerLink]="['/address/' | relativeUrl, status.address]"><u>sending them back</u></a> to replenish the faucet.</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
||||
error: string = '';
|
||||
user: any = undefined;
|
||||
txid: string = '';
|
||||
|
||||
|
||||
faucetStatusSubscription: Subscription;
|
||||
status: {
|
||||
min: number; // minimum amount to request at once (in sats)
|
||||
|
||||
@@ -31,7 +31,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
|
||||
lastBlockHeight: number;
|
||||
blockIndex: number;
|
||||
isLoading$ = new BehaviorSubject<boolean>(true);
|
||||
isLoading$ = new BehaviorSubject<boolean>(false);
|
||||
timeLtrSubscription: Subscription;
|
||||
timeLtr: boolean;
|
||||
chainDirection: string = 'right';
|
||||
@@ -95,6 +95,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
}
|
||||
}
|
||||
this.updateBlock({
|
||||
block: this.blockIndex,
|
||||
removed,
|
||||
changed,
|
||||
added
|
||||
@@ -110,8 +111,11 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
if (this.blockGraph) {
|
||||
this.blockGraph.clear(changes.index.currentValue > changes.index.previousValue ? this.chainDirection : this.poolDirection);
|
||||
}
|
||||
this.isLoading$.next(true);
|
||||
this.websocketService.startTrackMempoolBlock(changes.index.currentValue);
|
||||
if (!this.websocketService.startTrackMempoolBlock(changes.index.currentValue) && this.stateService.mempoolBlockState && this.stateService.mempoolBlockState.block === changes.index.currentValue) {
|
||||
this.resumeBlock(Object.values(this.stateService.mempoolBlockState.transactions));
|
||||
} else {
|
||||
this.isLoading$.next(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +157,19 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
this.isLoading$.next(false);
|
||||
}
|
||||
|
||||
resumeBlock(transactionsStripped: TransactionStripped[]): void {
|
||||
if (this.blockGraph) {
|
||||
this.firstLoad = false;
|
||||
this.blockGraph.setup(transactionsStripped, true);
|
||||
this.blockIndex = this.index;
|
||||
this.isLoading$.next(false);
|
||||
} else {
|
||||
requestAnimationFrame(() => {
|
||||
this.resumeBlock(transactionsStripped);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void {
|
||||
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`);
|
||||
if (!event.keyModifier) {
|
||||
|
||||
@@ -71,7 +71,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
this.mempoolBlockTransactions$ = this.stateService.liveMempoolBlockTransactions$.pipe(map(txMap => Object.values(txMap)));
|
||||
this.mempoolBlockTransactions$ = this.stateService.liveMempoolBlockTransactions$.pipe(map(({transactions}) => Object.values(transactions)));
|
||||
|
||||
this.network$ = this.stateService.networkChanged$;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + (blockWidth * 0.3) + containerOffset + 'px', transition: transition }" [class.blink]="txPosition?.accelerated"></div>
|
||||
<app-acceleration-sparkles [style]="{ position: 'absolute', right: 0}" [arrow]="arrowElement" [run]="acceleratingArrow"></app-acceleration-sparkles>
|
||||
<div *ngIf="arrowVisible" #arrowUp id="arrow-up" [ngStyle]="{'right': rightPosition + (blockWidth * 0.3) + containerOffset + 'px', transition: transition }" [class.blink]="txPosition?.accelerated"></div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Subscription, Observable, of, combineLatest } from 'rxjs';
|
||||
import { MempoolBlock } from '../../interfaces/websocket.interface';
|
||||
import { StateService } from '../../services/state.service';
|
||||
@@ -77,6 +77,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
maxArrowPosition = 0;
|
||||
rightPosition = 0;
|
||||
transition = 'background 2s, right 2s, transform 1s';
|
||||
@ViewChild('arrowUp')
|
||||
arrowElement: ElementRef<HTMLDivElement>;
|
||||
acceleratingArrow: boolean = false;
|
||||
|
||||
markIndex: number;
|
||||
txPosition: MempoolPosition;
|
||||
@@ -201,6 +204,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
this.markBlocksSubscription = this.stateService.markBlock$
|
||||
.subscribe((state) => {
|
||||
const oldTxPosition = this.txPosition;
|
||||
this.markIndex = undefined;
|
||||
this.txPosition = undefined;
|
||||
this.txFeePerVSize = undefined;
|
||||
@@ -209,6 +213,12 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
if (state.mempoolPosition) {
|
||||
this.txPosition = state.mempoolPosition;
|
||||
if (this.txPosition.accelerated && !oldTxPosition?.accelerated) {
|
||||
this.acceleratingArrow = true;
|
||||
setTimeout(() => {
|
||||
this.acceleratingArrow = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
if (state.txFeePerVSize) {
|
||||
this.txFeePerVSize = state.txFeePerVSize;
|
||||
|
||||
@@ -293,7 +293,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
).subscribe((accelerationHistory) => {
|
||||
for (const acceleration of accelerationHistory) {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional')) {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
@@ -747,7 +747,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
||||
|
||||
checkAccelerationEligibility() {
|
||||
if (this.tx) {
|
||||
this.tx.flags = getTransactionFlags(this.tx);
|
||||
this.tx.flags = getTransactionFlags(this.tx, null, null, this.tx.status?.block_time, this.stateService.network);
|
||||
const replaceableInputs = (this.tx.flags & (TransactionFlags.sighash_none | TransactionFlags.sighash_acp)) > 0n;
|
||||
const highSigop = (this.tx.sigops * 20) > this.tx.weight;
|
||||
this.eligibleForAcceleration = !replaceableInputs && !highSigop;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div *ngIf="officialMempoolSpace">
|
||||
<h2>Trademark Policy and Guidelines</h2>
|
||||
<h5>The Mempool Open Source Project ®</h5>
|
||||
<h6>Updated: July 3, 2024</h6>
|
||||
<h6>Updated: August 19, 2024</h6>
|
||||
<br>
|
||||
|
||||
<div class="text-left">
|
||||
@@ -100,11 +100,26 @@
|
||||
<p>The Mempool Accelerator Logo</p>
|
||||
<br><br>
|
||||
|
||||
<img src="/resources/mempool-research.png" style="width: 500px; max-width: 80%">
|
||||
<br><br>
|
||||
<p>The mempool research Logo</p>
|
||||
<br><br>
|
||||
|
||||
<app-svg-images name="goggles" height="96px"></app-svg-images>
|
||||
<br><br>
|
||||
<p>The Mempool Goggles Logo</p>
|
||||
<br><br>
|
||||
|
||||
<img src="/resources/mempool-transaction.png" style="width: 500px; max-width: 80%">
|
||||
<br><br>
|
||||
<p>The mempool transaction Logo</p>
|
||||
<br><br>
|
||||
|
||||
<img src="/resources/mempool-block-visualization.png" style="width: 500px; max-width: 80%">
|
||||
<br><br>
|
||||
<p>The mempool block visualization Logo</p>
|
||||
<br><br>
|
||||
|
||||
<img src="/resources/mempool-blocks-2-3-logo.jpeg" style="width: 500px; max-width: 80%">
|
||||
<br><br>
|
||||
<p>The mempool Blocks Logo</p>
|
||||
|
||||
@@ -606,16 +606,11 @@
|
||||
@if (!isLoadingTx) {
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||
<td class="text-wrap">{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
@if (accelerationInfo?.bidBoost) {
|
||||
<span class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band"> +{{ accelerationInfo.bidBoost | number }} </span><span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
<span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee + accelerationInfo.bidBoost"></app-fiat></span>
|
||||
} @else if (tx.feeDelta && !accelerationInfo) {
|
||||
<span class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band"> +{{ tx.feeDelta | number }} </span><span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
<span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee + tx.feeDelta"></app-fiat></span>
|
||||
} @else {
|
||||
<span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee"></app-fiat></span>
|
||||
}
|
||||
<td class="text-wrap">{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
@if (accelerationInfo?.bidBoost ?? tx.feeDelta > 0) {
|
||||
<span class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band"> +{{ accelerationInfo?.bidBoost ?? tx.feeDelta | number }} </span><span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
}
|
||||
<span class="fiat"><app-fiat [blockConversion]="tx.price" [value]="tx.fee + ((accelerationInfo?.bidBoost ?? tx.feeDelta) || 0)"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
} @else {
|
||||
|
||||
@@ -27,7 +27,7 @@ import { StorageService } from '../../services/storage.service';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { getTransactionFlags, getUnacceleratedFeeRate } from '../../shared/transaction.utils';
|
||||
import { Filter, TransactionFlags, toFilters } from '../../shared/filters.utils';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition } from '../../interfaces/node-api.interface';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition, OptimizedMempoolStats } from '../../interfaces/node-api.interface';
|
||||
import { LiquidUnblinding } from './liquid-ublinding';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { PriceService } from '../../services/price.service';
|
||||
@@ -139,6 +139,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
firstLoad = true;
|
||||
waitingForAccelerationInfo: boolean = false;
|
||||
isLoadingFirstSeen = false;
|
||||
mempoolStats: OptimizedMempoolStats[] = null;
|
||||
|
||||
featuresEnabled: boolean;
|
||||
segwitEnabled: boolean;
|
||||
@@ -196,7 +197,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||
this.websocketService.want(['blocks', 'mempool-blocks', 'live-2h-chart']);
|
||||
this.stateService.networkChanged$.subscribe(
|
||||
(network) => {
|
||||
this.network = network;
|
||||
@@ -358,12 +359,18 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}),
|
||||
).subscribe((accelerationHistory) => {
|
||||
for (const acceleration of accelerationHistory) {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional')) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
if (acceleration.txid === this.txId) {
|
||||
if (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') {
|
||||
if (acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
} else {
|
||||
this.tx.feeDelta = undefined;
|
||||
}
|
||||
}
|
||||
this.waitingForAccelerationInfo = false;
|
||||
this.setIsAccelerated();
|
||||
}
|
||||
@@ -763,8 +770,20 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.stateService.difficultyAdjustment$.pipe(startWith(null)),
|
||||
this.isAccelerated$,
|
||||
this.txChanged$,
|
||||
this.apiService.list2HStatistics$(),
|
||||
this.stateService.live2Chart$.pipe(startWith(null)),
|
||||
]).pipe(
|
||||
map(([position, mempoolBlocks, da, isAccelerated]) => {
|
||||
map(([position, mempoolBlocks, da, isAccelerated, _, mempoolStats, mempoolStat]) => {
|
||||
|
||||
if (this.mempoolStats === null) {
|
||||
this.mempoolStats = mempoolStats;
|
||||
}
|
||||
|
||||
if (this.mempoolStats.length && mempoolStat && this.mempoolStats[0].added !== mempoolStat.added) {
|
||||
this.mempoolStats.pop();
|
||||
this.mempoolStats.unshift(mempoolStat);
|
||||
}
|
||||
|
||||
return this.etaService.calculateETA(
|
||||
this.network,
|
||||
this.tx,
|
||||
@@ -774,6 +793,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.miningStats,
|
||||
isAccelerated,
|
||||
this.accelerationPositions,
|
||||
this.mempoolStats,
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -895,7 +915,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit');
|
||||
this.taprootEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'taproot');
|
||||
this.rbfEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'rbf');
|
||||
this.tx.flags = getTransactionFlags(this.tx);
|
||||
this.tx.flags = getTransactionFlags(this.tx, null, null, this.tx.status?.block_time, this.stateService.network);
|
||||
this.filters = this.tx.flags ? toFilters(this.tx.flags).filter(f => f.txPage) : [];
|
||||
this.checkAccelerationEligibility();
|
||||
} else {
|
||||
@@ -971,6 +991,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.isAcceleration = false;
|
||||
this.isAccelerated$.next(this.isAcceleration);
|
||||
this.eligibleForAcceleration = false;
|
||||
this.mempoolStats = null;
|
||||
this.leaveTransaction();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<span *ngSwitchCase="'output'" i18n="transaction.output">Output</span>
|
||||
<span *ngSwitchCase="'fee'" i18n="transaction.fee|Transaction fee">Fee</span>
|
||||
</ng-container>
|
||||
<span *ngIf="line.type !== 'fee'"> #{{ line.index + 1 }}</span>
|
||||
<span *ngIf="line.type !== 'fee'"> #{{ line.index }}</span>
|
||||
<ng-container [ngSwitch]="line.type">
|
||||
<span *ngSwitchCase="'input'">
|
||||
<ng-container *ngIf="line.status?.block_height">
|
||||
@@ -73,7 +73,7 @@
|
||||
<app-truncate [text]="line.txid"></app-truncate>
|
||||
</p>
|
||||
<ng-container [ngSwitch]="line.type">
|
||||
<p *ngSwitchCase="'input'"><span i18n="transaction.output">Output</span> #{{ line.vout + 1 }}
|
||||
<p *ngSwitchCase="'input'"><span i18n="transaction.output">Output</span> #{{ line.vout }}
|
||||
<ng-container *ngIf="line.status?.block_height">
|
||||
<ng-container *ngIf="line.blockHeight; else noBlockHeight">
|
||||
<ng-container *ngTemplateOutlet="nBlocksEarlier; context:{n: line.blockHeight - line?.status?.block_height, connector: true}"></ng-container>
|
||||
@@ -83,7 +83,7 @@
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</p>
|
||||
<p *ngSwitchCase="'output'"><span i18n="transaction.input">Input</span> #{{ line.vin + 1 }}
|
||||
<p *ngSwitchCase="'output'"><span i18n="transaction.input">Input</span> #{{ line.vin }}
|
||||
<ng-container *ngIf="line.blockHeight">
|
||||
<ng-container *ngIf="line?.status?.block_height; else noBlockHeight">
|
||||
<ng-container *ngTemplateOutlet="nBlocksLater; context:{n: line?.status?.block_height - line.blockHeight, connector: true}"></ng-container>
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface OptimizedMempoolStats {
|
||||
total_fee: number;
|
||||
mempool_byte_weight: number;
|
||||
vsizes: number[];
|
||||
vsizes_ps: number[];
|
||||
}
|
||||
|
||||
interface Ancestor {
|
||||
@@ -239,7 +240,7 @@ export interface TransactionStripped {
|
||||
acc?: boolean;
|
||||
flags?: number | null;
|
||||
time?: number;
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'added_prioritized' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated';
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'added_prioritized' | 'prioritized' | 'added_deprioritized' | 'deprioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated';
|
||||
context?: 'projected' | 'actual';
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,13 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
|
||||
}
|
||||
|
||||
export interface MempoolBlockDelta {
|
||||
block: number;
|
||||
added: TransactionStripped[];
|
||||
removed: string[];
|
||||
changed: { txid: string, rate: number, flags: number, acc: boolean }[];
|
||||
}
|
||||
export interface MempoolBlockState {
|
||||
block: number;
|
||||
transactions: TransactionStripped[];
|
||||
}
|
||||
export type MempoolBlockUpdate = MempoolBlockDelta | MempoolBlockState;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, SinglePoolStats } from '../interfaces/node-api.interface';
|
||||
import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, SinglePoolStats } from '../interfaces/node-api.interface';
|
||||
import { StateService } from './state.service';
|
||||
import { MempoolBlock } from '../interfaces/websocket.interface';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
@@ -7,6 +7,7 @@ import { MiningService, MiningStats } from './mining.service';
|
||||
import { getUnacceleratedFeeRate } from '../shared/transaction.utils';
|
||||
import { AccelerationEstimate } from '../components/accelerate-checkout/accelerate-checkout.component';
|
||||
import { Observable, combineLatest, map, of, share, shareReplay, tap } from 'rxjs';
|
||||
import { feeLevels } from '../app.constants';
|
||||
|
||||
export interface ETA {
|
||||
now: number, // time at which calculation performed
|
||||
@@ -113,6 +114,7 @@ export class EtaService {
|
||||
miningStats: MiningStats,
|
||||
isAccelerated: boolean,
|
||||
accelerationPositions: AccelerationPosition[],
|
||||
mempoolStats: OptimizedMempoolStats[] = [],
|
||||
): ETA | null {
|
||||
// return this.calculateETA(tx, this.accelerationPositions, position, mempoolBlocks, da, isAccelerated)
|
||||
if (!tx || !mempoolBlocks) {
|
||||
@@ -143,7 +145,17 @@ export class EtaService {
|
||||
|
||||
if (!isAccelerated) {
|
||||
const blocks = mempoolPosition.block + 1;
|
||||
const wait = da.adjustedTimeAvg * (mempoolPosition.block + 1);
|
||||
|
||||
const vsizeAhead = mempoolPosition.vsize + this.stateService.blockVSize * mempoolPosition.block;
|
||||
const incomingVsizePerBlock = this.estimateVsizePerSecond(tx, mempoolStats) * da.adjustedTimeAvg / 1000;
|
||||
const vsizeConsumedPerBlock = Math.max(
|
||||
this.stateService.blockVSize - incomingVsizePerBlock,
|
||||
0.05 * this.stateService.blockVSize // So that we don't return infinite ETA
|
||||
)
|
||||
|
||||
const blocksUntilMined = Math.ceil(vsizeAhead / vsizeConsumedPerBlock);
|
||||
const wait = blocksUntilMined * da.adjustedTimeAvg;
|
||||
|
||||
return {
|
||||
now,
|
||||
time: wait + now + da.timeOffset,
|
||||
@@ -279,4 +291,46 @@ export class EtaService {
|
||||
return tx.fee / (tx.weight / 4);
|
||||
|
||||
}
|
||||
|
||||
estimateVsizePerSecond(tx: Transaction, mempoolStats: OptimizedMempoolStats[], timeWindow: number = 15 * 60 * 1000): number {
|
||||
const nowMinusTimeSpan = (new Date().getTime() - timeWindow) / 1000;
|
||||
const vsizeAboveTransaction = mempoolStats
|
||||
// Remove datapoints older than now - timeWindow
|
||||
.filter(stat => stat.added > nowMinusTimeSpan)
|
||||
// Remove datapoints less than 45 seconds apart from the previous one
|
||||
.filter((el, i, arr) => {
|
||||
if (i === 0) {
|
||||
return true;
|
||||
}
|
||||
return arr[i - 1].added - el.added > 45;
|
||||
})
|
||||
// For each datapoint, compute the total vsize of transactions with higher fee rate
|
||||
.map(stat => {
|
||||
let vsizeAbove = 0;
|
||||
for (let i = feeLevels.length - 1; i >= 0; i--) {
|
||||
if (feeLevels[i] > tx.effectiveFeePerVsize) {
|
||||
vsizeAbove += stat.vsizes_ps[i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vsizeAbove;
|
||||
});
|
||||
|
||||
// vsizeAboveTransaction is a temporal series of past vsize values above the transaction's fee rate
|
||||
// From this array we need to estimate the future vsize per second
|
||||
// Naive first approach: take the median of the series
|
||||
if (!vsizeAboveTransaction.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sorted = Array.from(vsizeAboveTransaction).sort((a, b) => a - b);
|
||||
const middle = Math.floor(sorted.length / 2);
|
||||
|
||||
if (sorted.length % 2 === 0) {
|
||||
return (sorted[middle - 1] + sorted[middle]) / 2;
|
||||
}
|
||||
|
||||
return sorted[middle];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, Mempool
|
||||
import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { filter, map, scan, shareReplay } from 'rxjs/operators';
|
||||
import { filter, map, scan, share, shareReplay } from 'rxjs/operators';
|
||||
import { StorageService } from './storage.service';
|
||||
import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils';
|
||||
import { ActiveFilter } from '../shared/filters.utils';
|
||||
@@ -131,6 +131,7 @@ export class StateService {
|
||||
latestBlockHeight = -1;
|
||||
blocks: BlockExtended[] = [];
|
||||
mempoolSequence: number;
|
||||
mempoolBlockState: { block: number, transactions: { [txid: string]: TransactionStripped} };
|
||||
|
||||
backend$ = new BehaviorSubject<'esplora' | 'electrum' | 'none'>('esplora');
|
||||
networkChanged$ = new ReplaySubject<string>(1);
|
||||
@@ -143,7 +144,7 @@ export class StateService {
|
||||
mempoolInfo$ = new ReplaySubject<MempoolInfo>(1);
|
||||
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
||||
mempoolBlockUpdate$ = new Subject<MempoolBlockUpdate>();
|
||||
liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
|
||||
liveMempoolBlockTransactions$: Observable<{ block: number, transactions: { [txid: string]: TransactionStripped} }>;
|
||||
accelerations$ = new Subject<AccelerationDelta>();
|
||||
liveAccelerations$: Observable<Acceleration[]>;
|
||||
txConfirmed$ = new Subject<[string, BlockExtended]>();
|
||||
@@ -231,29 +232,40 @@ export class StateService {
|
||||
}
|
||||
});
|
||||
|
||||
this.liveMempoolBlockTransactions$ = this.mempoolBlockUpdate$.pipe(scan((transactions: { [txid: string]: TransactionStripped }, change: MempoolBlockUpdate): { [txid: string]: TransactionStripped } => {
|
||||
this.liveMempoolBlockTransactions$ = this.mempoolBlockUpdate$.pipe(scan((acc: { block: number, transactions: { [txid: string]: TransactionStripped } }, change: MempoolBlockUpdate): { block: number, transactions: { [txid: string]: TransactionStripped } } => {
|
||||
if (isMempoolState(change)) {
|
||||
const txMap = {};
|
||||
change.transactions.forEach(tx => {
|
||||
txMap[tx.txid] = tx;
|
||||
});
|
||||
return txMap;
|
||||
this.mempoolBlockState = {
|
||||
block: change.block,
|
||||
transactions: txMap
|
||||
};
|
||||
return this.mempoolBlockState;
|
||||
} else {
|
||||
change.added.forEach(tx => {
|
||||
transactions[tx.txid] = tx;
|
||||
acc.transactions[tx.txid] = tx;
|
||||
});
|
||||
change.removed.forEach(txid => {
|
||||
delete transactions[txid];
|
||||
delete acc.transactions[txid];
|
||||
});
|
||||
change.changed.forEach(tx => {
|
||||
if (transactions[tx.txid]) {
|
||||
transactions[tx.txid].rate = tx.rate;
|
||||
transactions[tx.txid].acc = tx.acc;
|
||||
if (acc.transactions[tx.txid]) {
|
||||
acc.transactions[tx.txid].rate = tx.rate;
|
||||
acc.transactions[tx.txid].acc = tx.acc;
|
||||
}
|
||||
});
|
||||
return transactions;
|
||||
this.mempoolBlockState = {
|
||||
block: change.block,
|
||||
transactions: acc.transactions
|
||||
};
|
||||
return this.mempoolBlockState;
|
||||
}
|
||||
}, {}));
|
||||
}, {}),
|
||||
share()
|
||||
);
|
||||
this.liveMempoolBlockTransactions$.subscribe();
|
||||
|
||||
// Emits the full list of pending accelerations each time it changes
|
||||
this.liveAccelerations$ = this.accelerations$.pipe(
|
||||
|
||||
@@ -35,6 +35,7 @@ export class WebsocketService {
|
||||
private isTrackingAddresses: string[] | false = false;
|
||||
private isTrackingAccelerations: boolean = false;
|
||||
private trackingMempoolBlock: number;
|
||||
private stoppingTrackMempoolBlock: any | null = null;
|
||||
private latestGitCommit = '';
|
||||
private onlineCheckTimeout: number;
|
||||
private onlineCheckTimeoutTwo: number;
|
||||
@@ -203,19 +204,31 @@ export class WebsocketService {
|
||||
this.websocketSubject.next({ 'track-asset': 'stop' });
|
||||
}
|
||||
|
||||
startTrackMempoolBlock(block: number, force: boolean = false) {
|
||||
startTrackMempoolBlock(block: number, force: boolean = false): boolean {
|
||||
if (this.stoppingTrackMempoolBlock) {
|
||||
clearTimeout(this.stoppingTrackMempoolBlock);
|
||||
}
|
||||
// skip duplicate tracking requests
|
||||
if (force || this.trackingMempoolBlock !== block) {
|
||||
this.websocketSubject.next({ 'track-mempool-block': block });
|
||||
this.isTrackingMempoolBlock = true;
|
||||
this.trackingMempoolBlock = block;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
stopTrackMempoolBlock() {
|
||||
this.websocketSubject.next({ 'track-mempool-block': -1 });
|
||||
stopTrackMempoolBlock(): void {
|
||||
if (this.stoppingTrackMempoolBlock) {
|
||||
clearTimeout(this.stoppingTrackMempoolBlock);
|
||||
}
|
||||
this.isTrackingMempoolBlock = false;
|
||||
this.trackingMempoolBlock = null;
|
||||
this.stoppingTrackMempoolBlock = setTimeout(() => {
|
||||
this.stoppingTrackMempoolBlock = null;
|
||||
this.websocketSubject.next({ 'track-mempool-block': -1 });
|
||||
this.trackingMempoolBlock = null;
|
||||
this.stateService.mempoolBlockState = null;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
startTrackRbf(mode: 'all' | 'fullRbf') {
|
||||
@@ -424,6 +437,7 @@ export class WebsocketService {
|
||||
if (response['projected-block-transactions'].blockTransactions) {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
this.stateService.mempoolBlockUpdate$.next({
|
||||
block: this.trackingMempoolBlock,
|
||||
transactions: response['projected-block-transactions'].blockTransactions.map(uncompressTx),
|
||||
});
|
||||
} else if (response['projected-block-transactions'].delta) {
|
||||
@@ -432,7 +446,7 @@ export class WebsocketService {
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||
} else {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(response['projected-block-transactions'].delta));
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(this.trackingMempoolBlock, response['projected-block-transactions'].delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export type AddressType = 'fee'
|
||||
| 'v0_p2wsh'
|
||||
| 'v1_p2tr'
|
||||
| 'confidential'
|
||||
| 'anchor'
|
||||
| 'unknown'
|
||||
|
||||
const ADDRESS_PREFIXES = {
|
||||
@@ -188,6 +189,12 @@ export class AddressTypeInfo {
|
||||
const v = vin[0];
|
||||
this.processScript(new ScriptInfo('scriptpubkey', v.prevout.scriptpubkey, v.prevout.scriptpubkey_asm));
|
||||
}
|
||||
} else if (this.type === 'unknown') {
|
||||
for (const v of vin) {
|
||||
if (v.prevout?.scriptpubkey === '51024e73') {
|
||||
this.type = 'anchor';
|
||||
}
|
||||
}
|
||||
}
|
||||
// and there's nothing more to learn from processing inputs for other types
|
||||
}
|
||||
@@ -197,6 +204,10 @@ export class AddressTypeInfo {
|
||||
if (!this.scripts.size) {
|
||||
this.processScript(new ScriptInfo('scriptpubkey', output.scriptpubkey, output.scriptpubkey_asm));
|
||||
}
|
||||
} else if (this.type === 'unknown') {
|
||||
if (output.scriptpubkey === '51024e73') {
|
||||
this.type = 'anchor';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,8 +170,9 @@ export function uncompressTx(tx: TransactionCompressed): TransactionStripped {
|
||||
};
|
||||
}
|
||||
|
||||
export function uncompressDeltaChange(delta: MempoolBlockDeltaCompressed): MempoolBlockDelta {
|
||||
export function uncompressDeltaChange(block: number, delta: MempoolBlockDeltaCompressed): MempoolBlockDelta {
|
||||
return {
|
||||
block,
|
||||
added: delta.added.map(uncompressTx),
|
||||
removed: delta.removed,
|
||||
changed: delta.changed.map(tx => ({
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
@case ('multisig') {
|
||||
<span i18n="address.bare-multisig">bare multisig</span>
|
||||
}
|
||||
@case ('anchor') {
|
||||
<span>anchor</span>
|
||||
}
|
||||
@case (null) {
|
||||
<span>unknown</span>
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ export const ScriptTemplates: { [type: string]: (...args: any) => ScriptTemplate
|
||||
ln_anchor: () => ({ type: 'ln_anchor', label: 'Lightning Anchor' }),
|
||||
ln_anchor_swept: () => ({ type: 'ln_anchor_swept', label: 'Swept Lightning Anchor' }),
|
||||
multisig: (m: number, n: number) => ({ type: 'multisig', m, n, label: $localize`:@@address-label.multisig:Multisig ${m}:multisigM: of ${n}:multisigN:` }),
|
||||
anchor: () => ({ type: 'anchor', label: 'anchor' }),
|
||||
};
|
||||
|
||||
export class ScriptInfo {
|
||||
@@ -266,7 +267,7 @@ export function parseMultisigScript(script: string): undefined | { m: number, n:
|
||||
if (!opN) {
|
||||
return;
|
||||
}
|
||||
if (!opN.startsWith('OP_PUSHNUM_')) {
|
||||
if (opN !== 'OP_0' && !opN.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const n = parseInt(opN.match(/[0-9]+/)?.[0] || '', 10);
|
||||
@@ -286,7 +287,7 @@ export function parseMultisigScript(script: string): undefined | { m: number, n:
|
||||
if (!opM) {
|
||||
return;
|
||||
}
|
||||
if (!opM.startsWith('OP_PUSHNUM_')) {
|
||||
if (opM !== 'OP_0' && !opM.startsWith('OP_PUSHNUM_')) {
|
||||
return;
|
||||
}
|
||||
const m = parseInt(opM.match(/[0-9]+/)?.[0] || '', 10);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstra
|
||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
|
||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark} from '@fortawesome/free-solid-svg-icons';
|
||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark, faCalendarCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
import { MenuComponent } from '../components/menu/menu.component';
|
||||
import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component';
|
||||
@@ -100,6 +100,7 @@ import { MempoolErrorComponent } from './components/mempool-error/mempool-error.
|
||||
import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component';
|
||||
import { PendingStatsComponent } from '../components/acceleration/pending-stats/pending-stats.component';
|
||||
import { AccelerationStatsComponent } from '../components/acceleration/acceleration-stats/acceleration-stats.component';
|
||||
import { AccelerationSparklesComponent } from '../components/acceleration/sparkles/acceleration-sparkles.component';
|
||||
|
||||
import { BlockViewComponent } from '../components/block-view/block-view.component';
|
||||
import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component';
|
||||
@@ -225,6 +226,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||
AccelerationsListComponent,
|
||||
AccelerationStatsComponent,
|
||||
PendingStatsComponent,
|
||||
AccelerationSparklesComponent,
|
||||
HttpErrorComponent,
|
||||
TwitterWidgetComponent,
|
||||
FaucetComponent,
|
||||
@@ -355,6 +357,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||
AccelerationsListComponent,
|
||||
AccelerationStatsComponent,
|
||||
PendingStatsComponent,
|
||||
AccelerationSparklesComponent,
|
||||
HttpErrorComponent,
|
||||
TwitterWidgetComponent,
|
||||
TwitterLogin,
|
||||
@@ -437,5 +440,6 @@ export class SharedModule {
|
||||
library.addIcons(faFaucetDrip);
|
||||
library.addIcons(faTimeline);
|
||||
library.addIcons(faCircleXmark);
|
||||
library.addIcons(faCalendarCheck);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TransactionFlags } from './filters.utils';
|
||||
import { getVarIntLength, opcodes, parseMultisigScript, isPoint } from './script.utils';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { CpfpInfo, RbfInfo } from '../interfaces/node-api.interface';
|
||||
import { CpfpInfo, RbfInfo, TransactionStripped } from '../interfaces/node-api.interface';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
||||
// Bitcoin Core default policy settings
|
||||
const TX_MAX_STANDARD_VERSION = 2;
|
||||
const MAX_STANDARD_TX_WEIGHT = 400_000;
|
||||
const MAX_BLOCK_SIGOPS_COST = 80_000;
|
||||
const MAX_STANDARD_TX_SIGOPS_COST = (MAX_BLOCK_SIGOPS_COST / 5);
|
||||
@@ -89,10 +89,13 @@ export function isDERSig(w: string): boolean {
|
||||
*
|
||||
* returns true early if any standardness rule is violated, otherwise false
|
||||
* (except for non-mandatory-script-verify-flag and p2sh script evaluation rules which are *not* enforced)
|
||||
*
|
||||
* As standardness rules change, we'll need to apply the rules in force *at the time* to older blocks.
|
||||
* For now, just pull out individual rules into versioned functions where necessary.
|
||||
*/
|
||||
export function isNonStandard(tx: Transaction): boolean {
|
||||
export function isNonStandard(tx: Transaction, height?: number, network?: string): boolean {
|
||||
// version
|
||||
if (tx.version > TX_MAX_STANDARD_VERSION) {
|
||||
if (isNonStandardVersion(tx, height, network)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -139,6 +142,8 @@ export function isNonStandard(tx: Transaction): boolean {
|
||||
}
|
||||
} else if (['unknown', 'provably_unspendable', 'empty'].includes(vin.prevout?.scriptpubkey_type || '')) {
|
||||
return true;
|
||||
} else if (isNonStandardAnchor(tx, height, network)) {
|
||||
return true;
|
||||
}
|
||||
// TODO: bad-witness-nonstandard
|
||||
}
|
||||
@@ -203,6 +208,51 @@ export function isNonStandard(tx: Transaction): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Individual versioned standardness rules
|
||||
|
||||
const V3_STANDARDNESS_ACTIVATION_HEIGHT = {
|
||||
'testnet4': 42_000,
|
||||
'testnet': 2_900_000,
|
||||
'signet': 211_000,
|
||||
'': 863_500,
|
||||
};
|
||||
function isNonStandardVersion(tx: Transaction, height?: number, network?: string): boolean {
|
||||
let TX_MAX_STANDARD_VERSION = 3;
|
||||
if (
|
||||
height != null
|
||||
&& network != null
|
||||
&& V3_STANDARDNESS_ACTIVATION_HEIGHT[network]
|
||||
&& height <= V3_STANDARDNESS_ACTIVATION_HEIGHT[network]
|
||||
) {
|
||||
// V3 transactions were non-standard to spend before v28.x (scheduled for 2024/09/30 https://github.com/bitcoin/bitcoin/issues/29891)
|
||||
TX_MAX_STANDARD_VERSION = 2;
|
||||
}
|
||||
|
||||
if (tx.version > TX_MAX_STANDARD_VERSION) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT = {
|
||||
'testnet4': 42_000,
|
||||
'testnet': 2_900_000,
|
||||
'signet': 211_000,
|
||||
'': 863_500,
|
||||
};
|
||||
function isNonStandardAnchor(tx: Transaction, height?: number, network?: string): boolean {
|
||||
if (
|
||||
height != null
|
||||
&& network != null
|
||||
&& ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT[network]
|
||||
&& height <= ANCHOR_STANDARDNESS_ACTIVATION_HEIGHT[network]
|
||||
) {
|
||||
// anchor outputs were non-standard to spend before v28.x (scheduled for 2024/09/30 https://github.com/bitcoin/bitcoin/issues/29891)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A witness program is any valid scriptpubkey that consists of a 1-byte push opcode
|
||||
// followed by a data push between 2 and 40 bytes.
|
||||
// https://github.com/bitcoin/bitcoin/blob/2c79abc7ad4850e9e3ba32a04c530155cda7f980/src/script/script.cpp#L224-L240
|
||||
@@ -289,7 +339,7 @@ export function isBurnKey(pubkey: string): boolean {
|
||||
].includes(pubkey);
|
||||
}
|
||||
|
||||
export function getTransactionFlags(tx: Transaction, cpfpInfo?: CpfpInfo, replacement?: boolean): bigint {
|
||||
export function getTransactionFlags(tx: Transaction, cpfpInfo?: CpfpInfo, replacement?: boolean, height?: number, network?: string): bigint {
|
||||
let flags = tx.flags ? BigInt(tx.flags) : 0n;
|
||||
|
||||
// Update variable flags (CPFP, RBF)
|
||||
@@ -439,7 +489,7 @@ export function getTransactionFlags(tx: Transaction, cpfpInfo?: CpfpInfo, replac
|
||||
flags |= TransactionFlags.batch_payout;
|
||||
}
|
||||
|
||||
if (isNonStandard(tx)) {
|
||||
if (isNonStandard(tx, height, network)) {
|
||||
flags |= TransactionFlags.nonstandard;
|
||||
}
|
||||
|
||||
@@ -458,4 +508,83 @@ export function getUnacceleratedFeeRate(tx: Transaction, accelerated: boolean):
|
||||
} else {
|
||||
return tx.effectiveFeePerVsize;
|
||||
}
|
||||
}
|
||||
|
||||
export function identifyPrioritizedTransactions(transactions: TransactionStripped[]): { prioritized: string[], deprioritized: string[] } {
|
||||
// find the longest increasing subsequence of transactions
|
||||
// (adapted from https://en.wikipedia.org/wiki/Longest_increasing_subsequence#Efficient_algorithms)
|
||||
// should be O(n log n)
|
||||
const X = transactions.slice(1).reverse(); // standard block order is by *decreasing* effective fee rate, but we want to iterate in increasing order (and skip the coinbase)
|
||||
if (X.length < 2) {
|
||||
return { prioritized: [], deprioritized: [] };
|
||||
}
|
||||
const N = X.length;
|
||||
const P: number[] = new Array(N);
|
||||
const M: number[] = new Array(N + 1);
|
||||
M[0] = -1; // undefined so can be set to any value
|
||||
|
||||
let L = 0;
|
||||
for (let i = 0; i < N; i++) {
|
||||
// Binary search for the smallest positive l ≤ L
|
||||
// such that X[M[l]].effectiveFeePerVsize > X[i].effectiveFeePerVsize
|
||||
let lo = 1;
|
||||
let hi = L + 1;
|
||||
while (lo < hi) {
|
||||
const mid = lo + Math.floor((hi - lo) / 2); // lo <= mid < hi
|
||||
if (X[M[mid]].rate > X[i].rate) {
|
||||
hi = mid;
|
||||
} else { // if X[M[mid]].effectiveFeePerVsize < X[i].effectiveFeePerVsize
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// After searching, lo == hi is 1 greater than the
|
||||
// length of the longest prefix of X[i]
|
||||
const newL = lo;
|
||||
|
||||
// The predecessor of X[i] is the last index of
|
||||
// the subsequence of length newL-1
|
||||
P[i] = M[newL - 1];
|
||||
M[newL] = i;
|
||||
|
||||
if (newL > L) {
|
||||
// If we found a subsequence longer than any we've
|
||||
// found yet, update L
|
||||
L = newL;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct the longest increasing subsequence
|
||||
// It consists of the values of X at the L indices:
|
||||
// ..., P[P[M[L]]], P[M[L]], M[L]
|
||||
const LIS: TransactionStripped[] = new Array(L);
|
||||
let k = M[L];
|
||||
for (let j = L - 1; j >= 0; j--) {
|
||||
LIS[j] = X[k];
|
||||
k = P[k];
|
||||
}
|
||||
|
||||
const lisMap = new Map<string, number>();
|
||||
LIS.forEach((tx, index) => lisMap.set(tx.txid, index));
|
||||
|
||||
const prioritized: string[] = [];
|
||||
const deprioritized: string[] = [];
|
||||
|
||||
let lastRate = 0;
|
||||
|
||||
for (const tx of X) {
|
||||
if (lisMap.has(tx.txid)) {
|
||||
lastRate = tx.rate;
|
||||
} else {
|
||||
if (Math.abs(tx.rate - lastRate) < 0.1) {
|
||||
// skip if the rate is almost the same as the previous transaction
|
||||
} else if (tx.rate <= lastRate) {
|
||||
prioritized.push(tx.txid);
|
||||
} else {
|
||||
deprioritized.push(tx.txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { prioritized, deprioritized };
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -457,6 +457,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="bee6b649ee82d9a7cde233070b665eec7c531b1d" datatype="html">
|
||||
<source>Plus <x id="INTERPOLATION" equiv-text="{{ estimate.txSummary.ancestorCount - 1 }}"/> unconfirmed ancestor(s)</source>
|
||||
<target>컨펌되지 않은 조상(들) <x id="INTERPOLATION" equiv-text="{{ estimate.txSummary.ancestorCount - 1 }}"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
@@ -491,6 +492,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="df89e157bacb4ab32e6ec725bf1eb176dc15201e" datatype="html">
|
||||
<source>Size in vbytes of this transaction (including unconfirmed ancestors)</source>
|
||||
<target>트랜잭션 크기 (확인되지 않은 조상 포함)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
@@ -499,6 +501,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="adbeb446bf941afda4d4a923b5e4ce0cf4a1c1b8" datatype="html">
|
||||
<source>In-band fees</source>
|
||||
<target>대역 내 수수료</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
@@ -624,6 +627,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="fad137784196a8fdc10588e27ed5d8ae95fe4e79" datatype="html">
|
||||
<source>Fees already paid by this transaction (including unconfirmed ancestors)</source>
|
||||
<target>이 트랜잭션이 이미 지불한 수수료 (확인되지 않은 조상 포함)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
@@ -632,6 +636,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="4169a885bc1747a38344bae64e6926c6d7d7ec43" datatype="html">
|
||||
<source>How much faster?</source>
|
||||
<target>얼마나 더 빠르게 원하시나요?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
@@ -640,6 +645,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="d1a62bdb732f1efbfdc8af6fbb4349b89015b5e5" datatype="html">
|
||||
<source>This will reduce your expected waiting time until the first confirmation to <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="START_TAG_APP_TIME" ctype="x-app_time" equiv-text="n" [time]="etaInfo.acceleratedETA" [fastRender]="false" [fixedRender]="true">"/><x id="CLOSE_TAG_APP_TIME" ctype="x-app_time" equiv-text="</strong></s"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</small>"/></source>
|
||||
<target>첫 번째 컨펌까지 예상 대기 시간이 <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="START_TAG_APP_TIME" ctype="x-app_time" equiv-text="n" [time]="etaInfo.acceleratedETA" [fastRender]="false" [fixedRender]="true">"/><x id="CLOSE_TAG_APP_TIME" ctype="x-app_time" equiv-text="</strong></s"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</small>"/>로 단축됩니다</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">76,77</context>
|
||||
@@ -657,6 +663,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="0b537472d5f7518ed2c2c2b747997b0447ec5ee8" datatype="html">
|
||||
<source>Next block market rate</source>
|
||||
<target>다음 블록 시장 수수료율</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">109</context>
|
||||
@@ -687,6 +694,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="c2836a2964adf9e369ee0a1ce67f991cf2aa435d" datatype="html">
|
||||
<source>Estimated extra fee required</source>
|
||||
<target>예측된 추가발생 수수료</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
@@ -695,6 +703,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="6c37b6a6f9e5ec98367ed744afa4b36800aa79ce" datatype="html">
|
||||
<source>Target rate</source>
|
||||
<target>목표율</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">131</context>
|
||||
@@ -703,6 +712,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="e26d365629446e476b5d437e343b5b02b49adea2" datatype="html">
|
||||
<source>Extra fee required</source>
|
||||
<target>필요한 추가 수수료</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">139</context>
|
||||
@@ -711,6 +721,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="26e50fa97db4eecde26ff892d725e61ca9201c48" datatype="html">
|
||||
<source>Mempool Accelerator™ fees</source>
|
||||
<target>멤풀 엑셀러레이터 수수료</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">153</context>
|
||||
@@ -719,6 +730,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="1ec82428244c76064090ea5a55827e3fada82306" datatype="html">
|
||||
<source>Accelerator Service Fee</source>
|
||||
<target>엑셀러레이터 서비스 수수료</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">157</context>
|
||||
@@ -727,6 +739,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="7d07b80b17dfab3582807759420b8d723c9e4414" datatype="html">
|
||||
<source>Transaction Size Surcharge</source>
|
||||
<target>트랜잭션 사이즈에 의한 추가 요금</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">169</context>
|
||||
@@ -735,6 +748,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="004732b44df582a2d24e2abbd3f46bc42ae8c546" datatype="html">
|
||||
<source>Estimated acceleration cost</source>
|
||||
<target>예상 가속 비용</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">185</context>
|
||||
@@ -743,6 +757,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="c9ec95585f57bd87212693db7cb00d9ed70d49b1" datatype="html">
|
||||
<source>Maximum acceleration cost</source>
|
||||
<target>최대 가속 비용</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">204</context>
|
||||
@@ -760,6 +775,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="f3ff11006f77909b9fca2e0fda0a72b097cd76de" datatype="html">
|
||||
<source>Available balance</source>
|
||||
<target>잔액</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">226</context>
|
||||
@@ -785,6 +801,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="7d89e94e98140d07d5c2bb12d6166b8b74506eb0" datatype="html">
|
||||
<source>Accelerate your Bitcoin transaction?</source>
|
||||
<target>비트코인 트랜잭션 가속하기</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">273</context>
|
||||
@@ -802,6 +819,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="f6a46cd5ca2087712a145f2c680e2aad5f926eaf" datatype="html">
|
||||
<source>Confirmation expected</source>
|
||||
<target>컨펌이 예상됩니다</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">287</context>
|
||||
@@ -1587,6 +1605,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="cf2ec414465d65ab24b354663d94d051a67e26e9" datatype="html">
|
||||
<source>Total vSize</source>
|
||||
<target>총 vSize</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
|
||||
@@ -510,7 +510,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="e4b2d9e6a2ab9e6ca34027ec03beaac42b7badd4" datatype="html">
|
||||
<source>sats</source>
|
||||
<target>sats</target>
|
||||
<target>sat</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">57</context>
|
||||
@@ -881,7 +881,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="65fd4251d8ddfe4017d4d83f8cec6f5a80d89289" datatype="html">
|
||||
<source>Pay</source>
|
||||
<target>Betale</target>
|
||||
<target>Betal</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">378</context>
|
||||
@@ -4846,7 +4846,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="615ba6c4511a36f93c225c725935fdbf16f162a5" datatype="html">
|
||||
<source>Amount (sats)</source>
|
||||
<target>Beløp (sats)</target>
|
||||
<target>Beløp (sat)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/faucet/faucet.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
@@ -6442,7 +6442,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="31443c29cb161e8aa661eb5035f675746ef95b45" datatype="html">
|
||||
<source>sats/tx</source>
|
||||
<target>sats/tx</target>
|
||||
<target>sat/tx</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/reward-stats/reward-stats.component.html</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
@@ -8145,7 +8145,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="6acd06bd5a3af583cd46c6d9f7954d7a2b44095e" datatype="html">
|
||||
<source>mSats</source>
|
||||
<target>mSats</target>
|
||||
<target>mSat</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/channel/channel-box/channel-box.component.html</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
|
||||
@@ -645,6 +645,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="d1a62bdb732f1efbfdc8af6fbb4349b89015b5e5" datatype="html">
|
||||
<source>This will reduce your expected waiting time until the first confirmation to <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="START_TAG_APP_TIME" ctype="x-app_time" equiv-text="n" [time]="etaInfo.acceleratedETA" [fastRender]="false" [fixedRender]="true">"/><x id="CLOSE_TAG_APP_TIME" ctype="x-app_time" equiv-text="</strong></s"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</small>"/></source>
|
||||
<target>İlk onaya kadar geçen bekleme süresini <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="START_TAG_APP_TIME" ctype="x-app_time" equiv-text="n" [time]="etaInfo.acceleratedETA" [fastRender]="false" [fixedRender]="true">"/><x id="CLOSE_TAG_APP_TIME" ctype="x-app_time" equiv-text="</strong></s"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</small>"/>kadar azaltacak.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/accelerate-checkout/accelerate-checkout.component.html</context>
|
||||
<context context-type="linenumber">76,77</context>
|
||||
@@ -1392,6 +1393,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="c3aaae1073e33c932a5c98f98c3520645c0e3a93" datatype="html">
|
||||
<source>Out-of-band fees</source>
|
||||
<target>Bant-dışı ücretler</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/acceleration-timeline/acceleration-timeline-tooltip.component.html</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
@@ -1791,6 +1793,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="a7c328c4773db932ff14a1954e15e43dca58e7b7" datatype="html">
|
||||
<source>Completed</source>
|
||||
<target>Tamamlandı</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/acceleration/accelerations-list/accelerations-list.component.html</context>
|
||||
<context context-type="linenumber">65</context>
|
||||
@@ -1799,6 +1802,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="64b582e0d8e3a28331a14d2a1017fa5d6ffb8d93" datatype="html">
|
||||
<source>Failed</source>
|
||||
<target>Başarısız oldu</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/acceleration/accelerations-list/accelerations-list.component.html</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
@@ -2320,6 +2324,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="9eb81e2576ffe4e8fb0a303e203040b6ab23cc22" datatype="html">
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="There are too many transactions on this address, more than your backend can handle. See more on <"/>There are too many transactions on this address, more than your backend can handle. See more on <x id="START_LINK" ctype="x-a" equiv-text="<a href="/docs/faq#address-lookup-issues">"/>setting up a stronger backend<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>.<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/><x id="LINE_BREAK" ctype="lb"/><x id="LINE_BREAK" ctype="lb"/> Consider viewing this address on the official Mempool website instead: </source>
|
||||
<target><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="There are too many transactions on this address, more than your backend can handle. See more on <"/> Bu adres üzerindeki işlem sayısı arka arayüzününüzün işleyemeyeceği kadar fazla. Daha kuvvetli bir arkayüz için <x id="START_LINK" ctype="x-a" equiv-text="<a href="/docs/faq#address-lookup-issues">"/>'ye bakın. <x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>.<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/><x id="LINE_BREAK" ctype="lb"/><x id="LINE_BREAK" ctype="lb"/> Ya da bu adresi resmi Mempool sitesinde görüntüleyin: </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/address/address.component.html</context>
|
||||
<context context-type="linenumber">204,207</context>
|
||||
@@ -2535,6 +2540,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.liquid.asset" datatype="html">
|
||||
<source>Browse an overview of the Liquid asset <x id="INTERPOLATION" equiv-text="this.assetContract[1]"/> (<x id="INTERPOLATION" equiv-text="this.assetContract[1]"/>): see issued amount, burned amount, circulating amount, related transactions, and more.</source>
|
||||
<target>Liquid varlığın genel görünümünü incele <x id="INTERPOLATION" equiv-text="this.assetContract[1]"/>(<x id="INTERPOLATION" equiv-text="this.assetContract[1]"/>): üretilen, yakılan, dolaşan miktarlır ve ilişkili işlemleri ve daha fazlasını gör. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/asset/asset.component.ts</context>
|
||||
<context context-type="linenumber">108</context>
|
||||
@@ -2800,6 +2806,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.block-fee-rates" datatype="html">
|
||||
<source>See Bitcoin feerates visualized over time, including minimum and maximum feerates per block along with feerates at various percentiles.</source>
|
||||
<target>Bitcoin ücret çizelgesinin zaman içindeki değişimini görüntüle. Minimum ve maksimum ücretler ve farklı yüzdelik dilimlerdeki ücretleri görüntüleyebilirsin. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
@@ -2824,6 +2831,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.block-fees" datatype="html">
|
||||
<source>See the average mining fees earned per Bitcoin block visualized in BTC and USD over time.</source>
|
||||
<target>Bitcoin bloğu başına ortalama madencilik ücretlerinin BTC ve USD cinsi olarak değişimini gör. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-fees-graph/block-fees-graph.component.ts</context>
|
||||
<context context-type="linenumber">70</context>
|
||||
@@ -3012,6 +3020,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.block-health" datatype="html">
|
||||
<source>See Bitcoin block health visualized over time. Block health is a measure of how many expected transactions were included in an actual mined block. Expected transactions are determined using Mempool's re-implementation of Bitcoin Core's transaction selection algorithm.</source>
|
||||
<target>Bitcoin blok sağlığını zaman içinde görüntüle. Blok sağlığı beklenen işlemlerin kaçının gerçekten bloğa dahil edildiğinin ölçüsüdür. Beklenen işlemler Mempool'un çalıştırdığı Bitcoin Core işlem seçme algoritması ile belirlenir.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-health-graph/block-health-graph.component.ts</context>
|
||||
<context context-type="linenumber">64</context>
|
||||
@@ -3298,6 +3307,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.block-rewards" datatype="html">
|
||||
<source>See Bitcoin block rewards in BTC and USD visualized over time. Block rewards are the total funds miners earn from the block subsidy and fees.</source>
|
||||
<target>Bitcoin blok ödüllerini BTC ve USD cinsinden zaman içerisinde görüntüle. Blok ödülleri yeni çıkarılan bitcoin ödülleri ve işlem ücretlerinin toplamıdır. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-rewards-graph/block-rewards-graph.component.ts</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
@@ -3322,6 +3332,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.block-sizes" datatype="html">
|
||||
<source>See Bitcoin block sizes (MB) and block weights (weight units) visualized over time.</source>
|
||||
<target>Bitcoin blok boyutlarını (MB) ve blok ağırlıklarını (ağırlık ünitesi) zaman içinde görselleştir.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts</context>
|
||||
<context context-type="linenumber">65</context>
|
||||
@@ -3445,6 +3456,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.liquid.block" datatype="html">
|
||||
<source>See size, weight, fee range, included transactions, and more for Liquid<x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> block <x id="BLOCK_HEIGHT" equiv-text="block.height"/> (<x id="BLOCK_ID" equiv-text="block.id"/>).</source>
|
||||
<target>Liquid <x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> bloğundaki <x id="BLOCK_HEIGHT" equiv-text="block.height"/> (<x id="BLOCK_ID" equiv-text="block.id"/>) boyut, ağırlık, ücret aralığı, dahil edilen işlemler ve daha fazlasını gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-view/block-view.component.ts</context>
|
||||
<context context-type="linenumber">112</context>
|
||||
@@ -3460,6 +3472,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.block" datatype="html">
|
||||
<source>See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin<x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> block <x id="BLOCK_HEIGHT" equiv-text="block.height"/> (<x id="BLOCK_ID" equiv-text="block.id"/>).</source>
|
||||
<target>Bitcoin <x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> block <x id="BLOCK_HEIGHT" equiv-text="block.height"/>(<x id="BLOCK_ID" equiv-text="block.id"/>) için boyut, ağırlıklar, ücret aralığı, dahili işlemler, denetim (beklene vs gerçek) ve daha fazlasını gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block-view/block-view.component.ts</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
@@ -3651,6 +3664,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="e170a90ee0d3a604adf439a60c890caff9152466" datatype="html">
|
||||
<source>This block does not belong to the main chain, it has been replaced by:</source>
|
||||
<target>Bu blok ana-zincire dahil değil ve şununla değiştirilebilir: </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/block/block.component.html</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
@@ -4173,6 +4187,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.liquid.blocks" datatype="html">
|
||||
<source>See the most recent Liquid<x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> blocks along with basic stats such as block height, block size, and more.</source>
|
||||
<target>En güncel Liquid <x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> blokları için blok yüksekliği, blok büyüklüğü vb temel dataları gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/blocks-list/blocks-list.component.ts</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
@@ -4180,6 +4195,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.blocks" datatype="html">
|
||||
<source>See the most recent Bitcoin<x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> blocks along with basic stats such as block height, block reward, block size, and more.</source>
|
||||
<target>En güncel Bitcoin <x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> blokları için blok yüksekliği, blok büyüklüğü vb temel dataları gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/blocks-list/blocks-list.component.ts</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
@@ -5162,6 +5178,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.hashrate" datatype="html">
|
||||
<source>See hashrate and difficulty for the Bitcoin<x id="PH" equiv-text="seoDescriptionNetwork(this.network)"/> network visualized over time.</source>
|
||||
<target>Bitcoin ağı <x id="PH" equiv-text="seoDescriptionNetwork(this.network)"/> için hashrate ve zorluk seviyelerinin değişimini zaman içinde gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/hashrate-chart/hashrate-chart.component.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
@@ -5189,6 +5206,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.descriptions.bitcoin.graphs.hashrate-pools" datatype="html">
|
||||
<source>See Bitcoin mining pool dominance visualized over time: see how top mining pools' share of total hashrate has fluctuated over time.</source>
|
||||
<target>Madencilik havuzu dominasyonunu değişimini zaman içinde gör : en büyük madencilik havuzlarının toplam havuzdan aldığı payın değişimini incele.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
@@ -5311,6 +5329,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="506d3b3e461d170c39745288b9ea96b9ac9b7f78" datatype="html">
|
||||
<source>Total amount of BTC held in non-dust Federation UTXOs that have expired timelocks</source>
|
||||
<target>Dust-dışı Federasyon UTXO'larındaki zaman kilidi bitmiş toplam BTC miktarını gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component.html</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
@@ -5510,6 +5529,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="3669efae1ff592688b4df067abf0a272e90af226" datatype="html">
|
||||
<source>Fund / Redemption Tx</source>
|
||||
<target>Fon/ Amortisman İşlemi</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
@@ -5581,6 +5601,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="52b32e9a8be459e6539a9b9214c2a17b23206a6c" datatype="html">
|
||||
<source>Number of times that the Federation's BTC holdings fall below 95% of the total L-BTC supply</source>
|
||||
<target>Federasyonun tuttuğu BTC miktarının toplam L-BTC'nin %95'inin altına düşme sayısı</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/liquid-reserves-audit/reserves-ratio-stats/reserves-ratio-stats.component.html</context>
|
||||
<context context-type="linenumber">6</context>
|
||||
@@ -5698,6 +5719,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.mempool-block" datatype="html">
|
||||
<source>See stats for <x id="PH" equiv-text="this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'"/><x id="PH_1" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> transactions in the mempool: fee range, aggregate size, and more. Mempool blocks are updated in real-time as the network receives new transactions.</source>
|
||||
<target>İşlemler <x id="PH" equiv-text="this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'"/><x id="PH_1" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> için mempool istatistiklerini göster: ücret aralığı, toplam büyüklük, ve fazlasını gör. Mempool blokları, ağa yeni işlem geldiğinde anlık olarak güncellenir. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/mempool-block/mempool-block.component.ts</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
@@ -5793,6 +5815,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.mining.dashboard" datatype="html">
|
||||
<source>Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.</source>
|
||||
<target>Anlık olarak hashrate, zorluk seviyesi, blok ödülleri, havuz dominasyonu vb madencilik istatistiklerini görüntüle. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/mining-dashboard/mining-dashboard.component.ts</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
@@ -6071,6 +6094,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.mining.pool" datatype="html">
|
||||
<source>See mining pool stats for <x id="PH" equiv-text="poolStats.pool.name"/>: most recent mined blocks, hashrate over time, total block reward to date, known coinbase addresses, and more.</source>
|
||||
<target>Madencilik havuzu istatistiklerini <x id="PH" equiv-text="poolStats.pool.name"/>: en son bulunan bloklar, hashrate'in zaman içindeki değişimi, bugüne kadarki toplam ödül miktarı, bilinen Coinbase adresleri vb gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/pool/pool-preview.component.ts</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
@@ -6305,6 +6329,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.rbf-list" datatype="html">
|
||||
<source>See the most recent RBF replacements on the Bitcoin<x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> network, updated in real-time.</source>
|
||||
<target>Bitcoin <x id="PH" equiv-text="seoDescriptionNetwork(this.stateService.network)"/> ağı üzerindeki en yeni RBF değişimlerini gerçek zamanlı olarak görüntüle.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/rbf-list/rbf-list.component.ts</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
@@ -6618,6 +6643,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="68d44b7bd049ae93c2bc15973eb5266aec64693e" datatype="html">
|
||||
<source>Cap outliers</source>
|
||||
<target>Sınır dışı değerler</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/statistics/statistics.component.html</context>
|
||||
<context context-type="linenumber">121</context>
|
||||
@@ -6626,6 +6652,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.graphs.mempool" datatype="html">
|
||||
<source>See mempool size (in MvB) and transactions per second (in vB/s) visualized over time.</source>
|
||||
<target>Mempool büyüklüğünün (MvB olarak) ve saniyedeki işlem sayısının (vB/s) zaman içindeki değişimini görselleştir.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/statistics/statistics.component.ts</context>
|
||||
<context context-type="linenumber">66</context>
|
||||
@@ -6633,6 +6660,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.tv" datatype="html">
|
||||
<source>See Bitcoin blocks and mempool congestion in real-time in a simplified format perfect for a TV.</source>
|
||||
<target>Bitcoin bloklarını ve mempool yoğunluğunu televizyon formatına uygun olarak doğru zamanlı gör</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/television/television.component.ts</context>
|
||||
<context context-type="linenumber">40</context>
|
||||
@@ -6667,6 +6695,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="48e4b0c012de5020053ecb26e9ac0d35a1f60688" datatype="html">
|
||||
<source>Comma-separated list of raw transactions</source>
|
||||
<target>Raw-işlem datalarının virgül ile ayrık gösterimi</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/test-transactions/test-transactions.component.html</context>
|
||||
<context context-type="linenumber">7</context>
|
||||
@@ -7113,6 +7142,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="52a68ca949dfcdeaaea81bec4d597256b8ad42b5" datatype="html">
|
||||
<source>Waiting for your transaction to appear in the mempool</source>
|
||||
<target>İşleminizin mempool'da gözükemsini bekliyoruz.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/tracker/tracker.component.html</context>
|
||||
<context context-type="linenumber">150</context>
|
||||
@@ -7121,6 +7151,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="5ad21d21f3e26ddfe0abeed499db5d5c0bd0e325" datatype="html">
|
||||
<source>Your transaction is in the mempool, but it will not be confirmed for some time.</source>
|
||||
<target>İşleminiz mempool'da yalnız yakın zamanda onaylanması beklenmiyor.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/tracker/tracker.component.html</context>
|
||||
<context context-type="linenumber">156</context>
|
||||
@@ -7129,6 +7160,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="809118722b27889f5424609d1779f356bcef2cc2" datatype="html">
|
||||
<source>Your transaction is near the top of the mempool, and is expected to confirm soon.</source>
|
||||
<target>İşleminizin mempool'un üst kademesinde, yakında onaylanması bekleniyor.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/tracker/tracker.component.html</context>
|
||||
<context context-type="linenumber">162</context>
|
||||
@@ -7137,6 +7169,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="ee76deb7716e90b79e557394b1d256079b7ec24e" datatype="html">
|
||||
<source>Your transaction is expected to confirm in the next block</source>
|
||||
<target>İşleminizin bir sonraki blokta onaylanması bekleniyor.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/tracker/tracker.component.html</context>
|
||||
<context context-type="linenumber">168</context>
|
||||
@@ -7188,6 +7221,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.bitcoin.transaction" datatype="html">
|
||||
<source>Get real-time status, addresses, fees, script info, and more for <x id="PH" equiv-text="network"/><x id="PH_1" equiv-text="seoDescription"/> transaction with txid <x id="PH_2" equiv-text="this.txId"/>.</source>
|
||||
<target>İşlemler <x id="PH" equiv-text="network"/><x id="PH_1" equiv-text="seoDescription"/> ve işlem id'si <x id="PH_2" equiv-text="this.txId"/> için anlık durum, adresler, ücretler, script vb bilgileri çek.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/tracker/tracker.component.ts</context>
|
||||
<context context-type="linenumber">413</context>
|
||||
@@ -7923,6 +7957,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="999bb1a0150c2815a6b4dd64a1850e763603e525" datatype="html">
|
||||
<source><x id="START_PARAGRAPH" ctype="x-p" equiv-text="For any such requ"/><x id="START_BOLD_TEXT" ctype="x-b" equiv-text="mempool.space mer"/>mempool.space merely provides data about the Bitcoin network.<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/> It cannot help you with retrieving funds, wallet issues, etc.<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/><x id="START_PARAGRAPH" ctype="x-p" equiv-text="For any such requ"/>For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/></source>
|
||||
<target><x id="START_PARAGRAPH" ctype="x-p" equiv-text="For any such requ"/><x id="START_BOLD_TEXT" ctype="x-b" equiv-text="mempool.space mer"/>mempool.space Bitcoin ağı hakkında sadece bilgi sağlar. <x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/>kaybettiğiniz fonları, cüzdanlar ile yaşadığınız sorunları çözmekte yardımcı olamaz. <x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/><x id="START_PARAGRAPH" ctype="x-p" equiv-text="For any such requ"/>İşlemler ile ilgili sorun yaşarsanız bu işlemi gerçekleştirdiğiniz entite ile iletişime geçmeniz gerekir. (cüzdan yazılımı, borsa vb)<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/docs/api-docs/api-docs.component.html</context>
|
||||
<context context-type="linenumber">15,16</context>
|
||||
@@ -8025,6 +8060,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.docs.faq" datatype="html">
|
||||
<source>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.</source>
|
||||
<target>Mempool nedir, neden işlemim onaylanmıyor, Açık Kaynak Kodlu Mempool projesinin bir kopyasını nasıl çalıştırabilirim? gibi temel sorulara cevaplar bulun.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/docs/docs/docs.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
@@ -8072,6 +8108,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.docs.websocket-bitcoin" datatype="html">
|
||||
<source>Documentation for the mempool.space WebSocket API service: get real-time info on blocks, mempools, transactions, addresses, and more.</source>
|
||||
<target>Mempool.space Websoket API servisi için, bloklardan gerçek-zamanlı bilgi çek, mempoollar, işlemler, adresler vb talepler için dökümantasyon. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/docs/docs/docs.component.ts</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
@@ -8087,6 +8124,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.docs.electrumrpc" datatype="html">
|
||||
<source>Documentation for our Electrum RPC interface: get instant, convenient, and reliable access to an Esplora instance.</source>
|
||||
<target>Electrum RPC için arayüz dökümantasyonu: Esplora'ya anında, kolayca ve emniyetli bir şekilde ulaşın. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/docs/docs/docs.component.ts</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
@@ -8403,6 +8441,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.channel" datatype="html">
|
||||
<source>Overview for Lightning channel <x id="PH" equiv-text="params.get('short_id')"/>. See channel capacity, the Lightning nodes involved, related on-chain transactions, and more.</source>
|
||||
<target>Lightning Kanalı <x id="PH" equiv-text="params.get('short_id')"/> için genel bakış sağlar. Kanal kapasitesi, bağlantılı Lightning nodeları, alakalı zincir üstü işlemler vb veriler. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/channel/channel-preview.component.ts</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
@@ -9030,6 +9069,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.dashboard" datatype="html">
|
||||
<source>Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).</source>
|
||||
<target>Lightning Network için istatistikleri getir. ( toplam kapasite, bağlantılar vb), Ligthning nodeları (kanallar, likidite) ve Lightning kanalları (durum, ücretler vb) </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts</context>
|
||||
<context context-type="linenumber">34</context>
|
||||
@@ -9139,6 +9179,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.node" datatype="html">
|
||||
<source>Overview for the Lightning network node named <x id="PH" equiv-text="node.alias"/>. See channels, capacity, location, fee stats, and more.</source>
|
||||
<target><x id="PH" equiv-text="node.alias"/> adındaki Lightning ağı nodu için genel bakış. Kanalları, kapasiteyi, lokasyonu, ücret bilgileri ve daha fazlasını gör. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/node/node-preview.component.ts</context>
|
||||
<context context-type="linenumber">52</context>
|
||||
@@ -9338,6 +9379,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.node-map" datatype="html">
|
||||
<source>See the channels of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details.</source>
|
||||
<target>Tor-dışı Lightning ağı nodelarını dünya haritası üzerinde görselleştir. Haritadaki noktaların üzerinde gezerek node adı ve detayları görebilirsiniz.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
@@ -9362,6 +9404,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.node-channel-map" datatype="html">
|
||||
<source>See the locations of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details.</source>
|
||||
<target>Tor-dışı Lightning ağı nodelarını dünya haritası üzerinde görselleştir. Haritadaki noktaların üzerinde gezerek node adı ve detayları görebilirsiniz.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-map/nodes-map.component.ts</context>
|
||||
<context context-type="linenumber">52</context>
|
||||
@@ -9369,6 +9412,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.nodes-network" datatype="html">
|
||||
<source>See the number of Lightning network nodes visualized over time by network: clearnet only (IPv4, IPv6), darknet (Tor, I2p, cjdns), and both.</source>
|
||||
<target>Ağ türüne göre Lightning ağı nodelarının zaman içerisindeki değişimini göster. Sadece clearnet (IPv4, IPv6), darknet (Tor, I2p, cjdns) ve iki tür bağlantı için. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
@@ -9437,6 +9481,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.nodes-country-overview" datatype="html">
|
||||
<source>See a geographical breakdown of the Lightning network: how many Lightning nodes are hosted in countries around the world, aggregate BTC capacity for each country, and more.</source>
|
||||
<target>Lightning network ağının coğrafi dağılımını görüntüle. Hangi ülkede kaç tane node bulunuyor, ülkeler için toplam BTC kapasitesi ve dha fazlası.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
@@ -9507,6 +9552,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.nodes-country" datatype="html">
|
||||
<source>Explore all the Lightning nodes hosted in <x id="PH" equiv-text="response.country.en"/> and see an overview of each node's capacity, number of open channels, and more.</source>
|
||||
<target><x id="PH" equiv-text="response.country.en"/> de çalıştırılan bütün Lightning nodeları içn node kapasitesi, açık node sayısı vb bilgileri incele.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-per-country/nodes-per-country.component.ts</context>
|
||||
<context context-type="linenumber">44</context>
|
||||
@@ -9589,6 +9635,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.nodes-per-isp" datatype="html">
|
||||
<source>Browse the top 100 ISPs hosting Lightning nodes along with stats like total number of nodes per ISP, aggregate BTC capacity per ISP, and more</source>
|
||||
<target>En fazla Lightning Node'u barındıran 100 ISP'yi ve onların ISP başı toplam node sayısı, ISP'nin toplam BTC kapasitesi vb verilerini incele.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
@@ -9651,6 +9698,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.nodes-isp" datatype="html">
|
||||
<source>Browse all Bitcoin Lightning nodes using the <x id="PH" equiv-text="response.isp"/> [AS<x id="PH_1" equiv-text="this.route.snapshot.params.isp"/>] ISP and see aggregate stats like total number of nodes, total capacity, and more for the ISP.</source>
|
||||
<target><x id="PH" equiv-text="response.isp"/> ISP [AS<x id="PH_1" equiv-text="this.route.snapshot.params.isp"/>] kulanan bütün Lightning nodelarını ve onların toplam node sayısı, toplam kapasites vb görüntüle. </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
@@ -9706,6 +9754,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.ranking.oldest" datatype="html">
|
||||
<source>See the oldest nodes on the Lightning network along with their capacity, number of channels, location, etc.</source>
|
||||
<target>Lightning ağındaki en eski nodları ve bu nodeların kanal sayısı, kapasitesi ve lokasyonunu vb dataları görüntüle.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
@@ -9713,6 +9762,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.ranking.liquidity" datatype="html">
|
||||
<source>See Lightning nodes with the most BTC liquidity deployed along with high-level stats like number of open channels, location, node age, and more.</source>
|
||||
<target>Lightning ağındaki en fazla BTC likiditesi olan nodelar için açık kanal sayısı, lokasyon, node yaşı vb dataları gör.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
@@ -9720,6 +9770,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="meta.description.lightning.ranking.channels" datatype="html">
|
||||
<source>See Lightning nodes with the most channels open along with high-level stats like total node capacity, node age, and more.</source>
|
||||
<target>Lightning nodeları için toplam node kapasitesi, node yaşı vb temel dataları görüntüle.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
@@ -10093,6 +10144,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="ba7f0c6fdfa0ab7afc59e9384bca0265d23fb018" datatype="html">
|
||||
<source>Your balance is too low.<x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/>Please <x id="START_LINK" ctype="x-a" equiv-text="<a class="top-up-link" href="/services/accelerator/overview">"/>top up your account<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>.</source>
|
||||
<target>Balansınız çok düşük. <x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/> lütfen <x id="START_LINK" ctype="x-a" equiv-text="<a class="top-up-link" href="/services/accelerator/overview">"/> hesabınıza ekleme yapınız <x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/shared/components/mempool-error/mempool-error.component.html</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
|
||||
BIN
frontend/src/resources/mempool-block-visualization.png
Normal file
BIN
frontend/src/resources/mempool-block-visualization.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
frontend/src/resources/mempool-research.png
Normal file
BIN
frontend/src/resources/mempool-research.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
frontend/src/resources/mempool-transaction.png
Normal file
BIN
frontend/src/resources/mempool-transaction.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
3
frontend/src/resources/profile/bitkey.svg
Normal file
3
frontend/src/resources/profile/bitkey.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="212" height="450" viewBox="0 0 212 450" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M114.005 2.14659C109.052 -0.71553 102.948 -0.715531 97.9948 2.14659L7.99481 54.1531C3.04732 57.012 0 62.2924 0 68.0065V172.043C0 177.758 3.04733 183.038 7.99482 185.897L69.0247 221.163C73.9722 224.022 77.0195 229.302 77.0195 235.016V433.998V437.998C77.0195 444.625 82.3921 449.998 89.0195 449.998H93.0195H118.981H123.031C129.658 449.998 135.031 444.625 135.031 437.998V233.751C135.444 228.531 138.395 223.809 142.975 221.163L204.005 185.897C208.953 183.038 212 177.758 212 172.043V68.0065C212 62.2924 208.953 57.012 204.005 54.1531L114.005 2.14659ZM112.07 68.1162C108.334 65.938 103.716 65.938 99.9803 68.1162L63.9718 89.1129C60.2841 91.2631 58.0164 95.2105 58.0164 99.4793V141.68C58.0164 145.948 60.2841 149.896 63.9718 152.046L99.9803 173.043C103.716 175.221 108.334 175.221 112.07 173.043L148.078 152.046C151.766 149.896 154.034 145.948 154.034 141.68V99.4793C154.034 95.2105 151.766 91.2631 148.078 89.1129L112.07 68.1162ZM172.046 338.527C164.047 333.861 154 339.631 154 348.892L154 356L154 412V419.108C154 428.369 164.047 434.14 172.046 429.473L196.046 415.473C199.733 413.322 202 409.376 202 405.108V362.892C202 358.624 199.733 354.678 196.046 352.527L172.046 338.527Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
frontend/src/resources/profile/leather.svg
Normal file
3
frontend/src/resources/profile/leather.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="387" height="377" viewBox="-58.05 -56.55 503.1 490.1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M249.327 137.966C283.745 132.618 333.52 96.2553 333.52 67.9135C333.52 59.3574 326.637 53.4752 316.576 53.4752C297.513 53.4752 265.212 82.3518 249.327 137.966ZM89.9409 278.606C44.9317 278.606 41.225 323.525 86.2343 323.525C106.356 323.525 130.714 315.504 143.422 301.065C124.889 285.023 109.533 278.606 89.9409 278.606ZM376.411 259.355C379.059 334.755 340.934 377 276.332 377C238.207 377 219.144 362.562 178.371 335.824C157.19 359.353 116.946 377 83.5867 377C-31.3192 377 -26.5536 229.943 90.4704 229.943C114.828 229.943 135.48 236.36 161.956 252.938L179.43 191.441C107.415 171.655 71.4077 116.041 106.886 36.3631H164.074C132.303 89.3035 154.013 133.153 194.256 137.966C215.967 60.4269 262.565 0 324.518 0C359.467 0 387.002 22.9943 387.002 64.705C387.002 131.549 300.161 186.094 234.5 191.441L207.494 287.162C238.207 322.99 323.459 357.749 323.459 259.355H376.411Z" fill="#F5F1ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1011 B |
@@ -108,6 +108,7 @@ http {
|
||||
~*^hi hi;
|
||||
~*^ne ne;
|
||||
~*^lt lt;
|
||||
~*^hr hr;
|
||||
}
|
||||
|
||||
map $cookie_lang $lang {
|
||||
@@ -145,6 +146,7 @@ http {
|
||||
~*^hi hi;
|
||||
~*^ne ne;
|
||||
~*^lt lt;
|
||||
~*^hr hr;
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
@@ -84,11 +84,11 @@ pkg install -y zsh sudo git screen curl wget neovim rsync nginx openssl openssh-
|
||||
|
||||
### Node.js + npm
|
||||
|
||||
Build Node.js v16.16.0 and npm v8 from source using `nvm`:
|
||||
Build Node.js v20.17.0 and npm v9 from source using `nvm`:
|
||||
```
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | zsh
|
||||
source $HOME/.zshrc
|
||||
nvm install v16.16.0 --shared-zlib
|
||||
nvm install v20.17.0 --shared-zlib
|
||||
nvm alias default node
|
||||
```
|
||||
|
||||
|
||||
@@ -392,9 +392,9 @@ DEBIAN_UNFURL_PKG+=(libxdamage-dev libxrandr-dev libgbm-dev libpango1.0-dev liba
|
||||
# packages needed for mempool ecosystem
|
||||
FREEBSD_PKG=()
|
||||
FREEBSD_PKG+=(zsh sudo git git-lfs screen curl wget calc neovim)
|
||||
FREEBSD_PKG+=(openssh-portable py39-pip rust llvm10 jq base64 libzmq4)
|
||||
FREEBSD_PKG+=(openssh-portable py311-pip rust llvm18 jq base64 libzmq4)
|
||||
FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc libevent libtool pkgconf)
|
||||
FREEBSD_PKG+=(nginx rsync py39-certbot-nginx mariadb1011-server keybase)
|
||||
FREEBSD_PKG+=(nginx rsync py311-certbot-nginx mariadb1011-server)
|
||||
FREEBSD_PKG+=(geoipupdate redis)
|
||||
|
||||
FREEBSD_UNFURL_PKG=()
|
||||
|
||||
@@ -40,7 +40,7 @@ update_repo()
|
||||
git fetch origin || exit 1
|
||||
for remote in origin;do
|
||||
git remote add "${remote}" "https://github.com/${remote}/mempool" >/dev/null 2>&1
|
||||
git fetch "${remote}" || exit 1
|
||||
git fetch "${remote}" --tags || exit 1
|
||||
done
|
||||
|
||||
if [ $(git tag -l "${REF}") ];then
|
||||
|
||||
@@ -32,6 +32,7 @@ map $http_accept_language $header_lang {
|
||||
~*^vi vi;
|
||||
~*^zh zh;
|
||||
~*^lt lt;
|
||||
~*^hr hr;
|
||||
}
|
||||
map $cookie_lang $lang {
|
||||
default $header_lang;
|
||||
@@ -67,4 +68,5 @@ map $cookie_lang $lang {
|
||||
~*^vi vi;
|
||||
~*^zh zh;
|
||||
~*^lt lt;
|
||||
~*^hr hr;
|
||||
}
|
||||
|
||||
4
unfurler/package-lock.json
generated
4
unfurler/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mempool-unfurl",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mempool-unfurl",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.0.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.11.41",
|
||||
"ejs": "^3.1.10",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mempool-unfurl",
|
||||
"version": "3.0.0-beta",
|
||||
"version": "3.1.0-dev",
|
||||
"description": "Renderer for mempool open graph link preview images",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user