diff --git a/README.md b/README.md index 0b53bcb0a..e65422775 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,22 @@ # The Mempool Open Source Project -Mempool is the fully featured mempool visualizer and block explorer website and API service running on [mempool.space](https://mempool.space/). The instructions below are for most users at home running on low-powered Raspberry Pi devices, but if you want to run a production website on a powerful server, see the [production setup guide](https://github.com/mempool/mempool/tree/master/production) +Mempool is the fully featured visualizer, explorer, and API service running on [mempool.space](https://mempool.space/), an open source project developed and operated for the benefit of the Bitcoin community, with a focus on the emerging transaction fee market to help our transition into a multi-layer ecosystem. -![mempool](https://pbs.twimg.com/media/Ei8p_flUcAEjfXE?format=jpg&name=4096x4096) +![mempool](https://mempool.space/resources/screenshots/v2.1.0-dashboard.png) -# Installation +## Installation Methods + +Mempool can be self-hosted on a wide variety of your own hardware, ranging from a simple one-click installation on a Raspberry Pi distro, all the way to an advanced high availability cluster of powerful servers for a production instance. We support the following installation methods, ranked in order from simple to advanced: + +1) One-click installation on: [Umbrel](https://github.com/getumbrel/umbrel), [RaspiBlitz](https://github.com/rootzoll/raspiblitz), [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo), or [MyNode](https://github.com/mynodebtc/mynode). +2) [Docker installation on Linux using docker-compose](https://github.com/mempool/mempool/tree/master/docker) +3) [Manual installation on Linux or FreeBSD](https://github.com/mempool/mempool#manual-installation) +4) [Production installation on a powerful FreeBSD server](https://github.com/mempool/mempool/tree/master/production) +5) [High Availability cluster using powerful FreeBSD servers](https://github.com/mempool/mempool/tree/master/production#high-availability) + +# Manual Installation + +The following instructions are for a manual installation on Linux or FreeBSD. The file and directory paths may need to be changed to match your OS. ## Dependencies diff --git a/backend/.gitignore b/backend/.gitignore index 32c205746..c4339712c 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -41,14 +41,3 @@ testem.log #System Files .DS_Store Thumbs.db - -cache.json -cache1.json -cache2.json -cache3.json -cache4.json -cache5.json -cache6.json -cache7.json -cache8.json -cache9.json diff --git a/backend/cache/.gitignore b/backend/cache/.gitignore new file mode 100644 index 000000000..a6c57f5fb --- /dev/null +++ b/backend/cache/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index e53df38f2..82bcfbe56 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -6,7 +6,8 @@ "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", "POLL_RATE_MS": 2000, - "CACHE_DIR": "./" + "CACHE_DIR": "./cache", + "CLEAR_PROTECTION_MINUTES": 20 }, "CORE_RPC": { "HOST": "127.0.0.1", diff --git a/backend/package.json b/backend/package.json index ae5f3dd81..a8add53e8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,7 +23,8 @@ "ng": "./node_modules/@angular/cli/bin/ng", "tsc": "./node_modules/typescript/bin/tsc", "build": "npm run tsc", - "start": "node --max-old-space-size=4096 dist/index.js", + "start": "node --max-old-space-size=2048 dist/index.js", + "start-production": "node --max-old-space-size=4096 dist/index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index 2956216c8..cbdff49f0 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -5,11 +5,13 @@ import memPool from './mempool'; import blocks from './blocks'; import logger from '../logger'; import config from '../config'; +import { TransactionExtended } from '../mempool.interfaces'; class DiskCache { - private static FILE_NAME = config.MEMPOOL.CACHE_DIR + 'cache.json'; - private static FILE_NAMES = config.MEMPOOL.CACHE_DIR + 'cache{number}.json'; - private static CHUNK_SIZE = 10000; + private static FILE_NAME = config.MEMPOOL.CACHE_DIR + '/cache.json'; + private static FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/cache{number}.json'; + private static CHUNK_FILES = 25; + constructor() { } async $saveCacheToDisk(): Promise { @@ -18,19 +20,24 @@ class DiskCache { } try { logger.debug('Writing mempool and blocks data to disk cache (async)...'); - const mempoolChunk_1 = Object.fromEntries(Object.entries(memPool.getMempool()).slice(0, DiskCache.CHUNK_SIZE)); + + const mempool = memPool.getMempool(); + const mempoolArray: TransactionExtended[] = []; + for (const tx in mempool) { + mempoolArray.push(mempool[tx]); + } + + const chunkSize = Math.floor(mempoolArray.length / DiskCache.CHUNK_FILES); + await fsPromises.writeFile(DiskCache.FILE_NAME, JSON.stringify({ blocks: blocks.getBlocks(), - mempool: mempoolChunk_1 + mempool: {}, + mempoolArray: mempoolArray.splice(0, chunkSize), }), {flag: 'w'}); - for (let i = 1; i < 10; i++) { - const mempoolChunk = Object.fromEntries( - Object.entries(memPool.getMempool()).slice( - DiskCache.CHUNK_SIZE * i, i === 9 ? undefined : DiskCache.CHUNK_SIZE * i + DiskCache.CHUNK_SIZE - ) - ); + for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { await fsPromises.writeFile(DiskCache.FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({ - mempool: mempoolChunk + mempool: {}, + mempoolArray: mempoolArray.splice(0, chunkSize), }), {flag: 'w'}); } logger.debug('Mempool and blocks data saved to disk cache'); @@ -49,13 +56,24 @@ class DiskCache { if (cacheData) { logger.info('Restoring mempool and blocks data from disk cache'); data = JSON.parse(cacheData); + if (data.mempoolArray) { + for (const tx of data.mempoolArray) { + data.mempool[tx.txid] = tx; + } + } } - for (let i = 1; i < 10; i++) { + for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { const fileName = DiskCache.FILE_NAMES.replace('{number}', i.toString()); if (fs.existsSync(fileName)) { const cacheData2 = JSON.parse(fs.readFileSync(fileName, 'utf8')); - Object.assign(data.mempool, cacheData2.mempool); + if (cacheData2.mempoolArray) { + for (const tx of cacheData2.mempoolArray) { + data.mempool[tx.txid] = tx; + } + } else { + Object.assign(data.mempool, cacheData2.mempool); + } } } diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 721ec4e2b..83367a6ac 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -10,7 +10,6 @@ import loadingIndicators from './loading-indicators'; class Mempool { private static WEBSOCKET_REFRESH_RATE_MS = 10000; - private static CLEAR_PROTECTION_MINUTES = 10; private inSync: boolean = false; private mempoolCache: { [txId: string]: TransactionExtended } = {}; private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, @@ -134,7 +133,6 @@ class Mempool { // Prevent mempool from clear on bitcoind restart by delaying the deletion if (this.mempoolProtection === 0 - && config.MEMPOOL.BACKEND === 'esplora' && currentMempoolSize > 20000 && transactions.length / currentMempoolSize <= 0.80 ) { @@ -144,7 +142,7 @@ class Mempool { setTimeout(() => { this.mempoolProtection = 2; logger.warn('Mempool clear protection resumed.'); - }, 1000 * 60 * Mempool.CLEAR_PROTECTION_MINUTES); + }, 1000 * 60 * config.MEMPOOL.CLEAR_PROTECTION_MINUTES); } let newMempool = {}; diff --git a/backend/src/config.ts b/backend/src/config.ts index 1d46ccfcb..1b3db8142 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -9,6 +9,7 @@ interface IConfig { API_URL_PREFIX: string; POLL_RATE_MS: number; CACHE_DIR: string; + CLEAR_PROTECTION_MINUTES: number; }; ESPLORA: { REST_API_URL: string; @@ -61,7 +62,8 @@ const defaults: IConfig = { 'SPAWN_CLUSTER_PROCS': 0, 'API_URL_PREFIX': '/api/v1/', 'POLL_RATE_MS': 2000, - 'CACHE_DIR': './' + 'CACHE_DIR': './cache', + 'CLEAR_PROTECTION_MINUTES': 20, }, 'ESPLORA': { 'REST_API_URL': 'http://127.0.0.1:3000', diff --git a/docker/README.md b/docker/README.md index 0a65eba0b..fa8f44bb2 100644 --- a/docker/README.md +++ b/docker/README.md @@ -8,7 +8,7 @@ In an empty dir create 2 sub-dirs mkdir -p data mysql/data mysql/db-scripts ``` -In the mysql/db-scripts sub-dir add the mariadb-structure.sql file from the mempool repo +In the `mysql/db-scripts` sub-dir add the `mariadb-structure.sql` file from the mempool repo Your dir should now look like that: @@ -28,7 +28,7 @@ data db-scripts mariadb-structure.sql ``` -In the main dir add the following docker-compose.yml +In the main dir add the following `docker-compose.yml` ```bash version: "3.7" diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 8b668e0de..6ffc50bc8 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -5,7 +5,6 @@ "BISQ_SEPARATE_BACKEND": false, "ITEMS_PER_PAGE": 10, "KEEP_BLOCKS_AMOUNT": 8, - "SPONSORS_ENABLED": false, "NGINX_PROTOCOL": "http", "NGINX_HOSTNAME": "127.0.0.1", "NGINX_PORT": "80" diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 508eb9509..c86101531 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -1,7 +1,7 @@

- +
@@ -10,10 +10,10 @@
-

About the project

+

The Mempool Open Source Project

-

The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.

+

An explorer and API developed and operated for the Bitcoin community, focusing on the emerging transaction fee market to help our transition into a multi-layer ecosystem, without any ads, altcoins, or third-party trackers.

@@ -141,9 +141,9 @@
- +
- +
@@ -189,6 +189,12 @@ + + + + + + diff --git a/frontend/src/app/components/about/about.component.ts b/frontend/src/app/components/about/about.component.ts index 4e598e693..6d7b3dbc1 100644 --- a/frontend/src/app/components/about/about.component.ts +++ b/frontend/src/app/components/about/about.component.ts @@ -20,7 +20,7 @@ export class AboutComponent implements OnInit, OnDestroy { donationStatus = 1; sponsors$: Observable; donationObj: any; - sponsorsEnabled = this.stateService.env.SPONSORS_ENABLED; + sponsorsEnabled = this.stateService.env.OFFICIAL_MEMPOOL_SPACE; sponsors = null; requestSubscription: Subscription | undefined; diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss index f03967281..b456c3cd1 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss @@ -11,6 +11,10 @@ z-index: 10; } +.blockLink:hover { + text-decoration: none; +} + .mined-block { position: absolute; top: 0px; diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index dcac245e7..ffe810cc7 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -3,7 +3,7 @@