Merge branch 'master' into nymkappa/clip-label-overflow
This commit is contained in:
commit
99af881cdd
@ -43,7 +43,9 @@
|
|||||||
"TLS_ENABLED": true
|
"TLS_ENABLED": true
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:3000"
|
"REST_API_URL": "http://127.0.0.1:3000",
|
||||||
|
"UNIX_SOCKET_PATH": "/tmp/esplora-bitcoin-mainnet",
|
||||||
|
"RETRY_UNIX_SOCKET_AFTER": 30000
|
||||||
},
|
},
|
||||||
"SECOND_CORE_RPC": {
|
"SECOND_CORE_RPC": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
|
6613
backend/package-lock.json
generated
6613
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -34,35 +34,35 @@
|
|||||||
"prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\""
|
"prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.20.12",
|
"@babel/core": "^7.21.3",
|
||||||
"@mempool/electrum-client": "1.1.9",
|
"@mempool/electrum-client": "1.1.9",
|
||||||
"@types/node": "^16.18.11",
|
"@types/node": "^18.15.3",
|
||||||
"axios": "~0.27.2",
|
"axios": "~0.27.2",
|
||||||
"bitcoinjs-lib": "~6.1.0",
|
"bitcoinjs-lib": "~6.1.0",
|
||||||
"crypto-js": "~4.1.1",
|
"crypto-js": "~4.1.1",
|
||||||
"express": "~4.18.2",
|
"express": "~4.18.2",
|
||||||
"maxmind": "~4.3.8",
|
"maxmind": "~4.3.8",
|
||||||
"mysql2": "~2.3.3",
|
"mysql2": "~3.2.0",
|
||||||
"node-worker-threads-pool": "~1.5.1",
|
"node-worker-threads-pool": "~1.5.1",
|
||||||
"socks-proxy-agent": "~7.0.0",
|
"socks-proxy-agent": "~7.0.0",
|
||||||
"typescript": "~4.7.4",
|
"typescript": "~4.7.4",
|
||||||
"ws": "~8.11.0"
|
"ws": "~8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.7",
|
"@babel/core": "^7.21.3",
|
||||||
"@babel/code-frame": "^7.18.6",
|
"@babel/code-frame": "^7.18.6",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/express": "^4.17.15",
|
"@types/express": "^4.17.15",
|
||||||
"@types/jest": "^29.2.5",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/ws": "~8.5.4",
|
"@types/ws": "~8.5.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||||
"@typescript-eslint/parser": "^5.48.1",
|
"@typescript-eslint/parser": "^5.55.0",
|
||||||
"eslint": "^8.31.0",
|
"eslint": "^8.36.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.7.0",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.5.0",
|
||||||
"prettier": "^2.8.2",
|
"prettier": "^2.8.4",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.5",
|
||||||
"ts-node": "^10.9.1"
|
"ts-node": "^10.9.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
"TLS_ENABLED": true
|
"TLS_ENABLED": true
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "__ESPLORA_REST_API_URL__"
|
"REST_API_URL": "__ESPLORA_REST_API_URL__",
|
||||||
|
"UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__",
|
||||||
|
"RETRY_UNIX_SOCKET_AFTER": "__ESPLORA_RETRY_UNIX_SOCKET_AFTER__"
|
||||||
},
|
},
|
||||||
"SECOND_CORE_RPC": {
|
"SECOND_CORE_RPC": {
|
||||||
"HOST": "__SECOND_CORE_RPC_HOST__",
|
"HOST": "__SECOND_CORE_RPC_HOST__",
|
||||||
|
@ -47,7 +47,7 @@ describe('Mempool Backend Config', () => {
|
|||||||
|
|
||||||
expect(config.ELECTRUM).toStrictEqual({ HOST: '127.0.0.1', PORT: 3306, TLS_ENABLED: true });
|
expect(config.ELECTRUM).toStrictEqual({ HOST: '127.0.0.1', PORT: 3306, TLS_ENABLED: true });
|
||||||
|
|
||||||
expect(config.ESPLORA).toStrictEqual({ REST_API_URL: 'http://127.0.0.1:3000' });
|
expect(config.ESPLORA).toStrictEqual({ REST_API_URL: 'http://127.0.0.1:3000', UNIX_SOCKET_PATH: null, RETRY_UNIX_SOCKET_AFTER: 30000 });
|
||||||
|
|
||||||
expect(config.CORE_RPC).toStrictEqual({
|
expect(config.CORE_RPC).toStrictEqual({
|
||||||
HOST: '127.0.0.1',
|
HOST: '127.0.0.1',
|
||||||
|
@ -3,65 +3,102 @@ import axios, { AxiosRequestConfig } from 'axios';
|
|||||||
import http from 'http';
|
import http from 'http';
|
||||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||||
import { IEsploraApi } from './esplora-api.interface';
|
import { IEsploraApi } from './esplora-api.interface';
|
||||||
|
import logger from '../../logger';
|
||||||
|
|
||||||
const axiosConnection = axios.create({
|
const axiosConnection = axios.create({
|
||||||
httpAgent: new http.Agent({ keepAlive: true })
|
httpAgent: new http.Agent({ keepAlive: true, })
|
||||||
});
|
});
|
||||||
|
|
||||||
class ElectrsApi implements AbstractBitcoinApi {
|
class ElectrsApi implements AbstractBitcoinApi {
|
||||||
axiosConfig: AxiosRequestConfig = {
|
private axiosConfigWithUnixSocket: AxiosRequestConfig = config.ESPLORA.UNIX_SOCKET_PATH ? {
|
||||||
|
socketPath: config.ESPLORA.UNIX_SOCKET_PATH,
|
||||||
|
timeout: 10000,
|
||||||
|
} : {
|
||||||
|
timeout: 10000,
|
||||||
|
};
|
||||||
|
private axiosConfigTcpSocketOnly: AxiosRequestConfig = {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() { }
|
unixSocketRetryTimeout;
|
||||||
|
activeAxiosConfig;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.activeAxiosConfig = this.axiosConfigWithUnixSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackToTcpSocket() {
|
||||||
|
if (!this.unixSocketRetryTimeout) {
|
||||||
|
logger.err(`Unable to connect to esplora unix socket. Falling back to tcp socket. Retrying unix socket in ${config.ESPLORA.RETRY_UNIX_SOCKET_AFTER / 1000} seconds`);
|
||||||
|
// Retry the unix socket after a few seconds
|
||||||
|
this.unixSocketRetryTimeout = setTimeout(() => {
|
||||||
|
logger.info(`Retrying to use unix socket for esplora now (applied for the next query)`);
|
||||||
|
this.activeAxiosConfig = this.axiosConfigWithUnixSocket;
|
||||||
|
this.unixSocketRetryTimeout = undefined;
|
||||||
|
}, config.ESPLORA.RETRY_UNIX_SOCKET_AFTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the TCP socket (reach a different esplora instance through nginx)
|
||||||
|
this.activeAxiosConfig = this.axiosConfigTcpSocketOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryWrapper<T>(url, responseType = 'json'): Promise<T> {
|
||||||
|
return axiosConnection.get<T>(url, { ...this.activeAxiosConfig, responseType: responseType })
|
||||||
|
.then((response) => response.data)
|
||||||
|
.catch((e) => {
|
||||||
|
if (e?.code === 'ECONNREFUSED') {
|
||||||
|
this.fallbackToTcpSocket();
|
||||||
|
// Retry immediately
|
||||||
|
return axiosConnection.get<T>(url, this.activeAxiosConfig)
|
||||||
|
.then((response) => response.data)
|
||||||
|
.catch((e) => {
|
||||||
|
logger.warn(`Cannot query esplora through the unix socket nor the tcp socket. Exception ${e}`);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]> {
|
$getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]> {
|
||||||
return axiosConnection.get<IEsploraApi.Transaction['txid'][]>(config.ESPLORA.REST_API_URL + '/mempool/txids', this.axiosConfig)
|
return this.$queryWrapper<IEsploraApi.Transaction['txid'][]>(config.ESPLORA.REST_API_URL + '/mempool/txids');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getRawTransaction(txId: string): Promise<IEsploraApi.Transaction> {
|
$getRawTransaction(txId: string): Promise<IEsploraApi.Transaction> {
|
||||||
return axiosConnection.get<IEsploraApi.Transaction>(config.ESPLORA.REST_API_URL + '/tx/' + txId, this.axiosConfig)
|
return this.$queryWrapper<IEsploraApi.Transaction>(config.ESPLORA.REST_API_URL + '/tx/' + txId);
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getTransactionHex(txId: string): Promise<string> {
|
$getTransactionHex(txId: string): Promise<string> {
|
||||||
return axiosConnection.get<string>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/hex', this.axiosConfig)
|
return this.$queryWrapper<string>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/hex');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHeightTip(): Promise<number> {
|
$getBlockHeightTip(): Promise<number> {
|
||||||
return axiosConnection.get<number>(config.ESPLORA.REST_API_URL + '/blocks/tip/height', this.axiosConfig)
|
return this.$queryWrapper<number>(config.ESPLORA.REST_API_URL + '/blocks/tip/height');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHashTip(): Promise<string> {
|
$getBlockHashTip(): Promise<string> {
|
||||||
return axiosConnection.get<string>(config.ESPLORA.REST_API_URL + '/blocks/tip/hash', this.axiosConfig)
|
return this.$queryWrapper<string>(config.ESPLORA.REST_API_URL + '/blocks/tip/hash');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getTxIdsForBlock(hash: string): Promise<string[]> {
|
$getTxIdsForBlock(hash: string): Promise<string[]> {
|
||||||
return axiosConnection.get<string[]>(config.ESPLORA.REST_API_URL + '/block/' + hash + '/txids', this.axiosConfig)
|
return this.$queryWrapper<string[]>(config.ESPLORA.REST_API_URL + '/block/' + hash + '/txids');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHash(height: number): Promise<string> {
|
$getBlockHash(height: number): Promise<string> {
|
||||||
return axiosConnection.get<string>(config.ESPLORA.REST_API_URL + '/block-height/' + height, this.axiosConfig)
|
return this.$queryWrapper<string>(config.ESPLORA.REST_API_URL + '/block-height/' + height);
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHeader(hash: string): Promise<string> {
|
$getBlockHeader(hash: string): Promise<string> {
|
||||||
return axiosConnection.get<string>(config.ESPLORA.REST_API_URL + '/block/' + hash + '/header', this.axiosConfig)
|
return this.$queryWrapper<string>(config.ESPLORA.REST_API_URL + '/block/' + hash + '/header');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlock(hash: string): Promise<IEsploraApi.Block> {
|
$getBlock(hash: string): Promise<IEsploraApi.Block> {
|
||||||
return axiosConnection.get<IEsploraApi.Block>(config.ESPLORA.REST_API_URL + '/block/' + hash, this.axiosConfig)
|
return this.$queryWrapper<IEsploraApi.Block>(config.ESPLORA.REST_API_URL + '/block/' + hash);
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getRawBlock(hash: string): Promise<Buffer> {
|
$getRawBlock(hash: string): Promise<Buffer> {
|
||||||
return axiosConnection.get<string>(config.ESPLORA.REST_API_URL + '/block/' + hash + "/raw", { ...this.axiosConfig, responseType: 'arraybuffer' })
|
return this.$queryWrapper<any>(config.ESPLORA.REST_API_URL + '/block/' + hash + "/raw", 'arraybuffer')
|
||||||
.then((response) => { return Buffer.from(response.data); });
|
.then((response) => { return Buffer.from(response.data); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,13 +119,11 @@ class ElectrsApi implements AbstractBitcoinApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$getOutspend(txId: string, vout: number): Promise<IEsploraApi.Outspend> {
|
$getOutspend(txId: string, vout: number): Promise<IEsploraApi.Outspend> {
|
||||||
return axiosConnection.get<IEsploraApi.Outspend>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/outspend/' + vout, this.axiosConfig)
|
return this.$queryWrapper<IEsploraApi.Outspend>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/outspend/' + vout);
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getOutspends(txId: string): Promise<IEsploraApi.Outspend[]> {
|
$getOutspends(txId: string): Promise<IEsploraApi.Outspend[]> {
|
||||||
return axiosConnection.get<IEsploraApi.Outspend[]>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/outspends', this.axiosConfig)
|
return this.$queryWrapper<IEsploraApi.Outspend[]>(config.ESPLORA.REST_API_URL + '/tx/' + txId + '/outspends');
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]> {
|
async $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]> {
|
||||||
|
@ -410,12 +410,13 @@ class Blocks {
|
|||||||
try {
|
try {
|
||||||
// Get all indexed block hash
|
// Get all indexed block hash
|
||||||
const unindexedBlockHeights = await blocksRepository.$getCPFPUnindexedBlocks();
|
const unindexedBlockHeights = await blocksRepository.$getCPFPUnindexedBlocks();
|
||||||
logger.info(`Indexing cpfp data for ${unindexedBlockHeights.length} blocks`);
|
|
||||||
|
|
||||||
if (!unindexedBlockHeights?.length) {
|
if (!unindexedBlockHeights?.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info(`Indexing cpfp data for ${unindexedBlockHeights.length} blocks`);
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let countThisRun = 0;
|
let countThisRun = 0;
|
||||||
@ -589,11 +590,10 @@ class Blocks {
|
|||||||
if (!fastForwarded) {
|
if (!fastForwarded) {
|
||||||
const lastBlock = await blocksRepository.$getBlockByHeight(blockExtended.height - 1);
|
const lastBlock = await blocksRepository.$getBlockByHeight(blockExtended.height - 1);
|
||||||
if (lastBlock !== null && blockExtended.previousblockhash !== lastBlock.id) {
|
if (lastBlock !== null && blockExtended.previousblockhash !== lastBlock.id) {
|
||||||
logger.warn(`Chain divergence detected at block ${lastBlock.height}, re-indexing most recent data`);
|
logger.warn(`Chain divergence detected at block ${lastBlock.height}, re-indexing most recent data`, logger.tags.mining);
|
||||||
// We assume there won't be a reorg with more than 10 block depth
|
// We assume there won't be a reorg with more than 10 block depth
|
||||||
await BlocksRepository.$deleteBlocksFrom(lastBlock.height - 10);
|
await BlocksRepository.$deleteBlocksFrom(lastBlock.height - 10);
|
||||||
await HashratesRepository.$deleteLastEntries();
|
await HashratesRepository.$deleteLastEntries();
|
||||||
await BlocksSummariesRepository.$deleteBlocksFrom(lastBlock.height - 10);
|
|
||||||
await cpfpRepository.$deleteClustersFrom(lastBlock.height - 10);
|
await cpfpRepository.$deleteClustersFrom(lastBlock.height - 10);
|
||||||
for (let i = 10; i >= 0; --i) {
|
for (let i = 10; i >= 0; --i) {
|
||||||
const newBlock = await this.$indexBlock(lastBlock.height - i);
|
const newBlock = await this.$indexBlock(lastBlock.height - i);
|
||||||
@ -604,7 +604,7 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
await mining.$indexDifficultyAdjustments();
|
await mining.$indexDifficultyAdjustments();
|
||||||
await DifficultyAdjustmentsRepository.$deleteLastAdjustment();
|
await DifficultyAdjustmentsRepository.$deleteLastAdjustment();
|
||||||
logger.info(`Re-indexed 10 blocks and summaries. Also re-indexed the last difficulty adjustments. Will re-index latest hashrates in a few seconds.`);
|
logger.info(`Re-indexed 10 blocks and summaries. Also re-indexed the last difficulty adjustments. Will re-index latest hashrates in a few seconds.`, logger.tags.mining);
|
||||||
indexer.reindex();
|
indexer.reindex();
|
||||||
}
|
}
|
||||||
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
@ -616,7 +616,7 @@ class Blocks {
|
|||||||
priceId: lastestPriceId,
|
priceId: lastestPriceId,
|
||||||
}]);
|
}]);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Cannot save block price for ${blockExtended.height} because the price updater hasnt completed yet. Trying again in 10 seconds.`, logger.tags.mining);
|
logger.debug(`Cannot save block price for ${blockExtended.height} because the price updater hasnt completed yet. Trying again in 10 seconds.`, logger.tags.mining);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
indexer.runSingleTask('blocksPrices');
|
indexer.runSingleTask('blocksPrices');
|
||||||
}, 10000);
|
}, 10000);
|
||||||
@ -736,7 +736,7 @@ class Blocks {
|
|||||||
|
|
||||||
// Index the response if needed
|
// Index the response if needed
|
||||||
if (Common.blocksSummariesIndexingEnabled() === true) {
|
if (Common.blocksSummariesIndexingEnabled() === true) {
|
||||||
await BlocksSummariesRepository.$saveSummary({height: block.height, mined: summary});
|
await BlocksSummariesRepository.$saveTransactions(block.height, block.hash, summary.transactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return summary.transactions;
|
return summary.transactions;
|
||||||
@ -852,11 +852,12 @@ class Blocks {
|
|||||||
if (cleanBlock.fee_amt_percentiles === null) {
|
if (cleanBlock.fee_amt_percentiles === null) {
|
||||||
const block = await bitcoinClient.getBlock(cleanBlock.hash, 2);
|
const block = await bitcoinClient.getBlock(cleanBlock.hash, 2);
|
||||||
const summary = this.summarizeBlock(block);
|
const summary = this.summarizeBlock(block);
|
||||||
await BlocksSummariesRepository.$saveSummary({ height: block.height, mined: summary });
|
await BlocksSummariesRepository.$saveTransactions(cleanBlock.height, cleanBlock.hash, summary.transactions);
|
||||||
cleanBlock.fee_amt_percentiles = await BlocksSummariesRepository.$getFeePercentilesByBlockId(cleanBlock.hash);
|
cleanBlock.fee_amt_percentiles = await BlocksSummariesRepository.$getFeePercentilesByBlockId(cleanBlock.hash);
|
||||||
}
|
}
|
||||||
if (cleanBlock.fee_amt_percentiles !== null) {
|
if (cleanBlock.fee_amt_percentiles !== null) {
|
||||||
cleanBlock.median_fee_amt = cleanBlock.fee_amt_percentiles[3];
|
cleanBlock.median_fee_amt = cleanBlock.fee_amt_percentiles[3];
|
||||||
|
await blocksRepository.$updateFeeAmounts(cleanBlock.hash, cleanBlock.fee_amt_percentiles, cleanBlock.median_fee_amt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class DiskCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.info('Error parsing ' + fileName + '. Skipping. Reason: ' + (e instanceof Error ? e.message : e));
|
logger.err('Error parsing ' + fileName + '. Skipping. Reason: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|||||||
import logger from '../../logger';
|
import logger from '../../logger';
|
||||||
|
|
||||||
class Icons {
|
class Icons {
|
||||||
private static FILE_NAME = './icons.json';
|
private static FILE_NAME = '/elements/asset_registry_db/icons.json';
|
||||||
private iconIds: string[] = [];
|
private iconIds: string[] = [];
|
||||||
private icons: { [assetId: string]: string; } = {};
|
private icons: { [assetId: string]: string; } = {};
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ class Mining {
|
|||||||
const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));
|
const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));
|
||||||
if (elapsedSeconds > 5) {
|
if (elapsedSeconds > 5) {
|
||||||
const progress = Math.round(totalBlockChecked / blocks.length * 100);
|
const progress = Math.round(totalBlockChecked / blocks.length * 100);
|
||||||
logger.info(`Indexing difficulty adjustment at block #${block.height} | Progress: ${progress}%`, logger.tags.mining);
|
logger.debug(`Indexing difficulty adjustment at block #${block.height} | Progress: ${progress}%`, logger.tags.mining);
|
||||||
timer = new Date().getTime() / 1000;
|
timer = new Date().getTime() / 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,8 +558,10 @@ class Mining {
|
|||||||
currentBlockHeight -= 10000;
|
currentBlockHeight -= 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalIndexed) {
|
if (totalIndexed > 0) {
|
||||||
logger.info(`Indexing missing coinstatsindex data completed`, logger.tags.mining);
|
logger.info(`Indexing missing coinstatsindex data completed. Indexed ${totalIndexed}`, logger.tags.mining);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Indexing missing coinstatsindex data completed. Indexed 0.`, logger.tags.mining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
|
|||||||
|
|
||||||
if (nextTx && !nextTx?.used) {
|
if (nextTx && !nextTx?.used) {
|
||||||
// Check if the package fits into this block
|
// Check if the package fits into this block
|
||||||
if (blockWeight + nextTx.ancestorWeight < config.MEMPOOL.BLOCK_WEIGHT_UNITS) {
|
if (blocks.length >= 7 || (blockWeight + nextTx.ancestorWeight < config.MEMPOOL.BLOCK_WEIGHT_UNITS)) {
|
||||||
const ancestors: AuditTransaction[] = Array.from(nextTx.ancestorMap.values());
|
const ancestors: AuditTransaction[] = Array.from(nextTx.ancestorMap.values());
|
||||||
// sort ancestors by dependency graph (equivalent to sorting by ascending ancestor count)
|
// sort ancestors by dependency graph (equivalent to sorting by ascending ancestor count)
|
||||||
const sortedTxSet = [...ancestors.sort((a, b) => { return (a.ancestorMap.size || 0) - (b.ancestorMap.size || 0); }), nextTx];
|
const sortedTxSet = [...ancestors.sort((a, b) => { return (a.ancestorMap.size || 0) - (b.ancestorMap.size || 0); }), nextTx];
|
||||||
@ -175,34 +175,14 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
|
|||||||
overflow = [];
|
overflow = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// pack any leftover transactions into the last block
|
|
||||||
for (const tx of overflow) {
|
if (overflow.length > 0) {
|
||||||
if (!tx || tx?.used) {
|
logger.warn('GBT overflow list unexpectedly non-empty after final block constructed');
|
||||||
continue;
|
|
||||||
}
|
|
||||||
blockWeight += tx.weight;
|
|
||||||
const mempoolTx = mempool[tx.txid];
|
|
||||||
// update original copy of this tx with effective fee rate & relatives data
|
|
||||||
mempoolTx.effectiveFeePerVsize = tx.score;
|
|
||||||
if (tx.ancestorMap.size > 0) {
|
|
||||||
cpfpClusters[tx.txid] = Array.from(tx.ancestorMap?.values()).map(a => a.txid);
|
|
||||||
mempoolTx.cpfpRoot = tx.txid;
|
|
||||||
}
|
|
||||||
mempoolTx.cpfpChecked = true;
|
|
||||||
transactions.push(tx);
|
|
||||||
tx.used = true;
|
|
||||||
}
|
}
|
||||||
const blockTransactions = transactions.map(t => mempool[t.txid]);
|
// add the final unbounded block if it contains any transactions
|
||||||
restOfArray.forEach(tx => {
|
if (transactions.length > 0) {
|
||||||
blockWeight += tx.weight;
|
blocks.push(transactions.map(t => mempool[t.txid]));
|
||||||
tx.effectiveFeePerVsize = tx.feePerVsize;
|
|
||||||
tx.cpfpChecked = false;
|
|
||||||
blockTransactions.push(tx);
|
|
||||||
});
|
|
||||||
if (blockTransactions.length) {
|
|
||||||
blocks.push(blockTransactions);
|
|
||||||
}
|
}
|
||||||
transactions = [];
|
|
||||||
|
|
||||||
const end = Date.now();
|
const end = Date.now();
|
||||||
const time = end - start;
|
const time = end - start;
|
||||||
|
@ -37,6 +37,8 @@ interface IConfig {
|
|||||||
};
|
};
|
||||||
ESPLORA: {
|
ESPLORA: {
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
|
UNIX_SOCKET_PATH: string | void | null;
|
||||||
|
RETRY_UNIX_SOCKET_AFTER: number;
|
||||||
};
|
};
|
||||||
LIGHTNING: {
|
LIGHTNING: {
|
||||||
ENABLED: boolean;
|
ENABLED: boolean;
|
||||||
@ -163,6 +165,8 @@ const defaults: IConfig = {
|
|||||||
},
|
},
|
||||||
'ESPLORA': {
|
'ESPLORA': {
|
||||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||||
|
'UNIX_SOCKET_PATH': null,
|
||||||
|
'RETRY_UNIX_SOCKET_AFTER': 30000,
|
||||||
},
|
},
|
||||||
'ELECTRUM': {
|
'ELECTRUM': {
|
||||||
'HOST': '127.0.0.1',
|
'HOST': '127.0.0.1',
|
||||||
|
@ -45,7 +45,8 @@ class Server {
|
|||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
private server: http.Server | undefined;
|
private server: http.Server | undefined;
|
||||||
private app: Application;
|
private app: Application;
|
||||||
private currentBackendRetryInterval = 5;
|
private currentBackendRetryInterval = 1;
|
||||||
|
private backendRetryCount = 0;
|
||||||
|
|
||||||
private maxHeapSize: number = 0;
|
private maxHeapSize: number = 0;
|
||||||
private heapLogInterval: number = 60;
|
private heapLogInterval: number = 60;
|
||||||
@ -184,17 +185,17 @@ class Server {
|
|||||||
indexer.$run();
|
indexer.$run();
|
||||||
|
|
||||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||||
this.currentBackendRetryInterval = 5;
|
this.backendRetryCount = 0;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
let loggerMsg = `Exception in runMainUpdateLoop(). Retrying in ${this.currentBackendRetryInterval} sec.`;
|
this.backendRetryCount++;
|
||||||
|
let loggerMsg = `Exception in runMainUpdateLoop() (count: ${this.backendRetryCount}). Retrying in ${this.currentBackendRetryInterval} sec.`;
|
||||||
loggerMsg += ` Reason: ${(e instanceof Error ? e.message : e)}.`;
|
loggerMsg += ` Reason: ${(e instanceof Error ? e.message : e)}.`;
|
||||||
if (e?.stack) {
|
if (e?.stack) {
|
||||||
loggerMsg += ` Stack trace: ${e.stack}`;
|
loggerMsg += ` Stack trace: ${e.stack}`;
|
||||||
}
|
}
|
||||||
// When we get a first Exception, only `logger.debug` it and retry after 5 seconds
|
// When we get a first Exception, only `logger.debug` it and retry after 5 seconds
|
||||||
// From the second Exception, `logger.warn` the Exception and increase the retry delay
|
// From the second Exception, `logger.warn` the Exception and increase the retry delay
|
||||||
// Maximum retry delay is 60 seconds
|
if (this.backendRetryCount >= 5) {
|
||||||
if (this.currentBackendRetryInterval > 5) {
|
|
||||||
logger.warn(loggerMsg);
|
logger.warn(loggerMsg);
|
||||||
mempool.setOutOfSync();
|
mempool.setOutOfSync();
|
||||||
} else {
|
} else {
|
||||||
@ -204,8 +205,6 @@ class Server {
|
|||||||
logger.debug(`AxiosError: ${e?.message}`);
|
logger.debug(`AxiosError: ${e?.message}`);
|
||||||
}
|
}
|
||||||
setTimeout(this.runMainUpdateLoop.bind(this), 1000 * this.currentBackendRetryInterval);
|
setTimeout(this.runMainUpdateLoop.bind(this), 1000 * this.currentBackendRetryInterval);
|
||||||
this.currentBackendRetryInterval *= 2;
|
|
||||||
this.currentBackendRetryInterval = Math.min(this.currentBackendRetryInterval, 60);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +275,7 @@ class Server {
|
|||||||
|
|
||||||
if (!this.warnedHeapCritical && this.maxHeapSize > warnThreshold) {
|
if (!this.warnedHeapCritical && this.maxHeapSize > warnThreshold) {
|
||||||
this.warnedHeapCritical = true;
|
this.warnedHeapCritical = true;
|
||||||
logger.warn(`Used ${(this.maxHeapSize / stats.heap_size_limit).toFixed(2)}% of heap limit (${formatBytes(this.maxHeapSize, byteUnits, true)} / ${formatBytes(stats.heap_size_limit, byteUnits)})!`);
|
logger.warn(`Used ${(this.maxHeapSize / stats.heap_size_limit * 100).toFixed(2)}% of heap limit (${formatBytes(this.maxHeapSize, byteUnits, true)} / ${formatBytes(stats.heap_size_limit, byteUnits)})!`);
|
||||||
}
|
}
|
||||||
if (this.lastHeapLogTime === null || (now - this.lastHeapLogTime) > (this.heapLogInterval * 1000)) {
|
if (this.lastHeapLogTime === null || (now - this.lastHeapLogTime) > (this.heapLogInterval * 1000)) {
|
||||||
logger.debug(`Memory usage: ${formatBytes(this.maxHeapSize, byteUnits)} / ${formatBytes(stats.heap_size_limit, byteUnits)}`);
|
logger.debug(`Memory usage: ${formatBytes(this.maxHeapSize, byteUnits)} / ${formatBytes(stats.heap_size_limit, byteUnits)}`);
|
||||||
|
@ -13,6 +13,48 @@ import chainTips from '../api/chain-tips';
|
|||||||
import blocks from '../api/blocks';
|
import blocks from '../api/blocks';
|
||||||
import BlocksAuditsRepository from './BlocksAuditsRepository';
|
import BlocksAuditsRepository from './BlocksAuditsRepository';
|
||||||
|
|
||||||
|
interface DatabaseBlock {
|
||||||
|
id: string;
|
||||||
|
height: number;
|
||||||
|
version: number;
|
||||||
|
timestamp: number;
|
||||||
|
bits: number;
|
||||||
|
nonce: number;
|
||||||
|
difficulty: number;
|
||||||
|
merkle_root: string;
|
||||||
|
tx_count: number;
|
||||||
|
size: number;
|
||||||
|
weight: number;
|
||||||
|
previousblockhash: string;
|
||||||
|
mediantime: number;
|
||||||
|
totalFees: number;
|
||||||
|
medianFee: number;
|
||||||
|
feeRange: string;
|
||||||
|
reward: number;
|
||||||
|
poolId: number;
|
||||||
|
poolName: string;
|
||||||
|
poolSlug: string;
|
||||||
|
avgFee: number;
|
||||||
|
avgFeeRate: number;
|
||||||
|
coinbaseRaw: string;
|
||||||
|
coinbaseAddress: string;
|
||||||
|
coinbaseSignature: string;
|
||||||
|
coinbaseSignatureAscii: string;
|
||||||
|
avgTxSize: number;
|
||||||
|
totalInputs: number;
|
||||||
|
totalOutputs: number;
|
||||||
|
totalOutputAmt: number;
|
||||||
|
medianFeeAmt: number;
|
||||||
|
feePercentiles: string;
|
||||||
|
segwitTotalTxs: number;
|
||||||
|
segwitTotalSize: number;
|
||||||
|
segwitTotalWeight: number;
|
||||||
|
header: string;
|
||||||
|
utxoSetChange: number;
|
||||||
|
utxoSetSize: number;
|
||||||
|
totalInputAmt: number;
|
||||||
|
}
|
||||||
|
|
||||||
const BLOCK_DB_FIELDS = `
|
const BLOCK_DB_FIELDS = `
|
||||||
blocks.hash AS id,
|
blocks.hash AS id,
|
||||||
blocks.height,
|
blocks.height,
|
||||||
@ -52,7 +94,7 @@ const BLOCK_DB_FIELDS = `
|
|||||||
blocks.header,
|
blocks.header,
|
||||||
blocks.utxoset_change AS utxoSetChange,
|
blocks.utxoset_change AS utxoSetChange,
|
||||||
blocks.utxoset_size AS utxoSetSize,
|
blocks.utxoset_size AS utxoSetSize,
|
||||||
blocks.total_input_amt AS totalInputAmts
|
blocks.total_input_amt AS totalInputAmt
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class BlocksRepository {
|
class BlocksRepository {
|
||||||
@ -171,6 +213,32 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update missing fee amounts fields
|
||||||
|
*
|
||||||
|
* @param blockHash
|
||||||
|
* @param feeAmtPercentiles
|
||||||
|
* @param medianFeeAmt
|
||||||
|
*/
|
||||||
|
public async $updateFeeAmounts(blockHash: string, feeAmtPercentiles, medianFeeAmt) : Promise<void> {
|
||||||
|
try {
|
||||||
|
const query = `
|
||||||
|
UPDATE blocks
|
||||||
|
SET fee_percentiles = ?, median_fee_amt = ?
|
||||||
|
WHERE hash = ?
|
||||||
|
`;
|
||||||
|
const params: any[] = [
|
||||||
|
JSON.stringify(feeAmtPercentiles),
|
||||||
|
medianFeeAmt,
|
||||||
|
blockHash
|
||||||
|
];
|
||||||
|
await DB.query(query, params);
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.err(`Cannot update fee amounts for block ${blockHash}. Reason: ' + ${e instanceof Error ? e.message : e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all block height that have not been indexed between [startHeight, endHeight]
|
* Get all block height that have not been indexed between [startHeight, endHeight]
|
||||||
*/
|
*/
|
||||||
@ -432,7 +500,7 @@ class BlocksRepository {
|
|||||||
|
|
||||||
const blocks: BlockExtended[] = [];
|
const blocks: BlockExtended[] = [];
|
||||||
for (const block of rows) {
|
for (const block of rows) {
|
||||||
blocks.push(await this.formatDbBlockIntoExtendedBlock(block));
|
blocks.push(await this.formatDbBlockIntoExtendedBlock(block as DatabaseBlock));
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks;
|
return blocks;
|
||||||
@ -459,37 +527,13 @@ class BlocksRepository {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.formatDbBlockIntoExtendedBlock(rows[0]);
|
return await this.formatDbBlockIntoExtendedBlock(rows[0] as DatabaseBlock);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
|
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get one block by hash
|
|
||||||
*/
|
|
||||||
public async $getBlockByHash(hash: string): Promise<object | null> {
|
|
||||||
try {
|
|
||||||
const query = `
|
|
||||||
SELECT ${BLOCK_DB_FIELDS}
|
|
||||||
FROM blocks
|
|
||||||
JOIN pools ON blocks.pool_id = pools.id
|
|
||||||
WHERE hash = ?;
|
|
||||||
`;
|
|
||||||
const [rows]: any[] = await DB.query(query, [hash]);
|
|
||||||
|
|
||||||
if (rows.length <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.formatDbBlockIntoExtendedBlock(rows[0]);
|
|
||||||
} catch (e) {
|
|
||||||
logger.err(`Cannot get indexed block ${hash}. Reason: ` + (e instanceof Error ? e.message : e));
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return blocks difficulty
|
* Return blocks difficulty
|
||||||
*/
|
*/
|
||||||
@ -599,7 +643,6 @@ class BlocksRepository {
|
|||||||
if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
|
if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
|
||||||
logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}`);
|
logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}`);
|
||||||
await this.$deleteBlocksFrom(blocks[idx - 1].height);
|
await this.$deleteBlocksFrom(blocks[idx - 1].height);
|
||||||
await BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height);
|
|
||||||
await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
|
await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
|
||||||
await DifficultyAdjustmentsRepository.$deleteAdjustementsFromHeight(blocks[idx - 1].height);
|
await DifficultyAdjustmentsRepository.$deleteAdjustementsFromHeight(blocks[idx - 1].height);
|
||||||
return false;
|
return false;
|
||||||
@ -619,7 +662,7 @@ class BlocksRepository {
|
|||||||
* Delete blocks from the database from blockHeight
|
* Delete blocks from the database from blockHeight
|
||||||
*/
|
*/
|
||||||
public async $deleteBlocksFrom(blockHeight: number) {
|
public async $deleteBlocksFrom(blockHeight: number) {
|
||||||
logger.info(`Delete newer blocks from height ${blockHeight} from the database`);
|
logger.info(`Delete newer blocks from height ${blockHeight} from the database`, logger.tags.mining);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await DB.query(`DELETE FROM blocks where height >= ${blockHeight}`);
|
await DB.query(`DELETE FROM blocks where height >= ${blockHeight}`);
|
||||||
@ -933,7 +976,7 @@ class BlocksRepository {
|
|||||||
*
|
*
|
||||||
* @param dbBlk
|
* @param dbBlk
|
||||||
*/
|
*/
|
||||||
private async formatDbBlockIntoExtendedBlock(dbBlk: any): Promise<BlockExtended> {
|
private async formatDbBlockIntoExtendedBlock(dbBlk: DatabaseBlock): Promise<BlockExtended> {
|
||||||
const blk: Partial<BlockExtended> = {};
|
const blk: Partial<BlockExtended> = {};
|
||||||
const extras: Partial<BlockExtension> = {};
|
const extras: Partial<BlockExtension> = {};
|
||||||
|
|
||||||
@ -997,6 +1040,7 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're missing block summary related field, check if we can populate them on the fly now
|
// If we're missing block summary related field, check if we can populate them on the fly now
|
||||||
|
// This is for example triggered upon re-org
|
||||||
if (Common.blocksSummariesIndexingEnabled() &&
|
if (Common.blocksSummariesIndexingEnabled() &&
|
||||||
(extras.medianFeeAmt === null || extras.feePercentiles === null))
|
(extras.medianFeeAmt === null || extras.feePercentiles === null))
|
||||||
{
|
{
|
||||||
@ -1004,11 +1048,12 @@ class BlocksRepository {
|
|||||||
if (extras.feePercentiles === null) {
|
if (extras.feePercentiles === null) {
|
||||||
const block = await bitcoinClient.getBlock(dbBlk.id, 2);
|
const block = await bitcoinClient.getBlock(dbBlk.id, 2);
|
||||||
const summary = blocks.summarizeBlock(block);
|
const summary = blocks.summarizeBlock(block);
|
||||||
await BlocksSummariesRepository.$saveSummary({ height: block.height, mined: summary });
|
await BlocksSummariesRepository.$saveTransactions(dbBlk.height, dbBlk.id, summary.transactions);
|
||||||
extras.feePercentiles = await BlocksSummariesRepository.$getFeePercentilesByBlockId(dbBlk.id);
|
extras.feePercentiles = await BlocksSummariesRepository.$getFeePercentilesByBlockId(dbBlk.id);
|
||||||
}
|
}
|
||||||
if (extras.feePercentiles !== null) {
|
if (extras.feePercentiles !== null) {
|
||||||
extras.medianFeeAmt = extras.feePercentiles[3];
|
extras.medianFeeAmt = extras.feePercentiles[3];
|
||||||
|
await this.$updateFeeAmounts(dbBlk.id, extras.feePercentiles, extras.medianFeeAmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import DB from '../database';
|
import DB from '../database';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import { BlockSummary } from '../mempool.interfaces';
|
import { BlockSummary, TransactionStripped } from '../mempool.interfaces';
|
||||||
|
|
||||||
class BlocksSummariesRepository {
|
class BlocksSummariesRepository {
|
||||||
public async $getByBlockId(id: string): Promise<BlockSummary | undefined> {
|
public async $getByBlockId(id: string): Promise<BlockSummary | undefined> {
|
||||||
@ -17,23 +17,17 @@ class BlocksSummariesRepository {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $saveSummary(params: { height: number, mined?: BlockSummary}) {
|
public async $saveTransactions(blockHeight: number, blockId: string, transactions: TransactionStripped[]): Promise<void> {
|
||||||
const blockId = params.mined?.id;
|
|
||||||
try {
|
try {
|
||||||
const transactions = JSON.stringify(params.mined?.transactions || []);
|
const transactionsStr = JSON.stringify(transactions);
|
||||||
await DB.query(`
|
await DB.query(`
|
||||||
INSERT INTO blocks_summaries (height, id, transactions, template)
|
INSERT INTO blocks_summaries
|
||||||
VALUE (?, ?, ?, ?)
|
SET height = ?, transactions = ?, id = ?
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE transactions = ?`,
|
||||||
transactions = ?
|
[blockHeight, transactionsStr, blockId, transactionsStr]);
|
||||||
`, [params.height, blockId, transactions, '[]', transactions]);
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
logger.debug(`Cannot save block summary transactions for ${blockId}. Reason: ${e instanceof Error ? e.message : e}`);
|
||||||
logger.debug(`Cannot save block summary for ${blockId} because it has already been indexed, ignoring`);
|
throw e;
|
||||||
} else {
|
|
||||||
logger.debug(`Cannot save block summary for ${blockId}. Reason: ${e instanceof Error ? e.message : e}`);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,19 +62,6 @@ class BlocksSummariesRepository {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete blocks from the database from blockHeight
|
|
||||||
*/
|
|
||||||
public async $deleteBlocksFrom(blockHeight: number) {
|
|
||||||
logger.info(`Delete newer blocks summary from height ${blockHeight} from the database`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await DB.query(`DELETE FROM blocks_summaries where height >= ${blockHeight}`);
|
|
||||||
} catch (e) {
|
|
||||||
logger.err('Cannot delete indexed blocks summaries. Reason: ' + (e instanceof Error ? e.message : e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fee percentiles if the block has already been indexed, [] otherwise
|
* Get the fee percentiles if the block has already been indexed, [] otherwise
|
||||||
*
|
*
|
||||||
|
@ -220,7 +220,7 @@ class HashratesRepository {
|
|||||||
* Delete hashrates from the database from timestamp
|
* Delete hashrates from the database from timestamp
|
||||||
*/
|
*/
|
||||||
public async $deleteHashratesFromTimestamp(timestamp: number) {
|
public async $deleteHashratesFromTimestamp(timestamp: number) {
|
||||||
logger.info(`Delete newer hashrates from timestamp ${new Date(timestamp * 1000).toUTCString()} from the database`);
|
logger.info(`Delete newer hashrates from timestamp ${new Date(timestamp * 1000).toUTCString()} from the database`, logger.tags.mining);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
|
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
|
||||||
|
@ -160,7 +160,7 @@ class PricesRepository {
|
|||||||
|
|
||||||
// Compute fiat exchange rates
|
// Compute fiat exchange rates
|
||||||
let latestPrice = rates[0] as ApiPrice;
|
let latestPrice = rates[0] as ApiPrice;
|
||||||
if (latestPrice.USD === -1) {
|
if (!latestPrice || latestPrice.USD === -1) {
|
||||||
latestPrice = priceUpdater.getEmptyPricesObj();
|
latestPrice = priceUpdater.getEmptyPricesObj();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class ForensicsService {
|
|||||||
|
|
||||||
private async $runTasks(): Promise<void> {
|
private async $runTasks(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
logger.info(`Running forensics scans`);
|
logger.debug(`Running forensics scans`);
|
||||||
|
|
||||||
if (config.MEMPOOL.BACKEND === 'esplora') {
|
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||||
await this.$runClosedChannelsForensics(false);
|
await this.$runClosedChannelsForensics(false);
|
||||||
@ -73,7 +73,7 @@ class ForensicsService {
|
|||||||
let progress = 0;
|
let progress = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info(`Started running closed channel forensics...`);
|
logger.debug(`Started running closed channel forensics...`);
|
||||||
let channels;
|
let channels;
|
||||||
if (onlyNewChannels) {
|
if (onlyNewChannels) {
|
||||||
channels = await channelsApi.$getClosedChannelsWithoutReason();
|
channels = await channelsApi.$getClosedChannelsWithoutReason();
|
||||||
@ -156,7 +156,7 @@ class ForensicsService {
|
|||||||
this.loggerTimer = new Date().getTime() / 1000;
|
this.loggerTimer = new Date().getTime() / 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info(`Closed channels forensics scan complete.`);
|
logger.debug(`Closed channels forensics scan complete.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$runClosedChannelsForensics() error: ' + (e instanceof Error ? e.message : e));
|
logger.err('$runClosedChannelsForensics() error: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ class ForensicsService {
|
|||||||
let progress = 0;
|
let progress = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info(`Started running open channel forensics...`);
|
logger.debug(`Started running open channel forensics...`);
|
||||||
const channels = await channelsApi.$getChannelsWithoutSourceChecked();
|
const channels = await channelsApi.$getChannelsWithoutSourceChecked();
|
||||||
|
|
||||||
for (const openChannel of channels) {
|
for (const openChannel of channels) {
|
||||||
@ -266,7 +266,7 @@ class ForensicsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Open channels forensics scan complete.`);
|
logger.debug(`Open channels forensics scan complete.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$runOpenedChannelsForensics() error: ' + (e instanceof Error ? e.message : e));
|
logger.err('$runOpenedChannelsForensics() error: ' + (e instanceof Error ? e.message : e));
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -283,7 +283,7 @@ class NetworkSyncService {
|
|||||||
} else {
|
} else {
|
||||||
log += ` for the first time`;
|
log += ` for the first time`;
|
||||||
}
|
}
|
||||||
logger.info(`${log}`, logger.tags.ln);
|
logger.debug(`${log}`, logger.tags.ln);
|
||||||
|
|
||||||
const channels = await channelsApi.$getChannelsByStatus([0, 1]);
|
const channels = await channelsApi.$getChannelsByStatus([0, 1]);
|
||||||
for (const channel of channels) {
|
for (const channel of channels) {
|
||||||
|
@ -15,16 +15,20 @@ class LightningStatsImporter {
|
|||||||
topologiesFolder = config.LIGHTNING.TOPOLOGY_FOLDER;
|
topologiesFolder = config.LIGHTNING.TOPOLOGY_FOLDER;
|
||||||
|
|
||||||
async $run(): Promise<void> {
|
async $run(): Promise<void> {
|
||||||
const [channels]: any[] = await DB.query('SELECT short_id from channels;');
|
try {
|
||||||
logger.info(`Caching funding txs for currently existing channels`, logger.tags.ln);
|
const [channels]: any[] = await DB.query('SELECT short_id from channels;');
|
||||||
await fundingTxFetcher.$fetchChannelsFundingTxs(channels.map(channel => channel.short_id));
|
logger.info(`Caching funding txs for currently existing channels`, logger.tags.ln);
|
||||||
|
await fundingTxFetcher.$fetchChannelsFundingTxs(channels.map(channel => channel.short_id));
|
||||||
|
|
||||||
if (config.MEMPOOL.NETWORK !== 'mainnet' || config.DATABASE.ENABLED === false) {
|
if (config.MEMPOOL.NETWORK !== 'mainnet' || config.DATABASE.ENABLED === false) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.$importHistoricalLightningStats();
|
||||||
|
await this.$cleanupIncorrectSnapshot();
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Exception in LightningStatsImporter::$run(). ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$importHistoricalLightningStats();
|
|
||||||
await this.$cleanupIncorrectSnapshot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +62,7 @@ class PoolsUpdater {
|
|||||||
if (this.currentSha === null) {
|
if (this.currentSha === null) {
|
||||||
logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl} over ${network}`, logger.tags.mining);
|
logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl} over ${network}`, logger.tags.mining);
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`pools-v2.json is outdated, fetch latest from ${this.poolsUrl} over ${network}`, logger.tags.mining);
|
logger.warn(`pools-v2.json is outdated, fetching latest from ${this.poolsUrl} over ${network}`, logger.tags.mining);
|
||||||
}
|
}
|
||||||
const poolsJson = await this.query(this.poolsUrl);
|
const poolsJson = await this.query(this.poolsUrl);
|
||||||
if (poolsJson === undefined) {
|
if (poolsJson === undefined) {
|
||||||
|
@ -222,7 +222,7 @@ class PriceUpdater {
|
|||||||
private async $insertMissingRecentPrices(type: 'hour' | 'day'): Promise<void> {
|
private async $insertMissingRecentPrices(type: 'hour' | 'day'): Promise<void> {
|
||||||
const existingPriceTimes = await PricesRepository.$getPricesTimes();
|
const existingPriceTimes = await PricesRepository.$getPricesTimes();
|
||||||
|
|
||||||
logger.info(`Fetching ${type === 'day' ? 'dai' : 'hour'}ly price history from exchanges and saving missing ones into the database`, logger.tags.mining);
|
logger.debug(`Fetching ${type === 'day' ? 'dai' : 'hour'}ly price history from exchanges and saving missing ones into the database`, logger.tags.mining);
|
||||||
|
|
||||||
const historicalPrices: PriceHistory[] = [];
|
const historicalPrices: PriceHistory[] = [];
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"types": ["node", "jest"],
|
"types": ["node", "jest"],
|
||||||
"lib": ["es2019", "dom"],
|
"lib": ["es2019", "dom"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
|
@ -204,7 +204,9 @@ Corresponding `docker-compose.yml` overrides:
|
|||||||
`mempool-config.json`:
|
`mempool-config.json`:
|
||||||
```json
|
```json
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:3000"
|
"REST_API_URL": "http://127.0.0.1:3000",
|
||||||
|
"UNIX_SOCKET_PATH": "/tmp/esplora-socket",
|
||||||
|
"RETRY_UNIX_SOCKET_AFTER": 30000
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -213,6 +215,8 @@ Corresponding `docker-compose.yml` overrides:
|
|||||||
api:
|
api:
|
||||||
environment:
|
environment:
|
||||||
ESPLORA_REST_API_URL: ""
|
ESPLORA_REST_API_URL: ""
|
||||||
|
ESPLORA_UNIX_SOCKET_PATH: ""
|
||||||
|
ESPLORA_RETRY_UNIX_SOCKET_AFTER: ""
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -42,7 +42,9 @@
|
|||||||
"TLS_ENABLED": __ELECTRUM_TLS_ENABLED__
|
"TLS_ENABLED": __ELECTRUM_TLS_ENABLED__
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "__ESPLORA_REST_API_URL__"
|
"REST_API_URL": "__ESPLORA_REST_API_URL__",
|
||||||
|
"UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__",
|
||||||
|
"RETRY_UNIX_SOCKET_AFTER": __ESPLORA_RETRY_UNIX_SOCKET_AFTER__
|
||||||
},
|
},
|
||||||
"SECOND_CORE_RPC": {
|
"SECOND_CORE_RPC": {
|
||||||
"HOST": "__SECOND_CORE_RPC_HOST__",
|
"HOST": "__SECOND_CORE_RPC_HOST__",
|
||||||
|
@ -46,6 +46,8 @@ __ELECTRUM_TLS_ENABLED__=${ELECTRUM_TLS_ENABLED:=false}
|
|||||||
|
|
||||||
# ESPLORA
|
# ESPLORA
|
||||||
__ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000}
|
__ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000}
|
||||||
|
__ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:=null}
|
||||||
|
__ESPLORA_RETRY_UNIX_SOCKET_AFTER__=${ESPLORA_RETRY_UNIX_SOCKET_AFTER:=30000}
|
||||||
|
|
||||||
# SECOND_CORE_RPC
|
# SECOND_CORE_RPC
|
||||||
__SECOND_CORE_RPC_HOST__=${SECOND_CORE_RPC_HOST:=127.0.0.1}
|
__SECOND_CORE_RPC_HOST__=${SECOND_CORE_RPC_HOST:=127.0.0.1}
|
||||||
@ -166,6 +168,8 @@ sed -i "s/__ELECTRUM_PORT__/${__ELECTRUM_PORT__}/g" mempool-config.json
|
|||||||
sed -i "s/__ELECTRUM_TLS_ENABLED__/${__ELECTRUM_TLS_ENABLED__}/g" mempool-config.json
|
sed -i "s/__ELECTRUM_TLS_ENABLED__/${__ELECTRUM_TLS_ENABLED__}/g" mempool-config.json
|
||||||
|
|
||||||
sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json
|
sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json
|
||||||
|
sed -i "s!__ESPLORA_UNIX_SOCKET_PATH__!${__ESPLORA_UNIX_SOCKET_PATH__}!g" mempool-config.json
|
||||||
|
sed -i "s!__ESPLORA_RETRY_UNIX_SOCKET_AFTER__!${__ESPLORA_RETRY_UNIX_SOCKET_AFTER__}!g" mempool-config.json
|
||||||
|
|
||||||
sed -i "s/__SECOND_CORE_RPC_HOST__/${__SECOND_CORE_RPC_HOST__}/g" mempool-config.json
|
sed -i "s/__SECOND_CORE_RPC_HOST__/${__SECOND_CORE_RPC_HOST__}/g" mempool-config.json
|
||||||
sed -i "s/__SECOND_CORE_RPC_PORT__/${__SECOND_CORE_RPC_PORT__}/g" mempool-config.json
|
sed -i "s/__SECOND_CORE_RPC_PORT__/${__SECOND_CORE_RPC_PORT__}/g" mempool-config.json
|
||||||
|
@ -127,7 +127,7 @@ describe('Mainnet', () => {
|
|||||||
|
|
||||||
cy.get('.search-box-container > .form-control').type('S').then(() => {
|
cy.get('.search-box-container > .form-control').type('S').then(() => {
|
||||||
cy.wait('@search-1wizS');
|
cy.wait('@search-1wizS');
|
||||||
cy.get('app-search-results button.dropdown-item').should('have.length', 5);
|
cy.get('app-search-results button.dropdown-item').should('have.length', 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('.search-box-container > .form-control').type('A').then(() => {
|
cy.get('.search-box-container > .form-control').type('A').then(() => {
|
||||||
|
@ -204,9 +204,9 @@
|
|||||||
<img class="image" src="/resources/profile/raspiblitz.svg" />
|
<img class="image" src="/resources/profile/raspiblitz.svg" />
|
||||||
<span>RaspiBlitz</span>
|
<span>RaspiBlitz</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/mynodebtc/mynode" target="_blank" title="MyNode">
|
<a href="https://github.com/mynodebtc/mynode" target="_blank" title="myNode">
|
||||||
<img class="image" src="/resources/profile/mynodebtc.jpg" />
|
<img class="image" src="/resources/profile/mynodebtc.png" />
|
||||||
<span>MyNode</span>
|
<span>myNode</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/RoninDojo/RoninDojo" target="_blank" title="RoninDojo">
|
<a href="https://github.com/RoninDojo/RoninDojo" target="_blank" title="RoninDojo">
|
||||||
<img class="image" src="/resources/profile/ronindojo.png" />
|
<img class="image" src="/resources/profile/ronindojo.png" />
|
||||||
@ -253,7 +253,7 @@
|
|||||||
<span>Sparrow</span>
|
<span>Sparrow</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ACINQ/phoenix" target="_blank" title="Phoenix Wallet by ACINQ">
|
<a href="https://github.com/ACINQ/phoenix" target="_blank" title="Phoenix Wallet by ACINQ">
|
||||||
<img class="image" src="/resources/profile/phoenix.jpg" />
|
<img class="image not-rounded" src="/resources/profile/phoenix.svg" />
|
||||||
<span>Phoenix</span>
|
<span>Phoenix</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/lnbits/lnbits-legend" target="_blank" title="LNbits">
|
<a href="https://github.com/lnbits/lnbits-legend" target="_blank" title="LNbits">
|
||||||
|
@ -23,7 +23,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
@Input() unavailable: boolean = false;
|
@Input() unavailable: boolean = false;
|
||||||
@Input() auditHighlighting: boolean = false;
|
@Input() auditHighlighting: boolean = false;
|
||||||
@Input() blockConversion: Price;
|
@Input() blockConversion: Price;
|
||||||
@Output() txClickEvent = new EventEmitter<TransactionStripped>();
|
@Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>();
|
||||||
@Output() txHoverEvent = new EventEmitter<string>();
|
@Output() txHoverEvent = new EventEmitter<string>();
|
||||||
@Output() readyEvent = new EventEmitter();
|
@Output() readyEvent = new EventEmitter();
|
||||||
|
|
||||||
@ -326,7 +326,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
if (event.target === this.canvas.nativeElement && event.pointerType === 'touch') {
|
if (event.target === this.canvas.nativeElement && event.pointerType === 'touch') {
|
||||||
this.setPreviewTx(event.offsetX, event.offsetY, true);
|
this.setPreviewTx(event.offsetX, event.offsetY, true);
|
||||||
} else if (event.target === this.canvas.nativeElement) {
|
} else if (event.target === this.canvas.nativeElement) {
|
||||||
this.onTxClick(event.offsetX, event.offsetY);
|
const keyMod = event.shiftKey || event.ctrlKey || event.metaKey;
|
||||||
|
const middleClick = event.which === 2 || event.button === 1;
|
||||||
|
this.onTxClick(event.offsetX, event.offsetY, keyMod || middleClick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,12 +411,12 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTxClick(cssX: number, cssY: number) {
|
onTxClick(cssX: number, cssY: number, keyModifier: boolean = false) {
|
||||||
const x = cssX * window.devicePixelRatio;
|
const x = cssX * window.devicePixelRatio;
|
||||||
const y = cssY * window.devicePixelRatio;
|
const y = cssY * window.devicePixelRatio;
|
||||||
const selected = this.scene.getTxAt({ x, y });
|
const selected = this.scene.getTxAt({ x, y });
|
||||||
if (selected && selected.txid) {
|
if (selected && selected.txid) {
|
||||||
this.txClickEvent.emit(selected);
|
this.txClickEvent.emit({ tx: selected, keyModifier });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,9 +612,13 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onTxClick(event: TransactionStripped): void {
|
onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void {
|
||||||
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.txid}`);
|
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`);
|
||||||
this.router.navigate([url]);
|
if (!event.keyModifier) {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
} else {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTxHover(txid: string): void {
|
onTxHover(txid: string): void {
|
||||||
|
@ -90,6 +90,8 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<app-testnet-alert *ngIf="network.val === 'liquidtestnet'"></app-testnet-alert>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@ -62,6 +62,8 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<app-testnet-alert *ngIf="network.val === 'testnet' || network.val === 'signet'"></app-testnet-alert>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@ -192,4 +192,4 @@ nav {
|
|||||||
margin: 33px 0px 0px -19px;
|
margin: 33px 0px 0px -19px;
|
||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { Observable, merge, of } from 'rxjs';
|
import { Observable, merge, of } from 'rxjs';
|
||||||
import { LanguageService } from '../../services/language.service';
|
import { LanguageService } from '../../services/language.service';
|
||||||
|
@ -107,8 +107,12 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
this.isLoading$.next(false);
|
this.isLoading$.next(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onTxClick(event: TransactionStripped): void {
|
onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void {
|
||||||
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.txid}`);
|
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`);
|
||||||
this.router.navigate([url]);
|
if (!event.keyModifier) {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
} else {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr" *ngIf="(difficultyAdjustments$ | async) as da;">
|
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr" *ngIf="(difficultyAdjustments$ | async) as da;">
|
||||||
<div class="flashing">
|
<div class="flashing">
|
||||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||||
<div @blockEntryTrigger [@.disabled]="!animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
<div @blockEntryTrigger [@.disabled]="i > 0 || !animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||||
<a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
|
<a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
|
||||||
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
||||||
<div class="block-body">
|
<div class="block-body">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener } from '@angular/core';
|
||||||
import { Subscription, Observable, fromEvent, merge, of, combineLatest } from 'rxjs';
|
import { Subscription, Observable, fromEvent, merge, of, combineLatest } from 'rxjs';
|
||||||
import { MempoolBlock } from '../../interfaces/websocket.interface';
|
import { MempoolBlock } from '../../interfaces/websocket.interface';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
@ -222,8 +222,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
clearTimeout(this.resetTransitionTimeout);
|
clearTimeout(this.resetTransitionTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(): void {
|
||||||
|
this.animateEntry = false;
|
||||||
|
}
|
||||||
|
|
||||||
trackByFn(index: number, block: MempoolBlock) {
|
trackByFn(index: number, block: MempoolBlock) {
|
||||||
return (block.isStack) ? 'stack' : block.index;
|
return (block.isStack) ? `stack-${block.index}` : block.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
|
reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
|
||||||
|
@ -137,9 +137,11 @@ export class StartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMouseDown(event: MouseEvent) {
|
onMouseDown(event: MouseEvent) {
|
||||||
this.mouseDragStartX = event.clientX;
|
if (!(event.which > 1 || event.button > 0)) {
|
||||||
this.resetMomentum(event.clientX);
|
this.mouseDragStartX = event.clientX;
|
||||||
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
|
this.resetMomentum(event.clientX);
|
||||||
|
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onPointerDown(event: PointerEvent) {
|
onPointerDown(event: PointerEvent) {
|
||||||
if (this.isiOS) {
|
if (this.isiOS) {
|
||||||
|
@ -8,10 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
|
<div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
|
||||||
<app-tx-features [tx]="tx"></app-tx-features>
|
<app-tx-features [tx]="tx"></app-tx-features>
|
||||||
<span *ngIf="cpfpInfo && (cpfpInfo.bestDescendant || cpfpInfo.descendants.length)" class="badge badge-primary mr-1">
|
<span *ngIf="cpfpInfo && (cpfpInfo?.bestDescendant || cpfpInfo?.descendants?.length || cpfpInfo?.ancestors?.length)" class="badge badge-primary ml-1 mr-1">
|
||||||
CPFP
|
|
||||||
</span>
|
|
||||||
<span *ngIf="cpfpInfo && !cpfpInfo.bestDescendant && !cpfpInfo.descendants.length && cpfpInfo.ancestors.length" class="badge badge-info mr-1">
|
|
||||||
CPFP
|
CPFP
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
<div infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
|
||||||
|
|
||||||
<ng-container *ngFor="let tx of transactions; let i = index; trackBy: trackByFn">
|
<ng-container *ngFor="let tx of transactions; let i = index; trackBy: trackByFn">
|
||||||
<div *ngIf="!transactionPage" class="header-bg box tx-page-container">
|
<div *ngIf="!transactionPage" class="header-bg box tx-page-container">
|
||||||
<a class="tx-link" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
|
<a class="tx-link" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
|
||||||
@ -11,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()" [attr.data-cy]="'tx-' + i">
|
<div class="header-bg box" [attr.data-cy]="'tx-' + i">
|
||||||
|
|
||||||
<div *ngIf="errorUnblinded" class="error-unblinded">{{ errorUnblinded }}</div>
|
<div *ngIf="errorUnblinded" class="error-unblinded">{{ errorUnblinded }}</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -321,6 +323,8 @@
|
|||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #assetBox let-item>
|
<ng-template #assetBox let-item>
|
||||||
{{ item.value / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} {{ assetsMinimal[item.asset][1] }}
|
{{ item.value / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} {{ assetsMinimal[item.asset][1] }}
|
||||||
<br />
|
<br />
|
||||||
|
@ -182,14 +182,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onScroll(): void {
|
onScroll(): void {
|
||||||
const scrollHeight = document.body.scrollHeight;
|
this.loadMore.emit();
|
||||||
const scrollTop = document.documentElement.scrollTop;
|
|
||||||
if (scrollHeight > 0) {
|
|
||||||
const percentageScrolled = scrollTop * 100 / scrollHeight;
|
|
||||||
if (percentageScrolled > 50) {
|
|
||||||
this.loadMore.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
haveBlindedOutputValues(tx: Transaction): boolean {
|
haveBlindedOutputValues(tx: Transaction): boolean {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<ng-template #pegout>
|
<ng-template #pegout>
|
||||||
<ng-container *ngIf="line.pegout; else normal">
|
<ng-container *ngIf="line.pegout; else normal">
|
||||||
<p *ngIf="!isConnector">Peg Out</p>
|
<p *ngIf="!isConnector">Peg Out</p>
|
||||||
<p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
|
<p *ngIf="line.displayValue != null"><app-amount [satoshis]="line.displayValue"></app-amount></p>
|
||||||
<p class="address">
|
<p class="address">
|
||||||
<app-truncate [text]="line.pegout"></app-truncate>
|
<app-truncate [text]="line.pegout"></app-truncate>
|
||||||
</p>
|
</p>
|
||||||
@ -55,18 +55,18 @@
|
|||||||
<p *ngSwitchCase="'output'"><span i18n="transaction.input">Input</span> #{{ line.vin + 1 }}</p>
|
<p *ngSwitchCase="'output'"><span i18n="transaction.input">Input</span> #{{ line.vin + 1 }}</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p>
|
<p *ngIf="line.displayValue == null && line.confidential" i18n="shared.confidential">Confidential</p>
|
||||||
<p *ngIf="line.value != null">
|
<p *ngIf="line.displayValue != null">
|
||||||
<ng-template [ngIf]="line.asset && line.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
|
<ng-template [ngIf]="line.asset && line.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
|
||||||
<div *ngIf="assetsMinimal && assetsMinimal[line.asset] else assetNotFound">
|
<div *ngIf="assetsMinimal && assetsMinimal[line.asset] else assetNotFound">
|
||||||
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: line }"></ng-container>
|
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: line }"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #assetNotFound>
|
<ng-template #assetNotFound>
|
||||||
{{ line.value }} <span class="symbol">{{ line.asset | slice : 0 : 7 }}</span>
|
{{ line.displayValue }} <span class="symbol">{{ line.asset | slice : 0 : 7 }}</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #defaultOutput>
|
<ng-template #defaultOutput>
|
||||||
<app-amount [blockConversion]="blockConversion" [satoshis]="line.value"></app-amount>
|
<app-amount [blockConversion]="blockConversion" [satoshis]="line.displayValue"></app-amount>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</p>
|
</p>
|
||||||
<p *ngIf="line.type !== 'fee' && line.address" class="address">
|
<p *ngIf="line.type !== 'fee' && line.address" class="address">
|
||||||
@ -76,5 +76,5 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #assetBox let-item>
|
<ng-template #assetBox let-item>
|
||||||
{{ item.value / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} <span class="symbol">{{ assetsMinimal[item.asset][1] }}</span>
|
{{ item.displayValue / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} <span class="symbol">{{ assetsMinimal[item.asset][1] }}</span>
|
||||||
</ng-template>
|
</ng-template>
|
@ -7,6 +7,7 @@ import { environment } from '../../../environments/environment';
|
|||||||
interface Xput {
|
interface Xput {
|
||||||
type: 'input' | 'output' | 'fee';
|
type: 'input' | 'output' | 'fee';
|
||||||
value?: number;
|
value?: number;
|
||||||
|
displayValue?: number;
|
||||||
index?: number;
|
index?: number;
|
||||||
txid?: string;
|
txid?: string;
|
||||||
vin?: number;
|
vin?: number;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Component, OnInit, Input, OnChanges, HostListener, Inject, LOCALE_ID } from '@angular/core';
|
import { Component, OnInit, Input, OnChanges, HostListener, Inject, LOCALE_ID } from '@angular/core';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { Outspend, Transaction } from '../../interfaces/electrs.interface';
|
import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { ReplaySubject, merge, Subscription, of } from 'rxjs';
|
import { ReplaySubject, merge, Subscription, of } from 'rxjs';
|
||||||
import { tap, switchMap } from 'rxjs/operators';
|
import { tap, switchMap } from 'rxjs/operators';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { AssetsService } from '../../services/assets.service';
|
import { AssetsService } from '../../services/assets.service';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
interface SvgLine {
|
interface SvgLine {
|
||||||
path: string;
|
path: string;
|
||||||
@ -20,6 +21,7 @@ interface SvgLine {
|
|||||||
interface Xput {
|
interface Xput {
|
||||||
type: 'input' | 'output' | 'fee';
|
type: 'input' | 'output' | 'fee';
|
||||||
value?: number;
|
value?: number;
|
||||||
|
displayValue?: number;
|
||||||
index?: number;
|
index?: number;
|
||||||
txid?: string;
|
txid?: string;
|
||||||
vin?: number;
|
vin?: number;
|
||||||
@ -74,6 +76,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
zeroValueThickness = 20;
|
zeroValueThickness = 20;
|
||||||
hasLine: boolean;
|
hasLine: boolean;
|
||||||
assetsMinimal: any;
|
assetsMinimal: any;
|
||||||
|
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||||
|
|
||||||
outspendsSubscription: Subscription;
|
outspendsSubscription: Subscription;
|
||||||
refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
|
refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
|
||||||
@ -167,7 +170,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
let voutWithFee = this.tx.vout.map((v, i) => {
|
let voutWithFee = this.tx.vout.map((v, i) => {
|
||||||
return {
|
return {
|
||||||
type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output',
|
type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output',
|
||||||
value: v?.value,
|
value: this.getOutputValue(v),
|
||||||
|
displayValue: v?.value,
|
||||||
address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(),
|
address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(),
|
||||||
index: i,
|
index: i,
|
||||||
pegout: v?.pegout?.scriptpubkey_address,
|
pegout: v?.pegout?.scriptpubkey_address,
|
||||||
@ -185,7 +189,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
let truncatedInputs = this.tx.vin.map((v, i) => {
|
let truncatedInputs = this.tx.vin.map((v, i) => {
|
||||||
return {
|
return {
|
||||||
type: 'input',
|
type: 'input',
|
||||||
value: v?.prevout?.value || (v?.is_coinbase && !totalValue ? 0 : undefined),
|
value: (v?.is_coinbase && !totalValue ? 0 : this.getInputValue(v)),
|
||||||
|
displayValue: v?.prevout?.value,
|
||||||
txid: v.txid,
|
txid: v.txid,
|
||||||
vout: v.vout,
|
vout: v.vout,
|
||||||
address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(),
|
address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(),
|
||||||
@ -229,14 +234,14 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calcTotalValue(tx: Transaction): number {
|
calcTotalValue(tx: Transaction): number {
|
||||||
const totalOutput = this.tx.vout.reduce((acc, v) => (v.value == null ? 0 : v.value) + acc, 0);
|
let totalOutput = this.tx.vout.reduce((acc, v) => (this.getOutputValue(v) || 0) + acc, 0);
|
||||||
// simple sum of outputs + fee for bitcoin
|
// simple sum of outputs + fee for bitcoin
|
||||||
if (!this.isLiquid) {
|
if (!this.isLiquid) {
|
||||||
return this.tx.fee ? totalOutput + this.tx.fee : totalOutput;
|
return this.tx.fee ? totalOutput + this.tx.fee : totalOutput;
|
||||||
} else {
|
} else {
|
||||||
const totalInput = this.tx.vin.reduce((acc, v) => (v?.prevout?.value == null ? 0 : v.prevout.value) + acc, 0);
|
const totalInput = this.tx.vin.reduce((acc, v) => (this.getInputValue(v) || 0) + acc, 0);
|
||||||
const confidentialInputCount = this.tx.vin.reduce((acc, v) => acc + (v?.prevout?.value == null ? 1 : 0), 0);
|
const confidentialInputCount = this.tx.vin.reduce((acc, v) => acc + (this.isUnknownInputValue(v) ? 1 : 0), 0);
|
||||||
const confidentialOutputCount = this.tx.vout.reduce((acc, v) => acc + (v.value == null ? 1 : 0), 0);
|
const confidentialOutputCount = this.tx.vout.reduce((acc, v) => acc + (this.isUnknownOutputValue(v) ? 1 : 0), 0);
|
||||||
|
|
||||||
// if there are unknowns on both sides, the total is indeterminate, so we'll just fudge it
|
// if there are unknowns on both sides, the total is indeterminate, so we'll just fudge it
|
||||||
if (confidentialInputCount && confidentialOutputCount) {
|
if (confidentialInputCount && confidentialOutputCount) {
|
||||||
@ -456,6 +461,34 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOutputValue(v: Vout): number | void {
|
||||||
|
if (!v) {
|
||||||
|
return null;
|
||||||
|
} else if (this.isLiquid && v.asset !== this.nativeAssetId) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputValue(v: Vin): number | void {
|
||||||
|
if (!v?.prevout) {
|
||||||
|
return null;
|
||||||
|
} else if (this.isLiquid && v.prevout.asset !== this.nativeAssetId) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return v.prevout.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnknownInputValue(v: Vin): boolean {
|
||||||
|
return v?.prevout?.value == null || this.isLiquid && v?.prevout?.asset !== this.nativeAssetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnknownOutputValue(v: Vout): boolean {
|
||||||
|
return v?.value == null || this.isLiquid && v?.asset !== this.nativeAssetId;
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('pointermove', ['$event'])
|
@HostListener('pointermove', ['$event'])
|
||||||
onPointerMove(event) {
|
onPointerMove(event) {
|
||||||
if (this.dir === 'rtl') {
|
if (this.dir === 'rtl') {
|
||||||
|
@ -8863,7 +8863,7 @@ export const faqData = [
|
|||||||
type: "endpoint",
|
type: "endpoint",
|
||||||
category: "advanced",
|
category: "advanced",
|
||||||
showConditions: bitcoinNetworks,
|
showConditions: bitcoinNetworks,
|
||||||
fragment: "how-big-is-mempool-used-by-mempool.space",
|
fragment: "how-big-is-mempool-used-by-mempool-space",
|
||||||
title: "How big is the mempool used by mempool.space?",
|
title: "How big is the mempool used by mempool.space?",
|
||||||
options: { officialOnly: true },
|
options: { officialOnly: true },
|
||||||
},
|
},
|
||||||
|
@ -207,7 +207,7 @@
|
|||||||
<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>
|
<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>
|
||||||
|
|
||||||
<ng-template type="how-big-is-mempool-used-by-mempool.space">
|
<ng-template type="how-big-is-mempool-used-by-mempool-space">
|
||||||
<p>mempool.space uses multiple Bitcoin nodes to obtain data: some with the default 300MB mempool memory limit (call these Small Nodes) and others with a much larger mempool memory limit (call these Big Nodes).</p>
|
<p>mempool.space uses multiple Bitcoin nodes to obtain data: some with the default 300MB mempool memory limit (call these Small Nodes) and others with a much larger mempool memory limit (call these Big Nodes).</p>
|
||||||
<p>Many nodes on the Bitcoin network are configured to run with the default 300MB mempool memory setting. When all 300MB of memory are used up, such nodes will reject transactions below a certain threshold feerate. Running Small Nodes allows mempool.space to tell you what this threshold feerate is—this is the "Purging" feerate that shows on the front page when mempools are full, which you can use to be reasonably sure that your transaction will be widely propagated.</p>
|
<p>Many nodes on the Bitcoin network are configured to run with the default 300MB mempool memory setting. When all 300MB of memory are used up, such nodes will reject transactions below a certain threshold feerate. Running Small Nodes allows mempool.space to tell you what this threshold feerate is—this is the "Purging" feerate that shows on the front page when mempools are full, which you can use to be reasonably sure that your transaction will be widely propagated.</p>
|
||||||
<p>Big Node mempools are so big that they don't need to reject (or purge) transactions. Such nodes allow for mempool.space to provide you with information on any pending transaction it has received—no matter how congested the mempool is, and no matter how low-feerate or low-priority the transaction is.</p>
|
<p>Big Node mempools are so big that they don't need to reject (or purge) transactions. Such nodes allow for mempool.space to provide you with information on any pending transaction it has received—no matter how congested the mempool is, and no matter how low-feerate or low-priority the transaction is.</p>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<span class="green-color" *ngIf="blockConversion; else noblockconversion">
|
<span [class]="colorClass" *ngIf="blockConversion; else noblockconversion">
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
(blockConversion.price[currency] > -1 ? blockConversion.price[currency] : null) ??
|
(blockConversion.price[currency] > -1 ? blockConversion.price[currency] : null) ??
|
||||||
@ -8,7 +8,7 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ng-template #noblockconversion>
|
<ng-template #noblockconversion>
|
||||||
<span class="green-color" *ngIf="(conversions$ | async) as conversions">
|
<span [class]="colorClass" *ngIf="(conversions$ | async) as conversions">
|
||||||
{{ (conversions[currency] > -1 ? conversions[currency] : 0) * value / 100000000 | fiatCurrency : digitsInfo : currency }}
|
{{ (conversions[currency] > -1 ? conversions[currency] : 0) * value / 100000000 | fiatCurrency : digitsInfo : currency }}
|
||||||
</span>
|
</span>
|
||||||
</ng-template>
|
</ng-template>
|
@ -1,3 +0,0 @@
|
|||||||
.green-color {
|
|
||||||
color: #3bcc49;
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import { StateService } from '../services/state.service';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-fiat',
|
selector: 'app-fiat',
|
||||||
templateUrl: './fiat.component.html',
|
templateUrl: './fiat.component.html',
|
||||||
styleUrls: ['./fiat.component.scss'],
|
styleUrls: [],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FiatComponent implements OnInit, OnDestroy {
|
export class FiatComponent implements OnInit, OnDestroy {
|
||||||
@ -17,6 +17,7 @@ export class FiatComponent implements OnInit, OnDestroy {
|
|||||||
@Input() value: number;
|
@Input() value: number;
|
||||||
@Input() digitsInfo = '1.2-2';
|
@Input() digitsInfo = '1.2-2';
|
||||||
@Input() blockConversion: Price;
|
@Input() blockConversion: Price;
|
||||||
|
@Input() colorClass = 'green-color';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md table-col">
|
||||||
<a class="subtitle" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.public_key }}</a>
|
<a class="subtitle" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.public_key }}</a>
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-col {
|
||||||
|
max-width: calc(100% - 470px);
|
||||||
|
}
|
||||||
|
|
||||||
.map-col {
|
.map-col {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -12,20 +12,22 @@ export class StorageService {
|
|||||||
|
|
||||||
setDefaultValueIfNeeded(key: string, defaultValue: string) {
|
setDefaultValueIfNeeded(key: string, defaultValue: string) {
|
||||||
const graphWindowPreference: string = this.getValue(key);
|
const graphWindowPreference: string = this.getValue(key);
|
||||||
|
const fragment = window.location.hash.replace('#', '');
|
||||||
|
|
||||||
if (graphWindowPreference === null) { // First visit to mempool.space
|
if (graphWindowPreference === null) { // First visit to mempool.space
|
||||||
if (this.router.url.includes('graphs') && key === 'graphWindowPreference' ||
|
if (window.location.pathname.includes('graphs') && key === 'graphWindowPreference' ||
|
||||||
this.router.url.includes('pools') && key === 'miningWindowPreference'
|
window.location.pathname.includes('pools') && key === 'miningWindowPreference'
|
||||||
) {
|
) {
|
||||||
this.setValue(key, this.route.snapshot.fragment ? this.route.snapshot.fragment : defaultValue);
|
this.setValue(key, fragment ? fragment : defaultValue);
|
||||||
} else {
|
} else {
|
||||||
this.setValue(key, defaultValue);
|
this.setValue(key, defaultValue);
|
||||||
}
|
}
|
||||||
} else if (this.router.url.includes('graphs') && key === 'graphWindowPreference' ||
|
} else if (window.location.pathname.includes('graphs') && key === 'graphWindowPreference' ||
|
||||||
this.router.url.includes('pools') && key === 'miningWindowPreference'
|
window.location.pathname.includes('pools') && key === 'miningWindowPreference'
|
||||||
) {
|
) {
|
||||||
// Visit a different graphs#fragment from last visit
|
// Visit a different graphs#fragment from last visit
|
||||||
if (this.route.snapshot.fragment !== null && graphWindowPreference !== this.route.snapshot.fragment) {
|
if (fragment !== null && graphWindowPreference !== fragment) {
|
||||||
this.setValue(key, this.route.snapshot.fragment);
|
this.setValue(key, fragment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<div class="container p-lg-0 pb-0" style="max-width: 100%; margin-top: 7px" *ngIf="storageService.getValue('hideWarning') !== 'hidden'">
|
||||||
|
<div class="alert alert-danger mb-0 text-center">
|
||||||
|
<div class="message-container" i18n="warning-testnet">This is a test network. Coins have no value.</div>
|
||||||
|
<button type="button" class="close" (click)="dismissWarning()">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,31 @@
|
|||||||
|
.alert-danger {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #b71c1c;
|
||||||
|
border-color: #b71c1c;
|
||||||
|
padding: 0.5rem 1.25rem;
|
||||||
|
margin: 0px 10px 0px 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container {
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
display: flex;
|
||||||
|
color: #fff;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { StorageService } from '../../../services/storage.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-testnet-alert',
|
||||||
|
templateUrl: './testnet-alert.component.html',
|
||||||
|
styleUrls: ['./testnet-alert.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class TestnetAlertComponent {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public storageService: StorageService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
dismissWarning(): void {
|
||||||
|
this.storageService.setValue('hideWarning', 'hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -84,6 +84,7 @@ import { SearchResultsComponent } from '../components/search-form/search-results
|
|||||||
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
import { TimestampComponent } from './components/timestamp/timestamp.component';
|
||||||
import { ToggleComponent } from './components/toggle/toggle.component';
|
import { ToggleComponent } from './components/toggle/toggle.component';
|
||||||
import { GeolocationComponent } from '../shared/components/geolocation/geolocation.component';
|
import { GeolocationComponent } from '../shared/components/geolocation/geolocation.component';
|
||||||
|
import { TestnetAlertComponent } from './components/testnet-alert/testnet-alert.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -162,6 +163,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
|||||||
TimestampComponent,
|
TimestampComponent,
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
GeolocationComponent,
|
GeolocationComponent,
|
||||||
|
TestnetAlertComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.4 KiB |
BIN
frontend/src/resources/profile/mynodebtc.png
Normal file
BIN
frontend/src/resources/profile/mynodebtc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.3 KiB |
46
frontend/src/resources/profile/phoenix.svg
Normal file
46
frontend/src/resources/profile/phoenix.svg
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 62.449287 55.101883"
|
||||||
|
version="1.1"
|
||||||
|
id="svg395"
|
||||||
|
sodipodi:docname="phoenix.svg"
|
||||||
|
width="62.449287"
|
||||||
|
height="55.101883"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview397"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="12.259259"
|
||||||
|
inkscape:cx="29.936556"
|
||||||
|
inkscape:cy="27.978852"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2091"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="32"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg395" />
|
||||||
|
<defs
|
||||||
|
id="defs389">
|
||||||
|
<style
|
||||||
|
id="style387">.outline{fill:#50b338;}.bg{fill:#fff;}</style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
class="outline"
|
||||||
|
d="m 58.070086,17.34 a 6,6 0 0 0 0.58,-0.65 7.16,7.16 0 0 0 1,-7 c -2.14,-6.51 -10.58,-9.69 -25.75,-9.69 -11.82,0 -23,0 -28.8400005,19.87 -7.53,25.62 -6.16,34.13 0.84,35.13 4.2400005,0.61 6.6200005,-1.62 9.0000005,-4 2,-2 4,-4.46 7,-4 14,2 24.1,-0.21 29.85,-4.83 a 7.7,7.7 0 0 0 3.15,-6.17 5.52,5.52 0 0 0 -0.49,-2.38 c 4.61,-1.11 7.58,-3.4 8,-7.76 0.33,-3.86 -1.45,-6.59 -4.34,-8.52 z m -0.67,8.07 c -0.43,4.74 -11.72,4.46 -26.44,3.59 a 1,1 0 0 0 -1.06,0.91 1,1 0 0 0 0.88,1.09 l 2.43,0.3 c 5.83,0.7 16.69,1.98 16.69,4.7 a 3.44,3.44 0 0 1 -1.41,2.56 c -4,3.4 -15.54,5.16 -24.77,3.76 -4.9,-0.74 -7.55,1.45 -9.9,3.38 -2.09,1.71 -3.73,3.06 -6.6800005,2.33 a 1.59,1.59 0 0 1 -1,-0.83 c -1.74,-3.2 -0.36,-12.85 3.76,-25.9 C 14.950086,5 22.490086,5 33.900086,5 c 12.22,0 19.69,2.24 21,6.32 a 2.17,2.17 0 0 1 -0.2,2.25 c -2.47,3.16 -15.44,1.86 -19.7,1.43 a 1.0054974,1.0054974 0 0 0 -0.21,2 l 2.06,0.23 c 8.4,0.88 21.1,2.22 20.55,8.18 z"
|
||||||
|
id="path391" />
|
||||||
|
<path
|
||||||
|
class="bg"
|
||||||
|
d="m 36.900086,17.22 -2.11,-0.22 a 1.0054974,1.0054974 0 0 1 0.21,-2 c 4.26,0.43 17.23,1.73 19.75,-1.43 a 2.17,2.17 0 0 0 0.2,-2.25 c -1.36,-4.08 -8.83,-6.32 -21.05,-6.32 -11.41,0 -19,0 -24.0000005,16.3 -4.12,13.05 -5.5,22.7 -3.72,25.9 a 1.59,1.59 0 0 0 1,0.83 c 2.9500005,0.73 4.5900005,-0.62 6.6800005,-2.33 2.35,-1.93 5,-4.12 9.9,-3.38 9.23,1.4 20.81,-0.36 24.77,-3.76 a 3.44,3.44 0 0 0 1.37,-2.56 c 0,-2.72 -10.86,-4 -16.69,-4.71 l -2.43,-0.29 a 1,1 0 0 1 -0.88,-1.09 1,1 0 0 1 1.06,-0.91 c 14.72,0.87 26,1.15 26.44,-3.59 0.55,-5.96 -12.15,-7.3 -20.5,-8.19 z"
|
||||||
|
id="path393" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
@ -1,5 +1,5 @@
|
|||||||
@reboot sleep 30 ; screen -dmS mainnet /bitcoin/electrs/electrs-start-mainnet
|
@reboot screen -dmS mainnet /bitcoin/electrs/electrs-start-mainnet
|
||||||
@reboot sleep 60 ; /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
|
@reboot /usr/local/bin/bitcoind -testnet >/dev/null 2>&1
|
||||||
@reboot sleep 70 ; screen -dmS testnet /bitcoin/electrs/electrs-start-testnet
|
@reboot screen -dmS testnet /bitcoin/electrs/electrs-start-testnet
|
||||||
@reboot sleep 80 ; /usr/local/bin/bitcoind -signet >/dev/null 2>&1
|
@reboot /usr/local/bin/bitcoind -signet >/dev/null 2>&1
|
||||||
@reboot sleep 90 ; screen -dmS signet /bitcoin/electrs/electrs-start-signet
|
@reboot screen -dmS signet /bitcoin/electrs/electrs-start-signet
|
||||||
|
@ -17,7 +17,7 @@ do
|
|||||||
--db-dir __ELECTRS_DATA_ROOT__ \
|
--db-dir __ELECTRS_DATA_ROOT__ \
|
||||||
--network liquid \
|
--network liquid \
|
||||||
--daemon-dir "${HOME}" \
|
--daemon-dir "${HOME}" \
|
||||||
--http-addr '[::]:3001' \
|
--http-socket-file '/elements/socket/esplora-liquid-mainnet' \
|
||||||
--cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
|
--cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
|
||||||
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
||||||
sleep 1
|
sleep 1
|
||||||
|
@ -17,7 +17,7 @@ do
|
|||||||
--db-dir __ELECTRS_DATA_ROOT__ \
|
--db-dir __ELECTRS_DATA_ROOT__ \
|
||||||
--network liquidtestnet \
|
--network liquidtestnet \
|
||||||
--daemon-dir "${HOME}" \
|
--daemon-dir "${HOME}" \
|
||||||
--http-addr '[::]:3004' \
|
--http-socket-file '/elements/socket/esplora-liquid-testnet' \
|
||||||
--cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
|
--cookie '__ELEMENTS_RPC_USER__:__ELEMENTS_RPC_PASS__' \
|
||||||
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
||||||
sleep 1
|
sleep 1
|
||||||
|
@ -14,7 +14,7 @@ do
|
|||||||
--cors '*' \
|
--cors '*' \
|
||||||
--db-dir __ELECTRS_DATA_ROOT__ \
|
--db-dir __ELECTRS_DATA_ROOT__ \
|
||||||
--daemon-dir "${HOME}" \
|
--daemon-dir "${HOME}" \
|
||||||
--http-addr '[::]:3000' \
|
--http-socket-file '/bitcoin/socket/esplora-bitcoin-mainnet' \
|
||||||
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
||||||
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ do
|
|||||||
--db-dir __ELECTRS_DATA_ROOT__ \
|
--db-dir __ELECTRS_DATA_ROOT__ \
|
||||||
--daemon-rpc-addr '127.0.0.1:38332' \
|
--daemon-rpc-addr '127.0.0.1:38332' \
|
||||||
--daemon-dir "${HOME}" \
|
--daemon-dir "${HOME}" \
|
||||||
--http-addr '[::]:3003' \
|
--http-socket-file '/bitcoin/socket/esplora-bitcoin-signet' \
|
||||||
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
||||||
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
||||||
sleep 1
|
sleep 1
|
||||||
|
@ -15,7 +15,7 @@ do
|
|||||||
--cors '*' \
|
--cors '*' \
|
||||||
--db-dir __ELECTRS_DATA_ROOT__ \
|
--db-dir __ELECTRS_DATA_ROOT__ \
|
||||||
--daemon-dir "${HOME}" \
|
--daemon-dir "${HOME}" \
|
||||||
--http-addr '[::]:3002' \
|
--http-socket-file '/bitcoin/socket/esplora-bitcoin-testnet' \
|
||||||
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
--cookie '__BITCOIN_RPC_USER__:__BITCOIN_RPC_PASS__' \
|
||||||
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
--precache-scripts "${HOME}/electrs/contrib/popular-scripts.txt"
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# start elements on reboot
|
# start elements on reboot
|
||||||
@reboot sleep 60 ; /usr/local/bin/elementsd -chain=liquidv1 >/dev/null 2>&1
|
@reboot /usr/local/bin/elementsd -chain=liquidv1 >/dev/null 2>&1
|
||||||
@reboot sleep 60 ; /usr/local/bin/elementsd -chain=liquidtestnet >/dev/null 2>&1
|
@reboot /usr/local/bin/elementsd -chain=liquidtestnet >/dev/null 2>&1
|
||||||
|
|
||||||
# start electrs on reboot
|
# start electrs on reboot
|
||||||
@reboot sleep 90 ; screen -dmS liquidv1 /elements/electrs/electrs-start-liquid
|
@reboot screen -dmS liquidv1 /elements/electrs/electrs-start-liquid
|
||||||
@reboot sleep 90 ; screen -dmS liquidtestnet /elements/electrs/electrs-start-liquidtestnet
|
@reboot screen -dmS liquidtestnet /elements/electrs/electrs-start-liquidtestnet
|
||||||
|
|
||||||
# hourly asset update and electrs restart
|
# hourly asset update and electrs restart
|
||||||
6 * * * * cd $HOME/asset_registry_db && git pull --quiet origin master && cd $HOME/asset_registry_testnet_db && git pull --quiet origin master && killall electrs
|
6 * * * * cd $HOME/asset_registry_db && git pull --quiet origin master && cd $HOME/asset_registry_testnet_db && git pull --quiet origin master && killall electrs
|
||||||
|
@ -192,6 +192,7 @@ case $OS in
|
|||||||
TOR_USER=_tor
|
TOR_USER=_tor
|
||||||
TOR_GROUP=_tor
|
TOR_GROUP=_tor
|
||||||
NGINX_USER=www
|
NGINX_USER=www
|
||||||
|
NGINX_GROUP=www
|
||||||
NGINX_ETC_FOLDER=/usr/local/etc/nginx
|
NGINX_ETC_FOLDER=/usr/local/etc/nginx
|
||||||
NGINX_CONFIGURATION=/usr/local/etc/nginx/nginx.conf
|
NGINX_CONFIGURATION=/usr/local/etc/nginx/nginx.conf
|
||||||
CERTBOT_PKG=py39-certbot
|
CERTBOT_PKG=py39-certbot
|
||||||
@ -209,6 +210,7 @@ case $OS in
|
|||||||
TOR_GROUP=debian-tor
|
TOR_GROUP=debian-tor
|
||||||
CERTBOT_PKG=python3-certbot-nginx
|
CERTBOT_PKG=python3-certbot-nginx
|
||||||
NGINX_USER=www-data
|
NGINX_USER=www-data
|
||||||
|
NGINX_GROUP=www-data
|
||||||
NGINX_ETC_FOLDER=/etc/nginx
|
NGINX_ETC_FOLDER=/etc/nginx
|
||||||
NGINX_CONFIGURATION=/etc/nginx/nginx.conf
|
NGINX_CONFIGURATION=/etc/nginx/nginx.conf
|
||||||
;;
|
;;
|
||||||
@ -301,12 +303,6 @@ BISQ_HOME=/bisq
|
|||||||
# tor HS folder
|
# tor HS folder
|
||||||
BISQ_TOR_HS=bisq
|
BISQ_TOR_HS=bisq
|
||||||
|
|
||||||
# Unfurl user/group
|
|
||||||
UNFURL_USER=unfurl
|
|
||||||
UNFURL_GROUP=unfurl
|
|
||||||
# Unfurl home folder
|
|
||||||
UNFURL_HOME=/unfurl
|
|
||||||
|
|
||||||
# liquid user/group
|
# liquid user/group
|
||||||
ELEMENTS_USER=elements
|
ELEMENTS_USER=elements
|
||||||
ELEMENTS_GROUP=elements
|
ELEMENTS_GROUP=elements
|
||||||
@ -396,7 +392,7 @@ DEBIAN_UNFURL_PKG+=(libxdamage-dev libxrandr-dev libgbm-dev libpango1.0-dev liba
|
|||||||
# packages needed for mempool ecosystem
|
# packages needed for mempool ecosystem
|
||||||
FREEBSD_PKG=()
|
FREEBSD_PKG=()
|
||||||
FREEBSD_PKG+=(zsh sudo git git-lfs screen curl wget calc neovim)
|
FREEBSD_PKG+=(zsh sudo git git-lfs screen curl wget calc neovim)
|
||||||
FREEBSD_PKG+=(openssh-portable py39-pip rust llvm90 jq base64 libzmq4)
|
FREEBSD_PKG+=(openssh-portable py39-pip rust llvm10 jq base64 libzmq4)
|
||||||
FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc libevent libtool pkgconf)
|
FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc libevent libtool pkgconf)
|
||||||
FREEBSD_PKG+=(nginx rsync py39-certbot-nginx mariadb105-server keybase)
|
FREEBSD_PKG+=(nginx rsync py39-certbot-nginx mariadb105-server keybase)
|
||||||
FREEBSD_PKG+=(geoipupdate)
|
FREEBSD_PKG+=(geoipupdate)
|
||||||
@ -547,6 +543,12 @@ zfsCreateFilesystems()
|
|||||||
zfs create -o "mountpoint=${ELEMENTS_HOME}/liquidv1" "${ZPOOL}/elements/liquidv1"
|
zfs create -o "mountpoint=${ELEMENTS_HOME}/liquidv1" "${ZPOOL}/elements/liquidv1"
|
||||||
zfs create -o "mountpoint=${ELEMENTS_ELECTRS_HOME}" "${ZPOOL}/elements/electrs"
|
zfs create -o "mountpoint=${ELEMENTS_ELECTRS_HOME}" "${ZPOOL}/elements/electrs"
|
||||||
|
|
||||||
|
# create /bitcoin/socket with custom ACL for electrs unix sockets
|
||||||
|
zfs create -o "mountpoint=${BITCOIN_HOME}/socket" "${ZPOOL}/bitcoin/socket"
|
||||||
|
|
||||||
|
# create /elements/socket with custom ACL for electrs unix sockets
|
||||||
|
zfs create -o "mountpoint=${ELEMENTS_HOME}/socket" "${ZPOOL}/elements/socket"
|
||||||
|
|
||||||
# Bitcoin Mainnet
|
# Bitcoin Mainnet
|
||||||
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
|
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
|
||||||
for folder in chainstate indexes blocks
|
for folder in chainstate indexes blocks
|
||||||
@ -630,6 +632,7 @@ zfsCreateFilesystems()
|
|||||||
ext4CreateDir()
|
ext4CreateDir()
|
||||||
{
|
{
|
||||||
mkdir -p "/backup" "${ELEMENTS_HOME}" "${BITCOIN_HOME}" "${MINFEE_HOME}" "${ELECTRS_HOME}" "${MEMPOOL_HOME}" "${MYSQL_HOME}" "${BITCOIN_ELECTRS_HOME}" "${ELEMENTS_HOME}/liquidv1" "${ELEMENTS_ELECTRS_HOME}"
|
mkdir -p "/backup" "${ELEMENTS_HOME}" "${BITCOIN_HOME}" "${MINFEE_HOME}" "${ELECTRS_HOME}" "${MEMPOOL_HOME}" "${MYSQL_HOME}" "${BITCOIN_ELECTRS_HOME}" "${ELEMENTS_HOME}/liquidv1" "${ELEMENTS_ELECTRS_HOME}"
|
||||||
|
|
||||||
# Bitcoin Mainnet
|
# Bitcoin Mainnet
|
||||||
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
|
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
|
||||||
for folder in chainstate indexes blocks
|
for folder in chainstate indexes blocks
|
||||||
@ -1019,7 +1022,7 @@ case $OS in
|
|||||||
osSudo "${ROOT_USER}" mkdir -p /usr/local/etc/syslog.d
|
osSudo "${ROOT_USER}" mkdir -p /usr/local/etc/syslog.d
|
||||||
osSudo "${ROOT_USER}" install -c -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool-logger" /usr/local/bin/mempool-logger
|
osSudo "${ROOT_USER}" install -c -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool-logger" /usr/local/bin/mempool-logger
|
||||||
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/syslog.conf" /usr/local/etc/syslog.d/mempool.conf
|
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/syslog.conf" /usr/local/etc/syslog.d/mempool.conf
|
||||||
|
|
||||||
echo "[*] Installing newsyslog configuration"
|
echo "[*] Installing newsyslog configuration"
|
||||||
osSudo "${ROOT_USER}" mkdir -p /usr/local/etc/newsyslog.conf.d
|
osSudo "${ROOT_USER}" mkdir -p /usr/local/etc/newsyslog.conf.d
|
||||||
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/newsyslog-mempool-backend.conf" /usr/local/etc/newsyslog.conf.d/newsyslog-mempool-backend.conf
|
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/newsyslog-mempool-backend.conf" /usr/local/etc/newsyslog.conf.d/newsyslog-mempool-backend.conf
|
||||||
@ -1057,17 +1060,8 @@ if [ "${TOR_INSTALL}" = ON ];then
|
|||||||
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/torrc" "${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/torrc" "${TOR_CONFIGURATION}"
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__TOR_RESOURCES__!${TOR_RESOURCES}!" "${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" sed -i.orig "s!__TOR_RESOURCES__!${TOR_RESOURCES}!" "${TOR_CONFIGURATION}"
|
||||||
|
|
||||||
echo "[*] Adding Tor HS configuration for Mempool"
|
|
||||||
if [ "${MEMPOOL_ENABLE}" = "ON" ];then
|
|
||||||
if ! grep "${MEMPOOL_TOR_HS}" "${TOR_CONFIGURATION}" >/dev/null 2>&1;then
|
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${MEMPOOL_TOR_HS}/ >> ${TOR_CONFIGURATION}"
|
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServicePort 80 127.0.0.1:81 >> ${TOR_CONFIGURATION}"
|
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceVersion 3 >> ${TOR_CONFIGURATION}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[*] Adding Tor HS configuration for Bisq"
|
echo "[*] Adding Tor HS configuration for Bisq"
|
||||||
if [ "${BISQ_ENABLE}" = "ON" ];then
|
if [ "${BISQ_MAINNET_ENABLE}" = "ON" ];then
|
||||||
if ! grep "${BISQ_TOR_HS}" "${TOR_CONFIGURATION}" >/dev/null 2>&1;then
|
if ! grep "${BISQ_TOR_HS}" "${TOR_CONFIGURATION}" >/dev/null 2>&1;then
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${BISQ_TOR_HS}/ >> ${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${BISQ_TOR_HS}/ >> ${TOR_CONFIGURATION}"
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServicePort 80 127.0.0.1:82 >> ${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServicePort 80 127.0.0.1:82 >> ${TOR_CONFIGURATION}"
|
||||||
@ -1076,7 +1070,7 @@ if [ "${TOR_INSTALL}" = ON ];then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[*] Adding Tor HS configuration for Liquid"
|
echo "[*] Adding Tor HS configuration for Liquid"
|
||||||
if [ "${LIQUID_ENABLE}" = "ON" ];then
|
if [ "${ELEMENTS_LIQUID_ENABLE}" = "ON" ];then
|
||||||
if ! grep "${LIQUID_TOR_HS}" "${TOR_CONFIGURATION}" >/dev/null 2>&1;then
|
if ! grep "${LIQUID_TOR_HS}" "${TOR_CONFIGURATION}" >/dev/null 2>&1;then
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${LIQUID_TOR_HS}/ >> ${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${LIQUID_TOR_HS}/ >> ${TOR_CONFIGURATION}"
|
||||||
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServicePort 80 127.0.0.1:83 >> ${TOR_CONFIGURATION}"
|
osSudo "${ROOT_USER}" /bin/sh -c "echo HiddenServicePort 80 127.0.0.1:83 >> ${TOR_CONFIGURATION}"
|
||||||
@ -1273,25 +1267,25 @@ if [ "${ELEMENTS_ELECTRS_INSTALL}" = ON ];then
|
|||||||
if [ "${ELEMENTS_LIQUIDTESTNET_ENABLE}" = ON ];then
|
if [ "${ELEMENTS_LIQUIDTESTNET_ENABLE}" = ON ];then
|
||||||
osSudo "${ROOT_USER}" chown -R "${ELEMENTS_USER}:${ELEMENTS_GROUP}" "${ELECTRS_LIQUIDTESTNET_DATA}"
|
osSudo "${ROOT_USER}" chown -R "${ELEMENTS_USER}:${ELEMENTS_GROUP}" "${ELECTRS_LIQUIDTESTNET_DATA}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[*] Cloning Liquid Electrs repo from ${ELEMENTS_ELECTRS_REPO_URL}"
|
echo "[*] Cloning Liquid Electrs repo from ${ELEMENTS_ELECTRS_REPO_URL}"
|
||||||
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
||||||
osSudo "${ELEMENTS_USER}" git clone --branch "${ELEMENTS_ELECTRS_REPO_BRANCH}" "${ELEMENTS_ELECTRS_REPO_URL}" "${ELEMENTS_HOME}/${ELEMENTS_ELECTRS_REPO_NAME}"
|
osSudo "${ELEMENTS_USER}" git clone --branch "${ELEMENTS_ELECTRS_REPO_BRANCH}" "${ELEMENTS_ELECTRS_REPO_URL}" "${ELEMENTS_HOME}/${ELEMENTS_ELECTRS_REPO_NAME}"
|
||||||
|
|
||||||
echo "[*] Checking out Liquid Electrs ${ELEMENTS_ELECTRS_LATEST_RELEASE}"
|
echo "[*] Checking out Liquid Electrs ${ELEMENTS_ELECTRS_LATEST_RELEASE}"
|
||||||
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_HOME}/${ELEMENTS_ELECTRS_REPO_NAME} && git checkout ${ELEMENTS_ELECTRS_LATEST_RELEASE}"
|
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_HOME}/${ELEMENTS_ELECTRS_REPO_NAME} && git checkout ${ELEMENTS_ELECTRS_LATEST_RELEASE}"
|
||||||
|
|
||||||
echo "[*] Cloning Liquid Asset Registry repo from ${LIQUID_ASSET_REGISTRY_DB_URL}"
|
echo "[*] Cloning Liquid Asset Registry repo from ${LIQUID_ASSET_REGISTRY_DB_URL}"
|
||||||
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
||||||
osSudo "${ELEMENTS_USER}" git clone "${LIQUID_ASSET_REGISTRY_DB_URL}" "${ELEMENTS_HOME}/${LIQUID_ASSET_REGISTRY_DB_NAME}"
|
osSudo "${ELEMENTS_USER}" git clone "${LIQUID_ASSET_REGISTRY_DB_URL}" "${ELEMENTS_HOME}/${LIQUID_ASSET_REGISTRY_DB_NAME}"
|
||||||
|
|
||||||
echo "[*] Cloning Liquid Asset Registry testnet repo from ${LIQUIDTESTNET_ASSET_REGISTRY_DB_URL}"
|
echo "[*] Cloning Liquid Asset Registry testnet repo from ${LIQUIDTESTNET_ASSET_REGISTRY_DB_URL}"
|
||||||
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
osSudo "${ELEMENTS_USER}" git config --global advice.detachedHead false
|
||||||
osSudo "${ELEMENTS_USER}" git clone "${LIQUIDTESTNET_ASSET_REGISTRY_DB_URL}" "${ELEMENTS_HOME}/${LIQUIDTESTNET_ASSET_REGISTRY_DB_NAME}"
|
osSudo "${ELEMENTS_USER}" git clone "${LIQUIDTESTNET_ASSET_REGISTRY_DB_URL}" "${ELEMENTS_HOME}/${LIQUIDTESTNET_ASSET_REGISTRY_DB_NAME}"
|
||||||
|
|
||||||
echo "[*] Building Liquid Electrs release binary"
|
echo "[*] Building Liquid Electrs release binary"
|
||||||
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
||||||
|
|
||||||
case $OS in
|
case $OS in
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] Patching Liquid Electrs code for FreeBSD"
|
echo "[*] Patching Liquid Electrs code for FreeBSD"
|
||||||
@ -1300,11 +1294,11 @@ if [ "${ELEMENTS_ELECTRS_INSTALL}" = ON ];then
|
|||||||
Debian)
|
Debian)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "[*] Building Liquid Electrs release binary"
|
echo "[*] Building Liquid Electrs release binary"
|
||||||
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# Core Lightning for Bitcoin #
|
# Core Lightning for Bitcoin #
|
||||||
##############################
|
##############################
|
||||||
@ -1331,10 +1325,10 @@ case $OS in
|
|||||||
public_ipv4=$( ifconfig | grep 'inet ' | awk -F ' ' '{ print $2 }' | grep -v '^103\.165\.192\.' | grep -v '^127\.0\.0\.1' )
|
public_ipv4=$( ifconfig | grep 'inet ' | awk -F ' ' '{ print $2 }' | grep -v '^103\.165\.192\.' | grep -v '^127\.0\.0\.1' )
|
||||||
public_ipv6=$( ifconfig | grep 'inet6' | awk -F ' ' '{ print $2 }' | grep -v '^2001:df6:7280::' | grep -v '^fe80::' | grep -v '^::1' )
|
public_ipv6=$( ifconfig | grep 'inet6' | awk -F ' ' '{ print $2 }' | grep -v '^2001:df6:7280::' | grep -v '^fe80::' | grep -v '^::1' )
|
||||||
|
|
||||||
crontab_cln+="@reboot sleep 60 ; screen -dmS main lightningd --rpc-file-mode 0660 --alias `hostname` --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
|
crontab_cln+="@reboot sleep 10 ; screen -dmS main lightningd --rpc-file-mode 0660 --alias `hostname` --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
|
||||||
crontab_cln+="@reboot sleep 90 ; screen -dmS tes lightningd --rpc-file-mode 0660 --alias `hostname` --network testnet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
|
crontab_cln+="@reboot sleep 10 ; screen -dmS tes lightningd --rpc-file-mode 0660 --alias `hostname` --network testnet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6\n"
|
||||||
crontab_cln+="@reboot sleep 120 ; screen -dmS sig lightningd --rpc-file-mode 0660 --alias `hostname` --network signet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6 \n"
|
crontab_cln+="@reboot sleep 10 ; screen -dmS sig lightningd --rpc-file-mode 0660 --alias `hostname` --network signet --disable-ip-discovery --autolisten false --bind-addr $public_ipv4 --announce-addr $public_ipv4 --bind-addr $public_ipv6 --announce-addr $public_ipv6 \n"
|
||||||
crontab_cln+="@reboot sleep 180 ; /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
|
crontab_cln+="@reboot sleep 20 ; /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
|
||||||
crontab_cln+="1 * * * * /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
|
crontab_cln+="1 * * * * /mempool/mempool.space/lightning-seeder >/dev/null 2>&1\n"
|
||||||
echo "${crontab_cln}" | crontab -u "${CLN_USER}" -
|
echo "${crontab_cln}" | crontab -u "${CLN_USER}" -
|
||||||
;;
|
;;
|
||||||
@ -1430,16 +1424,6 @@ fi
|
|||||||
|
|
||||||
if [ "${UNFURL_INSTALL}" = ON ];then
|
if [ "${UNFURL_INSTALL}" = ON ];then
|
||||||
|
|
||||||
echo "[*] Creating Unfurl user"
|
|
||||||
osGroupCreate "${UNFURL_GROUP}"
|
|
||||||
osUserCreate "${UNFURL_USER}" "${UNFURL_HOME}" "${UNFURL_GROUP}"
|
|
||||||
osSudo "${ROOT_USER}" chsh -s `which zsh` "${UNFURL_USER}"
|
|
||||||
|
|
||||||
echo "[*] Creating Unfurl folder"
|
|
||||||
osSudo "${ROOT_USER}" mkdir -p "${UNFURL_HOME}"
|
|
||||||
osSudo "${ROOT_USER}" chown -R "${UNFURL_USER}:${UNFURL_GROUP}" "${UNFURL_HOME}"
|
|
||||||
osSudo "${UNFURL_USER}" touch "${UNFURL_HOME}/.zshrc"
|
|
||||||
|
|
||||||
echo "[*] Insalling Unfurl source"
|
echo "[*] Insalling Unfurl source"
|
||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
@ -1530,7 +1514,6 @@ if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
|
|||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] FIXME: Bitcoin Minfee service must be installed manually on FreeBSD"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1548,7 +1531,6 @@ if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
|
|||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] FIXME: Bitcoin Testnet service must be installed manually on FreeBSD"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1566,7 +1548,6 @@ if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then
|
|||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] FIXME: Bitcoin Signet service must be installed manually on FreeBSD"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1584,7 +1565,6 @@ if [ "${ELEMENTS_LIQUID_ENABLE}" = ON ];then
|
|||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] FIXME: Bitcoin Liquid service must be installed manually on FreeBSD"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1602,7 +1582,6 @@ if [ "${ELEMENTS_LIQUID_ENABLE}" = ON ];then
|
|||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
echo "[*] FIXME: Bitcoin Liquid service must be installed manually on FreeBSD"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1841,6 +1820,9 @@ case $OS in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# wait for mysql to start
|
||||||
|
sleep 5
|
||||||
|
|
||||||
mysql << _EOF_
|
mysql << _EOF_
|
||||||
create database mempool;
|
create database mempool;
|
||||||
grant all on mempool.* to '${MEMPOOL_MAINNET_USER}'@'localhost' identified by '${MEMPOOL_MAINNET_PASS}';
|
grant all on mempool.* to '${MEMPOOL_MAINNET_USER}'@'localhost' identified by '${MEMPOOL_MAINNET_PASS}';
|
||||||
@ -1895,39 +1877,60 @@ chown "${MEMPOOL_USER}:${MEMPOOL_GROUP}" "${MEMPOOL_MYSQL_CREDENTIALS}"
|
|||||||
|
|
||||||
##### nginx
|
##### nginx
|
||||||
|
|
||||||
|
echo "[*] Adding Nginx configuration"
|
||||||
|
osSudo "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/nginx/nginx.conf" "${NGINX_CONFIGURATION}"
|
||||||
|
mkdir -p /var/cache/nginx/services /var/cache/nginx/api
|
||||||
|
chown "${NGINX_USER}:${NGINX_GROUP}" /var/cache/nginx/services /var/cache/nginx/api
|
||||||
|
ln -s "${MEMPOOL_HOME}/mempool" "${NGINX_ETC_FOLDER}/mempool"
|
||||||
|
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_USER__!${NGINX_USER}!" "${NGINX_CONFIGURATION}"
|
||||||
|
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_ETC_FOLDER__!${NGINX_ETC_FOLDER}!" "${NGINX_CONFIGURATION}"
|
||||||
|
|
||||||
|
if [ "${TOR_INSTALL}" = ON ];then
|
||||||
|
echo "[*] Read tor v3 onion hostnames"
|
||||||
|
|
||||||
|
NGINX_MEMPOOL_ONION=$(cat "${TOR_RESOURCES}/mempool/hostname")
|
||||||
|
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_MEMPOOL_ONION__!${NGINX_MEMPOOL_ONION%.onion}!" "${NGINX_CONFIGURATION}"
|
||||||
|
|
||||||
|
if [ "${ELEMENTS_LIQUID_ENABLE}" = "ON" ];then
|
||||||
|
NGINX_LIQUID_ONION=$(cat "${TOR_RESOURCES}/liquid/hostname")
|
||||||
|
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_LIQUID_ONION__!${NGINX_LIQUID_ONIONi%.onion}!" "${NGINX_CONFIGURATION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${BISQ_MAINNET_ENABLE}" = "ON" ];then
|
||||||
|
NGINX_BISQ_ONION=$(cat "${TOR_RESOURCES}/bisq/hostname")
|
||||||
|
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_BISQ_ONION__!${NGINX_BISQ_ONION%.onion}!" "${NGINX_CONFIGURATION}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
##### OS systemd
|
||||||
|
|
||||||
|
echo "[*] Setting permissions for electrs sockets"
|
||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
|
setfacl -m "user:bitcoin:full_set:f:allow,user:mempool:full_set:f:allow,user:www:full_set:f:allow,everyone@::f:allow" "${BITCOIN_HOME}/socket"
|
||||||
|
chown "${BITCOIN_USER}:${BITCOIN_GROUP}" "${BITCOIN_HOME}/socket"
|
||||||
|
setfacl -m "user:elements:full_set:f:allow,user:mempool:full_set:f:allow,user:www:full_set:f:allow,everyone@::f:allow" "${ELEMENTS_HOME}/socket"
|
||||||
|
chown "${ELEMENTS_USER}:${ELEMENTS_GROUP}" "${ELEMENTS_HOME}/socket"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
echo "[*] Adding Nginx configuration"
|
|
||||||
osSudo "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/nginx/nginx.conf" "${NGINX_CONFIGURATION}"
|
|
||||||
mkdir -p /var/cache/nginx/services /var/cache/nginx/api
|
|
||||||
chown ${NGINX_USER}: /var/cache/nginx/services /var/cache/nginx/api
|
|
||||||
ln -s /mempool/mempool /etc/nginx/mempool
|
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_USER__!${NGINX_USER}!" "${NGINX_CONFIGURATION}"
|
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_ETC_FOLDER__!${NGINX_ETC_FOLDER}!" "${NGINX_CONFIGURATION}"
|
|
||||||
if [ "${TOR_INSTALL}" = ON ];then
|
|
||||||
echo "[*] Read tor v3 onion hostnames"
|
|
||||||
NGINX_MEMPOOL_ONION=$(cat "${TOR_RESOURCES}/mempool/hostname")
|
|
||||||
NGINX_BISQ_ONION=$(cat "${TOR_RESOURCES}/bisq/hostname")
|
|
||||||
NGINX_LIQUID_ONION=$(cat "${TOR_RESOURCES}/liquid/hostname")
|
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_MEMPOOL_ONION__!${NGINX_MEMPOOL_ONION%.onion}!" "${NGINX_CONFIGURATION}"
|
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_BISQ_ONION__!${NGINX_BISQ_ONION%.onion}!" "${NGINX_CONFIGURATION}"
|
|
||||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_LIQUID_ONION__!${NGINX_LIQUID_ONIONi%.onion}!" "${NGINX_CONFIGURATION}"
|
|
||||||
fi
|
|
||||||
echo "[*] Restarting Nginx"
|
|
||||||
osSudo "${ROOT_USER}" service nginx restart
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
##### OS systemd
|
##### OS systemd
|
||||||
|
|
||||||
echo "[*] Updating systemd daemon configuration"
|
echo "[*] Updating system startup configuration"
|
||||||
case $OS in
|
case $OS in
|
||||||
|
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
|
echo 'nginx_enable="YES"' >> /etc/rc.conf
|
||||||
|
echo 'bitcoin_enable="YES"' >> /etc/rc.conf
|
||||||
|
echo 'tor_enable="YES"' >> /etc/rc.conf
|
||||||
|
echo 'postfix_enable="YES"' >> /etc/rc.conf
|
||||||
|
echo 'mysql_enable="YES"' >> /etc/rc.conf
|
||||||
|
echo 'mysql_dbdir="/mysql"' >> /etc/rc.conf
|
||||||
|
echo 'tor_enable="YES"' >> /etc/rc.conf
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -1959,6 +1962,9 @@ case $OS in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
echo "[*] Restarting Nginx"
|
||||||
|
osSudo "${ROOT_USER}" service nginx restart
|
||||||
|
|
||||||
##### OS set Linux user ulimits
|
##### OS set Linux user ulimits
|
||||||
|
|
||||||
echo "[*] Setting ulimits for users"
|
echo "[*] Setting ulimits for users"
|
||||||
@ -2060,20 +2066,12 @@ osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME} && ./upgrade" || true
|
|||||||
|
|
||||||
##### finish
|
##### finish
|
||||||
|
|
||||||
case $OS in
|
if [ "${TOR_INSTALL}" = ON ];then
|
||||||
|
echo "Your auto-generated Tor addresses are:"
|
||||||
FreeBSD)
|
echo "${NGINX_MEMPOOL_ONION}"
|
||||||
;;
|
echo "${NGINX_BISQ_ONION}"
|
||||||
|
echo "${NGINX_LIQUID_ONION}"
|
||||||
Debian)
|
fi
|
||||||
if [ "${TOR_INSTALL}" = ON ];then
|
|
||||||
echo "This are the generated Tor addresses:"
|
|
||||||
echo "${NGINX_MEMPOOL_ONION}"
|
|
||||||
echo "${NGINX_BISQ_ONION}"
|
|
||||||
echo "${NGINX_LIQUID_ONION}"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo 'Please reboot to start all the services.'
|
echo 'Please reboot to start all the services.'
|
||||||
|
@ -15,12 +15,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4000"
|
"REST_API_URL": "http://127.0.0.1:5000",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": false,
|
"ENABLED": false,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_BISQ_USER__",
|
"USERNAME": "__MEMPOOL_BISQ_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_BISQ_PASS__",
|
"PASSWORD": "__MEMPOOL_BISQ_PASS__",
|
||||||
"DATABASE": "mempool_bisq"
|
"DATABASE": "mempool_bisq"
|
||||||
|
@ -23,12 +23,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4001"
|
"REST_API_URL": "http://127.0.0.1:5001",
|
||||||
|
"UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-mainnet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_LIQUID_USER__",
|
"USERNAME": "__MEMPOOL_LIQUID_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_LIQUID_PASS__",
|
"PASSWORD": "__MEMPOOL_LIQUID_PASS__",
|
||||||
"DATABASE": "mempool_liquid"
|
"DATABASE": "mempool_liquid"
|
||||||
|
@ -23,12 +23,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4004"
|
"REST_API_URL": "http://127.0.0.1:5004",
|
||||||
|
"UNIX_SOCKET_PATH": "/elements/socket/esplora-liquid-testnet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_LIQUIDTESTNET_USER__",
|
"USERNAME": "__MEMPOOL_LIQUIDTESTNET_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_LIQUIDTESTNET_PASS__",
|
"PASSWORD": "__MEMPOOL_LIQUIDTESTNET_PASS__",
|
||||||
"DATABASE": "mempool_liquidtestnet"
|
"DATABASE": "mempool_liquidtestnet"
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4000"
|
"REST_API_URL": "http://127.0.0.1:5000",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet"
|
||||||
},
|
},
|
||||||
"LIGHTNING": {
|
"LIGHTNING": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
@ -41,7 +42,7 @@
|
|||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_MAINNET_LIGHTNING_USER__",
|
"USERNAME": "__MEMPOOL_MAINNET_LIGHTNING_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_MAINNET_LIGHTNING_PASS__",
|
"PASSWORD": "__MEMPOOL_MAINNET_LIGHTNING_PASS__",
|
||||||
"DATABASE": "mempool_mainnet_lightning"
|
"DATABASE": "mempool_mainnet_lightning"
|
||||||
|
@ -31,12 +31,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4000"
|
"REST_API_URL": "http://127.0.0.1:5000",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_MAINNET_USER__",
|
"USERNAME": "__MEMPOOL_MAINNET_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_MAINNET_PASS__",
|
"PASSWORD": "__MEMPOOL_MAINNET_PASS__",
|
||||||
"DATABASE": "mempool"
|
"DATABASE": "mempool"
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4003"
|
"REST_API_URL": "http://127.0.0.1:5003",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet"
|
||||||
},
|
},
|
||||||
"LIGHTNING": {
|
"LIGHTNING": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
@ -36,7 +37,7 @@
|
|||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_SIGNET_LIGHTNING_USER__",
|
"USERNAME": "__MEMPOOL_SIGNET_LIGHTNING_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_SIGNET_LIGHTNING_PASS__",
|
"PASSWORD": "__MEMPOOL_SIGNET_LIGHTNING_PASS__",
|
||||||
"DATABASE": "mempool_signet_lightning"
|
"DATABASE": "mempool_signet_lightning"
|
||||||
|
@ -22,12 +22,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4003"
|
"REST_API_URL": "http://127.0.0.1:5003",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_SIGNET_USER__",
|
"USERNAME": "__MEMPOOL_SIGNET_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_SIGNET_PASS__",
|
"PASSWORD": "__MEMPOOL_SIGNET_PASS__",
|
||||||
"DATABASE": "mempool_signet"
|
"DATABASE": "mempool_signet"
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4002"
|
"REST_API_URL": "http://127.0.0.1:5002",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet"
|
||||||
},
|
},
|
||||||
"LIGHTNING": {
|
"LIGHTNING": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
@ -36,7 +37,7 @@
|
|||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_TESTNET_LIGHTNING_USER__",
|
"USERNAME": "__MEMPOOL_TESTNET_LIGHTNING_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_TESTNET_LIGHTNING_PASS__",
|
"PASSWORD": "__MEMPOOL_TESTNET_LIGHTNING_PASS__",
|
||||||
"DATABASE": "mempool_testnet_lightning"
|
"DATABASE": "mempool_testnet_lightning"
|
||||||
|
@ -22,12 +22,13 @@
|
|||||||
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
"PASSWORD": "__BITCOIN_RPC_PASS__"
|
||||||
},
|
},
|
||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:4002"
|
"REST_API_URL": "http://127.0.0.1:5002",
|
||||||
|
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet"
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"SOCKET": "/var/run/mysql/mysql.sock",
|
||||||
"USERNAME": "__MEMPOOL_TESTNET_USER__",
|
"USERNAME": "__MEMPOOL_TESTNET_USER__",
|
||||||
"PASSWORD": "__MEMPOOL_TESTNET_PASS__",
|
"PASSWORD": "__MEMPOOL_TESTNET_PASS__",
|
||||||
"DATABASE": "mempool_testnet"
|
"DATABASE": "mempool_testnet"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# start on reboot
|
# start on reboot
|
||||||
@reboot sleep 10 ; $HOME/start
|
@reboot sleep 90 ; $HOME/start
|
||||||
|
|
||||||
# daily backup
|
# daily backup
|
||||||
37 13 * * * sleep 30 ; /mempool/mempool.space/backup >/dev/null 2>&1 &
|
37 13 * * * sleep 30 ; /mempool/mempool.space/backup >/dev/null 2>&1 &
|
||||||
|
@ -1 +1 @@
|
|||||||
@reboot sleep 120 ; /usr/local/bin/bitcoind >/dev/null 2>&1
|
@reboot /usr/local/bin/bitcoind >/dev/null 2>&1
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/var/log/nginx/access.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/access.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/error.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/error.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/bisq-access.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/bisq-access.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/bisq-error.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/bisq-error.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/liquid-access.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/liquid-access.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/liquid-error.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/liquid-error.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/mempool-access.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/mempool-access.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
/var/log/nginx/mempool-error.log nobody:nobody 644 10 * @T00 C /var/run/mempool.pid 30
|
/var/log/nginx/mempool-error.log www:www 644 10 * @T00 C /var/run/mempool.pid 30
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
upstream esplora-bitcoin-mainnet {
|
upstream esplora-bitcoin-mainnet {
|
||||||
server [::1]:3000 fail_timeout=10s max_fails=10 weight=99999;
|
server unix:/bitcoin/socket/esplora-bitcoin-mainnet fail_timeout=10s max_fails=10 weight=99999;
|
||||||
}
|
}
|
||||||
upstream esplora-liquid-mainnet {
|
upstream esplora-liquid-mainnet {
|
||||||
server [::1]:3001 fail_timeout=10s max_fails=10 weight=99999;
|
server unix:/elements/socket/esplora-liquid-mainnet fail_timeout=10s max_fails=10 weight=99999;
|
||||||
}
|
}
|
||||||
upstream esplora-bitcoin-testnet {
|
upstream esplora-bitcoin-testnet {
|
||||||
server [::1]:3002 fail_timeout=10s max_fails=10 weight=99999;
|
server unix:/bitcoin/socket/esplora-bitcoin-testnet fail_timeout=10s max_fails=10 weight=99999;
|
||||||
}
|
}
|
||||||
upstream esplora-bitcoin-signet {
|
upstream esplora-bitcoin-signet {
|
||||||
server [::1]:3003 fail_timeout=10s max_fails=10 weight=99999;
|
server unix:/bitcoin/socket/esplora-bitcoin-signet fail_timeout=10s max_fails=10 weight=99999;
|
||||||
}
|
}
|
||||||
upstream esplora-liquid-testnet {
|
upstream esplora-liquid-testnet {
|
||||||
server [::1]:3004 fail_timeout=10s max_fails=10 weight=99999;
|
server unix:/elements/socket/esplora-liquid-testnet fail_timeout=10s max_fails=10 weight=99999;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user