From fafe40cef0c509352d207c56fab4996286e6693f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 22 Aug 2022 19:12:04 +0000 Subject: [PATCH 01/12] Alternative transaction unfurl design --- .../transaction-preview.component.html | 129 +++++----------- .../transaction-preview.component.scss | 62 +++++++- .../transaction-preview.component.ts | 54 +++---- .../tx-bowtie-graph.component.html | 36 +++++ .../tx-bowtie-graph.component.scss | 6 + .../tx-bowtie-graph.component.ts | 145 ++++++++++++++++++ frontend/src/app/shared/shared.module.ts | 3 + 7 files changed, 315 insertions(+), 120 deletions(-) create mode 100644 frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html create mode 100644 frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss create mode 100644 frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts diff --git a/frontend/src/app/components/transaction/transaction-preview.component.html b/frontend/src/app/components/transaction/transaction-preview.component.html index f9f62c417..991f51948 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.html +++ b/frontend/src/app/components/transaction/transaction-preview.component.html @@ -13,104 +13,47 @@ - - {{ txId }} - +

+ + {{ txId }} + +

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timestamp - ‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }} -
First seen - ‎{{ transactionTime * 1000 | date:'yyyy-MM-dd HH:mm' }} - ?
Amount - Confidential - - - -
Size
Weight
Inputs{{ tx.vin.length }}Coinbase
+
+ +
+

+ Confidential + + + +

+

+ {{ tx.feePerVsize | feeRounding }} sat/vB +

- -
- - - - - - - - - - - +
+ +
Fee{{ tx.fee | number }} sat
Fee rate - {{ tx.feePerVsize | feeRounding }} sat/vB - -   - - -
+ - - + + - - - - - - - - - - - - - - -
Effective fee rate -
- {{ tx.effectiveFeePerVsize | feeRounding }} sat/vB - - - -
-
Coinbase{{ tx.vin[0].scriptsig | hex2ascii }}
Virtual size
Locktime
Outputs{{ tx.vout.length }}
+ + + + + + + + + + + +
OP_RETURN{{ vout.scriptpubkey_asm | hex2ascii }}
+
diff --git a/frontend/src/app/components/transaction/transaction-preview.component.scss b/frontend/src/app/components/transaction/transaction-preview.component.scss index a8e2a0acb..4a65dd0d8 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.scss +++ b/frontend/src/app/components/transaction/transaction-preview.component.scss @@ -69,7 +69,67 @@ } .tx-link { - display: inline-block; + display: inline; font-size: 28px; margin-bottom: 6px; } + +.graph-wrapper { + position: relative; + background: #181b2d; + padding: 10px; + padding-bottom: 0; + + .above-bow { + position: absolute; + top: 20px; + left: 0; + right: 0; + margin: auto; + text-align: center; + + .field { + font-size: 32px; + margin: 0; + + ::ng-deep .symbol { + font-size: 24px; + } + } + } + + .overlaid { + position: absolute; + bottom: 0; + left: 0; + right: 0; + width: 100%; + text-align: left; + font-size: 28px; + max-width: 90%; + margin: auto; + overflow: hidden; + + .opreturns { + width: auto; + margin: auto; + table-layout: auto; + background: #2d3348af; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + + td { + padding: 10px 10px; + + &.message { + overflow: hidden; + display: inline-block; + vertical-align: bottom; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; + } + } + } + } +} diff --git a/frontend/src/app/components/transaction/transaction-preview.component.ts b/frontend/src/app/components/transaction/transaction-preview.component.ts index 05ce623fb..15a881446 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.ts +++ b/frontend/src/app/components/transaction/transaction-preview.component.ts @@ -7,10 +7,9 @@ import { catchError, retryWhen, delay, - map } from 'rxjs/operators'; import { Transaction, Vout } from '../../interfaces/electrs.interface'; -import { of, merge, Subscription, Observable, Subject, timer, combineLatest, from } from 'rxjs'; +import { of, merge, Subscription, Observable, Subject, from } from 'rxjs'; import { StateService } from '../../services/state.service'; import { OpenGraphService } from 'src/app/services/opengraph.service'; import { ApiService } from 'src/app/services/api.service'; @@ -30,13 +29,16 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { isLoadingTx = true; error: any = undefined; errorUnblinded: any = undefined; - transactionTime = -1; subscription: Subscription; fetchCpfpSubscription: Subscription; cpfpInfo: CpfpInfo | null; showCpfpDetails = false; fetchCpfp$ = new Subject(); liquidUnblinding = new LiquidUnblinding(); + isLiquid = false; + totalValue: number; + opReturns: Vout[]; + extraData: 'none' | 'coinbase' | 'opreturn'; constructor( private route: ActivatedRoute, @@ -49,7 +51,12 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { ngOnInit() { this.stateService.networkChanged$.subscribe( - (network) => (this.network = network) + (network) => { + this.network = network; + if (this.network === 'liquid' || this.network == 'liquidtestnet') { + this.isLiquid = true; + } + } ); this.fetchCpfpSubscription = this.fetchCpfp$ @@ -152,12 +159,9 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; - - if (!tx.status.confirmed && tx.firstSeen) { - this.transactionTime = tx.firstSeen; - } else { - this.getTransactionTime(); - } + this.totalValue = this.tx.vout.reduce((acc, v) => v.value + acc, 0); + this.opReturns = this.getOpReturns(this.tx); + this.extraData = this.chooseExtraData(); if (!this.tx.status.confirmed) { if (tx.cpfpChecked) { @@ -181,26 +185,10 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { ); } - getTransactionTime() { - this.openGraphService.waitFor('tx-time'); - this.apiService - .getTransactionTimes$([this.tx.txid]) - .pipe( - catchError((err) => { - return of(0); - }) - ) - .subscribe((transactionTimes) => { - this.transactionTime = transactionTimes[0]; - this.openGraphService.waitOver('tx-time'); - }); - } - resetTransaction() { this.error = undefined; this.tx = null; this.isLoadingTx = true; - this.transactionTime = -1; this.cpfpInfo = null; this.showCpfpDetails = false; } @@ -217,6 +205,20 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { return tx.vout.map((v: Vout) => v.value || 0).reduce((a: number, b: number) => a + b); } + getOpReturns(tx: Transaction): Vout[] { + return tx.vout.filter((v) => v.scriptpubkey_type === 'op_return' && v.scriptpubkey_asm !== 'OP_RETURN'); + } + + chooseExtraData(): 'none' | 'opreturn' | 'coinbase' { + if (this.isCoinbase(this.tx)) { + return 'coinbase'; + } else if (this.opReturns?.length) { + return 'opreturn'; + } else { + return 'none'; + } + } + ngOnDestroy() { this.subscription.unsubscribe(); this.fetchCpfpSubscription.unsubscribe(); diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html new file mode 100644 index 000000000..d3452b5a9 --- /dev/null +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss new file mode 100644 index 000000000..ea45e4495 --- /dev/null +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss @@ -0,0 +1,6 @@ +.bowtie { + .line { + stroke: white; + fill: none; + } +} diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts new file mode 100644 index 000000000..68fdabeae --- /dev/null +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -0,0 +1,145 @@ +import { Component, OnInit, Input, OnChanges } from '@angular/core'; +import { Transaction } from '../../interfaces/electrs.interface'; + +interface SvgLine { + path: string; + style: string; + class?: string; +} + +@Component({ + selector: 'tx-bowtie-graph', + templateUrl: './tx-bowtie-graph.component.html', + styleUrls: ['./tx-bowtie-graph.component.scss'], +}) +export class TxBowtieGraphComponent implements OnInit, OnChanges { + @Input() tx: Transaction; + @Input() isLiquid: boolean = false; + @Input() width = 1200; + @Input() height = 600; + @Input() combinedWeight = 100; + @Input() minWeight = 2; // + @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen. + + inputs: SvgLine[]; + outputs: SvgLine[]; + middle: SvgLine; + + ngOnInit(): void { + this.initGraph(); + } + + ngOnChanges(): void { + this.initGraph(); + } + + initGraph(): void { + const totalValue = this.calcTotalValue(this.tx); + const voutWithFee = this.tx.vout.map(v => { return { type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output', value: v?.value }; }); + + if (this.tx.fee && !this.isLiquid) { + voutWithFee.unshift({ type: 'fee', value: this.tx.fee }); + } + + this.inputs = this.initLines('in', this.tx.vin.map(v => { return {type: 'input', value: v?.prevout?.value }; }), totalValue, this.maxStrands); + this.outputs = this.initLines('out', voutWithFee, totalValue, this.maxStrands); + + this.middle = { + path: `M ${(this.width / 2) - 50} ${(this.height / 2) + 0.5} L ${(this.width / 2) + 50} ${(this.height / 2) + 0.5}`, + style: `stroke-width: ${this.combinedWeight + 0.5}` + }; + } + + calcTotalValue(tx: Transaction): number { + const totalOutput = this.tx.vout.reduce((acc, v) => (v.value == null ? 0 : v.value) + acc, 0); + // simple sum of outputs + fee for bitcoin + if (!this.isLiquid) { + return this.tx.fee ? totalOutput + this.tx.fee : totalOutput; + } else { + const totalInput = this.tx.vin.reduce((acc, v) => (v?.prevout?.value == null ? 0 : v.prevout.value) + acc, 0); + const confidentialInputCount = this.tx.vin.reduce((acc, v) => acc + (v?.prevout?.value == null ? 1 : 0), 0); + const confidentialOutputCount = this.tx.vout.reduce((acc, v) => acc + (v.value == null ? 1 : 0), 0); + + // if there are unknowns on both sides, the total is indeterminate, so we'll just fudge it + if (confidentialInputCount && confidentialOutputCount) { + const knownInputCount = (tx.vin.length - confidentialInputCount) || 1; + const knownOutputCount = (tx.vout.length - confidentialOutputCount) || 1; + // assume confidential inputs/outputs have the same average value as the known ones + const adjustedTotalInput = totalInput + ((totalInput / knownInputCount) * confidentialInputCount); + const adjustedTotalOutput = totalOutput + ((totalOutput / knownOutputCount) * confidentialOutputCount); + return Math.max(adjustedTotalInput, adjustedTotalOutput) || 1; + } else { + // otherwise knowing the actual total of one side suffices + return Math.max(totalInput, totalOutput) || 1; + } + } + } + + initLines(side: 'in' | 'out', xputs: { type: string, value: number | void }[], total: number, maxVisibleStrands: number): SvgLine[] { + const lines = []; + let unknownCount = 0; + let unknownTotal = total == null ? this.combinedWeight : total; + xputs.forEach(put => { + if (put.value == null) { + unknownCount++; + } else { + unknownTotal -= put.value as number; + } + }); + const unknownShare = unknownTotal / unknownCount; + + // conceptual weights + const weights = xputs.map((put): number => this.combinedWeight * (put.value == null ? unknownShare : put.value as number) / total); + // actual displayed line thicknesses + const minWeights = weights.map((w) => Math.max(this.minWeight - 1, w) + 1); + const visibleStrands = Math.min(maxVisibleStrands, xputs.length); + const visibleWeight = minWeights.slice(0, visibleStrands).reduce((acc, v) => v + acc, 0); + const gaps = visibleStrands - 1; + + const innerTop = (this.height / 2) - (this.combinedWeight / 2); + const innerBottom = innerTop + this.combinedWeight; + // tracks the visual bottom of the endpoints of the previous line + let lastOuter = 0; + let lastInner = innerTop; + // gap between strands + const spacing = (this.height - visibleWeight) / gaps; + + for (let i = 0; i < xputs.length; i++) { + const weight = weights[i]; + const minWeight = minWeights[i]; + // set the vertical position of the (center of the) outer side of the line + let outer = lastOuter + (minWeight / 2); + const inner = Math.min(innerBottom + (minWeight / 2), Math.max(innerTop + (minWeight / 2), lastInner + (weight / 2))); + + // special case to center single input/outputs + if (xputs.length === 1) { + outer = (this.height / 2); + } + + lastOuter += minWeight + spacing; + lastInner += weight; + lines.push({ + path: this.makePath(side, outer, inner, minWeight), + style: this.makeStyle(minWeight, xputs[i].type), + class: xputs[i].type + }); + } + + return lines; + } + + makePath(side: 'in' | 'out', outer: number, inner: number, weight: number): string { + const start = side === 'in' ? (weight * 0.5) : this.width - (weight * 0.5); + const center = this.width / 2 + (side === 'in' ? -45 : 45 ); + const midpoint = (start + center) / 2; + return `M ${start} ${outer} C ${midpoint} ${outer}, ${midpoint} ${inner}, ${center} ${inner}`; + } + + makeStyle(minWeight, type): string { + if (type === 'fee') { + return `stroke-width: ${minWeight}; stroke: url(#fee-gradient)`; + } else { + return `stroke-width: ${minWeight}`; + } + } +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index f9de57834..c340fb50b 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -63,6 +63,7 @@ import { StatusViewComponent } from '../components/status-view/status-view.compo import { FeesBoxComponent } from '../components/fees-box/fees-box.component'; import { DifficultyComponent } from '../components/difficulty/difficulty.component'; import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component'; +import { TxBowtieGraphComponent } from '../components/tx-bowtie-graph/tx-bowtie-graph.component'; import { PrivacyPolicyComponent } from '../components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from '../components/trademark-policy/trademark-policy.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; @@ -138,6 +139,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati StatusViewComponent, FeesBoxComponent, DifficultyComponent, + TxBowtieGraphComponent, TermsOfServiceComponent, PrivacyPolicyComponent, TrademarkPolicyComponent, @@ -242,6 +244,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati StatusViewComponent, FeesBoxComponent, DifficultyComponent, + TxBowtieGraphComponent, TermsOfServiceComponent, PrivacyPolicyComponent, TrademarkPolicyComponent, From 3b2061bb5c25421262de269e8924235f39dae2ab Mon Sep 17 00:00:00 2001 From: Stephan Oeste Date: Sat, 27 Aug 2022 15:52:42 +0200 Subject: [PATCH 02/12] Only source mysql_credentials if present in prod install --- production/mempool-build-all | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/production/mempool-build-all b/production/mempool-build-all index c0e9a2c2a..7f851f813 100755 --- a/production/mempool-build-all +++ b/production/mempool-build-all @@ -12,7 +12,10 @@ ELEMENTS_RPC_USER=$(grep '^rpcuser' /elements/elements.conf | cut -d '=' -f2) ELEMENTS_RPC_PASS=$(grep '^rpcpassword' /elements/elements.conf | cut -d '=' -f2) # get mysql credentials -. /mempool/mysql_credentials +MYSQL_CRED_FILE=${HOME}/mempool/mysql_credentials +if [ -f "${MYSQL_CRED_FILE}" ];then + . ${MYSQL_CRED_FILE} +} if [ -f "${LOCKFILE}" ];then echo "upgrade already running? check lockfile ${LOCKFILE}" From c2c7448c45b5fa6b2d09ac1b4a458a1fb88da036 Mon Sep 17 00:00:00 2001 From: Stephan Oeste Date: Sat, 27 Aug 2022 15:58:01 +0200 Subject: [PATCH 03/12] Start cln after Bitcoind in prod installer --- production/install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/production/install b/production/install index 6d50e8bf7..71bcb7f6b 100755 --- a/production/install +++ b/production/install @@ -1287,9 +1287,9 @@ case $OS in osPackageInstall ${CLN_PKG} echo "[*] Installing Core Lightning mainnet Cronjob" - crontab_cln+='@reboot sleep 30 ; screen -dmS main lightningd --alias `hostname` --bitcoin-datadir /bitcoin\n' - crontab_cln+='@reboot sleep 60 ; screen -dmS sig lightningd --alias `hostname` --bitcoin-datadir /bitcoin --network signet\n' + crontab_cln+='@reboot sleep 60 ; screen -dmS main lightningd --alias `hostname` --bitcoin-datadir /bitcoin\n' crontab_cln+='@reboot sleep 90 ; screen -dmS tes lightningd --alias `hostname` --bitcoin-datadir /bitcoin --network testnet\n' + crontab_cln+='@reboot sleep 120 ; screen -dmS sig lightningd --alias `hostname` --bitcoin-datadir /bitcoin --network signet\n' echo "${crontab_cln}" | crontab -u "${CLN_USER}" - ;; Debian) From 4ca87e730c3e24b166bbbf7f636feee5215adbbc Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 27 Aug 2022 19:02:22 +0000 Subject: [PATCH 04/12] Fix unfurls for lightning pages with no geodata --- .../channel/channel-preview.component.html | 2 +- .../lightning/node/node-preview.component.html | 2 +- .../nodes-channels-map.component.ts | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/lightning/channel/channel-preview.component.html b/frontend/src/app/lightning/channel/channel-preview.component.html index c98929931..a847975c2 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.html +++ b/frontend/src/app/lightning/channel/channel-preview.component.html @@ -58,7 +58,7 @@
- +
diff --git a/frontend/src/app/lightning/node/node-preview.component.html b/frontend/src/app/lightning/node/node-preview.component.html index 0bb7255a6..a94882161 100644 --- a/frontend/src/app/lightning/node/node-preview.component.html +++ b/frontend/src/app/lightning/node/node-preview.component.html @@ -52,7 +52,7 @@
- +
diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts index 91de48186..c6a2f8f5c 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts @@ -22,6 +22,7 @@ export class NodesChannelsMap implements OnInit { @Input() channel: any[] = []; @Input() fitContainer = false; @Input() hasLocation = true; + @Input() placeholder = false; @Output() readyEvent = new EventEmitter(); channelsObservable: Observable; @@ -201,11 +202,26 @@ export class NodesChannelsMap implements OnInit { prepareChartOptions(nodes, channels) { let title: object; - if (channels.length === 0) { + if (channels.length === 0 && !this.placeholder) { this.chartOptions = null; return; } + // empty map fallback + if (channels.length === 0 && this.placeholder) { + title = { + textStyle: { + color: 'white', + fontSize: 18 + }, + text: $localize`No geolocation data available`, + left: 'center', + top: 'center' + }; + this.zoom = 1.5; + this.center = [0, 20]; + } + this.chartOptions = { silent: this.style === 'widget', title: title ?? undefined, From 141789b034abdc718787f345c469f94760165b19 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 27 Aug 2022 20:59:36 +0000 Subject: [PATCH 05/12] handle locale & preview in network detection regex --- frontend/src/app/services/state.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 14d220fa1..b0e018941 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -153,7 +153,12 @@ export class StateService { if (this.env.BASE_MODULE !== 'mempool' && this.env.BASE_MODULE !== 'liquid') { return; } - const networkMatches = url.match(/^\/(bisq|testnet|liquidtestnet|liquid|signet)/); + // horrible network regex breakdown: + // /^\/ starts with a forward slash... + // (?:[a-z]{2}(?:-[A-Z]{2})?\/)? optional locale prefix (non-capturing) + // (?:preview\/)? optional "preview" prefix (non-capturing) + // (bisq|testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1]) + const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(bisq|testnet|liquidtestnet|liquid|signet)/); switch (networkMatches && networkMatches[1]) { case 'liquid': if (this.network !== 'liquid') { From 626b4e61cd69a8f85822a546d0079d13df67fd83 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 24 Aug 2022 18:54:11 +0000 Subject: [PATCH 06/12] Restyle transaction preview diagram --- .../transaction-preview.component.html | 28 ++++--- .../transaction-preview.component.scss | 84 +++++++++++++------ .../transaction-preview.component.ts | 23 +++++ .../tx-bowtie-graph.component.html | 16 +++- .../tx-bowtie-graph.component.scss | 11 ++- .../tx-bowtie-graph.component.ts | 28 ++++++- 6 files changed, 145 insertions(+), 45 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction-preview.component.html b/frontend/src/app/components/transaction/transaction-preview.component.html index 991f51948..44be1dcfa 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.html +++ b/frontend/src/app/components/transaction/transaction-preview.component.html @@ -2,6 +2,9 @@

Transaction

+ + {{txId.slice(0,-4)}}{{txId.slice(-4)}} +
@@ -13,21 +16,24 @@
-

- - {{ txId }} - -

+
+ + Confidential + + + + + ‎{{ (tx.status.confirmed ? tx.status.block_time : transactionTime) * 1000 | date:'yyyy-MM-dd HH:mm' }} + Fee {{ tx.fee | number }} sat +
- +
-

- Confidential - - - +

+ +

{{ tx.feePerVsize | feeRounding }} sat/vB diff --git a/frontend/src/app/components/transaction/transaction-preview.component.scss b/frontend/src/app/components/transaction/transaction-preview.component.scss index 4a65dd0d8..7aefe0063 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.scss +++ b/frontend/src/app/components/transaction/transaction-preview.component.scss @@ -10,26 +10,10 @@ font-size: 28px; } -.btn-small-height { - line-height: 1.1; -} - -.arrow-green { - color: #1a9436; -} - -.arrow-red { - color: #dc3545; -} - .row { flex-direction: row; } -.effective-fee-container { - display: inline-block; -} - .title { h2 { line-height: 1; @@ -46,8 +30,9 @@ display: flex; flex-direction: row; justify-content: space-between; - align-items: center; - margin-bottom: 10px; + align-items: baseline; + margin-bottom: 2px; + max-width: 100%; h1 { font-size: 52px; @@ -58,6 +43,43 @@ .features { font-size: 24px; } + + & > * { + flex-grow: 0; + flex-shrink: 0; + } + + .tx-link { + flex-grow: 1; + flex-shrink: 1; + margin: 0 1em; + overflow: hidden; + white-space: nowrap; + display: flex; + flex-direction: row; + align-items: baseline; + + .truncated { + flex-grow: 1; + flex-shrink: 1; + overflow: hidden; + text-overflow: ellipsis; + margin-right: -2px; + } + + .last-four { + flex-shrink: 0; + flex-grow: 0; + } + } + + .features { + align-self: center; + } +} + +.top-data { + font-size: 28px; } .table { @@ -68,6 +90,23 @@ } } +.field { + font-size: 32px; + margin: 0; + + ::ng-deep .symbol { + font-size: 24px; + } + + .label { + color: #ffffff66; + } + + &.pair > *:first-child { + margin-right: 1em; + } +} + .tx-link { display: inline; font-size: 28px; @@ -87,15 +126,6 @@ right: 0; margin: auto; text-align: center; - - .field { - font-size: 32px; - margin: 0; - - ::ng-deep .symbol { - font-size: 24px; - } - } } .overlaid { diff --git a/frontend/src/app/components/transaction/transaction-preview.component.ts b/frontend/src/app/components/transaction/transaction-preview.component.ts index 15a881446..d30789f6b 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.ts +++ b/frontend/src/app/components/transaction/transaction-preview.component.ts @@ -29,6 +29,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { isLoadingTx = true; error: any = undefined; errorUnblinded: any = undefined; + transactionTime = -1; subscription: Subscription; fetchCpfpSubscription: Subscription; cpfpInfo: CpfpInfo | null; @@ -163,6 +164,12 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { this.opReturns = this.getOpReturns(this.tx); this.extraData = this.chooseExtraData(); + if (!tx.status.confirmed && tx.firstSeen) { + this.transactionTime = tx.firstSeen; + } else { + this.getTransactionTime(); + } + if (!this.tx.status.confirmed) { if (tx.cpfpChecked) { this.cpfpInfo = { @@ -185,10 +192,26 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { ); } + getTransactionTime() { + this.openGraphService.waitFor('tx-time'); + this.apiService + .getTransactionTimes$([this.tx.txid]) + .pipe( + catchError((err) => { + return of(0); + }) + ) + .subscribe((transactionTimes) => { + this.transactionTime = transactionTimes[0]; + this.openGraphService.waitOver('tx-time'); + }); + } + resetTransaction() { this.error = undefined; this.tx = null; this.isLoadingTx = true; + this.transactionTime = -1; this.cpfpInfo = null; this.showCpfpDetails = false; } diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html index d3452b5a9..c4771c58c 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.html @@ -5,14 +5,14 @@ markerUnits="strokeWidth" markerWidth="1.5" markerHeight="1" orient="auto"> - + - + + + + + + + + + - - + + diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss index ea45e4495..6de41b95f 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.scss @@ -1,6 +1,15 @@ .bowtie { .line { - stroke: white; fill: none; + + &.input { + stroke: url(#input-gradient); + } + &.output { + stroke: url(#output-gradient); + } + &.fee { + stroke: url(#fee-gradient); + } } } diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 68fdabeae..427a282a9 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -14,7 +14,7 @@ interface SvgLine { }) export class TxBowtieGraphComponent implements OnInit, OnChanges { @Input() tx: Transaction; - @Input() isLiquid: boolean = false; + @Input() network: string; @Input() width = 1200; @Input() height = 600; @Input() combinedWeight = 100; @@ -24,12 +24,32 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { inputs: SvgLine[]; outputs: SvgLine[]; middle: SvgLine; + isLiquid: boolean = false; + + gradientColors = { + '': ['#9339f4', '#105fb0'], + bisq: ['#9339f4', '#105fb0'], + // liquid: ['#116761', '#183550'], + liquid: ['#09a197', '#0f62af'], + // 'liquidtestnet': ['#494a4a', '#272e46'], + 'liquidtestnet': ['#d2d2d2', '#979797'], + // testnet: ['#1d486f', '#183550'], + testnet: ['#4edf77', '#10a0af'], + // signet: ['#6f1d5d', '#471850'], + signet: ['#d24fc8', '#a84fd2'], + }; + + gradient: string[] = ['#105fb0', '#105fb0']; ngOnInit(): void { + this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet'); + this.gradient = this.gradientColors[this.network]; this.initGraph(); } ngOnChanges(): void { + this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet'); + this.gradient = this.gradientColors[this.network]; this.initGraph(); } @@ -46,7 +66,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { this.middle = { path: `M ${(this.width / 2) - 50} ${(this.height / 2) + 0.5} L ${(this.width / 2) + 50} ${(this.height / 2) + 0.5}`, - style: `stroke-width: ${this.combinedWeight + 0.5}` + style: `stroke-width: ${this.combinedWeight + 0.5}; stroke: ${this.gradient[1]}` }; } @@ -132,6 +152,10 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { const start = side === 'in' ? (weight * 0.5) : this.width - (weight * 0.5); const center = this.width / 2 + (side === 'in' ? -45 : 45 ); const midpoint = (start + center) / 2; + // correct for svg horizontal gradient bug + if (Math.round(outer) === Math.round(inner)) { + outer -= 1; + } return `M ${start} ${outer} C ${midpoint} ${outer}, ${midpoint} ${inner}, ${center} ${inner}`; } From 7ba8a3da8444fcdd88bcf9bfbfa26286cda2d699 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 24 Aug 2022 16:11:15 +0200 Subject: [PATCH 07/12] Hardcode some condition to invalidate imported topology files --- .../lightning/sync-tasks/stats-importer.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/backend/src/tasks/lightning/sync-tasks/stats-importer.ts b/backend/src/tasks/lightning/sync-tasks/stats-importer.ts index 7ac1c5885..96274225e 100644 --- a/backend/src/tasks/lightning/sync-tasks/stats-importer.ts +++ b/backend/src/tasks/lightning/sync-tasks/stats-importer.ts @@ -367,6 +367,12 @@ class LightningStatsImporter { continue; } + if (this.isIncorrectSnapshot(timestamp, graph)) { + logger.debug(`Ignoring ${this.topologiesFolder}/${filename}, because we defined it as an incorrect snapshot`); + ++totalProcessed; + continue; + } + if (!logStarted) { logger.info(`Founds a topology file that we did not import. Importing historical lightning stats now.`); logStarted = true; @@ -397,7 +403,7 @@ class LightningStatsImporter { } } - async cleanupTopology(graph) { + cleanupTopology(graph): ILightningApi.NetworkGraph { const newGraph = { nodes: [], edges: [], @@ -456,6 +462,29 @@ class LightningStatsImporter { return newGraph; } + + private isIncorrectSnapshot(timestamp, graph): boolean { + if (timestamp >= 1549065600 /* 2019-02-02 */ && timestamp <= 1550620800 /* 2019-02-20 */ && graph.nodes.length < 2600) { + return true; + } + if (timestamp >= 1552953600 /* 2019-03-19 */ && timestamp <= 1556323200 /* 2019-05-27 */ && graph.nodes.length < 4000) { + return true; + } + if (timestamp >= 1557446400 /* 2019-05-10 */ && timestamp <= 1560470400 /* 2019-06-14 */ && graph.nodes.length < 4000) { + return true; + } + if (timestamp >= 1561680000 /* 2019-06-28 */ && timestamp <= 1563148800 /* 2019-07-15 */ && graph.nodes.length < 4000) { + return true; + } + if (timestamp >= 1574035200 /* 2019-11-18 */ && timestamp <= 1579305600 /* 2020-01-18 */ && graph.nodes.length < 3000) { + return true; + } + if (timestamp >= 1591142400 /* 2020-06-03 */ && timestamp <= 1592006400 /* 2020-06-13 */ && graph.nodes.length < 5500) { + return true; + } + + return false; + } } export default new LightningStatsImporter; From 6b049f2c333ba257b868da09cd27ba83d732f29c Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 28 Aug 2022 07:55:43 +0200 Subject: [PATCH 08/12] Manually delete some lightning_stats rows once the topology import is completed --- .../lightning/sync-tasks/stats-importer.ts | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/backend/src/tasks/lightning/sync-tasks/stats-importer.ts b/backend/src/tasks/lightning/sync-tasks/stats-importer.ts index 96274225e..e71b12375 100644 --- a/backend/src/tasks/lightning/sync-tasks/stats-importer.ts +++ b/backend/src/tasks/lightning/sync-tasks/stats-importer.ts @@ -8,6 +8,7 @@ import { isIP } from 'net'; import { Common } from '../../../api/common'; import channelsApi from '../../../api/explorer/channels.api'; import nodesApi from '../../../api/explorer/nodes.api'; +import { ResultSetHeader } from 'mysql2'; const fsPromises = promises; @@ -20,6 +21,7 @@ class LightningStatsImporter { await fundingTxFetcher.$fetchChannelsFundingTxs(channels.map(channel => channel.short_id)); await this.$importHistoricalLightningStats(); + await this.$cleanupIncorrectSnapshot(); } /** @@ -476,15 +478,55 @@ class LightningStatsImporter { if (timestamp >= 1561680000 /* 2019-06-28 */ && timestamp <= 1563148800 /* 2019-07-15 */ && graph.nodes.length < 4000) { return true; } - if (timestamp >= 1574035200 /* 2019-11-18 */ && timestamp <= 1579305600 /* 2020-01-18 */ && graph.nodes.length < 3000) { + if (timestamp >= 1571270400 /* 2019-11-17 */ && timestamp <= 1580601600 /* 2020-02-02 */ && graph.nodes.length < 4500) { return true; } if (timestamp >= 1591142400 /* 2020-06-03 */ && timestamp <= 1592006400 /* 2020-06-13 */ && graph.nodes.length < 5500) { return true; } + if (timestamp >= 1632787200 /* 2021-09-28 */ && timestamp <= 1633564800 /* 2021-10-07 */ && graph.nodes.length < 13000) { + return true; + } + if (timestamp >= 1634256000 /* 2021-10-15 */ && timestamp <= 1645401600 /* 2022-02-21 */ && graph.nodes.length < 17000) { + return true; + } + if (timestamp >= 1654992000 /* 2022-06-12 */ && timestamp <= 1661472000 /* 2022-08-26 */ && graph.nodes.length < 14000) { + return true; + } return false; } + + private async $cleanupIncorrectSnapshot(): Promise { + // We do not run this one automatically because those stats are not supposed to be inserted in the first + // place, but I write them here to remind us we manually run those queries + + // DELETE FROM lightning_stats + // WHERE ( + // UNIX_TIMESTAMP(added) >= 1549065600 AND UNIX_TIMESTAMP(added) <= 1550620800 AND node_count < 2600 OR + // UNIX_TIMESTAMP(added) >= 1552953600 AND UNIX_TIMESTAMP(added) <= 1556323200 AND node_count < 4000 OR + // UNIX_TIMESTAMP(added) >= 1557446400 AND UNIX_TIMESTAMP(added) <= 1560470400 AND node_count < 4000 OR + // UNIX_TIMESTAMP(added) >= 1561680000 AND UNIX_TIMESTAMP(added) <= 1563148800 AND node_count < 4000 OR + // UNIX_TIMESTAMP(added) >= 1571270400 AND UNIX_TIMESTAMP(added) <= 1580601600 AND node_count < 4500 OR + // UNIX_TIMESTAMP(added) >= 1591142400 AND UNIX_TIMESTAMP(added) <= 1592006400 AND node_count < 5500 OR + // UNIX_TIMESTAMP(added) >= 1632787200 AND UNIX_TIMESTAMP(added) <= 1633564800 AND node_count < 13000 OR + // UNIX_TIMESTAMP(added) >= 1634256000 AND UNIX_TIMESTAMP(added) <= 1645401600 AND node_count < 17000 OR + // UNIX_TIMESTAMP(added) >= 1654992000 AND UNIX_TIMESTAMP(added) <= 1661472000 AND node_count < 14000 + // ) + + // DELETE FROM node_stats + // WHERE ( + // UNIX_TIMESTAMP(added) >= 1549065600 AND UNIX_TIMESTAMP(added) <= 1550620800 OR + // UNIX_TIMESTAMP(added) >= 1552953600 AND UNIX_TIMESTAMP(added) <= 1556323200 OR + // UNIX_TIMESTAMP(added) >= 1557446400 AND UNIX_TIMESTAMP(added) <= 1560470400 OR + // UNIX_TIMESTAMP(added) >= 1561680000 AND UNIX_TIMESTAMP(added) <= 1563148800 OR + // UNIX_TIMESTAMP(added) >= 1571270400 AND UNIX_TIMESTAMP(added) <= 1580601600 OR + // UNIX_TIMESTAMP(added) >= 1591142400 AND UNIX_TIMESTAMP(added) <= 1592006400 OR + // UNIX_TIMESTAMP(added) >= 1632787200 AND UNIX_TIMESTAMP(added) <= 1633564800 OR + // UNIX_TIMESTAMP(added) >= 1634256000 AND UNIX_TIMESTAMP(added) <= 1645401600 OR + // UNIX_TIMESTAMP(added) >= 1654992000 AND UNIX_TIMESTAMP(added) <= 1661472000 + // ) + } } export default new LightningStatsImporter; From 930f1e4f091a931420cfcad2ea927bd84b151e42 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 28 Aug 2022 08:58:41 +0200 Subject: [PATCH 09/12] Truncate node alias if it's too long --- frontend/src/app/lightning/node/node.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 423b29afb..48103ec36 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -1,7 +1,7 @@

Lightning node
-

{{ node.alias }}

+

{{ node.alias }}

{{ node.public_key | shortenString : publicKeySize }} From d8e87bccab1cc15d063c498702953c6ed31b0b4f Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 28 Aug 2022 11:11:53 +0200 Subject: [PATCH 10/12] Fix js crash in node page and add loading spinner to channel tree chart --- .../channels-list.component.html | 2 -- .../app/lightning/node/node.component.html | 1 - .../node-channels.component.html | 9 ++++++- .../node-channels.component.scss | 9 +++++++ .../nodes-channels/node-channels.component.ts | 27 ++++++++++++++----- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/lightning/channels-list/channels-list.component.html b/frontend/src/app/lightning/channels-list/channels-list.component.html index 0dd2de183..b8920f4bc 100644 --- a/frontend/src/app/lightning/channels-list/channels-list.component.html +++ b/frontend/src/app/lightning/channels-list/channels-list.component.html @@ -87,8 +87,6 @@ -

Channels

- diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 423b29afb..d1c257854 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -131,7 +131,6 @@ -

Active channels map

diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.html b/frontend/src/app/lightning/nodes-channels/node-channels.component.html index 43a5fad60..8fc63793c 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.html +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.html @@ -1,2 +1,9 @@ -
+
+

Active channels map

+
+
+
+ +
+
diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.scss b/frontend/src/app/lightning/nodes-channels/node-channels.component.scss index e69de29bb..4d7b4de0e 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.scss +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.scss @@ -0,0 +1,9 @@ +.loading-spinner { + min-height: 455px; + z-index: 100; +} + +.spinner-border { + position: relative; + top: 225px; +} \ No newline at end of file diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts index 9d6d7df2b..074315b35 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts @@ -1,8 +1,8 @@ import { formatNumber } from '@angular/common'; -import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core'; import { Router } from '@angular/router'; import { ECharts, EChartsOption, TreemapSeriesOption } from 'echarts'; -import { Observable, tap } from 'rxjs'; +import { Observable, share, switchMap, tap } from 'rxjs'; import { lerpColor } from 'src/app/shared/graphs.utils'; import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe'; import { LightningApiService } from '../lightning-api.service'; @@ -25,7 +25,7 @@ export class NodeChannels implements OnChanges { }; channelsObservable$: Observable; - isLoading: true; + isLoading = true; constructor( @Inject(LOCALE_ID) public locale: string, @@ -41,9 +41,20 @@ export class NodeChannels implements OnChanges { this.channelsObservable$ = this.lightningApiService.getChannelsByNodeId$(this.publicKey, -1, 'active') .pipe( - tap((response) => { - const biggestCapacity = response.body[0].capacity; - this.prepareChartOptions(response.body.map(channel => { + switchMap((response) => { + this.isLoading = true; + if ((response.body?.length ?? 0) <= 0) { + return []; + } + return [response.body]; + }), + tap((body: any[]) => { + if (body.length === 0) { + this.isLoading = false; + return; + } + const biggestCapacity = body[0].capacity; + this.prepareChartOptions(body.map(channel => { return { name: channel.node.alias, value: channel.capacity, @@ -54,7 +65,9 @@ export class NodeChannels implements OnChanges { } }; })); - }) + this.isLoading = false; + }), + share(), ); } From 8ec61dd603cb52147f539e5f2c0b148a1a5e78e8 Mon Sep 17 00:00:00 2001 From: wiz Date: Sun, 28 Aug 2022 13:04:40 +0200 Subject: [PATCH 11/12] Update ops scripts for unfurler and cache warmer --- production/mempool-build-all | 18 +++++++ production/mempool-kill-all | 8 +++- production/mempool-start-all | 22 +++++++++ production/nginx-cache-warmer | 14 ++++-- production/unfurl-build | 62 ------------------------- production/unfurl-kill | 2 - production/unfurl-start | 6 --- production/unfurler-config.liquid.json | 17 +++++++ production/unfurler-config.mainnet.json | 17 +++++++ unfurler/package.json | 2 +- unfurler/puppeteer.config.json | 2 +- 11 files changed, 93 insertions(+), 77 deletions(-) delete mode 100755 production/unfurl-build delete mode 100755 production/unfurl-kill delete mode 100755 production/unfurl-start create mode 100644 production/unfurler-config.liquid.json create mode 100644 production/unfurler-config.mainnet.json diff --git a/production/mempool-build-all b/production/mempool-build-all index c0e9a2c2a..4d0e51d24 100755 --- a/production/mempool-build-all +++ b/production/mempool-build-all @@ -63,6 +63,19 @@ build_frontend() npm run build || exit 1 } +build_unfurler() +{ + local site="$1" + echo "[*] Building unfurler for ${site}" + [ -z "${HASH}" ] && exit 1 + cd "$HOME/${site}/unfurler" || exit 1 + if [ ! -e "config.json" ];then + cp "${HOME}/mempool/production/unfurler-config.${site}.json" "config.json" + fi + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install || exit 1 + npm run build || exit 1 +} + build_backend() { local site="$1" @@ -128,6 +141,11 @@ for repo in $backend_repos;do update_repo "${repo}" done +# build unfurlers +for repo in mainnet liquid;do + build_unfurler "${repo}" +done + # build backends for repo in $backend_repos;do build_backend "${repo}" diff --git a/production/mempool-kill-all b/production/mempool-kill-all index ae48552c2..b75969155 100755 --- a/production/mempool-kill-all +++ b/production/mempool-kill-all @@ -1,2 +1,8 @@ #!/usr/bin/env zsh -killall sh node +killall sh +killall node +killall chrome +killall xinit +for pid in `ps uaxww|grep warmer|grep zsh|awk '{print $2}'`;do + kill $pid +done diff --git a/production/mempool-start-all b/production/mempool-start-all index 94766d5ce..a6fdfa589 100755 --- a/production/mempool-start-all +++ b/production/mempool-start-all @@ -2,7 +2,29 @@ export NVM_DIR="$HOME/.nvm" source "$NVM_DIR/nvm.sh" +# start all mempool backends that exist for site in mainnet mainnet-lightning testnet testnet-lightning signet signet-lightning bisq 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' done + +# only start unfurler if GPU present +if pciconf -lv|grep -i nvidia >/dev/null 2>&1;then + export DISPLAY=:0 + screen -dmS x startx + sleep 3 + for site in mainnet liquid;do + cd "$HOME/${site}/unfurler" && \ + echo "starting mempool unfurler: ${site}" && \ + screen -dmS "unfurler-${site}" sh -c 'while true;do npm run unfurler;sleep 2;done' + done +fi + +# start nginx warm cacher +for site in mainnet;do + echo "starting mempool cache warmer: ${site}" + screen -dmS "warmer-${site}" $HOME/mempool/production/nginx-cache-warmer +done + +exit 0 diff --git a/production/nginx-cache-warmer b/production/nginx-cache-warmer index a4ece6e0b..3c0fd8ef1 100755 --- a/production/nginx-cache-warmer +++ b/production/nginx-cache-warmer @@ -2,6 +2,12 @@ hostname=$(hostname) slugs=(`curl -sSL https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json | jq -r '.slugs[]'`) +warm() +{ + echo "$1" + curl -i -s "$1" | head -1 +} + while true do for url in / \ '/api/v1/blocks' \ @@ -81,14 +87,14 @@ do for url in / \ '/api/v1/lightning/channels-geo?style=graph' \ do - curl -s "https://${hostname}${url}" >/dev/null + warm "https://${hostname}${url}" done for slug in $slugs do - curl -s "https://${hostname}/api/v1/mining/pool/${slug}" >/dev/null - curl -s "https://${hostname}/api/v1/mining/pool/${slug}/hashrate" >/dev/null - curl -s "https://${hostname}/api/v1/mining/pool/${slug}/blocks" >/dev/null + warm "https://${hostname}/api/v1/mining/pool/${slug}" + warm "https://${hostname}/api/v1/mining/pool/${slug}/hashrate" + warm "https://${hostname}/api/v1/mining/pool/${slug}/blocks" done sleep 10 diff --git a/production/unfurl-build b/production/unfurl-build deleted file mode 100755 index 5b838e0ae..000000000 --- a/production/unfurl-build +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env zsh -PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:$HOME/bin -HOSTNAME=$(hostname) -LOCATION=$(hostname|cut -d . -f2) -LOCKFILE="${HOME}/lock" -REF=$(echo "${1:=origin/master}"|sed -e 's!:!/!') - -if [ -f "${LOCKFILE}" ];then - echo "upgrade already running? check lockfile ${LOCKFILE}" - exit 1 -fi - -# on exit, remove lockfile but preserve exit code -trap "rv=\$?; rm -f "${LOCKFILE}"; exit \$rv" INT TERM EXIT - -# create lockfile -touch "${LOCKFILE}" - -# notify logged in users -echo "Upgrading unfurler to ${REF}" | wall - -update_repo() -{ - echo "[*] Upgrading unfurler to ${REF}" - cd "$HOME/unfurl/unfurler" || exit 1 - - git fetch origin || exit 1 - for remote in origin;do - git remote add "${remote}" "https://github.com/${remote}/mempool" >/dev/null 2>&1 - git fetch "${remote}" || exit 1 - done - - if [ $(git tag -l "${REF}") ];then - git reset --hard "tags/${REF}" || exit 1 - elif [ $(git branch -r -l "origin/${REF}") ];then - git reset --hard "origin/${REF}" || exit 1 - else - git reset --hard "${REF}" || exit 1 - fi - export HASH=$(git rev-parse HEAD) -} - -build_backend() -{ - echo "[*] Building backend for unfurler" - [ -z "${HASH}" ] && exit 1 - cd "$HOME/unfurl/unfurler" || exit 1 - if [ ! -e "config.json" ];then - cp "${HOME}/unfurl/production/mempool-config.unfurl.json" "config.json" - fi - npm install || exit 1 - npm run build || exit 1 -} - -update_repo -build_backend - -# notify everyone -echo "${HOSTNAME} unfurl updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.dev -echo "${HOSTNAME} unfurl updated to \`${REF}\` @ \`${HASH}\`" | /usr/local/bin/keybase chat send --nonblock --channel general "mempool.ops.${LOCATION}" - -exit 0 diff --git a/production/unfurl-kill b/production/unfurl-kill deleted file mode 100755 index ae48552c2..000000000 --- a/production/unfurl-kill +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env zsh -killall sh node diff --git a/production/unfurl-start b/production/unfurl-start deleted file mode 100755 index 29b5ddf3e..000000000 --- a/production/unfurl-start +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env zsh -export NVM_DIR="$HOME/.nvm" -source "$NVM_DIR/nvm.sh" - -cd "${HOME}/unfurl/unfurler/" && \ -screen -dmS "unfurl" sh -c 'while true;do npm run start-production;sleep 1;done' diff --git a/production/unfurler-config.liquid.json b/production/unfurler-config.liquid.json new file mode 100644 index 000000000..39df7e3fd --- /dev/null +++ b/production/unfurler-config.liquid.json @@ -0,0 +1,17 @@ +{ + "SERVER": { + "HOST": "https://liquid.network", + "HTTP_PORT": 8002 + }, + "MEMPOOL": { + "HTTP_HOST": "https://liquid.network", + "HTTP_PORT": 443, + "NETWORK": "liquid" + }, + "PUPPETEER": { + "CLUSTER_SIZE": 8, + "EXEC_PATH": "/usr/local/bin/chrome", + "MAX_PAGE_AGE": 86400, + "RENDER_TIMEOUT": 3000 + } +} diff --git a/production/unfurler-config.mainnet.json b/production/unfurler-config.mainnet.json new file mode 100644 index 000000000..752cd5706 --- /dev/null +++ b/production/unfurler-config.mainnet.json @@ -0,0 +1,17 @@ +{ + "SERVER": { + "HOST": "https://mempool.space", + "HTTP_PORT": 8001 + }, + "MEMPOOL": { + "HTTP_HOST": "https://mempool.space", + "HTTP_PORT": 443, + "NETWORK": "bitcoin" + }, + "PUPPETEER": { + "CLUSTER_SIZE": 8, + "EXEC_PATH": "/usr/local/bin/chrome", + "MAX_PAGE_AGE": 86400, + "RENDER_TIMEOUT": 3000 + } +} diff --git a/unfurler/package.json b/unfurler/package.json index 2d353bfdf..ca60201b3 100644 --- a/unfurler/package.json +++ b/unfurler/package.json @@ -11,7 +11,7 @@ "tsc": "./node_modules/typescript/bin/tsc", "build": "npm run tsc", "start": "node --max-old-space-size=2048 dist/index.js", - "start-production": "node --max-old-space-size=4096 dist/index.js", + "unfurler": "node --max-old-space-size=4096 dist/index.js", "lint": "./node_modules/.bin/eslint . --ext .ts", "lint:fix": "./node_modules/.bin/eslint . --ext .ts --fix", "prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\"" diff --git a/unfurler/puppeteer.config.json b/unfurler/puppeteer.config.json index 3de7b0652..b3a9b7fc4 100644 --- a/unfurler/puppeteer.config.json +++ b/unfurler/puppeteer.config.json @@ -41,6 +41,6 @@ "--use-mock-keychain", "--ignore-gpu-blacklist", "--ignore-gpu-blocklist", - "--use-gl=swiftshader" + "--use-gl=egl" ] } From f50bba1d39c8d2c5214bff3579af6d6ea347558e Mon Sep 17 00:00:00 2001 From: wiz Date: Sun, 28 Aug 2022 16:26:06 +0200 Subject: [PATCH 12/12] [ops] Fix typo in build script --- production/mempool-build-all | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/production/mempool-build-all b/production/mempool-build-all index a7c7864ef..6a4e0c028 100755 --- a/production/mempool-build-all +++ b/production/mempool-build-all @@ -15,7 +15,7 @@ ELEMENTS_RPC_PASS=$(grep '^rpcpassword' /elements/elements.conf | cut -d '=' -f2 MYSQL_CRED_FILE=${HOME}/mempool/mysql_credentials if [ -f "${MYSQL_CRED_FILE}" ];then . ${MYSQL_CRED_FILE} -} +fi if [ -f "${LOCKFILE}" ];then echo "upgrade already running? check lockfile ${LOCKFILE}"