Compare commits
23 Commits
dependabot
...
mononaut/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e8e1f3dee | ||
|
|
3b4eda432f | ||
|
|
5b2f613856 | ||
|
|
f1e2c893cc | ||
|
|
7d3d59c348 | ||
|
|
8719b424e5 | ||
|
|
ef498b55ed | ||
|
|
9718610104 | ||
|
|
937e82bb89 | ||
|
|
91bf35bb65 | ||
|
|
f0f6ee1919 | ||
|
|
7b837b96da | ||
|
|
c417470be2 | ||
|
|
dfd7877f82 | ||
|
|
136e80e5cf | ||
|
|
b3aed2f58b | ||
|
|
e75f913af3 | ||
|
|
0a9703f164 | ||
|
|
db321c3fa5 | ||
|
|
b6aeb5661f | ||
|
|
f08fa034cc | ||
|
|
aa9888a2fe | ||
|
|
3909148d6e |
@@ -42,6 +42,7 @@ class BitcoinRoutes {
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', this.getBlocks.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', this.getBlock)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/summary', this.getStrippedBlockTransactions)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/tx/:txid/summary', this.getStrippedBlockTransaction)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/audit-summary', this.getBlockAuditSummary)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/tx/:txid/audit', this.$getBlockTxAuditSummary)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', this.getBlockTipHeight)
|
||||
@@ -321,6 +322,20 @@ class BitcoinRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
private async getStrippedBlockTransaction(req: Request, res: Response) {
|
||||
try {
|
||||
const transaction = await blocks.$getSingleTxFromSummary(req.params.hash, req.params.txid);
|
||||
if (!transaction) {
|
||||
handleError(req, res, 404, `transaction not found in summary`);
|
||||
return;
|
||||
}
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||
res.json(transaction);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
private async getBlock(req: Request, res: Response) {
|
||||
try {
|
||||
const block = await blocks.$getBlock(req.params.hash);
|
||||
|
||||
@@ -1224,6 +1224,11 @@ class Blocks {
|
||||
return summary.transactions;
|
||||
}
|
||||
|
||||
public async $getSingleTxFromSummary(hash: string, txid: string): Promise<TransactionClassified | null> {
|
||||
const txs = await this.$getStrippedBlockTransactions(hash);
|
||||
return txs.find(tx => tx.txid === txid) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 15 blocks
|
||||
*
|
||||
|
||||
@@ -46,8 +46,11 @@ class AccelerationApi {
|
||||
private websocketConnected: boolean = false;
|
||||
private onDemandPollingEnabled = !config.MEMPOOL_SERVICES.ACCELERATIONS;
|
||||
private apiPath = config.MEMPOOL.OFFICIAL ? (config.MEMPOOL_SERVICES.API + '/accelerator/accelerations') : (config.EXTERNAL_DATA_SERVER.MEMPOOL_API + '/accelerations');
|
||||
private websocketPath = config.MEMPOOL_SERVICES?.API ? `${config.MEMPOOL_SERVICES.API.replace('https://', 'wss://').replace('http://', 'ws://')}/accelerator/ws` : '/';
|
||||
private _accelerations: Record<string, Acceleration> = {};
|
||||
private lastPoll = 0;
|
||||
private lastPing = Date.now();
|
||||
private lastPong = Date.now();
|
||||
private forcePoll = false;
|
||||
private myAccelerations: Record<string, { status: MyAccelerationStatus, added: number, acceleration?: Acceleration }> = {};
|
||||
|
||||
@@ -242,18 +245,18 @@ class AccelerationApi {
|
||||
while (this.useWebsocket) {
|
||||
this.startedWebsocketLoop = true;
|
||||
if (!this.ws) {
|
||||
this.ws = new WebSocket(`${config.MEMPOOL_SERVICES.API.replace('https://', 'ws://').replace('http://', 'ws://')}/accelerator/ws`);
|
||||
this.ws = new WebSocket(this.websocketPath);
|
||||
this.websocketConnected = true;
|
||||
|
||||
this.ws.on('open', () => {
|
||||
logger.info('Acceleration websocket opened');
|
||||
logger.info(`Acceleration websocket opened to ${this.websocketPath}`);
|
||||
this.ws?.send(JSON.stringify({
|
||||
'watch-accelerations': true
|
||||
}));
|
||||
});
|
||||
|
||||
this.ws.on('error', (error) => {
|
||||
logger.err('Acceleration websocket error: ' + error);
|
||||
logger.err(`Acceleration websocket error on ${this.websocketPath}: ` + error);
|
||||
this.ws = null;
|
||||
this.websocketConnected = false;
|
||||
});
|
||||
@@ -266,12 +269,33 @@ class AccelerationApi {
|
||||
|
||||
this.ws.on('message', (data, isBinary) => {
|
||||
try {
|
||||
const parsedMsg = JSON.parse((isBinary ? data : data.toString()) as string);
|
||||
const msg = (isBinary ? data : data.toString()) as string;
|
||||
const parsedMsg = msg?.length ? JSON.parse(msg) : null;
|
||||
this.handleWebsocketMessage(parsedMsg);
|
||||
} catch (e) {
|
||||
logger.warn('Failed to parse acceleration websocket message: ' + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on('ping', () => {
|
||||
logger.debug('received ping from acceleration websocket server');
|
||||
});
|
||||
|
||||
this.ws.on('pong', () => {
|
||||
logger.debug('received pong from acceleration websocket server');
|
||||
this.lastPong = Date.now();
|
||||
});
|
||||
} else {
|
||||
if (this.lastPing > this.lastPong && Date.now() - this.lastPing > 10000) {
|
||||
logger.warn('No pong received within 10 seconds, terminating connection');
|
||||
this.ws.terminate();
|
||||
this.ws = null;
|
||||
this.websocketConnected = false;
|
||||
} else if (Date.now() - this.lastPing > 30000) {
|
||||
logger.debug('sending ping to acceleration websocket server');
|
||||
this.ws.ping();
|
||||
this.lastPing = Date.now();
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
}
|
||||
|
||||
@@ -172,10 +172,6 @@
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
.btn-small-height {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<td class="pie-chart" rowspan="2" *ngIf="!chartPositionLeft">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
@if (hasCpfp) {
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right mt-0" (click)="onToggleCpfp()">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right mt-0" (click)="onToggleCpfp()">CPFP</button>
|
||||
}
|
||||
<ng-container *ngTemplateOutlet="pieChart"></ng-container>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
<tr>
|
||||
<td colspan="3" class="pt-0">
|
||||
<div class="d-flex justify-content-end align-items-start">
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right mt-0" (click)="onToggleCpfp()">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right mt-0" (click)="onToggleCpfp()">CPFP</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { WebsocketService } from '@app/services/websocket.service';
|
||||
import { SeoService } from '@app/services/seo.service';
|
||||
import { OpenGraphService } from '@app/services/opengraph.service';
|
||||
import { seoDescriptionNetwork } from '@app/shared/common.utils';
|
||||
import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blocks-list',
|
||||
@@ -49,6 +50,7 @@ export class BlocksList implements OnInit {
|
||||
private ogService: OpenGraphService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
) {
|
||||
this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool';
|
||||
@@ -182,7 +184,7 @@ export class BlocksList implements OnInit {
|
||||
}
|
||||
|
||||
pageChange(page: number): void {
|
||||
this.router.navigate(['blocks', page]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/blocks/'), page]);
|
||||
}
|
||||
|
||||
trackByBlock(index: number, block: BlockExtended): number {
|
||||
|
||||
@@ -267,7 +267,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
if (event.key === prevKey) {
|
||||
if (this.mempoolBlocks[this.markIndex - 1]) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/mempool-block/'), this.markIndex - 1]);
|
||||
} else {
|
||||
const blocks = this.stateService.blocksSubject$.getValue();
|
||||
for (const block of (blocks || [])) {
|
||||
|
||||
@@ -267,7 +267,7 @@
|
||||
}
|
||||
</div>
|
||||
@if (hasCpfp) {
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="toggleCpfp()">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>
|
||||
<button type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="toggleCpfp()">CPFP</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
<!-- CPFP Details -->
|
||||
<ng-template [ngIf]="showCpfpDetails">
|
||||
<br>
|
||||
<h2 class="text-left">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" size="xs"></fa-icon></h2>
|
||||
<div class="title">
|
||||
<h2 class="text-left" i18n="transaction.related-transactions|CPFP List">Related Transactions</h2>
|
||||
</div>
|
||||
<div class="box cpfp-details">
|
||||
<table class="table table-fixed table-borderless table-striped">
|
||||
<thead>
|
||||
|
||||
@@ -66,10 +66,6 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-small-height {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.arrow-green {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
@@ -406,6 +406,30 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
const auditAvailable = this.isAuditAvailable(height);
|
||||
const isCoinbase = this.tx.vin.some(v => v.is_coinbase);
|
||||
const fetchAudit = auditAvailable && !isCoinbase;
|
||||
|
||||
const addFirstSeen = (audit: TxAuditStatus | null, hash: string, height: number, txid: string, useFullSummary: boolean) => {
|
||||
if (
|
||||
this.isFirstSeenAvailable(height)
|
||||
&& !audit?.firstSeen // firstSeen is not already in audit
|
||||
&& (!audit || audit?.seen) // audit is disabled or tx is already seen (meaning 'firstSeen' is in block summary)
|
||||
) {
|
||||
return useFullSummary ?
|
||||
this.apiService.getStrippedBlockTransactions$(hash).pipe(
|
||||
map(strippedTxs => {
|
||||
return { audit, firstSeen: strippedTxs.find(tx => tx.txid === txid)?.time };
|
||||
}),
|
||||
catchError(() => of({ audit }))
|
||||
) :
|
||||
this.apiService.getStrippedBlockTransaction$(hash, txid).pipe(
|
||||
map(strippedTx => {
|
||||
return { audit, firstSeen: strippedTx?.time };
|
||||
}),
|
||||
catchError(() => of({ audit }))
|
||||
);
|
||||
}
|
||||
return of({ audit });
|
||||
};
|
||||
|
||||
if (fetchAudit) {
|
||||
// If block audit is already cached, use it to get transaction audit
|
||||
const blockAuditLoaded = this.apiService.getBlockAuditLoaded(hash);
|
||||
@@ -428,24 +452,31 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
accelerated: isAccelerated,
|
||||
firstSeen,
|
||||
};
|
||||
}),
|
||||
switchMap(audit => addFirstSeen(audit, hash, height, txid, true)),
|
||||
catchError(() => {
|
||||
return of({ audit: null });
|
||||
})
|
||||
)
|
||||
} else {
|
||||
return this.apiService.getBlockTxAudit$(hash, txid).pipe(
|
||||
retry({ count: 3, delay: 2000 }),
|
||||
switchMap(audit => addFirstSeen(audit, hash, height, txid, false)),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
return of({ audit: null });
|
||||
})
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return of(isCoinbase ? { coinbase: true } : null);
|
||||
const audit = isCoinbase ? { coinbase: true } : null;
|
||||
return addFirstSeen(audit, hash, height, txid, this.apiService.getBlockSummaryLoaded(hash));
|
||||
}
|
||||
}),
|
||||
).subscribe(auditStatus => {
|
||||
this.auditStatus = auditStatus;
|
||||
if (this.auditStatus?.firstSeen) {
|
||||
this.transactionTime = this.auditStatus.firstSeen;
|
||||
this.auditStatus = auditStatus?.audit;
|
||||
const firstSeen = this.auditStatus?.firstSeen || auditStatus['firstSeen'];
|
||||
if (firstSeen) {
|
||||
this.transactionTime = firstSeen;
|
||||
}
|
||||
this.setIsAccelerated();
|
||||
});
|
||||
@@ -922,6 +953,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'testnet4':
|
||||
if (blockHeight < this.stateService.env.TESTNET4_BLOCK_AUDIT_START_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'signet':
|
||||
if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||
return false;
|
||||
@@ -935,6 +971,34 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return true;
|
||||
}
|
||||
|
||||
isFirstSeenAvailable(blockHeight: number): boolean {
|
||||
if (this.stateService.env.BASE_MODULE !== 'mempool') {
|
||||
return false;
|
||||
}
|
||||
switch (this.stateService.network) {
|
||||
case 'testnet':
|
||||
if (this.stateService.env.TESTNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.TESTNET_TX_FIRST_SEEN_START_HEIGHT) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'testnet4':
|
||||
if (this.stateService.env.TESTNET4_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.TESTNET4_TX_FIRST_SEEN_START_HEIGHT) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'signet':
|
||||
if (this.stateService.env.SIGNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.SIGNET_TX_FIRST_SEEN_START_HEIGHT) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (this.stateService.env.MAINNET_TX_FIRST_SEEN_START_HEIGHT && blockHeight >= this.stateService.env.MAINNET_TX_FIRST_SEEN_START_HEIGHT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
resetTransaction() {
|
||||
this.firstLoad = false;
|
||||
this.gotInitialPosition = false;
|
||||
|
||||
@@ -18,6 +18,7 @@ export class ApiService {
|
||||
private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet
|
||||
|
||||
private requestCache = new Map<string, { subject: BehaviorSubject<any>, expiry: number }>;
|
||||
public blockSummaryLoaded: { [hash: string]: boolean } = {};
|
||||
public blockAuditLoaded: { [hash: string]: boolean } = {};
|
||||
|
||||
constructor(
|
||||
@@ -318,9 +319,14 @@ export class ApiService {
|
||||
}
|
||||
|
||||
getStrippedBlockTransactions$(hash: string): Observable<TransactionStripped[]> {
|
||||
this.setBlockSummaryLoaded(hash);
|
||||
return this.httpClient.get<TransactionStripped[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/summary');
|
||||
}
|
||||
|
||||
getStrippedBlockTransaction$(hash: string, txid: string): Observable<TransactionStripped> {
|
||||
return this.httpClient.get<TransactionStripped>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/tx/' + txid + '/summary');
|
||||
}
|
||||
|
||||
getDifficultyAdjustments$(interval: string | undefined): Observable<any> {
|
||||
return this.httpClient.get<any[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty-adjustments` +
|
||||
@@ -567,4 +573,12 @@ export class ApiService {
|
||||
getBlockAuditLoaded(hash) {
|
||||
return this.blockAuditLoaded[hash];
|
||||
}
|
||||
|
||||
async setBlockSummaryLoaded(hash: string) {
|
||||
this.blockSummaryLoaded[hash] = true;
|
||||
}
|
||||
|
||||
getBlockSummaryLoaded(hash) {
|
||||
return this.blockSummaryLoaded[hash];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,12 @@ export interface Env {
|
||||
AUDIT: boolean;
|
||||
MAINNET_BLOCK_AUDIT_START_HEIGHT: number;
|
||||
TESTNET_BLOCK_AUDIT_START_HEIGHT: number;
|
||||
TESTNET4_BLOCK_AUDIT_START_HEIGHT: number;
|
||||
SIGNET_BLOCK_AUDIT_START_HEIGHT: number;
|
||||
MAINNET_TX_FIRST_SEEN_START_HEIGHT: number;
|
||||
TESTNET_TX_FIRST_SEEN_START_HEIGHT: number;
|
||||
TESTNET4_TX_FIRST_SEEN_START_HEIGHT: number;
|
||||
SIGNET_TX_FIRST_SEEN_START_HEIGHT: number;
|
||||
HISTORICAL_PRICE: boolean;
|
||||
ACCELERATOR: boolean;
|
||||
ACCELERATOR_BUTTON: boolean;
|
||||
@@ -107,7 +112,12 @@ const defaultEnv: Env = {
|
||||
'AUDIT': false,
|
||||
'MAINNET_BLOCK_AUDIT_START_HEIGHT': 0,
|
||||
'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0,
|
||||
'TESTNET4_BLOCK_AUDIT_START_HEIGHT': 0,
|
||||
'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0,
|
||||
'MAINNET_TX_FIRST_SEEN_START_HEIGHT': 0,
|
||||
'TESTNET_TX_FIRST_SEEN_START_HEIGHT': 0,
|
||||
'TESTNET4_TX_FIRST_SEEN_START_HEIGHT': 0,
|
||||
'SIGNET_TX_FIRST_SEEN_START_HEIGHT': 0,
|
||||
'HISTORICAL_PRICE': true,
|
||||
'ACCELERATOR': false,
|
||||
'ACCELERATOR_BUTTON': true,
|
||||
|
||||
@@ -1403,4 +1403,8 @@ a {
|
||||
color: var(--fg);
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-small-height {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@@ -1089,6 +1089,7 @@ case $OS in
|
||||
echo "[*] Installing syslog configuration"
|
||||
osSudo "${ROOT_USER}" mkdir -p /usr/local/etc/syslog.d
|
||||
osSudo "${ROOT_USER}" install -c -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool-logger" /usr/local/bin/mempool-logger
|
||||
osSudo "${ROOT_USER}" install -c -m 755 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool-slacker" /usr/local/bin/mempool-slacker
|
||||
osSudo "${ROOT_USER}" install -c -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/syslog.conf" /usr/local/etc/syslog.d/mempool.conf
|
||||
|
||||
echo "[*] Installing redis configuration"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"MAINNET_BLOCK_AUDIT_START_HEIGHT": 773911,
|
||||
"TESTNET_BLOCK_AUDIT_START_HEIGHT": 2417829,
|
||||
"SIGNET_BLOCK_AUDIT_START_HEIGHT": 127609,
|
||||
"MAINNET_TX_FIRST_SEEN_START_HEIGHT": 838316,
|
||||
"ITEMS_PER_PAGE": 25,
|
||||
"LIGHTNING": true,
|
||||
"ACCELERATOR": true,
|
||||
|
||||
19
production/mempool-slacker
Executable file
19
production/mempool-slacker
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$SLACK_SYSLOG_WEBHOOK_URL" ]; then
|
||||
echo "Error: SLACK_SYSLOG_WEBHOOK_URL environment variable is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
trap "" PIPE
|
||||
while read input; do
|
||||
if [ ! -z "${input}" ]; then
|
||||
escaped_input=$(echo "$input" | sed 's/"/\\"/g')
|
||||
curl -X POST \
|
||||
-H 'Content-type: application/json' \
|
||||
--data "{\"text\":\"\`\`\`${escaped_input}\`\`\`\"}" \
|
||||
-s \
|
||||
"$SLACK_SYSLOG_WEBHOOK_URL" \
|
||||
>/dev/null 2>&1 &
|
||||
fi
|
||||
done
|
||||
@@ -55,10 +55,10 @@ location /api/block/ {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
try_files /dev/null @esplora-api-cache-forever;
|
||||
}
|
||||
# other API responses cannot be cached
|
||||
# we cache for 1s to mitigate DoS attacks
|
||||
location /api/ {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
try_files /dev/null @esplora-api-cache-disabled;
|
||||
try_files /dev/null @esplora-api-cache-minimal;
|
||||
}
|
||||
|
||||
###########
|
||||
@@ -171,6 +171,23 @@ location @esplora-api-cache-disabled {
|
||||
expires -1;
|
||||
}
|
||||
|
||||
location @esplora-api-cache-minimal {
|
||||
proxy_pass $esploraMainnet;
|
||||
|
||||
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 apihot;
|
||||
proxy_cache_valid 200 1s;
|
||||
proxy_redirect off;
|
||||
|
||||
expires 1s;
|
||||
}
|
||||
|
||||
location @esplora-api-cache-forever {
|
||||
proxy_pass $esploraMainnet;
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ location /testnet/api/block/ {
|
||||
rewrite ^/testnet/api/(.*) /$1 break;
|
||||
try_files /dev/null @esplora-testnet-api-cache-forever;
|
||||
}
|
||||
# other API responses cannot be cached
|
||||
# we cache for 1s to mitigate DoS attacks
|
||||
location /testnet/api/ {
|
||||
rewrite ^/testnet/api/(.*) /$1 break;
|
||||
try_files /dev/null @esplora-testnet-api-cache-disabled;
|
||||
try_files /dev/null @esplora-testnet-api-cache-minimal;
|
||||
}
|
||||
|
||||
###########
|
||||
@@ -160,3 +160,20 @@ location @esplora-testnet-api-cache-forever {
|
||||
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
location @esplora-testnet-api-cache-minimal {
|
||||
proxy_pass $esploraTestnet;
|
||||
|
||||
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 off;
|
||||
proxy_cache_use_stale error;
|
||||
proxy_cache apihot;
|
||||
proxy_cache_valid 200 1s;
|
||||
proxy_redirect off;
|
||||
|
||||
expires 1s;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local7.>=err |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.ops alerts
|
||||
local7.>=err |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-slacker
|
||||
local7.>=info |/usr/local/bin/sudo -u mempool /usr/local/bin/mempool-logger mempool.ops node100
|
||||
local7.>=info /var/log/mempool
|
||||
local7.* /var/log/mempool.debug
|
||||
|
||||
Reference in New Issue
Block a user