Merge branch 'master' into mononaut/ln-penalty-scan-optimization
This commit is contained in:
commit
0d797ff7fd
@ -3,6 +3,7 @@ import { IEsploraApi } from './esplora-api.interface';
|
||||
export interface AbstractBitcoinApi {
|
||||
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
|
||||
$getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
|
||||
$getTransactionHex(txId: string): Promise<string>;
|
||||
$getBlockHeightTip(): Promise<number>;
|
||||
$getBlockHashTip(): Promise<string>;
|
||||
$getTxIdsForBlock(hash: string): Promise<string[]>;
|
||||
|
@ -57,6 +57,11 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
});
|
||||
}
|
||||
|
||||
$getTransactionHex(txId: string): Promise<string> {
|
||||
return this.$getRawTransaction(txId, true)
|
||||
.then((tx) => tx.hex || '');
|
||||
}
|
||||
|
||||
$getBlockHeightTip(): Promise<number> {
|
||||
return this.bitcoindClient.getChainTips()
|
||||
.then((result: IBitcoinApi.ChainTips[]) => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Application, Request, Response } from 'express';
|
||||
import axios from 'axios';
|
||||
import * as bitcoinjs from 'bitcoinjs-lib';
|
||||
import config from '../../config';
|
||||
import websocketHandler from '../websocket-handler';
|
||||
import mempool from '../mempool';
|
||||
@ -87,7 +88,8 @@ class BitcoinRoutes {
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', this.getBlocks.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', this.getBlocks.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', this.getBlock)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/summary', this.getStrippedBlockTransactions);
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/summary', this.getStrippedBlockTransactions)
|
||||
.post(config.MEMPOOL.API_URL_PREFIX + 'psbt/addparents', this.postPsbtCompletion)
|
||||
;
|
||||
|
||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
||||
@ -241,6 +243,74 @@ class BitcoinRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the PSBT as text/plain body, parses it, and adds the full
|
||||
* parent transaction to each input that doesn't already have it.
|
||||
* This is used for BTCPayServer / Trezor users which need access to
|
||||
* the full parent transaction even with segwit inputs.
|
||||
* It will respond with a text/plain PSBT in the same format (hex|base64).
|
||||
*/
|
||||
private async postPsbtCompletion(req: Request, res: Response): Promise<void> {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
const notFoundError = `Couldn't get transaction hex for parent of input`;
|
||||
try {
|
||||
let psbt: bitcoinjs.Psbt;
|
||||
let format: 'hex' | 'base64';
|
||||
let isModified = false;
|
||||
try {
|
||||
psbt = bitcoinjs.Psbt.fromBase64(req.body);
|
||||
format = 'base64';
|
||||
} catch (e1) {
|
||||
try {
|
||||
psbt = bitcoinjs.Psbt.fromHex(req.body);
|
||||
format = 'hex';
|
||||
} catch (e2) {
|
||||
throw new Error(`Unable to parse PSBT`);
|
||||
}
|
||||
}
|
||||
for (const [index, input] of psbt.data.inputs.entries()) {
|
||||
if (!input.nonWitnessUtxo) {
|
||||
// Buffer.from ensures it won't be modified in place by reverse()
|
||||
const txid = Buffer.from(psbt.txInputs[index].hash)
|
||||
.reverse()
|
||||
.toString('hex');
|
||||
|
||||
let transactionHex: string;
|
||||
// If missing transaction, return 404 status error
|
||||
try {
|
||||
transactionHex = await bitcoinApi.$getTransactionHex(txid);
|
||||
if (!transactionHex) {
|
||||
throw new Error('');
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`${notFoundError} #${index} @ ${txid}`);
|
||||
}
|
||||
|
||||
psbt.updateInput(index, {
|
||||
nonWitnessUtxo: Buffer.from(transactionHex, 'hex'),
|
||||
});
|
||||
if (!isModified) {
|
||||
isModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isModified) {
|
||||
res.send(format === 'hex' ? psbt.toHex() : psbt.toBase64());
|
||||
} else {
|
||||
// Not modified
|
||||
// 422 Unprocessable Entity
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
||||
res.status(422).send(`Psbt had no missing nonWitnessUtxos.`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e instanceof Error && new RegExp(notFoundError).test(e.message)) {
|
||||
res.status(404).send(e.message);
|
||||
} else {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getTransactionStatus(req: Request, res: Response) {
|
||||
try {
|
||||
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true);
|
||||
|
@ -20,6 +20,11 @@ class ElectrsApi implements AbstractBitcoinApi {
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
$getTransactionHex(txId: string): Promise<string> {
|
||||
return axios.get<string>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/hex', this.axiosConfig)
|
||||
.then((response) => response.data);
|
||||
}
|
||||
|
||||
$getBlockHeightTip(): Promise<number> {
|
||||
return axios.get<number>(config.ESPLORA.REST_API_URL + '/blocks/tip/height', this.axiosConfig)
|
||||
.then((response) => response.data);
|
||||
|
@ -84,7 +84,7 @@ class Server {
|
||||
next();
|
||||
})
|
||||
.use(express.urlencoded({ extended: true }))
|
||||
.use(express.text())
|
||||
.use(express.text({ type: ['text/plain', 'application/base64'] }))
|
||||
;
|
||||
|
||||
this.server = http.createServer(this.app);
|
||||
|
@ -1,43 +0,0 @@
|
||||
import { query } from '../../utils/axios-query';
|
||||
import priceUpdater, { PriceFeed, PriceHistory } from '../price-updater';
|
||||
|
||||
class FtxApi implements PriceFeed {
|
||||
public name: string = 'FTX';
|
||||
public currencies: string[] = ['USD', 'BRZ', 'EUR', 'JPY', 'AUD'];
|
||||
|
||||
public url: string = 'https://ftx.com/api/markets/BTC/';
|
||||
public urlHist: string = 'https://ftx.com/api/markets/BTC/{CURRENCY}/candles?resolution={GRANULARITY}';
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public async $fetchPrice(currency): Promise<number> {
|
||||
const response = await query(this.url + currency);
|
||||
return response ? parseInt(response['result']['last'], 10) : -1;
|
||||
}
|
||||
|
||||
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||
const priceHistory: PriceHistory = {};
|
||||
|
||||
for (const currency of currencies) {
|
||||
if (this.currencies.includes(currency) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const response = await query(this.urlHist.replace('{GRANULARITY}', type === 'hour' ? '3600' : '86400').replace('{CURRENCY}', currency));
|
||||
const pricesRaw = response ? response['result'] : [];
|
||||
|
||||
for (const price of pricesRaw as any[]) {
|
||||
const time = Math.round(price['time'] / 1000);
|
||||
if (priceHistory[time] === undefined) {
|
||||
priceHistory[time] = priceUpdater.getEmptyPricesObj();
|
||||
}
|
||||
priceHistory[time][currency] = price['close'];
|
||||
}
|
||||
}
|
||||
|
||||
return priceHistory;
|
||||
}
|
||||
}
|
||||
|
||||
export default FtxApi;
|
@ -1,13 +1,11 @@
|
||||
import * as fs from 'fs';
|
||||
import path from "path";
|
||||
import { Common } from '../api/common';
|
||||
import config from '../config';
|
||||
import logger from '../logger';
|
||||
import PricesRepository from '../repositories/PricesRepository';
|
||||
import BitfinexApi from './price-feeds/bitfinex-api';
|
||||
import BitflyerApi from './price-feeds/bitflyer-api';
|
||||
import CoinbaseApi from './price-feeds/coinbase-api';
|
||||
import FtxApi from './price-feeds/ftx-api';
|
||||
import GeminiApi from './price-feeds/gemini-api';
|
||||
import KrakenApi from './price-feeds/kraken-api';
|
||||
|
||||
@ -48,7 +46,6 @@ class PriceUpdater {
|
||||
this.latestPrices = this.getEmptyPricesObj();
|
||||
|
||||
this.feeds.push(new BitflyerApi()); // Does not have historical endpoint
|
||||
this.feeds.push(new FtxApi());
|
||||
this.feeds.push(new KrakenApi());
|
||||
this.feeds.push(new CoinbaseApi());
|
||||
this.feeds.push(new BitfinexApi());
|
||||
|
@ -204,7 +204,7 @@
|
||||
<tx-bowtie-graph
|
||||
[tx]="tx"
|
||||
[width]="graphWidth"
|
||||
[height]="graphExpanded ? (maxInOut * 15) : graphHeight"
|
||||
[height]="graphHeight"
|
||||
[lineLimit]="inOutLimit"
|
||||
[maxStrands]="graphExpanded ? maxInOut : 24"
|
||||
[network]="network"
|
||||
|
@ -226,6 +226,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.waitingForTransaction = false;
|
||||
this.setMempoolBlocksSubscription();
|
||||
this.websocketService.startTrackTransaction(tx.txid);
|
||||
this.graphExpanded = false;
|
||||
this.setupGraph();
|
||||
|
||||
if (!tx.status.confirmed && tx.firstSeen) {
|
||||
@ -364,7 +365,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
setupGraph() {
|
||||
this.maxInOut = Math.min(this.inOutLimit, Math.max(this.tx?.vin?.length || 1, this.tx?.vout?.length + 1 || 1));
|
||||
this.graphHeight = Math.min(360, this.maxInOut * 80);
|
||||
this.graphHeight = this.graphExpanded ? this.maxInOut * 15 : Math.min(360, this.maxInOut * 80);
|
||||
}
|
||||
|
||||
toggleGraph() {
|
||||
@ -384,10 +385,12 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
expandGraph() {
|
||||
this.graphExpanded = true;
|
||||
this.graphHeight = this.maxInOut * 15;
|
||||
}
|
||||
|
||||
collapseGraph() {
|
||||
this.graphExpanded = false;
|
||||
this.graphHeight = Math.min(360, this.maxInOut * 80);
|
||||
}
|
||||
|
||||
// simulate normal anchor fragment behavior
|
||||
|
@ -20,7 +20,7 @@
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text table-sm table-tx-vin">
|
||||
<tbody>
|
||||
<ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length > inputRowLimit) ? tx.vin.slice(0, inputRowLimit - 2) : tx.vin.slice(0, inputRowLimit)) : tx.vin" [ngForTrackBy]="trackByIndexFn">
|
||||
<ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx.vin.slice(0, getVinLimit(tx))" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr [ngClass]="{
|
||||
'assetBox': (assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded) || inputIndex === vindex,
|
||||
'highlight': vin.prevout?.scriptpubkey_address === this.address && this.address !== ''
|
||||
@ -146,9 +146,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="tx.vin.length > inputRowLimit && tx['@vinLimit']">
|
||||
<tr *ngIf="tx.vin.length > getVinLimit(tx)">
|
||||
<td colspan="3" class="text-center">
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length }})</button>
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="showMoreInputs(tx)">
|
||||
<span *ngIf="getVinLimit(tx, true) >= tx.vin.length; else showMoreInputsLabel" i18n="show-all">Show all</span>
|
||||
<ng-template #showMoreInputsLabel>
|
||||
<span i18n="show-more">Show more</span>
|
||||
</ng-template>
|
||||
({{ tx.vin.length - getVinLimit(tx) }} <span i18n="inputs-remaining">remaining</span>)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -158,7 +164,7 @@
|
||||
<div class="col mobile-bottomcol">
|
||||
<table class="table table-borderless smaller-text table-sm table-tx-vout">
|
||||
<tbody>
|
||||
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] ? ((tx.vout.length > outputRowLimit) ? tx.vout.slice(0, outputRowLimit - 2) : tx.vout.slice(0, outputRowLimit)) : tx.vout" [ngForTrackBy]="trackByIndexFn">
|
||||
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx.vout.slice(0, getVoutLimit(tx))" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr [ngClass]="{
|
||||
'assetBox': assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex,
|
||||
'highlight': vout.scriptpubkey_address === this.address && this.address !== ''
|
||||
@ -257,9 +263,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="tx.vout.length > outputRowLimit && tx['@voutLimit']">
|
||||
<tr *ngIf="tx.vout.length > getVoutLimit(tx)">
|
||||
<td colspan="3" class="text-center">
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vout.length }})</button>
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="showMoreOutputs(tx)">
|
||||
<span *ngIf="getVoutLimit(tx, true) >= tx.vout.length; else showMoreOutputsLabel" i18n="show-all">Show all</span>
|
||||
<ng-template #showMoreOutputsLabel>
|
||||
<span i18n="show-more">Show more</span>
|
||||
</ng-template>
|
||||
({{ tx.vout.length - getVoutLimit(tx) }} <span i18n="outputs-remaining">remaining</span>)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -271,7 +283,7 @@
|
||||
<div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase && tx.fee !== -1">
|
||||
{{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block"> – {{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></span>
|
||||
</div>
|
||||
<div class="float-left mt-2-5 grey-info-text" *ngIf="tx.fee === -1" i18n="transactions-list.load-to-reveal-fee-info">Show all inputs to reveal fee data</div>
|
||||
<div class="float-left mt-2-5 grey-info-text" *ngIf="tx.fee === -1" i18n="transactions-list.load-to-reveal-fee-info">Show more inputs to reveal fee data</div>
|
||||
|
||||
<div class="float-right">
|
||||
<ng-container *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
|
||||
|
@ -18,6 +18,7 @@ import { ApiService } from '../../services/api.service';
|
||||
export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
network = '';
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
showMoreIncrement = 1000;
|
||||
|
||||
@Input() transactions: Transaction[];
|
||||
@Input() showConfirmations = false;
|
||||
@ -208,14 +209,50 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
loadMoreInputs(tx: Transaction): void {
|
||||
tx['@vinLimit'] = false;
|
||||
if (!tx['@vinLoaded']) {
|
||||
this.electrsApiService.getTransaction$(tx.txid)
|
||||
.subscribe((newTx) => {
|
||||
tx['@vinLoaded'] = true;
|
||||
tx.vin = newTx.vin;
|
||||
tx.fee = newTx.fee;
|
||||
this.ref.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.electrsApiService.getTransaction$(tx.txid)
|
||||
.subscribe((newTx) => {
|
||||
tx.vin = newTx.vin;
|
||||
tx.fee = newTx.fee;
|
||||
this.ref.markForCheck();
|
||||
});
|
||||
showMoreInputs(tx: Transaction): void {
|
||||
this.loadMoreInputs(tx);
|
||||
tx['@vinLimit'] = this.getVinLimit(tx, true);
|
||||
}
|
||||
|
||||
showMoreOutputs(tx: Transaction): void {
|
||||
tx['@voutLimit'] = this.getVoutLimit(tx, true);
|
||||
}
|
||||
|
||||
getVinLimit(tx: Transaction, next = false): number {
|
||||
let limit;
|
||||
if ((tx['@vinLimit'] || 0) > this.inputRowLimit) {
|
||||
limit = Math.min(tx['@vinLimit'] + (next ? this.showMoreIncrement : 0), tx.vin.length);
|
||||
} else {
|
||||
limit = Math.min((next ? this.showMoreIncrement : this.inputRowLimit), tx.vin.length);
|
||||
}
|
||||
if (tx.vin.length - limit <= 5) {
|
||||
limit = tx.vin.length;
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
getVoutLimit(tx: Transaction, next = false): number {
|
||||
let limit;
|
||||
if ((tx['@voutLimit'] || 0) > this.outputRowLimit) {
|
||||
limit = Math.min(tx['@voutLimit'] + (next ? this.showMoreIncrement : 0), tx.vout.length);
|
||||
} else {
|
||||
limit = Math.min((next ? this.showMoreIncrement : this.outputRowLimit), tx.vout.length);
|
||||
}
|
||||
if (tx.vout.length - limit <= 5) {
|
||||
limit = tx.vout.length;
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -298,7 +298,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
||||
// required to prevent this line overlapping its neighbor
|
||||
|
||||
if (this.tooltip || !xputs[i].rest) {
|
||||
const w = (this.width - Math.max(lastWeight, line.weight) - (2 * this.connectorWidth)) / 2; // approximate horizontal width of the curved section of the line
|
||||
const w = (this.txWidth - Math.max(lastWeight, line.weight) - (this.connectorWidth * 2)) / 2; // approximate horizontal width of the curved section of the line
|
||||
const y1 = line.outerY;
|
||||
const y2 = line.innerY;
|
||||
const t = (lastWeight + line.weight) / 2; // distance between center of this line and center of previous line
|
||||
@ -356,7 +356,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
||||
|
||||
makePath(side: 'in' | 'out', outer: number, inner: number, weight: number, offset: number, pad: number): string {
|
||||
const start = (weight * 0.5) + this.connectorWidth;
|
||||
const curveStart = Math.max(start + 5, pad - offset);
|
||||
const curveStart = Math.max(start + 5, pad + this.connectorWidth - offset);
|
||||
const end = this.width / 2 - (this.midWidth * 0.9) + 1;
|
||||
const curveEnd = end - offset - 10;
|
||||
const midpoint = (curveStart + curveEnd) / 2;
|
||||
|
@ -8562,20 +8562,6 @@ export const faqData = [
|
||||
fragment: "what-is-svb",
|
||||
title: "What is sat/vB?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "basics",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "what-is-full-mempool",
|
||||
title: "What does it mean for the mempool to be \"full\"?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "basics",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "why-empty-blocks",
|
||||
title: "Why are there empty blocks?",
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
category: "help",
|
||||
@ -8657,33 +8643,68 @@ export const faqData = [
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "what-is-full-mempool",
|
||||
title: "What does it mean for the mempool to be \"full\"?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "why-empty-blocks",
|
||||
title: "Why are there empty blocks?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "why-block-timestamps-dont-always-increase",
|
||||
title: "Why don't block timestamps always increase?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "why-dont-fee-ranges-match",
|
||||
title: "Why doesn't the fee range shown for a block match the feerates of transactions within the block?",
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
category: "self-hosting",
|
||||
fragment: "self-hosting",
|
||||
title: "Self-Hosting",
|
||||
showConditions: bitcoinNetworks
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "self-hosting",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "who-runs-this-website",
|
||||
title: "Who runs this website?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
category: "self-hosting",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "host-my-own-instance-raspberry-pi",
|
||||
title: "How can I host my own instance on a Raspberry Pi?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
category: "self-hosting",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "host-my-own-instance-linux-server",
|
||||
title: "How can I host my own instance on a Linux server?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
category: "self-hosting",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "install-mempool-with-docker",
|
||||
title: "Can I install Mempool using Docker?",
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
category: "self-hosting",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "address-lookup-issues",
|
||||
title: "Why do I get an error for certain address lookups on my Mempool instance?",
|
||||
|
@ -168,14 +168,6 @@
|
||||
<p>There are feerate estimates on the top of <a [routerLink]="['/' | relativeUrl]">the main dashboard</a> you can use as a guide. See <a [routerLink]="['/docs/faq' | relativeUrl]" fragment="looking-up-fee-estimates">this FAQ</a> for more on picking the right feerate.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="what-is-full-mempool">
|
||||
<p>When a Bitcoin transaction is made, it is stored in a Bitcoin node's mempool before it is confirmed into a block. When the rate of incoming transactions exceeds the rate transactions are confirmed, the mempool grows in size.</p><p>The default maximum size of a Bitcoin node's mempool is 300MB, so when there are 300MB of transactions in the mempool, we say it's "full".</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="why-empty-blocks">
|
||||
<p>When a new block is found, mining pools send miners a block template with no transactions so they can start searching for the next block as soon as possible. They send a block template full of transactions right afterward, but a full block template is a bigger data transfer and takes slightly longer to reach miners.</p><p>In this intervening time, which is usually no more than 1-2 seconds, miners sometimes get lucky and find a new block using the empty block template.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="why-is-transaction-stuck-in-mempool">
|
||||
<p>If it's been a while and your transaction hasn't confirmed, your transaction is probably using a lower feerate relative to other transactions currently in the mempool. Depending on how you made your transaction, there may be <a [routerLink]="['/docs/faq' | relativeUrl]" fragment="how-to-get-transaction-confirmed-quickly">ways to accelerate the process</a>.</p><p>There's no need to panic—a Bitcoin transaction will always either confirm completely (or not at all) at some point. As long as you have your transaction's ID, you can always see where your funds are.</p><p style='font-weight:700'>This site only provides data about the Bitcoin network—it cannot help you get your transaction confirmed quicker.</p>
|
||||
</ng-template>
|
||||
@ -208,6 +200,24 @@
|
||||
See the <a [routerLink]="['/graphs' | relativeUrl]">graphs page</a> for aggregate trends over time: mempool size over time and incoming transaction velocity over time.
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="what-is-full-mempool">
|
||||
<p>When a Bitcoin transaction is made, it is stored in a Bitcoin node's mempool before it is confirmed into a block. When the rate of incoming transactions exceeds the rate transactions are confirmed, the mempool grows in size.</p><p>By default, Bitcoin Core allocates 300MB of memory for its mempool, so when a node's mempool grows big enough to use all 300MB of allocated memory, we say it's "full".</p><p>Once a node's mempool is using all of its allocated memory, it will start rejecting new transactions below a certain feerate threshold—so when this is the case, be extra sure to set a feerate that (at a minimum) exceeds that threshold. The current threshold feerate (and memory usage) are displayed right on Mempool's front page.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="why-empty-blocks">
|
||||
<p>When a new block is found, mining pools send miners a block template with no transactions so they can start searching for the next block as soon as possible. They send a block template full of transactions right afterward, but a full block template is a bigger data transfer and takes slightly longer to reach miners.</p><p>In this intervening time, which is usually no more than 1-2 seconds, miners sometimes get lucky and find a new block using the empty block template.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="why-block-timestamps-dont-always-increase">
|
||||
<p>Block validation rules do not strictly require that a block's timestamp be more recent than the timestamp of the block preceding it. Without a central authority, it's impossible to know what the exact correct time is. Instead, the Bitcoin protocol requires that a block's timestamp meet certain requirements. One of those requirements is that a block's timestamp cannot be older than the median timestamp of the 12 blocks that came before it. See more details <a href="https://en.bitcoin.it/wiki/Block_timestamp" target="_blank">here</a>.</p><p>As a result, timestamps are only accurate to within an hour or so, which sometimes results in blocks with timestamps that appear out of order.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="why-dont-fee-ranges-match">
|
||||
<p>Mempool aims to show you the <i>effective feerate</i> range for blocks—how much would you actually need to pay to get a transaction included in a block.</p>
|
||||
<p>A transaction's effective feerate is not always the same as the feerate explicitly set for it. For example, if you see a 1 s/vb transaction in a block with a displayed feerate range of 5 s/vb to 72 s/vb, chances are that 1 s/vb transaction had a high-feerate child transaction that boosted its effective feerate to 5 s/vb or higher (this is how CPFP fee-bumping works). In such a case, it would be misleading to use 1 s/vb as the lower bound of the block's feerate range because it actually required more than 1 s/vb to confirm that transaction in that block.</p>
|
||||
<p>For unconfirmed CPFP transactions, Mempool will show the effective feerate (along with descendent & ancestor transaction information) on the transaction page. For confirmed transactions, CPFP relationships are not stored, so this additional information is not shown.</p>
|
||||
</ng-template>
|
||||
|
||||
<ng-template type="who-runs-this-website">
|
||||
The official mempool.space website is operated by The Mempool Open Source Project. See more information on our <a [routerLink]="['/about']">About page</a>. There are also many unofficial instances of this website operated by individual members of the Bitcoin community.
|
||||
</ng-template>
|
||||
|
@ -172,6 +172,7 @@ h3 {
|
||||
border-radius: 0.25rem;
|
||||
font-family: monospace;
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header table {
|
||||
|
@ -1,12 +1,12 @@
|
||||
# redirect mempool.space/liquid to liquid.network
|
||||
location /liquid {
|
||||
location = /liquid {
|
||||
rewrite /liquid/(.*) https://liquid.network/$1;
|
||||
rewrite /liquid https://liquid.network/;
|
||||
return 308;
|
||||
}
|
||||
|
||||
# redirect mempool.space/liquidtestnet to liquid.network/testnet
|
||||
location /liquidtestnet {
|
||||
location = /liquidtestnet {
|
||||
rewrite /liquidtestnet/(.*) https://liquid.network/testnet/$1;
|
||||
rewrite /liquidtestnet/ https://liquid.network/testnet/;
|
||||
rewrite /liquidtestnet https://liquid.network/testnet;
|
||||
@ -14,7 +14,7 @@ location /liquidtestnet {
|
||||
}
|
||||
|
||||
# redirect mempool.space/bisq to bisq.markets
|
||||
location /bisq {
|
||||
location = /bisq {
|
||||
rewrite /bisq/(.*) https://bisq.markets/$1;
|
||||
rewrite /bisq https://bisq.markets/;
|
||||
return 308;
|
||||
|
Loading…
x
Reference in New Issue
Block a user