Merge pull request #5040 from mempool/mononaut/testnet4

test
This commit is contained in:
wiz 2024-05-07 02:50:37 +09:00 committed by GitHub
commit d92bf14b50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 484 additions and 66 deletions

View file

@ -53,6 +53,44 @@ let routes: Routes = [
},
]
},
{
path: 'testnet4',
children: [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
},
{
path: '',
loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule),
data: { preload: true },
},
{
path: 'wallet',
children: [],
component: AddressGroupComponent,
data: {
networkSpecific: true,
}
},
{
path: 'status',
data: { networks: ['bitcoin', 'liquid'] },
component: StatusViewComponent
},
{
path: '',
loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
},
{
path: '**',
redirectTo: '/testnet4'
},
]
},
{
path: 'signet',
children: [

View file

@ -189,22 +189,22 @@ export const specialBlocks = {
'0': {
labelEvent: 'Genesis',
labelEventCompleted: 'The Genesis of Bitcoin',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'210000': {
labelEvent: 'Bitcoin\'s 1st Halving',
labelEventCompleted: 'Block Subsidy has halved to 25 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'420000': {
labelEvent: 'Bitcoin\'s 2nd Halving',
labelEventCompleted: 'Block Subsidy has halved to 12.5 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'630000': {
labelEvent: 'Bitcoin\'s 3rd Halving',
labelEventCompleted: 'Block Subsidy has halved to 6.25 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'709632': {
labelEvent: 'Taproot 🌱 activation',
@ -214,62 +214,62 @@ export const specialBlocks = {
'840000': {
labelEvent: 'Bitcoin\'s 4th Halving',
labelEventCompleted: 'Block Subsidy has halved to 3.125 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'1050000': {
labelEvent: 'Bitcoin\'s 5th Halving',
labelEventCompleted: 'Block Subsidy has halved to 1.5625 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'1260000': {
labelEvent: 'Bitcoin\'s 6th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.78125 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'1470000': {
labelEvent: 'Bitcoin\'s 7th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.390625 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'1680000': {
labelEvent: 'Bitcoin\'s 8th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.1953125 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'1890000': {
labelEvent: 'Bitcoin\'s 9th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.09765625 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'2100000': {
labelEvent: 'Bitcoin\'s 10th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.04882812 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'2310000': {
labelEvent: 'Bitcoin\'s 11th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.02441406 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'2520000': {
labelEvent: 'Bitcoin\'s 12th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.01220703 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'2730000': {
labelEvent: 'Bitcoin\'s 13th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.00610351 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'2940000': {
labelEvent: 'Bitcoin\'s 14th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.00305175 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
},
'3150000': {
labelEvent: 'Bitcoin\'s 15th Halving',
labelEventCompleted: 'Block Subsidy has halved to 0.00152587 BTC per block',
networks: ['mainnet', 'testnet'],
networks: ['mainnet', 'testnet', 'testnet4'],
}
};

View file

@ -266,6 +266,11 @@ const featureActivation = {
segwit: 872730,
taproot: 2032291,
},
testnet4: {
rbf: 0,
segwit: 0,
taproot: 0,
},
signet: {
rbf: 0,
segwit: 0,

View file

@ -43,5 +43,6 @@
<ng-template [ngIf]="network === 'liquid' && !forceBtc">L-</ng-template>
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
<ng-template [ngIf]="network === 'testnet'">t</ng-template>
<ng-template [ngIf]="network === 'testnet4'">t</ng-template>
<ng-template [ngIf]="network === 'signet'">s</ng-template>
</ng-template>

View file

@ -70,6 +70,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
liquid: ['var(--liquid)', 'var(--testnet-alt)'],
'liquidtestnet': ['var(--liquidtestnet)', 'var(--liquidtestnet-alt)'],
testnet: ['var(--testnet)', 'var(--testnet-alt)'],
testnet4: ['var(--testnet)', 'var(--testnet-alt)'],
signet: ['var(--signet)', 'var(--signet-alt)'],
};

View file

@ -36,6 +36,7 @@ export class ClockComponent implements OnInit {
liquid: ['#116761', '#183550'],
'liquidtestnet': ['#494a4a', '#272e46'],
testnet: ['#1d486f', '#183550'],
testnet4: ['#1d486f', '#183550'],
signet: ['#6f1d5d', '#471850'],
};

View file

@ -51,7 +51,8 @@
<div ngbDropdownMenu [ngClass]="{'dropdown-menu-right' : isMobile}">
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + (networkPaths['mainnet'] || '')" ngbDropdownItem class="mainnet"><app-svg-images name="bitcoin" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Mainnet</a>
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + (networkPaths['signet'] || '/signet')" ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet"><app-svg-images name="signet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Signet</a>
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + (networkPaths['testnet'] || '/testnet')" ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet"><app-svg-images name="testnet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet</a>
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + (networkPaths['testnet'] || '/testnet')" ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet"><app-svg-images name="testnet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet3</a>
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + (networkPaths['testnet4'] || '/testnet4')" ngbDropdownItem *ngIf="env.TESTNET4_ENABLED" class="testnet"><app-svg-images name="testnet4" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet4 <span class="badge badge-pill badge-warning beta-network" i18n="beta">beta</span></a>
<h6 class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
<a ngbDropdownItem class="liquid mr-1" [class.active]="network.val === 'liquid'" [routerLink]="networkPaths['liquid'] || '/'"><app-svg-images name="liquid" width="22" height="22" viewBox="0 0 125 125" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Liquid</a>
<a ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquidtestnet'" [routerLink]="networkPaths['liquidtestnet'] || '/testnet'"><app-svg-images name="liquidtestnet" width="22" height="22" viewBox="0 0 125 125" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Liquid Testnet</a>

View file

@ -169,4 +169,8 @@ nav {
margin-left: 5px;
margin-right: 0px;
}
}
.beta-network {
font-size: 8px;
}

View file

@ -15,7 +15,8 @@
<div [ngSwitch]="network.val">
<span *ngSwitchCase="'signet'" class="network signet"><app-svg-images name="signet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Signet</span>
<span *ngSwitchCase="'testnet'" class="network testnet"><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet</span>
<span *ngSwitchCase="'testnet'" class="network testnet"><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet3</span>
<span *ngSwitchCase="'testnet4'" class="network testnet"><app-svg-images name="testnet4" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet4</span>
<span *ngSwitchCase="'liquid'" class="network liquid"><app-svg-images name="liquid" width="35" height="35" viewBox="0 0 125 125" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Mainnet</span>
<span *ngSwitchCase="'liquidtestnet'" class="network liquidtestnet"><app-svg-images name="liquidtestnet" width="35" height="35" viewBox="0 0 125 125" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet</span>
<span *ngSwitchDefault class="network mainnet"><app-svg-images name="bitcoin" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Mainnet</span>

View file

@ -58,14 +58,15 @@
</ng-container>
</a>
<div (window:resize)="onResize()" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.LIQUID_TESTNET_ENABLED">
<div (window:resize)="onResize()" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.TESTNET4_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.LIQUID_TESTNET_ENABLED">
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split d-flex justify-content-center align-items-center" aria-haspopup="true">
<app-svg-images class="d-flex justify-content-center align-items-center current-network-svg" [name]="network.val === '' ? 'bitcoin' : network.val" width="20" height="20" viewBox="0 0 65 65"></app-svg-images>
</button>
<div ngbDropdownMenu [ngClass]="{'dropdown-menu-right' : isMobile}">
<a ngbDropdownItem class="mainnet" [routerLink]="networkPaths['mainnet'] || '/'"><app-svg-images name="bitcoin" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Mainnet</a>
<a ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet" [class.active]="network.val === 'signet'" [routerLink]="networkPaths['signet'] || '/signet'"><app-svg-images name="signet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Signet</a>
<a ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" [routerLink]="networkPaths['testnet'] || '/testnet'"><app-svg-images name="testnet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet</a>
<a ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" [routerLink]="networkPaths['testnet'] || '/testnet'"><app-svg-images name="testnet" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet3</a>
<a ngbDropdownItem *ngIf="env.TESTNET4_ENABLED" class="testnet" [class.active]="network.val === 'testnet4'" [routerLink]="networkPaths['testnet4'] || '/testnet4'"><app-svg-images name="testnet4" width="22" height="22" viewBox="0 0 65 65" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Testnet4 <span class="badge badge-pill badge-warning beta-network" i18n="beta">beta</span></a>
<h6 *ngIf="env.LIQUID_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + (networkPaths['liquid'] || '')" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'"><app-svg-images name="liquid" width="22" height="22" viewBox="0 0 125 125" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Liquid</a>
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + (networkPaths['liquidtestnet'] || '/testnet')" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquid'"><app-svg-images name="liquidtestnet" width="22" height="22" viewBox="0 0 125 125" style="width: 25px; height: 25px;" class="mainnet mr-1"></app-svg-images> Liquid Testnet</a>
@ -87,7 +88,7 @@
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
<a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="mining.mining-dashboard" title="Mining Dashboard"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-lightning" *ngIf="stateService.env.LIGHTNING">
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-lightning" *ngIf="stateService.env.LIGHTNING && lightningNetworks.includes(stateService.network)">
<a class="nav-link" [routerLink]="['/lightning' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'bolt']" [fixedWidth]="true" i18n-title="master-page.lightning" title="Lightning Explorer"></fa-icon>
</a>
</li>
@ -114,7 +115,7 @@
<div class="empty-sidenav"><!-- empty sidenav needed to push footer down the screen --></div>
<div class="flex-grow-1 d-flex flex-column">
<app-testnet-alert *ngIf="network.val === 'testnet' || network.val === 'signet'"></app-testnet-alert>
<app-testnet-alert *ngIf="network.val === 'testnet' || network.val === 'testnet4' || network.val === 'signet'"></app-testnet-alert>
<main style="min-width: 375px; max-width: 100vw">
<router-outlet></router-outlet>

View file

@ -243,6 +243,10 @@ nav {
}
}
.beta-network {
font-size: 8px;
}
.current-network-svg {
width: 20px;
height: 20px;

View file

@ -27,6 +27,7 @@ export class MasterPageComponent implements OnInit, OnDestroy {
subdomain = '';
networkPaths: { [network: string]: string };
networkPaths$: Observable<Record<string, string>>;
lightningNetworks = ['', 'mainnet', 'bitcoin', 'testnet', 'signet'];
footerVisible = true;
user: any = undefined;
servicesEnabled = false;

View file

@ -179,7 +179,7 @@ export class SearchFormComponent implements OnInit {
const lightningResults = result[1];
// Do not show date and timestamp results for liquid
const isNetworkBitcoin = this.network === '' || this.network === 'testnet' || this.network === 'signet';
const isNetworkBitcoin = this.network === '' || this.network === 'testnet' || this.network === 'testnet4' || this.network === 'signet';
const matchesBlockHeight = this.regexBlockheight.test(searchText) && parseInt(searchText) <= this.stateService.latestBlockHeight;
const matchesDateTime = this.regexDate.test(searchText) && new Date(searchText).toString() !== 'Invalid Date' && new Date(searchText).getTime() <= Date.now() && isNetworkBitcoin;

View file

@ -60,6 +60,9 @@
<ng-container *ngSwitchCase="'testnet'">
<ng-component *ngTemplateOutlet="bitcoinLogo; context: {$implicit: '#5fd15c', width, height, viewBox}"></ng-component>
</ng-container>
<ng-container *ngSwitchCase="'testnet4'">
<ng-component *ngTemplateOutlet="bitcoinLogo; context: {$implicit: '#5fd15c', width, height, viewBox}"></ng-component>
</ng-container>
<ng-container *ngSwitchCase="'liquid'">
<ng-component *ngTemplateOutlet="liquidLogo; context: {$implicit: '', width, height, viewBox, color1: '#2cccbf', color2: '#9ef2ed'}"></ng-component>
</ng-container>

View file

@ -5,7 +5,8 @@
<app-svg-images name="officialMempoolSpace" style="width: 144px; height: 36px" width="500" height="126" viewBox="0 0 500 126"></app-svg-images>
<div [ngSwitch]="network" class="network-label">
<span *ngSwitchCase="'signet'" class="network signet"><span class="name">Bitcoin Signet</span><app-svg-images name="signet" width="35" height="35" viewBox="0 0 65 65" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchCase="'testnet'" class="network testnet"><span class="name">Bitcoin Testnet</span><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchCase="'testnet'" class="network testnet"><span class="name">Bitcoin Testnet3</span><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchCase="'testnet4'" class="network testnet"><span class="name">Bitcoin Testnet4</span><app-svg-images name="testnet4" width="35" height="35" viewBox="0 0 65 65" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchCase="'liquid'" class="network liquid"><span class="name">Liquid</span><app-svg-images name="liquid" width="35" height="35" viewBox="0 0 125 125" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchCase="'liquidtestnet'" class="network liquidtestnet"><span class="name">Liquid Testnet</span><app-svg-images name="liquidtestnet" width="35" height="35" viewBox="0 0 125 125" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>
<span *ngSwitchDefault class="network mainnet"><span class="name">Bitcoin</span><app-svg-images name="bitcoin" width="35" height="35" viewBox="0 0 65 65" style="display: inline-block" class="mainnet ml-2"></app-svg-images></span>

View file

@ -91,6 +91,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
'liquidtestnet': ['#d2d2d2', '#979797', '#d2d2d200'],
// testnet: ['#1d486f', '#183550'],
testnet: ['#4edf77', '#10a0af', '#4edf7700'],
testnet4: ['#4edf77', '#10a0af', '#4edf7700'],
// signet: ['#6f1d5d', '#471850'],
signet: ['#d24fc8', '#a84fd2', '#d24fc800'],
};

View file

@ -1,5 +1,6 @@
const bitcoinNetworks = ["", "testnet", "signet"];
const bitcoinNetworks = ["", "testnet", "testnet4", "signet"];
const liquidNetworks = ["liquid", "liquidtestnet"];
const lightningNetworks = ["", "testnet", "signet"];
const miningTimeIntervals = "<code>24h</code>, <code>3d</code>, <code>1w</code>, <code>1m</code>, <code>3m</code>, <code>6m</code>, <code>1y</code>, <code>2y</code>, <code>3y</code>";
const emptyCodeSample = {
@ -6513,7 +6514,7 @@ export const restApiDocsData = [
category: "lightning",
fragment: "lightning",
title: "Lightning",
showConditions: bitcoinNetworks
showConditions: lightningNetworks
},
{
type: "endpoint",
@ -6525,7 +6526,7 @@ export const restApiDocsData = [
default: "<p>Returns network-wide stats such as total number of channels and nodes, total capacity, and average/median fee figures.</p><p>Pass one of the following for <code>:interval</code>: <code>latest</code>, <code>24h</code>, <code>3d</code>, <code>1w</code>, <code>1m</code>, <code>3m</code>, <code>6m</code>, <code>1y</code>, <code>2y</code>, <code>3y</code>.</p>"
},
urlString: "/v1/lightning/statistics/:interval",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -6621,7 +6622,7 @@ export const restApiDocsData = [
default: "<p>Returns Lightning nodes and channels that match a full-text, case-insensitive search <code>:query</code> across node aliases, node pubkeys, channel IDs, and short channel IDs.</p>"
},
urlString: "/v1/lightning/search?searchText=:query",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -6706,7 +6707,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of Lightning nodes running on clearnet in the requested <code>:country</code>, where <code>:country</code> is an ISO Alpha-2 country code.</p>"
},
urlString: "/v1/lightning/nodes/country/:country",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -6928,7 +6929,7 @@ export const restApiDocsData = [
default: "<p>Returns aggregate capacity and number of clearnet nodes per country. Capacity figures are in satoshis.</p>"
},
urlString: "/v1/lightning/nodes/countries",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7072,7 +7073,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of nodes hosted by a specified <code>:isp</code>, where <code>:isp</code> is an ISP's ASN.</p>"
},
urlString: "/v1/lightning/nodes/isp/:isp",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7191,7 +7192,7 @@ export const restApiDocsData = [
default: "<p>Returns aggregate capacity, number of nodes, and number of channels per ISP. Capacity figures are in satoshis.</p>"
},
urlString: "/v1/lightning/nodes/isp-ranking",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7303,7 +7304,7 @@ export const restApiDocsData = [
default: "<p>Returns two lists of the top 100 nodes: one ordered by liquidity (aggregate channel capacity) and the other ordered by connectivity (number of open channels).</p>"
},
urlString: "/v1/lightning/nodes/rankings",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7426,7 +7427,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of the top 100 nodes by liquidity (aggregate channel capacity).</p>"
},
urlString: "/v1/lightning/nodes/rankings/liquidity",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7623,7 +7624,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of the top 100 nodes by connectivity (number of open channels).</p>"
},
urlString: "/v1/lightning/nodes/rankings/connectivity",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -7819,7 +7820,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of the top 100 oldest nodes.</p>"
},
urlString: "/v1/lightning/nodes/rankings/age",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8006,7 +8007,7 @@ export const restApiDocsData = [
default: "<p>Returns details about a node with the given <code>:pubKey</code>.</p>"
},
urlString: "/v1/lightning/nodes/:pubKey",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8170,7 +8171,7 @@ export const restApiDocsData = [
default: "<p>Returns historical stats for a node with the given <code>:pubKey</code>.</p>"
},
urlString: "/v1/lightning/nodes/:pubKey/statistics",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8268,7 +8269,7 @@ export const restApiDocsData = [
default: "<p>Returns info about a Lightning channel with the given <code>:channelId</code>.</p>"
},
urlString: "/v1/lightning/channels/:channelId",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8433,7 +8434,7 @@ export const restApiDocsData = [
default: "<p>Returns channels that correspond to the given <code>:txid</code> (multiple transaction IDs can be specified).</p>"
},
urlString: "/v1/lightning/channels/txids?txId[]=:txid",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8634,7 +8635,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of a node's channels given its <code>:pubKey</code>. Ten channels are returned at a time. Use <code>:index</code> for paging. <code>:channelStatus</code> can be <code>open</code>, <code>active</code>, or <code>closed</code>.</p>"
},
urlString: "/v1/lightning/channels?public_key=:pubKey&status=:channelStatus",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8770,7 +8771,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of channels with corresponding node geodata.</p>"
},
urlString: "/v1/lightning/channels-geo",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {
@ -8878,7 +8879,7 @@ export const restApiDocsData = [
default: "<p>Returns a list of channels with corresponding geodata for a node with the given <code>:pubKey</code>.</p>"
},
urlString: "/v1/lightning/channels-geo/:pubKey",
showConditions: bitcoinNetworks,
showConditions: lightningNetworks,
showJsExamples: showJsExamplesDefaultFalse,
codeExample: {
default: {

View file

@ -133,7 +133,7 @@
<p>{{electrsPort}}</p>
<p class="subtitle">SSL</p>
<p>Enabled</p>
<p class="note" *ngIf="network.val !== 'signet'">Electrum RPC interface for Bitcoin Signet is <a href="/signet/docs/api/electrs">publicly available</a>. Electrum RPC interface for all other networks is available to <a href='https://mempool.space/enterprise'>sponsors</a> only—whitelisting is required.</p>
<p class="note" *ngIf="network.val !== 'signet' && network.val !== 'testnet4'">Electrum RPC interface for <a href="/signet/docs/api/electrs">Bitcoin Signet</a> and <a href="/testnet4/docs/api/electrs">Bitcoin Testnet4</a> is publicly available. Electrum RPC interface for all other networks is available to <a href='https://mempool.space/enterprise'>sponsors</a> only—whitelisting is required.</p>
</div>
</div>
</div>

View file

@ -102,6 +102,8 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
this.electrsPort = 50002; break;
case "testnet":
this.electrsPort = 60002; break;
case "testnet4":
this.electrsPort = 40002; break;
case "signet":
this.electrsPort = 60602; break;
case "liquid":
@ -170,6 +172,9 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
if (network === 'testnet') {
curlResponse = code.codeSampleTestnet.curl;
}
if (network === 'testnet4') {
curlResponse = code.codeSampleTestnet.curl;
}
if (network === 'signet') {
curlResponse = code.codeSampleSignet.curl;
}

View file

@ -111,7 +111,10 @@ export class CodeTemplateComponent implements OnInit {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
}
if (this.network === 'testnet') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
}
if (this.network === 'testnet4') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
}
if (this.network === 'signet') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
@ -144,7 +147,10 @@ init();`;
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
}
if (this.network === 'testnet') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
}
if (this.network === 'testnet4') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
}
if (this.network === 'signet') {
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
@ -212,6 +218,9 @@ yarn add @mempool/liquid.js`;
if (this.network === 'testnet') {
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleTestnet);
}
if (this.network === 'testnet4') {
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleTestnet);
}
if (this.network === 'signet') {
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleSignet);
}
@ -234,6 +243,9 @@ yarn add @mempool/liquid.js`;
if (this.network === 'testnet') {
return code.codeSampleTestnet.response;
}
if (this.network === 'testnet4') {
return code.codeSampleTestnet.response;
}
if (this.network === 'signet') {
return code.codeSampleSignet.response;
}
@ -247,7 +259,7 @@ yarn add @mempool/liquid.js`;
}
wrapPythonTemplate(code: any) {
return ( ( this.network === 'testnet' || this.network === 'signet' ) ? ( code.codeTemplate.python.replace( "wss://mempool.space/api/v1/ws", "wss://mempool.space/" + this.network + "/api/v1/ws" ) ) : code.codeTemplate.python );
return ( ( this.network === 'testnet' || this.network === 'testnet4' || this.network === 'signet' ) ? ( code.codeTemplate.python.replace( "wss://mempool.space/api/v1/ws", "wss://mempool.space/" + this.network + "/api/v1/ws" ) ) : code.codeTemplate.python );
}
replaceJSPlaceholder(text: string, code: any) {

View file

@ -41,6 +41,7 @@ export class EnterpriseService {
disableSubnetworks(): void {
this.stateService.env.TESTNET_ENABLED = false;
this.stateService.env.TESTNET4_ENABLED = false;
this.stateService.env.LIQUID_ENABLED = false;
this.stateService.env.LIQUID_TESTNET_ENABLED = false;
this.stateService.env.SIGNET_ENABLED = false;

View file

@ -58,7 +58,7 @@ export class MiningService {
// I think it's fine to hardcode this since we don't have x1000 hashrate jump everyday
// If we want to support the mining dashboard for testnet, we can hardcode it too
let selectedPower = 18;
if (this.stateService.network === 'testnet') {
if (this.stateService.network === 'testnet' || this.stateService.network === 'testnet4') {
selectedPower = 12;
}

View file

@ -9,6 +9,7 @@ const networkModules = {
subnets: [
{ name: 'mainnet', path: '' },
{ name: 'testnet', path: '/testnet' },
{ name: 'testnet4', path: '/testnet4' },
{ name: 'signet', path: '/signet' },
],
},
@ -68,7 +69,7 @@ export class NavigationService {
}
if (route.url?.length) {
path = [path, ...route.url.map(segment => segment.path).filter(path => {
return path.length && !['testnet', 'signet'].includes(path);
return path.length && !['testnet', 'testnet4', 'signet'].includes(path);
})].join('/');
}
route = route.firstChild;

View file

@ -81,7 +81,9 @@ export class SeoService {
getTitle(): string {
if (this.network === 'testnet')
return this.baseTitle + ' - Bitcoin Testnet';
return this.baseTitle + ' - Bitcoin Testnet3';
if (this.network === 'testnet4')
return this.baseTitle + ' - Bitcoin Testnet4';
if (this.network === 'signet')
return this.baseTitle + ' - Bitcoin Signet';
if (this.network === 'liquid')
@ -92,7 +94,7 @@ export class SeoService {
}
getDescription(): string {
if ( (this.network === 'testnet') || (this.network === 'signet') || (this.network === '') || (this.network == 'mainnet') )
if ( (this.network === 'testnet') || (this.network === 'testnet4') || (this.network === 'signet') || (this.network === '') || (this.network == 'mainnet') )
return this.baseDescription + ' See the real-time status of your transactions, browse network stats, and more.';
if ( (this.network === 'liquid') || (this.network === 'liquidtestnet') )
return this.baseDescription + ' See Liquid transactions & assets, get network info, and more.';

View file

@ -40,6 +40,7 @@ export interface Customization {
export interface Env {
TESTNET_ENABLED: boolean;
TESTNET4_ENABLED: boolean;
SIGNET_ENABLED: boolean;
LIQUID_ENABLED: boolean;
LIQUID_TESTNET_ENABLED: boolean;
@ -73,6 +74,7 @@ export interface Env {
const defaultEnv: Env = {
'TESTNET_ENABLED': false,
'TESTNET4_ENABLED': false,
'SIGNET_ENABLED': false,
'LIQUID_ENABLED': false,
'LIQUID_TESTNET_ENABLED': false,
@ -318,7 +320,7 @@ export class StateService {
// (?:preview\/)? optional "preview" prefix (non-capturing)
// (testnet|signet)/ network string (captured as networkMatches[1])
// ($|\/) network string must end or end with a slash
const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(testnet|signet)($|\/)/);
const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(testnet4?|signet)($|\/)/);
switch (networkMatches && networkMatches[1]) {
case 'signet':
if (this.network !== 'signet') {
@ -337,6 +339,12 @@ export class StateService {
}
}
return;
case 'testnet4':
if (this.network !== 'testnet4') {
this.network = 'testnet4';
this.networkChanged$.next('testnet4');
}
return;
default:
if (this.env.BASE_MODULE !== 'mempool') {
if (this.network !== this.env.BASE_MODULE) {
@ -385,7 +393,7 @@ export class StateService {
}
isAnyTestnet(): boolean {
return ['testnet', 'signet', 'liquidtestnet'].includes(this.network);
return ['testnet', 'testnet4', 'signet', 'liquidtestnet'].includes(this.network);
}
resetChainTip() {

View file

@ -151,7 +151,7 @@ export function nextRoundNumber(num: number): number {
export function seoDescriptionNetwork(network: string): string {
if( network === 'liquidtestnet' || network === 'testnet' ) {
return ' Testnet';
} else if( network === 'signet' || network === 'testnet' ) {
} else if( network === 'signet' || network === 'testnet' || network === 'testnet4') {
return ' ' + network.charAt(0).toUpperCase() + network.slice(1);
}
return '';

View file

@ -4,5 +4,6 @@
<ng-template [ngIf]="network === 'liquid'">L-</ng-template>
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
<ng-template [ngIf]="network === 'testnet'">t-</ng-template>
<ng-template [ngIf]="network === 'testnet4'">t-</ng-template>
<ng-template [ngIf]="network === 'signet'">s-</ng-template>{{ unit }}
</span>

View file

@ -46,7 +46,7 @@
<div class="links">
<p class="category" i18n="footer.explore">Explore</p>
<p><a *ngIf="env.MINING_DASHBOARD" [routerLink]="['/mining' | relativeUrl]" i18n="mining.mining-dashboard">Mining Dashboard</a></p>
<p><a *ngIf="env.LIGHTNING" [routerLink]="['/lightning' | relativeUrl]" i18n="master-page.lightning">Lightning Explorer</a></p>
<p><a *ngIf="env.LIGHTNING && lightningNetworks.includes(stateService.network)" [routerLink]="['/lightning' | relativeUrl]" i18n="master-page.lightning">Lightning Explorer</a></p>
<p><a [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.recent-blocks">Recent Blocks</a></p>
<p><a [routerLink]="['/tx/push' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</a></p>
<p><a [routerLink]="['/tx/test' | relativeUrl]" i18n="shared.test-transaction|Test Transaction">Test Transaction</a></p>
@ -62,10 +62,11 @@
<p><a [routerLink]="['/docs/faq' | relativeUrl]" i18n="faq.more-faq">More FAQs &raquo;</a></p>
</div>
<div class="links" *ngIf="officialMempoolSpace || env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.LIQUID_TESTNET_ENABLED else toolBox" >
<div class="links" *ngIf="officialMempoolSpace || env.TESTNET_ENABLED || env.TESTNET4_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.LIQUID_TESTNET_ENABLED else toolBox" >
<p class="category" i18n="footer.networks">Networks</p>
<p *ngIf="(officialMempoolSpace || (env.BASE_MODULE === 'mempool')) && (currentNetwork !== '') && (currentNetwork !== 'mainnet')"><a [href]="networkLink('mainnet')" i18n="footer.mainnet-explorer">Mainnet Explorer</a></p>
<p *ngIf="(officialMempoolSpace || (env.BASE_MODULE === 'mempool')) && (currentNetwork !== 'testnet') && env.TESTNET_ENABLED"><a [href]="networkLink('testnet')" i18n="footer.testnet-explorer">Testnet Explorer</a></p>
<p *ngIf="(officialMempoolSpace || (env.BASE_MODULE === 'mempool')) && (currentNetwork !== 'testnet') && env.TESTNET_ENABLED"><a [href]="networkLink('testnet')" i18n="footer.testnet3-explorer">Testnet3 Explorer</a></p>
<p *ngIf="(officialMempoolSpace || (env.BASE_MODULE === 'mempool')) && (currentNetwork !== 'testnet4') && env.TESTNET4_ENABLED"><a [href]="networkLink('testnet4')" i18n="footer.testnet4-explorer">Testnet4 Explorer</a> <span class="badge badge-pill badge-warning beta-network" i18n="beta">beta</span></p>
<p *ngIf="(officialMempoolSpace || (env.BASE_MODULE === 'mempool')) && (currentNetwork !== 'signet') && env.SIGNET_ENABLED"><a [href]="networkLink('signet')" i18n="footer.signet-explorer">Signet Explorer</a></p>
<p *ngIf="(officialMempoolSpace || env.LIQUID_ENABLED) && (currentNetwork !== 'liquidtestnet')"><a [href]="networkLink('liquidtestnet')" i18n="footer.liquid-testnet-explorer">Liquid Testnet Explorer</a></p>
<p *ngIf="(officialMempoolSpace || env.LIQUID_ENABLED) && (currentNetwork !== 'liquid')"><a [href]="networkLink('liquid')" i18n="footer.liquid-explorer">Liquid Explorer</a></p>

View file

@ -329,3 +329,8 @@ footer .nowrap {
margin-right: 10px;
}
}
.beta-network {
font-size: 8px;
margin-left: 1em;
}

View file

@ -27,6 +27,7 @@ export class GlobalFooterComponent implements OnInit {
network$: Observable<string>;
networkPaths: { [network: string]: string };
currentNetwork = '';
lightningNetworks = ['', 'mainnet', 'bitcoin', 'testnet', 'signet'];
loggedIn = false;
urlSubscription: Subscription;
isServicesPage = false;
@ -76,7 +77,7 @@ export class GlobalFooterComponent implements OnInit {
networkLink(network) {
const thisNetwork = network || 'mainnet';
if( network === '' || network === 'mainnet' || network === 'testnet' || network === 'signet' ) {
if( network === '' || network === 'mainnet' || network === 'testnet' || network === 'testnet4' || network === 'signet' ) {
return (this.env.BASE_MODULE === 'mempool' ? '' : this.env.MEMPOOL_WEBSITE_URL + this.urlLanguage) + this.networkPaths[thisNetwork] || '/';
}
if( network === 'liquid' || network === 'liquidtestnet' ) {

View file

@ -4,5 +4,6 @@
<ng-template [ngIf]="network === 'liquid'">L-</ng-template>
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
<ng-template [ngIf]="network === 'testnet'">t-</ng-template>
<ng-template [ngIf]="network === 'testnet4'">t-</ng-template>
<ng-template [ngIf]="network === 'signet'">s-</ng-template>sats
</span>

View file

@ -62,6 +62,20 @@ const ADDRESS_CHARS: {
+ `{20,100}`
+ `)`,
},
testnet4: {
base58: `[mn2]` // Starts with a single m, n, or 2 (P2PKH is m or n, 2 is P2SH)
+ BASE58_CHARS
+ `{33,34}`, // m|n is 34 length, 2 is 35 length (We match the first letter separately)
bech32: `(?:`
+ `tb1` // Starts with tb1
+ BECH32_CHARS_LW
+ `{20,100}` // As per bech32, 6 char checksum is minimum
+ `|`
+ `TB1` // All upper case version
+ BECH32_CHARS_UP
+ `{20,100}`
+ `)`,
},
signet: {
base58: `[mn2]`
+ BASE58_CHARS
@ -128,7 +142,7 @@ const ADDRESS_CHARS: {
type RegexTypeNoAddrNoBlockHash = | `transaction` | `blockheight` | `date` | `timestamp`;
export type RegexType = `address` | `blockhash` | RegexTypeNoAddrNoBlockHash;
export const NETWORKS = [`testnet`, `signet`, `liquid`, `liquidtestnet`, `mainnet`] as const;
export const NETWORKS = [`testnet`, `testnet4`, `signet`, `liquid`, `liquidtestnet`, `mainnet`] as const;
export type Network = typeof NETWORKS[number]; // Turn const array into union type
export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS
@ -144,6 +158,8 @@ function isNetworkAvailable(network: Network, env: Env): boolean {
switch (network) {
case 'testnet':
return env.TESTNET_ENABLED === true;
case 'testnet4':
return env.TESTNET4_ENABLED === true;
case 'signet':
return env.SIGNET_ENABLED === true;
case 'liquid':
@ -160,7 +176,7 @@ function isNetworkAvailable(network: Network, env: Env): boolean {
export function needBaseModuleChange(fromBaseModule: 'mempool' | 'liquid', toNetwork: Network): boolean {
if (!toNetwork) return false; // No target network means no change needed
if (fromBaseModule === 'mempool') {
return toNetwork !== 'mainnet' && toNetwork !== 'testnet' && toNetwork !== 'signet';
return toNetwork !== 'mainnet' && toNetwork !== 'testnet' && toNetwork !== 'testnet4' && toNetwork !== 'signet';
}
if (fromBaseModule === 'liquid') {
return toNetwork !== 'liquid' && toNetwork !== 'liquidtestnet';
@ -175,7 +191,7 @@ export function getTargetUrl(toNetwork: Network, address: string, env: Env): str
targetUrl += '/address/';
targetUrl += address;
}
if (toNetwork === 'mainnet' || toNetwork === 'testnet' || toNetwork === 'signet') {
if (toNetwork === 'mainnet' || toNetwork === 'testnet' || toNetwork === 'testnet4' || toNetwork === 'signet') {
targetUrl = env.MEMPOOL_WEBSITE_URL;
targetUrl += (toNetwork === 'mainnet' ? '' : `/${toNetwork}`);
targetUrl += '/address/';
@ -209,6 +225,9 @@ export function getRegex(type: RegexType, network?: Network): RegExp {
case `testnet`:
leadingZeroes = 8; // Assumes at least 32 bits of difficulty
break;
case `testnet4`:
leadingZeroes = 8; // Assumes at least 32 bits of difficulty
break;
case `signet`:
leadingZeroes = 5;
break;
@ -261,6 +280,15 @@ export function getRegex(type: RegexType, network?: Network): RegExp {
regex += `|`; // OR
regex += `(?:02|03)${HEX_CHARS}{64}`; // Compressed pubkey
break;
case `testnet4`:
regex += ADDRESS_CHARS.testnet.base58;
regex += `|`; // OR
regex += ADDRESS_CHARS.testnet.bech32;
regex += `|`; // OR
regex += `04${HEX_CHARS}{128}`; // Uncompressed pubkey
regex += `|`; // OR
regex += `(?:02|03)${HEX_CHARS}{64}`; // Compressed pubkey
break;
case `signet`:
regex += ADDRESS_CHARS.signet.base58;
regex += `|`; // OR

View file

@ -84,6 +84,39 @@ zmqpubrawtx=tcp://127.0.0.1:18335
#addnode=[2401:b140:4::92:205]:18333
#addnode=[2401:b140:4::92:206]:18333
[testnet4]
daemon=1
rpcbind=127.0.0.1:48332
rpcbind=[::1]:48332
bind=0.0.0.0:48333
bind=[::]:48333
zmqpubrawblock=tcp://127.0.0.1:48334
zmqpubrawtx=tcp://127.0.0.1:48335
#addnode=[2401:b140:1::92:201]:48333
#addnode=[2401:b140:1::92:202]:48333
#addnode=[2401:b140:1::92:203]:48333
#addnode=[2401:b140:1::92:204]:48333
#addnode=[2401:b140:1::92:205]:48333
#addnode=[2401:b140:1::92:206]:48333
#addnode=[2401:b140:2::92:201]:48333
#addnode=[2401:b140:2::92:202]:48333
#addnode=[2401:b140:2::92:203]:48333
#addnode=[2401:b140:2::92:204]:48333
#addnode=[2401:b140:2::92:205]:48333
#addnode=[2401:b140:2::92:206]:48333
#addnode=[2401:b140:3::92:201]:48333
#addnode=[2401:b140:3::92:202]:48333
#addnode=[2401:b140:3::92:203]:48333
#addnode=[2401:b140:3::92:204]:48333
#addnode=[2401:b140:3::92:205]:48333
#addnode=[2401:b140:3::92:206]:48333
#addnode=[2401:b140:4::92:201]:48333
#addnode=[2401:b140:4::92:202]:48333
#addnode=[2401:b140:4::92:203]:48333
#addnode=[2401:b140:4::92:204]:48333
#addnode=[2401:b140:4::92:205]:48333
#addnode=[2401:b140:4::92:206]:48333
[signet]
daemon=1
rpcbind=127.0.0.1:38332

View file

@ -96,6 +96,8 @@ build_backend()
-e "s!__MEMPOOL_MAINNET_PASS__!${MEMPOOL_MAINNET_PASS}!" \
-e "s!__MEMPOOL_TESTNET_USER__!${MEMPOOL_TESTNET_USER}!" \
-e "s!__MEMPOOL_TESTNET_PASS__!${MEMPOOL_TESTNET_PASS}!" \
-e "s!__MEMPOOL_TESTNET4_USER__!${MEMPOOL_TESTNET4_USER}!" \
-e "s!__MEMPOOL_TESTNET4_PASS__!${MEMPOOL_TESTNET4_PASS}!" \
-e "s!__MEMPOOL_SIGNET_USER__!${MEMPOOL_SIGNET_USER}!" \
-e "s!__MEMPOOL_SIGNET_PASS__!${MEMPOOL_SIGNET_PASS}!" \
-e "s!__MEMPOOL_MAINNET_LIGHTNING_USER__!${MEMPOOL_MAINNET_LIGHTNING_USER}!" \
@ -130,7 +132,7 @@ source "${NVM_DIR}/nvm.sh"
# what to look for
frontends=(mainnet liquid onbtc)
backends=(mainnet testnet signet liquid liquidtestnet onbtc)
backends=(mainnet testnet testnet4 signet liquid liquidtestnet onbtc)
frontend_repos=()
backend_repos=()

View file

@ -0,0 +1,84 @@
{
"MEMPOOL": {
"OFFICIAL": true,
"NETWORK": "testnet",
"BACKEND": "esplora",
"HTTP_PORT": 8990,
"MINED_BLOCKS_CACHE": 144,
"SPAWN_CLUSTER_PROCS": 0,
"API_URL_PREFIX": "/api/v1/",
"INDEXING_BLOCKS_AMOUNT": -1,
"AUDIT": true,
"RUST_GBT": true,
"POLL_RATE_MS": 1000,
"DISK_CACHE_BLOCK_INTERVAL": 1,
"MAX_PUSH_TX_SIZE_WEIGHT": 4000000,
"ALLOW_UNREACHABLE": true,
"MAX_TRACKED_ADDRESSES": 10
},
"SYSLOG" : {
"MIN_PRIORITY": "debug"
},
"CORE_RPC": {
"PORT": 48332,
"USERNAME": "__BITCOIN_RPC_USER__",
"PASSWORD": "__BITCOIN_RPC_PASS__"
},
"ESPLORA": {
"UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet4",
"FALLBACK": [
"http://node201.fmt.mempool.space:3005",
"http://node202.fmt.mempool.space:3005",
"http://node203.fmt.mempool.space:3005",
"http://node204.fmt.mempool.space:3005",
"http://node205.fmt.mempool.space:3005",
"http://node206.fmt.mempool.space:3005",
"http://node201.va1.mempool.space:3005",
"http://node202.va1.mempool.space:3005",
"http://node203.va1.mempool.space:3005",
"http://node204.va1.mempool.space:3005",
"http://node205.va1.mempool.space:3005",
"http://node206.va1.mempool.space:3005",
"http://node207.va1.mempool.space:3005",
"http://node208.va1.mempool.space:3005",
"http://node209.va1.mempool.space:3005",
"http://node210.va1.mempool.space:3005",
"http://node211.va1.mempool.space:3005",
"http://node212.va1.mempool.space:3005",
"http://node213.va1.mempool.space:3005",
"http://node214.va1.mempool.space:3005",
"http://node201.fra.mempool.space:3005",
"http://node202.fra.mempool.space:3005",
"http://node203.fra.mempool.space:3005",
"http://node204.fra.mempool.space:3005",
"http://node205.fra.mempool.space:3005",
"http://node206.fra.mempool.space:3005",
"http://node207.fra.mempool.space:3005",
"http://node208.fra.mempool.space:3005",
"http://node209.fra.mempool.space:3005",
"http://node210.fra.mempool.space:3005",
"http://node211.fra.mempool.space:3005",
"http://node212.fra.mempool.space:3005",
"http://node213.fra.mempool.space:3005",
"http://node214.fra.mempool.space:3005",
"http://node201.tk7.mempool.space:3005",
"http://node202.tk7.mempool.space:3005",
"http://node203.tk7.mempool.space:3005",
"http://node204.tk7.mempool.space:3005",
"http://node205.tk7.mempool.space:3005",
"http://node206.tk7.mempool.space:3005"
]
},
"DATABASE": {
"ENABLED": true,
"HOST": "127.0.0.1",
"SOCKET": "/var/run/mysql/mysql.sock",
"USERNAME": "__MEMPOOL_TESTNET4_USER__",
"PASSWORD": "__MEMPOOL_TESTNET4_PASS__",
"DATABASE": "mempool_testnet4"
},
"STATISTICS": {
"ENABLED": true,
"TX_PER_SECOND_SAMPLE_PERIOD": 150
}
}

View file

@ -4,7 +4,7 @@ source "$NVM_DIR/nvm.sh"
nvm use v20.12.0
# start all mempool backends that exist
for site in mainnet mainnet-lightning testnet testnet-lightning signet signet-lightning liquid liquidtestnet;do
for site in mainnet mainnet-lightning testnet testnet-lightning testnet4 signet signet-lightning liquid liquidtestnet;do
cd "${HOME}/${site}/backend/" && \
echo "starting mempool backend: ${site}" && \
screen -dmS "${site}" sh -c 'while true;do npm run start-production;sleep 1;done'

View file

@ -0,0 +1,162 @@
###########
# mempool #
###########
# Block the internal APIs of esplora
location /testnet4/api/internal/ {
return 404;
}
location /testnet4/api/v1/internal/ {
return 404;
}
# websocket has special HTTP headers
location /testnet4/api/v1/ws {
rewrite ^/testnet4/(.*) /$1 break;
try_files /dev/null @mempool-testnet4-api-v1-websocket;
}
# warm cache mining and mempool API responses
location /testnet4/api/v1/statistics {
rewrite ^/testnet4/(.*) /$1 break;
try_files /dev/null @mempool-testnet4-api-v1-cache-warm;
}
location /testnet4/api/v1/mining {
rewrite ^/testnet4/(.*) /$1 break;
try_files /dev/null @mempool-testnet4-api-v1-cache-warm;
}
# it's ok to cache blockchain data "forever", so we do 30d
location /testnet4/api/v1/block/ {
rewrite ^/testnet4/(.*) /$1 break;
try_files /dev/null @mempool-testnet4-api-v1-cache-forever;
}
# everything else gets "normal" cache
location /testnet4/api/v1 {
rewrite ^/testnet4/(.*) /$1 break;
try_files /dev/null @mempool-testnet4-api-v1-cache-normal;
}
###########
# esplora #
###########
# it's ok to cache blockchain data "forever", so we do 30d
location /testnet4/api/block/ {
rewrite ^/testnet4/api/(.*) /$1 break;
try_files /dev/null @esplora-testnet4-api-cache-forever;
}
# other API responses cannot be cached
location /testnet4/api/ {
rewrite ^/testnet4/api/(.*) /$1 break;
try_files /dev/null @esplora-testnet4-api-cache-disabled;
}
###########
# routing #
###########
location @mempool-testnet4-api-v1-websocket {
proxy_pass $mempoolTestnet4;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
}
location @mempool-testnet4-api-v1-cache-forever {
proxy_pass $mempoolTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_background_update on;
proxy_cache_use_stale updating;
proxy_cache apicold;
proxy_cache_valid 200 30d;
proxy_redirect off;
expires 30d;
}
location @mempool-testnet4-api-v1-cache-warm {
proxy_pass $mempoolTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_background_update on;
proxy_cache_use_stale updating;
proxy_cache apiwarm;
proxy_cache_valid 200 10s;
proxy_redirect off;
}
location @mempool-testnet4-api-v1-cache-normal {
proxy_pass $mempoolTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache apinormal;
proxy_cache_valid 200 10s;
proxy_redirect off;
expires 10s;
}
location @mempool-testnet4-api-v1-cache-disabled {
proxy_pass $mempoolTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
expires -1;
}
location @esplora-testnet4-api-cache-disabled {
proxy_pass $esploraTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
expires -1;
}
location @esplora-testnet4-api-cache-forever {
proxy_pass $esploraTestnet4;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_background_update on;
proxy_cache_use_stale updating;
proxy_cache apicold;
proxy_cache_valid 200 30d;
proxy_redirect off;
expires 30d;
}

View file

@ -55,12 +55,14 @@ http {
set $mempoolMainnetLightning "http://mempool-bitcoin-mainnet-lightning";
set $mempoolTestnet "http://mempool-bitcoin-testnet";
set $mempoolTestnetLightning "http://mempool-bitcoin-testnet-lightning";
set $mempoolTestnet4 "http://mempool-bitcoin-testnet4";
set $mempoolSignet "http://mempool-bitcoin-signet";
set $mempoolSignetLightning "http://mempool-bitcoin-signet-lightning";
# for blockstream/esplora daemons, see upstream-esplora.conf
set $esploraMainnet "http://esplora-bitcoin-mainnet";
set $esploraTestnet "http://esplora-bitcoin-testnet";
set $esploraTestnet4 "http://esplora-bitcoin-testnet4";
set $esploraSignet "http://esplora-bitcoin-signet";
# filesystem paths

View file

@ -5,5 +5,7 @@ include mempool/production/nginx/location-api-v1-lightning.conf;
include mempool/production/nginx/location-api.conf;
include mempool/production/nginx/location-testnet-api.conf;
include mempool/production/nginx/location-testnet-api-v1-lightning.conf;
include mempool/production/nginx/location-testnet4-api.conf;
#include mempool/production/nginx/location-testnet4-api-v1-lightning.conf;
include mempool/production/nginx/location-signet-api.conf;
include mempool/production/nginx/location-signet-api-v1-lightning.conf;

View file

@ -7,6 +7,9 @@ upstream esplora-liquid-mainnet {
upstream esplora-bitcoin-testnet {
server unix:/bitcoin/socket/esplora-bitcoin-testnet fail_timeout=10s max_fails=10 weight=99999;
}
upstream esplora-bitcoin-testnet4 {
server unix:/bitcoin/socket/esplora-bitcoin-testnet4 fail_timeout=10s max_fails=10 weight=99999;
}
upstream esplora-bitcoin-signet {
server unix:/bitcoin/socket/esplora-bitcoin-signet fail_timeout=10s max_fails=10 weight=99999;
}

View file

@ -7,9 +7,6 @@ upstream mempool-liquid-mainnet {
upstream mempool-bitcoin-testnet {
server 127.0.0.1:8997 fail_timeout=10s max_fails=10 weight=99999;
}
upstream mempool-bitcoin-bisq {
server 127.0.0.1:8996 fail_timeout=10s max_fails=10 weight=99999;
}
upstream mempool-bitcoin-signet {
server 127.0.0.1:8995 fail_timeout=10s max_fails=10 weight=99999;
}
@ -25,3 +22,6 @@ upstream mempool-bitcoin-testnet-lightning {
upstream mempool-bitcoin-signet-lightning {
server 127.0.0.1:8991 fail_timeout=10s max_fails=10 weight=99999;
}
upstream mempool-bitcoin-testnet4 {
server 127.0.0.1:8990 fail_timeout=10s max_fails=10 weight=99999;
}