diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 5c45838ee..dd5fb9ed0 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -12,7 +12,7 @@ "BLOCK_WEIGHT_UNITS": 4000000, "INITIAL_BLOCKS_AMOUNT": 8, "MEMPOOL_BLOCKS_AMOUNT": 8, - "INDEXING_BLOCKS_AMOUNT": 1100, + "INDEXING_BLOCKS_AMOUNT": 11000, "PRICE_FEED_UPDATE_INTERVAL": 600, "USE_SECOND_NODE_FOR_MINFEE": false, "EXTERNAL_ASSETS": [ @@ -44,6 +44,7 @@ "ENABLED": true, "HOST": "127.0.0.1", "PORT": 3306, + "SOCKET": "/var/run/mysql/mysql.sock", "DATABASE": "mempool", "USERNAME": "mempool", "PASSWORD": "mempool" diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 97a428c23..8d66f82ef 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -70,7 +70,7 @@ class BitcoinApi implements AbstractBitcoinApi { } return this.bitcoindClient.getBlock(hash) - .then((block: IBitcoinApi.Block) => this.convertBlock(block)); + .then((block: IBitcoinApi.Block) => BitcoinApi.convertBlock(block)); } $getAddress(address: string): Promise { @@ -186,7 +186,7 @@ class BitcoinApi implements AbstractBitcoinApi { return esploraTransaction; } - private convertBlock(block: IBitcoinApi.Block): IEsploraApi.Block { + static convertBlock(block: IBitcoinApi.Block): IEsploraApi.Block { return { id: block.hash, height: block.height, diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 40687060f..8f066b5a4 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -11,6 +11,7 @@ import { IEsploraApi } from './bitcoin/esplora-api.interface'; import poolsRepository from '../repositories/PoolsRepository'; import blocksRepository from '../repositories/BlocksRepository'; import loadingIndicators from './loading-indicators'; +import BitcoinApi from './bitcoin/bitcoin-api'; class Blocks { private blocks: BlockExtended[] = []; @@ -103,19 +104,28 @@ class Blocks { * @param transactions * @returns BlockExtended */ - private async $getBlockExtended(block: IEsploraApi.Block, transactions: TransactionExtended[]): Promise { - const blockExtended: BlockExtended = Object.assign({extras: {}}, block); + private async $getBlockExtended(block: IEsploraApi.Block, transactions: TransactionExtended[]): Promise { + const blockExtended: BlockExtended = Object.assign({ extras: {} }, block); blockExtended.extras.reward = transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0); blockExtended.extras.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]); - const stats = await bitcoinClient.getBlockStats(block.id); const coinbaseRaw: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(transactions[0].txid, true); blockExtended.extras.coinbaseRaw = coinbaseRaw.hex; - blockExtended.extras.medianFee = stats.feerate_percentiles[2]; // 50th percentiles - blockExtended.extras.feeRange = [stats.minfeerate, stats.feerate_percentiles, stats.maxfeerate].flat(); - blockExtended.extras.totalFees = stats.totalfee; - blockExtended.extras.avgFee = stats.avgfee; - blockExtended.extras.avgFeeRate = stats.avgfeerate; + + if (block.height === 0) { + blockExtended.extras.medianFee = 0; // 50th percentiles + blockExtended.extras.feeRange = [0, 0, 0, 0, 0, 0, 0]; + blockExtended.extras.totalFees = 0; + blockExtended.extras.avgFee = 0; + blockExtended.extras.avgFeeRate = 0; + } else { + const stats = await bitcoinClient.getBlockStats(block.id); + blockExtended.extras.medianFee = stats.feerate_percentiles[2]; // 50th percentiles + blockExtended.extras.feeRange = [stats.minfeerate, stats.feerate_percentiles, stats.maxfeerate].flat(); + blockExtended.extras.totalFees = stats.totalfee; + blockExtended.extras.avgFee = stats.avgfee; + blockExtended.extras.avgFeeRate = stats.avgfeerate; + } if (Common.indexingEnabled()) { let pool: PoolTag; @@ -230,7 +240,7 @@ class Blocks { indexedThisRun = 0; } const blockHash = await bitcoinApi.$getBlockHash(blockHeight); - const block = await bitcoinApi.$getBlock(blockHash); + const block = BitcoinApi.convertBlock(await bitcoinClient.getBlock(blockHash)); const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true); const blockExtended = await this.$getBlockExtended(block, transactions); await blocksRepository.$saveBlockInDatabase(blockExtended); @@ -267,7 +277,7 @@ class Blocks { if (blockchainInfo.blocks === blockchainInfo.headers) { const heightDiff = blockHeightTip % 2016; const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff); - const block = await bitcoinApi.$getBlock(blockHash); + const block = BitcoinApi.convertBlock(await bitcoinClient.getBlock(blockHash)); this.lastDifficultyAdjustmentTime = block.timestamp; this.currentDifficulty = block.difficulty; @@ -291,7 +301,7 @@ class Blocks { } const blockHash = await bitcoinApi.$getBlockHash(this.currentBlockHeight); - const block = await bitcoinApi.$getBlock(blockHash); + const block = BitcoinApi.convertBlock(await bitcoinClient.getBlock(blockHash)); const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash); const transactions = await this.$getTransactionsExtended(blockHash, block.height, false); const blockExtended: BlockExtended = await this.$getBlockExtended(block, transactions); @@ -323,23 +333,26 @@ class Blocks { /** * Index a block if it's missing from the database. Returns the block after indexing */ - public async $indexBlock(height: number): Promise { + public async $indexBlock(height: number): Promise { const dbBlock = await blocksRepository.$getBlockByHeight(height); if (dbBlock != null) { return this.prepareBlock(dbBlock); } const blockHash = await bitcoinApi.$getBlockHash(height); - const block = await bitcoinApi.$getBlock(blockHash); + const block = BitcoinApi.convertBlock(await bitcoinClient.getBlock(blockHash)); const transactions = await this.$getTransactionsExtended(blockHash, block.height, true); const blockExtended = await this.$getBlockExtended(block, transactions); await blocksRepository.$saveBlockInDatabase(blockExtended); - return blockExtended; + return this.prepareBlock(blockExtended); } - public async $getBlocksExtras(fromHeight: number): Promise { + public async $getBlocksExtras(fromHeight: number, limit: number = 15): Promise { + // Note - This API is breaking if indexing is not available. For now it is okay because we only + // use it for the mining pages, and mining pages should not be available if indexing is turned off. + // I'll need to fix it before we refactor the block(s) related pages try { loadingIndicators.setProgress('blocks', 0); @@ -360,10 +373,10 @@ class Blocks { } let nextHash = startFromHash; - for (let i = 0; i < 10 && currentHeight >= 0; i++) { + for (let i = 0; i < limit && currentHeight >= 0; i++) { let block = this.getBlocks().find((b) => b.height === currentHeight); if (!block && Common.indexingEnabled()) { - block = this.prepareBlock(await this.$indexBlock(currentHeight)); + block = await this.$indexBlock(currentHeight); } else if (!block) { block = this.prepareBlock(await bitcoinApi.$getBlock(nextHash)); } @@ -383,24 +396,25 @@ class Blocks { private prepareBlock(block: any): BlockExtended { return { id: block.id ?? block.hash, // hash for indexed block - timestamp: block?.timestamp ?? block?.blockTimestamp, // blockTimestamp for indexed block - height: block?.height, - version: block?.version, - bits: block?.bits, - nonce: block?.nonce, - difficulty: block?.difficulty, - merkle_root: block?.merkle_root, - tx_count: block?.tx_count, - size: block?.size, - weight: block?.weight, - previousblockhash: block?.previousblockhash, + timestamp: block.timestamp ?? block.blockTimestamp, // blockTimestamp for indexed block + height: block.height, + version: block.version, + bits: block.bits, + nonce: block.nonce, + difficulty: block.difficulty, + merkle_root: block.merkle_root, + tx_count: block.tx_count, + size: block.size, + weight: block.weight, + previousblockhash: block.previousblockhash, extras: { - medianFee: block?.medianFee, - feeRange: block?.feeRange ?? [], // TODO - reward: block?.reward, + medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, + feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan, + reward: block.reward ?? block?.extras?.reward, + totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees, pool: block?.extras?.pool ?? (block?.pool_id ? { - id: block?.pool_id, - name: block?.pool_name, + id: block.pool_id, + name: block.pool_name, } : undefined), } }; diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 14d23a477..c9c1da8e8 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -6,7 +6,7 @@ import logger from '../logger'; const sleep = (ms: number) => new Promise(res => setTimeout(res, ms)); class DatabaseMigration { - private static currentVersion = 12; + private static currentVersion = 14; private queryTimeout = 120000; private statisticsAddedIndexed = false; @@ -77,7 +77,7 @@ class DatabaseMigration { await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion); const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { await this.$executeQuery(connection, this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs')); await this.$executeQuery(connection, this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics')); @@ -161,6 +161,20 @@ class DatabaseMigration { await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `fees` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); } + if (databaseSchemaVersion < 13 && isBitcoin === true) { + await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `difficulty` DOUBLE UNSIGNED NOT NULL DEFAULT "0"'); + await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `median_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); + await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `avg_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); + await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `avg_fee_rate` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); + } + + if (databaseSchemaVersion < 14 && isBitcoin === true) { + logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`); + await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index + await this.$executeQuery(connection, 'ALTER TABLE `hashrates` DROP FOREIGN KEY `hashrates_ibfk_1`'); + await this.$executeQuery(connection, 'ALTER TABLE `hashrates` MODIFY `pool_id` SMALLINT UNSIGNED NOT NULL DEFAULT "0"'); + } + connection.release(); } catch (e) { connection.release(); @@ -180,7 +194,7 @@ class DatabaseMigration { return; } - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { // We don't use "CREATE INDEX IF NOT EXISTS" because it is not supported on old mariadb version 5.X @@ -218,7 +232,7 @@ class DatabaseMigration { * Check if 'table' exists in the database */ private async $checkIfTableExists(table: string): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`; const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -229,7 +243,7 @@ class DatabaseMigration { * Get current database version */ private async $getSchemaVersionFromDatabase(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT number FROM state WHERE name = 'schema_version';`; const [rows] = await this.$executeQuery(connection, query, true); connection.release(); @@ -240,7 +254,7 @@ class DatabaseMigration { * Create the `state` table */ private async $createMigrationStateTable(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const query = `CREATE TABLE IF NOT EXISTS state ( @@ -272,7 +286,7 @@ class DatabaseMigration { } transactionQueries.push(this.getUpdateToLatestSchemaVersionQuery()); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { await this.$executeQuery(connection, 'START TRANSACTION;'); for (const query of transactionQueries) { @@ -293,6 +307,7 @@ class DatabaseMigration { */ private getMigrationQueriesFromVersion(version: number): string[] { const queries: string[] = []; + const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK); if (version < 1) { if (config.MEMPOOL.NETWORK !== 'liquid' && config.MEMPOOL.NETWORK !== 'liquidtestnet') { @@ -300,11 +315,11 @@ class DatabaseMigration { } } - if (version < 7) { + if (version < 7 && isBitcoin === true) { queries.push(`INSERT INTO state(name, number, string) VALUES ('last_hashrates_indexing', 0, NULL)`); } - if (version < 9) { + if (version < 9 && isBitcoin === true) { queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`); } @@ -322,7 +337,7 @@ class DatabaseMigration { * Print current database version */ private async $printDatabaseVersion() { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await this.$executeQuery(connection, 'SELECT VERSION() as version;', true); logger.debug(`MIGRATIONS: Database engine version '${rows[0].version}'`); @@ -466,7 +481,7 @@ class DatabaseMigration { public async $truncateIndexedData(tables: string[]) { const allowedTables = ['blocks', 'hashrates']; - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { for (const table of tables) { if (!allowedTables.includes(table)) { diff --git a/backend/src/api/liquid/elements-parser.ts b/backend/src/api/liquid/elements-parser.ts index a2d4e1546..24c7ab949 100644 --- a/backend/src/api/liquid/elements-parser.ts +++ b/backend/src/api/liquid/elements-parser.ts @@ -33,7 +33,7 @@ class ElementsParser { } public async $getPegDataByMonth(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`; const [rows] = await connection.query(query); connection.release(); @@ -79,7 +79,7 @@ class ElementsParser { protected async $savePegToDatabase(height: number, blockTime: number, amount: number, txid: string, txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, final_tx: number): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `INSERT INTO elements_pegs( block, datetime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`; @@ -93,7 +93,7 @@ class ElementsParser { } protected async $getLatestBlockHeightFromDatabase(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT number FROM state WHERE name = 'last_elements_block'`; const [rows] = await connection.query(query); connection.release(); @@ -101,7 +101,7 @@ class ElementsParser { } protected async $saveLatestBlockToDatabase(blockHeight: number) { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`; await connection.query(query, [blockHeight]); connection.release(); diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index afcc89220..512388b36 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -80,8 +80,8 @@ class Mining { // We only run this once a week const latestTimestamp = await HashratesRepository.$getLatestRunTimestamp('last_weekly_hashrates_indexing'); - const now = new Date().getTime() / 1000; - if (now - latestTimestamp < 604800) { + const now = new Date(); + if ((now.getTime() / 1000) - latestTimestamp < 604800) { return; } @@ -94,7 +94,6 @@ class Mining { const hashrates: any[] = []; const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - const now = new Date(); const lastMonday = new Date(now.setDate(now.getDate() - (now.getDay() + 6) % 7)); const lastMondayMidnight = this.getDateMidnight(lastMonday); let toTimestamp = Math.round((lastMondayMidnight.getTime() - 604800) / 1000); @@ -108,7 +107,7 @@ class Mining { const fromTimestamp = toTimestamp - 604800; // Skip already indexed weeks - if (indexedTimestamp.includes(toTimestamp + 1)) { + if (indexedTimestamp.includes(toTimestamp)) { toTimestamp -= 604800; ++totalIndexed; continue; @@ -133,7 +132,7 @@ class Mining { for (const pool of pools) { hashrates.push({ - hashrateTimestamp: toTimestamp + 1, + hashrateTimestamp: toTimestamp, avgHashrate: pool['hashrate'], poolId: pool.poolId, share: pool['share'], @@ -202,7 +201,7 @@ class Mining { const fromTimestamp = toTimestamp - 86400; // Skip already indexed weeks - if (indexedTimestamp.includes(fromTimestamp)) { + if (indexedTimestamp.includes(toTimestamp)) { toTimestamp -= 86400; ++totalIndexed; continue; @@ -220,7 +219,7 @@ class Mining { hashrates.push({ hashrateTimestamp: toTimestamp, avgHashrate: lastBlockHashrate, - poolId: null, + poolId: 0, share: 1, type: 'daily', }); diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index 194ce0dd9..ff70c3cb9 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -66,7 +66,7 @@ class PoolsParser { logger.debug(`Found ${poolNames.length} unique mining pools`); // Get existing pools from the db - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); let existingPools; try { [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); @@ -152,7 +152,7 @@ class PoolsParser { * Manually add the 'unknown pool' */ private async insertUnknownPool() { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query({ sql: 'SELECT name from pools where name="Unknown"', timeout: 120000 }); if (rows.length === 0) { diff --git a/backend/src/api/statistics.ts b/backend/src/api/statistics.ts index 886ad78ba..3d99adcb7 100644 --- a/backend/src/api/statistics.ts +++ b/backend/src/api/statistics.ts @@ -155,7 +155,7 @@ class Statistics { } private async $createZeroedStatistic(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const query = `INSERT INTO statistics( added, @@ -216,7 +216,7 @@ class Statistics { } private async $create(statistics: Statistic): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const query = `INSERT INTO statistics( added, @@ -421,7 +421,7 @@ class Statistics { private async $get(id: number): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE id = ?`; const [rows] = await connection.query(query, [id]); connection.release(); @@ -435,7 +435,7 @@ class Statistics { public async $list2H(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`; const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -448,7 +448,7 @@ class Statistics { public async $list24H(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`; const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -461,7 +461,7 @@ class Statistics { public async $list1W(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDaysAvg(300, '1 WEEK'); // 5m interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -474,7 +474,7 @@ class Statistics { public async $list1M(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDaysAvg(1800, '1 MONTH'); // 30m interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -487,7 +487,7 @@ class Statistics { public async $list3M(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDaysAvg(7200, '3 MONTH'); // 2h interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -500,7 +500,7 @@ class Statistics { public async $list6M(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDaysAvg(10800, '6 MONTH'); // 3h interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -513,7 +513,7 @@ class Statistics { public async $list1Y(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDays(28800, '1 YEAR'); // 8h interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -526,7 +526,7 @@ class Statistics { public async $list2Y(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDays(28800, "2 YEAR"); // 8h interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); @@ -539,7 +539,7 @@ class Statistics { public async $list3Y(): Promise { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = this.getQueryForDays(43200, "3 YEAR"); // 12h interval const [rows] = await connection.query({ sql: query, timeout: this.queryTimeout }); connection.release(); diff --git a/backend/src/config.ts b/backend/src/config.ts index 97c3bb32a..56c60f965 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -43,6 +43,7 @@ interface IConfig { DATABASE: { ENABLED: boolean; HOST: string, + SOCKET: string, PORT: number; DATABASE: string; USERNAME: string; @@ -90,7 +91,7 @@ const defaults: IConfig = { 'BLOCK_WEIGHT_UNITS': 4000000, 'INITIAL_BLOCKS_AMOUNT': 8, 'MEMPOOL_BLOCKS_AMOUNT': 8, - 'INDEXING_BLOCKS_AMOUNT': 1100, // 0 = disable indexing, -1 = index all blocks + 'INDEXING_BLOCKS_AMOUNT': 11000, // 0 = disable indexing, -1 = index all blocks 'PRICE_FEED_UPDATE_INTERVAL': 600, 'USE_SECOND_NODE_FOR_MINFEE': false, 'EXTERNAL_ASSETS': [ @@ -121,6 +122,7 @@ const defaults: IConfig = { 'DATABASE': { 'ENABLED': true, 'HOST': '127.0.0.1', + 'SOCKET': '', 'PORT': 3306, 'DATABASE': 'mempool', 'USERNAME': 'mempool', diff --git a/backend/src/database.ts b/backend/src/database.ts index 9f2655016..55be0ffc3 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -1,23 +1,47 @@ import config from './config'; -import { createPool } from 'mysql2/promise'; +import { createPool, PoolConnection } from 'mysql2/promise'; import logger from './logger'; +import { PoolOptions } from 'mysql2/typings/mysql'; export class DB { - static pool = createPool({ - host: config.DATABASE.HOST, - port: config.DATABASE.PORT, - database: config.DATABASE.DATABASE, - user: config.DATABASE.USERNAME, - password: config.DATABASE.PASSWORD, - connectionLimit: 10, - supportBigNumbers: true, - timezone: '+00:00', - }); + static poolConfig = ():PoolOptions => { + let poolConfig:PoolOptions = { + port: config.DATABASE.PORT, + database: config.DATABASE.DATABASE, + user: config.DATABASE.USERNAME, + password: config.DATABASE.PASSWORD, + connectionLimit: 10, + supportBigNumbers: true, + timezone: '+00:00', + } + + if (config.DATABASE.SOCKET !== "") { + poolConfig.socketPath = config.DATABASE.SOCKET; + } else { + poolConfig.host = config.DATABASE.HOST; + } + + return poolConfig; + } + + static pool = createPool(DB.poolConfig()); + + static connectionsReady: number[] = []; + + static async getConnection() { + const connection: PoolConnection = await DB.pool.getConnection(); + const connectionId = connection['connection'].connectionId; + if (!DB.connectionsReady.includes(connectionId)) { + await connection.query(`SET time_zone='+00:00';`); + this.connectionsReady.push(connectionId); + } + return connection; + } } export async function checkDbConnection() { try { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); logger.info('Database connection established.'); connection.release(); } catch (e) { diff --git a/backend/src/index.ts b/backend/src/index.ts index c2de54521..d4b55e078 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -5,7 +5,7 @@ import * as WebSocket from 'ws'; import * as cluster from 'cluster'; import axios from 'axios'; -import { checkDbConnection } from './database'; +import { checkDbConnection, DB } from './database'; import config from './config'; import routes from './routes'; import blocks from './api/blocks'; @@ -97,8 +97,10 @@ class Server { await databaseMigration.$truncateIndexedData(tables); } await databaseMigration.$initializeOrMigrateDatabase(); - await this.$resetHashratesIndexingState(); - await poolsParser.migratePoolsJson(); + if (Common.indexingEnabled()) { + await this.$resetHashratesIndexingState(); + await poolsParser.migratePoolsJson(); + } } catch (e) { throw new Error(e instanceof Error ? e.message : 'Error'); } @@ -178,8 +180,8 @@ class Server { try { blocks.$generateBlockDatabase(); - mining.$generateNetworkHashrateHistory(); - mining.$generatePoolHashrateHistory(); + await mining.$generateNetworkHashrateHistory(); + await mining.$generatePoolHashrateHistory(); } catch (e) { logger.err(`Unable to run indexing right now, trying again later. ` + e); } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 0cab3c0db..0af5e2252 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -8,7 +8,7 @@ class BlocksRepository { * Save indexed block data in the database */ public async $saveBlockInDatabase(block: BlockExtended) { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const query = `INSERT INTO blocks( @@ -70,7 +70,7 @@ class BlocksRepository { return []; } - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query(` SELECT height @@ -116,7 +116,7 @@ class BlocksRepository { query += ` GROUP by pools.id`; - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); connection.release(); @@ -154,7 +154,7 @@ class BlocksRepository { } // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); connection.release(); @@ -194,7 +194,7 @@ class BlocksRepository { query += ` blockTimestamp BETWEEN FROM_UNIXTIME('${from}') AND FROM_UNIXTIME('${to}')`; // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); connection.release(); @@ -217,7 +217,7 @@ class BlocksRepository { LIMIT 1;`; // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query(query); connection.release(); @@ -253,7 +253,7 @@ class BlocksRepository { LIMIT 10`; // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); connection.release(); @@ -274,10 +274,13 @@ class BlocksRepository { * Get one block by height */ public async $getBlockByHeight(height: number): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query(` - SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes + SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, + pools.addresses as pool_addresses, pools.regexes as pool_regexes, + previous_block_hash as previousblockhash FROM blocks JOIN pools ON blocks.pool_id = pools.id WHERE height = ${height}; @@ -302,7 +305,7 @@ class BlocksRepository { public async $getBlocksDifficulty(interval: string | null): Promise { interval = Common.getSqlInterval(interval); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); // :D ... Yeah don't ask me about this one https://stackoverflow.com/a/40303162 // Basically, using temporary user defined fields, we are able to extract all @@ -353,7 +356,7 @@ class BlocksRepository { } public async $getOldestIndexedBlockHeight(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); connection.release(); diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 749d3cb57..5237e6cb7 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -20,9 +20,9 @@ class HashratesRepository { } query = query.slice(0, -1); - const connection = await DB.pool.getConnection(); + let connection; try { - // logger.debug(query); + connection = await DB.getConnection(); await connection.query(query); connection.release(); } catch (e: any) { @@ -35,18 +35,16 @@ class HashratesRepository { public async $getNetworkDailyHashrate(interval: string | null): Promise { interval = Common.getSqlInterval(interval); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate FROM hashrates`; if (interval) { query += ` WHERE hashrate_timestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() - AND hashrates.type = 'daily' - AND pool_id IS NULL`; + AND hashrates.type = 'daily'`; } else { - query += ` WHERE hashrates.type = 'daily' - AND pool_id IS NULL`; + query += ` WHERE hashrates.type = 'daily'`; } query += ` ORDER by hashrate_timestamp`; @@ -64,9 +62,12 @@ class HashratesRepository { } public async $getWeeklyHashrateTimestamps(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); - const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; + const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp + FROM hashrates + WHERE type = 'weekly' + GROUP BY hashrate_timestamp`; try { const [rows]: any[] = await connection.query(query); @@ -86,7 +87,7 @@ class HashratesRepository { public async $getPoolsWeeklyHashrate(interval: string | null): Promise { interval = Common.getSqlInterval(interval); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const topPoolsId = (await PoolsRepository.$getPoolsInfo('1w')).map((pool) => pool.poolId); let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate, share, pools.name as poolName @@ -120,7 +121,7 @@ class HashratesRepository { * Returns a pool hashrate history */ public async $getPoolWeeklyHashrate(poolId: number): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); // Find hashrate boundaries let query = `SELECT MIN(hashrate_timestamp) as firstTimestamp, MAX(hashrate_timestamp) as lastTimestamp @@ -163,7 +164,7 @@ class HashratesRepository { } public async $setLatestRunTimestamp(key: string, val: any = null) { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `UPDATE state SET number = ? WHERE name = ?`; try { @@ -175,7 +176,7 @@ class HashratesRepository { } public async $getLatestRunTimestamp(key: string): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const query = `SELECT number FROM state WHERE name = ?`; try { diff --git a/backend/src/repositories/PoolsRepository.ts b/backend/src/repositories/PoolsRepository.ts index 3f904888d..4c3fd67ce 100644 --- a/backend/src/repositories/PoolsRepository.ts +++ b/backend/src/repositories/PoolsRepository.ts @@ -8,7 +8,7 @@ class PoolsRepository { * Get all pools tagging info */ public async $getPools(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const [rows] = await connection.query('SELECT id, name, addresses, regexes FROM pools;'); connection.release(); return rows; @@ -18,7 +18,7 @@ class PoolsRepository { * Get unknown pool tagging info */ public async $getUnknownPool(): Promise { - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); const [rows] = await connection.query('SELECT id, name FROM pools where name = "Unknown"'); connection.release(); return rows[0]; @@ -42,7 +42,7 @@ class PoolsRepository { ORDER BY COUNT(height) DESC`; // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query); connection.release(); @@ -64,7 +64,7 @@ class PoolsRepository { LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?) GROUP BY pools.id`; - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, [from, to]); connection.release(); @@ -87,7 +87,7 @@ class PoolsRepository { WHERE pools.id = ?`; // logger.debug(query); - const connection = await DB.pool.getConnection(); + const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, [poolId]); connection.release(); diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 710cd8378..c6b3656e7 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -658,7 +658,7 @@ class Routes { public async getBlocksExtras(req: Request, res: Response) { try { - res.json(await blocks.$getBlocksExtras(parseInt(req.params.height, 10))) + res.json(await blocks.$getBlocksExtras(parseInt(req.params.height, 10), 15)); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } diff --git a/contributors/dsbaars.txt b/contributors/dsbaars.txt new file mode 100644 index 000000000..8e4dcbb9a --- /dev/null +++ b/contributors/dsbaars.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022. + +Signed: dsbaars \ No newline at end of file diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 6b2319a59..39d0e5fad 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -41,6 +41,7 @@ "DATABASE": { "ENABLED": __DATABASE_ENABLED__, "HOST": "__DATABASE_HOST__", + "SOCKET": "__DATABASE_SOCKET__", "PORT": __DATABASE_PORT__, "DATABASE": "__DATABASE_DATABASE__", "USERNAME": "__DATABASE_USERNAME__", diff --git a/docker/backend/start.sh b/docker/backend/start.sh index d26a93b08..f515fefd3 100644 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -13,7 +13,7 @@ __MEMPOOL_RECOMMENDED_FEE_PERCENTILE__=${MEMPOOL_RECOMMENDED_FEE_PERCENTILE:=50} __MEMPOOL_BLOCK_WEIGHT_UNITS__=${MEMPOOL_BLOCK_WEIGHT_UNITS:=4000000} __MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8} __MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8} -__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=1100} +__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=11000} __MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__=${MEMPOOL_PRICE_FEED_UPDATE_INTERVAL:=600} __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false} __MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[\"https://mempool.space/resources/pools.json\"]} @@ -42,6 +42,7 @@ __SECOND_CORE_RPC_PASSWORD__=${SECOND_CORE_RPC_PASSWORD:=mempool} # DATABASE __DATABASE_ENABLED__=${DATABASE_ENABLED:=true} __DATABASE_HOST__=${DATABASE_HOST:=127.0.0.1} +__DATABASE_SOCKET__=${DATABASE_SOCKET:=""} __DATABASE_PORT__=${DATABASE_PORT:=3306} __DATABASE_DATABASE__=${DATABASE_DATABASE:=mempool} __DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool} @@ -111,6 +112,8 @@ sed -i "s/__SECOND_CORE_RPC_PASSWORD__/${__SECOND_CORE_RPC_PASSWORD__}/g" mempoo sed -i "s/__DATABASE_ENABLED__/${__DATABASE_ENABLED__}/g" mempool-config.json sed -i "s/__DATABASE_HOST__/${__DATABASE_HOST__}/g" mempool-config.json +sed -i "s!__DATABASE_SOCKET__!${__DATABASE_SOCKET__}!g" mempool-config.json + sed -i "s/__DATABASE_PORT__/${__DATABASE_PORT__}/g" mempool-config.json sed -i "s/__DATABASE_DATABASE__/${__DATABASE_DATABASE__}/g" mempool-config.json sed -i "s/__DATABASE_USERNAME__/${__DATABASE_USERNAME__}/g" mempool-config.json diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 19285fc8f..1ef7e5fe0 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -31,6 +31,7 @@ import { MiningDashboardComponent } from './components/mining-dashboard/mining-d import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component'; import { HashrateChartPoolsComponent } from './components/hashrates-chart-pools/hashrate-chart-pools.component'; import { MiningStartComponent } from './components/mining-start/mining-start.component'; +import { BlocksList } from './components/blocks-list/blocks-list.component'; let routes: Routes = [ { @@ -75,6 +76,10 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ + { + path: 'blocks', + component: BlocksList, + }, { path: 'hashrate', component: HashrateChartComponent, @@ -190,6 +195,10 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ + { + path: 'blocks', + component: BlocksList, + }, { path: 'hashrate', component: HashrateChartComponent, @@ -299,6 +308,10 @@ let routes: Routes = [ path: 'mining', component: MiningStartComponent, children: [ + { + path: 'blocks', + component: BlocksList, + }, { path: 'hashrate', component: HashrateChartComponent, @@ -630,7 +643,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { initialNavigation: 'enabled', scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled' -})], + })], exports: [RouterModule] }) export class AppRoutingModule { } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 0dfc853cc..3affdc7ba 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -76,6 +76,7 @@ import { MiningStartComponent } from './components/mining-start/mining-start.com import { AmountShortenerPipe } from './shared/pipes/amount-shortener.pipe'; import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe'; import { DifficultyAdjustmentsTable } from './components/difficulty-adjustments-table/difficulty-adjustments-table.components'; +import { BlocksList } from './components/blocks-list/blocks-list.component'; @NgModule({ declarations: [ @@ -133,6 +134,7 @@ import { DifficultyAdjustmentsTable } from './components/difficulty-adjustments- MiningStartComponent, AmountShortenerPipe, DifficultyAdjustmentsTable, + BlocksList, ], imports: [ BrowserModule.withServerTransition({ appId: 'serverApp' }), diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index b0a440b61..c03a3a00a 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -170,13 +170,8 @@
-

Community Integrations

- +

Self-Hosted Integrations

+
+ +
+

Wallet Integrations

+ - +
diff --git a/frontend/src/app/components/amount/amount.component.html b/frontend/src/app/components/amount/amount.component.html index 07f669a81..c4946ddf8 100644 --- a/frontend/src/app/components/amount/amount.component.html +++ b/frontend/src/app/components/amount/amount.component.html @@ -1,12 +1,12 @@ - {{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }} + {{ addPlus && satoshis >= 0 ? '+' : '' }}{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }} Confidential - ‎{{ satoshis / 100000000 | number : digitsInfo }} + ‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis / 100000000 | number : digitsInfo }} L- tL- t diff --git a/frontend/src/app/components/amount/amount.component.ts b/frontend/src/app/components/amount/amount.component.ts index 068acf1a7..f9bba4318 100644 --- a/frontend/src/app/components/amount/amount.component.ts +++ b/frontend/src/app/components/amount/amount.component.ts @@ -18,6 +18,7 @@ export class AmountComponent implements OnInit, OnDestroy { @Input() satoshis: number; @Input() digitsInfo = '1.8-8'; @Input() noFiat = false; + @Input() addPlus = false; constructor( private stateService: StateService, diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html new file mode 100644 index 000000000..f50a5fff2 --- /dev/null +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -0,0 +1,96 @@ +
+

Blocks

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Height + PoolTimestampMined + RewardFeesTxsSize
+ {{ block.height + }} + + + + {{ block.extras.pool.name }} + + + ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} + + + + + + + + {{ block.tx_count | number }} + +
+
+
+
+
+ + + + + + + + + + + + + + + + +
+ + + +
+ +
\ No newline at end of file diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.scss b/frontend/src/app/components/blocks-list/blocks-list.component.scss new file mode 100644 index 000000000..9414348c1 --- /dev/null +++ b/frontend/src/app/components/blocks-list/blocks-list.component.scss @@ -0,0 +1,124 @@ +.container-xl { + max-width: 1400px; + padding-bottom: 100px; +} +.container-xl.widget { + padding-left: 0px; + padding-bottom: 0px; +} + +.container { + max-width: 100%; +} + +td { + padding-top: 0.7rem !important; + padding-bottom: 0.7rem !important; +} + +.clear-link { + color: white; +} + +.disabled { + pointer-events: none; + opacity: 0.5; +} + +.progress { + background-color: #2d3348; +} + +.pool { + width: 17%; +} +.pool.widget { + width: 40%; + @media (max-width: 576px) { + padding-left: 30px; + width: 60%; + } +} +.pool-name { + display: inline-block; + vertical-align: text-top; + padding-left: 10px; +} + +.height { + width: 10%; + @media (max-width: 1100px) { + width: 10%; + } +} +.height.widget { + width: 20%; + @media (max-width: 576px) { + width: 10%; + } +} + +.timestamp { + @media (max-width: 900px) { + display: none; + } +} + +.mined { + width: 13%; + @media (max-width: 576px) { + display: none; + } +} + +.txs { + padding-right: 40px; + @media (max-width: 1100px) { + padding-right: 10px; + } + @media (max-width: 875px) { + display: none; + } +} +.txs.widget { + padding-right: 0; + @media (max-width: 650px) { + display: none; + } +} + +.fees { + @media (max-width: 650px) { + display: none; + } +} +.fees.widget { + width: 20%; +} + +.reward { + @media (max-width: 576px) { + width: 7%; + padding-right: 30px; + } +} +.reward.widget { + width: 20%; + @media (max-width: 576px) { + width: 30%; + padding-right: 0; + } +} + +.size { + width: 12%; + @media (max-width: 1000px) { + width: 15%; + } + @media (max-width: 650px) { + width: 20%; + } + @media (max-width: 450px) { + display: none; + } +} diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts new file mode 100644 index 000000000..72727b734 --- /dev/null +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -0,0 +1,98 @@ +import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; +import { BehaviorSubject, combineLatest, Observable, timer } from 'rxjs'; +import { delayWhen, map, retryWhen, scan, skip, switchMap, tap } from 'rxjs/operators'; +import { BlockExtended } from 'src/app/interfaces/node-api.interface'; +import { ApiService } from 'src/app/services/api.service'; +import { StateService } from 'src/app/services/state.service'; +import { WebsocketService } from 'src/app/services/websocket.service'; + +@Component({ + selector: 'app-blocks-list', + templateUrl: './blocks-list.component.html', + styleUrls: ['./blocks-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class BlocksList implements OnInit { + @Input() widget: boolean = false; + + blocks$: Observable = undefined; + + isLoading = true; + fromBlockHeight = undefined; + paginationMaxSize: number; + page = 1; + lastPage = 1; + blocksCount: number; + fromHeightSubject: BehaviorSubject = new BehaviorSubject(this.fromBlockHeight); + skeletonLines: number[] = []; + + constructor( + private apiService: ApiService, + private websocketService: WebsocketService, + public stateService: StateService, + ) { + } + + ngOnInit(): void { + this.websocketService.want(['blocks']); + + this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; + this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; + + this.blocks$ = combineLatest([ + this.fromHeightSubject.pipe( + switchMap((fromBlockHeight) => { + this.isLoading = true; + return this.apiService.getBlocks$(this.page === 1 ? undefined : fromBlockHeight) + .pipe( + tap(blocks => { + if (this.blocksCount === undefined) { + this.blocksCount = blocks[0].height; + } + this.isLoading = false; + }), + map(blocks => { + for (const block of blocks) { + // @ts-ignore: Need to add an extra field for the template + block.extras.pool.logo = `./resources/mining-pools/` + + block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } + if (this.widget) { + return blocks.slice(0, 5); + } + return blocks; + }), + retryWhen(errors => errors.pipe(delayWhen(() => timer(1000)))) + ) + }) + ), + this.stateService.blocks$ + .pipe( + skip(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT - 1), + ), + ]) + .pipe( + scan((acc, blocks) => { + if (this.page > 1 || acc.length === 0 || (this.page === 1 && this.lastPage !== 1)) { + this.lastPage = this.page; + return blocks[0]; + } + this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height); + // @ts-ignore: Need to add an extra field for the template + blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + + blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + acc.unshift(blocks[1][0]); + acc = acc.slice(0, this.widget ? 5 : 15); + return acc; + }, []) + ); + } + + pageChange(page: number) { + this.fromHeightSubject.next(this.blocksCount - (page - 1) * 15); + } + + trackByBlock(index: number, block: BlockExtended) { + return block.height; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html index a04c60f6a..51872c932 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html @@ -1,5 +1,5 @@ -
- +
+
@@ -17,7 +17,7 @@ diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts index 24c44fe05..5e8b3ded7 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts @@ -43,7 +43,7 @@ export class DifficultyAdjustmentsTable implements OnInit { const change = (data.difficulty[i].difficulty / data.difficulty[i - 1].difficulty - 1) * 100; tableData.push(Object.assign(data.difficulty[i], { - change: change, + change: Math.round(change * 100) / 100, difficultyShorten: formatNumber( data.difficulty[i].difficulty / selectedPowerOfTen.divider, this.locale, '1.2-2') + selectedPowerOfTen.unit diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index 68209fed0..63a93a3e4 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -5,30 +5,32 @@
Reward stats
-
-
-
-
-
Miners Reward
-
- -
in the last 8 blocks
+
+
+
+
+
+
Miners Reward
+
+ +
in the last 8 blocks
+
-
-
-
Reward Per Tx
-
- {{ rewardStats.rewardPerTx | amountShortener }} - sats/tx -
in the last 8 blocks
+
+
Reward Per Tx
+
+ {{ rewardStats.rewardPerTx | amountShortener }} + sats/tx +
in the last 8 blocks
+
-
-
-
Average Fee
-
- {{ rewardStats.feePerTx | amountShortener}} - sats/tx -
in the last 8 blocks
+
+
Average Fee
+
+ {{ rewardStats.feePerTx | amountShortener}} + sats/tx +
in the last 8 blocks
+
@@ -36,24 +38,24 @@
-
+
-
Miners Reward
-
+
Miners Reward
+
-
Reward Per Tx
-
+
Reward Per Tx
+
-
Average Fee
-
+
Average Fee
+
@@ -95,7 +97,7 @@
-
+ + + +
+
+
+
+ Latest blocks +
+ + +
+
@@ -115,11 +131,11 @@ Adjustments -
-
+
\ No newline at end of file diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index fb2663812..a345be972 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -28,6 +28,9 @@ .card-body.pool-ranking { padding: 1.25rem 0.25rem 0.75rem 0.25rem; } +.card-text { + font-size: 22px; +} #blockchain-container { position: relative; @@ -56,50 +59,57 @@ padding-bottom: 3px; } -.fee-estimation-container { +.reward-container { display: flex; - justify-content: space-between; - @media (min-width: 376px) { - flex-direction: row; + flex-direction: row; + justify-content: space-around; + height: 76px; + .shared-block { + color: #ffffff66; + font-size: 12px; } .item { - max-width: 150px; - margin: 0; - width: -webkit-fill-available; - @media (min-width: 376px) { - margin: 0 auto 0px; - } - &:first-child{ + display: table-cell; + padding: 0 5px; + width: 100%; + &:nth-child(1) { display: none; @media (min-width: 485px) { - display: block; + display: table-cell; } @media (min-width: 768px) { display: none; } @media (min-width: 992px) { - display: block; + display: table-cell; } } - &:last-child { - margin-bottom: 0; - } - .card-text span { - color: #ffffff66; - font-size: 12px; - top: 0px; - } - .fee-text{ - border-bottom: 1px solid #ffffff1c; - width: fit-content; - margin: auto; - line-height: 1.45; - padding: 0px 2px; - } - .fiat { - display: block; - font-size: 14px !important; - } + } + .card-text { + font-size: 22px; + margin-top: -9px; + position: relative; + } + .card-text.skeleton { + margin-top: 0px; + } +} + +.more-padding { + padding: 18px; +} + +.card-wrapper { + .card { + height: auto !important; + } + .card-body { + display: flex; + flex: inherit; + text-align: center; + flex-direction: column; + justify-content: space-around; + padding: 22px 20px; } } diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index 606bac5f1..c05c951b3 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -36,8 +36,8 @@ export class MiningDashboardComponent implements OnInit { return { 'totalReward': totalReward, - 'rewardPerTx': totalReward / totalTx, - 'feePerTx': totalFee / totalTx, + 'rewardPerTx': Math.round(totalReward / totalTx), + 'feePerTx': Math.round(totalFee / totalTx), } }) ); diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 1470e6211..790f38cfc 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -21,7 +21,10 @@
Height {{ diffChange.difficultyShorten }} - {{ diffChange.change >= 0 ? '+' : '' }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}% + {{ diffChange.change >= 0 ? '+' : '' }}{{ diffChange.change | amountShortener }}%
- +
@@ -143,7 +146,10 @@ - +
{{ vout.scriptpubkey_address | shortenString : 16 }} @@ -242,13 +248,13 @@ -
+
{{ tx.fee / (tx.weight / 4) | feeRounding }} sat/vB  – {{ tx.fee | number }} sat
- + -   - - + + +
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.scss b/frontend/src/app/components/transactions-list/transactions-list.component.scss index 5a96d2d3c..4f20be835 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.scss +++ b/frontend/src/app/components/transactions-list/transactions-list.component.scss @@ -1,10 +1,12 @@ .arrow-td { width: 20px; + padding-top: 0; + padding-bottom: 0; } .green, .grey, .red { font-size: 16px; - top: -2px; + top: 1px; position: relative; @media( min-width: 576px){ font-size: 19px; @@ -119,3 +121,11 @@ h2 { line-height: 1; } + +.highlight { + background-color: #181b2d; +} + +.summary { + margin-top: 10px; +} diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 7d3c5d0e4..63c5c7367 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -1,11 +1,11 @@ import { Component, OnInit, Input, ChangeDetectionStrategy, OnChanges, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; import { StateService } from '../../services/state.service'; -import { Observable, forkJoin, ReplaySubject, BehaviorSubject, merge, of, Subject, Subscription } from 'rxjs'; -import { Outspend, Transaction } from '../../interfaces/electrs.interface'; +import { Observable, forkJoin, ReplaySubject, BehaviorSubject, merge, Subscription } from 'rxjs'; +import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface'; import { ElectrsApiService } from '../../services/electrs-api.service'; import { environment } from 'src/environments/environment'; import { AssetsService } from 'src/app/services/assets.service'; -import { map, share, switchMap, tap } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { BlockExtended } from 'src/app/interfaces/node-api.interface'; @Component({ @@ -23,6 +23,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { @Input() transactionPage = false; @Input() errorUnblinded = false; @Input() outputIndex: number; + @Input() address: string = ''; @Output() loadMore = new EventEmitter(); @@ -98,6 +99,21 @@ export class TransactionsListComponent implements OnInit, OnChanges { if (this.outspends[i]) { return; } + + if (this.address) { + const addressIn = tx.vout + .filter((v: Vout) => v.scriptpubkey_address === this.address) + .map((v: Vout) => v.value || 0) + .reduce((a: number, b: number) => a + b, 0); + + const addressOut = tx.vin + .filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address) + .map((v: Vin) => v.prevout.value || 0) + .reduce((a: number, b: number) => a + b, 0); + + tx['addressValue'] = addressIn || -addressOut; + } + observableObject[i] = this.electrsApiService.getOutspends$(tx.txid); }); this.refreshOutspends$.next(observableObject); @@ -119,7 +135,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { } getTotalTxOutput(tx: Transaction) { - return tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b); + return tx.vout.map((v: Vout) => v.value || 0).reduce((a: number, b: number) => a + b); } switchCurrency() { diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index 803a7227e..ecd0ac598 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -72,7 +72,7 @@ export interface Vout { scriptpubkey: string; scriptpubkey_asm: string; scriptpubkey_type: string; - scriptpubkey_address: string; + scriptpubkey_address?: string; value: number; // Elements valuecommitment?: number; diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 858da3273..da228a833 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -151,6 +151,13 @@ export class ApiService { ); } + getBlocks$(from: number): Observable { + return this.httpClient.get( + this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks-extras` + + (from !== undefined ? `/${from}` : ``) + ); + } + getHistoricalDifficulty$(interval: string | undefined): Observable { return this.httpClient.get( this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty` + diff --git a/frontend/src/app/shared/pipes/amount-shortener.pipe.ts b/frontend/src/app/shared/pipes/amount-shortener.pipe.ts index 49b452cd9..5c58b7513 100644 --- a/frontend/src/app/shared/pipes/amount-shortener.pipe.ts +++ b/frontend/src/app/shared/pipes/amount-shortener.pipe.ts @@ -1,22 +1,39 @@ import { Pipe, PipeTransform } from '@angular/core'; +// https://medium.com/@thunderroid/angular-short-number-suffix-pipe-1k-2m-3b-dded4af82fb4 + @Pipe({ name: 'amountShortener' }) export class AmountShortenerPipe implements PipeTransform { - transform(num: number, ...args: number[]): unknown { - const digits = args[0] || 1; - const lookup = [ - { value: 1, symbol: '' }, - { value: 1e3, symbol: 'k' }, - { value: 1e6, symbol: 'M' }, - { value: 1e9, symbol: 'G' }, - { value: 1e12, symbol: 'T' }, - { value: 1e15, symbol: 'P' }, - { value: 1e18, symbol: 'E' } + transform(number: number, args?: any): any { + if (isNaN(number)) return null; // will only work value is a number + if (number === null) return null; + if (number === 0) return null; + let abs = Math.abs(number); + const rounder = Math.pow(10, 1); + const isNegative = number < 0; // will also work for Negetive numbers + let key = ''; + + const powers = [ + { key: 'E', value: 10e18 }, + { key: 'P', value: 10e15 }, + { key: 'T', value: 10e12 }, + { key: 'B', value: 10e9 }, + { key: 'M', value: 10e6 }, + { key: 'K', value: 1000 } ]; - const rx = /\.0+$|(\.[0-9]*[1-9])0+$/; - var item = lookup.slice().reverse().find((item) => num >= item.value); - return item ? (num / item.value).toFixed(digits).replace(rx, '$1') + item.symbol : '0'; + + for (let i = 0; i < powers.length; i++) { + let reduced = abs / powers[i].value; + reduced = Math.round(reduced * rounder) / rounder; + if (reduced >= 1) { + abs = reduced; + key = powers[i].key; + break; + } + } + + return (isNegative ? '-' : '') + abs + key; } -} +} \ No newline at end of file diff --git a/frontend/src/resources/profile/start9.png b/frontend/src/resources/profile/start9.png new file mode 100644 index 000000000..2cfe19ded Binary files /dev/null and b/frontend/src/resources/profile/start9.png differ diff --git a/production/install b/production/install index ad2eca9a4..ce823f10e 100755 --- a/production/install +++ b/production/install @@ -269,7 +269,7 @@ ELEMENTS_ELECTRS_HOME=${ELEMENTS_HOME}/electrs MEMPOOL_REPO_URL=https://github.com/mempool/mempool MEMPOOL_REPO_NAME=mempool -MEMPOOL_REPO_BRANCH=wiz/installer2 +MEMPOOL_REPO_BRANCH=master MEMPOOL_LATEST_RELEASE=v2.3.1 BITCOIN_REPO_URL=https://github.com/bitcoin/bitcoin @@ -326,7 +326,7 @@ FREEBSD_PKG=() FREEBSD_PKG+=(zsh sudo git screen curl wget calc neovim) FREEBSD_PKG+=(openssh-portable py38-pip rust llvm90) FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc libevent libtool pkgconf) -FREEBSD_PKG+=(nginx rsync py38-certbot-nginx ) +FREEBSD_PKG+=(nginx rsync py38-certbot-nginx mariadb105-server) ############################# ##### utility functions ##### @@ -819,8 +819,13 @@ osSudo "${ROOT_USER}" chown -R "${MEMPOOL_USER}:${MEMPOOL_GROUP}" "${MEMPOOL_HOM osSudo "${MEMPOOL_USER}" touch "${MEMPOOL_HOME}/.zshrc" echo "[*] Cloning Mempool repo from ${MEMPOOL_REPO_URL}" +osSudo "${MEMPOOL_USER}" git config --global pull.rebase true osSudo "${MEMPOOL_USER}" git config --global advice.detachedHead false osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}" +osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-build-all upgrade +osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-kill-all stop +osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-start-all start +osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-restart-all restart echo "[*] Installing nvm.sh from GitHub" osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh' @@ -1256,6 +1261,48 @@ if [ "${ELEMENTS_LIQUIDTESTNET_ENABLE}" = ON ];then osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}" fi +if [ "${BISQ_ENABLE}" = ON ];then + echo "[*] Creating Mempool instance for Bisq" + osSudo "${MEMPOOL_USER}" git config --global advice.detachedHead false + osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/bisq" + + echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Bisq" + osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}" +fi + +##### mariadb + +echo "[*] Adding MySQL configuration" +case $OS in + + FreeBSD) + osSudo "${ROOT_USER}" service mysql-server start + ;; + Debian) + osSudo "${ROOT_USER}" service mysql start + ;; +esac + +mysql << _EOF_ +create database mempool; +grant all on mempool.* to 'mempool'@'localhost' identified by 'mempool'; + +create database mempool_testnet; +grant all on mempool_testnet.* to 'mempool_testnet'@'localhost' identified by 'mempool_testnet'; + +create database mempool_signet; +grant all on mempool_signet.* to 'mempool_signet'@'localhost' identified by 'mempool_signet'; + +create database mempool_liquid; +grant all on mempool_liquid.* to 'mempool_liquid'@'localhost' identified by 'mempool_liquid'; + +create database mempool_liquidtestnet; +grant all on mempool_liquidtestnet.* to 'mempool_liquidtestnet'@'localhost' identified by 'mempool_liquidtestnet'; + +create database mempool_bisq; +grant all on mempool_bisq.* to 'mempool_bisq'@'localhost' identified by 'mempool_bisq'; +_EOF_ + ##### nginx echo "[*] Adding Nginx configuration" diff --git a/production/mempool-upgrade-all b/production/mempool-build-all similarity index 62% rename from production/mempool-upgrade-all rename to production/mempool-build-all index 3aecdbd93..75cd03313 100755 --- a/production/mempool-upgrade-all +++ b/production/mempool-build-all @@ -4,6 +4,12 @@ HOSTNAME=$(hostname) LOCKFILE="${HOME}/lock" REF=$(echo "${1:=origin/master}"|sed -e 's!:!/!') +# get rpc credentials +BITCOIN_RPC_USER=$(grep '^rpcuser' /bitcoin/bitcoin.conf | cut -d '=' -f2) +BITCOIN_RPC_PASS=$(grep '^rpcpassword' /bitcoin/bitcoin.conf | cut -d '=' -f2) +ELEMENTS_RPC_USER=$(grep '^rpcuser' /elements/elements.conf | cut -d '=' -f2) +ELEMENTS_RPC_PASS=$(grep '^rpcpassword' /elements/elements.conf | cut -d '=' -f2) + if [ -f "${LOCKFILE}" ];then echo "upgrade already running? check lockfile ${LOCKFILE}" exit 1 @@ -42,6 +48,9 @@ build_frontend() echo "[*] Building frontend for ${site}" [ -z "${HASH}" ] && exit 1 cd "$HOME/${site}/frontend" || exit 1 + if [ ! -e "mempool-frontend-config.json" ];then + cp "${HOME}/mempool/production/mempool-frontend-config.${site}.json" "mempool-frontend-config.json" + fi npm install --no-optional || exit 1 npm run build || exit 1 } @@ -52,6 +61,15 @@ build_backend() echo "[*] Building backend for ${site}" [ -z "${HASH}" ] && exit 1 cd "$HOME/${site}/backend" || exit 1 + if [ ! -e "mempool-config.json" ];then + cp "${HOME}/mempool/production/mempool-config.${site}.json" "mempool-config.json" + sed -i .orig \ + -e "s!__BITCOIN_RPC_USER__!${BITCOIN_RPC_USER}!" \ + -e "s!__BITCOIN_RPC_PASS__!${BITCOIN_RPC_PASS}!" \ + -e "s!__ELEMENTS_RPC_USER__!${ELEMENTS_RPC_USER}!" \ + -e "s!__ELEMENTS_RPC_PASS__!${ELEMENTS_RPC_PASS}!" \ + "mempool-config.json" + fi npm install --no-optional || exit 1 npm run build || exit 1 } @@ -60,16 +78,28 @@ ship_frontend() { local site="$1" cd "$HOME/${site}/frontend" || exit 1 - rsync -av ./dist/mempool/browser/* "${HOME}/public_html/${site}/" || exit 1 + mkdir -p "${HOME}/public_html/${site}/" + rsync -av "./dist/mempool/browser/" "${HOME}/public_html/${site}/" || exit 1 } export NVM_DIR="${HOME}/.nvm" source "${NVM_DIR}/nvm.sh" -for target in mainnet testnet signet liquid liquidtestnet bisq;do update_repo "${target}";done -for target in mainnet testnet signet liquid liquidtestnet bisq;do build_backend "${target}";done -for target in mainnet liquid bisq;do build_frontend "${target}";done -for target in mainnet liquid bisq;do ship_frontend "${target}";done +for target in mainnet testnet signet liquid liquidtestnet bisq;do + update_repo "${target}" +done + +for target in mainnet testnet signet liquid liquidtestnet bisq;do + build_backend "${target}" +done + +for target in mainnet liquid bisq;do + build_frontend "${target}" +done + +for target in mainnet liquid bisq;do + ship_frontend "${target}" +done echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev echo "${HOSTNAME} updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.ops diff --git a/production/mempool-config.bisq.json b/production/mempool-config.bisq.json index f4b2e1480..6eef30192 100644 --- a/production/mempool-config.bisq.json +++ b/production/mempool-config.bisq.json @@ -12,8 +12,8 @@ "MIN_PRIORITY": "debug" }, "CORE_RPC": { - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4000" @@ -22,9 +22,9 @@ "ENABLED": false, "HOST": "127.0.0.1", "PORT": 3306, - "USERNAME": "bmempool", - "PASSWORD": "bmempool", - "DATABASE": "bmempool" + "USERNAME": "mempool_bisq", + "PASSWORD": "mempool_bisq", + "DATABASE": "mempool_bisq" }, "STATISTICS": { "ENABLED": true, diff --git a/production/mempool-config.liquid.json b/production/mempool-config.liquid.json index dc29af452..1d3e30731 100644 --- a/production/mempool-config.liquid.json +++ b/production/mempool-config.liquid.json @@ -13,13 +13,13 @@ }, "CORE_RPC": { "PORT": 7041, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__ELEMENTS_RPC_USER__", + "PASSWORD": "__ELEMENTS_RPC_PASS__" }, "SECOND_CORE_RPC": { "PORT": 8332, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4001" @@ -28,9 +28,9 @@ "ENABLED": true, "HOST": "127.0.0.1", "PORT": 3306, - "USERNAME": "lmempool", - "PASSWORD": "lmempool", - "DATABASE": "lmempool" + "USERNAME": "mempool_liquid", + "PASSWORD": "mempool_liquid", + "DATABASE": "mempool_liquid" }, "STATISTICS": { "ENABLED": true, diff --git a/production/mempool-config.liquidtestnet.json b/production/mempool-config.liquidtestnet.json index 17081a8ea..8805f4045 100644 --- a/production/mempool-config.liquidtestnet.json +++ b/production/mempool-config.liquidtestnet.json @@ -13,13 +13,13 @@ }, "CORE_RPC": { "PORT": 7040, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__ELEMENTS_RPC_USER__", + "PASSWORD": "__ELEMENTS_RPC_PASS__" }, "SECOND_CORE_RPC": { "PORT": 8332, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4004" diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index 49ce69479..2be5c565c 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -15,13 +15,13 @@ }, "CORE_RPC": { "PORT": 8332, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "SECOND_CORE_RPC": { "PORT": 8302, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4000" diff --git a/production/mempool-config.signet.json b/production/mempool-config.signet.json index 57246160c..5fb395dbe 100644 --- a/production/mempool-config.signet.json +++ b/production/mempool-config.signet.json @@ -13,8 +13,8 @@ }, "CORE_RPC": { "PORT": 38332, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4003" @@ -23,9 +23,9 @@ "ENABLED": true, "HOST": "127.0.0.1", "PORT": 3306, - "USERNAME": "smempool", - "PASSWORD": "smempool", - "DATABASE": "smempool" + "USERNAME": "mempool_signet", + "PASSWORD": "mempool_signet", + "DATABASE": "mempool_signet" }, "STATISTICS": { "ENABLED": true, diff --git a/production/mempool-config.testnet.json b/production/mempool-config.testnet.json index e96689d06..b17fcd98e 100644 --- a/production/mempool-config.testnet.json +++ b/production/mempool-config.testnet.json @@ -13,8 +13,8 @@ }, "CORE_RPC": { "PORT": 18332, - "USERNAME": "foo", - "PASSWORD": "bar" + "USERNAME": "__BITCOIN_RPC_USER__", + "PASSWORD": "__BITCOIN_RPC_PASS__" }, "ESPLORA": { "REST_API_URL": "http://127.0.0.1:4002" @@ -23,9 +23,9 @@ "ENABLED": true, "HOST": "127.0.0.1", "PORT": 3306, - "USERNAME": "tmempool", - "PASSWORD": "tmempool", - "DATABASE": "tmempool" + "USERNAME": "mempool_testnet", + "PASSWORD": "mempool_testnet", + "DATABASE": "mempool_testnet" }, "STATISTICS": { "ENABLED": true, diff --git a/production/mempool-frontend-config.bisq.json b/production/mempool-frontend-config.bisq.json new file mode 100644 index 000000000..3ddb807f9 --- /dev/null +++ b/production/mempool-frontend-config.bisq.json @@ -0,0 +1,14 @@ +{ + "BASE_MODULE": "bisq", + "OFFICIAL_MEMPOOL_SPACE": true, + "TESTNET_ENABLED": true, + "LIQUID_ENABLED": true, + "LIQUID_TESTNET_ENABLED": true, + "BISQ_ENABLED": true, + "BISQ_SEPARATE_BACKEND": true, + "SIGNET_ENABLED": true, + "MEMPOOL_WEBSITE_URL": "https://mempool.space", + "LIQUID_WEBSITE_URL": "https://liquid.network", + "BISQ_WEBSITE_URL": "https://bisq.markets", + "ITEMS_PER_PAGE": 25 +} diff --git a/production/mempool-frontend-config.json b/production/mempool-frontend-config.json deleted file mode 100644 index 1a5849f52..000000000 --- a/production/mempool-frontend-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "TESTNET_ENABLED": true, - "LIQUID_ENABLED": true, - "LIQUID_TESTNET_ENABLED": true, - "BISQ_ENABLED": true, - "BISQ_SEPARATE_BACKEND": true, - "SIGNET_ENABLED": true, - "ITEMS_PER_PAGE": 25, - "KEEP_BLOCKS_AMOUNT": 8, - "NGINX_PROTOCOL": "http", - "NGINX_HOSTNAME": "127.0.0.1", - "NGINX_PORT": "80" -} diff --git a/production/mempool-frontend-config.liquid.json b/production/mempool-frontend-config.liquid.json new file mode 100644 index 000000000..6a7c79d52 --- /dev/null +++ b/production/mempool-frontend-config.liquid.json @@ -0,0 +1,17 @@ +{ + "BASE_MODULE": "liquid", + "OFFICIAL_MEMPOOL_SPACE": true, + "TESTNET_ENABLED": true, + "LIQUID_ENABLED": true, + "LIQUID_TESTNET_ENABLED": true, + "BISQ_ENABLED": true, + "BISQ_SEPARATE_BACKEND": true, + "SIGNET_ENABLED": true, + "MEMPOOL_WEBSITE_URL": "https://mempool.space", + "LIQUID_WEBSITE_URL": "https://liquid.network", + "BISQ_WEBSITE_URL": "https://bisq.markets", + "ITEMS_PER_PAGE": 25, + "BLOCK_WEIGHT_UNITS": 300000, + "MEMPOOL_BLOCKS_AMOUNT": 2, + "KEEP_BLOCKS_AMOUNT": 16 +} diff --git a/production/mempool-frontend-config.mainnet.json b/production/mempool-frontend-config.mainnet.json new file mode 100644 index 000000000..525cdb115 --- /dev/null +++ b/production/mempool-frontend-config.mainnet.json @@ -0,0 +1,13 @@ +{ + "OFFICIAL_MEMPOOL_SPACE": true, + "TESTNET_ENABLED": true, + "LIQUID_ENABLED": true, + "LIQUID_TESTNET_ENABLED": true, + "BISQ_ENABLED": true, + "BISQ_SEPARATE_BACKEND": true, + "SIGNET_ENABLED": true, + "MEMPOOL_WEBSITE_URL": "https://mempool.space", + "LIQUID_WEBSITE_URL": "https://liquid.network", + "BISQ_WEBSITE_URL": "https://bisq.markets", + "ITEMS_PER_PAGE": 25 +} diff --git a/production/mempool-install-all b/production/mempool-install-all deleted file mode 100755 index 6fc302dcd..000000000 --- a/production/mempool-install-all +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/local/bin/zsh -export NVM_DIR="$HOME/.nvm" -source "$NVM_DIR/nvm.sh" -for site in mainnet liquid testnet bisq signet liquidtestnet -do - #git clone https://github.com/mempool/mempool "${HOME}/${site}" - mkdir -p "${HOME}/public_html/${site}/" - - cd "${HOME}/${site}/backend/" - cp "../production/mempool-config.${site}.json" "mempool-config.json" - - npm install - npm run build - - if [ "${site}" = "mainnet" ] - then - cd "${HOME}/${site}/frontend/" - npm install - npm run build - rsync -av ./dist/mempool/browser/* "${HOME}/public_html/${site}/" - fi -done